diff --git a/.gitignore b/.gitignore index 1c0e35d65..e1cc944e2 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,4 @@ gradle **/*.swo build/kotlin/sessions/ **/*.iml +Server/hasRan.txt diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 306a67b70..03a4e5b25 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,16 +1,20 @@ variables: # This will suppress any download for dependencies and plugins or upload messages which would clutter the console log. # `showDateTime` will show the passed time in milliseconds. You need to specify `--batch-mode` to make this work. - MAVEN_OPTS: "-Dhttps.protocols=TLSv1.2 -Dmaven.repo.local=$CI_PROJECT_DIR/.m2/repository -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=WARN -Dorg.slf4j.simpleLogger.showDateTime=true -Djava.awt.headless=true -Xmx1024m" + MAVEN_OPTS: "-Dhttps.protocols=TLSv1.2 -Dmaven.repo.local=$CI_PROJECT_DIR/.m2/repository -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=WARN -Dorg.slf4j.simpleLogger.showDateTime=true -Djava.awt.headless=true -Xmx3g" # As of Maven 3.3.0 instead of this you may define these options in `.mvn/maven.config` so the same config is used # when running from the command line. MAVEN_CLI_OPTS: "--batch-mode --errors --fail-fast --show-version" -# This template uses jdk8 for verifying and deploying images -image: maven:3-openjdk-11 +image: maven:3-openjdk-11-slim + +workflow: + rules: + - if: $CI_PIPELINE_SOURCE == 'merge_request_event' before_script: - - apt-get update -qq && apt-get install -qq -y --no-install-recommends jq + - 'free -mh' + - 'apt-get update -q' # Cache downloaded dependencies and plugins between builds. # To keep cache across branches add 'key: "$CI_JOB_NAME"' @@ -22,7 +26,7 @@ verify:jdk11: stage: build script: - 'cd Server' - - 'mvn $MAVEN_CLI_OPTS verify -DskipTests' + - 'mvn $MAVEN_CLI_OPTS verify' except: variables: - $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH @@ -30,6 +34,7 @@ verify:jdk11: test:json: stage: test script: + - 'apt-get install -q -y --no-install-recommends jq' - 'find . -name \*.json -type f -print0 | xargs -0 -n1 -P8 jq empty' except: variables: diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000..7078b8c93 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,20 @@ +# Starting out with the openjdk-11-slim image +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 + +# Make sure ./run has permissions +RUN chmod +x run + +# Run it +CMD ["./run"] diff --git a/README.md b/README.md index 4bf8abdb3..caf98bfe5 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ There are many ways everyone can contribute! From the most seasoned programmers * **Wiki Editors**: Did you know we have a wiki? Well it's always in need of people to fill it out and stay on top of it. Editing the wiki is one of the easiest ways you can contribute to 2009Scape! If you're an active player and have the will, there's so much you could be helping out with over at the wiki. [Click here to go to the wiki](https://cdn.2009scape.org/wiki/doku.php?id=start). -* **JSON editors**: We could always use more JSON editors! Please note that JSON editing **must** be done using the [Thanos Tool](https://gitlab.com/2009scape/rs09-thanos-tool/-/jobs/artifacts/master/raw/build/libs/thanostool.jar?job=build). +* **JSON editors**: We could always use more JSON editors! Please note that JSON editing **must** be done using the [Thanos Tool](https://gitlab.com/2009scape/tools/rs09-thanos-tool/-/jobs/5153567519/artifacts/file/build/libs/zaros.jar). **This does not run on modern Java** and requires **Java 11** to run (using the usual 'java -jar zaros.jar'). * **Authenticity Auditors**: As a remake, authenticity is central to our core values! We could always use someone to go through the game and create large lists of simple tasks that can be done to bring us closer to the authentic 2009 game! The preferred way to do this is one-area-at-a-time. If you want to see an example of some audits we've done in the past, take a look [here](https://gitlab.com/2009scape/2009scape/-/issues/46). @@ -131,7 +131,7 @@ We use the AGPL 3.0 license, which can be found [here](https://www.gnu.org/licen ### Contact -**Reminder: There is no official support for setting up your own server. Do not ping/dm developers, or ask in support channels about setting up your server. Support for the live server and 2009Scape single-player is available in the Discord.** +**Reminder: There is no official support for setting up your own server. Do not ping/dm developers, or ask in support channels about setting up your server. Support for the live server and 2009Scape single-player is available on the [2009Scape forums](https://forum.2009scape.org/).** [license-shield]: https://img.shields.io/badge/license-AGPL--3.0-informational @@ -149,4 +149,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/ObjectParser.xml b/Server/data/ObjectParser.xml index 3a354d63b..e132f1f69 100644 --- a/Server/data/ObjectParser.xml +++ b/Server/data/ObjectParser.xml @@ -22,8 +22,10 @@ - + + + diff --git a/Server/data/botdata/bot_dialogue.json b/Server/data/botdata/bot_dialogue.json index 175cd4ea4..c225b117e 100644 --- a/Server/data/botdata/bot_dialogue.json +++ b/Server/data/botdata/bot_dialogue.json @@ -13,7 +13,7 @@ "This is my own dialogue", "@name run.", "@name you cant hide from me.", - "jajajajajaja", + "Jajajajajaja", "How do i get to Varrock @name?", "How do i get to Camelot @name?", "How do i get to Taverly @name?", @@ -21,17 +21,17 @@ "How do i get to Yanille @name?", "How do i get to Catherby @name?", "Gotta go srry.", - "wiggle wiggle wiggle yeah", + "Wiggle wiggle wiggle yeah", "@name lookin goofy", "@name lookin stoopid", "@name lookin like a red bracket", "@name get yo weasel lookin ass outta here", "@name lookin like some sort of twocan sam", " /hop world 1 got a noob here!", - "woot", + "Woot", "Hows your day been @name?", - "heyy @name :)", - "gtg srry", + "Heyy @name :)", + "Gtg srry", "I wont answer your questions @name", "Stop asking questions @name", "Roflmao", @@ -48,7 +48,7 @@ "Nice Armor @name!", "Nice Weapon @name!", "Venezuela #1", - "2009Scape and chill @name?", + "2009scape and chill @name?", "@name is my girlfriend", "Buying gf", "Bank sale pm me for info", @@ -61,14 +61,14 @@ "Fuckin sit @name.", "Error: Botting client broken tell Evilwaffles.", "@name bots all the time", - "report @name", - "apparently @name bots", + "Report @name", + "Apparently @name bots", "@name hates kermit", "There are no mods on to help you", "Report me, you wont", "Yes, Im botting. And?", "ERROR: BOTSCRIPT 404. REPORT TO BOT OWNER (Evilwaffles)", - "flash2:wave: FUCK", + "Flash2:wave: FUCK", "I love 2009Scape!", "Ja Ja Ja Ja", "This is fun!", @@ -105,9 +105,9 @@ "Trade me @name", "LOL", "How do I get to lumbrich", - "bruh", - "poggers", - "shitpost", + "Bruh", + "Poggers", + "Shitpost", "I wish i could find an RS Gf", "Where do you find runite ore @name?", "Where is the best coal location @name?", @@ -135,41 +135,39 @@ "Im not a bot", "Nah Im a real person", "Bruh are you even a real person lol?", - "e", + "E", "Hellooooo @name", - "wc lvl @name?", - "fletching level @name?", - "firemaking level @name?", + "Wc lvl @name?", + "Fletching level @name?", + "Firemaking level @name?", "Have you seen the dude in the ge?", "Wonderful weather today @name", "Lowkey drunk af rn", "I am so tired", "Wassup @name", - "follow me @name!", + "Follow me @name!", "Server goes brrrr", - "bruh i am not a bot", + "Bruh i am not a bot", "I think @name is a bot", "Are you a bot @name?", - "insert spiderman meme here", - "pot calling the kettle black etc", - "ooh, a piece of candy", + "Insert spiderman meme here", + "Pot calling the kettle black etc", + "Ooh, a piece of candy", "I love woodcutting", "Im going to go level up later", "I love mining shooting stars", " /this @name looks dumb", "AAAAAAAAAAAAAAAAAAAAAAAAHHHHHH!!!", - "como estas @name", - "Summer = always the best community manaer", - "so how about that ceikry guy", - "so how about that kermit dude", - "woah is an abusive mod", + "Como estas @name", + "So how about that ceikry guy", + "So how about that kermit dude", + "Woah is an abusive mod", "I heard Woah and Ceikry are dating now", "House party in Yanille!", "Can i have some gp @name?", "Where do i find partyhats?", "Why do mobs drop boxes?", "What exp rate do you play on @name?", - "Have you met Summer?", "Hey @name", "Hey @name", "Hey @name", @@ -190,33 +188,33 @@ "NobodyCLP has big feet", "Chicken tendies for dindin", "Spicey Chicken tendies is litty as a mf titty", - "red bracket stinky", - "ra ra rasputin", - "lover of the russian queen", + "Red bracket stinky", + "Ra ra rasputin", + "Lover of the russian queen", "Whens the next update coming out?", "How many players are maxed?", "I dont use discord @name", "I dont use the CC @name", "Why should i use discord?", "2009Scape is life", - "brb gotta make dinner", + "Brb gotta make dinner", "I need to go to the GE", "@name can i have a ge tele?", - "lol @name shut up", + "Lol @name shut up", "Where are teak trees?", "How do i make planks @name?", "Idk about that scraggley ass alex guy.", "Rusty? More like Crusty af lmfao.", "I need to sell some stuff on the ge", "I have so many logs to sell lol", - "nah", - "yes", + "Nah", + "Yes", "Where can i mine iron?", "Where can i mine tin?", "Where can i mine copper?", "Where can i mine clay?", "Where can i mine coal?", - "no", + "No", "We are no strangers to love", "You know the rules and so do I", "A full commitment's what I'm thinking of", @@ -230,10 +228,10 @@ "Never gonna make you cry", "Never gonna say goodbye", "Never gonna tell a lie and hurt you", - "why", - "why", - "why", - "why", + "Why", + "Why", + "Why", + "Why", "Why does it not show your messages in the chatbox?", "Why do you not show up in chat?", "Why do you not show up in chat @name?", @@ -241,7 +239,7 @@ "When did you start playing @name?", "When did you start on this server @name?", "When did you first get here @name?", - "russias greatest love machine", + "Russias greatest love machine", "Never gonna run around and desert you", "Two things are infinite, the universe & @names stupidity", "If you tell the truth, you dont have to remember anything.", @@ -259,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?", @@ -283,26 +281,26 @@ "@name?", "@name how long have you played", "@name world 1 or world 2?", - "what is your main world @name?", + "What is your main world @name?", "I prefer world 1 tbh", "I prefer world 2 tbh", "@name world 1 for life", "@name fog bots when?", - "damn somalian pirates", - "bracket more like brrrrrr acket", - "why the racket bracket", + "Damn somalian pirates", + "Bracket more like brrrrrr acket", + "Why the racket bracket", "Hi @name I am dad", "@name likes dad jokes", - "ur nuts @name", - "lootshare go brr", - "partay with froggay", + "Ur nuts @name", + "Lootshare go brr", + "Partay with froggay", "Know what else is lame? Not being able to play 2009scape right now", "Can you even grind on osrs", - "i botted to 88 fishing", + "I botted to 88 fishing", "Do not forget to vote in running polls!", "Always check announcments", - "we thrivin", - "ship @name", + "We thrivin", + "Ship @name", "Dont forget to vote 2009scape!", "Kermit is too legit 2 quit", "Out here on the range we are having fun", @@ -312,11 +310,11 @@ "I dont like him @name", "I dont like it @name", "Im an American.", - "ima go pickup my lady ting", - "that portuguese dude is something else", + "Ima go pickup my lady ting", + "That portuguese dude is something else", "@name!! @name!!", - "bowdi boy", - "i love bowdi... sometimes", + "Bowdi boy", + "I love bowdi... sometimes", "@name = @name", "I'm bacon.. go on dad... say it", "I'm going to leave.", @@ -340,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", @@ -362,40 +360,23 @@ "530 gang for life", "@name, have you read the rules?", "@name, have you checked out the forums?", - "why are the leprechauns moving?", + "Why are the leprechauns moving?", "I am beyond the point of no return!", "Nothin' personnel, kid.", "All your base are belong to us", "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!", @@ -412,7 +393,7 @@ "Fluff", "Bwuh", "I'm literally just mothin' bro", - "Certified 2009scape classic.", + "Certified 2009Scape Classic.", "Make sure to like, comment and subscribe @name it really helps", "Real 2009scape hours!!! Murder that like if you up!!!!", "W00t!", @@ -426,32 +407,32 @@ "Drip check", "Uhuhuhuhuhuhuhuhu", "Ahhhhhhhhhhhhhhhh", - "yall got any deez?", + "Yall got any deez?", "Deez nuts", "When she give you that yoinky sploinky", - "Sir, this is a 2009scape.", + "Sir, this is a 2009Scape.", "Oh dear! You are dead!", "Cor blimey, mate! What are ye doing in me pockets?", - "yeah i got funnies", - "read muv luv", + "Yeah i got funnies", + "Read muv luv", "Why doesn't she love me bros?", "@name is off the goop don't listen to the clown", - "do yall got laptops", - "cfunny play the alien warp sound effect", + "Do yall got laptops", + "Cfunny play the alien warp sound effect", "Ware to all whom doom hither, where cope is all yet constant.", "Who wants to come G W D?", "How do I make money?", "Ironmen stand alone together!", - "based", - "where is she", + "Based", + "Where is she", "*S N I I I I I I I I I F F F F F F F F F F F*", "Hey @name, who was in Paris?", "Huh whuh?", - "who's lone?", - "who *is* he?", + "Who's lone?", + "Who *is* he?", "Hi @name, I'm Dad!", - "anyone wanna play melty blood after I high alch these bows?", - "buru nyaaaa", + "Anyone wanna play melty blood after I high alch these bows?", + "Buru nyaaaa", "Number 15, Taverley Dungeon Black Dragons.", "House party at Rimmington! Host: @name!", "Spending 200m! Trade me, no junk!", @@ -461,19 +442,19 @@ "Nice outfit @name, does it come in children's size 12?", ":)", ":D", - "die screaming", + "Die screaming", "Only on Tuesdays", - "we stan 2009scape", + "We stan 2009scape", "There is no spoon", "Where's the cow level?", "Join 2009scape Clan Chat!", "Join 2009scape Cc!", "Chop chop!", - "Runescape rox my sox!!!!!!1", + "2009Scape rox my sox!!!!!!1", "Type ::scripts for all 99s", "Help I just died to Evil Chicken and I lost my stuff!", "Can you fight Bandos in this game?", - "one bad gloop", + "One bad gloop", "@name is not cute or funny", "You have committed crimes against Gielinor and her people. What say you?", "Barrows trauma", @@ -488,7 +469,7 @@ "Praise Andrew Gower", "Please help me I can't stop playing 2009scape", "Are you ignoring me, @name?!", - "i agree with this rfc can we put it in the game please", + "I agree with this rfc can we put it in the game please", "@name, we've been trying to reach you about your car's extended warranty", "Trimming armour, 25k gp", "Hey why does my furniture disappear?", @@ -498,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!!!", @@ -523,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.", @@ -596,7 +1081,7 @@ "Rusty = the grinch, @name = santa", "@name the red nose reindeer", "Put one foot in front of the other", - "and soon you will be walking out the doooor", + "And soon you will be walking out the doooor", "Elf on the shelf time", "@name loves pinecones", "I love the smell of pinecones", @@ -695,21 +1180,21 @@ "Woah got those roofies strapped", "Why is my drink cloudy?", "@name and woah be wrestlin", - "bekky want sum fuk?", + "Bekky want sum fuk?", "Damn @name you got an ass", "Is that a footlong in your pants or are you happy to see me?", "@name so hot i gotta change my pants", "@name is a sex god", - "bruh we hype today", - "dont be silly wrap the willy @name!", - "wrap it before you smack it @name", - "can i get a reeeeee @name?", - "valentines day is the best day", + "Bruh we hype today", + "Dont be silly wrap the willy @name!", + "Wrap it before you smack it @name", + "Can i get a reeeeee @name?", + "Valentines day is the best day", "@name shut up before i smack you with my crusty sock", "Valentines day, more like me and my hand day smh", "If you think these quotes are wild just wait @name", "All im saying is we have never seen @name and biden in the same room", - "red rocket, red rocket!", + "Red rocket, red rocket!", "Woahs favourite game is red rocket", "@name sexy af today", "Happy Valentines day @name!!!", @@ -754,4 +1239,4 @@ "@name and i are going to look for the easter bunny!!", "Hint for anyone who sees this you must dig above eagles peak" ] -} \ No newline at end of file +} diff --git a/Server/data/botdata/botnames.txt b/Server/data/botdata/botnames.txt index 73757dde8..22c5715a8 100644 --- a/Server/data/botdata/botnames.txt +++ b/Server/data/botdata/botnames.txt @@ -1,768 +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 -07Ash +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 -07Ato07 -07Chris -07Dans +07Ben +07Geno +07Jack +07Lax +07Panda 07Paul -07Skillerino -07TroyScape -07milo456 -07scaping +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 0BAMA +0Behind0You0 0CdanC0 +0G 0GCore 0Giesel42O +0IIll0llIIII +0JL 0Joker0 +0KAT 0LKI -0SRS +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 -0srs +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 -2007Anthony +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 @@ -771,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 @@ -819,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 @@ -916,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 @@ -1017,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 @@ -1072,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 @@ -1128,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 @@ -1283,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 @@ -1335,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 @@ -1396,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 @@ -1475,6 +3323,9 @@ ArckenSphere Arclight Arcnan Arctic +Arctic Roach +Arctic Vaggy +ArcticCamel ArcticMank ArcticTitan Arcticas @@ -1484,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 @@ -1493,12 +3352,17 @@ Areimer0606 Arekusei Arena ArenaRebuild +Arend Nest +AreolaArnold Areolas Areoloth +Areow Ares +Ares 3060 Ares1701 AresArmy AresKnight +Arescoth Aretx Arezrazer Arfex @@ -1507,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 -Ashwin07 +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 @@ -1794,7 +3910,14 @@ Avernic Avero Avertehs Avest09 +AvestGIM Avex +Avexaon +Avfc Holte +Avg Tom +AvgMG +AvgScape +Aviaatar Avian AvidTech Aviiation @@ -1803,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 @@ -1890,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 @@ -1992,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 @@ -2010,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 -BackintheOSR +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 @@ -2112,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 @@ -2162,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 @@ -2223,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 @@ -2267,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 @@ -2313,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 @@ -2348,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 -Bierscape07 +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 @@ -2748,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 @@ -2822,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 -Bobbydrake07 +Bobbydrake09 +Bobbysweggg BobbyyHill Bobi Bobicles2 Bobidabob +Bobite92 +Bobo Bagins Bobrobber Bobrusu Bobs @@ -2884,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 @@ -2930,76 +6286,171 @@ Bomer Bommerche Bommijn Bompa +Bompanero +Bompton Bomtastic +Bon Pa Tin +Bon fee BonQong Bonar +Bonar Slayer Bond -Bond07 +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 @@ -3008,8 +6459,13 @@ Borgiie Boriix BoringName Boris +Boris King7 +Boris sfisgh +Borkiin BorkingCorp Born +Born Abroad +Born to Hodl BornToTrade BornTwoGrind BornofDragon @@ -3019,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 @@ -3223,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 @@ -3263,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 @@ -3353,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 @@ -3446,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 @@ -3569,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 @@ -3713,6 +7891,7 @@ CaptainDredd CaptainLuker CaptainOboy CaptainObvio +CaptainWalt Captainshiny Captainzzz Captan @@ -3720,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 @@ -3765,6 +7962,7 @@ Carlyle Carmillae Carna Carnadova +CarneGrande Carnie CarnifexVeil Carnitas @@ -3773,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 @@ -3867,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 @@ -3998,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 @@ -4207,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 @@ -4219,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 @@ -4288,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 @@ -4531,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 +Corp Scape +Corp09RS +CorpLeecher +CorpRng +CorpSlapper CorpToMax +Corping Bro Corpletics -CorposRS +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 @@ -4878,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 @@ -4913,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 @@ -5141,6 +10670,7 @@ Dalski Daltficiency Dalton Dalton4theft +Dalton_LB Daltonnn Dalundo Dalzii @@ -5148,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 @@ -5216,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 @@ -5249,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 -Dasgirl07 +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 @@ -5389,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 @@ -5429,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 -Delphan07 +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 @@ -5564,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 @@ -5579,9 +11518,12 @@ Dennisfr Dennispanda1 Denny Dennyispro +Denonweff DenouncedGod Denoxite +DenseLayer Denssoni +Densu DentalMental Dented Dentistry @@ -5589,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 @@ -5641,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 @@ -5862,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 @@ -6082,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 @@ -6123,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 @@ -6223,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 @@ -6302,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 @@ -6534,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 @@ -6578,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 @@ -6601,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 @@ -6670,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 @@ -6730,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 @@ -6787,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 @@ -6816,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 -Enginerd07 +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 -Erikito07 +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 @@ -6952,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 @@ -6987,10 +14389,16 @@ Espuky Espyria Esquelito Essencehour +Essex RS Essylle +Est Bellum Estafeta1 +Estar +Estebaan +Esteedee Esteeme Esteet +Esteffano 3d EsterKai Esterbrook Estetica @@ -6998,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 @@ -7020,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 @@ -7179,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 @@ -7302,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 @@ -7455,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 @@ -7506,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 @@ -7626,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 @@ -7743,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 @@ -7832,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 @@ -7911,374 +16279,886 @@ 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 -Froz07 +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 @@ -8286,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 @@ -8323,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 @@ -8467,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 @@ -8515,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 @@ -8821,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 @@ -8890,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 @@ -9070,12 +18767,19 @@ Gummibier Gummy GummyTushie Gumpie123 +Gumri +Gun +Gunalations Gunci +GundarV Gundrace Gundrak Gune Gunfire +Gunite Gunjamini +Gunman +Gunn a Gunna Gunnar Gunnawr @@ -9084,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 @@ -9243,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 @@ -9267,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 @@ -9296,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 @@ -9372,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 @@ -9386,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 @@ -9513,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 @@ -9668,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 @@ -9711,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 @@ -9856,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 @@ -9895,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 @@ -9924,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 @@ -10105,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 @@ -10176,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 @@ -10201,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 @@ -10334,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 @@ -10422,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 @@ -10441,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 @@ -10503,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 @@ -10524,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 @@ -10565,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 @@ -10720,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 @@ -10739,11 +22562,19 @@ IronOdum IronOldBoy IronOnPatch IronOrca +IronOreAgate IronOsiriss IronOxideMan +IronOxidizer +IronPangtar +IronParis +IronPeteMan IronPills IronPrice +IronPrickles +IronProf IronPumitaz +IronQben IronQli IronQueen IronRait @@ -10756,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 @@ -10840,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 @@ -10892,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 @@ -11064,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 @@ -11132,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 -JazzMaster07 +JazzMaster09 +Jazzeh Jazzjr89 +Jazzly Jazzy JazzyFlips +Jb Godranger +Jba123 +Jbevzzz Jblieves88 +Jbob72 +Jbt Jbvff Jc_TheCops +Jcb4646 Jcdelavici Jchn Jcmalone23 @@ -11215,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 @@ -11439,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 @@ -11525,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 @@ -11626,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 -JuggaloOSR +Juggalo09R Juggerthot +Jugimaster JuhQ Juheniqus Juhhu @@ -11674,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 @@ -11697,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 @@ -11741,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 @@ -11757,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 @@ -11824,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 @@ -11899,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 @@ -11984,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 @@ -12059,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 @@ -12096,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 @@ -12135,11 +25484,14 @@ Kelgone Kelh Kellarivelho Keller +Kells o_o Kelly Kellycollin2 Kelohonka Keloo +Kelps Kelso523 +Kelso561 Keltik KeltonThaGod Keltuzazz @@ -12148,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 @@ -12256,10 +25684,15 @@ KhatrickZain Khayri Demon Khazad Khazad Dum +Khazanedar Khazu +Khealim Khest +Khint +Khirean Khiru Khleb +KhooNBust Khoosh Khoppa Khornebull @@ -12267,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 @@ -12284,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 -KingGoblin07 +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 @@ -12526,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 @@ -12604,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 @@ -12667,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 @@ -12780,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 @@ -12800,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 @@ -12942,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 @@ -13027,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 @@ -13098,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 @@ -13196,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 @@ -13214,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 @@ -13231,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 @@ -13345,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 @@ -13593,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 @@ -13657,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 @@ -13921,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 @@ -14064,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 @@ -14113,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 @@ -14215,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 @@ -14236,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 @@ -14334,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 @@ -14375,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 @@ -14392,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 @@ -14424,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 @@ -14470,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 @@ -14502,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 @@ -14583,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 @@ -14789,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 @@ -14840,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 @@ -14945,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 @@ -14983,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 @@ -15081,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 @@ -15103,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 @@ -15162,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 @@ -15241,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 @@ -15302,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 @@ -15379,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 @@ -15416,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 @@ -15435,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 @@ -15515,7 +32554,9 @@ Mugarooni Mugen Mugge Muggerman3 +Muggled Muggles +Muggtjuven Mugrub Mugy Mugzy @@ -15523,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 @@ -15641,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 @@ -15730,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 @@ -15928,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 @@ -16083,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 @@ -16110,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 @@ -16285,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 @@ -16318,426 +34172,1036 @@ 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 +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 -OSRhys -OSRvape 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 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 @@ -16750,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 -Opticplex07 +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 @@ -16955,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 @@ -17112,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 @@ -17173,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 @@ -17206,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 @@ -17273,6 +36258,7 @@ PathTracker Pathogen Pathways Patio +Patious Patmantheman PatoVelaz9 Patojo @@ -17281,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 @@ -17301,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 @@ -17315,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 @@ -17362,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 @@ -17395,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 @@ -17501,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 @@ -17558,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 @@ -17652,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 @@ -17752,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 @@ -17963,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 @@ -17979,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 @@ -18022,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 @@ -18041,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 @@ -18094,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 @@ -18314,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 @@ -18353,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 @@ -18485,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 @@ -18719,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 @@ -18840,12 +39396,16 @@ Reduced Reductive Redus Redux +Redux Lux Redz Redza14 +Ree Mastered Reeb Reececup Reecesoa +Reecie ReedBadass +Reee Reeebow ReeeeZ Reeeps @@ -18856,7 +39416,10 @@ ReefyReefer Reel_Arphios Reels Reemov3d +Reer Reeyu +Reeze Tech +Reflection44 Refleksfrode Refleksi Refluxerino @@ -18865,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 @@ -19040,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 @@ -19072,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 @@ -19240,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 @@ -19318,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 @@ -19398,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 @@ -19461,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 @@ -19570,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 @@ -19647,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 @@ -19725,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 @@ -19788,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 @@ -20043,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 @@ -20107,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 @@ -20187,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 @@ -20210,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 @@ -20436,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 @@ -20673,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 @@ -20765,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 @@ -20815,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 @@ -20885,6 +43413,7 @@ Skold Skolder Skolzera SkoobiDooby +Skooier Bibs SkoomaPls Skopix Skopusnik @@ -20894,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 @@ -20917,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 @@ -20965,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 @@ -21002,6 +43613,8 @@ SlayerKingv2 SlayerRyan Slayerburger Slayerdog +Slayerg1g +SlayerisGame Slayerknox Slayerman Slayers @@ -21011,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 @@ -21034,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 @@ -21216,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 @@ -21281,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 @@ -21354,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 @@ -21438,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 @@ -21458,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 @@ -21590,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 @@ -21633,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 @@ -21651,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 @@ -21843,61 +45220,111 @@ 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 SteveGuy241 SteveMCOG SteveMerch -SteveOwner07 +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 @@ -21907,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 @@ -21924,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 @@ -22046,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 @@ -22097,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 @@ -22154,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 @@ -22163,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 @@ -22200,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 @@ -22553,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 @@ -22612,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 @@ -22736,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 @@ -22918,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 @@ -22970,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 @@ -23018,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 @@ -23337,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 -Tombea07 +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 @@ -23465,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 @@ -23500,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 @@ -23537,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 @@ -23550,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 @@ -23638,11 +48821,15 @@ Trickster122 Tricomb Trictagon1 Trictagon2 +Tricx +Trid3nt Trieuce Triforce13 Trigamy +TriggaTriggs Triggadd Triggahappy +Trigger Me TriggeredMot Triggeredm3 Triggering @@ -23652,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 @@ -23675,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 @@ -23760,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 @@ -23788,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 @@ -23809,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 @@ -23907,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 @@ -24129,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 -ValgeHunt07 +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 @@ -24290,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 @@ -24302,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 @@ -24384,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 @@ -24590,262 +50758,535 @@ 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 -Warrior07rs +Warrior09rs WarriorHome Warstroy Wartika @@ -24855,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 @@ -24900,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 @@ -25017,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 @@ -25281,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 @@ -25315,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 @@ -25339,11 +52213,17 @@ Wrath0fMath Wrathchildxd Wrathinsea Wrav +WreXham AFC +Wreck These +WreckAndRoll Wreckage Wreckanism +Wrecked Wreckfull Wreckin4Days Wreckon +Wreckonize +Wrecky Wreckzu Wrekdum Wrektem @@ -25351,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 @@ -25493,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 @@ -25508,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 @@ -25532,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 @@ -25854,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 @@ -25899,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 @@ -26125,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 @@ -26200,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 @@ -26256,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 @@ -26322,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 @@ -26364,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 @@ -26447,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 @@ -26791,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 @@ -26841,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 @@ -26990,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 @@ -27050,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 @@ -27094,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 @@ -27150,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 @@ -27220,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 @@ -27392,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 @@ -27455,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 @@ -27517,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 @@ -27585,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 -fwft07 +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 @@ -27733,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 @@ -28046,6 +57736,7 @@ iPink iPlugg iPod iPop +iPray_Guthix iPresident iPunchCones iPure @@ -28058,9 +57749,14 @@ iQuittedCiao iRNGizzed iRekt iRepToronto +iReue +iRev Man iRezlo +iRoN cUj0 iRoNmAnSdUmB +iRonAllot iRunTh3World +iRunzo iRycoda iSaran iScape @@ -28070,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 @@ -28189,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 @@ -28314,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 @@ -28365,7 +58434,10 @@ jojojo357 jokaranpampu jokemyster jokerpoker +jokkefar jokr +jolle rmm +jon ko jonehehe jones jonk33n @@ -28373,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 @@ -28430,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 @@ -28504,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 @@ -28606,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 @@ -28765,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 @@ -28962,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 @@ -28980,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 @@ -29038,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 -mlkia07 +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 @@ -29274,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 @@ -29297,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 @@ -29319,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 @@ -29374,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 @@ -29479,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 @@ -29498,6 +60714,8 @@ pashol pasismi paskapylly6 pastry +pat summitt +patbeerpants patches4623 patnazNkryme patriciogmz @@ -29505,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 @@ -29515,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 @@ -29667,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 @@ -29733,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 @@ -29762,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 @@ -29796,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 @@ -30095,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 @@ -30146,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 @@ -30290,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 @@ -30354,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 @@ -30488,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 -tomriddle07 +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 now07 +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 @@ -30677,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 @@ -30994,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 @@ -31179,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/account_limit_exceptions.conf b/Server/data/configs/account_limit_exceptions.conf new file mode 100644 index 000000000..c412ca256 --- /dev/null +++ b/Server/data/configs/account_limit_exceptions.conf @@ -0,0 +1,2 @@ +[exceptions] +"127.0.0.1" = 5 \ No newline at end of file diff --git a/Server/data/configs/clue_rewards.json b/Server/data/configs/clue_rewards.json index bbe03ffe6..5554b133b 100644 --- a/Server/data/configs/clue_rewards.json +++ b/Server/data/configs/clue_rewards.json @@ -389,31 +389,31 @@ { "minAmount": 1, "weight": "5", - "id": 10666, + "id": 7334, "maxAmount": 1 }, { "minAmount": 1, "weight": "5", - "id": 10669, + "id": 7340, "maxAmount": 1 }, { "minAmount": 1, "weight": "5", - "id": 10672, + "id": 7346, "maxAmount": 1 }, { "minAmount": 1, "weight": "5", - "id": 10675, + "id": 7352, "maxAmount": 1 }, { "minAmount": 1, "weight": "5", - "id": 10678, + "id": 7358, "maxAmount": 1 }, { @@ -949,7 +949,7 @@ { "minAmount": 1, "weight": "5", - "id": 10667, + "id": 7336, "maxAmount": 1 }, { @@ -961,7 +961,7 @@ { "minAmount": 1, "weight": "5", - "id": 10670, + "id": 7342, "maxAmount": 1 }, { @@ -973,7 +973,7 @@ { "minAmount": 1, "weight": "5", - "id": 10673, + "id": 7348, "maxAmount": 1 }, { @@ -985,7 +985,7 @@ { "minAmount": 1, "weight": "5", - "id": 10676, + "id": 7354, "maxAmount": 1 }, { @@ -997,7 +997,7 @@ { "minAmount": 1, "weight": "5", - "id": 10679, + "id": 7360, "maxAmount": 1 }, { diff --git a/Server/data/configs/door_configs.json b/Server/data/configs/door_configs.json index 21568f895..ec17288a2 100644 --- a/Server/data/configs/door_configs.json +++ b/Server/data/configs/door_configs.json @@ -73,7 +73,7 @@ }, { "id": "92", - "replaceId": "17317", + "replaceId": "91", "fence": "false", "metal": "false" }, @@ -193,7 +193,7 @@ }, { "id": "1553", - "replaceId": "1552", + "replaceId": "1556", "fence": "true", "metal": "false" }, @@ -301,7 +301,7 @@ }, { "id": "2025", - "replaceId": "1534", + "replaceId": "2026", "fence": "false", "metal": "false" }, @@ -312,14 +312,14 @@ "metal": "false" }, { - "id": "2036", + "id": "1530", "replaceId": "1531", "fence": "false", "metal": "false" }, { "id": "2039", - "replaceId": "1561", + "replaceId": "1560", "fence": "false", "metal": "true" }, @@ -332,13 +332,13 @@ { "id": "2050", "replaceId": "15511", - "fence": "false", + "fence": "true", "metal": "false" }, { "id": "2051", "replaceId": "15513", - "fence": "false", + "fence": "true", "metal": "false" }, { @@ -896,12 +896,12 @@ { "id": "3725", "replaceId": "3727", - "fence": "false", + "fence": "true", "metal": "false" }, { "id": "3726", - "replaceId": "3727", + "replaceId": "3728", "fence": "true", "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" }, @@ -979,19 +979,19 @@ }, { "id": "4423", - "replaceId": "4429", + "replaceId": "4425", "fence": "false", "metal": "false" }, { "id": "4424", - "replaceId": "11382", + "replaceId": "4426", "fence": "false", "metal": "false" }, { "id": "4427", - "replaceId": "11380", + "replaceId": "4429", "fence": "false", "metal": "false" }, @@ -1238,12 +1238,12 @@ { "id": "7049", "replaceId": "7051", - "fence": "false", + "fence": "true", "metal": "false" }, { "id": "7050", - "replaceId": "23919", + "replaceId": "7052", "fence": "true", "metal": "false" }, @@ -1340,7 +1340,7 @@ { "id": "8810", "replaceId": "8812", - "fence": "false", + "fence": "true", "metal": "false" }, { @@ -1759,7 +1759,7 @@ }, { "id": "14245", - "replaceId": "14248", + "replaceId": "14246", "fence": "true", "metal": "false" }, @@ -1983,7 +1983,15 @@ "id": "20391", "replaceId": "28518", "fence": "false", - "metal": "true" + "metal": "true", + "autowalk": "true" + }, + { + "id": "28514", + "replaceId": "28518", + "fence": "false", + "metal": "true", + "autowalk": "true" }, { "id": "21065", @@ -2144,13 +2152,13 @@ { "id": "24369", "replaceId": "24373", - "fence": "true", + "fence": "false", "metal": "false" }, { "id": "24370", "replaceId": "24374", - "fence": "true", + "fence": "false", "metal": "false" }, { @@ -2177,18 +2185,6 @@ "fence": "false", "metal": "false" }, - { - "id": "24560", - "replaceId": "15515", - "fence": "false", - "metal": "false" - }, - { - "id": "24561", - "replaceId": "15517", - "fence": "false", - "metal": "false" - }, { "id": "24565", "replaceId": "36316", @@ -2357,11 +2353,25 @@ "fence": "false", "metal": "true" }, + { + "id": "26081", + "replaceId": "26083", + "fence": "false", + "metal": "true", + "autowalk": "true" + }, + { + "id": "26082", + "replaceId": "26084", + "fence": "false", + "metal": "true", + "autowalk": "true" + }, { "id": "26130", "replaceId": "26132", "fence": "false", - "metal": "false" + "metal": "true" }, { "id": "26131", @@ -2507,12 +2517,6 @@ "fence": "false", "metal": "false" }, - { - "id": "28514", - "replaceId": "28518", - "fence": "true", - "metal": "true" - }, { "id": "28589", "replaceId": "28588", @@ -2671,7 +2675,7 @@ }, { "id": "31815", - "replaceId": "31816", + "replaceId": "31817", "fence": "false", "metal": "false" }, @@ -2798,7 +2802,7 @@ { "id": "34780", "replaceId": "34778", - "fence": "false", + "fence": "true", "metal": "false" }, { diff --git a/Server/data/configs/drop_tables.json b/Server/data/configs/drop_tables.json index d10827f19..b726152c4 100644 --- a/Server/data/configs/drop_tables.json +++ b/Server/data/configs/drop_tables.json @@ -16,7 +16,7 @@ } ], "charm": [], - "ids": "1,2,3,16,24,170,351,359,663,726,727,728,729,730,1086,2675,3223,3224,3225,3915,5923", + "ids": "1,2,3,16,24,170,351,359,663,726,727,728,729,730,1081,1084,1085,1086,1087,1088,1089,2675,2683,3223,3224,3225,3915,5923", "description": "", "main": [ { @@ -145,7 +145,7 @@ } ], "charm": [], - "ids": "4,5,6,25,352,353,354,360,361,362,363,2776,3226,3227,5924", + "ids": "4,5,6,25,352,353,354,360,361,362,363,2776,2684,3226,3227,5924", "description": "", "main": [ { @@ -1757,52 +1757,9 @@ } ], "charm": [], - "ids": "21", + "ids": "21,1072", "description": "", - "main": [ - { - "minAmount": "200", - "weight": "105.0", - "id": "995", - "maxAmount": "300" - }, - { - "minAmount": "2", - "weight": "8.0", - "id": "560", - "maxAmount": "2" - }, - { - "minAmount": "1", - "weight": "6.0", - "id": "565", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "1993", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "2.0", - "id": "569", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "1.0", - "id": "1601", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "1.0", - "id": "444", - "maxAmount": "1" - } - ] + "main": [] }, { "default": [ @@ -1996,6 +1953,20 @@ "maxAmount": "4" } ], + "tertiary": [ + { + "minAmount": "1", + "weight": "1.0", + "id": "7980", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "127.0", + "id": "0", + "maxAmount": "1" + } + ], "ids": "50,2642", "description": "", "main": [ @@ -2481,7 +2452,7 @@ "minAmount": "75", "weight": "1.0", "id": "556", - "maxAmount": "5" + "maxAmount": "75" }, { "minAmount": "10", @@ -2787,7 +2758,7 @@ "maxAmount": "1" } ], - "ids": "59,60,4400", + "ids": "59,60", "description": "", "main": [ { @@ -2940,7 +2911,7 @@ "maxAmount": "1" } ], - "ids": "61,1004,1221,1473,1474,4401,7207", + "ids": "61,1004,1221,1473,1474,4400,4401,7207", "description": "", "main": [ { @@ -3291,7 +3262,7 @@ } ], "charm": [], - "ids": "73,74,75,76,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": [ { @@ -3533,20 +3504,7 @@ ], "ids": "78,1005,2482,3711", "description": "", - "main": [ - { - "minAmount": "1", - "weight": "50.0", - "id": "7829", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "100.0", - "id": "0", - "maxAmount": "1" - } - ] + "main": [] }, { "default": [ @@ -4193,14 +4151,7 @@ "charm": [], "ids": "89,987", "description": "", - "main": [ - { - "minAmount": "1", - "weight": "50.0", - "id": "7821", - "maxAmount": "1" - } - ] + "main": [] }, { "default": [ @@ -4423,12 +4374,6 @@ "ids": "94,3844,3850,3851", "description": "", "main": [ - { - "minAmount": "1", - "weight": "50.0", - "id": "1391", - "maxAmount": "1" - }, { "minAmount": "1", "weight": "50.0", @@ -5010,20 +4955,7 @@ ], "ids": "105,1195", "description": "", - "main": [ - { - "minAmount": "1", - "weight": "25.0", - "id": "7815", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "100.0", - "id": "0", - "maxAmount": "1" - } - ] + "main": [] }, { "default": [ @@ -5080,20 +5012,7 @@ ], "ids": "106", "description": "", - "main": [ - { - "minAmount": "1", - "weight": "25.0", - "id": "7815", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "100.0", - "id": "0", - "maxAmount": "1" - } - ] + "main": [] }, { "default": [ @@ -5136,6 +5055,26 @@ "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": "110,1582,1583,1584,1585,1586,7003,7004", "description": "", "main": [ @@ -5145,12 +5084,6 @@ "id": "1353", "maxAmount": "1" }, - { - "minAmount": "1", - "weight": "2.0", - "id": "1179", - "maxAmount": "1" - }, { "minAmount": "1", "weight": "2.0", @@ -5160,7 +5093,7 @@ { "minAmount": "1", "weight": "1.0", - "id": "1145", + "id": "1393", "maxAmount": "1" }, { @@ -5169,12 +5102,6 @@ "id": "1333", "maxAmount": "1" }, - { - "minAmount": "1", - "weight": "1.0", - "id": "1393", - "maxAmount": "1" - }, { "minAmount": "150", "weight": "10.0", @@ -5199,24 +5126,30 @@ "id": "565", "maxAmount": "5" }, - { - "minAmount": "2", - "weight": "1.0", - "id": "563", - "maxAmount": "2" - }, { "minAmount": "37", "weight": "1.0", "id": "554", "maxAmount": "37" }, + { + "minAmount": "2", + "weight": "1.0", + "id": "563", + "maxAmount": "2" + }, { "minAmount": "1", "weight": "19.0", "id": "14424", "maxAmount": "1" }, + { + "minAmount": "60", + "weight": "40.0", + "id": "995", + "maxAmount": "60" + }, { "minAmount": "15", "weight": "7.0", @@ -5229,24 +5162,18 @@ "id": "995", "maxAmount": "25" }, - { - "minAmount": "50", - "weight": "1.0", - "id": "995", - "maxAmount": "50" - }, - { - "minAmount": "60", - "weight": "37.0", - "id": "995", - "maxAmount": "60" - }, { "minAmount": "300", "weight": "2.0", "id": "995", "maxAmount": "300" }, + { + "minAmount": "50", + "weight": "1.0", + "id": "995", + "maxAmount": "50" + }, { "minAmount": "1", "weight": "3.0", @@ -5276,18 +5203,6 @@ "weight": "1.0", "id": "31", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "0.32", - "id": "10976", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "0.0256", - "id": "10977", - "maxAmount": "1" } ] }, @@ -5332,19 +5247,39 @@ "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": "111,3072,4685,4686,4687", "description": "", "main": [ { "minAmount": "1", "weight": "5.0", - "id": "1363", + "id": "1309", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "1309", + "weight": "4.0", + "id": "1195", "maxAmount": "1" }, { @@ -5359,22 +5294,10 @@ "id": "1281", "maxAmount": "1" }, - { - "minAmount": "1", - "weight": "4.0", - "id": "1195", - "maxAmount": "1" - }, - { - "minAmount": "5", - "weight": "2.0", - "id": "1067", - "maxAmount": "5" - }, { "minAmount": "1", "weight": "1.0", - "id": "1181", + "id": "1067", "maxAmount": "1" }, { @@ -5386,7 +5309,7 @@ { "minAmount": "1", "weight": "1.0", - "id": "1315", + "id": "1181", "maxAmount": "1" }, { @@ -5449,6 +5372,24 @@ "id": "14422", "maxAmount": "1" }, + { + "minAmount": "117", + "weight": "32.0", + "id": "995", + "maxAmount": "117" + }, + { + "minAmount": "53", + "weight": "12.0", + "id": "995", + "maxAmount": "53" + }, + { + "minAmount": "196", + "weight": "10.0", + "id": "995", + "maxAmount": "196" + }, { "minAmount": "8", "weight": "7.0", @@ -5461,21 +5402,9 @@ "id": "995", "maxAmount": "22" }, - { - "minAmount": "117", - "weight": "36.0", - "id": "995", - "maxAmount": "117" - }, - { - "minAmount": "196", - "weight": "10.0", - "id": "995", - "maxAmount": "196" - }, { "minAmount": "400", - "weight": "2.0", + "weight": "1.0", "id": "995", "maxAmount": "400" }, @@ -5508,18 +5437,6 @@ "weight": "1.0", "id": "5733", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "0.32", - "id": "10976", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "0.0256", - "id": "10977", - "maxAmount": "1" } ] }, @@ -5564,6 +5481,26 @@ "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": "112,1587,1588,1681,4534,4688,4706", "description": "", "main": [ @@ -5675,18 +5612,18 @@ "id": "14422", "maxAmount": "1" }, - { - "minAmount": "2", - "weight": "8.0", - "id": "995", - "maxAmount": "2" - }, { "minAmount": "37", "weight": "19.0", "id": "995", "maxAmount": "37" }, + { + "minAmount": "2", + "weight": "8.0", + "id": "995", + "maxAmount": "2" + }, { "minAmount": "119", "weight": "10.0", @@ -5717,18 +5654,6 @@ "id": "1969", "maxAmount": "1" }, - { - "minAmount": "1", - "weight": "0.32", - "id": "10976", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "0.0256", - "id": "10977", - "maxAmount": "1" - }, { "minAmount": "1", "weight": "4.0", @@ -5933,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" } ] }, @@ -6176,162 +6061,38 @@ "maxAmount": "1" } ], - "ids": "115,374,2801,3419", - "description": "", - "main": [ + "tertiary": [ { "minAmount": "1", - "weight": "50.0", - "id": "7844", + "weight": "9973.0", + "id": "0", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "10977", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", + "weight": "25.0", "id": "10976", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "5323", + "weight": "2.0", + "id": "10977", + "maxAmount": "1" + } + ], + "ids": "115,374,852,2801,3419,5174", + "description": "", + "main": [ + { + "minAmount": "1", + "weight": "19.0", + "id": "14422", "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", + "weight": "109.0", "id": "0", "maxAmount": "1" } @@ -6865,298 +6626,184 @@ "weight": "1.0", "id": "12163", "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "489.0", + "id": "0", + "maxAmount": "1" } ], "ids": "117,4689,4690,4691,4692,4693", "description": "", "main": [ { - "minAmount": "3", - "weight": "50.0", - "id": "884", - "maxAmount": "3" + "minAmount": "1", + "weight": "5.0", + "id": "1153", + "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", + "weight": "4.0", "id": "1203", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", - "id": "1153", - "maxAmount": "1" - }, - { - "minAmount": "10", - "weight": "12.5", - "id": "886", - "maxAmount": "10" - }, - { - "minAmount": "1", - "weight": "25.0", + "weight": "3.0", "id": "1191", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "2.0", "id": "1295", "maxAmount": "1" }, - { - "minAmount": "1", - "weight": "8.0", - "id": "1309", - "maxAmount": "1" - }, { "minAmount": "3", - "weight": "8.0", - "id": "888", + "weight": "6.0", + "id": "884", "maxAmount": "3" }, - { - "minAmount": "1", - "weight": "8.0", - "id": "1207", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "8.0", - "id": "1119", - "maxAmount": "1" - }, - { - "minAmount": "7", - "weight": "25.0", - "id": "555", - "maxAmount": "7" - }, { "minAmount": "15", - "weight": "8.0", + "weight": "3.0", "id": "554", "maxAmount": "15" }, + { + "minAmount": "7", + "weight": "3.0", + "id": "555", + "maxAmount": "7" + }, { "minAmount": "2", - "weight": "8.0", - "id": "564", + "weight": "3.0", + "id": "563", "maxAmount": "2" }, { - "minAmount": "2", - "weight": "8.0", - "id": "563", - "maxAmount": "3" - }, - { - "minAmount": "6", - "weight": "8.0", - "id": "561", - "maxAmount": "6" + "minAmount": "10", + "weight": "2.0", + "id": "886", + "maxAmount": "10" }, { "minAmount": "3", - "weight": "8.0", + "weight": "2.0", "id": "558", "maxAmount": "3" }, { "minAmount": "2", - "weight": "8.0", - "id": "560", + "weight": "2.0", + "id": "564", "maxAmount": "2" }, { "minAmount": "2", - "weight": "8.0", + "weight": "1.0", "id": "562", "maxAmount": "2" }, - { - "minAmount": "1", - "weight": "50.0", - "id": "199", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "201", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "203", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "8.0", - "id": "207", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "205", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "8.0", - "id": "209", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "8.0", - "id": "211", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "8.0", - "id": "213", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "8.0", - "id": "217", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "8.0", - "id": "215", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "8.0", - "id": "2485", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5322", - "maxAmount": "3" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5318", - "maxAmount": "4" - }, - { - "minAmount": "4", - "weight": "25.0", - "id": "5319", - "maxAmount": "4" - }, - { - "minAmount": "8", - "weight": "25.0", - "id": "5319", - "maxAmount": "8" - }, { "minAmount": "2", - "weight": "25.0", - "id": "5323", + "weight": "1.0", + "id": "560", "maxAmount": "2" }, - { - "minAmount": "4", - "weight": "25.0", - "id": "5324", - "maxAmount": "4" - }, { "minAmount": "1", - "weight": "8.0", - "id": "5100", + "weight": "7.0", + "id": "14424", "maxAmount": "1" }, { - "minAmount": "3", - "weight": "8.0", - "id": "5320", - "maxAmount": "3" + "minAmount": "1", + "weight": "18.0", + "id": "14430", + "maxAmount": "1" }, { - "minAmount": "1", + "minAmount": "38", + "weight": "14.0", + "id": "995", + "maxAmount": "38" + }, + { + "minAmount": "52", + "weight": "10.0", + "id": "995", + "maxAmount": "52" + }, + { + "minAmount": "15", "weight": "8.0", - "id": "5321", - "maxAmount": "4" + "id": "995", + "maxAmount": "15" }, { "minAmount": "8", - "weight": "50.0", + "weight": "6.0", "id": "995", - "maxAmount": "80" + "maxAmount": "8" }, { - "minAmount": "80", - "weight": "8.0", + "minAmount": "88", + "weight": "2.0", "id": "995", - "maxAmount": "300" + "maxAmount": "88" }, { "minAmount": "1", - "weight": "50.0", + "weight": "11.0", "id": "225", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", + "weight": "6.0", "id": "1917", "maxAmount": "1" }, { "minAmount": "1", - "weight": "8.0", + "weight": "2.0", "id": "1446", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", + "weight": "1.6544", + "id": "0", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "3.0", + "id": "14426", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "0.32", "id": "10976", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", + "weight": "0.0256", "id": "10977", "maxAmount": "1" }, { - "minAmount": "1", - "weight": "5.0", - "id": "31", - "maxAmount": "1" - }, - { - "minAmount": "15", - "weight": "12.5", - "id": "886", - "maxAmount": "15" - }, - { - "minAmount": "1", - "weight": "1400.0", - "id": "0", - "maxAmount": "1" + "minAmount": "6", + "weight": "2.0", + "id": "561", + "maxAmount": "6" } ] }, @@ -8136,12 +7783,6 @@ "id": "1181", "maxAmount": "1" }, - { - "minAmount": "1", - "weight": "1.0", - "id": "9185", - "maxAmount": "1" - }, { "minAmount": "4", "weight": "10.0", @@ -8622,7 +8263,7 @@ { "minAmount": "1", "weight": "100.0", - "id": "3186", + "id": "526", "maxAmount": "1" }, { @@ -8666,20 +8307,7 @@ ], "ids": "132,1463,1464,1487,2301,4344,4363,5852,6943,7211,7213,7215,7217,7219,7221,7223,7225,7227", "description": "", - "main": [ - { - "minAmount": "1", - "weight": "50.0", - "id": "7856", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "100.0", - "id": "0", - "maxAmount": "1" - } - ] + "main": [] }, { "default": [ @@ -8730,20 +8358,7 @@ ], "ids": "133", "description": "", - "main": [ - { - "minAmount": "1", - "weight": "25.0", - "id": "7821", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "100.0", - "id": "0", - "maxAmount": "1" - } - ] + "main": [] }, { "default": [ @@ -10421,13 +10036,13 @@ { "minAmount": "15", "weight": "4.0", - "id": "6964", + "id": "995", "maxAmount": "15" }, { "minAmount": "25", "weight": "1.0", - "id": "6964", + "id": "995", "maxAmount": "25" }, { @@ -11771,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": [] }, { @@ -11851,16 +11430,10 @@ "ids": "412", "description": "", "main": [ - { - "minAmount": "1", - "weight": "50.0", - "id": "7833", - "maxAmount": "1" - }, { "minAmount": "1", "weight": "100.0", - "id": "0", + "id": "530", "maxAmount": "1" } ] @@ -11868,7 +11441,7 @@ { "default": [], "charm": [], - "ids": "438,439,440,441,442,443,655,4470", + "ids": "438,439,440,441,442,443,4470", "description": "Tree Spirit (Hostile Woodcutting Random Event/Enchanted Valley)", "main": [ { @@ -12022,7 +11595,7 @@ "maxAmount": "1" } ], - "ids": "490,1958,1959,1961,1962,1963,1964,1965,1966,1967,1968,2015,2016,2017,2018,2019,6753,6754,6755,6756,6757,6758,6759,6760", + "ids": "490,1959,1962,1963,1964,1965,1966,1967,1968,2015,2016,2017,2018,2019,6753,6754,6755,6756,6757,6758,6759,6760", "description": "", "main": [ { @@ -12223,12 +11796,6 @@ "id": "12070", "maxAmount": "1" }, - { - "minAmount": "1", - "weight": "5.0", - "id": "6808", - "maxAmount": "1" - }, { "minAmount": "1", "weight": "5.0", @@ -12353,7 +11920,7 @@ { "minAmount": "1", "weight": "100.0", - "id": "526", + "id": "592", "maxAmount": "1" }, { @@ -13613,75 +13180,6 @@ "description": "", "main": [] }, - { - "default": [ - { - "minAmount": "1", - "weight": "100.0", - "id": "532", - "maxAmount": "1" - } - ], - "charm": [], - "ids": "852,5174", - "description": "", - "main": [ - { - "minAmount": "1", - "weight": "5.0", - "id": "5292", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5294", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5311", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5106", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5100", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5321", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5302", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5304", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5296", - "maxAmount": "1" - } - ] - }, { "default": [], "charm": [ @@ -14825,104 +14323,80 @@ "main": [ { "minAmount": "15", - "weight": "50.0", + "weight": "13.0", "id": "556", - "maxAmount": "20" + "maxAmount": "15" }, { "minAmount": "2", - "weight": "50.0", - "id": "562", - "maxAmount": "2" - }, - { - "minAmount": "2", - "weight": "50.0", + "weight": "9.0", "id": "561", "maxAmount": "2" }, + { + "minAmount": "2", + "weight": "7.0", + "id": "562", + "maxAmount": "2" + }, { "minAmount": "1", - "weight": "50.0", + "weight": "6.0", "id": "563", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "4.0", "id": "560", "maxAmount": "1" }, + { + "minAmount": "20", + "weight": "3.0", + "id": "556", + "maxAmount": "20" + }, { "minAmount": "1", - "weight": "25.0", + "weight": "1.0", "id": "565", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", - "id": "199", + "weight": "14.0", + "id": "14424", + "maxAmount": "1" + }, + { + "minAmount": "12", + "weight": "36.0", + "id": "995", + "maxAmount": "12" + }, + { + "minAmount": "42", + "weight": "3.0", + "id": "995", + "maxAmount": "42" + }, + { + "minAmount": "1", + "weight": "2.0", + "id": "1381", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", - "id": "201", + "weight": "2.0", + "id": "14426", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", - "id": "205", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "207", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "209", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "211", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "213", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "215", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "2485", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "217", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "31", + "weight": "28.0", + "id": "0", "maxAmount": "1" } ] @@ -14940,130 +14414,95 @@ "ids": "1022", "description": "", "main": [ - { - "minAmount": "1", - "weight": "50.0", - "id": "199", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "201", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "203", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "205", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "207", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "209", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "211", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "213", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "215", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "2485", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "217", - "maxAmount": "1" - }, { "minAmount": "15", - "weight": "50.0", + "weight": "13.0", "id": "555", - "maxAmount": "20" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "563", - "maxAmount": "1" + "maxAmount": "15" }, { "minAmount": "2", - "weight": "50.0", + "weight": "9.0", "id": "561", "maxAmount": "2" }, { "minAmount": "2", - "weight": "50.0", + "weight": "7.0", "id": "562", "maxAmount": "2" }, { "minAmount": "1", - "weight": "25.0", + "weight": "6.0", + "id": "563", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "4.0", "id": "560", "maxAmount": "1" }, + { + "minAmount": "20", + "weight": "3.0", + "id": "555", + "maxAmount": "20" + }, { "minAmount": "1", - "weight": "25.0", + "weight": "1.0", "id": "565", "maxAmount": "1" }, { - "minAmount": "10", - "weight": "50.0", + "minAmount": "1", + "weight": "14.0", + "id": "14424", + "maxAmount": "1" + }, + { + "minAmount": "12", + "weight": "36.0", "id": "995", - "maxAmount": "40" + "maxAmount": "12" + }, + { + "minAmount": "42", + "weight": "3.0", + "id": "995", + "maxAmount": "42" }, { "minAmount": "1", - "weight": "25.0", + "weight": "2.0", "id": "1383", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "31", + "weight": "2.0", + "id": "14426", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "28.0", + "id": "0", "maxAmount": "1" } ] }, { - "default": [], + "default": [ + { + "minAmount": "1", + "weight": "1.0", + "id": "3325", + "maxAmount": "1" + } + ], "charm": [ { "minAmount": "1", @@ -16199,135 +15638,6 @@ } ] }, - { - "default": [ - { - "minAmount": "1", - "weight": "100.0", - "id": "526", - "maxAmount": "1" - } - ], - "charm": [], - "ids": "1087", - "description": "", - "main": [ - { - "minAmount": "2", - "weight": "25.0", - "id": "562", - "maxAmount": "9" - }, - { - "minAmount": "6", - "weight": "25.0", - "id": "554", - "maxAmount": "6" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "199", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "201", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "205", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "203", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "207", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "209", - "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": "217", - "maxAmount": "1" - }, - { - "minAmount": "3", - "weight": "25.0", - "id": "877", - "maxAmount": "12" - }, - { - "minAmount": "7", - "weight": "25.0", - "id": "882", - "maxAmount": "7" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1203", - "maxAmount": "1" - }, - { - "minAmount": "3", - "weight": "50.0", - "id": "995", - "maxAmount": "25" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1965", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "436", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "313", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "7767", - "maxAmount": "1" - } - ] - }, { "default": [ { @@ -16372,178 +15682,136 @@ "ids": "1096", "description": "", "main": [ - { - "minAmount": "16", - "weight": "25.0", - "id": "561", - "maxAmount": "16" - }, - { - "minAmount": "25", - "weight": "25.0", - "id": "557", - "maxAmount": "25" - }, - { - "minAmount": "65", - "weight": "25.0", - "id": "557", - "maxAmount": "65" - }, - { - "minAmount": "4", - "weight": "25.0", - "id": "563", - "maxAmount": "4" - }, { "minAmount": "1", - "weight": "25.0", + "weight": "4.0", "id": "1119", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", - "id": "1339", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "1357", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "1183", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", + "weight": "3.0", "id": "1341", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "1347", + "weight": "3.0", + "id": "1339", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", + "weight": "1.0", + "id": "1183", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", "id": "3122", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "199", + "weight": "1.0", + "id": "1121", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "201", + "weight": "1.0", + "id": "1347", "maxAmount": "1" }, { - "minAmount": "1", - "weight": "25.0", - "id": "203", - "maxAmount": "1" + "minAmount": "80", + "weight": "8.0", + "id": "557", + "maxAmount": "80" }, { - "minAmount": "1", - "weight": "25.0", - "id": "205", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "207", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "209", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "213", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "215", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "2485", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "211", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "217", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "995", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "10976", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "10977", - "maxAmount": "1" + "minAmount": "16", + "weight": "5.0", + "id": "561", + "maxAmount": "16" }, { "minAmount": "4", - "weight": "25.0", - "id": "362", + "weight": "3.0", + "id": "563", "maxAmount": "4" }, { - "minAmount": "6", + "minAmount": "65", + "weight": "1.0", + "id": "557", + "maxAmount": "65" + }, + { + "minAmount": "25", + "weight": "1.0", + "id": "557", + "maxAmount": "25" + }, + { + "minAmount": "1", + "weight": "15.0", + "id": "14424", + "maxAmount": "1" + }, + { + "minAmount": "40", + "weight": "29.0", + "id": "995", + "maxAmount": "40" + }, + { + "minAmount": "135", "weight": "25.0", + "id": "995", + "maxAmount": "135" + }, + { + "minAmount": "190", + "weight": "10.0", + "id": "995", + "maxAmount": "190" + }, + { + "minAmount": "20", + "weight": "4.0", + "id": "995", + "maxAmount": "20" + }, + { + "minAmount": "420", + "weight": "1.0", + "id": "995", + "maxAmount": "420" + }, + { + "minAmount": "6", + "weight": "3.0", "id": "454", "maxAmount": "6" }, + { + "minAmount": "4", + "weight": "2.0", + "id": "360", + "maxAmount": "4" + }, { "minAmount": "1", "weight": "5.0", - "id": "31", + "id": "14426", "maxAmount": "1" }, { "minAmount": "1", - "weight": "69.0", - "id": "0", + "weight": "2.0", + "id": "1357", "maxAmount": "1" } ] @@ -16594,86 +15862,134 @@ "main": [ { "minAmount": "1", - "weight": "25.0", - "id": "1183", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1341", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "3122", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1121", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1347", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", + "weight": "4.0", "id": "1119", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "3.0", + "id": "1341", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "3.0", "id": "1339", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "1357", + "weight": "1.0", + "id": "1183", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", - "id": "995", + "weight": "1.0", + "id": "3122", "maxAmount": "1" }, { - "minAmount": "4", - "weight": "25.0", - "id": "563", - "maxAmount": "4" + "minAmount": "1", + "weight": "1.0", + "id": "1121", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "1347", + "maxAmount": "1" }, { "minAmount": "80", - "weight": "25.0", + "weight": "8.0", "id": "557", "maxAmount": "80" }, + { + "minAmount": "16", + "weight": "5.0", + "id": "561", + "maxAmount": "16" + }, + { + "minAmount": "4", + "weight": "3.0", + "id": "563", + "maxAmount": "4" + }, + { + "minAmount": "65", + "weight": "1.0", + "id": "557", + "maxAmount": "65" + }, + { + "minAmount": "25", + "weight": "1.0", + "id": "557", + "maxAmount": "25" + }, { "minAmount": "1", - "weight": "25.0", - "id": "10976", + "weight": "15.0", + "id": "14424", "maxAmount": "1" }, + { + "minAmount": "40", + "weight": "29.0", + "id": "995", + "maxAmount": "40" + }, + { + "minAmount": "135", + "weight": "25.0", + "id": "995", + "maxAmount": "135" + }, + { + "minAmount": "190", + "weight": "10.0", + "id": "995", + "maxAmount": "190" + }, + { + "minAmount": "20", + "weight": "4.0", + "id": "995", + "maxAmount": "20" + }, + { + "minAmount": "420", + "weight": "1.0", + "id": "995", + "maxAmount": "420" + }, + { + "minAmount": "6", + "weight": "3.0", + "id": "454", + "maxAmount": "6" + }, + { + "minAmount": "4", + "weight": "2.0", + "id": "360", + "maxAmount": "4" + }, { "minAmount": "1", "weight": "5.0", - "id": "31", + "id": "14426", "maxAmount": "1" }, { "minAmount": "1", - "weight": "100.0", - "id": "0", + "weight": "2.0", + "id": "1357", "maxAmount": "1" } ] @@ -16724,74 +16040,134 @@ "main": [ { "minAmount": "1", - "weight": "25.0", - "id": "1357", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1341", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "3122", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1347", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", + "weight": "4.0", "id": "1119", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "3.0", + "id": "1341", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "3.0", "id": "1339", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", - "id": "995", + "weight": "1.0", + "id": "1183", "maxAmount": "1" }, { - "minAmount": "2", - "weight": "25.0", - "id": "563", - "maxAmount": "2" + "minAmount": "1", + "weight": "1.0", + "id": "3122", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "1121", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "1347", + "maxAmount": "1" }, { "minAmount": "80", - "weight": "25.0", + "weight": "8.0", "id": "557", "maxAmount": "80" }, + { + "minAmount": "16", + "weight": "5.0", + "id": "561", + "maxAmount": "16" + }, + { + "minAmount": "4", + "weight": "3.0", + "id": "563", + "maxAmount": "4" + }, + { + "minAmount": "65", + "weight": "1.0", + "id": "557", + "maxAmount": "65" + }, + { + "minAmount": "25", + "weight": "1.0", + "id": "557", + "maxAmount": "25" + }, { "minAmount": "1", - "weight": "25.0", - "id": "5323", + "weight": "15.0", + "id": "14424", "maxAmount": "1" }, + { + "minAmount": "40", + "weight": "29.0", + "id": "995", + "maxAmount": "40" + }, + { + "minAmount": "135", + "weight": "25.0", + "id": "995", + "maxAmount": "135" + }, + { + "minAmount": "190", + "weight": "10.0", + "id": "995", + "maxAmount": "190" + }, + { + "minAmount": "20", + "weight": "4.0", + "id": "995", + "maxAmount": "20" + }, + { + "minAmount": "420", + "weight": "1.0", + "id": "995", + "maxAmount": "420" + }, + { + "minAmount": "6", + "weight": "3.0", + "id": "454", + "maxAmount": "6" + }, + { + "minAmount": "4", + "weight": "2.0", + "id": "360", + "maxAmount": "4" + }, { "minAmount": "1", "weight": "5.0", - "id": "31", + "id": "14426", "maxAmount": "1" }, { "minAmount": "1", - "weight": "100.0", - "id": "0", + "weight": "2.0", + "id": "1357", "maxAmount": "1" } ] @@ -18333,189 +17709,135 @@ "main": [ { "minAmount": "1", - "weight": "25.0", - "id": "1197", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1147", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1113", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "10.0", - "id": "1127", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "10.0", - "id": "1185", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "1353", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "1365", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "1299", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", + "weight": "4.0", "id": "1285", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "3.0", + "id": "1365", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "2.0", + "id": "1355", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "2.0", "id": "1211", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", - "id": "1355", + "weight": "1.0", + "id": "1197", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "1147", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "1113", "maxAmount": "1" }, { "minAmount": "50", - "weight": "25.0", + "weight": "8.0", "id": "556", "maxAmount": "50" }, + { + "minAmount": "10", + "weight": "7.0", + "id": "562", + "maxAmount": "10" + }, { "minAmount": "7", - "weight": "25.0", + "weight": "4.0", "id": "565", "maxAmount": "7" }, { "minAmount": "37", - "weight": "25.0", + "weight": "1.0", "id": "554", "maxAmount": "37" }, { "minAmount": "3", - "weight": "25.0", + "weight": "1.0", "id": "563", "maxAmount": "3" }, - { - "minAmount": "10", - "weight": "25.0", - "id": "562", - "maxAmount": "10" - }, { "minAmount": "1", - "weight": "50.0", - "id": "199", + "weight": "23.0", + "id": "14424", "maxAmount": "1" }, { - "minAmount": "1", - "weight": "50.0", - "id": "215", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "203", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "201", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "205", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "211", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "207", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "209", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "213", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "2485", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "217", - "maxAmount": "1" + "minAmount": "132", + "weight": "40.0", + "id": "995", + "maxAmount": "132" }, { "minAmount": "30", - "weight": "50.0", + "weight": "7.0", "id": "995", "maxAmount": "30" }, + { + "minAmount": "44", + "weight": "6.0", + "id": "995", + "maxAmount": "44" + }, + { + "minAmount": "220", + "weight": "6.0", + "id": "995", + "maxAmount": "220" + }, + { + "minAmount": "460", + "weight": "1.0", + "id": "995", + "maxAmount": "460" + }, { "minAmount": "1", - "weight": "25.0", + "weight": "3.0", "id": "379", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "1.0", "id": "133", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "1.0", "id": "31", "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "5.0", + "id": "14426", + "maxAmount": "1" } ] }, @@ -18553,6 +17875,20 @@ "maxAmount": "6" } ], + "tertiary": [ + { + "minAmount": "1", + "weight": "255.0", + "id": "0", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "7981", + "maxAmount": "1" + } + ], "ids": "1158,1159,1160,3835,3836,4234", "description": "", "main": [ @@ -18749,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": [ { @@ -19085,20 +18421,7 @@ ], "ids": "1196,1197", "description": "", - "main": [ - { - "minAmount": "1", - "weight": "25.0", - "id": "7815", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "100.0", - "id": "0", - "maxAmount": "1" - } - ] + "main": [] }, { "default": [ @@ -19167,170 +18490,116 @@ "main": [ { "minAmount": "1", - "weight": "25.0", + "weight": "4.0", "id": "1311", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "1365", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", + "weight": "3.0", "id": "1353", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", + "weight": "3.0", + "id": "1365", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", "id": "1197", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "1163", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", + "weight": "1.0", "id": "1073", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "1135", + "weight": "1.0", + "id": "1163", "maxAmount": "1" }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1065", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1099", - "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": "203", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "205", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "207", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "209", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "211", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "213", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "215", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "2485", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "217", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "995", - "maxAmount": "220" - }, { "minAmount": "75", - "weight": "25.0", + "weight": "8.0", "id": "554", "maxAmount": "75" }, - { - "minAmount": "1", - "weight": "25.0", - "id": "2357", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "361", - "maxAmount": "1" - }, { "minAmount": "15", - "weight": "25.0", + "weight": "5.0", "id": "562", "maxAmount": "15" }, + { + "minAmount": "5", + "weight": "3.0", + "id": "560", + "maxAmount": "5" + }, + { + "minAmount": "37", + "weight": "1.0", + "id": "554", + "maxAmount": "37" + }, + { + "minAmount": "132", + "weight": "40.0", + "id": "995", + "maxAmount": "132" + }, + { + "minAmount": "44", + "weight": "29.0", + "id": "995", + "maxAmount": "44" + }, + { + "minAmount": "220", + "weight": "10.0", + "id": "995", + "maxAmount": "220" + }, + { + "minAmount": "11", + "weight": "7.0", + "id": "995", + "maxAmount": "11" + }, + { + "minAmount": "460", + "weight": "1.0", + "id": "995", + "maxAmount": "460" + }, + { + "minAmount": "1", + "weight": "3.0", + "id": "361", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "2.0", + "id": "2357", + "maxAmount": "1" + }, { "minAmount": "10", - "weight": "25.0", + "weight": "1.0", "id": "1734", "maxAmount": "10" }, { "minAmount": "1", "weight": "5.0", - "id": "1", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "31", + "id": "14426", "maxAmount": "1" } ] @@ -19730,10 +18999,10 @@ "maxAmount": "1" }, { - "minAmount": "110", + "minAmount": "30", "weight": "5.0", "id": "313", - "maxAmount": "30" + "maxAmount": "110" }, { "minAmount": "1", @@ -19895,10 +19164,10 @@ "maxAmount": "1" }, { - "minAmount": "110", + "minAmount": "30", "weight": "5.0", "id": "313", - "maxAmount": "30" + "maxAmount": "110" }, { "minAmount": "1", @@ -20810,72 +20079,244 @@ "charm": [ { "minAmount": "1", - "weight": "28.0", + "weight": "55.0", + "id": "0", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "13.0", "id": "12158", "maxAmount": "1" }, { "minAmount": "1", - "weight": "14.0", + "weight": "6.0", "id": "12159", "maxAmount": "1" }, { "minAmount": "1", - "weight": "56.0", + "weight": "25.0", "id": "12160", "maxAmount": "1" }, { "minAmount": "1", - "weight": "2.0", + "weight": "1.0", "id": "12163", "maxAmount": "1" } ], - "ids": "2455,2456", - "description": "Waterbirth Dagannoths", + "ids": "1338", + "description": "Chaos Tunnels / Lighthouse Dagannoths", "main": [ { "minAmount": "1", - "weight": "60.0", - "id": "1353", + "weight": "5.0", + "id": "1237", "maxAmount": "1" }, { "minAmount": "1", - "weight": "60.0", + "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": "30" + "maxAmount": "15" }, { "minAmount": "3", - "weight": "16.0", + "weight": "1.0", "id": "828", "maxAmount": "3" }, { "minAmount": "1", - "weight": "16.0", - "id": "1243", + "weight": "18.0", + "id": "14428", "maxAmount": "1" }, { "minAmount": "1", - "weight": "24.0", - "id": "3758", + "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": "24.0", - "id": "3748", + "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": "16.0", - "id": "3757", + "weight": "1.0", + "id": "5733", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "14426", + "maxAmount": "1" + } + ] + }, + { + "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": "2455", + "description": "Waterbirth Dagannoths (Melee Variant)", + "main": [ + { + "minAmount": "1", + "weight": "1.0", + "id": "3749", "maxAmount": "1" }, { @@ -20884,12 +20325,6 @@ "id": "3751", "maxAmount": "1" }, - { - "minAmount": "1", - "weight": "1.0", - "id": "3749", - "maxAmount": "1" - }, { "minAmount": "1", "weight": "1.0", @@ -20904,75 +20339,433 @@ }, { "minAmount": "1", - "weight": "64.0", - "id": "199", + "weight": "160.0", + "id": "1353", "maxAmount": "1" }, { - "minAmount": "1", - "weight": "64.0", - "id": "201", - "maxAmount": "1" + "minAmount": "3", + "weight": "160.0", + "id": "828", + "maxAmount": "3" + }, + { + "minAmount": "15", + "weight": "80.0", + "id": "886", + "maxAmount": "15" }, { "minAmount": "1", - "weight": "64.0", - "id": "203", + "weight": "48.0", + "id": "3748", "maxAmount": "1" }, { "minAmount": "1", "weight": "48.0", - "id": "209", + "id": "3757", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "48.0", + "id": "3758", "maxAmount": "1" }, { "minAmount": "1", "weight": "32.0", - "id": "2485", + "id": "3767", "maxAmount": "1" }, { "minAmount": "1", "weight": "32.0", - "id": "211", + "id": "3761", "maxAmount": "1" }, { "minAmount": "1", "weight": "32.0", - "id": "217", + "id": "3799", "maxAmount": "1" }, + { + "minAmount": "10", + "weight": "160.0", + "id": "555", + "maxAmount": "10" + }, + { + "minAmount": "1", + "weight": "160.0", + "id": "561", + "maxAmount": "10" + }, + { + "minAmount": "1", + "weight": "160.0", + "id": "563", + "maxAmount": "10" + }, + { + "minAmount": "1", + "weight": "32.0", + "id": "565", + "maxAmount": "10" + }, { "minAmount": "1", "weight": "64.0", - "id": "207", + "id": "14424", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "16.0", + "id": "14428", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "160.0", + "id": "6155", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "160.0", + "id": "590", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "112.0", + "id": "411", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "80.0", + "id": "377", + "maxAmount": "1" + }, + { + "minAmount": "47", + "weight": "32.0", + "id": "995", + "maxAmount": "47" + }, + { + "minAmount": "3", + "weight": "32.0", + "id": "995", + "maxAmount": "3" + }, + { + "minAmount": "38", + "weight": "32.0", + "id": "995", + "maxAmount": "38" + }, + { + "minAmount": "26", + "weight": "32.0", + "id": "995", + "maxAmount": "26" + }, + { + "minAmount": "81", + "weight": "32.0", + "id": "995", + "maxAmount": "81" + }, + { + "minAmount": "1", + "weight": "32.0", + "id": "3801", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "10.0", + "id": "1438", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "10.0", + "id": "1440", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "10.0", + "id": "1442", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "10.0", + "id": "1444", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "10.0", + "id": "1446", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "10.0", + "id": "1448", "maxAmount": "1" }, { "minAmount": "1", "weight": "8.0", - "id": "5296", + "id": "1454", "maxAmount": "1" }, { "minAmount": "1", - "weight": "8.0", - "id": "5300", + "weight": "4.0", + "id": "1452", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "4.0", + "id": "1462", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "16.0", + "id": "5733", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "16.0", + "id": "14426", + "maxAmount": "1" + } + ] + }, + { + "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": "2456", + "description": "Waterbirth Dagannoths (Ranged Variant)", + "main": [ + { + "minAmount": "1", + "weight": "1.0", + "id": "3749", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "3751", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "3753", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "3755", + "maxAmount": "1" + }, + { + "minAmount": "30", + "weight": "160.0", + "id": "886", + "maxAmount": "30" + }, + { + "minAmount": "1", + "weight": "48.0", + "id": "3748", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "48.0", + "id": "3757", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "48.0", + "id": "3758", "maxAmount": "1" }, { "minAmount": "1", "weight": "32.0", - "id": "215", + "id": "3767", "maxAmount": "1" }, { "minAmount": "1", - "weight": "146.0", + "weight": "32.0", + "id": "3761", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "32.0", + "id": "3799", + "maxAmount": "1" + }, + { + "minAmount": "3", + "weight": "32.0", + "id": "828", + "maxAmount": "3" + }, + { + "minAmount": "1", + "weight": "32.0", + "id": "6149", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "32.0", + "id": "6143", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "128.0", + "id": "14424", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "16.0", + "id": "14428", + "maxAmount": "1" + }, + { + "minAmount": "10", + "weight": "480.0", + "id": "402", + "maxAmount": "10" + }, + { + "minAmount": "20", + "weight": "160.0", + "id": "45", + "maxAmount": "20" + }, + { + "minAmount": "47", + "weight": "32.0", "id": "995", - "maxAmount": "74" + "maxAmount": "47" + }, + { + "minAmount": "3", + "weight": "32.0", + "id": "995", + "maxAmount": "3" + }, + { + "minAmount": "38", + "weight": "32.0", + "id": "995", + "maxAmount": "38" + }, + { + "minAmount": "26", + "weight": "32.0", + "id": "995", + "maxAmount": "26" + }, + { + "minAmount": "81", + "weight": "32.0", + "id": "995", + "maxAmount": "81" + }, + { + "minAmount": "5", + "weight": "128.0", + "id": "403", + "maxAmount": "5" + }, + { + "minAmount": "1", + "weight": "80.0", + "id": "405", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "80.0", + "id": "413", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "80.0", + "id": "363", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "32.0", + "id": "6169", + "maxAmount": "1" }, { "minAmount": "1", @@ -20988,172 +20781,64 @@ }, { "minAmount": "1", - "weight": "56.0", - "id": "407", - "maxAmount": "1" - }, - { - "minAmount": "12", - "weight": "56.0", - "id": "413", - "maxAmount": "12" - }, - { - "minAmount": "1", - "weight": "3.0", - "id": "1440", + "weight": "32.0", + "id": "3801", "maxAmount": "1" }, { "minAmount": "1", - "weight": "3.0", + "weight": "10.0", "id": "1438", "maxAmount": "1" }, { "minAmount": "1", - "weight": "3.0", + "weight": "10.0", + "id": "1440", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "10.0", "id": "1442", "maxAmount": "1" }, { "minAmount": "1", - "weight": "3.0", + "weight": "10.0", + "id": "1444", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "10.0", + "id": "1446", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "10.0", "id": "1448", "maxAmount": "1" }, { "minAmount": "1", - "weight": "3.0", + "weight": "8.0", + "id": "1454", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "4.0", + "id": "1452", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "4.0", "id": "1462", "maxAmount": "1" }, - { - "minAmount": "1", - "weight": "3.0", - "id": "1446", - "maxAmount": "1" - }, - { - "minAmount": "12", - "weight": "80.0", - "id": "45", - "maxAmount": "12" - }, - { - "minAmount": "1", - "weight": "16.0", - "id": "3761", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "16.0", - "id": "3767", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "16.0", - "id": "3799", - "maxAmount": "1" - }, - { - "minAmount": "15", - "weight": "80.0", - "id": "555", - "maxAmount": "15" - }, - { - "minAmount": "6", - "weight": "80.0", - "id": "561", - "maxAmount": "6" - }, - { - "minAmount": "10", - "weight": "32.0", - "id": "565", - "maxAmount": "10" - }, - { - "minAmount": "5", - "weight": "80.0", - "id": "563", - "maxAmount": "10" - }, - { - "minAmount": "1", - "weight": "80.0", - "id": "590", - "maxAmount": "1" - }, - { - "minAmount": "15", - "weight": "32.0", - "id": "314", - "maxAmount": "15" - }, - { - "minAmount": "50", - "weight": "16.0", - "id": "313", - "maxAmount": "50" - }, - { - "minAmount": "100", - "weight": "16.0", - "id": "313", - "maxAmount": "100" - }, - { - "minAmount": "1", - "weight": "16.0", - "id": "311", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "16.0", - "id": "301", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "80.0", - "id": "377", - "maxAmount": "1" - }, - { - "minAmount": "5", - "weight": "40.0", - "id": "327", - "maxAmount": "5" - }, - { - "minAmount": "3", - "weight": "40.0", - "id": "345", - "maxAmount": "3" - }, - { - "minAmount": "10", - "weight": "240.0", - "id": "401", - "maxAmount": "10" - }, - { - "minAmount": "10", - "weight": "32.0", - "id": "3711", - "maxAmount": "10" - }, - { - "minAmount": "1", - "weight": "16.0", - "id": "31", - "maxAmount": "1" - }, { "minAmount": "1", "weight": "16.0", @@ -21162,8 +20847,8 @@ }, { "minAmount": "1", - "weight": "1400.0", - "id": "0", + "weight": "16.0", + "id": "14426", "maxAmount": "1" } ] @@ -22651,79 +22336,85 @@ "main": [ { "minAmount": "1", - "weight": "3.0", + "weight": "12.0", "id": "1353", "maxAmount": "1" }, { "minAmount": "1", - "weight": "1.0", + "weight": "4.0", "id": "1197", "maxAmount": "1" }, { "minAmount": "1", - "weight": "1.0", + "weight": "4.0", "id": "3053", "maxAmount": "1" }, { "minAmount": "1", - "weight": "1.0", + "weight": "4.0", "id": "1073", "maxAmount": "1" }, { "minAmount": "1", - "weight": "1.0", + "weight": "4.0", "id": "1163", "maxAmount": "1" }, { "minAmount": "1", - "weight": "0.25", + "weight": "1.0", "id": "4103", "maxAmount": "1" }, { "minAmount": "1", - "weight": "33.0", + "weight": "130.0", "id": "14424", "maxAmount": "1" }, { "minAmount": "2", - "weight": "33.0", + "weight": "130.0", "id": "14424", "maxAmount": "2" }, { "minAmount": "3", - "weight": "12.0", + "weight": "47.0", "id": "14424", "maxAmount": "3" }, { "minAmount": "1", - "weight": "19.0", + "weight": "76.0", "id": "14428", "maxAmount": "1" }, { "minAmount": "460", - "weight": "1.0", + "weight": "4.0", "id": "995", "maxAmount": "460" }, { "minAmount": "1", - "weight": "18.0", + "weight": "72.0", "id": "0", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", + "weight": "4.0", + "id": "12070", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "20.0", "id": "14426", "maxAmount": "1" } @@ -22770,6 +22461,20 @@ "maxAmount": "1" } ], + "tertiary": [ + { + "minAmount": "1", + "weight": "2999.0", + "id": "0", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "7978", + "maxAmount": "1" + } + ], "ids": "1608,1609,4229", "description": "", "main": [ @@ -22812,7 +22517,7 @@ { "minAmount": "1", "weight": "2.0", - "id": "4150", + "id": "4160", "maxAmount": "10" }, { @@ -23024,129 +22729,147 @@ "main": [ { "minAmount": "1", - "weight": "25.0", + "weight": "16.0", "id": "1311", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "1365", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", + "weight": "12.0", "id": "1353", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "4153", + "weight": "12.0", + "id": "1365", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "1163", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", + "weight": "4.0", "id": "4129", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "1157", + "weight": "4.0", + "id": "1163", "maxAmount": "1" }, { - "minAmount": "37", - "weight": "50.0", - "id": "554", - "maxAmount": "37" + "minAmount": "1", + "weight": "2.0", + "id": "4153", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "4101", + "maxAmount": "1" }, { "minAmount": "75", - "weight": "50.0", + "weight": "32.0", "id": "554", "maxAmount": "75" }, { "minAmount": "15", - "weight": "50.0", + "weight": "12.0", "id": "562", "maxAmount": "15" }, { "minAmount": "5", - "weight": "25.0", + "weight": "12.0", "id": "560", "maxAmount": "5" }, { - "minAmount": "11", - "weight": "50.0", + "minAmount": "37", + "weight": "4.0", + "id": "554", + "maxAmount": "37" + }, + { + "minAmount": "132", + "weight": "160.0", "id": "995", - "maxAmount": "498" + "maxAmount": "132" }, { - "minAmount": "35", - "weight": "25.0", - "id": "7937", - "maxAmount": "35" + "minAmount": "44", + "weight": "108.0", + "id": "995", + "maxAmount": "44" + }, + { + "minAmount": "220", + "weight": "40.0", + "id": "995", + "maxAmount": "220" + }, + { + "minAmount": "11", + "weight": "28.0", + "id": "995", + "maxAmount": "11" + }, + { + "minAmount": "460", + "weight": "4.0", + "id": "995", + "maxAmount": "460" }, { "minAmount": "3", - "weight": "25.0", - "id": "2359", - "maxAmount": "3" - }, - { - "minAmount": "3", - "weight": "25.0", + "weight": "8.0", "id": "2353", "maxAmount": "3" }, + { + "minAmount": "3", + "weight": "8.0", + "id": "2359", + "maxAmount": "3" + }, { "minAmount": "1", - "weight": "25.0", + "weight": "8.0", "id": "2357", "maxAmount": "1" }, { "minAmount": "10", - "weight": "25.0", + "weight": "4.0", "id": "1734", "maxAmount": "10" }, { "minAmount": "1", - "weight": "5.0", - "id": "4101", + "weight": "4.0", + "id": "12070", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "1", + "weight": "20.0", + "id": "14426", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "31", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "100.0", + "weight": "1.0", "id": "0", "maxAmount": "1" + }, + { + "minAmount": "35", + "weight": "8.0", + "id": "7937", + "maxAmount": "35" } ] }, @@ -23348,207 +23071,123 @@ "main": [ { "minAmount": "1", - "weight": "25.0", + "weight": "16.0", "id": "1420", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", + "weight": "16.0", "id": "1203", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", + "weight": "8.0", "id": "1191", "maxAmount": "1" }, - { - "minAmount": "1", - "weight": "5.0", - "id": "1199", - "maxAmount": "1" - }, { "minAmount": "3", - "weight": "50.0", + "weight": "23.0", "id": "556", "maxAmount": "3" }, { "minAmount": "3", - "weight": "50.0", + "weight": "16.0", "id": "562", "maxAmount": "3" }, { "minAmount": "6", - "weight": "50.0", + "weight": "8.0", "id": "562", "maxAmount": "6" }, { "minAmount": "2", - "weight": "25.0", + "weight": "23.0", "id": "564", "maxAmount": "2" }, - { - "minAmount": "6", - "weight": "25.0", - "id": "554", - "maxAmount": "6" - }, { "minAmount": "7", - "weight": "25.0", + "weight": "8.0", "id": "554", "maxAmount": "7" }, - { - "minAmount": "1", - "weight": "50.0", - "id": "199", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "203", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "201", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "205", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "213", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "207", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "209", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "211", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "2485", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "217", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "215", - "maxAmount": "1" - }, { "minAmount": "13", - "weight": "50.0", + "weight": "80.0", "id": "995", "maxAmount": "13" }, { "minAmount": "26", - "weight": "50.0", + "weight": "64.0", "id": "995", - "maxAmount": "35" + "maxAmount": "26" }, { "minAmount": "13", - "weight": "50.0", + "weight": "176.0", "id": "7937", "maxAmount": "13" }, - { - "minAmount": "23", - "weight": "50.0", - "id": "7937", - "maxAmount": "23" - }, { "minAmount": "7", - "weight": "50.0", + "weight": "40.0", "id": "313", "maxAmount": "7" }, { "minAmount": "15", - "weight": "50.0", + "weight": "176.0", "id": "313", "maxAmount": "15" }, { "minAmount": "1", - "weight": "25.0", + "weight": "8.0", "id": "440", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "8.0", "id": "221", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", + "weight": "2.0", "id": "4105", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "4105", + "weight": "8.0", + "id": "1", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "12070", + "weight": "272.0", + "id": "14424", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "31", + "weight": "16.0", + "id": "14426", "maxAmount": "1" }, { - "minAmount": "1", - "weight": "100.0", - "id": "0", - "maxAmount": "1" + "minAmount": "35", + "weight": "64.0", + "id": "995", + "maxAmount": "35" } ] }, @@ -23598,189 +23237,135 @@ "main": [ { "minAmount": "1", - "weight": "25.0", - "id": "1163", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1073", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1197", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "8.0", - "id": "4131", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", + "weight": "4.0", "id": "1311", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", + "weight": "3.0", + "id": "1353", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "3.0", "id": "1365", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", - "id": "1353", + "weight": "1.0", + "id": "1197", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "1073", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "1163", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "4131", "maxAmount": "1" }, { "minAmount": "5", - "weight": "25.0", + "weight": "3.0", "id": "560", "maxAmount": "5" }, { "minAmount": "10", - "weight": "25.0", + "weight": "3.0", "id": "560", "maxAmount": "10" }, - { - "minAmount": "3", - "weight": "12.5", - "id": "562", - "maxAmount": "3" - }, { "minAmount": "37", - "weight": "12.5", + "weight": "1.0", "id": "562", "maxAmount": "37" }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5281", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5297", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5280", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5298", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5299", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5106", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5296", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5301", - "maxAmount": "1" - }, { "minAmount": "1", "weight": "8.0", - "id": "5303", + "id": "14428", "maxAmount": "1" }, { - "minAmount": "1", - "weight": "8.0", - "id": "5302", - "maxAmount": "1" + "minAmount": "132", + "weight": "40.0", + "id": "995", + "maxAmount": "132" }, { - "minAmount": "10", - "weight": "50.0", + "minAmount": "44", + "weight": "29.0", + "id": "995", + "maxAmount": "44" + }, + { + "minAmount": "220", + "weight": "10.0", + "id": "995", + "maxAmount": "220" + }, + { + "minAmount": "11", + "weight": "7.0", + "id": "995", + "maxAmount": "11" + }, + { + "minAmount": "460", + "weight": "1.0", "id": "995", "maxAmount": "460" }, { "minAmount": "1", - "weight": "50.0", + "weight": "3.0", "id": "361", "maxAmount": "1" }, + { + "minAmount": "1", + "weight": "2.0", + "id": "2357", + "maxAmount": "1" + }, { "minAmount": "10", - "weight": "25.0", + "weight": "1.0", "id": "1734", "maxAmount": "10" }, { "minAmount": "1", - "weight": "25.0", - "id": "2357", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "8.0", + "weight": "1.0", "id": "12070", "maxAmount": "1" }, { "minAmount": "1", - "weight": "16.0", + "weight": "4.0", + "id": "14426", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", "id": "31", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "100.0", - "id": "0", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "8.0", - "id": "5300", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "8.0", - "id": "5304", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5295", - "maxAmount": "1" } ] }, @@ -23825,6 +23410,20 @@ "maxAmount": "1" } ], + "tertiary": [ + { + "minAmount": "1", + "weight": "1.0", + "id": "7979", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "4999.0", + "id": "0", + "maxAmount": "1" + } + ], "ids": "1615,4230", "description": "", "main": [ @@ -24027,6 +23626,20 @@ "maxAmount": "1" } ], + "tertiary": [ + { + "minAmount": "1", + "weight": "1.0", + "id": "7977", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1999.0", + "id": "0", + "maxAmount": "1" + } + ], "ids": "1616,1617,4228", "description": "", "main": [ @@ -24204,12 +23817,6 @@ "id": "4109", "maxAmount": "1" }, - { - "minAmount": "1", - "weight": "5.0", - "id": "7977", - "maxAmount": "1" - }, { "minAmount": "1", "weight": "5.0", @@ -24473,283 +24080,165 @@ "maxAmount": "1" } ], + "tertiary": [ + { + "minAmount": "1", + "weight": "114872.0", + "id": "0", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "128.0", + "id": "7976", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "13000.0", + "id": "12109", + "maxAmount": "1" + } + ], "ids": "1620,1621,4227", "description": "", "main": [ { "minAmount": "1", - "weight": "50.0", + "weight": "12.0", "id": "1279", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", + "weight": "12.0", "id": "1207", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", - "id": "1295", + "weight": "4.0", + "id": "4121", "maxAmount": "1" }, { "minAmount": "5", - "weight": "25.0", + "weight": "4.0", "id": "826", "maxAmount": "5" }, { "minAmount": "1", - "weight": "25.0", - "id": "1069", + "weight": "3.0", + "id": "1295", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "4121", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "1147", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", + "weight": "1.0", "id": "4117", "maxAmount": "1" }, { - "minAmount": "1", - "weight": "50.0", + "minAmount": "2", + "weight": "24.0", + "id": "561", + "maxAmount": "2" + }, + { + "minAmount": "4", + "weight": "16.0", + "id": "561", + "maxAmount": "4" + }, + { + "minAmount": "2", + "weight": "12.0", + "id": "563", + "maxAmount": "2" + }, + { + "minAmount": "6", + "weight": "8.0", + "id": "561", + "maxAmount": "6" + }, + { + "minAmount": "2", + "weight": "8.0", "id": "555", "maxAmount": "2" }, { "minAmount": "7", - "weight": "50.0", + "weight": "8.0", "id": "554", "maxAmount": "7" }, - { - "minAmount": "2", - "weight": "50.0", - "id": "561", - "maxAmount": "2" - }, - { - "minAmount": "4", - "weight": "50.0", - "id": "561", - "maxAmount": "4" - }, - { - "minAmount": "2", - "weight": "50.0", - "id": "563", - "maxAmount": "2" - }, - { - "minAmount": "3", - "weight": "50.0", - "id": "563", - "maxAmount": "3" - }, - { - "minAmount": "50", - "weight": "25.0", - "id": "560", - "maxAmount": "50" - }, { "minAmount": "1", - "weight": "50.0", - "id": "199", + "weight": "40.0", + "id": "14424", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", - "id": "201", + "weight": "72.0", + "id": "14430", "maxAmount": "1" }, { - "minAmount": "1", - "weight": "50.0", - "id": "203", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "205", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "209", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "211", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "207", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "213", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "215", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "2485", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "217", - "maxAmount": "1" - }, - { - "minAmount": "4", - "weight": "50.0", - "id": "5318", - "maxAmount": "4" - }, - { - "minAmount": "4", - "weight": "50.0", - "id": "5319", - "maxAmount": "4" - }, - { - "minAmount": "4", - "weight": "50.0", - "id": "5324", - "maxAmount": "4" - }, - { - "minAmount": "3", - "weight": "50.0", - "id": "5322", - "maxAmount": "3" - }, - { - "minAmount": "3", - "weight": "50.0", - "id": "5308", - "maxAmount": "3" - }, - { - "minAmount": "2", - "weight": "50.0", - "id": "5309", - "maxAmount": "2" - }, - { - "minAmount": "3", - "weight": "50.0", - "id": "5320", - "maxAmount": "3" - }, - { - "minAmount": "2", - "weight": "50.0", - "id": "5323", - "maxAmount": "4" - }, - { - "minAmount": "2", - "weight": "5.0", - "id": "5321", - "maxAmount": "2" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5295", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5096", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", + "minAmount": "15", + "weight": "64.0", "id": "995", - "maxAmount": "1" + "maxAmount": "15" }, { "minAmount": "5", - "weight": "50.0", + "weight": "48.0", "id": "995", "maxAmount": "5" }, + { + "minAmount": "28", + "weight": "48.0", + "id": "995", + "maxAmount": "28" + }, + { + "minAmount": "62", + "weight": "16.0", + "id": "995", + "maxAmount": "62" + }, + { + "minAmount": "42", + "weight": "12.0", + "id": "995", + "maxAmount": "42" + }, { "minAmount": "1", - "weight": "50.0", + "weight": "4.0", + "id": "995", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "84.0", "id": "225", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", - "id": "12109", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", + "weight": "4.0", "id": "5733", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "447", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "7983", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "31", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "88.9239", - "id": "0", + "weight": "8.0", + "id": "14426", "maxAmount": "1" } ] @@ -25957,15 +25446,23 @@ "maxAmount": "1" } ], + "tertiary": [ + { + "minAmount": "1", + "weight": "511.0", + "id": "0", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "7975", + "maxAmount": "1" + } + ], "ids": "1648,1649,1650,1651,1652,1653,1654,1655,1656,1657", "description": "", "main": [ - { - "minAmount": "1", - "weight": "50.0", - "id": "1059", - "maxAmount": "1" - }, { "minAmount": "1", "weight": "25.0", @@ -26093,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", @@ -26671,12 +26168,6 @@ "id": "686", "maxAmount": "1" }, - { - "minAmount": "1", - "weight": "5.0", - "id": "4170", - "maxAmount": "1" - }, { "minAmount": "1", "weight": "25.0", @@ -26957,12 +26448,6 @@ "id": "1129", "maxAmount": "1" }, - { - "minAmount": "1", - "weight": "5.0", - "id": "4170", - "maxAmount": "1" - }, { "minAmount": "2", "weight": "50.0", @@ -27591,12 +27076,6 @@ "weight": "100.0", "id": "526", "maxAmount": "1" - }, - { - "minAmount": "2", - "weight": "100.0", - "id": "526", - "maxAmount": "2" } ], "charm": [], @@ -27650,12 +27129,6 @@ "weight": "50.0", "id": "995", "maxAmount": "15" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "7910", - "maxAmount": "1" } ] }, @@ -27939,7 +27412,7 @@ "maxAmount": "1" } ], - "ids": "1832,5750", + "ids": "5750", "description": "", "main": [ { @@ -28459,7 +27932,7 @@ "maxAmount": "1" } ], - "ids": "1960,5360", + "ids": "1958,1961,1960,5360", "description": "", "main": [ { @@ -28476,7 +27949,7 @@ }, { "minAmount": "1", - "weight": "5.0", + "weight": "2.0", "id": "4675", "maxAmount": "1" }, @@ -28666,12 +28139,6 @@ "id": "5733", "maxAmount": "1" }, - { - "minAmount": "1", - "weight": "5.0", - "id": "6808", - "maxAmount": "1" - }, { "minAmount": "1", "weight": "5.0", @@ -28947,20 +28414,7 @@ ], "ids": "2033,4920,4921", "description": "", - "main": [ - { - "minAmount": "1", - "weight": "25.0", - "id": "7824", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "100.0", - "id": "0", - "maxAmount": "1" - } - ] + "main": [] }, { "default": [], @@ -28998,14 +28452,7 @@ ], "ids": "2035", "description": "", - "main": [ - { - "minAmount": "1", - "weight": "100.0", - "id": "0", - "maxAmount": "1" - } - ] + "main": [] }, { "default": [ @@ -29022,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": [ @@ -30009,6 +29406,18 @@ "id": "1438", "maxAmount": "1" }, + { + "minAmount": "1", + "weight": "25.0", + "id": "1456", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "25.0", + "id": "1450", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "25.0", @@ -30181,6 +29590,18 @@ "id": "1438", "maxAmount": "1" }, + { + "minAmount": "1", + "weight": "25.0", + "id": "1456", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "25.0", + "id": "1450", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "25.0", @@ -30353,6 +29774,18 @@ "id": "1438", "maxAmount": "1" }, + { + "minAmount": "1", + "weight": "25.0", + "id": "1456", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "25.0", + "id": "1450", + "maxAmount": "1" + }, { "minAmount": "1", "weight": "25.0", @@ -31291,13 +30724,13 @@ "charm": [ { "minAmount": "1", - "weight": "30.0", + "weight": "40.0", "id": "0", "maxAmount": "1" }, { "minAmount": "1", - "weight": "70.0", + "weight": "88.0", "id": "12168", "maxAmount": "1" } @@ -31307,116 +30740,92 @@ "main": [ { "minAmount": "1", - "weight": "12.0", - "id": "1387", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "1.26449", - "id": "6568", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "0.158062", + "weight": "1.0", "id": "6526", "maxAmount": "1" }, { - "minAmount": "10", - "weight": "50.0", + "minAmount": "1", + "weight": "1.0", + "id": "6568", + "maxAmount": "1" + }, + { + "minAmount": "25", + "weight": "256.0", "id": "557", - "maxAmount": "80" + "maxAmount": "25" }, { - "minAmount": "10", - "weight": "50.0", + "minAmount": "25", + "weight": "256.0", "id": "554", - "maxAmount": "80" + "maxAmount": "25" }, { - "minAmount": "10", - "weight": "25.0", + "minAmount": "25", + "weight": "128.0", "id": "556", - "maxAmount": "80" - }, - { - "minAmount": "10", - "weight": "25.0", - "id": "555", - "maxAmount": "80" + "maxAmount": "25" }, { "minAmount": "5", - "weight": "25.0", + "weight": "64.0", "id": "562", - "maxAmount": "10" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "561", - "maxAmount": "8" - }, - { - "minAmount": "2", - "weight": "25.0", - "id": "563", "maxAmount": "5" }, { - "minAmount": "1", - "weight": "12.0", + "minAmount": "2", + "weight": "64.0", + "id": "561", + "maxAmount": "2" + }, + { + "minAmount": "2", + "weight": "32.0", "id": "560", - "maxAmount": "6" + "maxAmount": "2" }, { "minAmount": "1", - "weight": "12.0", - "id": "565", - "maxAmount": "6" - }, - { - "minAmount": "1", - "weight": "12.0", - "id": "4697", - "maxAmount": "10" - }, - { - "minAmount": "10", - "weight": "12.0", - "id": "4699", - "maxAmount": "40" - }, - { - "minAmount": "1", - "weight": "12.0", - "id": "1442", + "weight": "8.0", + "id": "1617", "maxAmount": "1" }, { "minAmount": "1", - "weight": "300.0", - "id": "6529", - "maxAmount": "700" + "weight": "32.0", + "id": "1619", + "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "64.0", + "id": "1621", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "128.0", + "id": "1623", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "160.0", "id": "9194", "maxAmount": "1" }, { - "minAmount": "4", - "weight": "12.0", - "id": "7936", - "maxAmount": "10" + "minAmount": "100", + "weight": "1280.0", + "id": "6529", + "maxAmount": "1000" }, { "minAmount": "1", - "weight": "12.0", - "id": "31", + "weight": "1622.0", + "id": "0", "maxAmount": "1" } ] @@ -31426,13 +30835,13 @@ "charm": [ { "minAmount": "1", - "weight": "30.0", + "weight": "40.0", "id": "0", "maxAmount": "1" }, { "minAmount": "1", - "weight": "70.0", + "weight": "88.0", "id": "12168", "maxAmount": "1" } @@ -31442,80 +30851,74 @@ "main": [ { "minAmount": "1", - "weight": "200.0", - "id": "6529", - "maxAmount": "700" - }, - { - "minAmount": "2", - "weight": "25.0", - "id": "1775", - "maxAmount": "2" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "149", + "weight": "8.0", + "id": "2347", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "161", - "maxAmount": "1" - }, - { - "minAmount": "4", - "weight": "25.0", - "id": "1780", - "maxAmount": "12" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "1391", - "maxAmount": "1" - }, - { - "minAmount": "3", - "weight": "5.0", - "id": "1744", - "maxAmount": "3" - }, - { - "minAmount": "3", - "weight": "5.0", - "id": "1741", - "maxAmount": "3" - }, - { - "minAmount": "2", - "weight": "5.0", - "id": "1738", - "maxAmount": "8" - }, - { - "minAmount": "2", - "weight": "5.0", - "id": "7936", - "maxAmount": "6" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "379", + "weight": "8.0", + "id": "1755", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", + "weight": "8.0", + "id": "946", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "8.0", + "id": "1935", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "8.0", + "id": "1931", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "1617", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "4.0", + "id": "1619", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "8.0", + "id": "1621", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "16.0", + "id": "1623", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "20.0", "id": "9194", "maxAmount": "1" }, + { + "minAmount": "100", + "weight": "160.0", + "id": "6529", + "maxAmount": "700" + }, { "minAmount": "1", - "weight": "5.0", - "id": "31", + "weight": "263.0", + "id": "0", "maxAmount": "1" } ] @@ -31525,13 +30928,13 @@ "charm": [ { "minAmount": "1", - "weight": "30.0", + "weight": "40.0", "id": "0", "maxAmount": "1" }, { "minAmount": "2", - "weight": "70.0", + "weight": "88.0", "id": "12168", "maxAmount": "2" } @@ -31541,85 +30944,67 @@ "main": [ { "minAmount": "9", - "weight": "0.7534515", + "weight": "1.0", "id": "6522", - "maxAmount": "19" + "maxAmount": "29" }, { "minAmount": "1", - "weight": "0.7534515", + "weight": "1.0", "id": "6523", "maxAmount": "1" }, { "minAmount": "1", - "weight": "0.7534515", + "weight": "1.0", "id": "6525", "maxAmount": "1" }, { "minAmount": "1", - "weight": "0.7534515", + "weight": "1.0", "id": "6568", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "149", + "weight": "1.0", + "id": "1617", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "161", + "weight": "4.0", + "id": "1619", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "167", - "maxAmount": "1" - }, - { - "minAmount": "100", - "weight": "200.0", - "id": "6529", - "maxAmount": "2000" - }, - { - "minAmount": "15000", - "weight": "0.7534515", - "id": "6529", - "maxAmount": "15000" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "7054", + "weight": "8.0", + "id": "1621", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "379", + "weight": "16.0", + "id": "1623", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "20.0", "id": "9194", "maxAmount": "1" }, { - "minAmount": "1", - "weight": "7.0", - "id": "31", - "maxAmount": "1" + "minAmount": "100", + "weight": "160.0", + "id": "6529", + "maxAmount": "2000" }, { "minAmount": "1", - "weight": "25.0", + "weight": "299.0", "id": "0", "maxAmount": "1" } @@ -31630,13 +31015,13 @@ "charm": [ { "minAmount": "1", - "weight": "30.0", + "weight": "40.0", "id": "0", "maxAmount": "1" }, { "minAmount": "2", - "weight": "70.0", + "weight": "88.0", "id": "12168", "maxAmount": "2" } @@ -31646,398 +31031,62 @@ "main": [ { "minAmount": "1", - "weight": "0.7151277", + "weight": "1.0", "id": "6528", "maxAmount": "1" }, { "minAmount": "1", - "weight": "0.7151277", + "weight": "1.0", "id": "6524", "maxAmount": "1" }, { "minAmount": "1", - "weight": "0.7151277", + "weight": "1.0", "id": "6568", "maxAmount": "1" }, - { - "minAmount": "12", - "weight": "7.0", - "id": "562", - "maxAmount": "12" - }, { "minAmount": "1", - "weight": "25.0", - "id": "149", + "weight": "1.0", + "id": "1617", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "161", + "weight": "4.0", + "id": "1619", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "167", + "weight": "8.0", + "id": "1621", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "16.0", + "id": "1623", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "20.0", + "id": "9194", "maxAmount": "1" }, { "minAmount": "100", - "weight": "200.0", + "weight": "160.0", "id": "6529", "maxAmount": "2000" }, { "minAmount": "1", - "weight": "25.0", - "id": "7054", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "379", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "9194", - "maxAmount": "3" - }, - { - "minAmount": "1", - "weight": "7.0", - "id": "31", - "maxAmount": "1" - } - ] - }, - { - "default": [ - { - "minAmount": "1", - "weight": "100.0", - "id": "526", - "maxAmount": "1" - } - ], - "charm": [], - "ids": "2683", - "description": "", - "main": [ - { - "minAmount": "1", - "weight": "25.0", - "id": "1203", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "882", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "877", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "199", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "201", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "203", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "205", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "207", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "209", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "211", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "213", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "215", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "2485", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "217", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "995", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1965", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "562", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "558", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "436", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "554", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "313", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1440", - "maxAmount": "1" - }, - { - "minAmount": "4", - "weight": "25.0", - "id": "557", - "maxAmount": "4" - }, - { - "minAmount": "1", - "weight": "9.52", - "id": "12070", - "maxAmount": "1" - } - ] - }, - { - "default": [ - { - "minAmount": "1", - "weight": "100.0", - "id": "526", - "maxAmount": "1" - } - ], - "charm": [], - "ids": "2684", - "description": "", - "main": [ - { - "minAmount": "1", - "weight": "25.0", - "id": "882", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "877", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1139", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1203", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "199", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "201", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "203", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "205", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "207", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "209", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "211", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "213", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "215", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "2485", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "217", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "995", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1965", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "436", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "313", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1440", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "562", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "558", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "557", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1381", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "1", + "weight": "300.0", + "id": "0", "maxAmount": "1" } ] @@ -32683,211 +31732,183 @@ "maxAmount": "1" } ], - "ids": "2783", - "description": "", - "main": [ + "tertiary": [ { "minAmount": "1", - "weight": "50.0", - "id": "1367", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "1313", + "weight": "9973.0", + "id": "0", "maxAmount": "1" }, { "minAmount": "1", "weight": "25.0", - "id": "1361", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "1183", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1113", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1163", - "maxAmount": "1" - }, - { - "minAmount": "47", - "weight": "25.0", - "id": "556", - "maxAmount": "47" - }, - { - "minAmount": "10", - "weight": "25.0", - "id": "562", - "maxAmount": "10" - }, - { - "minAmount": "7", - "weight": "25.0", - "id": "565", - "maxAmount": "7" - }, - { - "minAmount": "3", - "weight": "25.0", - "id": "560", - "maxAmount": "5" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "199", - "maxAmount": "2" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "201", - "maxAmount": "2" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "203", - "maxAmount": "2" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "205", - "maxAmount": "2" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "207", - "maxAmount": "2" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "209", - "maxAmount": "2" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "211", - "maxAmount": "2" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "213", - "maxAmount": "2" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "215", - "maxAmount": "2" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "2485", - "maxAmount": "2" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "217", - "maxAmount": "2" - }, - { - "minAmount": "64", - "weight": "25.0", - "id": "995", - "maxAmount": "64" - }, - { - "minAmount": "95", - "weight": "25.0", - "id": "995", - "maxAmount": "95" - }, - { - "minAmount": "152", - "weight": "25.0", - "id": "995", - "maxAmount": "152" - }, - { - "minAmount": "220", - "weight": "25.0", - "id": "995", - "maxAmount": "220" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "121", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "385", - "maxAmount": "2" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1456", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "2361", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "10977", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", "id": "10976", "maxAmount": "1" }, { "minAmount": "1", - "weight": "26.6", - "id": "31", + "weight": "2.0", + "id": "10977", + "maxAmount": "1" + } + ], + "ids": "2783", + "description": "", + "main": [ + { + "minAmount": "1", + "weight": "16.0", + "id": "1313", "maxAmount": "1" }, { "minAmount": "1", - "weight": "2.13", + "weight": "12.0", + "id": "1367", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "8.0", + "id": "1361", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "4.0", + "id": "1183", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "4.0", + "id": "1113", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "4.0", + "id": "1163", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", "id": "11235", "maxAmount": "1" }, + { + "minAmount": "47", + "weight": "32.0", + "id": "556", + "maxAmount": "47" + }, + { + "minAmount": "10", + "weight": "28.0", + "id": "562", + "maxAmount": "10" + }, + { + "minAmount": "3", + "weight": "20.0", + "id": "560", + "maxAmount": "3" + }, + { + "minAmount": "7", + "weight": "16.0", + "id": "565", + "maxAmount": "7" + }, + { + "minAmount": "5", + "weight": "4.0", + "id": "560", + "maxAmount": "5" + }, { "minAmount": "1", - "weight": "150.0", - "id": "0", + "weight": "60.0", + "id": "14424", + "maxAmount": "1" + }, + { + "minAmount": "2", + "weight": "16.0", + "id": "14424", + "maxAmount": "2" + }, + { + "minAmount": "152", + "weight": "159.0", + "id": "995", + "maxAmount": "152" + }, + { + "minAmount": "64", + "weight": "24.0", + "id": "995", + "maxAmount": "64" + }, + { + "minAmount": "95", + "weight": "24.0", + "id": "995", + "maxAmount": "95" + }, + { + "minAmount": "220", + "weight": "20.0", + "id": "995", + "maxAmount": "220" + }, + { + "minAmount": "1", + "weight": "12.0", + "id": "385", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "8.0", + "id": "2361", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "4.0", + "id": "1456", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "4.0", + "id": "121", + "maxAmount": "1" + }, + { + "minAmount": "2", + "weight": "4.0", + "id": "385", + "maxAmount": "2" + }, + { + "minAmount": "1", + "weight": "4.0", + "id": "12070", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "12.0", + "id": "14426", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "12.0", + "id": "31", "maxAmount": "1" } ] @@ -33950,207 +32971,189 @@ "description": "", "main": [ { - "minAmount": "1", - "weight": "6.175", - "id": "3757", - "maxAmount": "1" + "minAmount": "25", + "weight": "10.0", + "id": "866", + "maxAmount": "50" }, { "minAmount": "1", - "weight": "6.175", - "id": "6724", + "weight": "7.0", + "id": "2489", "maxAmount": "1" }, { - "minAmount": "1", - "weight": "6.175", - "id": "6739", - "maxAmount": "1" + "minAmount": "5", + "weight": "5.0", + "id": "805", + "maxAmount": "10" + }, + { + "minAmount": "10", + "weight": "5.0", + "id": "810", + "maxAmount": "25" }, { "minAmount": "200", - "weight": "50.0", - "id": "884", - "maxAmount": "700" - }, - { - "minAmount": "50", - "weight": "50.0", - "id": "886", - "maxAmount": "250" - }, - { - "minAmount": "200", - "weight": "35.0", + "weight": "5.0", "id": "863", "maxAmount": "500" }, { "minAmount": "50", - "weight": "35.0", + "weight": "5.0", "id": "865", "maxAmount": "150" }, - { - "minAmount": "25", - "weight": "35.0", - "id": "866", - "maxAmount": "50" - }, - { - "minAmount": "5", - "weight": "25.0", - "id": "805", - "maxAmount": "10" - }, - { - "minAmount": "2", - "weight": "25.0", - "id": "9144", - "maxAmount": "12" - }, { "minAmount": "1", - "weight": "25.0", - "id": "2489", + "weight": "1.0", + "id": "3757", "maxAmount": "1" }, { "minAmount": "1", - "weight": "6.175", - "id": "6133", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "6.175", - "id": "6135", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "3758", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", + "weight": "1.0", "id": "3748", "maxAmount": "1" }, { "minAmount": "1", - "weight": "6.175", + "weight": "1.0", + "id": "3758", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "6724", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "6739", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", "id": "3749", "maxAmount": "1" }, { "minAmount": "1", - "weight": "8.0", - "id": "5281", + "weight": "1.0", + "id": "6133", "maxAmount": "1" }, { "minAmount": "1", - "weight": "8.0", - "id": "5297", + "weight": "1.0", + "id": "6135", "maxAmount": "1" }, { "minAmount": "1", - "weight": "8.0", - "id": "5296", + "weight": "1.0", + "id": "6733", "maxAmount": "1" }, + { + "minAmount": "2", + "weight": "5.0", + "id": "9144", + "maxAmount": "12" + }, + { + "minAmount": "50", + "weight": "5.0", + "id": "886", + "maxAmount": "250" + }, + { + "minAmount": "200", + "weight": "4.0", + "id": "884", + "maxAmount": "500" + }, { "minAmount": "1", - "weight": "8.0", - "id": "5298", + "weight": "7.0", + "id": "14428", "maxAmount": "1" }, { - "minAmount": "1", - "weight": "8.0", - "id": "5299", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "8.0", - "id": "5301", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "85.0", - "id": "5302", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "8.0", - "id": "5303", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "15.0", - "id": "1442", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "15.0", - "id": "1454", - "maxAmount": "1" - }, - { - "minAmount": "900", - "weight": "75.0", + "minAmount": "156", + "weight": "10.0", "id": "995", - "maxAmount": "900" + "maxAmount": "156" }, { "minAmount": "1", - "weight": "50.0", - "id": "45", - "maxAmount": "3" - }, - { - "minAmount": "1", - "weight": "50.0", + "weight": "6.0", "id": "413", "maxAmount": "1" }, + { + "minAmount": "10", + "weight": "5.0", + "id": "45", + "maxAmount": "30" + }, + { + "minAmount": "5", + "weight": "5.0", + "id": "385", + "maxAmount": "5" + }, { "minAmount": "1", - "weight": "25.0", + "weight": "5.0", "id": "207", "maxAmount": "1" }, { - "minAmount": "1", - "weight": "25.0", - "id": "314", - "maxAmount": "200" + "minAmount": "50", + "weight": "5.0", + "id": "1518", + "maxAmount": "150" + }, + { + "minAmount": "50", + "weight": "3.0", + "id": "1516", + "maxAmount": "150" }, { "minAmount": "1", - "weight": "6.175", + "weight": "2.0", + "id": "9431", + "maxAmount": "1" + }, + { + "minAmount": "250", + "weight": "1.0", + "id": "314", + "maxAmount": "500" + }, + { + "minAmount": "1", + "weight": "1.0", "id": "12070", "maxAmount": "1" }, { "minAmount": "1", - "weight": "15.0", - "id": "31", + "weight": "10.0", + "id": "14426", "maxAmount": "1" }, { "minAmount": "1", - "weight": "6.175", - "id": "6733", + "weight": "8.0", + "id": "31", "maxAmount": "1" } ] @@ -34176,195 +33179,195 @@ "main": [ { "minAmount": "1", - "weight": "50.0", - "id": "1397", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", + "weight": "10.0", "id": "1399", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", + "weight": "5.0", "id": "1395", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0743", - "id": "6562", + "weight": "4.0", + "id": "1397", "maxAmount": "1" }, { - "minAmount": "10", - "weight": "10.0", + "minAmount": "1", + "weight": "1.0", "id": "1392", "maxAmount": "10" }, { "minAmount": "1", - "weight": "5.0743", - "id": "6739", + "weight": "1.0", + "id": "3757", "maxAmount": "1" }, { "minAmount": "1", - "weight": "15.0", - "id": "1438", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "15.0", - "id": "1440", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "15.0", - "id": "1444", - "maxAmount": "1" - }, - { - "minAmount": "155", - "weight": "50.0", - "id": "556", - "maxAmount": "155" - }, - { - "minAmount": "32", - "weight": "50.0", - "id": "4698", - "maxAmount": "32" - }, - { - "minAmount": "77", - "weight": "15.0", - "id": "560", - "maxAmount": "77" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "3758", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", + "weight": "1.0", "id": "3748", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0743", + "weight": "1.0", + "id": "3758", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "6562", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "6739", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "3755", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", "id": "6139", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0743", + "weight": "1.0", "id": "6141", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0743", - "id": "3755", + "weight": "1.0", + "id": "6731", "maxAmount": "1" }, { - "minAmount": "900", - "weight": "75.0", - "id": "995", - "maxAmount": "900" + "minAmount": "100", + "weight": "6.0", + "id": "556", + "maxAmount": "200" + }, + { + "minAmount": "50", + "weight": "5.0", + "id": "557", + "maxAmount": "100" + }, + { + "minAmount": "25", + "weight": "2.0", + "id": "565", + "maxAmount": "75" + }, + { + "minAmount": "25", + "weight": "2.0", + "id": "563", + "maxAmount": "75" + }, + { + "minAmount": "25", + "weight": "2.0", + "id": "561", + "maxAmount": "75" + }, + { + "minAmount": "25", + "weight": "2.0", + "id": "560", + "maxAmount": "75" + }, + { + "minAmount": "25", + "weight": "2.0", + "id": "4698", + "maxAmount": "75" + }, + { + "minAmount": "1", + "weight": "7.0", + "id": "14428", + "maxAmount": "1" + }, + { + "minAmount": "25", + "weight": "10.0", + "id": "1441", + "maxAmount": "75" + }, + { + "minAmount": "25", + "weight": "7.0", + "id": "1439", + "maxAmount": "75" + }, + { + "minAmount": "25", + "weight": "7.0", + "id": "1445", + "maxAmount": "75" + }, + { + "minAmount": "5", + "weight": "10.0", + "id": "385", + "maxAmount": "5" + }, + { + "minAmount": "1", + "weight": "5.0", + "id": "413", + "maxAmount": "1" }, { "minAmount": "150", - "weight": "50.0", + "weight": "5.0", "id": "7937", "maxAmount": "150" }, { "minAmount": "1", - "weight": "25.0", + "weight": "5.0", "id": "207", "maxAmount": "1" }, + { + "minAmount": "972", + "weight": "3.0", + "id": "995", + "maxAmount": "972" + }, { "minAmount": "1", - "weight": "5.0743", + "weight": "1.0", "id": "12070", "maxAmount": "1" }, { "minAmount": "1", - "weight": "8.0", - "id": "5281", + "weight": "10.0", + "id": "14426", "maxAmount": "1" }, { "minAmount": "1", "weight": "8.0", - "id": "5297", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "8.0", - "id": "5296", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "8.0", - "id": "5298", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "8.0", - "id": "5299", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "8.0", - "id": "5301", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "8.0", - "id": "5302", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "8.0", - "id": "5303", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "15.0", "id": "31", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0743", - "id": "6731", - "maxAmount": "1" - }, - { - "minAmount": "97", - "weight": "15.0", - "id": "565", - "maxAmount": "97" } ] }, @@ -34389,242 +33392,224 @@ "main": [ { "minAmount": "1", - "weight": "25.0", - "id": "3757", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "1343", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1357", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1315", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1359", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "6.4048", - "id": "6739", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", + "weight": "17.0", "id": "1193", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "11.0", + "id": "1343", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "7.0", + "id": "1357", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "4.0", "id": "1119", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "1123", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "3758", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "3748", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "6.4048", - "id": "6737", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "6.4048", - "id": "6735", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "6.4048", - "id": "2570", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "6.4048", - "id": "6129", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "6.4048", - "id": "6130", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "147", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "15.0", - "id": "159", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "15.0", - "id": "165", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "15.0", - "id": "141", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "15.0", - "id": "191", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "15.0", - "id": "129", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "2361", - "maxAmount": "1" - }, - { - "minAmount": "25", - "weight": "15.0", - "id": "448", - "maxAmount": "25" - }, - { - "minAmount": "100", - "weight": "15.0", - "id": "454", - "maxAmount": "100" - }, - { - "minAmount": "50", - "weight": "15.0", - "id": "441", - "maxAmount": "50" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "2353", - "maxAmount": "1" - }, - { - "minAmount": "5", - "weight": "15.0", - "id": "365", - "maxAmount": "5" - }, - { - "minAmount": "5", - "weight": "15.0", - "id": "373", - "maxAmount": "5" - }, - { - "minAmount": "5", - "weight": "15.0", - "id": "385", - "maxAmount": "5" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "207", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "15.0", - "id": "1446", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "15.0", - "id": "1440", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "15.0", - "id": "1448", - "maxAmount": "1" - }, - { - "minAmount": "900", - "weight": "75.0", - "id": "995", - "maxAmount": "900" - }, - { - "minAmount": "1", - "weight": "15.0", - "id": "31", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "6.4048", - "id": "12070", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1355", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", + "weight": "3.0", "id": "1273", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "1365", + "weight": "2.0", + "id": "1123", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "2.0", + "id": "3757", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "1359", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "6739", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "3748", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "3758", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "1315", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "2570", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "6129", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "6130", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "6737", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "6735", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "2456", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "141", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "129", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "147", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "159", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "165", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "191", + "maxAmount": "1" + }, + { + "minAmount": "25", + "weight": "10.0", + "id": "448", + "maxAmount": "25" + }, + { + "minAmount": "1", + "weight": "3.0", + "id": "2361", + "maxAmount": "1" + }, + { + "minAmount": "100", + "weight": "2.0", + "id": "454", + "maxAmount": "100" + }, + { + "minAmount": "150", + "weight": "1.0", + "id": "441", + "maxAmount": "150" + }, + { + "minAmount": "15", + "weight": "1.0", + "id": "2354", + "maxAmount": "30" + }, + { + "minAmount": "156", + "weight": "10.0", + "id": "995", + "maxAmount": "156" + }, + { + "minAmount": "1", + "weight": "7.0", + "id": "207", + "maxAmount": "1" + }, + { + "minAmount": "5", + "weight": "7.0", + "id": "365", + "maxAmount": "5" + }, + { + "minAmount": "5", + "weight": "4.0", + "id": "373", + "maxAmount": "5" + }, + { + "minAmount": "5", + "weight": "1.0", + "id": "385", + "maxAmount": "5" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "12070", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "10.0", + "id": "14426", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "8.0", + "id": "31", "maxAmount": "1" } ] @@ -34943,260 +33928,196 @@ "maxAmount": "2" } ], + "tertiary": [ + { + "minAmount": "1", + "weight": "9999.0", + "id": "0", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "11286", + "maxAmount": "1" + } + ], "ids": "3068,3069,3070,3071", "description": "", "main": [ { "minAmount": "1", - "weight": "50.0", - "id": "1285", + "weight": "16.0", + "id": "1283", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", + "weight": "8.0", "id": "1361", "maxAmount": "1" }, { "minAmount": "1", - "weight": "25.0", - "id": "1357", + "weight": "8.0", + "id": "6324", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", + "weight": "4.0", "id": "1399", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", + "weight": "1.0", "id": "6809", "maxAmount": "1" }, + { + "minAmount": "2", + "weight": "8.0", + "id": "8882", + "maxAmount": "12" + }, + { + "minAmount": "2", + "weight": "8.0", + "id": "9142", + "maxAmount": "12" + }, + { + "minAmount": "2", + "weight": "8.0", + "id": "9144", + "maxAmount": "12" + }, + { + "minAmount": "10", + "weight": "28.0", + "id": "566", + "maxAmount": "10" + }, + { + "minAmount": "30", + "weight": "16.0", + "id": "556", + "maxAmount": "30" + }, + { + "minAmount": "30", + "weight": "16.0", + "id": "555", + "maxAmount": "30" + }, + { + "minAmount": "7", + "weight": "16.0", + "id": "565", + "maxAmount": "7" + }, + { + "minAmount": "3", + "weight": "4.0", + "id": "563", + "maxAmount": "3" + }, + { + "minAmount": "1", + "weight": "48.0", + "id": "14424", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "16.0", + "id": "14422", + "maxAmount": "1" + }, + { + "minAmount": "132", + "weight": "139.0", + "id": "995", + "maxAmount": "132" + }, + { + "minAmount": "220", + "weight": "36.0", + "id": "995", + "maxAmount": "220" + }, + { + "minAmount": "30", + "weight": "28.0", + "id": "995", + "maxAmount": "30" + }, + { + "minAmount": "44", + "weight": "24.0", + "id": "995", + "maxAmount": "44" + }, + { + "minAmount": "460", + "weight": "4.0", + "id": "995", + "maxAmount": "460" + }, { "minAmount": "60", - "weight": "25.0", + "weight": "20.0", "id": "7937", "maxAmount": "60" }, - { - "minAmount": "11", - "weight": "25.0", - "id": "556", - "maxAmount": "64" - }, - { - "minAmount": "13", - "weight": "25.0", - "id": "555", - "maxAmount": "33" - }, - { - "minAmount": "10", - "weight": "25.0", - "id": "562", - "maxAmount": "10" - }, - { - "minAmount": "2", - "weight": "25.0", - "id": "563", - "maxAmount": "5" - }, - { - "minAmount": "2", - "weight": "25.0", - "id": "565", - "maxAmount": "11" - }, - { - "minAmount": "5", - "weight": "5.0", - "id": "566", - "maxAmount": "14" - }, - { - "minAmount": "2", - "weight": "25.0", - "id": "8882", - "maxAmount": "26" - }, - { - "minAmount": "8", - "weight": "50.0", - "id": "9142", - "maxAmount": "32" - }, - { - "minAmount": "7", - "weight": "50.0", - "id": "9143", - "maxAmount": "29" - }, - { - "minAmount": "5", - "weight": "50.0", - "id": "9144", - "maxAmount": "22" - }, { "minAmount": "1", - "weight": "25.0", - "id": "2485", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5301", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5303", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5300", - "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": "5292", - "maxAmount": "1" - }, - { - "minAmount": "3", - "weight": "25.0", - "id": "5295", - "maxAmount": "3" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5323", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5293", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5321", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5105", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5311", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5296", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "5294", - "maxAmount": "1" - }, - { - "minAmount": "20", - "weight": "50.0", - "id": "995", - "maxAmount": "420" - }, - { - "minAmount": "3", - "weight": "25.0", - "id": "379", - "maxAmount": "3" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "365", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "149", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "133", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", + "weight": "4.0", "id": "9463", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "5680", + "weight": "8.0", + "id": "379", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", + "weight": "4.0", + "id": "1454", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "4.0", + "id": "1452", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "4.0", + "id": "133", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "4.0", + "id": "12070", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "20.0", + "id": "14426", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "8.0", "id": "31", "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "0.110511", - "id": "11286", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "100.0", - "id": "0", - "maxAmount": "1" } ] }, @@ -36875,14 +35796,7 @@ ] }, { - "default": [ - { - "minAmount": "1", - "weight": "100.0", - "id": "592", - "maxAmount": "1" - } - ], + "default": [], "charm": [ { "minAmount": "1", @@ -36918,238 +35832,46 @@ "ids": "3406,6217,7714,7715", "description": "", "main": [ - { - "minAmount": "2", - "weight": "50.0", - "id": "882", - "maxAmount": "9" - }, { "minAmount": "1", - "weight": "50.0", - "id": "877", - "maxAmount": "10" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "1155", + "weight": "111.0", + "id": "0", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", - "id": "1351", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "1139", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "1277", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "1265", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "884", + "weight": "4.0", + "id": "555", "maxAmount": "5" }, { "minAmount": "1", - "weight": "50.0", - "id": "1101", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "1349", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "1267", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", - "id": "886", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1141", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "1353", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "1269", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "1381", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "1383", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "288", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1993", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "1913", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "1891", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "1917", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "6794", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", + "weight": "1.0", "id": "556", - "maxAmount": "10" + "maxAmount": "5" }, { "minAmount": "1", - "weight": "50.0", - "id": "555", - "maxAmount": "11" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "562", - "maxAmount": "6" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "560", - "maxAmount": "2" - }, - { - "minAmount": "1", - "weight": "50.0", + "weight": "4.0", "id": "995", - "maxAmount": "40" + "maxAmount": "30" }, { "minAmount": "1", - "weight": "50.0", - "id": "2355", + "weight": "4.0", + "id": "995", + "maxAmount": "20" + }, + { + "minAmount": "1", + "weight": "3.0", + "id": "14426", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", - "id": "442", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1718", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "25.0", - "id": "1444", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "5733", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "6691", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "193", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "1716", - "maxAmount": "1" - }, - { - "minAmount": "10", - "weight": "5.0", - "id": "2", - "maxAmount": "40" - }, - { - "minAmount": "1", - "weight": "5.0", - "id": "31", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "100.0", - "id": "0", + "weight": "1.0", + "id": "1", "maxAmount": "1" } ] @@ -38439,7 +37161,7 @@ { "minAmount": "4", "weight": "25.0", - "id": "4150", + "id": "4160", "maxAmount": "5" }, { @@ -39045,12 +37767,6 @@ "id": "1129", "maxAmount": "1" }, - { - "minAmount": "1", - "weight": "5.0", - "id": "4170", - "maxAmount": "1" - }, { "minAmount": "2", "weight": "50.0", @@ -41087,7 +39803,20 @@ ] }, { - "default": [], + "default": [ + { + "minAmount": "1", + "weight": "1.0", + "id": "532", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "9080", + "maxAmount": "1" + } + ], "charm": [ { "minAmount": "1", @@ -44723,280 +43452,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": [], @@ -46771,7 +45226,7 @@ { "minAmount": "1", "weight": "5.0", - "id": "5733", + "id": "12070", "maxAmount": "1" }, { @@ -46887,7 +45342,7 @@ { "minAmount": "1", "weight": "5.0", - "id": "1", + "id": "12070", "maxAmount": "1" } ] @@ -48532,10 +46987,10 @@ "maxAmount": "2" }, { - "minAmount": "3", + "minAmount": "1", "weight": "500.0", "id": "7054", - "maxAmount": "1" + "maxAmount": "3" }, { "minAmount": "1", @@ -56130,30 +54585,44 @@ "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": "7078", "description": "", "main": [ { "minAmount": "1", - "weight": "50.0", - "id": "12570", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "50.0", + "weight": "2.0", "id": "1239", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", - "id": "1", + "weight": "2.0", + "id": "12570", "maxAmount": "1" }, { "minAmount": "1", - "weight": "100.0", + "weight": "124.0", "id": "0", "maxAmount": "1" } @@ -56200,30 +54669,38 @@ "maxAmount": "1" } ], - "ids": "7079,7080", - "description": "", - "main": [ + "tertiary": [ { "minAmount": "1", - "weight": "50.0", - "id": "1239", + "weight": "9973.0", + "id": "0", "maxAmount": "1" }, { "minAmount": "1", - "weight": "50.0", - "id": "12570", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", + "weight": "25.0", "id": "10976", "maxAmount": "1" }, { "minAmount": "1", - "weight": "100.0", + "weight": "2.0", + "id": "10977", + "maxAmount": "1" + } + ], + "ids": "7079,7081", + "description": "", + "main": [ + { + "minAmount": "1", + "weight": "2.0", + "id": "12570", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "126.0", "id": "0", "maxAmount": "1" } @@ -56270,36 +54747,38 @@ "maxAmount": "1" } ], - "ids": "7081,7082", - "description": "", - "main": [ + "tertiary": [ { "minAmount": "1", - "weight": "25.0", - "id": "12570", + "weight": "9973.0", + "id": "0", "maxAmount": "1" }, { "minAmount": "1", "weight": "25.0", - "id": "1239", - "maxAmount": "1" - }, - { - "minAmount": "1", - "weight": "5.0", "id": "10976", "maxAmount": "1" }, { "minAmount": "1", - "weight": "5.0", + "weight": "2.0", "id": "10977", "maxAmount": "1" + } + ], + "ids": "7080,7082", + "description": "", + "main": [ + { + "minAmount": "1", + "weight": "2.0", + "id": "1239", + "maxAmount": "1" }, { "minAmount": "1", - "weight": "100.0", + "weight": "126.0", "id": "0", "maxAmount": "1" } @@ -59084,7 +57563,7 @@ { "minAmount": "1", "weight": "100.0", - "id": "3179", + "id": "526", "maxAmount": "1" } ], @@ -59103,7 +57582,7 @@ } ], "charm": [], - "ids": "1904,6368,6367,912,913,914,255,256,27,34,35,689,690,691,3814,3816,4402,4403", + "ids": "1904,6368,6367,912,913,914,253,254,255,256,258,27,34,35,689,690,691,3814,3816,4402,4403", "description": "", "main": [ { @@ -59913,7 +58392,7 @@ { "minAmount": "1", "weight": "1.0", - "id": "527", + "id": "526", "maxAmount": "1" } ], @@ -59958,7 +58437,7 @@ { "minAmount": "1", "weight": "1.0", - "id": "617", + "id": "995", "maxAmount": "25" }, { @@ -59974,5 +58453,387 @@ "maxAmount": "1" } ] + }, + { + "default": [ + { + "minAmount": "1", + "weight": "1.0", + "id": "526", + "maxAmount": "1" + } + ], + "charm": [], + "ids": "447,448,449,917", + "description": "Jail Guard", + "main": [] + }, + { + "default": [ + { + "minAmount": "1", + "weight": "1.0", + "id": "526", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "2337", + "maxAmount": "1" + } + ], + "charm": [], + "ids": "48", + "description": "", + "main": [] + }, + { + "default": [ + { + "minAmount": "1", + "weight": "1.0", + "id": "526", + "maxAmount": "1" + } + ], + "charm": [], + "ids": "76", + "description": "Entrana Zombie", + "main": [ + { + "minAmount": "1", + "weight": "4.0", + "id": "1139", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "1291", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "50.0", + "id": "1351", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "1349", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "4.0", + "id": "556", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "4.0", + "id": "882", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "561", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "14424", + "maxAmount": "1" + }, + { + "minAmount": "5", + "weight": "50.0", + "id": "313", + "maxAmount": "5" + }, + { + "minAmount": "18", + "weight": "3.0", + "id": "995", + "maxAmount": "18" + }, + { + "minAmount": "28", + "weight": "2.0", + "id": "995", + "maxAmount": "28" + }, + { + "minAmount": "1", + "weight": "7.0", + "id": "0", + "maxAmount": "1" + } + ] + }, + { + "default": [ + { + "minAmount": "1", + "weight": "1.0", + "id": "526", + "maxAmount": "1" + } + ], + "charm": [], + "ids": "1993", + "description": "", + "main": [] + }, + { + "default": [ + { + "minAmount": "1", + "weight": "1.0", + "id": "532", + "maxAmount": "1" + } + ], + "charm": [], + "ids": "6779", + "description": "", + "main": [ + { + "minAmount": "1", + "weight": "0.25", + "id": "10976", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "0.02", + "id": "10977", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "99.73", + "id": "0", + "maxAmount": "1" + } + ] + }, + { + "default": [], + "charm": [], + "ids": "1832", + "description": "Cave Bug (Level 6)", + "main": [ + { + "minAmount": "8", + "weight": "5.0", + "id": "555", + "maxAmount": "8" + }, + { + "minAmount": "1", + "weight": "5.0", + "id": "561", + "maxAmount": "1" + }, + { + "minAmount": "6", + "weight": "2.0", + "id": "557", + "maxAmount": "6" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "561", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "23.0", + "id": "14424", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "2.0", + "id": "235", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "2.0", + "id": "221", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "2.0", + "id": "223", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "225", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "231", + "maxAmount": "1" + }, + { + "minAmount": "3", + "weight": "8.0", + "id": "995", + "maxAmount": "3" + }, + { + "minAmount": "1", + "weight": "64.0", + "id": "0", + "maxAmount": "1" + }, + { + "minAmount": "8", + "weight": "3.0", + "id": "995", + "maxAmount": "8" + }, + { + "minAmount": "1", + "weight": "5.0", + "id": "36", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "3.0", + "id": "590", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "4527", + "maxAmount": "1" + } + ] + }, + { + "default": [ + { + "minAmount": "1", + "weight": "1.0", + "id": "526", + "maxAmount": "1" + } + ], + "charm": [], + "ids": "3057,3059,3058,3060,3061,3062,3063,3064,3065,3066", + "description": "Champion's Challenge NPC - activity", + "main": [] + }, + { + "default": [], + "charm": [], + "ids": "2459,2460,2461,2462", + "description": "Pheasant NPC (level 4) - Freaky Forester - Random event", + "main": [ + { + "minAmount": "1", + "weight": "1.0", + "id": "6178", + "maxAmount": "1" + } + ] + }, + { + "default": [ + { + "minAmount": "1", + "weight": "1.0", + "id": "526", + "maxAmount": "1" + }, + { + "minAmount": "1", + "weight": "1.0", + "id": "4184", + "maxAmount": "1" + } + ], + "charm": [], + "ids": "1676", + "description": "Experiment (level 51)", + "main": [] + }, + { + "default": [ + { + "minAmount": "1", + "weight": "1.0", + "id": "526", + "maxAmount": "1" + } + ], + "charm": [], + "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 2e765ce2e..ca2b6f773 100644 --- a/Server/data/configs/ground_spawns.json +++ b/Server/data/configs/ground_spawns.json @@ -1,618 +1,670 @@ [ { - "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": "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,196610}-{1,2908,9805,0,196610}-{1,2902,9806,0,196610}-{1,2906,9796,0,196610}-{1,2905,9801,0,196610}" - }, - { - "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}" - }, - { - "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,2840,2974,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}" - }, - { - "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}" - }, - { - "item_id": "564", - "loc_data": "{3,2947,3898,0,19661000}" - }, - { - "item_id": "590", - "loc_data": "{1,3112,3369,2,10485900}-{1,3209,3734,0,13107300}" - }, - { - "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}" - }, - { - "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,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": "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,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}" - }, - { - "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": "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": "2351", - "loc_data": "{1,3062,3657,0,11141270}" - }, - { - "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": "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": "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,3106,3956,0,60}-{1,2566,9526,0,30}-{1,3215,9625,0,80}-{1,3218,9887,0,33}-{1,2700,3407,0,100}-" + }, + { + "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": "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,3463,9478,2,45}-{1,3461,9480,2,45}-{1,3461,9482,2,45}-{1,3461,9484,2,45}-" + }, + { + "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": "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 bb3d2219d..95087003a 100644 --- a/Server/data/configs/interface_configs.json +++ b/Server/data/configs/interface_configs.json @@ -1,7 +1,7 @@ [ { "id": "5", - "interfaceType": "1", + "interfaceType": "8", "walkable": "true", "tabIndex": "-1" }, @@ -25,7 +25,49 @@ }, { "id": "24", - "interfaceType": "1", + "interfaceType": "8", + "walkable": "true", + "tabIndex": "-1" + }, + { + "id": "34", + "interfaceType": "8", + "walkable": "true", + "tabIndex": "-1" + }, + { + "id": "38", + "interfaceType": "8", + "walkable": "true", + "tabIndex": "-1" + }, + { + "id": "42", + "interfaceType": "8", + "walkable": "true", + "tabIndex": "-1" + }, + { + "id": "46", + "interfaceType": "8", + "walkable": "true", + "tabIndex": "-1" + }, + { + "id": "57", + "interfaceType": "8", + "walkable": "true", + "tabIndex": "-1" + }, + { + "id": "58", + "interfaceType": "8", + "walkable": "true", + "tabIndex": "-1" + }, + { + "id": "59", + "interfaceType": "8", "walkable": "true", "tabIndex": "-1" }, @@ -53,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", @@ -125,6 +191,12 @@ "walkable": "true", "tabIndex": "-1" }, + { + "id": "139", + "interfaceType": "8", + "walkable": "true", + "tabIndex": "-1" + }, { "id": "140", "interfaceType": "4", @@ -137,6 +209,12 @@ "walkable": "true", "tabIndex": "3" }, + { + "id": "169", + "interfaceType": "8", + "walkable": "true", + "tabIndex": "-1" + }, { "id": "172", "interfaceType": "4", @@ -197,9 +275,33 @@ "walkable": "true", "tabIndex": "6" }, + { + "id": "194", + "interfaceType": "8", + "walkable": "true", + "tabIndex": "-1" + }, + { + "id": "195", + "interfaceType": "8", + "walkable": "true", + "tabIndex": "-1" + }, + { + "id": "196", + "interfaceType": "8", + "walkable": "true", + "tabIndex": "-1" + }, { "id": "198", - "interfaceType": "1", + "interfaceType": "8", + "walkable": "true", + "tabIndex": "-1" + }, + { + "id": "209", + "interfaceType": "8", "walkable": "true", "tabIndex": "-1" }, @@ -257,6 +359,12 @@ "walkable": "false", "tabIndex": "-1" }, + { + "id": "240", + "interfaceType": "3", + "walkable": "true", + "tabIndex": "-1" + }, { "id": "241", "interfaceType": "4", @@ -281,6 +389,36 @@ "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", + "walkable": "true", + "tabIndex": "-1" + }, { "id": "259", "interfaceType": "2", @@ -371,6 +509,12 @@ "walkable": "false", "tabIndex": "-1" }, + { + "id": "328", + "interfaceType": "8", + "walkable": "true", + "tabIndex": "-1" + }, { "id": "336", "interfaceType": "3", @@ -395,6 +539,12 @@ "walkable": "true", "tabIndex": "-1" }, + { + "id": "377", + "interfaceType": "8", + "walkable": "true", + "tabIndex": "-1" + }, { "id": "380", "interfaceType": "1", @@ -449,12 +599,24 @@ "walkable": "true", "tabIndex": "4" }, + { + "id": "418", + "interfaceType": "8", + "walkable": "true", + "tabIndex": "-1" + }, { "id": "421", "interfaceType": "4", "walkable": "false", "tabIndex": "-1" }, + { + "id": "428", + "interfaceType": "8", + "walkable": "true", + "tabIndex": "-1" + }, { "id": "430", "interfaceType": "2", @@ -473,6 +635,30 @@ "walkable": "true", "tabIndex": "-1" }, + { + "id": "485", + "interfaceType": "8", + "walkable": "true", + "tabIndex": "-1" + }, + { + "id": "486", + "interfaceType": "8", + "walkable": "true", + "tabIndex": "-1" + }, + { + "id": "487", + "interfaceType": "8", + "walkable": "true", + "tabIndex": "-1" + }, + { + "id": "488", + "interfaceType": "8", + "walkable": "true", + "tabIndex": "-1" + }, { "id": "498", "interfaceType": "0", @@ -493,7 +679,7 @@ }, { "id": "532", - "interfaceType": "1", + "interfaceType": "8", "walkable": "true", "tabIndex": "-1" }, @@ -605,9 +791,15 @@ "walkable": "false", "tabIndex": "-1" }, + { + "id": "653", + "interfaceType": "8", + "walkable": "true", + "tabIndex": "-1" + }, { "id": "656", - "interfaceType": "1", + "interfaceType": "8", "walkable": "true", "tabIndex": "-1" }, @@ -721,7 +913,7 @@ }, { "id": "809", - "interfaceType": "1", + "interfaceType": "8", "walkable": "true", "tabIndex": "-1" } diff --git a/Server/data/configs/item_configs.json b/Server/data/configs/item_configs.json index b2ee7cdf8..3c29faaa8 100644 --- a/Server/data/configs/item_configs.json +++ b/Server/data/configs/item_configs.json @@ -702,29 +702,33 @@ }, { "remove_head": "true", - "destroy_message": "I can borrow another from the armoury near the Fight Arena.", "shop_price": "10", "examine": "A helmet, as worn by the minions of General Khazard.", "durability": null, - "name": "Khazard helmet", "weight": "2.7", + "destroy": "true", + "equipment_slot": "0", + "destroy_message": "I can borrow another from the armoury near the Fight Arena.", + "name": "Khazard helmet", + "tradeable": "false", "archery_ticket_price": "0", "id": "74", - "bonuses": "0,0,0,0,0,4,5,3,0,0,0,0,0,0,0", - "equipment_slot": "0" + "bonuses": "0,0,0,0,0,4,5,3,0,0,0,0,0,0,0" }, { - "destroy_message": "I can borrow another from the armoury near the Fight Arena.", - "remove_sleeves": "true", "shop_price": "12", "examine": "Armour, as worn by the minions of General Khazard.", "durability": null, - "name": "Khazard armour", "weight": "0.5", + "destroy": "true", + "equipment_slot": "4", + "destroy_message": "I can borrow another from the armoury near the Fight Arena.", + "remove_sleeves": "true", + "name": "Khazard armour", + "tradeable": "false", "archery_ticket_price": "0", "id": "75", - "bonuses": "0,0,0,0,0,9,11,10,0,0,0,0,0,0,0", - "equipment_slot": "4" + "bonuses": "0,0,0,0,0,9,11,10,0,0,0,0,0,0,0" }, { "destroy_message": "The lazy guard probably has a spare set...", @@ -761,6 +765,13 @@ "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,16", "equipment_slot": "13" }, + { + "examine": "A lever to open something perhaps?", + "durability": null, + "name": "Lever", + "archery_ticket_price": "0", + "id": "83" + }, { "destroy_message": "You can reclaim this item from the place you found it.", "examine": "The power in this staff causes it to vibrate gently.", @@ -2759,7 +2770,7 @@ "id": "291" }, { - "examine": "A book on elven history in northern RuneScape.", + "examine": "A book on elven history in northern 2009Scape.", "durability": null, "name": "Book on baxtorian", "archery_ticket_price": "0", @@ -2906,7 +2917,7 @@ "requirements": "{20,10}", "shop_price": "5", "ge_buy_limit": "100", - "examine": "Useful for catching salmon or trout.In Daemonhiem: Just add feathers.", + "examine": "Useful for catching salmon or trout.", "grand_exchange_price": "123", "durability": null, "name": "Fly fishing rod", @@ -3419,7 +3430,7 @@ }, { "ge_buy_limit": "20000", - "examine": "Wow, this is a big fish.", + "examine": "I should try cooking this.", "grand_exchange_price": "490", "durability": null, "name": "Raw bass", @@ -3581,6 +3592,7 @@ { "ge_buy_limit": "20000", "grand_exchange_price": "1615", + "examine": "", "durability": null, "name": "Raw shark", "tradeable": "true", @@ -3616,7 +3628,7 @@ }, { "ge_buy_limit": "20000", - "examine": "Tastier when cooked.", + "examine": "A rare catch.", "grand_exchange_price": "2114", "durability": null, "name": "Raw manta ray", @@ -3663,7 +3675,7 @@ }, { "ge_buy_limit": "20000", - "examine": "Looks wrinkly.", + "examine": "A rare catch.", "grand_exchange_price": "2397", "durability": null, "name": "Raw sea turtle", @@ -4231,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" }, { @@ -4906,7 +4926,7 @@ }, { "ge_buy_limit": "10000", - "examine": "These would feed a dog for months.", + "examine": "These would feed a dog for months!", "grand_exchange_price": "1300", "durability": null, "name": "Dragon bones", @@ -5032,7 +5052,7 @@ { "shop_price": "1", "ge_buy_limit": "100", - "examine": "Issued by RuneScape Council to all new citizens.", + "examine": "Issued by 2009Scape Council to all new citizens.", "grand_exchange_price": "123", "durability": null, "name": "Newcomer map", @@ -5535,7 +5555,7 @@ }, { "ge_buy_limit": "100", - "examine": "A book on the history of astronomy in RuneScape.", + "examine": "A book on the history of astronomy in 2009Scape.", "grand_exchange_price": "658", "durability": null, "name": "Astronomy book", @@ -5699,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", @@ -5707,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", @@ -6224,6 +6244,7 @@ "attack_speed": "5", "weapon_interface": "6", "turn180_anim": "820", + "equip_audio": "2248", "render_anim": "292", "defence_anim": "397", "equipment_slot": "3", @@ -6527,11 +6548,29 @@ "archery_ticket_price": "0", "id": "704" }, + { + "shop_price": "1", + "examine": "A mixture of strong chemicals.", + "durability": null, + "name": "Mixed chemicals", + "weight": "0.03", + "archery_ticket_price": "0", + "id": "705" + }, + { + "shop_price": "1", + "examine": "A mixture of strong chemicals.", + "durability": null, + "name": "Mixed chemicals", + "weight": "0.035", + "archery_ticket_price": "0", + "id": "706" + }, { "examine": "A mixture of strong chemicals.", "durability": null, "name": "Chemical compound", - "weight": "2", + "weight": "0.035", "archery_ticket_price": "0", "id": "707" }, @@ -6812,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", @@ -6960,6 +7001,7 @@ "attack_speed": "6", "weapon_interface": "17", "turn180_anim": "4227", + "equip_audio": "2244", "render_anim": "175", "defence_anim": "424", "equipment_slot": "3", @@ -7010,6 +7052,7 @@ "attack_speed": "4", "weapon_interface": "1", "turn180_anim": "1206", + "equip_audio": "2247", "render_anim": "28", "defence_anim": "420", "equipment_slot": "3", @@ -7235,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", @@ -7252,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", @@ -7270,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", @@ -7288,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", @@ -7306,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", @@ -7325,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", @@ -7341,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", @@ -7358,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", @@ -7376,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", @@ -7394,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", @@ -7412,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", @@ -7430,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", @@ -7447,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", @@ -7464,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", @@ -7482,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", @@ -7500,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", @@ -7518,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", @@ -7536,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", @@ -7553,6 +7620,7 @@ "name": "Poisoned dart(p)", "archery_ticket_price": "0", "id": "818", + "equip_audio": "2244", "equipment_slot": "3" }, { @@ -7628,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", @@ -7645,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", @@ -7663,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", @@ -7681,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", @@ -7699,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", @@ -7717,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", @@ -7734,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", @@ -7751,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", @@ -7769,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", @@ -7787,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", @@ -7805,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", @@ -7823,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", @@ -7856,6 +7936,7 @@ "durability": null, "weight": "8", "weapon_interface": "17", + "equip_audio": "2244", "render_anim": "175", "attack_audios": "2700,0,0,0", "name": "Crossbow" @@ -7893,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" @@ -7930,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" @@ -7953,6 +8036,7 @@ "attack_speed": "4", "two_handed": "true", "weapon_interface": "16", + "equip_audio": "2244", "render_anim": "2588", "defence_anim": "424", "equipment_slot": "3", @@ -7984,6 +8068,7 @@ "attack_speed": "6", "two_handed": "true", "weapon_interface": "16", + "equip_audio": "2244", "render_anim": "2588", "defence_anim": "424", "equipment_slot": "3", @@ -8015,6 +8100,7 @@ "attack_speed": "6", "two_handed": "true", "weapon_interface": "16", + "equip_audio": "2244", "render_anim": "2588", "defence_anim": "424", "equipment_slot": "3", @@ -8046,6 +8132,7 @@ "attack_speed": "4", "two_handed": "true", "weapon_interface": "16", + "equip_audio": "2244", "render_anim": "2588", "defence_anim": "424", "equipment_slot": "3", @@ -8077,6 +8164,7 @@ "attack_speed": "6", "two_handed": "true", "weapon_interface": "16", + "equip_audio": "2244", "render_anim": "2588", "defence_anim": "424", "equipment_slot": "3", @@ -8109,6 +8197,7 @@ "attack_speed": "4", "two_handed": "true", "weapon_interface": "16", + "equip_audio": "2244", "render_anim": "2588", "defence_anim": "424", "equipment_slot": "3", @@ -8141,6 +8230,7 @@ "attack_speed": "6", "two_handed": "true", "weapon_interface": "16", + "equip_audio": "2244", "render_anim": "2588", "defence_anim": "424", "equipment_slot": "3", @@ -8172,6 +8262,7 @@ "attack_speed": "4", "two_handed": "true", "weapon_interface": "16", + "equip_audio": "2244", "render_anim": "2588", "defence_anim": "424", "equipment_slot": "3", @@ -8205,6 +8296,7 @@ "attack_speed": "6", "two_handed": "true", "weapon_interface": "16", + "equip_audio": "2244", "render_anim": "2588", "defence_anim": "424", "equipment_slot": "3", @@ -8238,6 +8330,7 @@ "attack_speed": "4", "two_handed": "true", "weapon_interface": "16", + "equip_audio": "2244", "render_anim": "2588", "defence_anim": "424", "equipment_slot": "3", @@ -8267,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", @@ -8285,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", @@ -8304,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", @@ -8322,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", @@ -8340,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", @@ -8358,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", @@ -8377,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", @@ -8395,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", @@ -8413,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", @@ -8432,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", @@ -8450,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", @@ -8469,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", @@ -8487,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", @@ -8505,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", @@ -9892,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", @@ -9917,6 +10025,7 @@ "durability": null, "weight": "9", "absorb": "1,0,1", + "equip_audio": "2242", "equipment_slot": "7", "grand_exchange_price": "55", "name": "Iron platelegs", @@ -9942,6 +10051,7 @@ "durability": null, "weight": "9", "absorb": "1,0,1", + "equip_audio": "2242", "equipment_slot": "7", "grand_exchange_price": "426", "name": "Steel platelegs", @@ -9967,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", @@ -9992,6 +10103,7 @@ "durability": null, "weight": "10", "absorb": "1,0,3", + "equip_audio": "2242", "equipment_slot": "7", "grand_exchange_price": "3649", "name": "Adamant platelegs", @@ -10013,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", @@ -10040,6 +10153,7 @@ "durability": null, "weight": "9", "absorb": "1,0,2", + "equip_audio": "2242", "equipment_slot": "7", "grand_exchange_price": "4995", "name": "Black platelegs", @@ -10065,6 +10179,7 @@ "durability": null, "weight": "9", "absorb": "2,0,4", + "equip_audio": "2242", "equipment_slot": "7", "lendable": "true", "grand_exchange_price": "38500", @@ -10310,6 +10425,7 @@ "durability": null, "weight": "5.4", "absorb": "0,4,2", + "equip_audio": "2241", "equipment_slot": "7", "lendable": "true", "grand_exchange_price": "2228", @@ -10505,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", @@ -10529,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", @@ -10554,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", @@ -10580,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", @@ -10606,6 +10726,7 @@ "durability": null, "weight": "11", "absorb": "2,0,4", + "equip_audio": "2239", "equipment_slot": "4", "remove_sleeves": "true", "grand_exchange_price": "9790", @@ -10632,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", @@ -10658,6 +10780,7 @@ "durability": null, "weight": "9.9", "absorb": "3,0,6", + "equip_audio": "2239", "equipment_slot": "4", "lendable": "true", "remove_sleeves": "true", @@ -10758,6 +10881,7 @@ "durability": null, "weight": "6.8", "absorb": "0,6,3", + "equip_audio": "2241", "equipment_slot": "4", "lendable": "true", "grand_exchange_price": "4544", @@ -10783,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", @@ -10807,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", @@ -10832,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", @@ -10857,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", @@ -10883,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", @@ -10909,6 +11038,7 @@ "durability": null, "weight": "1.8", "absorb": "1,0,3", + "equip_audio": "2240", "equipment_slot": "0", "lendable": "true", "grand_exchange_price": "11400", @@ -10935,6 +11065,7 @@ "durability": null, "weight": "1.3", "absorb": "2,0,4", + "equip_audio": "2240", "equipment_slot": "0", "lendable": "true", "grand_exchange_price": "60500", @@ -10961,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", @@ -10985,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", @@ -11010,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", @@ -11036,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", @@ -11062,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", @@ -11089,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", @@ -11115,6 +11252,7 @@ "durability": null, "weight": "2.7", "absorb": "1,0,3", + "equip_audio": "2240", "remove_beard": "true", "equipment_slot": "0", "lendable": "true", @@ -11142,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", @@ -11192,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", @@ -11220,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" }, { @@ -11235,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", @@ -11258,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", @@ -11284,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", @@ -11309,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", @@ -11334,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", @@ -11358,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", @@ -11382,6 +11529,7 @@ "durability": null, "weight": "3.64", "absorb": "3,0,7", + "equip_audio": "2245", "equipment_slot": "5", "lendable": "true", "grand_exchange_price": "22800", @@ -11407,6 +11555,7 @@ "durability": null, "weight": "3", "absorb": "5,0,11", + "equip_audio": "2245", "equipment_slot": "5", "lendable": "true", "grand_exchange_price": "431900", @@ -11429,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", @@ -11452,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", @@ -11478,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", @@ -11503,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", @@ -11527,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", @@ -11551,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", @@ -11575,6 +11730,7 @@ "durability": null, "weight": "5.4", "absorb": "3,0,7", + "equip_audio": "2245", "equipment_slot": "5", "lendable": "true", "grand_exchange_price": "35600", @@ -11601,6 +11757,7 @@ "weight": "0.4", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "defence_anim": "378", "equipment_slot": "3", @@ -11630,6 +11787,7 @@ "weight": "0.4", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "defence_anim": "378", "equipment_slot": "3", @@ -11649,7 +11807,8 @@ "name": "Bronze dagger", "tradeable": "true", "archery_ticket_price": "0", - "id": "1206" + "id": "1206", + "equip_audio": "" }, { "requirements": "{0,5}", @@ -11660,6 +11819,7 @@ "weight": "0.4", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "defence_anim": "378", "equipment_slot": "3", @@ -11690,6 +11850,7 @@ "weight": "0.3", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "defence_anim": "378", "equipment_slot": "3", @@ -11720,6 +11881,7 @@ "weight": "0.4", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "defence_anim": "378", "equipment_slot": "3", @@ -11750,6 +11912,7 @@ "weight": "0.4", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "defence_anim": "378", "equipment_slot": "3", @@ -11782,6 +11945,7 @@ "weight": "0.4", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "defence_anim": "397", "equipment_slot": "3", @@ -11813,6 +11977,7 @@ "weight": "0.4", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "defence_anim": "378", "equipment_slot": "3", @@ -11842,6 +12007,7 @@ "weight": "0.4", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "defence_anim": "378", "equipment_slot": "3", @@ -11871,6 +12037,7 @@ "weight": "0.4", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "defence_anim": "378", "equipment_slot": "3", @@ -11901,6 +12068,7 @@ "weight": "0.4", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "defence_anim": "378", "equipment_slot": "3", @@ -11931,6 +12099,7 @@ "weight": "0.3", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "defence_anim": "378", "equipment_slot": "3", @@ -11961,6 +12130,7 @@ "weight": "0.4", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "defence_anim": "378", "equipment_slot": "3", @@ -11991,6 +12161,7 @@ "weight": "0.4", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "defence_anim": "378", "equipment_slot": "3", @@ -12023,6 +12194,7 @@ "weight": "0.4", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "defence_anim": "397", "equipment_slot": "3", @@ -12054,6 +12226,7 @@ "weight": "0.4", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "defence_anim": "378", "equipment_slot": "3", @@ -12112,6 +12285,7 @@ "durability": null, "weight": "2.2", "weapon_interface": "14", + "equip_audio": "2247", "render_anim": "28", "name": "Bronze spear" }, @@ -12129,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", @@ -12183,6 +12358,7 @@ "durability": null, "weight": "2.2", "weapon_interface": "14", + "equip_audio": "2247", "render_anim": "28", "name": "Steel spear" }, @@ -12219,6 +12395,7 @@ "durability": null, "weight": "1.8", "weapon_interface": "14", + "equip_audio": "2247", "render_anim": "28", "name": "Mithril spear" }, @@ -12255,6 +12432,7 @@ "durability": null, "weight": "2", "weapon_interface": "14", + "equip_audio": "2247", "render_anim": "28", "name": "Adamant spear" }, @@ -12291,6 +12469,7 @@ "durability": null, "weight": "2.2", "weapon_interface": "14", + "equip_audio": "2247", "render_anim": "28", "lendable": "true", "name": "Rune spear" @@ -12329,6 +12508,7 @@ "durability": null, "weight": "2", "weapon_interface": "14", + "equip_audio": "2247", "render_anim": "28", "lendable": "true", "name": "Dragon spear" @@ -12348,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", @@ -12383,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", @@ -12414,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", @@ -12472,6 +12655,7 @@ "durability": null, "weight": "1.8", "weapon_interface": "14", + "equip_audio": "2247", "render_anim": "28", "name": "Mithril spear(p)" }, @@ -12508,6 +12692,7 @@ "durability": null, "weight": "2", "weapon_interface": "14", + "equip_audio": "2247", "render_anim": "28", "name": "Adamant spear(p)" }, @@ -12544,6 +12729,7 @@ "durability": null, "weight": "2.2", "weapon_interface": "14", + "equip_audio": "2247", "render_anim": "28", "lendable": "true", "name": "Rune spear(p)" @@ -12582,6 +12768,7 @@ "durability": null, "weight": "2", "weapon_interface": "14", + "equip_audio": "2247", "render_anim": "28", "lendable": "true", "name": "Dragon spear(p)" @@ -12603,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", @@ -12631,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", @@ -12660,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", @@ -12689,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", @@ -12718,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", @@ -12747,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", @@ -12791,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" @@ -12827,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" @@ -12864,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" @@ -12901,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" @@ -12938,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" @@ -12975,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" @@ -13012,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", @@ -13049,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" @@ -13085,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" @@ -13122,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" @@ -13159,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" @@ -13196,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" @@ -13233,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" @@ -13270,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", @@ -13309,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", @@ -13347,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" @@ -13384,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" @@ -13422,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" @@ -13460,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" @@ -13498,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" @@ -13536,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" @@ -13574,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", @@ -13611,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" @@ -13647,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" @@ -13684,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" @@ -13720,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" @@ -13757,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" @@ -13794,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" @@ -13830,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", @@ -13867,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" @@ -13903,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" @@ -13940,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" @@ -13976,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" @@ -14013,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" @@ -14050,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" @@ -14087,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", @@ -14109,10 +14338,12 @@ "weight": "1.3", "attack_speed": "5", "weapon_interface": "2", + "equip_audio": "2229", "defence_anim": "397", "equipment_slot": "3", "attack_anims": "395,395,401,395", "grand_exchange_price": "8", + "attack_audios": "2498,2498,2497,2498", "name": "Iron axe", "tradeable": "true", "archery_ticket_price": "0", @@ -14136,11 +14367,13 @@ "weight": "1.35", "attack_speed": "5", "weapon_interface": "2", + "equip_audio": "2229", "render_anim": "2586", "defence_anim": "397", "equipment_slot": "3", "attack_anims": "395,395,401,395", "grand_exchange_price": "44", + "attack_audios": "2498,2498,2497,2498", "name": "Bronze axe", "tradeable": "true", "archery_ticket_price": "0", @@ -14165,11 +14398,13 @@ "weight": "1.3", "attack_speed": "5", "weapon_interface": "2", + "equip_audio": "2229", "render_anim": "2586", "defence_anim": "397", "equipment_slot": "3", "attack_anims": "395,395,401,395", "grand_exchange_price": "36", + "attack_audios": "2498,2498,2497,2498", "name": "Steel axe", "tradeable": "true", "archery_ticket_price": "0", @@ -14194,11 +14429,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", "grand_exchange_price": "141", + "attack_audios": "2498,2498,2497,2498", "name": "Mithril axe", "tradeable": "true", "archery_ticket_price": "0", @@ -14223,11 +14460,13 @@ "weight": "2", "attack_speed": "5", "weapon_interface": "2", + "equip_audio": "2229", "render_anim": "2586", "defence_anim": "397", "equipment_slot": "3", "attack_anims": "395,395,401,395", "grand_exchange_price": "592", + "attack_audios": "2498,2498,2497,2498", "name": "Adamant axe", "tradeable": "true", "archery_ticket_price": "0", @@ -14249,11 +14488,13 @@ "examine": "A powerful axe.", "durability": null, "weapon_interface": "2", + "equip_audio": "2229", "render_anim": "2586", "defence_anim": "397", "equipment_slot": "3", "attack_anims": "395,395,401,395", "grand_exchange_price": "7495", + "attack_audios": "2498,2498,2497,2498", "name": "Rune axe", "tradeable": "true", "archery_ticket_price": "0", @@ -14277,11 +14518,13 @@ "weight": "2", "attack_speed": "5", "weapon_interface": "2", + "equip_audio": "2229", "render_anim": "2586", "defence_anim": "397", "equipment_slot": "3", "attack_anims": "395,395,401,395", "grand_exchange_price": "335", + "attack_audios": "2498,2498,2497,2498", "name": "Black axe", "tradeable": "true", "archery_ticket_price": "0", @@ -14305,11 +14548,13 @@ "weight": "2.7", "attack_speed": "6", "weapon_interface": "2", + "equip_audio": "2232", "render_anim": "2586", "defence_anim": "397", "equipment_slot": "3", "attack_anims": "395,395,401,395", "grand_exchange_price": "21", + "attack_audios": "2498,2498,2497,2498", "name": "Iron battleaxe", "tradeable": "true", "archery_ticket_price": "0", @@ -14334,11 +14579,13 @@ "weight": "2.7", "attack_speed": "6", "weapon_interface": "2", + "equip_audio": "2232", "render_anim": "2586", "defence_anim": "397", "equipment_slot": "3", "attack_anims": "395,395,401,395", "grand_exchange_price": "208", + "attack_audios": "2498,2498,2497,2498", "name": "Steel battleaxe", "tradeable": "true", "archery_ticket_price": "0", @@ -14363,11 +14610,13 @@ "weight": "2.7", "attack_speed": "6", "weapon_interface": "2", + "equip_audio": "2232", "render_anim": "2586", "defence_anim": "397", "equipment_slot": "3", "attack_anims": "395,395,401,395", "grand_exchange_price": "556", + "attack_audios": "2498,2498,2497,2498", "name": "Black battleaxe", "tradeable": "true", "archery_ticket_price": "0", @@ -14392,11 +14641,13 @@ "weight": "2", "attack_speed": "6", "weapon_interface": "2", + "equip_audio": "2232", "render_anim": "2586", "defence_anim": "397", "equipment_slot": "3", "attack_anims": "395,395,401,395", "grand_exchange_price": "809", + "attack_audios": "2498,2498,2497,2498", "name": "Mithril battleaxe", "tradeable": "true", "archery_ticket_price": "0", @@ -14421,11 +14672,13 @@ "weight": "3", "attack_speed": "6", "weapon_interface": "2", + "equip_audio": "2232", "render_anim": "2586", "defence_anim": "397", "equipment_slot": "3", "attack_anims": "395,395,401,395", "grand_exchange_price": "2311", + "attack_audios": "2498,2498,2497,2498", "name": "Adamant battleaxe", "tradeable": "true", "archery_ticket_price": "0", @@ -14449,12 +14702,14 @@ "weight": "2", "attack_speed": "6", "weapon_interface": "2", + "equip_audio": "2232", "render_anim": "2586", "defence_anim": "397", "equipment_slot": "3", "attack_anims": "395,395,401,395", "lendable": "true", "grand_exchange_price": "24800", + "attack_audios": "2498,2498,2497,2498", "name": "Rune battleaxe", "tradeable": "true", "archery_ticket_price": "0", @@ -14478,11 +14733,13 @@ "weight": "2.7", "attack_speed": "6", "weapon_interface": "2", + "equip_audio": "2232", "render_anim": "2586", "defence_anim": "397", "equipment_slot": "3", "attack_anims": "395,395,401,395", "grand_exchange_price": "15", + "attack_audios": "2498,2498,2497,2498", "name": "Bronze battleaxe", "tradeable": "true", "archery_ticket_price": "0", @@ -14514,6 +14771,7 @@ "attack_anims": "395,395,401,395", "lendable": "true", "grand_exchange_price": "123200", + "attack_audios": "2498,2498,2497,2498", "name": "Dragon battleaxe", "tradeable": "true", "archery_ticket_price": "0", @@ -14555,6 +14813,7 @@ "durability": null, "weight": "2", "weapon_interface": "1", + "equip_audio": "2247", "render_anim": "28", "attack_audios": "2555,0,0,0", "name": "Staff" @@ -14591,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" @@ -14628,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" @@ -14665,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" @@ -14702,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" @@ -14739,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" @@ -14776,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" @@ -14813,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" @@ -14850,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" @@ -14888,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" @@ -14926,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" @@ -14963,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" @@ -14977,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", @@ -15034,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" @@ -15071,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" @@ -15094,6 +15359,7 @@ "attack_speed": "5", "weapon_interface": "1", "turn180_anim": "1206", + "equip_audio": "2247", "render_anim": "28", "defence_anim": "420", "equipment_slot": "3", @@ -15179,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", @@ -15199,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", @@ -15226,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", @@ -15254,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", @@ -15281,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", @@ -15309,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", @@ -15337,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", @@ -15365,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", @@ -15395,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", @@ -15988,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.", @@ -16024,6 +16309,7 @@ "examine": "Maybe I should read this...", "durability": null, "name": "A magic scroll", + "tradeable": "false", "archery_ticket_price": "0", "id": "1505" }, @@ -16039,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", @@ -16052,6 +16338,13 @@ "archery_ticket_price": "0", "id": "1508" }, + { + "examine": "Turnip growing for beginners.", + "durability": null, + "name": "Book", + "archery_ticket_price": "0", + "id": "1509" + }, { "examine": "A picture of a lady called Elena.", "durability": null, @@ -16577,7 +16870,7 @@ "id": "1583" }, { - "examine": "Apparently my name is Hartigan", + "examine": "Apparently my name is Hartigen.", "durability": null, "name": "Id papers", "tradeable": "false", @@ -18621,7 +18914,8 @@ "name": "Woad leaf", "tradeable": "true", "archery_ticket_price": "0", - "id": "1793" + "id": "1793", + "equip_audio": "" }, { "ge_buy_limit": "100", @@ -19205,7 +19499,7 @@ }, { "ge_buy_limit": "1000", - "examine": "I need to cook this first / Freshly cooked ugthanki meat.", + "examine": "I need to cook this first.", "grand_exchange_price": "486", "durability": null, "name": "Raw ugthanki meat", @@ -19283,6 +19577,14 @@ "archery_ticket_price": "0", "id": "1866" }, + { + "examine": "It's all burnt.", + "durability": null, + "name": "Burnt pitta bread", + "tradeable": "true", + "archery_ticket_price": "0", + "id": "1867" + }, { "durability": null, "name": "Burnt pitta bread", @@ -19885,7 +20187,7 @@ }, { "ge_buy_limit": "100", - "examine": "Common bucket: It's a bucket of water.2008 Easter event: It's a bucket of water from the Easter Bunny's warren. Regular bucket filled from Braindeath Island: It's a bucket of... water?", + "examine": "It's a bucket of water.", "grand_exchange_price": "72", "durability": null, "name": "Bucket of water", @@ -20258,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", @@ -20277,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", @@ -20697,6 +20999,14 @@ "archery_ticket_price": "0", "id": "2012" }, + { + "examine": "Eew, it's horribly burnt.", + "durability": null, + "name": "Burnt curry", + "tradeable": "true", + "archery_ticket_price": "0", + "id": "2013" + }, { "durability": null, "name": "Burnt curry", @@ -24254,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", @@ -24271,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", @@ -24292,6 +24604,7 @@ "attack_speed": "4", "weapon_interface": "1", "turn180_anim": "1206", + "equip_audio": "2247", "defence_anim": "420", "render_anim": "28", "equipment_slot": "3", @@ -24306,6 +24619,13 @@ "stand_turn_anim": "1209", "bonuses": "-1,-1,6,6,0,2,3,1,6,0,0,2,0,0,0" }, + { + "examine": "A heavy key made of bronze. (Prince Ali Rescue).", + "durability": null, + "name": "Bronze key", + "archery_ticket_price": "0", + "id": "2418" + }, { "examine": "A grey woollen wig.", "durability": null, @@ -24999,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", @@ -25023,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", @@ -25047,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", @@ -25072,6 +25395,7 @@ "durability": null, "weight": "5.4", "absorb": "0,5,2", + "equip_audio": "2241", "equipment_slot": "7", "lendable": "true", "grand_exchange_price": "2504", @@ -25097,6 +25421,7 @@ "durability": null, "weight": "5.4", "absorb": "0,6,3", + "equip_audio": "2241", "equipment_slot": "7", "lendable": "true", "grand_exchange_price": "3142", @@ -25122,6 +25447,7 @@ "durability": null, "weight": "5.4", "absorb": "0,7,3", + "equip_audio": "2241", "equipment_slot": "7", "lendable": "true", "grand_exchange_price": "4744", @@ -25147,6 +25473,7 @@ "durability": null, "weight": "6.8", "absorb": "0,6,3", + "equip_audio": "2241", "equipment_slot": "4", "lendable": "true", "grand_exchange_price": "5420", @@ -25172,6 +25499,7 @@ "durability": null, "weight": "6.8", "absorb": "0,6,3", + "equip_audio": "2241", "equipment_slot": "4", "lendable": "true", "grand_exchange_price": "6680", @@ -25197,6 +25525,7 @@ "durability": null, "weight": "6.8", "absorb": "0,6,3", + "equip_audio": "2241", "equipment_slot": "4", "lendable": "true", "grand_exchange_price": "8098", @@ -25436,6 +25765,7 @@ { "shop_price": "140", "grand_exchange_price": "140", + "examine": "Bones are for burying!", "durability": null, "name": "Bones", "tradeable": "true", @@ -25806,7 +26136,7 @@ "id": "2573" }, { - "examine": "Used by navigators to find their position in RuneScape.", + "examine": "Used by navigators to find their position in 2009Scape.", "durability": null, "name": "Sextant", "weight": "0.2", @@ -25822,7 +26152,7 @@ "id": "2575" }, { - "examine": "A navigator's chart of RuneScape.", + "examine": "A navigator's chart of 2009Scape.", "durability": null, "name": "Chart", "archery_ticket_price": "0", @@ -25908,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", @@ -25930,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", @@ -25956,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", @@ -25982,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)", @@ -26006,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", @@ -26028,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", @@ -26054,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", @@ -26079,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)", @@ -26103,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", @@ -26129,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)", @@ -26153,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)", @@ -26178,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", @@ -26203,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", @@ -26229,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)", @@ -26253,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)", @@ -26278,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", @@ -26303,6 +26649,7 @@ "durability": null, "weight": "9.07", "absorb": "3,0,6", + "equip_audio": "2239", "equipment_slot": "4", "lendable": "true", "remove_sleeves": "true", @@ -26330,6 +26677,7 @@ "durability": null, "weight": "9", "absorb": "2,0,4", + "equip_audio": "2242", "equipment_slot": "7", "lendable": "true", "grand_exchange_price": "243400", @@ -26357,6 +26705,7 @@ "durability": null, "weight": "2", "absorb": "1,0,3", + "equip_audio": "2240", "remove_beard": "true", "equipment_slot": "0", "lendable": "true", @@ -26383,6 +26732,7 @@ "durability": null, "weight": "5", "absorb": "3,0,7", + "equip_audio": "2245", "equipment_slot": "5", "lendable": "true", "grand_exchange_price": "392700", @@ -26408,6 +26758,7 @@ "durability": null, "weight": "9.07", "absorb": "3,0,6", + "equip_audio": "2239", "equipment_slot": "4", "lendable": "true", "remove_sleeves": "true", @@ -26434,6 +26785,7 @@ "durability": null, "weight": "9", "absorb": "2,0,4", + "equip_audio": "2242", "equipment_slot": "7", "lendable": "true", "grand_exchange_price": "95400", @@ -26460,6 +26812,7 @@ "durability": null, "weight": "2.7", "absorb": "1,0,3", + "equip_audio": "2240", "remove_beard": "true", "equipment_slot": "0", "lendable": "true", @@ -26486,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)", @@ -26748,6 +27103,7 @@ "durability": null, "weight": "9", "absorb": "3,0,6", + "equip_audio": "2239", "equipment_slot": "4", "lendable": "true", "remove_sleeves": "true", @@ -26774,6 +27130,7 @@ "durability": null, "weight": "9", "absorb": "2,0,4", + "equip_audio": "2242", "equipment_slot": "7", "lendable": "true", "grand_exchange_price": "281500", @@ -26800,6 +27157,7 @@ "durability": null, "weight": "2", "absorb": "1,0,3", + "equip_audio": "2240", "remove_beard": "true", "equipment_slot": "0", "lendable": "true", @@ -26826,6 +27184,7 @@ "durability": null, "weight": "5", "absorb": "3,0,7", + "equip_audio": "2245", "equipment_slot": "5", "lendable": "true", "grand_exchange_price": "499500", @@ -26851,6 +27210,7 @@ "durability": null, "weight": "9.07", "absorb": "3,0,6", + "equip_audio": "2239", "equipment_slot": "4", "lendable": "true", "remove_sleeves": "true", @@ -26877,6 +27237,7 @@ "durability": null, "weight": "9", "absorb": "2,0,4", + "equip_audio": "2242", "equipment_slot": "7", "lendable": "true", "grand_exchange_price": "444000", @@ -26903,6 +27264,7 @@ "durability": null, "weight": "2", "absorb": "1,0,3", + "equip_audio": "2240", "remove_beard": "true", "equipment_slot": "0", "lendable": "true", @@ -26929,6 +27291,7 @@ "durability": null, "weight": "5.4", "absorb": "3,0,7", + "equip_audio": "2245", "equipment_slot": "5", "lendable": "true", "grand_exchange_price": "795900", @@ -26954,6 +27317,7 @@ "durability": null, "weight": "9.07", "absorb": "3,0,6", + "equip_audio": "2239", "equipment_slot": "4", "lendable": "true", "remove_sleeves": "true", @@ -26980,6 +27344,7 @@ "durability": null, "weight": "9", "absorb": "2,0,4", + "equip_audio": "2242", "equipment_slot": "7", "lendable": "true", "grand_exchange_price": "169900", @@ -27006,6 +27371,7 @@ "durability": null, "weight": "2", "absorb": "1,0,3", + "equip_audio": "2240", "remove_beard": "true", "equipment_slot": "0", "lendable": "true", @@ -27032,6 +27398,7 @@ "durability": null, "weight": "5.4", "absorb": "3,0,7", + "equip_audio": "2245", "equipment_slot": "5", "lendable": "true", "grand_exchange_price": "247300", @@ -30257,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" @@ -30286,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" @@ -30364,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", @@ -30382,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", @@ -30408,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", @@ -30429,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", @@ -30451,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", @@ -30472,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", @@ -30493,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", @@ -30513,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", @@ -30536,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", @@ -30570,6 +30946,7 @@ "durability": null, "destroy": "true", "weight": "0.3", + "equip_audio": "2237", "equipment_slot": "10", "grand_exchange_price": "51400", "name": "Climbing boots", @@ -30595,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" }, { @@ -30748,55 +31126,62 @@ "examine": "These blackened Jogre bones have been somehow burnt.", "durability": null, "name": "Burnt jogre bones", + "tradeable": "false", "weight": "0.8", "archery_ticket_price": "0", "id": "3127" }, { - "examine": "See article", + "examine": "Burnt Jogre bones smothered with raw Karambwanji Paste.", "durability": null, "name": "Pasty jogre bones", - "weight": "1", + "tradeable": "false", + "weight": "0.8", "archery_ticket_price": "0", "id": "3128" }, { - "examine": "See article", + "examine": "Burnt Jogre bones smothered with cooked Karambwanji paste.", "durability": null, "name": "Pasty jogre bones", - "weight": "1", + "tradeable": "false", + "weight": "0.8", "archery_ticket_price": "0", "id": "3129" }, { - "examine": "Burnt Jogre bones marinated in lovely Karambwanji sauce. Perfect./Burnt Jogre bones marinated in lovely Karabwanji sauce. Not quite right.", + "examine": "Burnt Jogre bones marinated in a lovely Karambwanji sauce. Perfect.", "durability": null, "name": "Marinated j' bones", - "weight": "1", + "tradeable": "false", + "weight": "0.8", "archery_ticket_price": "0", "id": "3130" }, { - "examine": "See article", + "examine": "Jogre bones smothered with raw Karambwanji paste.", "durability": null, "name": "Pasty jogre bones", - "weight": "1", + "tradeable": "false", + "weight": "0.8", "archery_ticket_price": "0", "id": "3131" }, { - "examine": "See article", + "examine": "Jogre bones smothered with cooked Karambwanji paste.", "durability": null, "name": "Pasty jogre bones", - "weight": "1", + "tradeable": "false", + "weight": "0.8", "archery_ticket_price": "0", "id": "3132" }, { - "examine": "Burnt Jogre bones marinated in lovely Karambwanji sauce. Perfect./Burnt Jogre bones marinated in lovely Karabwanji sauce. Not quite right.", + "examine": "Jogre Bones marinated in Karambwanji sauce. Not quite right.", "durability": null, "name": "Marinated j' bones", - "weight": "1", + "tradeable": "false", + "weight": "0.8", "archery_ticket_price": "0", "id": "3133" }, @@ -31117,6 +31502,7 @@ "two_handed": "true", "weapon_interface": "14", "turn180_anim": "1206", + "equip_audio": "2247", "defence_anim": "2079", "render_anim": "28", "equipment_slot": "3", @@ -31141,6 +31527,7 @@ "two_handed": "true", "weapon_interface": "14", "turn180_anim": "1206", + "equip_audio": "2247", "defence_anim": "2079", "render_anim": "28", "equipment_slot": "3", @@ -31166,6 +31553,7 @@ "two_handed": "true", "weapon_interface": "14", "turn180_anim": "1206", + "equip_audio": "2247", "defence_anim": "2079", "render_anim": "28", "equipment_slot": "3", @@ -31191,6 +31579,7 @@ "two_handed": "true", "weapon_interface": "14", "turn180_anim": "1206", + "equip_audio": "2247", "defence_anim": "2079", "render_anim": "28", "equipment_slot": "3", @@ -31216,6 +31605,7 @@ "two_handed": "true", "weapon_interface": "14", "turn180_anim": "1206", + "equip_audio": "2247", "defence_anim": "2079", "render_anim": "28", "equipment_slot": "3", @@ -31241,6 +31631,7 @@ "two_handed": "true", "weapon_interface": "14", "turn180_anim": "1206", + "equip_audio": "2247", "defence_anim": "2079", "render_anim": "28", "equipment_slot": "3", @@ -31255,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, @@ -31295,7 +31687,7 @@ "id": "3178" }, { - "examine": "These are small/medium sized monkey bones.", + "examine": "These are smallish monkey bones.", "grand_exchange_price": "383", "durability": null, "name": "Monkey bones", @@ -31305,7 +31697,7 @@ "id": "3179" }, { - "examine": "These are small/medium sized monkey bones.", + "examine": "These are medium sized monkey bones.", "grand_exchange_price": "383", "durability": null, "name": "Monkey bones", @@ -31315,7 +31707,7 @@ "id": "3180" }, { - "examine": "These are small/medium sized monkey bones.", + "examine": "These are quite large monkey bones.", "grand_exchange_price": "383", "durability": null, "name": "Monkey bones", @@ -31325,7 +31717,7 @@ "id": "3181" }, { - "examine": "These are small/medium sized monkey bones.", + "examine": "These are quite large monkey bones.", "grand_exchange_price": "383", "durability": null, "name": "Monkey bones", @@ -31336,7 +31728,7 @@ }, { "ge_buy_limit": "10000", - "examine": "These are small/medium sized monkey bones.", + "examine": "These are small monkey bones.", "grand_exchange_price": "588", "durability": null, "name": "Monkey bones", @@ -31355,7 +31747,7 @@ "id": "3184" }, { - "examine": "These are small/medium sized monkey bones.", + "examine": "These are smallish monkey bones. They smell extremely nauseating.", "grand_exchange_price": "383", "durability": null, "name": "Monkey bones", @@ -31365,7 +31757,7 @@ "id": "3185" }, { - "examine": "These are small/medium sized monkey bones.", + "examine": "These are small monkey bones. They smell extremely nauseating.", "grand_exchange_price": "383", "durability": null, "name": "Monkey bones", @@ -31429,6 +31821,7 @@ "durability": null, "weight": "3", "weapon_interface": "15", + "equip_audio": "2247", "render_anim": "28", "name": "Bronze halberd" }, @@ -31465,6 +31858,7 @@ "durability": null, "weight": "3.1", "weapon_interface": "15", + "equip_audio": "2247", "render_anim": "28", "name": "Iron halberd" }, @@ -31502,6 +31896,7 @@ "durability": null, "weight": "3", "weapon_interface": "15", + "equip_audio": "2247", "render_anim": "28", "name": "Steel halberd" }, @@ -31534,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" }, @@ -31576,6 +31972,7 @@ "durability": null, "weight": "2.7", "weapon_interface": "15", + "equip_audio": "2247", "render_anim": "28", "name": "Mithril halberd" }, @@ -31613,6 +32010,7 @@ "durability": null, "weight": "3.6", "weapon_interface": "15", + "equip_audio": "2247", "render_anim": "28", "name": "Adamant halberd" }, @@ -31650,6 +32048,7 @@ "durability": null, "weight": "3", "weapon_interface": "15", + "equip_audio": "2247", "render_anim": "28", "lendable": "true", "name": "Rune halberd" @@ -31689,6 +32088,7 @@ "durability": null, "weight": "3.1", "weapon_interface": "15", + "equip_audio": "2247", "render_anim": "28", "lendable": "true", "name": "Dragon halberd" @@ -31952,7 +32352,7 @@ "id": "3240" }, { - "examine": "One of RuneScape's many citizens. In Karamja or South of Falador: One of RuneScape's many citizens, apparently trying to be an explorer. In Lumbridge: One of Lumbridge's many citizens. In a Falador home: One of RuneScape's many citizens. He looks worried ", + "examine": "One of 2009Scape's many citizens.", "durability": null, "name": "Man", "archery_ticket_price": "0", @@ -32790,6 +33190,7 @@ "examine": "Wooden foot protection.", "durability": null, "weight": "0.9", + "equip_audio": "2237", "equipment_slot": "10", "lendable": "true", "grand_exchange_price": "3808", @@ -33582,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", @@ -33612,6 +34014,7 @@ "durability": null, "weight": "9", "absorb": "2,0,4", + "equip_audio": "2242", "equipment_slot": "7", "lendable": "true", "grand_exchange_price": "2200000", @@ -33654,6 +34057,7 @@ "durability": null, "weight": "2", "absorb": "1,0,3", + "equip_audio": "2240", "remove_beard": "true", "equipment_slot": "0", "lendable": "true", @@ -33680,6 +34084,7 @@ "durability": null, "weight": "5", "absorb": "3,0,7", + "equip_audio": "2245", "equipment_slot": "5", "lendable": "true", "grand_exchange_price": "515400", @@ -34888,7 +35293,7 @@ "equipment_slot": "3" }, { - "examine": "This will teleport me to Rellekka when I play it. A musical intrument that I can magically play.", + "examine": "A musical intrument that I can magically play.", "durability": null, "name": "Lyre", "weight": "1.8", @@ -34897,7 +35302,7 @@ "equipment_slot": "3" }, { - "examine": "This will teleport me to Rellekka when I play it. A musical intrument that I can magically play.", + "examine": "This will teleport me to Rellekka when I play it.", "durability": null, "name": "Enchanted lyre", "weight": "1.8", @@ -34906,7 +35311,7 @@ "equipment_slot": "3" }, { - "examine": "This will teleport me to Rellekka when I play it. A musical intrument that I can magically play.", + "examine": "This will teleport me to Rellekka when I play it.", "durability": null, "name": "Enchanted lyre(1)", "weight": "1.8", @@ -34944,7 +35349,9 @@ "examine": "The lowest maintenance pet you will ever have.", "walk_anim": "6658", "durability": null, + "weight": "1", "turn90ccw_anim": "6663", + "two_handed": "true", "turn180_anim": "6659", "render_anim": "792", "equipment_slot": "3", @@ -35360,7 +35767,8 @@ "name": "Sticky red goop", "tradeable": "true", "archery_ticket_price": "0", - "id": "3747" + "id": "3747", + "alchemizable": "true" }, { "remove_head": "true", @@ -35372,6 +35780,7 @@ "id": "3748", "absorb": "1,0,2", "bonuses": "0,0,0,-6,-2,19,21,16,-1,19,6,0,0,0,0", + "alchemizable": "true", "equipment_slot": "0" }, { @@ -35487,18 +35896,19 @@ "id": "3756" }, { - "attack_anims": "390,390,381,390", "examine": "A sword used only by Fremennik warriors.", "durability": null, - "name": "Fremennik blade", "weight": "1.8", - "archery_ticket_price": "0", "attack_speed": "4", "weapon_interface": "6", - "id": "3757", - "bonuses": "6,29,-2,0,0,0,1,0,0,0,0,28,0,0,0", + "alchemizable": "true", "defence_anim": "397", - "equipment_slot": "3" + "equipment_slot": "3", + "attack_anims": "390,390,381,390", + "name": "Fremennik blade", + "archery_ticket_price": "0", + "id": "3757", + "bonuses": "6,29,-2,0,0,0,1,0,0,0,0,28,0,0,0" }, { "examine": "A shield worn by Fremennik warriors.", @@ -35509,6 +35919,7 @@ "archery_ticket_price": "0", "id": "3758", "bonuses": "0,0,0,-8,-2,27,31,29,-1,29,30,0,0,0,0", + "alchemizable": "true", "equipment_slot": "5" }, { @@ -36870,7 +37281,9 @@ "turn90ccw_anim": "822", "weapon_interface": "6", "turn180_anim": "820", + "equip_audio": "2248", "render_anim": "1", + "castle_wars_ticket_price": "5", "equipment_slot": "3", "stand_anim": "808", "name": "Decorative sword", @@ -36888,6 +37301,7 @@ "archery_ticket_price": "0", "id": "4069", "bonuses": "0,0,0,-30,-10,32,31,24,-6,31,5,0,0,0,0", + "castle_wars_ticket_price": "8", "equipment_slot": "4" }, { @@ -36897,6 +37311,7 @@ "archery_ticket_price": "0", "id": "4070", "bonuses": "0,0,0,-21,-7,17,16,15,-4,16,2,0,0,0,0", + "castle_wars_ticket_price": "6", "equipment_slot": "7" }, { @@ -36910,6 +37325,7 @@ "archery_ticket_price": "0", "id": "4071", "bonuses": "0,0,0,-3,-1,7,8,6,-1,7,3,0,0,0,0", + "castle_wars_ticket_price": "4", "equipment_slot": "0" }, { @@ -36921,6 +37337,7 @@ "archery_ticket_price": "0", "id": "4072", "bonuses": "0,0,0,-8,-2,13,15,14,-1,14,5,0,0,0,0", + "castle_wars_ticket_price": "6", "equipment_slot": "5" }, { @@ -37033,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", @@ -37451,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" }, { @@ -37473,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" }, { @@ -37488,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", @@ -37511,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", @@ -37534,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", @@ -37557,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", @@ -37582,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", @@ -37864,6 +38290,7 @@ "two_handed": "true", "weapon_interface": "14", "turn180_anim": "1206", + "equip_audio": "2247", "defence_anim": "2079", "render_anim": "28", "equipment_slot": "3", @@ -37886,6 +38313,7 @@ "archery_ticket_price": "0", "attack_speed": "5", "id": "4159", + "equip_audio": "2247", "equipment_slot": "3" }, { @@ -38025,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" @@ -38175,7 +38604,7 @@ "id": "4189" }, { - "examine": "See article", + "examine": "A typical garden brush.", "durability": null, "name": "Garden brush", "weight": "1", @@ -38183,7 +38612,7 @@ "id": "4190" }, { - "examine": "See article", + "examine": "A typical garden brush, with a cane tied to it.", "durability": null, "name": "Extended brush", "weight": "1", @@ -38191,7 +38620,7 @@ "id": "4191" }, { - "examine": "See article", + "examine": "A typical garden brush, with two canes tied to it.", "durability": null, "name": "Extended brush", "weight": "1", @@ -38199,7 +38628,7 @@ "id": "4192" }, { - "examine": "See article", + "examine": "A typical garden brush, with three canes tied to it.", "durability": null, "name": "Extended brush", "weight": "1", @@ -38213,14 +38642,16 @@ "name": "Arms", "weight": "2", "archery_ticket_price": "0", - "id": "4195" + "id": "4195", + "alchemizable": "true" }, { "examine": "A pair of lifeless, rotting legs.", "durability": null, "name": "Legs", "archery_ticket_price": "0", - "id": "4196" + "id": "4196", + "alchemizable": "true" }, { "examine": "No brain: A gruesome, decapitated head, whose brain has rotted away. Brain: A gruesome, decapitated head - its eyes stare lifelessly at nothing.", @@ -38228,7 +38659,8 @@ "name": "Decapitated head", "weight": "3", "archery_ticket_price": "0", - "id": "4197" + "id": "4197", + "alchemizable": "true" }, { "examine": "No brain: A gruesome, decapitated head, whose brain has rotted away. Brain: A gruesome, decapitated head - its eyes stare lifelessly at nothing.", @@ -38236,7 +38668,8 @@ "name": "Decapitated head", "weight": "3", "archery_ticket_price": "0", - "id": "4198" + "id": "4198", + "alchemizable": "true" }, { "shop_price": "50", @@ -38244,7 +38677,8 @@ "durability": null, "name": "Pickled brain", "archery_ticket_price": "0", - "id": "4199" + "id": "4199", + "alchemizable": "true" }, { "examine": "A mould for making silver lightning conductors.", @@ -38263,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", @@ -38322,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", @@ -38352,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", @@ -38372,16 +38808,17 @@ "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", - "bonuses": "0,0,0,0,96,0,0,0,0,0,0,0,0,0,68" + "bonuses": "0,0,0,0,100,0,0,0,0,0,0,0,0,0,70" }, { "requirements": "{4,70}-{16,50}", @@ -38392,16 +38829,17 @@ "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", - "bonuses": "0,0,0,0,92,0,0,0,0,0,0,0,0,0,66" + "bonuses": "0,0,0,0,100,0,0,0,0,0,0,0,0,0,70" }, { "requirements": "{4,70}-{16,50}", @@ -38412,16 +38850,17 @@ "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", - "bonuses": "0,0,0,0,88,0,0,0,0,0,0,0,0,0,64" + "bonuses": "0,0,0,0,100,0,0,0,0,0,0,0,0,0,70" }, { "requirements": "{4,70}-{16,50}", @@ -38432,16 +38871,17 @@ "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", - "bonuses": "0,0,0,0,84,0,0,0,0,0,0,0,0,0,62" + "bonuses": "0,0,0,0,100,0,0,0,0,0,0,0,0,0,70" }, { "requirements": "{4,70}-{16,50}", @@ -38452,16 +38892,17 @@ "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", - "bonuses": "0,0,0,0,80,0,0,0,0,0,0,0,0,0,60" + "bonuses": "0,0,0,0,100,0,0,0,0,0,0,0,0,0,70" }, { "requirements": "{4,70}-{16,50}", @@ -38472,16 +38913,17 @@ "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", - "bonuses": "0,0,0,0,76,0,0,0,0,0,0,0,0,0,58" + "bonuses": "0,0,0,0,100,0,0,0,0,0,0,0,0,0,70" }, { "requirements": "{4,70}-{16,50}", @@ -38492,16 +38934,17 @@ "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", - "bonuses": "0,0,0,0,72,0,0,0,0,0,0,0,0,0,56" + "bonuses": "0,0,0,0,100,0,0,0,0,0,0,0,0,0,70" }, { "requirements": "{4,70}-{16,50}", @@ -38512,16 +38955,17 @@ "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", - "bonuses": "0,0,0,0,68,0,0,0,0,0,0,0,0,0,54" + "bonuses": "0,0,0,0,100,0,0,0,0,0,0,0,0,0,70" }, { "requirements": "{4,70}-{16,50}", @@ -38532,16 +38976,17 @@ "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", - "bonuses": "0,0,0,0,64,0,0,0,0,0,0,0,0,0,52" + "bonuses": "0,0,0,0,100,0,0,0,0,0,0,0,0,0,70" }, { "requirements": "{1,70}-{16,50}", @@ -38584,7 +39029,7 @@ "archery_ticket_price": "0", "id": "4226", "absorb": "6,0,12", - "bonuses": "0,0,0,-10,-10,49,52,51,0,78,68,0,0,0,0", + "bonuses": "0,0,0,-10,-10,51,54,53,0,80,70,0,0,0,0", "equipment_slot": "5" }, { @@ -38598,7 +39043,7 @@ "archery_ticket_price": "0", "id": "4227", "absorb": "6,0,12", - "bonuses": "0,0,0,-10,-10,47,50,49,0,76,66,0,0,0,0", + "bonuses": "0,0,0,-10,-10,51,54,53,0,80,70,0,0,0,0", "equipment_slot": "5" }, { @@ -38612,7 +39057,7 @@ "archery_ticket_price": "0", "id": "4228", "absorb": "6,0,12", - "bonuses": "0,0,0,-10,-10,45,48,47,0,74,65,0,0,0,0", + "bonuses": "0,0,0,-10,-10,51,54,53,0,80,70,0,0,0,0", "equipment_slot": "5" }, { @@ -38626,7 +39071,7 @@ "archery_ticket_price": "0", "id": "4229", "absorb": "6,0,12", - "bonuses": "0,0,0,-10,-10,43,46,45,0,72,63,0,0,0,0", + "bonuses": "0,0,0,-10,-10,51,54,53,0,80,70,0,0,0,0", "equipment_slot": "5" }, { @@ -38640,7 +39085,7 @@ "archery_ticket_price": "0", "id": "4230", "absorb": "6,0,12", - "bonuses": "0,0,0,-10,-10,41,44,43,0,70,61,0,0,0,0", + "bonuses": "0,0,0,-10,-10,51,54,53,0,80,70,0,0,0,0", "equipment_slot": "5" }, { @@ -38654,7 +39099,7 @@ "archery_ticket_price": "0", "id": "4231", "absorb": "6,0,12", - "bonuses": "0,0,0,-10,-10,39,42,41,0,68,59,0,0,0,0", + "bonuses": "0,0,0,-10,-10,51,54,53,0,80,70,0,0,0,0", "equipment_slot": "5" }, { @@ -38668,7 +39113,7 @@ "archery_ticket_price": "0", "id": "4232", "absorb": "6,0,12", - "bonuses": "0,0,0,-10,-10,37,40,39,0,66,58,0,0,0,0", + "bonuses": "0,0,0,-10,-10,51,54,53,0,80,70,0,0,0,0", "equipment_slot": "5" }, { @@ -38682,7 +39127,7 @@ "archery_ticket_price": "0", "id": "4233", "absorb": "6,0,12", - "bonuses": "0,0,0,-10,-10,35,38,37,0,64,56,0,0,0,0", + "bonuses": "0,0,0,-10,-10,51,54,53,0,80,70,0,0,0,0", "equipment_slot": "5" }, { @@ -38696,7 +39141,7 @@ "archery_ticket_price": "0", "id": "4234", "absorb": "6,0,12", - "bonuses": "0,0,0,-10,-10,33,36,35,0,62,54,0,0,0,0", + "bonuses": "0,0,0,-10,-10,51,54,53,0,80,70,0,0,0,0", "equipment_slot": "5" }, { @@ -38877,7 +39322,7 @@ "id": "4255" }, { - "examine": "A pot of crushed bones.", + "examine": "A pot of crushed small ninja monkey bones.", "durability": null, "name": "Bonemeal", "weight": "1", @@ -38901,7 +39346,7 @@ "id": "4258" }, { - "examine": "A pot of crushed monkey bones.", + "examine": "A pot of crushed burnt jogre bones.", "durability": null, "name": "Bonemeal", "weight": "1", @@ -38989,7 +39434,7 @@ "id": "4269" }, { - "examine": "A pot of crushed [type of bones].", + "examine": "A pot of crushed medium ninja monkey bones.", "durability": null, "name": "Bonemeal", "weight": "1", @@ -38997,7 +39442,7 @@ "id": "4270" }, { - "examine": "A pot of crushed [type of bones].", + "examine": "A pot of crushed Dagannoth-king bones.", "durability": null, "name": "Bonemeal", "weight": "1", @@ -39325,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" }, { @@ -41160,8 +41606,10 @@ "turn90ccw_anim": "822", "weapon_interface": "6", "turn180_anim": "820", + "equip_audio": "2248", "render_anim": "1", "defence_anim": "397", + "castle_wars_ticket_price": "50", "equipment_slot": "3", "attack_anims": "390,390,381,390", "stand_anim": "808", @@ -41180,6 +41628,7 @@ "archery_ticket_price": "0", "id": "4504", "bonuses": "0,0,0,-30,-10,46,44,38,-6,44,20,0,0,0,0", + "castle_wars_ticket_price": "80", "equipment_slot": "4" }, { @@ -41189,6 +41638,7 @@ "archery_ticket_price": "0", "id": "4505", "bonuses": "0,0,0,-21,-7,24,22,20,-4,22,5,0,0,0,0", + "castle_wars_ticket_price": "60", "equipment_slot": "7" }, { @@ -41202,6 +41652,7 @@ "archery_ticket_price": "0", "id": "4506", "bonuses": "0,0,0,-3,-1,10,11,9,-1,10,5,0,0,0,0", + "castle_wars_ticket_price": "40", "equipment_slot": "0" }, { @@ -41213,6 +41664,7 @@ "archery_ticket_price": "0", "id": "4507", "bonuses": "0,0,0,-8,-2,18,22,20,-1,20,20,0,0,0,0", + "castle_wars_ticket_price": "60", "equipment_slot": "5" }, { @@ -41226,8 +41678,10 @@ "turn90ccw_anim": "822", "weapon_interface": "6", "turn180_anim": "820", + "equip_audio": "2248", "render_anim": "1", "defence_anim": "397", + "castle_wars_ticket_price": "500", "equipment_slot": "3", "attack_anims": "390,390,381,390", "stand_anim": "808", @@ -41246,6 +41700,7 @@ "archery_ticket_price": "0", "id": "4509", "bonuses": "0,0,0,-30,-10,65,63,55,-6,63,30,0,0,0,0", + "castle_wars_ticket_price": "800", "equipment_slot": "4" }, { @@ -41255,6 +41710,7 @@ "archery_ticket_price": "0", "id": "4510", "bonuses": "0,0,0,-21,-7,33,31,29,-4,31,10,0,0,0,0", + "castle_wars_ticket_price": "600", "equipment_slot": "7" }, { @@ -41268,6 +41724,7 @@ "archery_ticket_price": "0", "id": "4511", "bonuses": "0,0,0,-3,-1,14,15,13,-1,14,6,0,0,0,0", + "castle_wars_ticket_price": "400", "equipment_slot": "0" }, { @@ -41279,6 +41736,7 @@ "archery_ticket_price": "0", "id": "4512", "bonuses": "0,0,0,-8,-2,27,31,29,-1,29,30,0,0,0,0", + "castle_wars_ticket_price": "600", "equipment_slot": "5" }, { @@ -41288,6 +41746,7 @@ "archery_ticket_price": "0", "id": "4513", "bonuses": "0,0,0,0,0,0,1,2,0,1,0,0,0,0,0", + "castle_wars_ticket_price": "10", "equipment_slot": "0" }, { @@ -41296,6 +41755,7 @@ "archery_ticket_price": "0", "id": "4514", "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "castle_wars_ticket_price": "10", "equipment_slot": "1" }, { @@ -41305,6 +41765,7 @@ "archery_ticket_price": "0", "id": "4515", "bonuses": "0,0,0,0,0,0,1,2,0,1,0,0,0,0,0", + "castle_wars_ticket_price": "10", "equipment_slot": "0" }, { @@ -41313,6 +41774,7 @@ "archery_ticket_price": "0", "id": "4516", "bonuses": "0,0,0,0,0,0,1,1,0,2,0,0,0,0,0", + "castle_wars_ticket_price": "10", "equipment_slot": "1" }, { @@ -41640,7 +42102,7 @@ }, { "remove_head": "true", - "requirements": "{1,5}-{18,35}", + "requirements": "{1,5}", "shop_price": "650", "ge_buy_limit": "100", "examine": "You don't want to wear it inside-out.", @@ -41737,15 +42199,17 @@ "stand_turn_anim": "823" }, { - "examine": "Perhaps not the most powerful weapon in RuneScape.", + "examine": "Perhaps not the most powerful weapon in 2009Scape.", "durability": null, "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", @@ -41824,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", @@ -41858,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", @@ -41891,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", @@ -41907,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", @@ -41952,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", @@ -42192,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", @@ -42281,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", @@ -42298,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", @@ -42307,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", @@ -42315,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", @@ -42324,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", @@ -42332,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", @@ -42341,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", @@ -42349,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", @@ -42358,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", @@ -42433,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", @@ -42657,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" }, @@ -42745,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" @@ -42821,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", @@ -42864,6 +43336,7 @@ "durability": null, "weight": "13", "weapon_interface": "2", + "equip_audio": "2232", "render_anim": "134", "name": "Dharok's greataxe" }, @@ -42885,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", @@ -42912,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", @@ -42939,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", @@ -42983,6 +43459,7 @@ "durability": null, "weight": "2.2", "weapon_interface": "14", + "equip_audio": "2247", "render_anim": "28", "name": "Guthan's warspear" }, @@ -43004,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", @@ -43031,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", @@ -43057,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", @@ -43101,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" @@ -43123,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", @@ -43150,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", @@ -43191,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", @@ -43220,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", @@ -43249,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", @@ -43276,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", @@ -43303,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", @@ -43347,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" @@ -43369,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", @@ -43396,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", @@ -43949,7 +44440,7 @@ "id": "4854" }, { - "examine": "A pot of crushed [type of bones].", + "examine": "A pot of crushed gorilla monkey bones.", "durability": null, "name": "Bonemeal", "weight": "1", @@ -44048,6 +44539,7 @@ "attack_speed": "6", "weapon_interface": "1", "turn180_anim": "1206", + "equip_audio": "2247", "defence_anim": "420", "render_anim": "28", "equipment_slot": "3", @@ -44073,6 +44565,7 @@ "attack_speed": "6", "weapon_interface": "1", "turn180_anim": "1206", + "equip_audio": "2247", "defence_anim": "420", "render_anim": "28", "equipment_slot": "3", @@ -44098,6 +44591,7 @@ "attack_speed": "6", "weapon_interface": "1", "turn180_anim": "1206", + "equip_audio": "2247", "defence_anim": "420", "render_anim": "28", "equipment_slot": "3", @@ -44123,6 +44617,7 @@ "attack_speed": "6", "weapon_interface": "1", "turn180_anim": "1206", + "equip_audio": "2247", "defence_anim": "420", "render_anim": "28", "equipment_slot": "3", @@ -44149,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", @@ -44332,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", @@ -44392,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", @@ -44410,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}", @@ -44526,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", @@ -44550,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}", @@ -44611,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", @@ -44640,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" }, { @@ -44653,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" }, { @@ -44666,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" }, { @@ -44679,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", @@ -44712,6 +45228,7 @@ "durability": null, "weight": "2", "absorb": "2,0,5", + "equip_audio": "2240", "remove_beard": "true", "equipment_slot": "0", "grand_exchange_price": "232455", @@ -44727,6 +45244,7 @@ "durability": null, "weight": "2", "absorb": "2,0,5", + "equip_audio": "2240", "remove_beard": "true", "equipment_slot": "0", "grand_exchange_price": "232455", @@ -44742,6 +45260,7 @@ "durability": null, "weight": "2", "absorb": "2,0,5", + "equip_audio": "2240", "remove_beard": "true", "equipment_slot": "0", "grand_exchange_price": "232455", @@ -44757,6 +45276,7 @@ "durability": null, "weight": "2", "absorb": "2,0,5", + "equip_audio": "2240", "remove_beard": "true", "equipment_slot": "0", "grand_exchange_price": "232455", @@ -44773,6 +45293,7 @@ "durability": null, "weight": "2", "absorb": "2,0,5", + "equip_audio": "2240", "remove_beard": "true", "equipment_slot": "0", "grand_exchange_price": "368200", @@ -44792,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}", @@ -44908,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", @@ -44932,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}", @@ -44993,6 +45523,7 @@ "durability": null, "weight": "9", "absorb": "5,0,10", + "equip_audio": "2239", "equipment_slot": "4", "remove_sleeves": "true", "grand_exchange_price": "245900", @@ -45022,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" }, { @@ -45035,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" }, { @@ -45048,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" }, { @@ -45061,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", @@ -45091,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", @@ -45150,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", @@ -45169,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}", @@ -45285,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", @@ -45304,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}", @@ -45369,6 +45920,7 @@ "durability": null, "weight": "6", "absorb": "0,10,5", + "equip_audio": "2241", "equipment_slot": "4", "remove_sleeves": "true", "grand_exchange_price": "1500000", @@ -45398,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" }, { @@ -45411,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" }, { @@ -45424,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" }, { @@ -45437,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", @@ -45470,6 +46027,7 @@ "durability": null, "weight": "5", "absorb": "2,0,5", + "equip_audio": "2240", "remove_beard": "true", "equipment_slot": "0", "grand_exchange_price": "71798", @@ -45485,6 +46043,7 @@ "durability": null, "weight": "5", "absorb": "2,0,5", + "equip_audio": "2240", "remove_beard": "true", "equipment_slot": "0", "grand_exchange_price": "71798", @@ -45500,6 +46059,7 @@ "durability": null, "weight": "5", "absorb": "2,0,5", + "equip_audio": "2240", "remove_beard": "true", "equipment_slot": "0", "grand_exchange_price": "71798", @@ -45515,6 +46075,7 @@ "durability": null, "weight": "5", "absorb": "2,0,5", + "equip_audio": "2240", "remove_beard": "true", "equipment_slot": "0", "grand_exchange_price": "71798", @@ -45531,6 +46092,7 @@ "durability": null, "weight": "5", "absorb": "2,0,5", + "equip_audio": "2240", "remove_beard": "true", "equipment_slot": "0", "grand_exchange_price": "78200", @@ -45557,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", @@ -45575,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", @@ -45593,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", @@ -45611,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", @@ -45630,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", @@ -45650,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}", @@ -45711,6 +46282,7 @@ "durability": null, "weight": "9", "absorb": "5,0,10", + "equip_audio": "2240", "equipment_slot": "4", "remove_sleeves": "true", "grand_exchange_price": "271600", @@ -45740,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" }, { @@ -45753,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" }, { @@ -45766,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" }, { @@ -45779,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", @@ -45812,6 +46389,7 @@ "durability": null, "weight": "1", "absorb": "2,0,5", + "equip_audio": "2240", "remove_beard": "true", "equipment_slot": "0", "grand_exchange_price": "459537", @@ -45827,6 +46405,7 @@ "durability": null, "weight": "1", "absorb": "2,0,5", + "equip_audio": "2240", "remove_beard": "true", "equipment_slot": "0", "grand_exchange_price": "459537", @@ -45842,6 +46421,7 @@ "durability": null, "weight": "1", "absorb": "2,0,5", + "equip_audio": "2240", "remove_beard": "true", "equipment_slot": "0", "grand_exchange_price": "459537", @@ -45857,6 +46437,7 @@ "durability": null, "weight": "1", "absorb": "2,0,5", + "equip_audio": "2240", "remove_beard": "true", "equipment_slot": "0", "grand_exchange_price": "459537", @@ -45873,6 +46454,7 @@ "durability": null, "weight": "1", "absorb": "2,0,5", + "equip_audio": "2240", "remove_beard": "true", "equipment_slot": "0", "grand_exchange_price": "648900", @@ -45892,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}", @@ -46008,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", @@ -46032,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}", @@ -46093,6 +46684,7 @@ "durability": null, "weight": "5", "absorb": "5,0,10", + "equip_audio": "2241", "equipment_slot": "4", "remove_sleeves": "true", "grand_exchange_price": "174000", @@ -46122,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" }, { @@ -46135,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" }, { @@ -46148,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" }, { @@ -46161,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", @@ -46309,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", @@ -46339,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", @@ -46766,6 +47366,7 @@ "name": "Dwarven battleaxe", "archery_ticket_price": "0", "id": "5060", + "equip_audio": "2232", "equipment_slot": "3" }, { @@ -46774,6 +47375,7 @@ "name": "Dwarven battleaxe", "archery_ticket_price": "0", "id": "5061", + "equip_audio": "2232", "equipment_slot": "3" }, { @@ -48386,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)", @@ -48403,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)", @@ -48420,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)", @@ -48437,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)", @@ -48454,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)", @@ -48471,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)", @@ -48488,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)", @@ -48505,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)", @@ -48522,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)", @@ -48540,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)", @@ -48732,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)", @@ -48748,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)", @@ -48764,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)", @@ -48780,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)", @@ -48796,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)", @@ -48812,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)", @@ -48828,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)", @@ -48844,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)", @@ -48860,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)", @@ -48877,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)", @@ -49205,7 +49807,7 @@ "id": "5517" }, { - "examine": "When empty:", + "examine": "This contains mystical teleport information...", "durability": null, "name": "Scrying orb", "weight": "1", @@ -49213,7 +49815,7 @@ "id": "5518" }, { - "examine": "When empty:", + "examine": "This orb apparently contains a cypher spell.", "durability": null, "name": "Scrying orb", "weight": "1", @@ -49775,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" }, @@ -49786,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" }, @@ -49798,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" }, @@ -49922,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" }, @@ -50028,7 +50638,7 @@ "stand_turn_anim": "330" }, { - "examine": "A pot of crushed [type of bones].", + "examine": "A pot of crushed bearded gorilla monkey bones.", "durability": null, "name": "Bonemeal", "weight": "1", @@ -50205,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", @@ -50222,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", @@ -50240,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", @@ -50258,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", @@ -50276,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", @@ -50294,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", @@ -50312,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", @@ -50329,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", @@ -50346,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", @@ -50364,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", @@ -50382,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", @@ -50400,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", @@ -50418,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", @@ -50436,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", @@ -50454,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", @@ -50471,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", @@ -50489,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", @@ -50507,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", @@ -50524,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", @@ -50541,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", @@ -50575,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", @@ -50593,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", @@ -50611,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", @@ -50628,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", @@ -50646,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", @@ -50663,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", @@ -50681,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", @@ -50700,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", @@ -50718,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", @@ -50737,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", @@ -50755,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", @@ -50773,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", @@ -50791,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", @@ -50809,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", @@ -50828,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", @@ -50846,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", @@ -50865,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", @@ -50882,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", @@ -50900,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", @@ -50919,6 +51568,7 @@ "weight": "0.4", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "equipment_slot": "3", "grand_exchange_price": "483", @@ -50946,6 +51596,7 @@ "weight": "0.4", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "equipment_slot": "3", "grand_exchange_price": "350", @@ -50974,6 +51625,7 @@ "weight": "0.4", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "equipment_slot": "3", "grand_exchange_price": "575", @@ -51002,6 +51654,7 @@ "weight": "0.3", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "equipment_slot": "3", "grand_exchange_price": "538", @@ -51026,6 +51679,7 @@ "ge_buy_limit": "100", "durability": null, "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "equipment_slot": "3", "grand_exchange_price": "800", @@ -51054,6 +51708,7 @@ "weight": "0.4", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "equipment_slot": "3", "lendable": "true", @@ -51084,6 +51739,7 @@ "weight": "0.4", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "defence_anim": "397", "equipment_slot": "3", @@ -51118,6 +51774,7 @@ "weight": "0.4", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "equipment_slot": "3", "grand_exchange_price": "664", @@ -51145,6 +51802,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" }, @@ -51163,6 +51821,7 @@ "weight": "0.4", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "equipment_slot": "3", "grand_exchange_price": "4128", @@ -51190,6 +51849,7 @@ "weight": "0.4", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "equipment_slot": "3", "grand_exchange_price": "5088", @@ -51218,6 +51878,7 @@ "weight": "0.4", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "equipment_slot": "3", "grand_exchange_price": "4729", @@ -51246,6 +51907,7 @@ "weight": "0.3", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "equipment_slot": "3", "grand_exchange_price": "3476", @@ -51270,6 +51932,7 @@ "ge_buy_limit": "100", "durability": null, "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "equipment_slot": "3", "grand_exchange_price": "4612", @@ -51298,6 +51961,7 @@ "weight": "0.4", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "equipment_slot": "3", "lendable": "true", @@ -51328,6 +51992,7 @@ "weight": "0.4", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "defence_anim": "397", "equipment_slot": "3", @@ -51362,6 +52027,7 @@ "weight": "0.4", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "equipment_slot": "3", "grand_exchange_price": "4921", @@ -51389,6 +52055,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" }, @@ -51411,6 +52078,7 @@ "two_handed": "true", "weapon_interface": "14", "turn180_anim": "1206", + "equip_audio": "2247", "render_anim": "28", "equipment_slot": "3", "grand_exchange_price": "861", @@ -51444,6 +52112,7 @@ "two_handed": "true", "weapon_interface": "14", "turn180_anim": "1206", + "equip_audio": "2247", "render_anim": "28", "equipment_slot": "3", "grand_exchange_price": "676", @@ -51478,6 +52147,7 @@ "two_handed": "true", "weapon_interface": "14", "turn180_anim": "1206", + "equip_audio": "2247", "render_anim": "28", "equipment_slot": "3", "grand_exchange_price": "596", @@ -51512,6 +52182,7 @@ "two_handed": "true", "weapon_interface": "14", "turn180_anim": "1206", + "equip_audio": "2247", "render_anim": "28", "equipment_slot": "3", "grand_exchange_price": "802", @@ -51546,6 +52217,7 @@ "two_handed": "true", "weapon_interface": "14", "turn180_anim": "1206", + "equip_audio": "2247", "render_anim": "28", "equipment_slot": "3", "grand_exchange_price": "1394", @@ -51568,30 +52240,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", @@ -51627,6 +52300,7 @@ "durability": null, "weight": "2", "weapon_interface": "14", + "equip_audio": "2247", "render_anim": "28", "lendable": "true", "name": "Dragon spear(p+)" @@ -51654,6 +52328,7 @@ "two_handed": "true", "weapon_interface": "14", "turn180_anim": "1206", + "equip_audio": "2247", "render_anim": "28", "equipment_slot": "3", "grand_exchange_price": "7557", @@ -51687,6 +52362,7 @@ "two_handed": "true", "weapon_interface": "14", "turn180_anim": "1206", + "equip_audio": "2247", "render_anim": "28", "equipment_slot": "3", "grand_exchange_price": "8394", @@ -51721,6 +52397,7 @@ "two_handed": "true", "weapon_interface": "14", "turn180_anim": "1206", + "equip_audio": "2247", "render_anim": "28", "equipment_slot": "3", "grand_exchange_price": "8336", @@ -51755,6 +52432,7 @@ "two_handed": "true", "weapon_interface": "14", "turn180_anim": "1206", + "equip_audio": "2247", "render_anim": "28", "equipment_slot": "3", "grand_exchange_price": "7035", @@ -51787,6 +52465,7 @@ "two_handed": "true", "weapon_interface": "14", "turn180_anim": "1206", + "equip_audio": "2247", "render_anim": "28", "equipment_slot": "3", "grand_exchange_price": "7530", @@ -51809,30 +52488,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", @@ -51868,6 +52548,7 @@ "durability": null, "weight": "2", "weapon_interface": "14", + "equip_audio": "2247", "render_anim": "28", "lendable": "true", "name": "Dragon spear(p++)" @@ -51910,6 +52591,7 @@ "two_handed": "true", "weapon_interface": "14", "turn180_anim": "1206", + "equip_audio": "2247", "render_anim": "28", "equipment_slot": "3", "grand_exchange_price": "3351", @@ -51944,6 +52626,7 @@ "two_handed": "true", "weapon_interface": "14", "turn180_anim": "1206", + "equip_audio": "2247", "render_anim": "28", "equipment_slot": "3", "grand_exchange_price": "9065", @@ -55056,7 +55739,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", @@ -55064,7 +55747,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", @@ -55462,7 +56145,7 @@ "id": "6124" }, { - "examine": "This will teleport me to Rellekka when I play it. A musical intrument that I can magically play.", + "examine": "This will teleport me to Rellekka when I play it.", "durability": null, "name": "Enchanted lyre(2)", "weight": "1.8", @@ -55471,7 +56154,7 @@ "equipment_slot": "3" }, { - "examine": "This will teleport me to Rellekka when I play it. A musical intrument that I can magically play.", + "examine": "This will teleport me to Rellekka when I play it.", "durability": null, "name": "Enchanted lyre(3)", "weight": "1.8", @@ -55480,7 +56163,7 @@ "equipment_slot": "3" }, { - "examine": "This will teleport me to Rellekka when I play it. A musical intrument that I can magically play.", + "examine": "This will teleport me to Rellekka when I play it.", "durability": null, "name": "Enchanted lyre(4)", "weight": "1.8", @@ -55691,18 +56374,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", @@ -55714,18 +56398,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", @@ -57625,7 +58310,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", @@ -57758,7 +58443,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", @@ -57869,7 +58554,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", @@ -57980,7 +58665,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", @@ -58721,7 +59406,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", @@ -59080,7 +59765,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", @@ -59260,36 +59945,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.", @@ -59527,6 +60214,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", @@ -59551,11 +60239,13 @@ "weight": "2.7", "attack_speed": "6", "weapon_interface": "2", + "equip_audio": "2232", "render_anim": "2586", "defence_anim": "397", "equipment_slot": "3", - "attack_anims": "395,395,395,395", + "attack_anims": "395,395,401,395", "grand_exchange_price": "1459", + "attack_audios": "2498,2498,2497,2498", "name": "White battleaxe", "tradeable": "true", "archery_ticket_price": "0", @@ -59580,6 +60270,7 @@ "weight": "0.4", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "defence_anim": "378", "equipment_slot": "3", @@ -59610,6 +60301,7 @@ "weight": "0.4", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "equipment_slot": "3", "grand_exchange_price": "497", @@ -59638,6 +60330,7 @@ "weight": "0.4", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "equipment_slot": "3", "grand_exchange_price": "982", @@ -59666,6 +60359,7 @@ "weight": "0.4", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "equipment_slot": "3", "grand_exchange_price": "8422", @@ -59705,11 +60399,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" }, @@ -59731,10 +60426,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", @@ -59762,6 +60459,7 @@ "turn90ccw_anim": "1208", "weapon_interface": "1", "turn180_anim": "1206", + "equip_audio": "2247", "render_anim": "28", "equipment_slot": "3", "grand_exchange_price": "650", @@ -59808,6 +60506,7 @@ "durability": null, "weight": "1.8", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "1381", "name": "White sword" }, @@ -59844,6 +60543,7 @@ "durability": null, "weight": "1.8", "weapon_interface": "6", + "equip_audio": "2248", "render_anim": "1", "name": "White longsword" }, @@ -59881,6 +60581,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" @@ -59918,6 +60619,7 @@ "durability": null, "weight": "1", "weapon_interface": "6", + "equip_audio": "2248", "render_anim": "1", "attack_audios": "2500,0,2517,0", "name": "White scimitar" @@ -59940,6 +60642,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", @@ -59993,6 +60696,7 @@ "durability": null, "weight": "9.9", "absorb": "1,0,2", + "equip_audio": "2239", "equipment_slot": "4", "remove_sleeves": "true", "grand_exchange_price": "5837", @@ -60043,6 +60747,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", @@ -60068,6 +60773,7 @@ "examine": "A full face helmet.", "durability": null, "weight": "2.7", + "equip_audio": "2240", "remove_beard": "true", "equipment_slot": "0", "grand_exchange_price": "2624", @@ -60166,6 +60872,7 @@ "durability": null, "weight": "3.6", "absorb": "1,0,2", + "equip_audio": "2245", "equipment_slot": "5", "grand_exchange_price": "1360", "name": "White sq shield", @@ -60191,6 +60898,7 @@ "durability": null, "weight": "5.4", "absorb": "1,0,2", + "equip_audio": "2245", "equipment_slot": "5", "grand_exchange_price": "2390", "name": "White kiteshield", @@ -60999,7 +61707,7 @@ "id": "6727" }, { - "examine": "A pot of crushed [type of bones].", + "examine": "A pot of crushed small zombie monkey bones.", "durability": null, "name": "Bonemeal", "weight": "1", @@ -61126,11 +61834,13 @@ "rare_item": "true", "durability": null, "weapon_interface": "2", + "equip_audio": "2229", "render_anim": "2586", "defence_anim": "397", "equipment_slot": "3", "attack_anims": "395,395,395,395", "grand_exchange_price": "813100", + "attack_audios": "2498,2498,2497,2498", "name": "Dragon axe", "tradeable": "true", "archery_ticket_price": "0", @@ -61758,7 +62468,7 @@ "bonuses": "0,0,0,-31,-18,43,45,41,-4,68,20,0,0,0,0" }, { - "examine": "A pot of crushed [type of bones].", + "examine": "A pot of crushed large zombie monkey bones.", "durability": null, "name": "Bonemeal", "weight": "1", @@ -64308,6 +65018,7 @@ "durability": null, "weight": "3", "weapon_interface": "7", + "equip_audio": "2248", "render_anim": "124", "lendable": "true", "attack_audios": "2503,0,2504,0", @@ -65941,6 +66652,7 @@ "durability": null, "weight": "5.4", "absorb": "3,0,7", + "equip_audio": "2245", "equipment_slot": "5", "grand_exchange_price": "34200", "name": "Rune shield(h1)", @@ -66013,6 +66725,7 @@ "durability": null, "weight": "5.4", "absorb": "3,0,7", + "equip_audio": "2245", "equipment_slot": "5", "grand_exchange_price": "32400", "name": "Rune shield(h2)", @@ -66085,6 +66798,7 @@ "durability": null, "weight": "5", "absorb": "3,0,7", + "equip_audio": "2245", "equipment_slot": "5", "grand_exchange_price": "32700", "name": "Rune shield(h3)", @@ -66157,6 +66871,7 @@ "durability": null, "weight": "5", "absorb": "3,0,7", + "equip_audio": "2245", "equipment_slot": "5", "grand_exchange_price": "32500", "name": "Rune shield(h4)", @@ -66229,6 +66944,7 @@ "durability": null, "weight": "5", "absorb": "3,0,7", + "equip_audio": "2245", "equipment_slot": "5", "grand_exchange_price": "32400", "name": "Rune shield(h5)", @@ -66347,15 +67063,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", @@ -66370,15 +67087,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", @@ -66390,18 +67108,19 @@ "id": "7373" }, { - "requirements": "{1,50}-{4,40}", + "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", @@ -66413,18 +67132,19 @@ "id": "7375" }, { - "requirements": "{1,50}-{4,40}", + "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", @@ -66439,15 +67159,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", @@ -66462,15 +67183,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", @@ -66485,15 +67207,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", @@ -66508,15 +67231,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", @@ -66664,7 +67388,7 @@ "id": "7397" }, { - "requirements": "{1,40}-{6,20}", + "requirements": "{1,20}-{6,40}", "ge_buy_limit": "2", "examine": "Enchanted Wizards robes.", "durability": null, @@ -66696,7 +67420,7 @@ }, { "remove_head": "true", - "requirements": "{1,40}-{6,20}", + "requirements": "{1,20}-{6,40}", "ge_buy_limit": "2", "examine": "A three pointed hat of magic.", "grand_exchange_price": "8845", @@ -67285,6 +68009,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", @@ -67377,6 +68102,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" }, { @@ -67388,6 +68114,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" }, { @@ -67399,6 +68126,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" }, { @@ -67410,6 +68138,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" }, { @@ -67421,6 +68150,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" }, { @@ -67431,6 +68161,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" }, { @@ -67442,6 +68173,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" }, { @@ -67453,6 +68185,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" }, { @@ -67464,6 +68197,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" }, { @@ -67476,6 +68210,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" }, { @@ -68728,9 +69463,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", @@ -68751,9 +69487,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", @@ -68776,9 +69513,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", @@ -68799,9 +69537,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", @@ -68822,9 +69561,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", @@ -68845,9 +69585,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", @@ -68868,9 +69609,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 +69633,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", @@ -68914,9 +69657,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 +69681,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", @@ -69220,6 +69965,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" }, @@ -69228,6 +69974,7 @@ "name": "Wooden shield", "archery_ticket_price": "0", "id": "7676", + "equip_audio": "2250", "equipment_slot": "5" }, { @@ -69293,6 +70040,7 @@ "archery_ticket_price": "0", "attack_speed": "3", "id": "7684", + "equip_audio": "2244", "equipment_slot": "3" }, { @@ -69946,7 +70694,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", @@ -69964,7 +70712,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)", @@ -70342,14 +71090,16 @@ "equipment_slot": "3" }, { - "attack_anims": "395,395,395,395", + "attack_anims": "395,395,401,395", "examine": "A heavy duty axe.", "durability": null, + "attack_audios": "2498,2498,2497,2498", "name": "Anger battleaxe", "archery_ticket_price": "0", "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" }, @@ -70374,6 +71124,7 @@ "two_handed": "true", "weapon_interface": "14", "turn180_anim": "1206", + "equip_audio": "2247", "render_anim": "28", "defence_anim": "2079", "equipment_slot": "3", @@ -70411,7 +71162,7 @@ "id": "7812" }, { - "examine": "See article", + "examine": "There is a goblin bone in here.", "durability": null, "name": "Bone in vinegar", "weight": "1", @@ -70419,7 +71170,7 @@ "id": "7813" }, { - "examine": "This needs a good polish.", + "examine": "This belongs in a museum!", "durability": null, "name": "Goblin skull", "archery_ticket_price": "0", @@ -70433,7 +71184,7 @@ "id": "7815" }, { - "examine": "See article", + "examine": "There is a bear bone in here.", "durability": null, "name": "Bone in vinegar", "weight": "1", @@ -70441,21 +71192,21 @@ "id": "7816" }, { - "examine": "This needs a good polish.", + "examine": "This belongs in a museum!", "durability": null, "name": "Bear ribs", "archery_ticket_price": "0", "id": "7817" }, { - "examine": "Unpolished: This needs a good polish.", + "examine": "This needs a good polish.", "durability": null, "name": "Ram skull", "archery_ticket_price": "0", "id": "7818" }, { - "examine": "See article", + "examine": "There is a ram bone in here.", "durability": null, "name": "Bone in vinegar", "weight": "1", @@ -70463,7 +71214,7 @@ "id": "7819" }, { - "examine": "Unpolished: This needs a good polish.", + "examine": "This belongs in a museum!", "durability": null, "name": "Ram skull", "archery_ticket_price": "0", @@ -70477,7 +71228,7 @@ "id": "7821" }, { - "examine": "See article", + "examine": "There is a unicorn bone in here.", "durability": null, "name": "Bone in vinegar", "weight": "1", @@ -70485,7 +71236,7 @@ "id": "7822" }, { - "examine": "This needs a good polish.", + "examine": "This belongs in a museum!", "durability": null, "name": "Unicorn bone", "archery_ticket_price": "0", @@ -70499,7 +71250,7 @@ "id": "7824" }, { - "examine": "See article", + "examine": "There is a giant rat bone in here.", "durability": null, "name": "Bone in vinegar", "weight": "1", @@ -70507,7 +71258,7 @@ "id": "7825" }, { - "examine": "This needs a good polish.", + "examine": "This belongs in a museum!", "durability": null, "name": "Giant rat bone", "archery_ticket_price": "0", @@ -70522,7 +71273,7 @@ "id": "7827" }, { - "examine": "See article", + "examine": "There is a giant bat bone in here.", "durability": null, "name": "Bone in vinegar", "weight": "1", @@ -70530,7 +71281,7 @@ "id": "7828" }, { - "examine": "This needs a good polish.", + "examine": "This belongs in a museum!", "durability": null, "name": "Giant bat wing", "weight": "0.9", @@ -70546,7 +71297,7 @@ "id": "7830" }, { - "examine": "See article", + "examine": "There is a wolf bone in here.", "durability": null, "name": "Bone in vinegar", "weight": "1", @@ -70554,7 +71305,7 @@ "id": "7831" }, { - "examine": "This needs a good polish.", + "examine": "This belongs in a museum!", "durability": null, "name": "Wolf bone", "weight": "0.9", @@ -70570,7 +71321,7 @@ "id": "7833" }, { - "examine": "See article", + "examine": "There is a bat bone in here.", "durability": null, "name": "Bone in vinegar", "weight": "1", @@ -70578,7 +71329,7 @@ "id": "7834" }, { - "examine": "This needs a good polish.", + "examine": "This belongs in a museum!", "durability": null, "name": "Bat wing", "weight": "0.9", @@ -70594,7 +71345,7 @@ "id": "7836" }, { - "examine": "See article", + "examine": "There is a rat bone in here.", "durability": null, "name": "Bone in vinegar", "weight": "1", @@ -70602,7 +71353,7 @@ "id": "7837" }, { - "examine": "This needs a good polish.", + "examine": "This belongs in a museum!", "durability": null, "name": "Rat bone", "weight": "0.9", @@ -70618,7 +71369,7 @@ "id": "7839" }, { - "examine": "See article", + "examine": "There is a baby blue dragon bone in here.", "durability": null, "name": "Bone in vinegar", "weight": "1", @@ -70626,7 +71377,7 @@ "id": "7840" }, { - "examine": "This needs a good polish.", + "examine": "This belongs in a museum!", "durability": null, "name": "Baby dragon bone", "weight": "0.9", @@ -70642,7 +71393,7 @@ "id": "7842" }, { - "examine": "See article", + "examine": "There is a ogre bone in here.", "durability": null, "name": "Bone in vinegar", "weight": "1", @@ -70650,7 +71401,7 @@ "id": "7843" }, { - "examine": "This needs a good polish.", + "examine": "This belongs in a museum!", "durability": null, "name": "Ogre ribs", "weight": "0.9", @@ -70666,7 +71417,7 @@ "id": "7845" }, { - "examine": "See article", + "examine": "There is a jogre bone in here.", "durability": null, "name": "Bone in vinegar", "weight": "1", @@ -70674,7 +71425,7 @@ "id": "7846" }, { - "examine": "This needs a good polish.", + "examine": "This belongs in a museum!", "durability": null, "name": "Jogre bone", "weight": "0.9", @@ -70690,7 +71441,7 @@ "id": "7848" }, { - "examine": "See article", + "examine": "There is a zogre bone in here.", "durability": null, "name": "Bone in vinegar", "weight": "1", @@ -70698,7 +71449,7 @@ "id": "7849" }, { - "examine": "This needs a good polish.", + "examine": "This belongs in a museum!", "durability": null, "name": "Zogre bone", "weight": "0.9", @@ -70714,7 +71465,7 @@ "id": "7851" }, { - "examine": "See article", + "examine": "There is a mogre bone in here.", "durability": null, "name": "Bone in vinegar", "weight": "1", @@ -70722,7 +71473,7 @@ "id": "7852" }, { - "examine": "This needs a good polish.", + "examine": "This belongs in a museum!", "durability": null, "name": "Mogre bone", "weight": "0.9", @@ -70730,14 +71481,14 @@ "id": "7853" }, { - "examine": "This needs a good polish./ This belongs in a museum!", + "examine": "This needs a good polish.", "durability": null, "name": "Monkey paw", "archery_ticket_price": "0", "id": "7854" }, { - "examine": "See article", + "examine": "There is a monkey bone in here.", "durability": null, "name": "Bone in vinegar", "weight": "1", @@ -70745,7 +71496,7 @@ "id": "7855" }, { - "examine": "This needs a good polish./ This belongs in a museum!", + "examine": "This belongs in a museum!", "durability": null, "name": "Monkey paw", "archery_ticket_price": "0", @@ -70760,7 +71511,7 @@ "id": "7857" }, { - "examine": "See article", + "examine": "There is a Dagannoth bone in here.", "durability": null, "name": "Bone in vinegar", "weight": "1", @@ -70768,7 +71519,7 @@ "id": "7858" }, { - "examine": "This needs a good polish.", + "examine": "This belongs in a museum!", "durability": null, "name": "Dagannoth ribs", "weight": "0.9", @@ -70784,7 +71535,7 @@ "id": "7860" }, { - "examine": "See article", + "examine": "There is a snake bone in here.", "durability": null, "name": "Bone in vinegar", "weight": "1", @@ -70792,7 +71543,7 @@ "id": "7861" }, { - "examine": "This needs a good polish.", + "examine": "This belongs in a museum!", "durability": null, "name": "Snake spine", "weight": "0.9", @@ -70808,7 +71559,7 @@ "id": "7863" }, { - "examine": "See article", + "examine": "There is a zombie bone in here.", "durability": null, "name": "Bone in vinegar", "weight": "1", @@ -70816,7 +71567,7 @@ "id": "7864" }, { - "examine": "This needs a good polish.", + "examine": "This belongs in a museum!", "durability": null, "name": "Zombie bone", "weight": "0.9", @@ -70832,7 +71583,7 @@ "id": "7866" }, { - "examine": "See article", + "examine": "There is a werewolf bone in here.", "durability": null, "name": "Bone in vinegar", "weight": "1", @@ -70840,7 +71591,7 @@ "id": "7867" }, { - "examine": "This needs a good polish.", + "examine": "This belongs in a museum!", "durability": null, "name": "Werewolf bone", "weight": "0.9", @@ -70856,7 +71607,7 @@ "id": "7869" }, { - "examine": "See article", + "examine": "There is a moss giant bone in here.", "durability": null, "name": "Bone in vinegar", "weight": "1", @@ -70864,7 +71615,7 @@ "id": "7870" }, { - "examine": "This needs a good polish.", + "examine": "This belongs in a museum!", "durability": null, "name": "Moss giant bone", "weight": "0.9", @@ -70880,7 +71631,7 @@ "id": "7872" }, { - "examine": "See article", + "examine": "There is a fire giant bone in here.", "durability": null, "name": "Bone in vinegar", "weight": "1", @@ -70888,7 +71639,7 @@ "id": "7873" }, { - "examine": "This needs a good polish.", + "examine": "This bone belongs in a museum!", "durability": null, "name": "Fire giant bone", "weight": "0.9", @@ -70904,7 +71655,7 @@ "id": "7875" }, { - "examine": "See article", + "examine": "There is an ice giant bone in here.", "durability": null, "name": "Bone in vinegar", "weight": "1", @@ -70912,7 +71663,7 @@ "id": "7876" }, { - "examine": "This needs a good polish.", + "examine": "This belongs in a museum!", "durability": null, "name": "Ice giant ribs", "weight": "0.9", @@ -70928,7 +71679,7 @@ "id": "7878" }, { - "examine": "See article", + "examine": "There is a terrorbird bone in here.", "durability": null, "name": "Bone in vinegar", "weight": "1", @@ -70936,7 +71687,7 @@ "id": "7879" }, { - "examine": "This needs a good polish.", + "examine": "This belongs in a museum!", "durability": null, "name": "Terrorbird wing", "weight": "0.9", @@ -70952,7 +71703,7 @@ "id": "7881" }, { - "examine": "See article", + "examine": "There is a ghoul bone in here.", "durability": null, "name": "Bone in vinegar", "weight": "1", @@ -70960,7 +71711,7 @@ "id": "7882" }, { - "examine": "This needs a good polish.", + "examine": "This belongs in a museum!", "durability": null, "name": "Ghoul bone", "weight": "0.9", @@ -70976,7 +71727,7 @@ "id": "7884" }, { - "examine": "See article", + "examine": "There is a troll bone in here.", "durability": null, "name": "Bone in vinegar", "weight": "1", @@ -70984,7 +71735,7 @@ "id": "7885" }, { - "examine": "This needs a good polish.", + "examine": "This belongs in a museum!", "durability": null, "name": "Troll bone", "weight": "0.9", @@ -71000,7 +71751,7 @@ "id": "7887" }, { - "examine": "See article", + "examine": "There is a seagull bone in here.", "durability": null, "name": "Bone in vinegar", "weight": "1", @@ -71008,7 +71759,7 @@ "id": "7888" }, { - "examine": "This needs a good polish.", + "examine": "This belongs in a museum!", "durability": null, "name": "Seagull wing", "weight": "0.9", @@ -71024,7 +71775,7 @@ "id": "7890" }, { - "examine": "See article", + "examine": "There is an undead cow bone in here.", "durability": null, "name": "Bone in vinegar", "weight": "1", @@ -71032,7 +71783,7 @@ "id": "7891" }, { - "examine": "This needs a good polish.", + "examine": "This belongs in a museum!", "durability": null, "name": "Undead cow ribs", "weight": "0.9", @@ -71041,7 +71792,7 @@ }, { "shop_price": "160", - "examine": "Unpolished: This needs a good polish. Polished: This bone belongs in a museum!", + "examine": "This needs a good polish.", "durability": null, "name": "Experiment bone", "weight": "0.9", @@ -71049,7 +71800,7 @@ "id": "7893" }, { - "examine": "See article", + "examine": "There is an experiment bone in here.", "durability": null, "name": "Bone in vinegar", "weight": "1", @@ -71058,7 +71809,7 @@ }, { "shop_price": "160", - "examine": "Unpolished: This needs a good polish. Polished: This bone belongs in a museum!", + "examine": "This belongs in a museum!", "durability": null, "name": "Experiment bone", "weight": "0.9", @@ -71074,7 +71825,7 @@ "id": "7896" }, { - "examine": "See article", + "examine": "There is a rabbit bone in here.", "durability": null, "name": "Bone in vinegar", "weight": "1", @@ -71082,7 +71833,7 @@ "id": "7897" }, { - "examine": "This needs a good polish.", + "examine": "This belongs in a museum!", "durability": null, "name": "Rabbit bone", "weight": "0.9", @@ -71090,7 +71841,7 @@ "id": "7898" }, { - "examine": "Dirty:This bone needs a good polish.", + "examine": "This bone needs a good polish.", "durability": null, "name": "Basilisk bone", "weight": "0.9", @@ -71098,7 +71849,7 @@ "id": "7899" }, { - "examine": "See article", + "examine": "There is a basilisk bone in here.", "durability": null, "name": "Bone in vinegar", "weight": "1", @@ -71106,7 +71857,7 @@ "id": "7900" }, { - "examine": "Dirty:This bone needs a good polish.", + "examine": "This belongs in a museum!", "durability": null, "name": "Basilisk bone", "weight": "0.9", @@ -71122,7 +71873,7 @@ "id": "7902" }, { - "examine": "See article", + "examine": "There is a desert lizard bone in here.", "durability": null, "name": "Bone in vinegar", "weight": "1", @@ -71130,7 +71881,7 @@ "id": "7903" }, { - "examine": "This needs a good polish.", + "examine": "This belongs in a museum!", "durability": null, "name": "Desert lizard bone", "weight": "0.9", @@ -71146,7 +71897,7 @@ "id": "7905" }, { - "examine": "See article", + "examine": "There is a cave goblin bone in here.", "durability": null, "name": "Bone in vinegar", "weight": "1", @@ -71154,7 +71905,7 @@ "id": "7906" }, { - "examine": "This needs a good polish.", + "examine": "This belongs in a museum!", "durability": null, "name": "Cave goblin skull", "weight": "0.9", @@ -71170,7 +71921,7 @@ "id": "7908" }, { - "examine": "See article", + "examine": "There is a big frog bone in here.", "durability": null, "name": "Bone in vinegar", "weight": "1", @@ -71178,7 +71929,7 @@ "id": "7909" }, { - "examine": "This needs a good polish.", + "examine": "This belongs in a museum!", "durability": null, "name": "Big frog leg", "weight": "1", @@ -71194,7 +71945,7 @@ "id": "7911" }, { - "examine": "See article", + "examine": "There is a vulture bone in here.", "durability": null, "name": "Bone in vinegar", "weight": "1", @@ -71202,7 +71953,7 @@ "id": "7912" }, { - "examine": "This needs a good polish.", + "examine": "This belongs in a museum!", "durability": null, "name": "Vulture wing", "weight": "0.9", @@ -71218,7 +71969,7 @@ "id": "7914" }, { - "examine": "See article", + "examine": "There is a jackal bone in here.", "durability": null, "name": "Bone in vinegar", "weight": "1", @@ -71226,7 +71977,7 @@ "id": "7915" }, { - "examine": "This needs a good polish.", + "examine": "This belongs in a museum!", "durability": null, "name": "Jackal bone", "weight": "0.9", @@ -71290,9 +72041,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.", @@ -71434,7 +72188,7 @@ }, { "ge_buy_limit": "20000", - "examine": "I should try cooking this./Freshly caught. Needs cooking. (Fresh Monkfish)", + "examine": "I should try cooking this.", "grand_exchange_price": "618", "durability": null, "name": "Raw monkfish", @@ -71650,6 +72404,7 @@ "id": "7973" }, { + "examine": "I should get it stuffed!", "durability": null, "name": "Crawling hand", "archery_ticket_price": "0", @@ -71714,6 +72469,7 @@ "id": "7981" }, { + "examine": "I could mount this on my wall!", "durability": null, "name": "Crawling hand", "archery_ticket_price": "0", @@ -71721,7 +72477,7 @@ "id": "7982" }, { - "examine": "I should get it stuffed!", + "examine": "I could mount this on my wall!", "durability": null, "name": "Cockatrice head", "tradeable": "false", @@ -71731,7 +72487,7 @@ "id": "7983" }, { - "examine": "I should get it stuffed!", + "examine": "I could mount this on my wall!", "durability": null, "name": "Basilisk head", "tradeable": "false", @@ -71741,7 +72497,7 @@ "id": "7984" }, { - "examine": "I should get it stuffed!", + "examine": "I could mount this on my wall!", "durability": null, "name": "Kurask head", "tradeable": "false", @@ -71751,7 +72507,7 @@ "id": "7985" }, { - "examine": "I should get it stuffed!", + "examine": "I could mount this on my wall!", "durability": null, "name": "Abyssal head", "tradeable": "false", @@ -71761,7 +72517,7 @@ "id": "7986" }, { - "examine": "I should get it stuffed!", + "examine": "I could mount these on my wall!", "durability": null, "name": "Kbd heads", "tradeable": "false", @@ -71771,7 +72527,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", @@ -71786,7 +72542,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", @@ -71801,14 +72557,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", @@ -71816,7 +72572,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", @@ -72545,6 +73301,7 @@ "id": "8248" }, { + "examine": "A trophy of a mighty slayer!", "durability": null, "name": "Crawling hand", "archery_ticket_price": "0", @@ -72552,7 +73309,7 @@ "id": "8260" }, { - "examine": "I should get it stuffed!", + "examine": "A trophy of a mighty slayer!", "durability": null, "name": "Cockatrice head", "tradeable": "false", @@ -72562,7 +73319,7 @@ "id": "8261" }, { - "examine": "I should get it stuffed!", + "examine": "A trophy of a mighty slayer!", "durability": null, "name": "Basilisk head", "tradeable": "false", @@ -72572,7 +73329,7 @@ "id": "8262" }, { - "examine": "I should get it stuffed!", + "examine": "A trophy of a mighty slayer!", "durability": null, "name": "Kurask head", "tradeable": "false", @@ -72582,7 +73339,7 @@ "id": "8263" }, { - "examine": "I should get it stuffed!", + "examine": "A trophy of a mighty slayer!!", "durability": null, "name": "Abyssal head", "tradeable": "false", @@ -72592,7 +73349,7 @@ "id": "8264" }, { - "examine": "I should get it stuffed!", + "examine": "A trophy of a mighty dragon-slayer!", "durability": null, "name": "Kbd heads", "tradeable": "false", @@ -72602,7 +73359,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", @@ -73193,6 +73950,7 @@ }, { "remove_head": "true", + "requirements": "{1,40}", "examine": "The colours represent crest.", "durability": null, "name": "Rune heraldic helm", @@ -73205,6 +73963,7 @@ }, { "remove_head": "true", + "requirements": "{1,40}", "examine": "The colours represent crest.", "durability": null, "name": "Rune heraldic helm", @@ -73217,6 +73976,7 @@ }, { "remove_head": "true", + "requirements": "{1,40}", "examine": "The colours represent crest.", "durability": null, "name": "Rune heraldic helm", @@ -73229,6 +73989,7 @@ }, { "remove_head": "true", + "requirements": "{1,40}", "examine": "The colours represent crest.", "durability": null, "name": "Rune heraldic helm", @@ -73241,6 +74002,7 @@ }, { "remove_head": "true", + "requirements": "{1,40}", "examine": "The colours represent crest.", "durability": null, "name": "Rune heraldic helm", @@ -73253,6 +74015,7 @@ }, { "remove_head": "true", + "requirements": "{1,40}", "examine": "The colours represent crest.", "durability": null, "name": "Rune heraldic helm", @@ -73265,6 +74028,7 @@ }, { "remove_head": "true", + "requirements": "{1,40}", "examine": "The colours represent crest.", "durability": null, "name": "Rune heraldic helm", @@ -73277,6 +74041,7 @@ }, { "remove_head": "true", + "requirements": "{1,40}", "examine": "The colours represent crest.", "durability": null, "name": "Rune heraldic helm", @@ -73289,6 +74054,7 @@ }, { "remove_head": "true", + "requirements": "{1,40}", "examine": "The colours represent crest.", "durability": null, "name": "Rune heraldic helm", @@ -73301,6 +74067,7 @@ }, { "remove_head": "true", + "requirements": "{1,40}", "examine": "The colours represent crest.", "durability": null, "name": "Rune heraldic helm", @@ -73313,6 +74080,7 @@ }, { "remove_head": "true", + "requirements": "{1,40}", "examine": "The colours represent crest.", "durability": null, "name": "Rune heraldic helm", @@ -73325,6 +74093,7 @@ }, { "remove_head": "true", + "requirements": "{1,40}", "examine": "The colours represent crest.", "durability": null, "name": "Rune heraldic helm", @@ -73337,6 +74106,7 @@ }, { "remove_head": "true", + "requirements": "{1,40}", "examine": "The colours represent crest.", "durability": null, "name": "Rune heraldic helm", @@ -73349,6 +74119,7 @@ }, { "remove_head": "true", + "requirements": "{1,40}", "examine": "The colours represent crest.", "durability": null, "name": "Rune heraldic helm", @@ -73361,6 +74132,7 @@ }, { "remove_head": "true", + "requirements": "{1,40}", "examine": "The colours represent crest.", "durability": null, "name": "Rune heraldic helm", @@ -73373,6 +74145,7 @@ }, { "remove_head": "true", + "requirements": "{1,40}", "examine": "The colours represent crest.", "durability": null, "name": "Rune heraldic helm", @@ -74932,6 +75705,7 @@ }, { "remove_head": "true", + "requirements": "{1,5}", "examine": "The colours represent crest.", "durability": null, "name": "Steel heraldic helm", @@ -74944,6 +75718,7 @@ }, { "remove_head": "true", + "requirements": "{1,5}", "examine": "The colours represent crest.", "durability": null, "name": "Steel heraldic helm", @@ -74956,6 +75731,7 @@ }, { "remove_head": "true", + "requirements": "{1,5}", "examine": "The colours represent crest.", "durability": null, "name": "Steel heraldic helm", @@ -74968,6 +75744,7 @@ }, { "remove_head": "true", + "requirements": "{1,5}", "examine": "The colours represent crest.", "durability": null, "name": "Steel heraldic helm", @@ -74980,6 +75757,7 @@ }, { "remove_head": "true", + "requirements": "{1,5}", "examine": "The colours represent crest.", "durability": null, "name": "Steel heraldic helm", @@ -74992,6 +75770,7 @@ }, { "remove_head": "true", + "requirements": "{1,5}", "examine": "The colours represent crest.", "durability": null, "name": "Steel heraldic helm", @@ -75004,6 +75783,7 @@ }, { "remove_head": "true", + "requirements": "{1,5}", "examine": "The colours represent crest.", "durability": null, "name": "Steel heraldic helm", @@ -75016,6 +75796,7 @@ }, { "remove_head": "true", + "requirements": "{1,5}", "examine": "The colours represent crest.", "durability": null, "name": "Steel heraldic helm", @@ -75028,6 +75809,7 @@ }, { "remove_head": "true", + "requirements": "{1,5}", "examine": "The colours represent crest.", "durability": null, "name": "Steel heraldic helm", @@ -75040,6 +75822,7 @@ }, { "remove_head": "true", + "requirements": "{1,5}", "examine": "The colours represent crest.", "durability": null, "name": "Steel heraldic helm", @@ -75052,6 +75835,7 @@ }, { "remove_head": "true", + "requirements": "{1,5}", "examine": "The colours represent crest.", "durability": null, "name": "Steel heraldic helm", @@ -75064,6 +75848,7 @@ }, { "remove_head": "true", + "requirements": "{1,5}", "examine": "The colours represent crest.", "durability": null, "name": "Steel heraldic helm", @@ -75076,6 +75861,7 @@ }, { "remove_head": "true", + "requirements": "{1,5}", "examine": "The colours represent crest.", "durability": null, "name": "Steel heraldic helm", @@ -75088,6 +75874,7 @@ }, { "remove_head": "true", + "requirements": "{1,5}", "examine": "The colours represent crest.", "durability": null, "name": "Steel heraldic helm", @@ -75100,6 +75887,7 @@ }, { "remove_head": "true", + "requirements": "{1,5}", "examine": "The colours represent crest.", "durability": null, "name": "Steel heraldic helm", @@ -75112,6 +75900,7 @@ }, { "remove_head": "true", + "requirements": "{1,5}", "examine": "The colours represent crest.", "durability": null, "name": "Steel heraldic helm", @@ -75123,436 +75912,500 @@ "equipment_slot": "0" }, { - "lendable": "true", + "requirements": "{1,40}", "examine": "A large metal shield.", - "grand_exchange_price": "32583", "durability": null, + "weight": "5.4", + "absorb": "3,0,7", + "equip_audio": "2245", + "equipment_slot": "5", + "lendable": "true", + "grand_exchange_price": "32583", "name": "Rune kiteshield", "tradeable": "true", - "weight": "5.4", "archery_ticket_price": "0", "id": "8714", - "absorb": "3,0,7", - "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0", - "equipment_slot": "5" + "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0" }, { - "lendable": "true", + "requirements": "{1,40}", "examine": "A large metal shield.", - "grand_exchange_price": "32583", "durability": null, + "weight": "5.4", + "absorb": "3,0,7", + "equip_audio": "2245", + "equipment_slot": "5", + "lendable": "true", + "grand_exchange_price": "32583", "name": "Rune kiteshield", "tradeable": "true", - "weight": "5.4", "archery_ticket_price": "0", "id": "8716", - "absorb": "3,0,7", - "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0", - "equipment_slot": "5" + "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0" }, { - "lendable": "true", + "requirements": "{1,40}", "examine": "A large metal shield.", - "grand_exchange_price": "32583", "durability": null, + "weight": "5.4", + "absorb": "3,0,7", + "equip_audio": "2245", + "equipment_slot": "5", + "lendable": "true", + "grand_exchange_price": "32583", "name": "Rune kiteshield", "tradeable": "true", - "weight": "5.4", "archery_ticket_price": "0", "id": "8718", - "absorb": "3,0,7", - "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0", - "equipment_slot": "5" + "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0" }, { - "lendable": "true", + "requirements": "{1,40}", "examine": "A large metal shield.", - "grand_exchange_price": "32583", "durability": null, + "weight": "5.4", + "absorb": "3,0,7", + "equip_audio": "2245", + "equipment_slot": "5", + "lendable": "true", + "grand_exchange_price": "32583", "name": "Rune kiteshield", "tradeable": "true", - "weight": "5.4", "archery_ticket_price": "0", "id": "8720", - "absorb": "3,0,7", - "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0", - "equipment_slot": "5" + "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0" }, { - "lendable": "true", + "requirements": "{1,40}", "examine": "A large metal shield.", - "grand_exchange_price": "32583", "durability": null, + "weight": "5.4", + "absorb": "3,0,7", + "equip_audio": "2245", + "equipment_slot": "5", + "lendable": "true", + "grand_exchange_price": "32583", "name": "Rune kiteshield", "tradeable": "true", - "weight": "5.4", "archery_ticket_price": "0", "id": "8722", - "absorb": "3,0,7", - "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0", - "equipment_slot": "5" + "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0" }, { - "lendable": "true", + "requirements": "{1,40}", "examine": "A large metal shield.", - "grand_exchange_price": "32583", "durability": null, + "weight": "5.4", + "absorb": "3,0,7", + "equip_audio": "2245", + "equipment_slot": "5", + "lendable": "true", + "grand_exchange_price": "32583", "name": "Rune kiteshield", "tradeable": "true", - "weight": "5.4", "archery_ticket_price": "0", "id": "8724", - "absorb": "3,0,7", - "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0", - "equipment_slot": "5" + "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0" }, { - "lendable": "true", + "requirements": "{1,40}", "examine": "A large metal shield.", - "grand_exchange_price": "32583", "durability": null, + "weight": "5.4", + "absorb": "3,0,7", + "equip_audio": "2245", + "equipment_slot": "5", + "lendable": "true", + "grand_exchange_price": "32583", "name": "Rune kiteshield", "tradeable": "true", - "weight": "5.4", "archery_ticket_price": "0", "id": "8726", - "absorb": "3,0,7", - "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0", - "equipment_slot": "5" + "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0" }, { - "lendable": "true", + "requirements": "{1,40}", "examine": "A large metal shield.", - "grand_exchange_price": "32583", "durability": null, + "weight": "5.4", + "absorb": "3,0,7", + "equip_audio": "2245", + "equipment_slot": "5", + "lendable": "true", + "grand_exchange_price": "32583", "name": "Rune kiteshield", "tradeable": "true", - "weight": "5.4", "archery_ticket_price": "0", "id": "8728", - "absorb": "3,0,7", - "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0", - "equipment_slot": "5" + "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0" }, { - "lendable": "true", + "requirements": "{1,40}", "examine": "A large metal shield.", - "grand_exchange_price": "32583", "durability": null, + "weight": "5.4", + "absorb": "3,0,7", + "equip_audio": "2245", + "equipment_slot": "5", + "lendable": "true", + "grand_exchange_price": "32583", "name": "Rune kiteshield", "tradeable": "true", - "weight": "5.4", "archery_ticket_price": "0", "id": "8730", - "absorb": "3,0,7", - "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0", - "equipment_slot": "5" + "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0" }, { - "lendable": "true", + "requirements": "{1,40}", "examine": "A large metal shield.", - "grand_exchange_price": "32583", "durability": null, + "weight": "5.4", + "absorb": "3,0,7", + "equip_audio": "2245", + "equipment_slot": "5", + "lendable": "true", + "grand_exchange_price": "32583", "name": "Rune kiteshield", "tradeable": "true", - "weight": "5.4", "archery_ticket_price": "0", "id": "8732", - "absorb": "3,0,7", - "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0", - "equipment_slot": "5" + "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0" }, { - "lendable": "true", + "requirements": "{1,40}", "examine": "A large metal shield.", - "grand_exchange_price": "32583", "durability": null, + "weight": "5.4", + "absorb": "3,0,7", + "equip_audio": "2245", + "equipment_slot": "5", + "lendable": "true", + "grand_exchange_price": "32583", "name": "Rune kiteshield", "tradeable": "true", - "weight": "5.4", "archery_ticket_price": "0", "id": "8734", - "absorb": "3,0,7", - "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0", - "equipment_slot": "5" + "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0" }, { - "lendable": "true", + "requirements": "{1,40}", "examine": "A large metal shield.", - "grand_exchange_price": "32583", "durability": null, + "weight": "5.4", + "absorb": "3,0,7", + "equip_audio": "2245", + "equipment_slot": "5", + "lendable": "true", + "grand_exchange_price": "32583", "name": "Rune kiteshield", "tradeable": "true", - "weight": "5.4", "archery_ticket_price": "0", "id": "8736", - "absorb": "3,0,7", - "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0", - "equipment_slot": "5" + "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0" }, { - "lendable": "true", + "requirements": "{1,40}", "examine": "A large metal shield.", - "grand_exchange_price": "32583", "durability": null, + "weight": "5.4", + "absorb": "3,0,7", + "equip_audio": "2245", + "equipment_slot": "5", + "lendable": "true", + "grand_exchange_price": "32583", "name": "Rune kiteshield", "tradeable": "true", - "weight": "5.4", "archery_ticket_price": "0", "id": "8738", - "absorb": "3,0,7", - "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0", - "equipment_slot": "5" + "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0" }, { - "lendable": "true", + "requirements": "{1,40}", "examine": "A large metal shield.", - "grand_exchange_price": "32583", "durability": null, + "weight": "5.4", + "absorb": "3,0,7", + "equip_audio": "2245", + "equipment_slot": "5", + "lendable": "true", + "grand_exchange_price": "32583", "name": "Rune kiteshield", "tradeable": "true", - "weight": "5.4", "archery_ticket_price": "0", "id": "8740", - "absorb": "3,0,7", - "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0", - "equipment_slot": "5" + "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0" }, { - "lendable": "true", + "requirements": "{1,40}", "examine": "A large metal shield.", - "grand_exchange_price": "32583", "durability": null, + "weight": "5.4", + "absorb": "3,0,7", + "equip_audio": "2245", + "equipment_slot": "5", + "lendable": "true", + "grand_exchange_price": "32583", "name": "Rune kiteshield", "tradeable": "true", - "weight": "5.4", "archery_ticket_price": "0", "id": "8742", - "absorb": "3,0,7", - "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0", - "equipment_slot": "5" + "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0" }, { - "lendable": "true", + "requirements": "{1,40}", "examine": "A large metal shield.", - "grand_exchange_price": "32583", "durability": null, + "weight": "5.4", + "absorb": "3,0,7", + "equip_audio": "2245", + "equipment_slot": "5", + "lendable": "true", + "grand_exchange_price": "32583", "name": "Rune kiteshield", "tradeable": "true", - "weight": "5.4", "archery_ticket_price": "0", "id": "8744", - "absorb": "3,0,7", - "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,0,0,0,0", - "equipment_slot": "5" + "bonuses": "0,0,0,-8,-2,44,48,46,-1,46,40,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": "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", @@ -75804,9 +76657,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", @@ -75834,6 +76689,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" }, @@ -75845,6 +76701,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" }, @@ -75857,6 +76714,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" }, @@ -75869,6 +76727,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" }, @@ -75881,6 +76740,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" }, @@ -75893,6 +76753,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" }, @@ -75905,6 +76766,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" }, @@ -76041,6 +76903,7 @@ "weight": "0.4", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "defence_anim": "378", "equipment_slot": "3", @@ -76072,6 +76935,7 @@ "weight": "0.4", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "defence_anim": "378", "equipment_slot": "3", @@ -76103,6 +76967,7 @@ "weight": "0.4", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "defence_anim": "378", "equipment_slot": "3", @@ -76134,6 +76999,7 @@ "weight": "0.4", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "defence_anim": "378", "equipment_slot": "3", @@ -76235,15 +77101,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", @@ -77197,6 +78064,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" }, { @@ -77277,6 +78145,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.", @@ -77910,30 +78779,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.", @@ -77999,6 +78869,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.", @@ -78023,6 +78894,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.", @@ -78047,6 +78919,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.", @@ -78331,6 +79204,7 @@ "durability": null, "weight": "4", "weapon_interface": "17", + "equip_audio": "2244", "render_anim": "175", "attack_audios": "2700,0,0,0", "name": "Bronze crossbow" @@ -78355,6 +79229,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", @@ -78391,6 +79266,7 @@ "durability": null, "weight": "4", "weapon_interface": "17", + "equip_audio": "2244", "render_anim": "175", "attack_audios": "2700,0,0,0", "name": "Iron crossbow" @@ -78427,6 +79303,7 @@ "durability": null, "weight": "5", "weapon_interface": "17", + "equip_audio": "2244", "render_anim": "175", "attack_audios": "2700,0,0,0", "name": "Steel crossbow" @@ -78463,6 +79340,7 @@ "durability": null, "weight": "6", "weapon_interface": "17", + "equip_audio": "2244", "render_anim": "175", "attack_audios": "2700,0,0,0", "name": "Mith crossbow" @@ -78500,6 +79378,7 @@ "durability": null, "weight": "6", "weapon_interface": "17", + "equip_audio": "2244", "render_anim": "175", "attack_audios": "2700,0,0,0", "name": "Adamant crossbow" @@ -78537,6 +79416,7 @@ "durability": null, "weight": "6", "weapon_interface": "17", + "equip_audio": "2244", "render_anim": "175", "lendable": "true", "attack_audios": "2700,0,0,0", @@ -81189,6 +82069,7 @@ "durability": null, "weight": "2", "absorb": "1,0,2", + "equip_audio": "2238", "remove_beard": "true", "equipment_slot": "0", "grand_exchange_price": "4619", @@ -81499,10 +82380,12 @@ "weight": "1.8", "attack_speed": "4", "weapon_interface": "5", + "equip_audio": "2248", "render_anim": "2584", "defence_anim": "397", "equipment_slot": "3", "attack_anims": "381,390,390,390", + "attack_audios": "2500,2500,2517,2500", "name": "Training sword", "archery_ticket_price": "0", "id": "9703", @@ -83451,6 +84334,7 @@ "destroy": "true", "archery_ticket_price": "0", "id": "9920", + "equip_audio": "3227", "remove_beard": "true", "equipment_slot": "0" }, @@ -84101,7 +84985,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", @@ -84900,6 +85786,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" }, { @@ -84915,15 +85802,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", @@ -84945,6 +85833,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" }, { @@ -84960,15 +85849,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", @@ -84983,15 +85873,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", @@ -85721,30 +86612,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", @@ -86951,6 +87843,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)", @@ -86976,6 +87869,7 @@ "durability": null, "weight": "3", "absorb": "1,0,3", + "equip_audio": "2240", "equipment_slot": "0", "grand_exchange_price": "20800", "name": "Rune helm (h2)", @@ -87001,6 +87895,7 @@ "durability": null, "weight": "4", "absorb": "1,0,3", + "equip_audio": "2240", "equipment_slot": "0", "grand_exchange_price": "20800", "name": "Rune helm (h3)", @@ -87026,6 +87921,7 @@ "durability": null, "weight": "2", "absorb": "1,0,3", + "equip_audio": "2240", "equipment_slot": "0", "grand_exchange_price": "21100", "name": "Rune helm (h4)", @@ -87051,6 +87947,7 @@ "durability": null, "weight": "2", "absorb": "1,0,3", + "equip_audio": "2240", "equipment_slot": "0", "grand_exchange_price": "24900", "name": "Rune helm (h5)", @@ -87076,6 +87973,7 @@ "durability": null, "weight": "3", "absorb": "1,0,2", + "equip_audio": "2240", "equipment_slot": "0", "grand_exchange_price": "31500", "name": "Adamant helm (h1)", @@ -87101,6 +87999,7 @@ "durability": null, "weight": "3", "absorb": "1,0,2", + "equip_audio": "2240", "equipment_slot": "0", "grand_exchange_price": "10100", "name": "Adamant helm (h2)", @@ -87126,6 +88025,7 @@ "durability": null, "weight": "3", "absorb": "1,0,2", + "equip_audio": "2240", "equipment_slot": "0", "grand_exchange_price": "8797", "name": "Adamant helm (h3)", @@ -87151,6 +88051,7 @@ "durability": null, "weight": "3", "absorb": "1,0,2", + "equip_audio": "2240", "equipment_slot": "0", "grand_exchange_price": "10900", "name": "Adamant helm (h4)", @@ -87176,6 +88077,7 @@ "durability": null, "weight": "3", "absorb": "1,0,2", + "equip_audio": "2240", "equipment_slot": "0", "grand_exchange_price": "17100", "name": "Adamant helm (h5)", @@ -87200,6 +88102,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)", @@ -87224,6 +88127,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)", @@ -87248,6 +88152,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", @@ -87273,6 +88178,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)", @@ -87297,6 +88203,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)", @@ -87317,7 +88224,7 @@ { "remove_sleeves": "true", "ge_buy_limit": "2", - "examine": "See table", + "examine": "Bob says: 'Never give your password out to anyone.'", "grand_exchange_price": "8196", "durability": null, "name": "Bob shirt", @@ -87339,7 +88246,7 @@ { "remove_sleeves": "true", "ge_buy_limit": "2", - "examine": "See table", + "examine": "Bob says: 'Always check the second trade screen.'", "grand_exchange_price": "14700", "durability": null, "name": "Bob shirt", @@ -87361,7 +88268,7 @@ { "remove_sleeves": "true", "ge_buy_limit": "2", - "examine": "See table", + "examine": "Bob says: 'Never trade in the wilderness!'", "grand_exchange_price": "8403", "durability": null, "name": "Bob shirt", @@ -87383,7 +88290,7 @@ { "remove_sleeves": "true", "ge_buy_limit": "2", - "examine": "See table", + "examine": "Bob says: 'A bank pin will keep your items secure.'", "grand_exchange_price": "9230", "durability": null, "name": "Bob shirt", @@ -87405,7 +88312,7 @@ { "remove_sleeves": "true", "ge_buy_limit": "2", - "examine": "See table", + "examine": "Bob says: 'Keep your computer keylogger free and virus scanned.'", "grand_exchange_price": "8197", "durability": null, "name": "Bob shirt", @@ -87517,6 +88424,7 @@ "durability": null, "weight": "1", "absorb": "0,3,1", + "equip_audio": "2238", "equipment_slot": "0", "lendable": "true", "grand_exchange_price": "6100000", @@ -87541,6 +88449,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", @@ -87665,6 +88574,7 @@ "durability": null, "weight": "2", "absorb": "3,0,6", + "equip_audio": "2242", "equipment_slot": "7", "lendable": "true", "grand_exchange_price": "92800000", @@ -87690,6 +88600,7 @@ "durability": null, "weight": "9", "absorb": "4,0,9", + "equip_audio": "2234", "equipment_slot": "4", "lendable": "true", "remove_sleeves": "true", @@ -87717,6 +88628,7 @@ "durability": null, "weight": "1", "absorb": "2,0,4", + "equip_audio": "2240", "equipment_slot": "0", "lendable": "true", "grand_exchange_price": "41600000", @@ -87742,6 +88654,7 @@ "durability": null, "weight": "2", "absorb": "5,0,11", + "equip_audio": "2245", "equipment_slot": "5", "lendable": "true", "grand_exchange_price": "65100000", @@ -87907,15 +88820,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", @@ -87933,6 +88847,7 @@ "durability": null, "weight": "6", "absorb": "0,6,3", + "equip_audio": "2241", "equipment_slot": "4", "remove_sleeves": "true", "grand_exchange_price": "155200", @@ -87952,12 +88867,13 @@ "id": "10371" }, { - "requirements": "{4,70}", + "requirements": "{1,40}-{4,70}", "ge_buy_limit": "2", "examine": "Zamorak blessed dragonhide chaps.", "durability": null, "weight": "5", "absorb": "0,4,2", + "equip_audio": "2241", "equipment_slot": "7", "grand_exchange_price": "35100", "name": "Zamorak chaps", @@ -87982,6 +88898,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", @@ -88003,15 +88920,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", @@ -88030,6 +88948,7 @@ "durability": null, "weight": "6", "absorb": "0,6,3", + "equip_audio": "2241", "equipment_slot": "4", "remove_sleeves": "true", "grand_exchange_price": "39800", @@ -88049,12 +88968,13 @@ "id": "10379" }, { - "requirements": "{4,70}", + "requirements": "{1,40}-{4,70}", "ge_buy_limit": "2", "examine": "Guthix blessed dragonhide chaps.", "durability": null, "weight": "5", "absorb": "0,4,2", + "equip_audio": "2241", "equipment_slot": "7", "grand_exchange_price": "7346", "name": "Guthix chaps", @@ -88080,6 +89000,7 @@ "durability": null, "weight": "0.85", "absorb": "0,3,1", + "equip_audio": "2238", "equipment_slot": "0", "grand_exchange_price": "27500", "name": "Guthix coif", @@ -88101,15 +89022,16 @@ "requirements": "{1,70}-{4,40}", "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", @@ -88129,6 +89051,7 @@ "destroy": "true", "weight": "6", "absorb": "0,6,3", + "equip_audio": "2241", "equipment_slot": "4", "remove_sleeves": "true", "grand_exchange_price": "164100", @@ -88148,18 +89071,19 @@ "id": "10387" }, { - "requirements": "{4,70}", + "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", @@ -88177,6 +89101,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", @@ -88715,28 +89640,31 @@ "id": "10439" }, { - "requirements": "{5,60}", "ge_buy_limit": "2", "turn90cw_anim": "1207", "examine": "A Saradomin crozier.", "walk_anim": "1205", - "durability": null, - "weight": "2", "turn90ccw_anim": "1208", "attack_speed": "5", - "weapon_interface": "1", "turn180_anim": "1206", - "render_anim": "28", + "defence_anim": "420", "equipment_slot": "3", + "attack_anims": "419,419,419,419", "grand_exchange_price": "8611", "stand_anim": "813", - "name": "Saradomin crozier", "tradeable": "true", "run_anim": "1210", "archery_ticket_price": "0", "id": "10440", "stand_turn_anim": "1209", - "bonuses": "7,-1,25,10,0,2,3,1,10,0,0,32,6,0,0" + "bonuses": "7,-1,25,10,0,2,3,1,10,0,0,32,6,0,0", + "requirements": "{5,60}", + "durability": null, + "weight": "2", + "weapon_interface": "1", + "render_anim": "28", + "attack_audios": "2555,0,0,0", + "name": "Saradomin crozier" }, { "ge_buy_limit": "2", @@ -88748,28 +89676,31 @@ "id": "10441" }, { - "requirements": "{5,60}", "ge_buy_limit": "2", "turn90cw_anim": "1207", "examine": "A Guthix crozier.", "walk_anim": "1205", - "durability": null, - "weight": "2", "turn90ccw_anim": "1208", "attack_speed": "5", - "weapon_interface": "1", "turn180_anim": "1206", - "render_anim": "28", + "defence_anim": "420", "equipment_slot": "3", + "attack_anims": "419,419,419,419", "grand_exchange_price": "2925", "stand_anim": "813", - "name": "Guthix crozier", "tradeable": "true", "run_anim": "1210", "archery_ticket_price": "0", "id": "10442", "stand_turn_anim": "1209", - "bonuses": "7,-1,25,10,0,2,3,1,10,0,0,32,6,0,0" + "bonuses": "7,-1,25,10,0,2,3,1,10,0,0,32,6,0,0", + "requirements": "{5,60}", + "durability": null, + "weight": "2", + "weapon_interface": "1", + "render_anim": "28", + "attack_audios": "2555,0,0,0", + "name": "Guthix crozier" }, { "ge_buy_limit": "2", @@ -88781,30 +89712,34 @@ "id": "10443" }, { - "requirements": "{5,60}", "ge_buy_limit": "2", "turn90cw_anim": "1207", "examine": "A Zamorak crozier.", "walk_anim": "1205", - "durability": null, - "weight": "2", "turn90ccw_anim": "1208", "attack_speed": "5", - "weapon_interface": "1", "turn180_anim": "1206", - "render_anim": "28", + "defence_anim": "420", "equipment_slot": "3", + "attack_anims": "419,419,419,419", "grand_exchange_price": "2794", "stand_anim": "813", - "name": "Zamorak crozier", "tradeable": "true", "run_anim": "1210", "archery_ticket_price": "0", "id": "10444", "stand_turn_anim": "1209", - "bonuses": "7,-1,25,10,0,2,3,1,10,0,0,32,6,0,0" + "bonuses": "7,-1,25,10,0,2,3,1,10,0,0,32,6,0,0", + "requirements": "{5,60}", + "durability": null, + "weight": "2", + "weapon_interface": "1", + "render_anim": "28", + "attack_audios": "2555,0,0,0", + "name": "Zamorak crozier" }, { + "attack_anims": "", "ge_buy_limit": "2", "grand_exchange_price": "2794", "durability": null, @@ -89714,13 +90649,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}", @@ -89756,6 +90692,7 @@ "destroy": "true", "weight": "4", "absorb": "3,0,6", + "equip_audio": "2239", "equipment_slot": "4", "remove_sleeves": "true", "name": "Fighter torso", @@ -90369,6 +91306,7 @@ }, { "remove_head": "true", + "requirements": "{1,40}", "examine": "Protective headwear made from crabs. Better than it sounds.", "durability": null, "weight": "2.7", @@ -90384,6 +91322,7 @@ }, { "remove_head": "true", + "requirements": "{1,40}-{4,40}", "examine": "A helm fit for any Fremennik ranger.", "durability": null, "weight": "1", @@ -90437,6 +91376,7 @@ "durability": null, "weight": "9.9", "absorb": "1,0,2", + "equip_audio": "2239", "equipment_slot": "4", "remove_sleeves": "true", "grand_exchange_price": "4595", @@ -90995,6 +91935,7 @@ "equipment_slot": "1" }, { + "requirements": "{1,10}", "examine": "A black kitesheild with a heraldic design.", "grand_exchange_price": "15249", "durability": null, @@ -91008,33 +91949,37 @@ "equipment_slot": "5" }, { + "requirements": "{1,30}", + "ge_buy_limit": "2", "examine": "A shield with a heraldic design.", - "grand_exchange_price": "16339", "durability": null, + "weight": "5", + "absorb": "3,0,6", + "equipment_slot": "5", + "grand_exchange_price": "16339", "name": "Adamant shield(h1)", "tradeable": "true", - "weight": "5", "archery_ticket_price": "0", "id": "10666", - "absorb": "3,0,6", - "bonuses": "0,0,0,-8,-2,27,31,29,-1,29,0,0,0,0,0", - "equipment_slot": "5" + "bonuses": "0,0,0,-8,-2,27,31,29,-1,29,0,0,0,0,0" }, { "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}", "examine": "A black kiteshield with a heraldic design.", "grand_exchange_price": "9751", "durability": null, @@ -91048,33 +91993,37 @@ "equipment_slot": "5" }, { + "requirements": "{1,30}", + "ge_buy_limit": "2", "examine": "A shield with a heraldic design.", - "grand_exchange_price": "3353", "durability": null, + "weight": "5", + "absorb": "3,0,6", + "equipment_slot": "5", + "grand_exchange_price": "3353", "name": "Adamant shield(h2)", "tradeable": "true", - "weight": "5", "archery_ticket_price": "0", "id": "10669", - "absorb": "3,0,6", - "bonuses": "0,0,0,-8,-2,27,31,29,-1,29,0,0,0,0,0", - "equipment_slot": "5" + "bonuses": "0,0,0,-8,-2,27,31,29,-1,29,0,0,0,0,0" }, { "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}", "examine": "A black shield with a heraldic design.", "grand_exchange_price": "6791", "durability": null, @@ -91088,33 +92037,37 @@ "equipment_slot": "5" }, { + "requirements": "{1,30}", + "ge_buy_limit": "2", "examine": "A shield with a heraldic design.", - "grand_exchange_price": "5566", "durability": null, + "weight": "5", + "absorb": "3,0,6", + "equipment_slot": "5", + "grand_exchange_price": "5566", "name": "Adamant shield(h3)", "tradeable": "true", - "weight": "5", "archery_ticket_price": "0", "id": "10672", - "absorb": "3,0,6", - "bonuses": "0,0,0,-8,-2,27,31,29,-1,29,0,0,0,0,0", - "equipment_slot": "5" + "bonuses": "0,0,0,-8,-2,27,31,29,-1,29,0,0,0,0,0" }, { "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}", "examine": "A black shield with a heraldic design.", "grand_exchange_price": "5159", "durability": null, @@ -91128,33 +92081,37 @@ "equipment_slot": "5" }, { + "requirements": "{1,30}", + "ge_buy_limit": "2", "examine": "A shield with a heraldic design.", - "grand_exchange_price": "3219", "durability": null, + "weight": "5", + "absorb": "3,0,6", + "equipment_slot": "5", + "grand_exchange_price": "3219", "name": "Adamant shield(h4)", "tradeable": "true", - "weight": "5", "archery_ticket_price": "0", "id": "10675", - "absorb": "3,0,6", - "bonuses": "0,0,0,-8,-2,27,31,29,-1,29,0,0,0,0,0", - "equipment_slot": "5" + "bonuses": "0,0,0,-8,-2,27,31,29,-1,29,0,0,0,0,0" }, { "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}", "examine": "A black shield with a heraldic design.", "grand_exchange_price": "8451", "durability": null, @@ -91168,31 +92125,34 @@ "equipment_slot": "5" }, { + "requirements": "{1,30}", + "ge_buy_limit": "2", "examine": "A shield with a heraldic design.", - "grand_exchange_price": "5905", "durability": null, + "weight": "5", + "absorb": "3,0,6", + "equipment_slot": "5", + "grand_exchange_price": "5905", "name": "Adamant shield(h5)", "tradeable": "true", - "weight": "5", "archery_ticket_price": "0", "id": "10678", - "absorb": "3,0,6", - "bonuses": "0,0,0,-8,-2,27,31,29,-1,29,0,0,0,0,0", - "equipment_slot": "5" + "bonuses": "0,0,0,-8,-2,27,31,29,-1,29,0,0,0,0,0" }, { "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!", @@ -91226,6 +92186,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" }, { @@ -91234,6 +92195,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" }, { @@ -91242,6 +92204,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" }, { @@ -91250,6 +92213,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" }, { @@ -91305,32 +92269,36 @@ "equipment_slot": "10" }, { - "remove_sleeves": "true", + "requirements": "{1,10}", "examine": "Black platebody with trim.", - "grand_exchange_price": "90744", "durability": null, + "weight": "9.07", + "absorb": "1,0,2", + "equip_audio": "2239", + "equipment_slot": "4", + "remove_sleeves": "true", + "grand_exchange_price": "90744", "name": "Black platebody (t)", "tradeable": "true", - "weight": "9.07", "archery_ticket_price": "0", "id": "10690", - "absorb": "1,0,2", - "bonuses": "0,0,0,-30,-10,41,40,30,-6,40,0,0,0,0,0", - "equipment_slot": "4" + "bonuses": "0,0,0,-30,-10,41,40,30,-6,40,0,0,0,0,0" }, { - "remove_sleeves": "true", + "requirements": "{1,10}", "examine": "Black platebody with gold trim.", - "grand_exchange_price": "230800", "durability": null, + "weight": "9.07", + "absorb": "1,0,2", + "equip_audio": "2239", + "equipment_slot": "4", + "remove_sleeves": "true", + "grand_exchange_price": "230800", "name": "Black platebody (g)", "tradeable": "true", - "weight": "9.07", "archery_ticket_price": "0", "id": "10691", - "absorb": "1,0,2", - "bonuses": "0,0,0,-30,-10,41,40,30,-6,40,0,0,0,0,0", - "equipment_slot": "4" + "bonuses": "0,0,0,-30,-10,41,40,30,-6,40,0,0,0,0,0" }, { "lendable": "true", @@ -91393,236 +92361,277 @@ "equipment_slot": "10" }, { - "remove_sleeves": "true", + "requirements": "{1,30}", + "ge_buy_limit": "2", "examine": "Adamant platebody with trim.", - "grand_exchange_price": "79300", "durability": null, + "weight": "9.07", + "absorb": "2,0,4", + "equip_audio": "2239", + "equipment_slot": "4", + "remove_sleeves": "true", + "grand_exchange_price": "79300", "name": "Adam platebody (t)", "tradeable": "true", - "weight": "9.07", "archery_ticket_price": "0", "id": "10697", - "absorb": "2,0,4", - "bonuses": "0,0,0,-30,-10,65,63,55,-6,63,0,0,0,0,0", - "equipment_slot": "4" + "bonuses": "0,0,0,-30,-10,65,63,55,-6,63,0,0,0,0,0" }, { - "remove_sleeves": "true", + "requirements": "{1,30}", + "ge_buy_limit": "2", "examine": "Adamant platebody with gold trim.", - "grand_exchange_price": "207912", "durability": null, + "weight": "9.07", + "absorb": "2,0,4", + "equip_audio": "2239", + "equipment_slot": "4", + "remove_sleeves": "true", + "grand_exchange_price": "207912", "name": "Adam platebody (g)", "tradeable": "true", - "weight": "9.07", "archery_ticket_price": "0", "id": "10698", - "absorb": "2,0,4", - "bonuses": "0,0,0,-30,-10,65,63,55,-6,63,0,0,0,0,0", - "equipment_slot": "4" + "bonuses": "0,0,0,-30,-10,65,63,55,-6,63,0,0,0,0,0" }, { "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", + "requirements": "{1,40}", "examine": "A rune helmet with a heraldic design.", - "grand_exchange_price": "26127", "durability": null, + "weight": "3", + "absorb": "1,0,3", + "equip_audio": "2240", + "equipment_slot": "0", + "grand_exchange_price": "26127", "name": "Rune helm (h2)", "tradeable": "true", - "weight": "3", "archery_ticket_price": "0", "id": "10705", - "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", + "requirements": "{1,40}", "examine": "A rune helmet with a heraldic design.", - "grand_exchange_price": "37333", "durability": null, + "weight": "4", + "absorb": "1,0,3", + "equip_audio": "2240", + "equipment_slot": "0", + "grand_exchange_price": "37333", "name": "Rune helm (h3)", "tradeable": "true", - "weight": "4", "archery_ticket_price": "0", "id": "10706", - "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", + "requirements": "{1,40}", "examine": "A rune helmet with a heraldic design.", - "grand_exchange_price": "29600", "durability": null, + "weight": "2", + "absorb": "1,0,3", + "equip_audio": "2240", + "equipment_slot": "0", + "grand_exchange_price": "29600", "name": "Rune helm (h4)", "tradeable": "true", - "weight": "2", "archery_ticket_price": "0", "id": "10707", - "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", + "requirements": "{1,40}", "examine": "A rune helmet with a heraldic design.", - "grand_exchange_price": "41325", "durability": null, + "weight": "2", + "absorb": "1,0,3", + "equip_audio": "2240", + "equipment_slot": "0", + "grand_exchange_price": "41325", "name": "Rune helm (h5)", "tradeable": "true", - "weight": "2", "archery_ticket_price": "0", "id": "10708", - "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", + "requirements": "{1,30}", + "ge_buy_limit": "2", "examine": "An adamant helmet with a heraldic design.", - "grand_exchange_price": "28407", "durability": null, + "weight": "3", + "absorb": "1,0,2", + "equip_audio": "2240", + "equipment_slot": "0", + "grand_exchange_price": "28407", "name": "Adamant helm (h1)", "tradeable": "true", - "weight": "3", "archery_ticket_price": "0", "id": "10709", - "absorb": "1,0,2", - "bonuses": "0,0,0,-6,-2,19,21,16,-1,19,0,0,0,0,0", - "equipment_slot": "0" + "bonuses": "0,0,0,-6,-2,19,21,16,-1,19,0,0,0,0,0" }, { "remove_head": "true", + "requirements": "{1,30}", + "ge_buy_limit": "2", "examine": "An adamant helmet with a heraldic design.", - "grand_exchange_price": "9839", "durability": null, + "weight": "3", + "absorb": "1,0,2", + "equip_audio": "2240", + "equipment_slot": "0", + "grand_exchange_price": "9839", "name": "Adamant helm (h2)", "tradeable": "true", - "weight": "3", "archery_ticket_price": "0", "id": "10710", - "absorb": "1,0,2", - "bonuses": "0,0,0,-6,-2,19,21,16,-1,19,0,0,0,0,0", - "equipment_slot": "0" + "bonuses": "0,0,0,-6,-2,19,21,16,-1,19,0,0,0,0,0" }, { "remove_head": "true", + "requirements": "{1,30}", + "ge_buy_limit": "2", "examine": "An adamant helmet with a heraldic design.", - "grand_exchange_price": "8485", "durability": null, + "weight": "3", + "absorb": "1,0,2", + "equip_audio": "2240", + "equipment_slot": "0", + "grand_exchange_price": "8485", "name": "Adamant helm (h3)", "tradeable": "true", - "weight": "3", "archery_ticket_price": "0", "id": "10711", - "absorb": "1,0,2", - "bonuses": "0,0,0,-6,-2,19,21,16,-1,19,0,0,0,0,0", - "equipment_slot": "0" + "bonuses": "0,0,0,-6,-2,19,21,16,-1,19,0,0,0,0,0" }, { "remove_head": "true", + "requirements": "{1,30}", + "ge_buy_limit": "2", "examine": "An Adamant helmet with a heraldic design.", - "grand_exchange_price": "10774", "durability": null, + "weight": "3", + "absorb": "1,0,2", + "equip_audio": "2240", + "equipment_slot": "0", + "grand_exchange_price": "10774", "name": "Adamant helm (h4)", "tradeable": "true", - "weight": "3", "archery_ticket_price": "0", "id": "10712", - "absorb": "1,0,2", - "bonuses": "0,0,0,-6,-2,19,21,16,-1,19,0,0,0,0,0", - "equipment_slot": "0" + "bonuses": "0,0,0,-6,-2,19,21,16,-1,19,0,0,0,0,0" }, { "remove_head": "true", + "requirements": "{1,30}", + "ge_buy_limit": "2", "examine": "An adamant helmet with a heraldic design.", - "grand_exchange_price": "17390", "durability": null, + "weight": "3", + "absorb": "1,0,2", + "equip_audio": "2240", + "equipment_slot": "0", + "grand_exchange_price": "17390", "name": "Adamant helm (h5)", "tradeable": "true", - "weight": "3", "archery_ticket_price": "0", "id": "10713", - "absorb": "1,0,2", - "bonuses": "0,0,0,-6,-2,19,21,16,-1,19,0,0,0,0,0", - "equipment_slot": "0" + "bonuses": "0,0,0,-6,-2,19,21,16,-1,19,0,0,0,0,0" }, { "remove_sleeves": "true", @@ -91737,6 +92746,7 @@ "destroy": "true", "archery_ticket_price": "0", "id": "10723", + "equip_audio": "3227", "remove_beard": "true", "equipment_slot": "0" }, @@ -91797,7 +92807,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.", @@ -91818,7 +92830,7 @@ }, { "turn90cw_anim": "821", - "examine": "Perhaps not the most powerful weapon in RuneScape.", + "examine": "Perhaps not the most powerful weapon in 2009Scape.", "walk_anim": "1830", "durability": null, "weight": "0.4", @@ -91826,10 +92838,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", @@ -91863,6 +92877,7 @@ "attack_speed": "7", "two_handed": "true", "turn180_anim": "820", + "equip_audio": "2247", "render_anim": "1383", "equipment_slot": "3", "stand_anim": "847", @@ -92094,10 +93109,12 @@ "equipment_slot": "0" }, { + "requirements": "{1,40}", "examine": "Rune platebody in the colours of Zamorak.", "durability": null, "weight": "9", "absorb": "3,0,6", + "equip_audio": "2239", "equipment_slot": "4", "lendable": "true", "remove_sleeves": "true", @@ -92109,6 +93126,7 @@ "bonuses": "0,0,0,-30,-10,82,80,72,-6,80,0,0,0,0,0" }, { + "requirements": "{1,40}", "examine": "Rune platebody in the colours of Saradomin.", "durability": null, "weight": "9.07", @@ -92124,10 +93142,12 @@ "bonuses": "0,0,0,-30,-10,82,80,72,-6,80,0,0,0,0,0" }, { + "requirements": "{1,40}", "examine": "Rune platebody in the colours of Guthix.", "durability": null, "weight": "9.07", "absorb": "3,0,6", + "equip_audio": "2239", "equipment_slot": "4", "lendable": "true", "remove_sleeves": "true", @@ -92139,10 +93159,12 @@ "bonuses": "0,0,0,-30,-10,82,80,72,-6,80,0,0,0,0,0" }, { - "examine": "Rune platebody with complete gold trim & plating.", + "requirements": "{1,40}", + "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", @@ -92154,6 +93176,7 @@ "bonuses": "0,0,0,-30,-10,82,80,72,-6,80,0,0,0,0,0" }, { + "requirements": "{5,20}", "remove_sleeves": "true", "examine": "Blessed vestments of Saradomin.", "grand_exchange_price": "106389", @@ -92166,6 +93189,7 @@ "equipment_slot": "4" }, { + "requirements": "{5,20}", "remove_sleeves": "true", "examine": "Zamorak Vestments.", "grand_exchange_price": "151000", @@ -92179,6 +93203,7 @@ "equipment_slot": "4" }, { + "requirements": "{5,20}", "remove_sleeves": "true", "examine": "Blessed vestments of Guthix.", "grand_exchange_price": "80181", @@ -92192,20 +93217,22 @@ "equipment_slot": "4" }, { - "remove_sleeves": "true", + "requirements": "{1,40}-{4,70}", "examine": "Zamorak blessed dragonhide body armour.", - "grand_exchange_price": "84966", "durability": null, + "weight": "6", + "absorb": "0,6,3", + "equipment_slot": "4", + "remove_sleeves": "true", + "grand_exchange_price": "84966", "name": "Zamorak d'hide", "tradeable": "true", - "weight": "6", "archery_ticket_price": "0", "id": "10790", - "absorb": "0,6,3", - "bonuses": "0,0,0,-15,30,55,47,60,50,55,0,0,0,0,0", - "equipment_slot": "4" + "bonuses": "0,0,0,-15,30,55,47,60,50,55,0,0,0,0,0" }, { + "requirements": "{1,40}-{4,70}", "shop_price": "13000", "examine": "Saradomin blessed dragonhide body armour.", "durability": null, @@ -92222,6 +93249,7 @@ "bonuses": "0,0,0,-15,30,55,47,60,50,55,0,0,0,0,0" }, { + "requirements": "{1,40}-{4,70}", "shop_price": "13", "examine": "Guthix blessed dragonhide body armour.", "durability": null, @@ -92238,6 +93266,7 @@ }, { "lendable": "true", + "requirements": "{4,40}", "examine": "Endorsed by Robin Hood.", "grand_exchange_price": "3070557", "durability": null, @@ -92250,10 +93279,12 @@ "equipment_slot": "0" }, { + "requirements": "{1,40}", "examine": "Rune platebody with gold trim.", "durability": null, "weight": "9.07", "absorb": "3,0,6", + "equip_audio": "2239", "equipment_slot": "4", "lendable": "true", "remove_sleeves": "true", @@ -92265,10 +93296,12 @@ "bonuses": "0,0,0,-30,-10,82,80,72,-6,80,0,0,0,0,0" }, { + "requirements": "{1,40}", "examine": "Rune platebody with trim.", "durability": null, "weight": "9.07", "absorb": "3,0,6", + "equip_audio": "2239", "equipment_slot": "4", "lendable": "true", "remove_sleeves": "true", @@ -92535,6 +93568,7 @@ "durability": null, "weight": "2.2", "absorb": "2,0,4", + "equip_audio": "2240", "equipment_slot": "0", "grand_exchange_price": "44800", "name": "Helm of neitiznot", @@ -92622,6 +93656,7 @@ "archery_ticket_price": "0", "id": "10840", "bonuses": "0,0,0,0,0,0,0,0,5,-5,0,0,0,0,0", + "alchemizable": "true", "equipment_slot": "3" }, { @@ -92736,6 +93771,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.", @@ -92746,6 +93782,9 @@ "archery_ticket_price": "0", "id": "10858", "stand_turn_anim": "7040", + "defence_anim": "7050", + "attack_anims": "7041,7041,7048,7049", + "attack_audios": "2503,0,2504,0", "bonuses": "-4,27,21,4,0,0,0,0,4,-1,0,26,0,0,0" }, { @@ -92888,7 +93927,7 @@ }, { "shop_price": "128", - "examine": "I can keep my grub in here.", + "examine": "I can keep my items in here, but I'll need a banker to get them out.", "durability": null, "name": "Plain satchel", "weight": "0.1", @@ -92897,7 +93936,7 @@ "equipment_slot": "5" }, { - "examine": "I can keep my grub in here.", + "examine": "I can keep my seeds and herbs in here.", "durability": null, "name": "Green satchel", "weight": "0.1", @@ -92907,7 +93946,7 @@ }, { "shop_price": "128", - "examine": "I can keep my grub in here.", + "examine": "I can keep my summoning charms in here.", "durability": null, "name": "Red satchel", "weight": "0.1", @@ -92925,7 +93964,7 @@ "equipment_slot": "5" }, { - "examine": "I can keep my grub in here.", + "examine": "I can keep my ores in here.", "durability": null, "name": "Gold satchel", "weight": "0.1", @@ -92934,7 +93973,7 @@ "equipment_slot": "5" }, { - "examine": "I can keep my grub in here.", + "examine": "I can keep my runes in here.", "durability": null, "name": "Rune satchel", "weight": "0.1", @@ -92993,6 +94032,7 @@ "two_handed": "true", "weapon_interface": "8", "turn180_anim": "5867", + "equip_audio": "2246", "defence_anim": "5866", "render_anim": "985", "equipment_slot": "3", @@ -93353,6 +94393,7 @@ "weight": "0.9", "archery_ticket_price": "0", "id": "10933", + "equip_audio": "2237", "equipment_slot": "10" }, { @@ -94407,6 +95448,7 @@ }, { "durability": null, + "attack_audios": "2508,2508,25092508", "name": "Mace", "archery_ticket_price": "0", "attack_speed": "5", @@ -94440,10 +95482,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", @@ -95276,6 +96320,7 @@ "attack_speed": "6", "weapon_interface": "17", "turn180_anim": "4227", + "equip_audio": "2244", "render_anim": "175", "equipment_slot": "3", "stand_anim": "4591", @@ -95305,6 +96350,7 @@ "attack_speed": "6", "weapon_interface": "17", "turn180_anim": "4227", + "equip_audio": "2244", "render_anim": "175", "equipment_slot": "3", "stand_anim": "4591", @@ -95749,6 +96795,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", @@ -95765,6 +96812,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", @@ -95792,6 +96840,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", @@ -95808,6 +96857,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", @@ -96085,29 +97135,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", @@ -96329,6 +97381,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" }, { @@ -96338,6 +97391,7 @@ "rare_item": "true", "durability": null, "weight": "7.2", + "equip_audio": "2245", "equipment_slot": "5", "grand_exchange_price": "7700000", "name": "Dragonfire shield", @@ -96502,6 +97556,7 @@ "durability": null, "weight": "2", "absorb": "2,0,4", + "equip_audio": "2240", "remove_beard": "true", "equipment_slot": "0", "lendable": "true", @@ -96862,6 +97917,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", @@ -96894,6 +97950,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", @@ -96927,6 +97984,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", @@ -96960,6 +98018,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", @@ -96993,6 +98052,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", @@ -97026,6 +98086,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", @@ -97058,6 +98119,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", @@ -97109,6 +98171,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", @@ -97141,6 +98204,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", @@ -97173,6 +98237,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", @@ -97224,6 +98289,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", @@ -97256,6 +98322,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", @@ -97289,6 +98356,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", @@ -97342,6 +98410,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", @@ -97375,6 +98444,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", @@ -97408,6 +98478,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", @@ -97440,6 +98511,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", @@ -97463,6 +98535,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", @@ -97496,6 +98569,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", @@ -97529,6 +98603,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", @@ -97554,11 +98629,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", @@ -97582,6 +98659,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", @@ -97647,6 +98725,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", @@ -97677,6 +98756,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", @@ -97685,6 +98765,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.", @@ -97699,6 +98780,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", @@ -97732,6 +98814,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", @@ -98274,7 +99357,7 @@ "examine": "One/Two doses of fishy super energy potion.", "grand_exchange_price": "1062", "durability": null, - "name": "Sup. energy mix(2)", + "name": "Super energy mix(2)", "tradeable": "true", "archery_ticket_price": "0", "weight": "0.1", @@ -98284,7 +99367,7 @@ "ge_buy_limit": "1000", "grand_exchange_price": "1062", "durability": null, - "name": "Sup. energy mix(2)", + "name": "Super energy mix(2)", "tradeable": "true", "archery_ticket_price": "0", "id": "11482" @@ -98294,7 +99377,7 @@ "examine": "One/Two doses of fishy super energy potion.", "grand_exchange_price": "562", "durability": null, - "name": "Sup. energy mix(1)", + "name": "Super energy mix(1)", "tradeable": "true", "archery_ticket_price": "0", "weight": "0.1", @@ -98304,7 +99387,7 @@ "ge_buy_limit": "1000", "grand_exchange_price": "562", "durability": null, - "name": "Sup. energy mix(1)", + "name": "Super energy mix(1)", "tradeable": "true", "archery_ticket_price": "0", "id": "11484" @@ -98314,7 +99397,7 @@ "examine": "One/Two doses of fishy Super strength potion.", "grand_exchange_price": "955", "durability": null, - "name": "Sup. str. mix(2)", + "name": "Super strength mix(2)", "tradeable": "true", "archery_ticket_price": "0", "weight": "0.1", @@ -98324,7 +99407,7 @@ "ge_buy_limit": "1000", "grand_exchange_price": "955", "durability": null, - "name": "Sup. str. mix(2)", + "name": "Super strength mix(2)", "tradeable": "true", "archery_ticket_price": "0", "id": "11486" @@ -98334,7 +99417,7 @@ "examine": "One/Two doses of fishy Super strength potion.", "grand_exchange_price": "1040", "durability": null, - "name": "Sup. str. mix(1)", + "name": "Super strength mix(1)", "tradeable": "true", "archery_ticket_price": "0", "weight": "0.1", @@ -98344,7 +99427,7 @@ "ge_buy_limit": "1000", "grand_exchange_price": "1040", "durability": null, - "name": "Sup. str. mix(1)", + "name": "Super strength mix(1)", "tradeable": "true", "archery_ticket_price": "0", "id": "11488" @@ -98394,7 +99477,7 @@ "examine": "One/Two doses of fishy super restore potion.", "grand_exchange_price": "3670", "durability": null, - "name": "Sup. restore mix(2)", + "name": "Super restore mix(2)", "tradeable": "true", "archery_ticket_price": "0", "weight": "0.1", @@ -98404,7 +99487,7 @@ "ge_buy_limit": "1000", "grand_exchange_price": "3670", "durability": null, - "name": "Sup. restore mix(2)", + "name": "Super restore mix(2)", "tradeable": "true", "archery_ticket_price": "0", "id": "11494" @@ -98414,7 +99497,7 @@ "examine": "One/Two doses of fishy super restore potion.", "grand_exchange_price": "3104", "durability": null, - "name": "Sup. restore mix(1)", + "name": "Super restore mix(1)", "tradeable": "true", "archery_ticket_price": "0", "weight": "0.1", @@ -98424,7 +99507,7 @@ "ge_buy_limit": "1000", "grand_exchange_price": "3104", "durability": null, - "name": "Sup. restore mix(1)", + "name": "Super restore mix(1)", "tradeable": "true", "archery_ticket_price": "0", "id": "11496" @@ -98434,7 +99517,7 @@ "examine": "One/two doses of fishy super defence potion", "grand_exchange_price": "38", "durability": null, - "name": "Sup. def. mix(2)", + "name": "Super defence mix(2)", "tradeable": "true", "archery_ticket_price": "0", "weight": "0.1", @@ -98444,7 +99527,7 @@ "ge_buy_limit": "1000", "grand_exchange_price": "38", "durability": null, - "name": "Sup. def. mix(2)", + "name": "Super defence mix(2)", "tradeable": "true", "archery_ticket_price": "0", "id": "11498" @@ -98454,7 +99537,7 @@ "examine": "One/two doses of fishy super defence potion", "grand_exchange_price": "66", "durability": null, - "name": "Sup. def. mix(1)", + "name": "Super defence mix(1)", "tradeable": "true", "archery_ticket_price": "0", "weight": "0.1", @@ -98464,7 +99547,7 @@ "ge_buy_limit": "1000", "grand_exchange_price": "66", "durability": null, - "name": "Sup. def. mix(1)", + "name": "Super defence mix(1)", "tradeable": "true", "archery_ticket_price": "0", "id": "11500" @@ -99069,6 +100152,7 @@ "durability": null, "weight": "10", "weapon_interface": "7", + "equip_audio": "2248", "render_anim": "124", "lendable": "true", "attack_audios": "3846,0,0,0", @@ -99109,6 +100193,7 @@ "durability": null, "weight": "10", "weapon_interface": "7", + "equip_audio": "2248", "render_anim": "124", "lendable": "true", "attack_audios": "3846,0,0,0", @@ -99148,6 +100233,7 @@ "durability": null, "weight": "10", "weapon_interface": "7", + "equip_audio": "2248", "render_anim": "124", "lendable": "true", "attack_audios": "3846,0,0,0", @@ -99187,6 +100273,7 @@ "durability": null, "weight": "10", "weapon_interface": "7", + "equip_audio": "2248", "render_anim": "124", "lendable": "true", "attack_audios": "3846,0,0,0", @@ -99375,6 +100462,7 @@ "durability": null, "weight": "3", "weapon_interface": "14", + "equip_audio": "2247", "render_anim": "28", "lendable": "true", "name": "Zamorakian spear" @@ -99397,6 +100485,7 @@ "durability": null, "weight": "0.5", "absorb": "0,5,2", + "equip_audio": "2240", "equipment_slot": "0", "lendable": "true", "grand_exchange_price": "4100000", @@ -99423,6 +100512,7 @@ "durability": null, "weight": "4", "absorb": "0,10,5", + "equip_audio": "2239", "equipment_slot": "4", "lendable": "true", "remove_sleeves": "true", @@ -99445,11 +100535,12 @@ { "requirements": "{1,70}-{4,70}", "ge_buy_limit": "2", - "examine": "A rune plateskirt in the colours of Armadyl.", + "examine": "A plateskirt of great craftsmanship.", "rare_item": "true", "durability": null, "weight": "8", "absorb": "2,0,4", + "equip_audio": "2242", "equipment_slot": "7", "lendable": "true", "grand_exchange_price": "14800000", @@ -99528,6 +100619,7 @@ "rare_item": "true", "durability": null, "weight": "6", + "equip_audio": "2237", "equipment_slot": "10", "lendable": "true", "grand_exchange_price": "111700", @@ -99572,6 +100664,7 @@ "durability": null, "weight": "3", "weapon_interface": "7", + "equip_audio": "2248", "render_anim": "124", "lendable": "true", "attack_audios": "3846,0,0,0", @@ -99593,6 +100686,7 @@ "durability": null, "rare_item": "true", "weight": "1", + "equip_audio": "2237", "equipment_slot": "10", "lendable": "true", "grand_exchange_price": "95400", @@ -99613,7 +100707,7 @@ }, { "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A scroll for Sir Tiffy. The wax seal is broken. / Before reading the scroll: A scroll for Sir Tiffy. It is sealed with a wax insignia.", + "examine": "A scroll for Sir Tiffy. It is sealed with a wax insignia.", "durability": null, "name": "Knight's notes", "tradeable": "false", @@ -99623,7 +100717,7 @@ }, { "destroy_message": "You can reclaim this item from the place you found it.", - "examine": "A scroll for Sir Tiffy. The wax seal is broken. / Before reading the scroll: A scroll for Sir Tiffy. It is sealed with a wax insignia.", + "examine": "A scroll for Sir Tiffy. The wax seal is broken.", "durability": null, "name": "Knight's notes", "tradeable": "false", @@ -99692,6 +100786,7 @@ "durability": null, "weight": "2", "weapon_interface": "1", + "equip_audio": "2247", "render_anim": "28", "attack_audios": "2555,0,0,0", "name": "Mystic steam staff" @@ -100074,6 +101169,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", @@ -102330,7 +103426,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", @@ -104153,28 +105250,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", @@ -105407,28 +106504,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", @@ -105475,16 +106572,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" }, @@ -106055,6 +107153,7 @@ "weight": "3", "archery_ticket_price": "0", "id": "12565", + "equip_audio": "2237", "equipment_slot": "10" }, { @@ -106756,6 +107855,7 @@ "weight": "0.4", "archery_ticket_price": "0", "id": "12634", + "equip_audio": "2238", "equipment_slot": "1" }, { @@ -106868,6 +107968,7 @@ "weight": "0.4", "archery_ticket_price": "0", "id": "12645", + "equip_audio": "2238", "equipment_slot": "1" }, { @@ -106913,7 +108014,7 @@ "id": "12650" }, { - "examine": "Common bucket: It's a bucket of water.2008 Easter event: It's a bucket of water from the Easter Bunny's warren. Regular bucket filled from Braindeath Island: It's a bucket of... water?", + "examine": "It's a bucket of water.", "grand_exchange_price": "74", "durability": null, "name": "Bucket of water", @@ -107095,6 +108196,7 @@ "durability": null, "weight": "1.3", "absorb": "2,0,4", + "equip_audio": "2240", "equipment_slot": "0", "lendable": "true", "grand_exchange_price": "61200", @@ -107110,6 +108212,7 @@ "durability": null, "weight": "1.3", "absorb": "2,0,4", + "equip_audio": "2240", "equipment_slot": "0", "lendable": "true", "grand_exchange_price": "61200", @@ -107159,6 +108262,7 @@ "durability": null, "weight": "0.5", "absorb": "0,5,2", + "equip_audio": "2240", "equipment_slot": "0", "lendable": "true", "grand_exchange_price": "3932989", @@ -107174,6 +108278,7 @@ "durability": null, "weight": "0.5", "absorb": "0,5,2", + "equip_audio": "2240", "equipment_slot": "0", "lendable": "true", "grand_exchange_price": "3932989", @@ -107318,6 +108423,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)", @@ -107333,6 +108439,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)", @@ -107871,14 +108978,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", @@ -108838,61 +109945,73 @@ }, { "destroy_message": "You can reclaim this item from the place you found it.", + "requirements": "{15,50}", "shop_price": "75", "examine": "A pair of mystical gloves.", "durability": null, "name": "Irit gloves", + "tradeable": "false", "archery_ticket_price": "0", "id": "12856", "equipment_slot": "9" }, { "destroy_message": "You can reclaim this item from the place you found it.", + "requirements": "{15,60}", "shop_price": "100", "examine": "A pair of mystical gloves.", "durability": null, "name": "Avantoe gloves", + "tradeable": "false", "archery_ticket_price": "0", "id": "12857", "equipment_slot": "9" }, { "destroy_message": "You can reclaim this item from the place you found it.", + "requirements": "{15,70}", "shop_price": "200", "examine": "A pair of mystical gloves.", "durability": null, "name": "Kwuarm gloves", + "tradeable": "false", "archery_ticket_price": "0", "id": "12858", "equipment_slot": "9" }, { "destroy_message": "You can reclaim this item from the place you found it.", + "requirements": "{15,80}", "shop_price": "200", "examine": "A pair of mystical gloves.", "durability": null, "name": "Cadantine gloves", + "tradeable": "false", "archery_ticket_price": "0", "id": "12859", "equipment_slot": "9" }, { "destroy_message": "You can reclaim this item from the place you found it.", + "requirements": "{10,65}", "shop_price": "200", "examine": "A pair of mystical gloves.", "durability": null, "name": "Swordfish gloves", - "weight": "0.2", + "tradeable": "false", + "weight": "1", "archery_ticket_price": "0", "id": "12860", "equipment_slot": "9" }, { "destroy_message": "You can reclaim this item from the place you found it.", + "requirements": "{10,90}", "shop_price": "200", "examine": "A pair of mystical gloves", "durability": null, "name": "Shark gloves", + "tradeable": "false", "weight": "1", "archery_ticket_price": "0", "id": "12861", @@ -108900,21 +110019,25 @@ }, { "destroy_message": "You can reclaim this item from the place you found it.", + "requirements": "{18,70}", "shop_price": "200", "examine": "A pair of mystical gloves.", "durability": null, "name": "Dragon slayer gloves", + "tradeable": "false", "archery_ticket_price": "0", "id": "12862", "equipment_slot": "9" }, { "destroy_message": "You can buy a new pair from the druids at the Fist of Guthix activity.", + "requirements": "{20,10}", "shop_price": "75", "examine": "A pair of mystical gloves.", "durability": null, "name": "Air runecrafting gloves", "tradeable": "false", + "weight": "1", "destroy": "true", "archery_ticket_price": "0", "id": "12863", @@ -108922,11 +110045,13 @@ }, { "destroy_message": "You can buy a new pair from the druids at the Fist of Guthix activity.", + "requirements": "{20,20}", "shop_price": "75", "examine": "A pair of mystical gloves.", "durability": null, "name": "Water runecrafting gloves", "tradeable": "false", + "weight": "1", "destroy": "true", "archery_ticket_price": "0", "id": "12864", @@ -108934,11 +110059,13 @@ }, { "destroy_message": "You can buy a new pair from the druids at the Fist of Guthix minigame.", + "requirements": "{20,30}", "shop_price": "75", "examine": "A pair of mystical gloves.", "durability": null, "name": "Earth runecrafting gloves", "tradeable": "false", + "weight": "1", "destroy": "true", "archery_ticket_price": "0", "id": "12865", @@ -108946,7 +110073,7 @@ }, { "remove_head": "true", - "requirements": "{1,60}-{6,50}", + "requirements": "{1,50}-{6,60}", "shop_price": "250", "examine": "Its arcane power is waning.", "durability": null, @@ -108961,7 +110088,7 @@ }, { "remove_head": "true", - "requirements": "{1,60}-{6,50}", + "requirements": "{1,50}-{6,60}", "shop_price": "250", "examine": "Its arcane power is waning.", "durability": null, @@ -108976,7 +110103,7 @@ }, { "remove_head": "true", - "requirements": "{1,60}-{6,50}", + "requirements": "{1,50}-{6,60}", "shop_price": "250", "examine": "Its arcane power is waning.", "durability": null, @@ -108991,7 +110118,7 @@ }, { "remove_head": "true", - "requirements": "{1,60}-{6,50}", + "requirements": "{1,50}-{6,60}", "shop_price": "250", "examine": "Its arcane power is waning.", "durability": null, @@ -109006,7 +110133,7 @@ }, { "remove_head": "true", - "requirements": "{1,60}-{6,50}", + "requirements": "{1,50}-{6,60}", "shop_price": "250", "examine": "Its arcane power is waning.", "durability": null, @@ -109021,7 +110148,7 @@ }, { "remove_head": "true", - "requirements": "{1,60}-{6,50}", + "requirements": "{1,50}-{6,60}", "shop_price": "250", "ge_buy_limit": "10", "examine": "Its arcane power is waning.", @@ -109254,7 +110381,7 @@ }, { "remove_head": "true", - "requirements": "{1,20}-{6,10}", + "requirements": "{1,10}-{6,20}", "shop_price": "100", "examine": "A worn-out hood, formerly worn by the followers of Guthix.", "durability": null, @@ -109269,7 +110396,7 @@ }, { "remove_head": "true", - "requirements": "{1,20}-{6,10}", + "requirements": "{1,10}-{6,20}", "shop_price": "100", "examine": "A worn-out hood, formerly worn by the followers of Guthix.", "durability": null, @@ -109284,7 +110411,7 @@ }, { "remove_head": "true", - "requirements": "{1,20}-{6,10}", + "requirements": "{1,10}-{6,20}", "shop_price": "100", "examine": "A worn-out hood, formerly worn by the followers of Guthix.", "durability": null, @@ -109299,7 +110426,7 @@ }, { "remove_head": "true", - "requirements": "{1,20}-{6,10}", + "requirements": "{1,10}-{6,20}", "shop_price": "100", "examine": "A worn-out hood, formerly worn by the followers of Guthix.", "durability": null, @@ -109314,7 +110441,7 @@ }, { "remove_head": "true", - "requirements": "{1,20}-{6,10}", + "requirements": "{1,10}-{6,20}", "shop_price": "100", "examine": "A worn-out hood, formerly worn by the followers of Guthix.", "durability": null, @@ -109329,7 +110456,7 @@ }, { "remove_head": "true", - "requirements": "{1,20}-{6,10}", + "requirements": "{1,10}-{6,20}", "shop_price": "100", "ge_buy_limit": "10", "examine": "A worn-out hood, formerly worn by the followers of Guthix.", @@ -109353,7 +110480,7 @@ "id": "12893" }, { - "requirements": "{1,20}-{6,10}", + "requirements": "{1,10}-{6,20}", "shop_price": "300", "examine": "A magical robe top worn by followers of Guthix.", "durability": null, @@ -109369,7 +110496,7 @@ "bonuses": "0,0,0,7,0,0,0,0,7,0,7,0,0,0,0" }, { - "requirements": "{1,20}-{6,10}", + "requirements": "{1,10}-{6,20}", "shop_price": "300", "examine": "A magical robe top worn by followers of Guthix.", "durability": null, @@ -109385,7 +110512,7 @@ "bonuses": "0,0,0,7,0,0,0,0,7,0,7,0,0,0,0" }, { - "requirements": "{1,20}-{6,10}", + "requirements": "{1,10}-{6,20}", "shop_price": "300", "examine": "A magical robe top worn by followers of Guthix.", "durability": null, @@ -109401,7 +110528,7 @@ "bonuses": "0,0,0,7,0,0,0,0,7,0,7,0,0,0,0" }, { - "requirements": "{1,20}-{6,10}", + "requirements": "{1,10}-{6,20}", "shop_price": "300", "examine": "A magical robe top worn by followers of Guthix.", "durability": null, @@ -109417,7 +110544,7 @@ "bonuses": "0,0,0,7,0,0,0,0,7,0,7,0,0,0,0" }, { - "requirements": "{1,20}-{6,10}", + "requirements": "{1,10}-{6,20}", "shop_price": "300", "examine": "A magical robe top worn by followers of Guthix.", "durability": null, @@ -109433,7 +110560,7 @@ "bonuses": "0,0,0,7,0,0,0,0,7,0,7,0,0,0,0" }, { - "requirements": "{1,20}-{6,10}", + "requirements": "{1,10}-{6,20}", "shop_price": "300", "ge_buy_limit": "10", "examine": "A magical robe top worn by followers of Guthix.", @@ -109459,7 +110586,7 @@ "id": "12900" }, { - "requirements": "{1,20}-{6,10}", + "requirements": "{1,10}-{6,20}", "shop_price": "200", "examine": "A magical robe bottom worn by a person who follows Guthix.", "durability": null, @@ -109474,7 +110601,7 @@ "bonuses": "0,0,0,6,0,0,0,0,6,0,6,0,0,0,0" }, { - "requirements": "{1,20}-{6,10}", + "requirements": "{1,10}-{6,20}", "shop_price": "200", "examine": "A magical robe bottom worn by a person who follows Guthix.", "durability": null, @@ -109489,7 +110616,7 @@ "bonuses": "0,0,0,6,0,0,0,0,6,0,6,0,0,0,0" }, { - "requirements": "{1,20}-{6,10}", + "requirements": "{1,10}-{6,20}", "shop_price": "200", "examine": "A magical robe bottom worn by a person who follows Guthix.", "durability": null, @@ -109504,7 +110631,7 @@ "bonuses": "0,0,0,6,0,0,0,0,6,0,6,0,0,0,0" }, { - "requirements": "{1,20}-{6,10}", + "requirements": "{1,10}-{6,20}", "shop_price": "200", "examine": "A magical robe bottom worn by a person who follows Guthix.", "durability": null, @@ -109519,7 +110646,7 @@ "bonuses": "0,0,0,6,0,0,0,0,6,0,6,0,0,0,0" }, { - "requirements": "{1,20}-{6,10}", + "requirements": "{1,10}-{6,20}", "shop_price": "200", "examine": "A magical robe bottom worn by a person who follows Guthix.", "durability": null, @@ -109534,7 +110661,7 @@ "bonuses": "0,0,0,6,0,0,0,0,6,0,6,0,0,0,0" }, { - "requirements": "{1,20}-{6,10}", + "requirements": "{1,10}-{6,20}", "shop_price": "200", "ge_buy_limit": "10", "examine": "A magical robe bottom worn by a person who follows Guthix.", @@ -109972,6 +111099,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", @@ -109988,6 +111116,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", @@ -110004,6 +111133,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", @@ -110020,6 +111150,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", @@ -110036,6 +111167,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", @@ -110053,6 +111185,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", @@ -110079,6 +111212,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", @@ -110095,6 +111229,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", @@ -110111,6 +111246,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", @@ -110127,6 +111263,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", @@ -110143,6 +111280,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", @@ -110160,6 +111298,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", @@ -110186,6 +111325,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", @@ -110202,6 +111342,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", @@ -110218,6 +111359,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", @@ -110234,6 +111376,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", @@ -110250,6 +111393,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", @@ -110267,6 +111411,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", @@ -110293,6 +111438,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", @@ -110309,6 +111455,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", @@ -110325,6 +111472,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", @@ -110341,6 +111489,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", @@ -110357,6 +111506,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", @@ -110374,6 +111524,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", @@ -110394,7 +111545,7 @@ }, { "remove_head": "true", - "requirements": "{1,20}-{6,10}", + "requirements": "{1,10}-{6,20}", "shop_price": "50", "examine": "A magical hood, especially created for combat spells.", "durability": null, @@ -110409,7 +111560,7 @@ }, { "remove_head": "true", - "requirements": "{1,20}-{6,10}", + "requirements": "{1,10}-{6,20}", "shop_price": "50", "examine": "A magical hood, especially created for combat spells.", "durability": null, @@ -110424,7 +111575,7 @@ }, { "remove_head": "true", - "requirements": "{1,20}-{6,10}", + "requirements": "{1,10}-{6,20}", "shop_price": "50", "examine": "A magical hood, especially created for combat spells.", "durability": null, @@ -110439,7 +111590,7 @@ }, { "remove_head": "true", - "requirements": "{1,20}-{6,10}", + "requirements": "{1,10}-{6,20}", "shop_price": "50", "examine": "A magical hood, especially created for combat spells.", "durability": null, @@ -110454,7 +111605,7 @@ }, { "remove_head": "true", - "requirements": "{1,20}-{6,10}", + "requirements": "{1,10}-{6,20}", "shop_price": "50", "examine": "A magical hood, especially created for combat spells.", "durability": null, @@ -110469,7 +111620,7 @@ }, { "remove_head": "true", - "requirements": "{1,20}-{6,10}", + "requirements": "{1,10}-{6,20}", "shop_price": "50", "ge_buy_limit": "10", "examine": "A magical hood, especially created for combat spells.", @@ -110493,7 +111644,7 @@ "id": "12970" }, { - "requirements": "{1,20}-{6,10}", + "requirements": "{1,10}-{6,20}", "shop_price": "150", "examine": "A magical robe top, especially made for combat spells.", "durability": null, @@ -110509,7 +111660,7 @@ "bonuses": "0,0,0,7,0,1,1,1,7,0,8,0,0,0,0" }, { - "requirements": "{1,20}-{6,10}", + "requirements": "{1,10}-{6,20}", "shop_price": "150", "examine": "A magical robe top, especially made for combat spells.", "durability": null, @@ -110525,7 +111676,7 @@ "bonuses": "0,0,0,7,0,1,1,1,7,0,8,0,0,0,0" }, { - "requirements": "{1,20}-{6,10}", + "requirements": "{1,10}-{6,20}", "shop_price": "150", "examine": "A magical robe top, especially made for combat spells.", "durability": null, @@ -110541,7 +111692,7 @@ "bonuses": "0,0,0,7,0,1,1,1,7,0,8,0,0,0,0" }, { - "requirements": "{1,20}-{6,10}", + "requirements": "{1,10}-{6,20}", "shop_price": "150", "examine": "A magical robe top, especially made for combat spells.", "durability": null, @@ -110557,7 +111708,7 @@ "bonuses": "0,0,0,7,0,1,1,1,7,0,8,0,0,0,0" }, { - "requirements": "{1,20}-{6,10}", + "requirements": "{1,10}-{6,20}", "shop_price": "150", "examine": "A magical robe top, especially made for combat spells.", "durability": null, @@ -110573,7 +111724,7 @@ "bonuses": "0,0,0,7,0,1,1,1,7,0,8,0,0,0,0" }, { - "requirements": "{1,20}-{6,10}", + "requirements": "{1,10}-{6,20}", "shop_price": "150", "ge_buy_limit": "10", "examine": "A magical robe top, especially made for combat spells.", @@ -110599,7 +111750,7 @@ "id": "12977" }, { - "requirements": "{1,20}-{6,10}", + "requirements": "{1,10}-{6,20}", "shop_price": "100", "examine": "A magical robe bottom, especially created for combat spells.", "durability": null, @@ -110614,7 +111765,7 @@ "bonuses": "0,0,0,6,0,1,1,1,6,0,7,0,0,0,0" }, { - "requirements": "{1,20}-{6,10}", + "requirements": "{1,10}-{6,20}", "shop_price": "100", "examine": "A magical robe bottom, especially created for combat spells.", "durability": null, @@ -110629,7 +111780,7 @@ "bonuses": "0,0,0,6,0,1,1,1,6,0,7,0,0,0,0" }, { - "requirements": "{1,20}-{6,10}", + "requirements": "{1,10}-{6,20}", "shop_price": "100", "examine": "A magical robe bottom, especially created for combat spells.", "durability": null, @@ -110644,7 +111795,7 @@ "bonuses": "0,0,0,6,0,1,1,1,6,0,7,0,0,0,0" }, { - "requirements": "{1,20}-{6,10}", + "requirements": "{1,10}-{6,20}", "shop_price": "100", "examine": "A magical robe bottom, especially created for combat spells.", "durability": null, @@ -110659,7 +111810,7 @@ "bonuses": "0,0,0,6,0,1,1,1,6,0,7,0,0,0,0" }, { - "requirements": "{1,20}-{6,10}", + "requirements": "{1,10}-{6,20}", "shop_price": "100", "examine": "A magical robe bottom, especially created for combat spells.", "durability": null, @@ -110674,7 +111825,7 @@ "bonuses": "0,0,0,6,0,1,1,1,6,0,7,0,0,0,0" }, { - "requirements": "{1,20}-{6,10}", + "requirements": "{1,10}-{6,20}", "shop_price": "100", "ge_buy_limit": "10", "examine": "A magical robe bottom, especially created for combat spells.", @@ -111647,6 +112798,7 @@ "attack_speed": "6", "weapon_interface": "17", "turn180_anim": "4227", + "equip_audio": "2244", "render_anim": "175", "equipment_slot": "3", "grand_exchange_price": "261", @@ -113540,6 +114692,7 @@ "durability": null, "destroy": "false", "weapon_interface": "5", + "equip_audio": "2248", "attack_audios": "2517,2517,2500,2517", "name": "Leaf-bladed sword" }, @@ -115365,6 +116518,7 @@ "two_handed": "true", "weapon_interface": "7", "absorb": "0,0,0", + "equip_audio": "2248", "render_anim": "124", "lendable": "true", "destroy_message": "Drop", @@ -115541,6 +116695,7 @@ "attack_speed": "4", "weapon_interface": "11", "absorb": "0,0,0", + "equip_audio": "2249", "render_anim": "1578", "lendable": "true", "destroy_message": "Drop", @@ -115627,6 +116782,7 @@ "two_handed": "true", "weapon_interface": "7", "absorb": "0,0,0", + "equip_audio": "2248", "render_anim": "1579", "lendable": "true", "destroy_message": "Drop", @@ -115645,6 +116801,7 @@ "two_handed": "true", "weapon_interface": "7", "absorb": "0,0,0", + "equip_audio": "2248", "render_anim": "1579", "lendable": "true", "destroy_message": "Drop", @@ -115663,6 +116820,7 @@ "two_handed": "true", "weapon_interface": "7", "absorb": "0,0,0", + "equip_audio": "2248", "render_anim": "1579", "lendable": "true", "destroy_message": "Drop", @@ -115681,6 +116839,7 @@ "two_handed": "true", "weapon_interface": "7", "absorb": "0,0,0", + "equip_audio": "2248", "render_anim": "1579", "lendable": "true", "destroy_message": "Drop", @@ -115709,45 +116868,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" }, { @@ -115779,17 +116941,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" }, { @@ -115801,6 +116964,7 @@ "two_handed": "true", "weapon_interface": "7", "absorb": "0,0,0", + "equip_audio": "2248", "render_anim": "1579", "lendable": "true", "destroy_message": "Drop", @@ -115811,17 +116975,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" }, { @@ -115837,8 +117002,8 @@ "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" }, { - "destroy_message": "You can get another of these from Doric.", - "examine": "Doric's instructions for the task he gave you.", + "destroy_message": "You can get another by talking about jobs with citizens and tutors around Lumbridge.", + "examine": "This reminds me of my current job and who set me to the task.", "durability": null, "name": "Task list", "tradeable": "false", @@ -115857,6 +117022,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", @@ -115876,6 +117042,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", @@ -115893,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", @@ -115910,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", @@ -115926,6 +117095,7 @@ "attack_speed": "5", "weapon_interface": "2", "absorb": "0,0,0", + "equip_audio": "2229", "lendable": "true", "destroy_message": "Drop", "grand_exchange_price": "7263", @@ -115942,6 +117112,7 @@ "attack_speed": "5", "weapon_interface": "2", "absorb": "0,0,0", + "equip_audio": "2229", "lendable": "true", "destroy_message": "Drop", "grand_exchange_price": "3794577", @@ -115977,10 +117148,11 @@ "absorb": "0,0,0", "render_anim": "2586", "defence_anim": "397", - "attack_anims": "395,395,395,395", + "attack_anims": "395,395,401,395", "lendable": "true", "destroy_message": "Drop", "grand_exchange_price": "123019", + "attack_audios": "2498,2498,2497,2498", "name": "Dragon battleaxe", "archery_ticket_price": "0", "id": "13472", @@ -115993,6 +117165,7 @@ "attack_speed": "6", "weapon_interface": "10", "absorb": "0,0,0", + "equip_audio": "2233", "render_anim": "1430", "lendable": "true", "destroy_message": "Drop", @@ -116059,6 +117232,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", @@ -116078,6 +117252,7 @@ "two_handed": "true", "weapon_interface": "15", "absorb": "0,0,0", + "equip_audio": "2247", "render_anim": "28", "lendable": "true", "destroy_message": "Drop", @@ -116097,11 +117272,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", @@ -116114,6 +117291,7 @@ "attack_speed": "5", "weapon_interface": "4", "absorb": "0,0,0", + "equip_audio": "2232", "lendable": "true", "destroy_message": "Drop", "grand_exchange_price": "18546", @@ -116151,59 +117329,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" }, { @@ -116263,73 +117445,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" }, { @@ -116347,59 +117534,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" }, { @@ -116459,45 +117650,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" }, { @@ -116718,6 +117912,7 @@ "two_handed": "true", "weapon_interface": "16", "absorb": "0,0,0", + "equip_audio": "2244", "lendable": "true", "destroy_message": "Drop", "grand_exchange_price": "193", @@ -116734,6 +117929,7 @@ "two_handed": "true", "weapon_interface": "16", "absorb": "0,0,0", + "equip_audio": "2244", "lendable": "true", "destroy_message": "Drop", "grand_exchange_price": "111", @@ -116750,6 +117946,7 @@ "two_handed": "true", "weapon_interface": "16", "absorb": "0,0,0", + "equip_audio": "2244", "lendable": "true", "destroy_message": "Drop", "grand_exchange_price": "486", @@ -116766,6 +117963,7 @@ "two_handed": "true", "weapon_interface": "16", "absorb": "0,0,0", + "equip_audio": "2244", "lendable": "true", "destroy_message": "Drop", "grand_exchange_price": "253", @@ -116783,6 +117981,7 @@ "two_handed": "true", "weapon_interface": "16", "absorb": "0,0,0", + "equip_audio": "2244", "lendable": "true", "destroy_message": "Drop", "grand_exchange_price": "1211", @@ -116800,6 +117999,7 @@ "two_handed": "true", "weapon_interface": "16", "absorb": "0,0,0", + "equip_audio": "2244", "lendable": "true", "destroy_message": "Drop", "grand_exchange_price": "655", @@ -116832,6 +118032,7 @@ "attack_speed": "6", "weapon_interface": "17", "absorb": "0,0,0", + "equip_audio": "2244", "render_anim": "175", "lendable": "true", "destroy_message": "Drop", @@ -117076,17 +118277,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" }, { @@ -117188,17 +118390,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" }, { @@ -117448,9 +118651,9 @@ }, { "destroy_message": "Hopefully, the family has more papyrus and charcoal.", - "examine": "Your best impression of a map of RuneScape.", + "examine": "Your best impression of a map of 2009Scape.", "durability": null, - "name": "Map of runescape", + "name": "Map of 2009Scape", "destroy": "true", "archery_ticket_price": "0", "attack_speed": "4", @@ -118102,6 +119305,7 @@ "attack_speed": "5", "weapon_interface": "1", "absorb": "0,0,0", + "equip_audio": "2247", "render_anim": "28", "equipment_slot": "3", "destroy_message": "Drop", @@ -118118,6 +119322,7 @@ "attack_speed": "5", "weapon_interface": "1", "absorb": "0,0,0", + "equip_audio": "2247", "render_anim": "28", "equipment_slot": "3", "destroy_message": "Drop", @@ -118134,6 +119339,7 @@ "attack_speed": "5", "weapon_interface": "1", "absorb": "0,0,0", + "equip_audio": "2247", "render_anim": "28", "equipment_slot": "3", "destroy_message": "Drop", @@ -118150,6 +119356,7 @@ "attack_speed": "5", "weapon_interface": "1", "absorb": "0,0,0", + "equip_audio": "2247", "render_anim": "28", "equipment_slot": "3", "destroy_message": "Drop", @@ -118166,6 +119373,7 @@ "attack_speed": "5", "weapon_interface": "1", "absorb": "0,0,0", + "equip_audio": "2247", "render_anim": "28", "equipment_slot": "3", "destroy_message": "Drop", @@ -118182,6 +119390,7 @@ "attack_speed": "5", "weapon_interface": "1", "absorb": "0,0,0", + "equip_audio": "2247", "render_anim": "28", "equipment_slot": "3", "destroy_message": "Drop", @@ -118198,6 +119407,7 @@ "attack_speed": "5", "weapon_interface": "1", "absorb": "0,0,0", + "equip_audio": "2247", "render_anim": "28", "equipment_slot": "3", "destroy_message": "Drop", @@ -118214,6 +119424,7 @@ "attack_speed": "5", "weapon_interface": "1", "absorb": "0,0,0", + "equip_audio": "2247", "render_anim": "28", "equipment_slot": "3", "destroy_message": "Drop", @@ -118230,6 +119441,7 @@ "attack_speed": "5", "weapon_interface": "1", "absorb": "0,0,0", + "equip_audio": "2247", "render_anim": "28", "equipment_slot": "3", "destroy_message": "No", @@ -118246,6 +119458,7 @@ "attack_speed": "5", "weapon_interface": "1", "absorb": "0,0,0", + "equip_audio": "2247", "render_anim": "28", "equipment_slot": "3", "destroy_message": "Drop", @@ -118262,6 +119475,7 @@ "attack_speed": "5", "weapon_interface": "1", "absorb": "0,0,0", + "equip_audio": "2247", "render_anim": "28", "equipment_slot": "3", "destroy_message": "Drop", @@ -118278,6 +119492,7 @@ "attack_speed": "5", "weapon_interface": "1", "absorb": "0,0,0", + "equip_audio": "2247", "render_anim": "28", "equipment_slot": "3", "destroy_message": "Drop", @@ -118294,6 +119509,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.", @@ -118528,6 +119744,7 @@ "equipment_slot": "0" }, { + "requirements": "{11,62}", "shop_price": "100", "examine": "It burns, burns, burns...", "durability": null, @@ -118543,20 +119760,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, @@ -119323,6 +120542,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", @@ -119445,6 +120665,7 @@ "destroy": "true", "attack_speed": "4", "absorb": "0,0,0", + "equip_audio": "2245", "equipment_slot": "5", "destroy_message": "Drop", "grand_exchange_price": "39982", @@ -119476,6 +120697,7 @@ "destroy": "true", "attack_speed": "4", "absorb": "0,0,0", + "equip_audio": "2245", "equipment_slot": "5", "destroy_message": "Drop", "grand_exchange_price": "936814", @@ -119506,6 +120728,7 @@ "destroy": "true", "attack_speed": "4", "absorb": "0,0,0", + "equip_audio": "2245", "equipment_slot": "5", "destroy_message": "Drop", "grand_exchange_price": "15076199", @@ -119536,6 +120759,7 @@ "destroy": "true", "attack_speed": "4", "absorb": "0,0,0", + "equip_audio": "2245", "equipment_slot": "5", "destroy_message": "Drop", "grand_exchange_price": "40003587", @@ -119566,6 +120790,7 @@ "destroy": "true", "attack_speed": "4", "absorb": "0,0,0", + "equip_audio": "2245", "equipment_slot": "5", "destroy_message": "Drop", "grand_exchange_price": "28420591", @@ -119596,6 +120821,7 @@ "destroy": "true", "attack_speed": "4", "absorb": "0,0,0", + "equip_audio": "2245", "equipment_slot": "5", "destroy_message": "Drop", "grand_exchange_price": "3616112", @@ -120090,6 +121316,7 @@ "two_handed": "true", "weapon_interface": "7", "absorb": "0,0,0", + "equip_audio": "2248", "render_anim": "124", "lendable": "true", "destroy_message": "Drop", @@ -120107,6 +121334,7 @@ "two_handed": "true", "weapon_interface": "15", "absorb": "0,0,0", + "equip_audio": "2247", "render_anim": "28", "lendable": "true", "destroy_message": "Drop", @@ -120126,6 +121354,7 @@ "lendable": "true", "destroy_message": "Drop", "grand_exchange_price": "7917", + "attack_audios": "2508,2508,25092508", "name": "Rune mace", "archery_ticket_price": "0", "id": "13780", @@ -120146,17 +121375,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" }, { @@ -120216,17 +121446,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" }, { @@ -120455,17 +121686,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" }, { @@ -120525,17 +121757,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" }, { @@ -120738,17 +121971,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" }, { @@ -120808,17 +122042,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" }, { @@ -120878,23 +122113,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", @@ -120948,17 +122184,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" }, { @@ -121309,18 +122546,19 @@ "id": "13867", "stand_turn_anim": "1209", "bonuses": "13,-1,65,18,0,5,7,4,18,0,0,72,0,0,10", - "requirements": "{6,78}", + "requirements": "{0,78}-{6,78}", "shop_price": "300000", "durability": null, "destroy": "false", "weapon_interface": "1", + "equip_audio": "2247", "render_anim": "28", "attack_audios": "2555,0,0,0", "name": "Zuriel's staff" }, { "destroy_message": "Drop", - "requirements": "{6,78}", + "requirements": "{0,78}-{6,78}", "shop_price": "300000", "examine": "This item degrades in combat, and will turn to dust.", "grand_exchange_price": "2116989", @@ -121352,10 +122590,12 @@ "id": "13869", "stand_turn_anim": "1209", "bonuses": "13,-1,65,18,0,5,7,4,18,0,0,72,0,0,10", + "requirements": "{0,78}-{6,78}", "shop_price": "300000", "durability": null, "destroy": "true", "weapon_interface": "1", + "equip_audio": "2247", "render_anim": "28", "attack_audios": "2555,0,0,0", "name": "Zuriel's staff (deg)" @@ -121465,6 +122705,7 @@ "destroy": "false", "attack_speed": "4", "absorb": "0,6,3", + "equip_audio": "2238", "equipment_slot": "0", "destroy_message": "Drop", "grand_exchange_price": "236411", @@ -121497,6 +122738,7 @@ "destroy": "true", "attack_speed": "4", "absorb": "0,6,3", + "equip_audio": "2238", "equipment_slot": "0", "destroy_message": "Drop", "grand_exchange_price": "236411", @@ -121515,6 +122757,7 @@ "attack_speed": "6", "weapon_interface": "18", "absorb": "0,0,0", + "equip_audio": "2244", "render_anim": "1381", "equipment_slot": "3", "destroy_message": "Drop", @@ -121535,6 +122778,7 @@ "attack_speed": "6", "weapon_interface": "18", "absorb": "0,0,0", + "equip_audio": "2244", "render_anim": "1381", "equipment_slot": "3", "destroy_message": "Drop", @@ -121555,6 +122799,7 @@ "attack_speed": "6", "weapon_interface": "18", "absorb": "0,0,0", + "equip_audio": "2244", "render_anim": "1381", "equipment_slot": "3", "destroy_message": "Drop", @@ -121575,6 +122820,7 @@ "attack_speed": "6", "weapon_interface": "18", "absorb": "0,0,0", + "equip_audio": "2244", "render_anim": "1381", "equipment_slot": "3", "destroy_message": "Drop", @@ -121612,6 +122858,7 @@ "destroy": "false", "attack_speed": "4", "absorb": "6,0,12", + "equip_audio": "2239", "equipment_slot": "4", "destroy_message": "Drop", "remove_sleeves": "true", @@ -121644,6 +122891,7 @@ "destroy": "true", "attack_speed": "4", "absorb": "6,0,12", + "equip_audio": "2239", "equipment_slot": "4", "destroy_message": "Drop", "remove_sleeves": "true", @@ -121710,6 +122958,7 @@ "destroy": "false", "attack_speed": "4", "absorb": "4,0,8", + "equip_audio": "2242", "equipment_slot": "7", "destroy_message": "Drop", "grand_exchange_price": "1735029", @@ -121741,6 +122990,7 @@ "destroy": "true", "attack_speed": "4", "absorb": "4,0,8", + "equip_audio": "2242", "equipment_slot": "7", "destroy_message": "Drop", "grand_exchange_price": "1735029", @@ -121805,6 +123055,7 @@ "destroy": "false", "attack_speed": "4", "absorb": "3,0,6", + "equip_audio": "2240", "equipment_slot": "0", "destroy_message": "Drop", "grand_exchange_price": "1301986", @@ -121815,17 +123066,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" }, { @@ -121837,6 +123089,7 @@ "destroy": "true", "attack_speed": "4", "absorb": "3,0,6", + "equip_audio": "2240", "equipment_slot": "0", "destroy_message": "Drop", "grand_exchange_price": "1301986", @@ -121857,7 +123110,7 @@ "absorb": "0,0,0", "defence_anim": "397", "equipment_slot": "3", - "attack_anims": "381,390,390,390", + "attack_anims": "390,390,381,390", "destroy_message": "Drop", "grand_exchange_price": "10464249", "tradeable": "true", @@ -121870,7 +123123,8 @@ "shop_price": "300000", "durability": null, "destroy": "false", - "weapon_interface": "5", + "weapon_interface": "6", + "equip_audio": "2248", "render_anim": "1426", "attack_audios": "2500,2500,2517,2500", "name": "Vesta's longsword" @@ -121901,7 +123155,7 @@ "absorb": "0,0,0", "defence_anim": "397", "equipment_slot": "3", - "attack_anims": "381,390,390,390", + "attack_anims": "390,390,381,390", "destroy_message": "Drop", "grand_exchange_price": "10464249", "run_anim": "824", @@ -121913,7 +123167,8 @@ "shop_price": "300000", "durability": null, "destroy": "true", - "weapon_interface": "5", + "weapon_interface": "6", + "equip_audio": "2248", "render_anim": "1426", "attack_audios": "2500,2500,2517,2500", "name": "Vesta's longsword (deg)" @@ -121944,6 +123199,7 @@ "durability": null, "destroy": "false", "weapon_interface": "10", + "equip_audio": "2233", "attack_audios": "2504,0,0,0", "name": "Statius's warhammer" }, @@ -121984,6 +123240,7 @@ "durability": null, "destroy": "true", "weapon_interface": "10", + "equip_audio": "2233", "render_anim": "1426", "attack_audios": "2504,0,0,0", "name": "Statius' warhammer (deg)" @@ -121994,7 +123251,7 @@ "walk_anim": "1205", "has_special": "true", "turn90ccw_anim": "1208", - "attack_speed": "4", + "attack_speed": "5", "two_handed": "true", "turn180_anim": "1206", "absorb": "0,0,0", @@ -122016,6 +123273,7 @@ "destroy": "false", "weight": "4.2", "weapon_interface": "14", + "equip_audio": "2247", "render_anim": "28", "name": "Vesta's spear" }, @@ -122038,15 +123296,20 @@ "requirements": "{0,78}", "shop_price": "180000", "examine": "This item degrades in combat, and will turn to dust.", + "walk_anim": "1205", "has_special": "true", "durability": null, "destroy": "true", "attack_speed": "5", "two_handed": "true", + "turn90ccw_anim": "1208", "weapon_interface": "14", "absorb": "0,0,0", + "equip_audio": "2247", + "defence_anim": "2079", "render_anim": "28", "equipment_slot": "3", + "attack_anims": "2080,2081,2082,2080", "destroy_message": "Drop", "grand_exchange_price": "5264612", "name": "Vesta's spear (deg)", @@ -122055,13 +123318,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", @@ -122073,7 +123337,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", @@ -122086,13 +123350,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", @@ -122103,7 +123368,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, @@ -122121,7 +123386,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", @@ -122134,7 +123399,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, @@ -122151,13 +123416,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", @@ -122168,7 +123434,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", @@ -122181,13 +123447,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", @@ -122197,7 +123464,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, @@ -122214,7 +123481,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", @@ -122227,7 +123494,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, @@ -122244,7 +123511,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, @@ -122261,7 +123528,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", @@ -122275,13 +123542,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", @@ -122291,7 +123559,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.", @@ -122303,6 +123571,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", @@ -122316,7 +123585,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.", @@ -122338,7 +123607,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.", @@ -122350,6 +123619,7 @@ "weapon_interface": "5", "turn180_anim": "1206", "absorb": "0,0,0", + "equip_audio": "2248", "render_anim": "1426", "equipment_slot": "3", "destroy_message": "Drop", @@ -122362,7 +123632,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, @@ -122370,6 +123640,7 @@ "attack_speed": "6", "weapon_interface": "8", "absorb": "0,0,0", + "equip_audio": "2233", "defence_anim": "397", "equipment_slot": "3", "destroy_message": "Drop", @@ -122382,7 +123653,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", @@ -122395,7 +123666,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, @@ -122403,6 +123674,7 @@ "attack_speed": "6", "weapon_interface": "8", "absorb": "0,0,0", + "equip_audio": "2233", "equipment_slot": "3", "destroy_message": "Drop", "grand_exchange_price": "3033516", @@ -122412,7 +123684,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, @@ -122421,6 +123693,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", @@ -122432,7 +123705,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, @@ -122447,7 +123720,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, @@ -122456,6 +123729,7 @@ "two_handed": "true", "weapon_interface": "14", "absorb": "0,0,0", + "equip_audio": "2247", "render_anim": "28", "equipment_slot": "3", "destroy_message": "Drop", @@ -122466,7 +123740,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, @@ -122484,7 +123758,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", @@ -122497,7 +123771,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, @@ -122514,7 +123788,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, @@ -122531,7 +123805,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", @@ -122544,7 +123818,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, @@ -122561,7 +123835,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, @@ -122578,7 +123852,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", @@ -122592,7 +123866,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, @@ -122608,7 +123882,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, @@ -122616,6 +123890,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", @@ -122629,7 +123904,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", @@ -122642,7 +123917,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, @@ -122650,6 +123925,7 @@ "attack_speed": "6", "weapon_interface": "1", "absorb": "0,0,0", + "equip_audio": "2247", "render_anim": "28", "equipment_slot": "3", "destroy_message": "Drop", @@ -122660,7 +123936,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, @@ -122678,7 +123954,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", @@ -122691,7 +123967,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, @@ -122708,7 +123984,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, @@ -122725,7 +124001,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", @@ -122738,7 +124014,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, @@ -122755,13 +124031,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", @@ -122772,7 +124049,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", @@ -122786,13 +124063,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", @@ -122802,7 +124080,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, @@ -122810,6 +124088,7 @@ "attack_speed": "6", "weapon_interface": "18", "absorb": "0,0,0", + "equip_audio": "2244", "render_anim": "1381", "equipment_slot": "3", "destroy_message": "Drop", @@ -122820,7 +124099,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, @@ -122828,6 +124107,7 @@ "attack_speed": "6", "weapon_interface": "18", "absorb": "0,0,0", + "equip_audio": "2244", "render_anim": "1381", "equipment_slot": "3", "destroy_message": "Drop", @@ -122838,7 +124118,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, @@ -122846,6 +124126,7 @@ "attack_speed": "6", "weapon_interface": "18", "absorb": "0,0,0", + "equip_audio": "2244", "render_anim": "1381", "equipment_slot": "3", "destroy_message": "Drop", @@ -122856,7 +124137,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, @@ -122864,6 +124145,7 @@ "attack_speed": "6", "weapon_interface": "18", "absorb": "0,0,0", + "equip_audio": "2244", "render_anim": "1381", "equipment_slot": "3", "destroy_message": "Drop", @@ -122874,7 +124156,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, @@ -122891,7 +124173,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, @@ -122944,6 +124226,7 @@ "destroy": "true", "attack_speed": "4", "absorb": "2,0,4", + "equip_audio": "2240", "equipment_slot": "0", "destroy_message": "Drop", "grand_exchange_price": "121835", @@ -122973,6 +124256,7 @@ "destroy": "true", "attack_speed": "4", "absorb": "2,0,4", + "equip_audio": "2240", "equipment_slot": "0", "destroy_message": "Drop", "grand_exchange_price": "121835", @@ -122988,6 +124272,7 @@ "destroy": "true", "attack_speed": "4", "absorb": "5,0,11", + "equip_audio": "2245", "equipment_slot": "5", "destroy_message": "Drop", "grand_exchange_price": "79703", @@ -123016,6 +124301,7 @@ "destroy": "true", "attack_speed": "4", "absorb": "5,0,11", + "equip_audio": "2245", "equipment_slot": "5", "destroy_message": "Drop", "grand_exchange_price": "79703", @@ -123074,6 +124360,7 @@ "destroy": "true", "attack_speed": "4", "absorb": "3,0,6", + "equip_audio": "2242", "equipment_slot": "7", "destroy_message": "Drop", "grand_exchange_price": "87957", @@ -123102,6 +124389,7 @@ "destroy": "true", "attack_speed": "4", "absorb": "3,0,6", + "equip_audio": "2242", "equipment_slot": "7", "destroy_message": "Drop", "grand_exchange_price": "87957", @@ -123120,12 +124408,14 @@ "attack_speed": "6", "weapon_interface": "2", "absorb": "0,0,0", + "equip_audio": "2232", "render_anim": "2586", "defence_anim": "397", "equipment_slot": "3", - "attack_anims": "395,395,395,395", + "attack_anims": "395,395,401,395", "destroy_message": "Drop", "grand_exchange_price": "332229", + "attack_audios": "2498,2498,2497,2498", "name": "Corrupt dragon battleaxe", "archery_ticket_price": "0", "id": "13973", @@ -123141,9 +124431,10 @@ "attack_speed": "4", "render_anim": "2586", "defence_anim": "397", - "attack_anims": "395,395,395,395", + "attack_anims": "395,395,401,395", "destroy_message": "Drop", "grand_exchange_price": "332229", + "attack_audios": "2498,2498,2497,2498", "name": "Corrupt dragon battleaxe", "tradeable": "true", "archery_ticket_price": "0", @@ -123160,12 +124451,14 @@ "attack_speed": "6", "weapon_interface": "2", "absorb": "0,0,0", + "equip_audio": "2232", "render_anim": "2586", "defence_anim": "397", "equipment_slot": "3", - "attack_anims": "395,395,395,395", + "attack_anims": "395,395,401,395", "destroy_message": "Drop", "grand_exchange_price": "332229", + "attack_audios": "2498,2498,2497,2498", "name": "C. dragon battleaxe (deg)", "archery_ticket_price": "0", "id": "13975", @@ -123179,6 +124472,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", @@ -123196,6 +124490,7 @@ "durability": null, "destroy": "true", "attack_speed": "4", + "equip_audio": "2248", "defence_anim": "397", "attack_anims": "396,396,395,396", "destroy_message": "Drop", @@ -123214,6 +124509,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", @@ -123235,6 +124531,7 @@ "attack_speed": "4", "weapon_interface": "6", "absorb": "0,0,0", + "equip_audio": "2248", "render_anim": "1928", "defence_anim": "397", "equipment_slot": "3", @@ -123258,7 +124555,8 @@ "destroy": "true", "archery_ticket_price": "0", "attack_speed": "4", - "id": "13980" + "id": "13980", + "equip_audio": "" }, { "shop_price": "60000", @@ -123268,10 +124566,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", @@ -123288,6 +124589,7 @@ "attack_speed": "5", "weapon_interface": "5", "absorb": "0,0,0", + "equip_audio": "2248", "render_anim": "1582", "defence_anim": "397", "equipment_slot": "3", @@ -123321,6 +124623,7 @@ "attack_speed": "5", "weapon_interface": "5", "absorb": "0,0,0", + "equip_audio": "2248", "render_anim": "1582", "equipment_slot": "3", "destroy_message": "Drop", @@ -123340,11 +124643,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", @@ -123362,6 +124667,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", @@ -123377,11 +124683,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", @@ -123396,6 +124704,7 @@ "two_handed": "true", "weapon_interface": "14", "absorb": "0,0,0", + "equip_audio": "2247", "render_anim": "28", "defence_anim": "2079", "equipment_slot": "3", @@ -123432,6 +124741,7 @@ "two_handed": "true", "weapon_interface": "14", "absorb": "0,0,0", + "equip_audio": "2247", "render_anim": "28", "defence_anim": "2079", "equipment_slot": "3", @@ -123444,7 +124754,7 @@ "bonuses": "55,55,55,0,0,5,5,5,5,5,0,60,0,0,0" }, { - "destroy_message": "You can get another copy by searching bookshelves around RuneScape.", + "destroy_message": "You can get another copy by searching bookshelves around 2009Scape.", "shop_price": "5", "examine": "A book about the Wilderness.", "durability": null, @@ -123938,6 +125248,7 @@ "attack_speed": "4", "weapon_interface": "5", "absorb": "0,0,0", + "equip_audio": "2248", "render_anim": "1629", "equipment_slot": "3", "destroy_message": "Drop", @@ -124012,7 +125323,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", @@ -124020,18 +125333,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", @@ -124126,7 +125440,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", @@ -124268,6 +125584,7 @@ "destroy": "true", "attack_speed": "4", "absorb": "0,3,1", + "equip_audio": "", "equipment_slot": "0", "destroy_message": "Drop", "name": "Sacred clay coif", @@ -124720,7 +126037,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", @@ -124749,7 +126068,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", @@ -124778,7 +126099,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", @@ -124807,7 +126130,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", @@ -124836,7 +126161,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", @@ -126904,6 +128231,7 @@ "attack_speed": "6", "weapon_interface": "10", "absorb": "0,0,0", + "equip_audio": "", "equipment_slot": "3", "destroy_message": "Drop", "name": "Warhammer (class 1)", @@ -128309,6 +129637,7 @@ "destroy": "true", "attack_speed": "4", "absorb": "0,0,0", + "equip_audio": "", "equipment_slot": "0", "destroy_message": "Drop", "name": "Coif (class 1)", @@ -128327,7 +129656,8 @@ "destroy": "true", "archery_ticket_price": "0", "attack_speed": "4", - "id": "14412" + "id": "14412", + "equip_audio": "" }, { "remove_head": "true", @@ -128337,6 +129667,7 @@ "destroy": "true", "attack_speed": "4", "absorb": "0,0,0", + "equip_audio": "", "equipment_slot": "0", "destroy_message": "Drop", "name": "Coif (class 2)", @@ -128365,6 +129696,7 @@ "destroy": "true", "attack_speed": "4", "absorb": "0,0,0", + "equip_audio": "", "equipment_slot": "0", "destroy_message": "Drop", "name": "Coif (class 3)", @@ -128393,6 +129725,7 @@ "destroy": "true", "attack_speed": "4", "absorb": "0,0,0", + "equip_audio": "", "equipment_slot": "0", "destroy_message": "Drop", "name": "Coif (class 4)", @@ -128870,6 +130203,7 @@ "destroy": "true", "attack_speed": "4", "absorb": "4,0,9", + "equip_audio": "2239", "equipment_slot": "4", "lendable": "true", "destroy_message": "Drop", @@ -128901,6 +130235,7 @@ "destroy": "true", "attack_speed": "4", "absorb": "4,0,9", + "equip_audio": "", "lendable": "true", "destroy_message": "Drop", "grand_exchange_price": "4453643", @@ -128910,30 +130245,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", @@ -128963,6 +130299,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", @@ -129012,6 +130349,7 @@ "destroy": "true", "attack_speed": "4", "absorb": "2,0,4", + "equip_audio": "2242", "equipment_slot": "7", "lendable": "true", "destroy_message": "Drop", @@ -129043,6 +130381,7 @@ "destroy": "true", "attack_speed": "4", "absorb": "3,0,6", + "equip_audio": "2239", "equipment_slot": "4", "lendable": "true", "destroy_message": "Drop", @@ -129553,7 +130892,7 @@ "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" }, { - "destroy_message": "You gained this orb from Dark Squall's base. You can probably get another one from visiting the same place.", + "destroy_message": "You can make another by casting the Charge Air Orb spell on the Abyssal Rift in the center of the Abyss.", "examine": "This orb can used to teleport people...somehow.", "durability": null, "name": "Strange teleorb", @@ -129690,6 +131029,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" }, { @@ -129703,6 +131043,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" }, { @@ -129715,6 +131056,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" }, { @@ -129754,46 +131096,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", @@ -129873,7 +131218,7 @@ { "destroy_message": "Drop", "shop_price": "1000", - "examine": "This will teleport me to Rellekka when I play it. ", + "examine": "This will teleport me to Rellekka when I play it.", "durability": null, "name": "Enchanted lyre(5)", "destroy": "true", @@ -129887,7 +131232,7 @@ { "destroy_message": "Drop", "shop_price": "1000", - "examine": "This will teleport me to Rellekka when I play it. ", + "examine": "This will teleport me to Rellekka when I play it.", "durability": null, "name": "Enchanted lyre(6)", "destroy": "true", @@ -130619,82 +131964,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.", @@ -130741,5 +132089,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 1fb75f150..000000000 --- a/Server/data/configs/music_configs.json +++ /dev/null @@ -1,1552 +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,9791}" - }, - { - "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,9791}-{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": "{2368,4352,2431,4415}" - }, - { - "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": "{2688,4800,2751,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,4416,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": "" - }, - { - "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": "{2560,3648,2623,3711}" - }, - { - "id": "290", - "borders": "{2560,3584,2623,3647}" - }, - { - "id": "291", - "borders": "{2688,3648,2751,3711}" - }, - { - "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,3007,4991}-{3008,4928,3071,4991}" - }, - { - "id": "403", - "borders": "{3008,5056,3071,5119}" - }, - { - "id": "404", - "borders": "{2823,5250,2844,5310}-{2844,5250,2878,5280}" - }, - { - "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": "{2496,4736,2559,4799}" - }, - { - "id": "412", - "borders": "{3008,4800,3071,4863}" - }, - { - "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": "" - }, - { - "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": "542", - "borders": "{3072,9280,3135,9343}" - }, - { - "id": "544", - "borders": "{2304,3648,2367,3711}-{2304,3584,2367,3647}" - }, - { - "id": "555", - "borders": "" - }, - { - "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,3327,9855}-{3072,5184,3135,5247}-{3008,5184,3071,5247}" - }, - { - "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}" - } -] diff --git a/Server/data/configs/music_regions.json b/Server/data/configs/music_regions.json new file mode 100644 index 000000000..ebfb70c82 --- /dev/null +++ b/Server/data/configs/music_regions.json @@ -0,0 +1,2566 @@ +[ + { + "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": "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 326a072fb..71d194eab 100644 --- a/Server/data/configs/npc_configs.json +++ b/Server/data/configs/npc_configs.json @@ -163,14 +163,14 @@ }, { "examine": "Known for his light-fingered qualities.", - "melee_animation": "284", + "melee_animation": "422", "range_animation": "0", "attack_speed": "4", "respawn_delay": "60", - "defence_animation": "285", + "defence_animation": "0", "weakness": "9", "magic_animation": "0", - "death_animation": "287", + "death_animation": "836", "name": "Thief", "defence_level": "10", "safespot": null, @@ -182,12 +182,12 @@ }, { "examine": "He tries to keep order around here.", - "melee_animation": "381", + "melee_animation": "400", "range_animation": "0", - "combat_audio": "511,513,512", + "combat_audio": "2549,1979,512", "attack_speed": "4", "respawn_delay": "60", - "defence_animation": "1156", + "defence_animation": "404", "weakness": "9", "magic_animation": "0", "death_animation": "836", @@ -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", @@ -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,9 +1743,9 @@ }, { "examine": "A vicious mountain wolf.", - "slayer_task": "92", "melee_animation": "6579", "range_animation": "6579", + "combat_audio": "909,912,911", "attack_speed": "4", "defence_animation": "6578", "weakness": "9", @@ -1797,9 +1764,9 @@ }, { "examine": "A vicious mountain wolf.", - "slayer_task": "92", "melee_animation": "6579", "range_animation": "6579", + "combat_audio": "909,912,911", "attack_speed": "4", "defence_animation": "6578", "weakness": "7", @@ -1834,7 +1801,6 @@ }, { "examine": "He doesn't seem pleased to see me.", - "slayer_task": "27", "melee_animation": "6562", "range_animation": "6562", "attack_speed": "4", @@ -1917,7 +1883,6 @@ { "agg_radius": "", "examine": "Eeek! A ghost!", - "slayer_task": "36", "melee_animation": "5540", "range_animation": "5540", "combat_audio": "436,439,438", @@ -1942,7 +1907,6 @@ }, { "examine": "Eeek! A ghost!", - "slayer_task": "36", "melee_animation": "5540", "range_animation": "0", "combat_audio": "436,439,438", @@ -1967,7 +1931,6 @@ }, { "examine": "Eek! A bear!", - "slayer_task": "6", "melee_animation": "4925", "range_animation": "0", "combat_audio": "300,302,301", @@ -1992,7 +1955,6 @@ }, { "examine": "Eek! A bear!", - "slayer_task": "6", "melee_animation": "4925", "range_animation": "0", "combat_audio": "300,302,301", @@ -2035,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", @@ -2061,7 +2022,6 @@ }, { "examine": "Tiny", - "slayer_task": "71", "melee_animation": "6254", "range_animation": "0", "combat_audio": "3611,3612,3610", @@ -2082,7 +2042,6 @@ }, { "examine": "A very large elemental adversary.", - "slayer_task": "33", "melee_animation": "4666", "range_animation": "4666", "combat_audio": "447,451,450", @@ -2107,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", @@ -2132,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", @@ -2161,13 +2118,14 @@ "range_animation": "128", "attack_speed": "5", "defence_animation": "129", + "weakness": "7", "magic_animation": "128", "death_animation": "131", "name": "Jogre", "defence_level": "41", "safespot": null, "lifepoints": "50", - "strength_level": "1", + "strength_level": "36", "id": "113", "aggressive": "true", "clue_level": "1", @@ -2182,6 +2140,7 @@ "magic_level": "27", "respawn_delay": "25", "defence_animation": "360", + "slayer_exp": "", "magic_animation": "359", "death_animation": "361", "name": "Mogre", @@ -2218,7 +2177,6 @@ }, { "examine": "Big, ugly, and smelly.", - "slayer_task": "44", "melee_animation": "359", "range_animation": "359", "attack_speed": "4", @@ -2243,7 +2201,6 @@ }, { "examine": "A very large foe.", - "slayer_task": "44", "melee_animation": "4652", "range_animation": "4652", "combat_audio": "448,451,450", @@ -2268,7 +2225,6 @@ }, { "examine": "A short angry guy.", - "slayer_task": "29", "melee_animation": "99", "range_animation": "0", "attack_speed": "7", @@ -2289,7 +2245,6 @@ }, { "examine": "A dwarf gone bad.", - "slayer_task": "29", "melee_animation": "99", "range_animation": "0", "combat_audio": "2567,419,418", @@ -2314,7 +2269,6 @@ }, { "examine": "A mountain dwelling short angry guy.", - "slayer_task": "29", "melee_animation": "99", "range_animation": "0", "attack_speed": "6", @@ -2383,7 +2337,6 @@ }, { "examine": "A strange, inhuman, elemental warrior.", - "slayer_task": "30", "melee_animation": "393", "range_animation": "0", "attack_speed": "4", @@ -2407,7 +2360,6 @@ }, { "examine": "A cold-hearted elemental warrior.", - "slayer_task": "48", "melee_animation": "451", "range_animation": "451", "combat_audio": "2500,530,529", @@ -2432,7 +2384,6 @@ }, { "examine": "Looks pretty otherworldly to me!", - "slayer_task": "65", "range_animation": "0", "attack_speed": "5", "magic_level": "45", @@ -2496,7 +2447,6 @@ }, { "examine": "An inhabitant of icy regions.", - "slayer_task": "7", "melee_animation": "5669", "range_animation": "0", "defence_animation": "0", @@ -2514,7 +2464,6 @@ }, { "examine": "Perhaps our oldest relatives?", - "slayer_task": "61", "melee_animation": "220", "range_animation": "0", "combat_audio": "629,631,630", @@ -2560,7 +2509,6 @@ }, { "examine": "A nasty, poisonous arachnid.", - "slayer_task": "76", "melee_animation": "5327", "range_animation": "0", "combat_audio": "537,539,538", @@ -2600,7 +2548,6 @@ }, { "examine": "A giant raptor.", - "slayer_task": "7", "melee_animation": "1010", "range_animation": "0", "combat_audio": "832,834,833", @@ -2664,7 +2611,6 @@ }, { "examine": "Must be the pack leader.", - "slayer_task": "92", "melee_animation": "6559", "range_animation": "0", "respawn_delay": "60", @@ -2699,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", @@ -2720,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", @@ -2743,7 +2687,6 @@ }, { "examine": "A cold-hearted elemental warrior.", - "slayer_task": "48", "melee_animation": "451", "range_animation": "451", "combat_audio": "2500,530,529", @@ -2768,7 +2711,6 @@ }, { "examine": "A sea bird.", - "slayer_task": "7", "melee_animation": "0", "range_animation": "0", "defence_animation": "0", @@ -2785,7 +2727,6 @@ }, { "examine": "A sea bird.", - "slayer_task": "7", "melee_animation": "0", "range_animation": "0", "defence_animation": "0", @@ -2802,7 +2743,6 @@ }, { "examine": "A sea bird.", - "slayer_task": "7", "melee_animation": "0", "range_animation": "0", "defence_animation": "0", @@ -2851,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", @@ -2871,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", @@ -3102,6 +3039,7 @@ "examine": "He jumps out and attacks people.", "melee_animation": "422", "range_animation": "0", + "combat_audio": "511,513,512", "respawn_delay": "60", "defence_animation": "0", "weakness": "9", @@ -3142,7 +3080,7 @@ "combat_audio": "2503,15,512", "attack_speed": "5", "respawn_delay": "25", - "defence_animation": "0", + "defence_animation": "388", "weakness": "7", "magic_animation": "0", "death_animation": "836", @@ -3559,7 +3497,6 @@ }, { "examine": "A rocky slug.", - "slayer_task": "69", "melee_animation": "1595", "range_animation": "1595", "combat_audio": "729,731,730", @@ -3581,7 +3518,6 @@ }, { "examine": "A rocky slug.", - "slayer_task": "69", "melee_animation": "1595", "range_animation": "1595", "combat_audio": "729,731,730", @@ -3660,7 +3596,6 @@ }, { "examine": "A dwarven guard.", - "slayer_task": "29", "melee_animation": "99", "range_animation": "0", "combat_audio": "511,513,512", @@ -3850,7 +3785,6 @@ }, { "examine": "A dirty rat.", - "slayer_task": "67", "melee_animation": "4933", "range_animation": "0", "combat_audio": "703,705,704", @@ -4002,44 +3936,53 @@ "attack_level": "1" }, { - "examine": "He's guarding the door.", - "melee_animation": "0", + "examine": "It's one of General Khazard's guards.", + "melee_animation": "395", "range_animation": "0", - "defence_animation": "0", + "combat_audio": "511,513,512", + "attack_speed": "6", + "respawn_delay": "60", + "defence_animation": "397", + "weakness": "6", "magic_animation": "0", - "death_animation": "0", + "death_animation": "836", "name": "Khazard Guard", - "defence_level": "1", + "defence_level": "22", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "lifepoints": "31", + "strength_level": "22", "id": "253", "range_level": "1", - "attack_level": "1" + "attack_level": "22" }, { "examine": "He's been guarding the tavern for a bit too long.", - "melee_animation": "0", + "melee_animation": "395", "range_animation": "0", - "defence_animation": "0", + "combat_audio": "511,513,512", + "attack_speed": "6", + "respawn_delay": "60", + "defence_animation": "397", + "weakness": "6", "magic_animation": "0", - "death_animation": "0", + "death_animation": "836", "name": "Khazard Guard", - "defence_level": "1", + "defence_level": "22", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "lifepoints": "31", + "strength_level": "22", "id": "254", "range_level": "1", - "attack_level": "1" + "attack_level": "22" }, { "examine": "His armour indicates he's a Khazard Guard.", "melee_animation": "395", "range_animation": "0", + "combat_audio": "511,513,512", "attack_speed": "6", "respawn_delay": "60", - "defence_animation": "0", + "defence_animation": "397", "weakness": "6", "magic_animation": "0", "death_animation": "836", @@ -4056,9 +3999,10 @@ "examine": "His armour indicates he's a Khazard Guard.", "melee_animation": "395", "range_animation": "0", + "combat_audio": "511,513,512", "attack_speed": "6", "respawn_delay": "60", - "defence_animation": "0", + "defence_animation": "397", "weakness": "6", "magic_animation": "0", "death_animation": "836", @@ -4071,6 +4015,66 @@ "range_level": "1", "attack_level": "22" }, + { + "examine": "He takes the slaves to the arena. He rarely takes them back..", + "melee_animation": "395", + "range_animation": "0", + "combat_audio": "511,513,512", + "attack_speed": "6", + "respawn_delay": "60", + "defence_animation": "397", + "weakness": "6", + "magic_animation": "0", + "death_animation": "836", + "name": "Khazard Guard", + "defence_level": "22", + "safespot": null, + "lifepoints": "31", + "strength_level": "22", + "id": "257", + "range_level": "1", + "attack_level": "22" + }, + { + "examine": "He looks really nasty. Smells bad too.", + "melee_animation": "7049", + "range_animation": "0", + "combat_audio": "511,513,512", + "attack_speed": "4", + "respawn_delay": "60", + "defence_animation": "7050", + "weakness": "6", + "magic_animation": "0", + "death_animation": "836", + "name": "General Khazard", + "defence_level": "80", + "safespot": null, + "lifepoints": "170", + "strength_level": "78", + "id": "258", + "range_level": "1", + "attack_level": "75" + }, + { + "examine": "A tough looking barman.", + "melee_animation": "0", + "range_animation": "0", + "combat_audio": "511,513,512", + "attack_speed": "6", + "respawn_delay": "60", + "defence_animation": "397", + "weakness": "6", + "magic_animation": "0", + "death_animation": "0", + "name": "Khazard barman", + "defence_level": "1", + "safespot": null, + "lifepoints": "10", + "strength_level": "1", + "id": "259", + "range_level": "1", + "attack_level": "1" + }, { "examine": "A slave fighter. He looks mistreated and weak.", "melee_animation": "0", @@ -4087,29 +4091,103 @@ "range_level": "1", "attack_level": "1" }, + { + "examine": "She looks wealthy.", + "melee_animation": "0", + "range_animation": "0", + "defence_animation": "0", + "magic_animation": "0", + "death_animation": "0", + "name": "Lady Servil", + "defence_level": "1", + "safespot": null, + "lifepoints": "10", + "strength_level": "1", + "id": "264", + "range_level": "1", + "attack_level": "1" + }, + { + "examine": "A young squire. He looks worse for wear.", + "magic_animation": "0", + "death_animation": "0", + "name": "Jeremy Servil", + "defence_level": "0", + "safespot": null, + "lifepoints": "10", + "strength_level": "0", + "id": "265", + "clue_level": "0", + "range_level": "0", + "attack_level": "0" + }, + { + "examine": "A young squire. He looks worse for wear.", + "magic_animation": "0", + "death_animation": "0", + "name": "Jeremy Servil", + "defence_level": "0", + "safespot": null, + "lifepoints": "10", + "strength_level": "0", + "id": "266", + "clue_level": "0", + "range_level": "0", + "attack_level": "0" + }, + { + "examine": "Jeremy father, Sir Servil.", + "magic_animation": "0", + "death_animation": "0", + "name": "Justin Servil", + "defence_level": "0", + "safespot": null, + "lifepoints": "10", + "strength_level": "0", + "id": "267", + "clue_level": "0", + "range_level": "0", + "attack_level": "0" + }, + { + "examine": "A scruffy looking chap.", + "magic_animation": "0", + "death_animation": "0", + "name": "Local", + "defence_level": "0", + "safespot": null, + "lifepoints": "10", + "strength_level": "0", + "id": "268", + "clue_level": "0", + "range_level": "0", + "attack_level": "0" + }, { "melee_animation": "6581", "respawn_delay": "60", "defence_animation": "6578", "death_animation": "6576", "name": "Bouncer", - "defence_level": "1", + "defence_level": "120", "safespot": null, "lifepoints": "116", - "strength_level": "1", + "strength_level": "120", "id": "269", "aggressive": "true", "range_level": "1", - "attack_level": "1" + "attack_level": "120" }, { "examine": "Khazard's strongest ogre warrior.", - "melee_animation": "2100", - "range_animation": "0", - "defence_animation": "0", - "weakness": "6", - "magic_animation": "0", - "death_animation": "8576", + "melee_animation": "359", + "range_animation": "359", + "attack_speed": "6", + "magic_level": "1", + "respawn_delay": "25", + "defence_animation": "360", + "magic_animation": "359", + "death_animation": "361", "name": "Khazard Ogre", "defence_level": "46", "safespot": null, @@ -4117,11 +4195,12 @@ "strength_level": "46", "id": "270", "aggressive": "true", + "bonuses": "22,22,22,0,0,0,0,0,0,0,0,20,0,0,0", "range_level": "1", "attack_level": "46" }, { - "examine": "A large", + "examine": "A large angry scorpion", "melee_animation": "6254", "range_animation": "0", "combat_audio": "3611,3612,3610", @@ -4139,13 +4218,56 @@ "range_level": "36", "attack_level": "27" }, + { + "examine": "He walks with a slight limp.", + "melee_animation": "401", + "range_animation": "0", + "combat_audio": "511,513,512", + "magic_level": "1", + "respawn_delay": "30", + "defence_animation": "404", + "weakness": "", + "magic_animation": "0", + "death_animation": "836", + "name": "Lucien", + "defence_level": "10", + "safespot": null, + "lifepoints": "17", + "strength_level": "11", + "id": "272", + "aggressive": "", + "range_level": "1", + "attack_level": "12" + }, + { + "examine": "He walks with a slight limp.", + "melee_animation": "", + "range_animation": "", + "combat_audio": "", + "defence_animation": "404", + "weakness": "", + "magic_animation": "", + "death_animation": "836", + "name": "Lucien", + "defence_level": "", + "safespot": null, + "lifepoints": "", + "strength_level": "", + "id": "273", + "aggressive": "", + "range_level": "", + "attack_level": "12" + }, { "examine": "A guard who has devoted their life to Armadyl.", "range_animation": "0", + "melee_animation": "401", + "combat_audio": "511,513,512", "respawn_delay": "60", - "defence_animation": "0", + "defence_animation": "404", "weakness": "8", "magic_animation": "0", + "death_animation": "836", "name": "Guardian of Armadyl", "defence_level": "30", "safespot": null, @@ -4158,9 +4280,13 @@ { "examine": "A guard who has devoted their life to Armadyl.", "range_animation": "0", - "defence_animation": "0", + "melee_animation": "401", + "combat_audio": "511,513,512", + "respawn_delay": "60", + "defence_animation": "404", "weakness": "8", "magic_animation": "0", + "death_animation": "836", "name": "Guardian of Armadyl", "defence_level": "30", "safespot": null, @@ -4172,21 +4298,28 @@ }, { "examine": "Intimidating!", + "start_gfx": "99", "combat_style": "2", + "start_height": "96", "melee_animation": "0", "range_animation": "0", + "combat_audio": "511,513,512", + "attack_speed": "8", + "magic_level": "62", + "end_gfx": "101", "respawn_delay": "60", - "defence_animation": "0", + "defence_animation": "425", "weakness": "3", - "magic_animation": "0", + "magic_animation": "711", "death_animation": "836", "name": "Fire Warrior of Lesarkus", - "defence_level": "55", + "defence_level": "78", "safespot": null, - "lifepoints": "78", - "strength_level": "55", + "lifepoints": "59", + "strength_level": "78", "id": "277", "range_level": "1", + "projectile": "127", "attack_level": "55" }, { @@ -4651,131 +4784,130 @@ "range_level": "1", "attack_level": "1" }, - { - "examine": "A mourner, or plague healer.", - "melee_animation": "414", - "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": "2", - "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": "2", - "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": "2", - "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": "2", - "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.", @@ -4794,93 +4926,111 @@ "attack_level": "1" }, { - "examine": "Man...", - "name": "Man", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", + "examine": "One of Gielinor's many citizens.", + "melee_animation": "422", + "range_animation": "422", "combat_audio": "511,513,512", - "strength_level": "1", + "attack_speed": "4", + "magic_level": "6", + "respawn_delay": "18", + "defence_animation": "404", + "weakness": "9", + "magic_animation": "422", + "death_animation": "9055", + "name": "Man", + "defence_level": "5", + "safespot": null, + "lifepoints": "7", + "strength_level": "2", "id": "359", "clue_level": "0", "range_level": "1", + "attack_level": "5" + }, + { + "examine": "One of Gielinor's many citizens.", + "melee_animation": "422", + "range_animation": "", + "combat_audio": "511,506,505", + "attack_speed": "4", + "respawn_delay": "50", + "defence_animation": "404", + "weakness": "", + "magic_animation": "", + "death_animation": "836", + "name": "Woman", + "defence_level": "1", + "safespot": null, + "lifepoints": "13", + "strength_level": "1", + "id": "360", + "clue_level": "", + "bonuses": "0,0,0,0,0,0,0,0,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": "422", + "combat_audio": "511,506,505", + "attack_speed": "4", + "respawn_delay": "50", + "defence_animation": "404", + "weakness": "", + "magic_animation": "", + "death_animation": "836", + "name": "Woman", + "defence_level": "10", + "safespot": null, + "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": "10" + }, + { + "examine": "One of Gielinor's many citizens.", + "melee_animation": "422", + "range_animation": "", + "combat_audio": "511,506,505", + "attack_speed": "4", + "respawn_delay": "50", + "defence_animation": "404", + "weakness": "", + "magic_animation": "", + "death_animation": "836", + "name": "Woman", + "defence_level": "2", + "safespot": null, + "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": "1" }, { "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": "10", "safespot": null, - "lifepoints": "2", - "strength_level": "2", - "id": "360", - "clue_level": "0", - "range_level": "1", - "attack_level": "2" - }, - { - "examine": "One of Gielinor's many citizens.", - "melee_animation": "422", - "range_animation": "0", - "combat_audio": "511,506,505", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Woman", - "defence_level": "2", - "safespot": null, - "lifepoints": "100", - "strength_level": "2", - "id": "361", - "clue_level": "0", - "range_level": "1", - "attack_level": "2" - }, - { - "examine": "One of Gielinor's many citizens.", - "melee_animation": "422", - "range_animation": "0", - "combat_audio": "511,506,505", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Woman", - "defence_level": "2", - "safespot": null, - "lifepoints": "2", - "strength_level": "2", - "id": "362", - "clue_level": "0", - "range_level": "1", - "attack_level": "2" - }, - { - "examine": "One of Gielinor's many citizens.", - "melee_animation": "422", - "range_animation": "0", - "combat_audio": "511,506,505", - "defence_animation": "0", - "weakness": "9", - "magic_animation": "0", - "death_animation": "836", - "name": "Woman", - "defence_level": "2", - "safespot": null, - "lifepoints": "100", - "strength_level": "2", + "lifepoints": "23", + "strength_level": "10", "id": "363", - "clue_level": "0", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", "range_level": "1", - "attack_level": "2" + "attack_level": "10" }, { "examine": "King Lathas of East Ardougne.", @@ -4944,23 +5094,6 @@ "range_level": "1", "attack_level": "1" }, - { - "examine": "A mourner", - "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", @@ -4979,7 +5112,6 @@ }, { "examine": "Big, ugly, and smelly.", - "slayer_task": "64", "melee_animation": "359", "range_animation": "359", "attack_speed": "6", @@ -5071,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", @@ -5093,6 +5226,7 @@ "strength_level": "1", "id": "386", "range_level": "1", + "respawn_delay": "60", "attack_level": "1" }, { @@ -5105,6 +5239,7 @@ "strength_level": "1", "id": "387", "range_level": "1", + "respawn_delay": "60", "attack_level": "1" }, { @@ -5162,7 +5297,6 @@ }, { "examine": "Beefy.", - "slayer_task": "20", "melee_animation": "5849", "range_animation": "5849", "combat_audio": "369,371,370", @@ -5233,6 +5367,7 @@ "examine": "Insects buzzing around.", "melee_animation": "0", "range_animation": "0", + "combat_audio": "819,821,820", "defence_animation": "0", "magic_animation": "0", "death_animation": "0", @@ -5242,13 +5377,12 @@ "lifepoints": "10", "strength_level": "1", "id": "411", - "aggressive": "true", + "aggressive": "false", "range_level": "1", "attack_level": "1" }, { "examine": "An annoying flappy thing.", - "slayer_task": "5", "melee_animation": "4915", "range_animation": "4915", "attack_speed": "5", @@ -5385,6 +5519,7 @@ "examine": "Dead but not gone.", "melee_animation": "390", "range_animation": "0", + "combat_audio": "511,439,438", "respawn_delay": "50", "defence_animation": "0", "weakness": "9", @@ -5403,6 +5538,7 @@ "examine": "Dead but not gone.", "melee_animation": "390", "range_animation": "0", + "combat_audio": "511,439,438", "respawn_delay": "50", "defence_animation": "0", "weakness": "9", @@ -5421,6 +5557,7 @@ "examine": "Dead but not gone.", "melee_animation": "390", "range_animation": "0", + "combat_audio": "511,439,438", "respawn_delay": "50", "defence_animation": "0", "weakness": "9", @@ -5437,9 +5574,9 @@ }, { "examine": "Dead but not gone.", - "slayer_task": "73", "melee_animation": "390", "range_animation": "0", + "combat_audio": "511,439,438", "respawn_delay": "50", "defence_animation": "0", "weakness": "9", @@ -5457,9 +5594,9 @@ }, { "examine": "Dead but not gone.", - "slayer_task": "73", "melee_animation": "390", "range_animation": "0", + "combat_audio": "511,439,438", "magic_level": "1", "respawn_delay": "50", "defence_animation": "0", @@ -5684,7 +5821,6 @@ }, { "examine": "Overgrown vermin.", - "slayer_task": "67", "melee_animation": "4933", "range_animation": "0", "combat_audio": "703,705,704", @@ -5706,7 +5842,7 @@ }, { "examine": "I wonder who he's guarding?", - "melee_animation": "0", + "melee_animation": "401", "range_animation": "0", "combat_audio": "511,513,512", "attack_speed": "5", @@ -5746,7 +5882,7 @@ }, { "examine": "I wonder who he's guarding?", - "melee_animation": "0", + "melee_animation": "401", "range_animation": "0", "combat_audio": "511,513,512", "attack_speed": "5", @@ -6093,7 +6229,6 @@ }, { "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", @@ -6162,7 +6297,6 @@ }, { "examine": "A vicious", - "slayer_task": "71", "melee_animation": "6261", "range_animation": "0", "combat_audio": "3611,3612,3610", @@ -6249,7 +6383,6 @@ }, { "examine": "A minion of Rashiliyia.", - "slayer_task": "75", "melee_animation": "5485", "range_animation": "0", "attack_speed": "5", @@ -6270,7 +6403,6 @@ }, { "examine": "A minion of Rashiliyia.", - "slayer_task": "75", "melee_animation": "5485", "range_animation": "0", "defence_animation": "0", @@ -6288,7 +6420,6 @@ }, { "examine": "The animated dead; one of Rashiliyia's minions.", - "slayer_task": "93", "melee_animation": "5568", "range_animation": "0", "attack_speed": "5", @@ -6309,7 +6440,6 @@ }, { "examine": "The animated dead; one of Rashiliyia's minions.", - "slayer_task": "93", "melee_animation": "5571", "range_animation": "0", "attack_speed": "5", @@ -6566,13 +6696,13 @@ "attack_level": "1" }, { - "examine": "Like a grave keeper", + "examine": "He clearly takes pride in his appearance.", "range_animation": "0", "magic_level": "60", "defence_animation": "0", "weakness": "10", "magic_animation": "0", - "name": "Shopkeeper", + "name": "Shopkeeper Kofi", "defence_level": "60", "safespot": null, "lifepoints": "285", @@ -6582,6 +6712,23 @@ "range_level": "60", "attack_level": "60" }, + { + "examine": "Needs a haircut.", + "range_animation": "0", + "magic_level": "60", + "defence_animation": "0", + "weakness": "10", + "magic_animation": "0", + "name": "Shop assistant", + "defence_level": "60", + "safespot": null, + "lifepoints": "285", + "strength_level": "60", + "id": "533", + "aggressive": "true", + "range_level": "60", + "attack_level": "60" + }, { "examine": "Sells stuff.", "melee_animation": "0", @@ -6758,6 +6905,16 @@ "range_level": "1", "attack_level": "1" }, + { + "examine": "A being from a mysterious realm.", + "name": "Jukat", + "id": "564" + }, + { + "examine": "Is he invisible, or just floating clothing?", + "name": "Irksol", + "id": "566" + }, { "examine": "Looks strange and mysterious.", "melee_animation": "0", @@ -7064,6 +7221,38 @@ "range_level": "1", "attack_level": "1" }, + { + "examine": "A student busy studying the digsite.", + "melee_animation": "0", + "range_animation": "0", + "defence_animation": "0", + "magic_animation": "0", + "death_animation": "0", + "name": "Student", + "defence_level": "1", + "safespot": null, + "lifepoints": "10", + "strength_level": "1", + "id": "615", + "range_level": "1", + "attack_level": "1" + }, + { + "examine": "A student busy studying the digsite.", + "melee_animation": "0", + "range_animation": "0", + "defence_animation": "0", + "magic_animation": "0", + "death_animation": "0", + "name": "Student", + "defence_level": "1", + "safespot": null, + "lifepoints": "10", + "strength_level": "1", + "id": "616", + "range_level": "1", + "attack_level": "1" + }, { "examine": "A student busy studying the digsite.", "melee_animation": "0", @@ -7536,7 +7725,6 @@ }, { "examine": "A big, scary, jet-black demon.", - "slayer_task": "8", "melee_animation": "64", "range_animation": "64", "combat_audio": "397,399,398", @@ -7873,32 +8061,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.", @@ -7944,39 +8143,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.", @@ -8125,7 +8327,6 @@ }, { "examine": "A badly-behaved goblin.", - "slayer_task": "38", "melee_animation": "6184", "range_animation": "0", "defence_animation": "0", @@ -8301,51 +8502,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.", @@ -8399,20 +8600,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.", @@ -8766,7 +8968,6 @@ }, { "examine": "A vicious desert wolf.", - "slayer_task": "92", "melee_animation": "6559", "range_animation": "0", "defence_animation": "0", @@ -8896,7 +9097,6 @@ }, { "examine": "Tough-looking.", - "slayer_task": "64", "range_animation": "0", "attack_speed": "4", "magic_level": "1", @@ -8927,7 +9127,6 @@ }, { "examine": "These ogres protect the city.", - "slayer_task": "64", "melee_animation": "0", "range_animation": "0", "defence_animation": "0", @@ -8945,7 +9144,6 @@ }, { "examine": "An ogre that guards.", - "slayer_task": "64", "melee_animation": "0", "range_animation": "0", "defence_animation": "0", @@ -8963,7 +9161,6 @@ }, { "examine": "Tries to keep the peace.", - "slayer_task": "64", "melee_animation": "2100", "range_animation": "0", "defence_animation": "0", @@ -9013,7 +9210,6 @@ }, { "examine": "Big and ugly looking.", - "slayer_task": "64", "melee_animation": "2100", "range_animation": "0", "respawn_delay": "60", @@ -9048,7 +9244,6 @@ }, { "examine": "Funnily enough", - "slayer_task": "64", "melee_animation": "0", "range_animation": "0", "defence_animation": "0", @@ -9065,7 +9260,6 @@ }, { "examine": "Funnily enough", - "slayer_task": "64", "melee_animation": "0", "range_animation": "0", "defence_animation": "0", @@ -9082,7 +9276,6 @@ }, { "examine": "Funnily enough", - "slayer_task": "64", "melee_animation": "0", "range_animation": "0", "defence_animation": "0", @@ -9518,7 +9711,7 @@ }, { "examine": "I wonder who he's guarding?", - "melee_animation": "0", + "melee_animation": "401", "range_animation": "0", "combat_audio": "511,513,512", "attack_speed": "5", @@ -9636,7 +9829,6 @@ }, { "examine": "Must be related to Elvarg.", - "slayer_task": "41", "melee_animation": "80", "range_animation": "80", "combat_audio": "408,410,409", @@ -9842,7 +10034,6 @@ }, { "examine": "Converts grass to beef.", - "slayer_task": "20", "melee_animation": "5849", "range_animation": "5849", "combat_audio": "369,371,370", @@ -9863,10 +10054,11 @@ }, { "examine": "He's had a fair bit to drink...", - "start_gfx": "33", + "start_gfx": "", "combat_style": "1", "melee_animation": "99", "range_animation": "99", + "combat_audio": "2707,419,418", "attack_speed": "5", "magic_level": "20", "defence_animation": "101", @@ -9902,7 +10094,6 @@ }, { "examine": "It's one of Iban's pets.", - "slayer_task": "76", "melee_animation": "5327", "range_animation": "0", "combat_audio": "537,539,538", @@ -9921,7 +10112,6 @@ }, { "examine": "It's one of Iban's pet vermin.", - "slayer_task": "67", "range_animation": "0", "combat_audio": "703,705,704", "defence_animation": "0", @@ -10285,6 +10475,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", @@ -10310,7 +10505,6 @@ }, { "examine": "A nasty, poisonous arachnid.", - "slayer_task": "76", "melee_animation": "5327", "range_animation": "0", "combat_audio": "537,539,538", @@ -10369,7 +10563,6 @@ }, { "examine": "Yep. Definitely a chicken.", - "slayer_task": "7", "melee_animation": "5387", "range_animation": "5387", "combat_audio": "355,357,356", @@ -10390,7 +10583,6 @@ }, { "examine": "He rules the", - "slayer_task": "7", "melee_animation": "5387", "range_animation": "0", "respawn_delay": "60", @@ -10492,7 +10684,7 @@ "attack_level": "24" }, { - "examine": "Could his name be 'Lurch'?", + "examine": "It looks very hungry!", "melee_animation": "7183", "slayer_exp": "50", "death_animation": "7185", @@ -10723,6 +10915,22 @@ "range_level": "1", "attack_level": "1" }, + { + "examine": "A sergeant of the Imperial Guard.", + "melee_animation": "0", + "range_animation": "0", + "defence_animation": "0", + "magic_animation": "0", + "death_animation": "0", + "name": "Sergeant", + "defence_level": "1", + "safespot": null, + "lifepoints": "10", + "strength_level": "1", + "id": "1062", + "range_level": "1", + "attack_level": "1" + }, { "examine": "A soldier of the Imperial Guard.", "melee_animation": "0", @@ -10754,18 +10962,14 @@ }, { "examine": "A soldier of the Imperial Guard.", - "combat_audio": "511,513,512", - "melee_animation": "393", - "attack_speed": "4", - "magic_level": "1", - "defence_animation": "397", - "death_animation": "836", "name": "Soldier", "defence_level": "44", "safespot": null, "lifepoints": "55", + "combat_audio": "511,513,512", "strength_level": "44", "id": "1065", + "magic_level": "1", "range_level": "1", "attack_level": "44" }, @@ -10840,21 +11044,18 @@ { "examine": "The Burthorpe Castle cook.", "melee_animation": "422", - "range_animation": "422", - "combat_audio": "511,506,505", - "attack_speed": "4", - "defence_animation": "404", - "weakness": "9", - "magic_animation": "422", - "death_animation": "9055", + "magic_level": "1", + "respawn_delay": "60", + "defence_animation": "425", + "death_animation": "836", "name": "Eadburg", - "defence_level": "1", + "defence_level": "3", "safespot": null, "lifepoints": "10", - "strength_level": "1", + "strength_level": "2", "id": "1072", "range_level": "1", - "attack_level": "1" + "attack_level": "3" }, { "examine": "A Burthorpe Castle Archer.", @@ -10902,12 +11103,10 @@ }, { "examine": "A Burthorpe Castle guard.", - "melee_animation": "395", - "range_animation": "0", + "melee_animation": "401", "combat_audio": "511,513,512", "magic_level": "1", - "defence_animation": "0", - "magic_animation": "0", + "defence_animation": "404", "death_animation": "836", "name": "Guard", "defence_level": "33", @@ -10921,12 +11120,10 @@ }, { "examine": "A Burthorpe Castle guard.", - "melee_animation": "395", - "range_animation": "0", + "melee_animation": "401", "combat_audio": "511,513,512", "magic_level": "1", - "defence_animation": "0", - "magic_animation": "0", + "defence_animation": "404", "death_animation": "836", "name": "Guard", "defence_level": "33", @@ -10939,35 +11136,77 @@ "attack_level": "33" }, { - "examine": "A servant for Prince Anlaf.", - "melee_animation": "422", - "range_animation": "422", - "combat_audio": "511,513,512", - "defence_animation": "404", - "magic_animation": "422", - "death_animation": "9055", - "name": "Servant", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", - "strength_level": "1", - "id": "1081", - "range_level": "1", - "attack_level": "1" + "examine": "An off-duty Burthorpe Castle guard.", + "name": "Harold", + "id": "1078" }, { + "examine": "Barman of the Toad and Chicken.", + "name": "Tostig", + "id": "1079" + }, + { + "examine": "Head servant for Prince Anlaf.", + "name": "Eohric", + "id": "1080" + }, + { + "examine": "A servant for Prince Anlaf.", + "melee_animation": "422", + "magic_level": "1", + "respawn_delay": "60", + "defence_animation": "425", "death_animation": "836", - "name": "Ocga", - "defence_level": "1", + "name": "Servant", + "defence_level": "3", "safespot": null, "lifepoints": "10", + "strength_level": "3", + "id": "1081", + "range_level": "1", + "attack_level": "3" + }, + { + "examine": "Smithy for Burthorpe.", + "name": "Dunstan", + "id": "1082" + }, + { + "examine": "Shopkeeper for Burthorpe.", + "name": "Wistan", + "id": "1083" + }, + { + "examine": "A citizen of Burthorpe.", "melee_animation": "422", - "strength_level": "1", + "magic_level": "1", + "respawn_delay": "60", + "defence_animation": "425", + "death_animation": "836", + "name": "Breoca", + "defence_level": "3", + "safespot": null, + "lifepoints": "10", + "strength_level": "3", + "id": "1084", + "range_level": "1", + "attack_level": "3" + }, + { + "examine": "A citizen of Burthorpe.", + "melee_animation": "422", + "magic_level": "1", + "respawn_delay": "60", + "defence_animation": "425", + "death_animation": "836", + "name": "Ocga", + "defence_level": "3", + "safespot": null, + "lifepoints": "10", + "strength_level": "3", "id": "1085", "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" + "attack_level": "3" }, { "name": "Man", @@ -10982,46 +11221,57 @@ "attack_level": "1" }, { + "examine": "A citizen of Burthorpe.", + "melee_animation": "422", + "magic_level": "1", + "respawn_delay": "60", + "defence_animation": "425", "death_animation": "836", "name": "Penda", - "defence_level": "1", + "defence_level": "3", "safespot": null, "lifepoints": "10", - "melee_animation": "422", - "strength_level": "1", + "strength_level": "3", "id": "1087", "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" + "attack_level": "3" }, { + "examine": "A citizen of Burthorpe.", + "melee_animation": "422", + "magic_level": "1", + "respawn_delay": "60", + "defence_animation": "425", "death_animation": "836", "name": "Hygd", - "defence_level": "1", + "defence_level": "3", "safespot": null, "lifepoints": "10", - "melee_animation": "422", - "strength_level": "1", + "strength_level": "2", "id": "1088", "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" + "attack_level": "3" }, { + "examine": "A citizen of Burthorpe.", + "melee_animation": "422", + "magic_level": "1", + "respawn_delay": "60", + "defence_animation": "425", "death_animation": "836", "name": "Ceolburg", - "defence_level": "1", + "defence_level": "3", "safespot": null, "lifepoints": "10", - "melee_animation": "422", - "strength_level": "1", + "strength_level": "2", "id": "1089", "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" + "attack_level": "3" + }, + { + "examine": "A pretty young woman with an air of mystery around her.", + "name": "Hild", + "id": "1090" }, { "examine": "The knight seems to be watching something.", @@ -11045,7 +11295,6 @@ }, { "examine": "The biggest and baddest troll.", - "slayer_task": "83", "melee_animation": "6523", "range_animation": "0", "respawn_delay": "60", @@ -11053,57 +11302,78 @@ "weakness": "7", "magic_animation": "0", "name": "Rock", - "defence_level": "63", + "defence_level": "70", "safespot": null, - "lifepoints": "90", - "strength_level": "63", + "lifepoints": "140", + "strength_level": "110", "id": "1095", "aggressive": "true", + "bonuses": "60,60,60,0,0,35,60,35,200,200,0,100,0,0,0", "range_level": "1", - "attack_level": "63" + "attack_level": "70" }, { "examine": "Careful, he is a big.", - "slayer_task": "83", - "range_animation": "0", + "melee_animation": "284", + "range_animation": "284", "attack_speed": "7", "respawn_delay": "60", - "defence_animation": "0", + "defence_animation": "285", "weakness": "7", "magic_animation": "0", + "death_animation": "287", "name": "Stick", - "defence_level": "61", + "defence_level": "60", "safespot": null, - "lifepoints": "87", - "strength_level": "61", + "lifepoints": "135", + "strength_level": "110", "id": "1096", "aggressive": "true", + "bonuses": "50,50,50,0,0,30,30,50,200,200,0,80,0,0,0", "range_level": "1", - "attack_level": "61" + "attack_level": "60" }, { - "name": "Pee Hat", - "defence_level": "1", - "safespot": null, - "lifepoints": "126", - "strength_level": "1", - "id": "1097", - "range_level": "1", + "examine": "A nasty looking troll.", + "melee_animation": "284", + "range_animation": "284", + "attack_speed": "6", "respawn_delay": "60", - "attack_level": "1" - }, - { - "name": "Kraka", - "defence_level": "1", + "defence_animation": "285", + "weakness": "7", + "magic_animation": "284", + "death_animation": "287", + "name": "Pee Hat", + "defence_level": "50", "safespot": null, "lifepoints": "120", - "strength_level": "1", + "strength_level": "100", + "id": "1097", + "aggressive": "true", + "bonuses": "40,40,40,0,0,25,25,40,200,200,0,70,0,0,0", + "range_level": "1", + "attack_level": "50" + }, + { + "examine": "A nasty looking troll.", + "melee_animation": "284", + "range_animation": "284", "attack_speed": "6", + "respawn_delay": "60", + "defence_animation": "285", + "weakness": "7", + "magic_animation": "284", + "death_animation": "287", + "name": "Kraka", + "defence_level": "50", + "safespot": null, + "lifepoints": "120", + "strength_level": "100", "id": "1098", "aggressive": "true", + "bonuses": "40,40,40,0,0,25,25,40,200,400,0,70,0,0,0", "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" + "attack_level": "50" }, { "examine": "Human is his speciality.", @@ -11144,7 +11414,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", @@ -11166,7 +11435,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", @@ -11188,7 +11456,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", @@ -11210,7 +11477,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", @@ -11232,7 +11498,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", @@ -11253,7 +11518,6 @@ }, { "examine": "Small for a troll, but mean and ugly.", - "slayer_task": "83", "melee_animation": "284", "range_animation": "284", "attack_speed": "5", @@ -11272,7 +11536,6 @@ }, { "examine": "Small for a troll, but mean and ugly.", - "slayer_task": "83", "melee_animation": "284", "range_animation": "284", "attack_speed": "5", @@ -11291,7 +11554,6 @@ }, { "examine": "Small for a troll, but mean and ugly.", - "slayer_task": "83", "melee_animation": "284", "range_animation": "284", "attack_speed": "5", @@ -11310,7 +11572,6 @@ }, { "examine": "Small for a troll, but mean and ugly.", - "slayer_task": "83", "melee_animation": "284", "range_animation": "284", "attack_speed": "5", @@ -11329,7 +11590,6 @@ }, { "examine": "Small for a troll, but mean and ugly.", - "slayer_task": "83", "melee_animation": "284", "range_animation": "284", "attack_speed": "5", @@ -11348,7 +11608,6 @@ }, { "examine": "Small for a troll, but mean and ugly.", - "slayer_task": "83", "melee_animation": "284", "range_animation": "284", "attack_speed": "5", @@ -11367,7 +11626,6 @@ }, { "examine": "Small for a troll, but mean and ugly.", - "slayer_task": "83", "melee_animation": "284", "range_animation": "284", "attack_speed": "5", @@ -11386,13 +11644,14 @@ }, { "examine": "One of the troll generals.", - "slayer_task": "83", + "melee_animation": "1259", "range_animation": "0", "attack_speed": "5", "respawn_delay": "60", "defence_animation": "0", "weakness": "7", "magic_animation": "0", + "death_animation": "287", "name": "Troll general", "defence_level": "65", "safespot": null, @@ -11405,13 +11664,14 @@ }, { "examine": "One of the troll generals.", - "slayer_task": "83", + "melee_animation": "1259", "range_animation": "0", "attack_speed": "5", "respawn_delay": "60", "defence_animation": "0", "weakness": "7", "magic_animation": "0", + "death_animation": "287", "name": "Troll general", "defence_level": "65", "safespot": null, @@ -11424,13 +11684,14 @@ }, { "examine": "One of the troll generals.", - "slayer_task": "83", + "melee_animation": "1259", "range_animation": "0", "attack_speed": "5", "respawn_delay": "60", "defence_animation": "0", "weakness": "7", "magic_animation": "0", + "death_animation": "287", "name": "Troll general", "defence_level": "65", "safespot": null, @@ -11443,12 +11704,13 @@ }, { "examine": "He's watching the arena.", - "slayer_task": "83", + "melee_animation": "1259", "range_animation": "0", "respawn_delay": "60", "defence_animation": "0", "weakness": "7", "magic_animation": "0", + "death_animation": "287", "name": "Troll spectator", "defence_level": "47", "safespot": null, @@ -11460,12 +11722,13 @@ }, { "examine": "He's watching the arena.", - "slayer_task": "83", + "melee_animation": "1259", "range_animation": "0", "respawn_delay": "60", "defence_animation": "0", "weakness": "7", "magic_animation": "0", + "death_animation": "287", "name": "Troll spectator", "defence_level": "47", "safespot": null, @@ -11477,12 +11740,13 @@ }, { "examine": "He's watching the arena.", - "slayer_task": "83", + "melee_animation": "1259", "range_animation": "0", "respawn_delay": "60", "defence_animation": "0", "weakness": "7", "magic_animation": "0", + "death_animation": "287", "name": "Troll spectator", "defence_level": "47", "safespot": null, @@ -11494,12 +11758,13 @@ }, { "examine": "He's watching the arena.", - "slayer_task": "83", + "melee_animation": "1259", "range_animation": "0", "respawn_delay": "60", "defence_animation": "0", "weakness": "7", "magic_animation": "0", + "death_animation": "287", "name": "Troll spectator", "defence_level": "47", "safespot": null, @@ -11511,12 +11776,13 @@ }, { "examine": "He's watching the arena.", - "slayer_task": "83", + "melee_animation": "1259", "range_animation": "0", "respawn_delay": "60", "defence_animation": "0", "weakness": "7", "magic_animation": "0", + "death_animation": "287", "name": "Troll spectator", "defence_level": "47", "safespot": null, @@ -11528,12 +11794,13 @@ }, { "examine": "He's watching the arena.", - "slayer_task": "83", + "melee_animation": "1259", "range_animation": "0", "respawn_delay": "60", "defence_animation": "0", "weakness": "7", "magic_animation": "0", + "death_animation": "287", "name": "Troll spectator", "defence_level": "47", "safespot": null, @@ -11545,12 +11812,13 @@ }, { "examine": "He's watching the arena.", - "slayer_task": "83", + "melee_animation": "1259", "range_animation": "0", "respawn_delay": "60", "defence_animation": "0", "weakness": "7", "magic_animation": "0", + "death_animation": "287", "name": "Troll spectator", "defence_level": "47", "safespot": null, @@ -11562,92 +11830,98 @@ }, { "examine": "An unusually large troll.", - "slayer_task": "83", "melee_animation": "1158", "range_animation": "0", "respawn_delay": "60", "defence_animation": "0", "weakness": "7", "magic_animation": "0", + "death_animation": "287", "name": "Dad", - "defence_level": "55", + "defence_level": "50", "safespot": null, - "lifepoints": "157", - "strength_level": "55", + "lifepoints": "120", + "strength_level": "120", "id": "1125", + "bonuses": "40,40,40,0,0,25,25,40,200,200,0,70,0,0,0", "range_level": "1", - "attack_level": "55" + "attack_level": "60" }, { "examine": "He's guarding the cells.", - "slayer_task": "83", "range_animation": "0", + "melee_animation": "1158", + "respawn_delay": "60", "defence_animation": "0", "weakness": "7", "magic_animation": "0", + "death_animation": "287", "name": "Twig", - "defence_level": "50", + "defence_level": "25", "safespot": null, - "lifepoints": "71", - "strength_level": "50", + "lifepoints": "90", + "strength_level": "90", "id": "1126", + "aggressive": "true", + "bonuses": "20,20,20,0,0,0,0,10,200,200,0,20,0,0,0", "range_level": "1", - "attack_level": "50" + "attack_level": "40" }, { "examine": "He's guarding the cells.", - "slayer_task": "83", "range_animation": "0", + "melee_animation": "1158", "respawn_delay": "60", "defence_animation": "0", "weakness": "7", "magic_animation": "0", + "death_animation": "287", "name": "Berry", - "defence_level": "50", + "defence_level": "40", "safespot": null, - "lifepoints": "71", - "strength_level": "50", + "lifepoints": "90", + "strength_level": "90", "id": "1127", + "aggressive": "true", + "bonuses": "20,20,20,0,0,0,0,10,200,200,0,20,0,0,0", "range_level": "1", - "attack_level": "50" + "attack_level": "40" }, { "examine": "He's guarding the cells.", - "slayer_task": "83", - "melee_animation": "5821", "range_animation": "0", "defence_animation": "0", "weakness": "7", "magic_animation": "0", "name": "Twig", - "defence_level": "50", + "defence_level": "25", "safespot": null, - "lifepoints": "71", - "strength_level": "50", + "lifepoints": "90", + "strength_level": "90", "id": "1128", + "bonuses": "20,20,20,0,0,0,0,10,200,200,0,20,0,0,0", "range_level": "1", - "attack_level": "50" + "attack_level": "40" }, { "examine": "He's guarding the cells.", - "slayer_task": "83", "range_animation": "0", "respawn_delay": "60", "defence_animation": "0", "weakness": "7", "magic_animation": "0", "name": "Berry", - "defence_level": "50", + "defence_level": "40", "safespot": null, - "lifepoints": "71", - "strength_level": "50", + "lifepoints": "90", + "strength_level": "90", "id": "1129", + "bonuses": "20,20,20,0,0,0,0,10,200,200,0,20,0,0,0", "range_level": "1", - "attack_level": "50" + "attack_level": "40" }, { "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", @@ -11673,7 +11947,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", @@ -11699,7 +11972,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", @@ -11725,7 +11997,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", @@ -11751,7 +12022,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", @@ -11824,7 +12094,6 @@ }, { "examine": "Small for a troll, but mean and ugly.", - "slayer_task": "83", "melee_animation": "284", "range_animation": "284", "attack_speed": "5", @@ -11843,7 +12112,6 @@ }, { "examine": "He's fast asleep.", - "slayer_task": "83", "melee_animation": "0", "range_animation": "0", "defence_animation": "0", @@ -11988,6 +12256,7 @@ "attack_level": "1" }, { + "death_animation": "", "name": "Guard", "defence_level": "1", "safespot": null, @@ -12051,7 +12320,6 @@ }, { "examine": "I don't think insect repellent will work...", - "slayer_task": "53", "melee_animation": "6223", "range_animation": "6223", "combat_audio": "571,573,572", @@ -12075,34 +12343,32 @@ }, { "examine": "I don't think insect repellent will work...", - "slayer_task": "53", "melee_animation": "6224", + "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": "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", - "magic_level": "1", - "defence_animation": "6227", "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", @@ -12127,7 +12393,6 @@ }, { "examine": "I don't think insect repellent will work...", - "slayer_task": "53", "melee_animation": "6223", "range_animation": "6223", "combat_audio": "571,573,572", @@ -12151,7 +12416,6 @@ }, { "examine": "I don't think insect repellent will work...", - "slayer_task": "53", "melee_animation": "6223", "range_animation": "6223", "attack_speed": "4", @@ -12177,13 +12441,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", "magic_level": "150", - "respawn_delay": "45", + "respawn_delay": "0", "defence_animation": "6232", "weakness": "10", "magic_animation": "6241", @@ -12202,12 +12465,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", @@ -12225,7 +12487,6 @@ }, { "examine": "I don't think insect repellent will work...", - "slayer_task": "53", "melee_animation": "0", "range_animation": "0", "defence_animation": "0", @@ -12242,15 +12503,15 @@ "attack_level": "1" }, { - "examine": "A huge beast", - "melee_animation": "313", - "range_animation": "0", + "examine": "A huge beast, resembling in some ways a lion, but mostly a twisted nightmare.", + "melee_animation": "1199", + "range_animation": "1197", "attack_speed": "5", "respawn_delay": "60", - "defence_animation": "0", + "defence_animation": "1200", "weakness": "9", - "magic_animation": "0", - "death_animation": "315", + "magic_animation": "1197", + "death_animation": "1201", "name": "The Shaikahan", "defence_level": "50", "safespot": null, @@ -12263,7 +12524,6 @@ }, { "examine": "He looks pretty handy with that bow.", - "slayer_task": "31", "start_gfx": "250", "combat_style": "1", "melee_animation": "428", @@ -12287,7 +12547,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", @@ -12391,7 +12650,6 @@ }, { "examine": "Eek! A bear cub!", - "slayer_task": "6", "range_animation": "0", "combat_audio": "498,500,499", "respawn_delay": "60", @@ -12421,7 +12679,6 @@ }, { "examine": "What big teeth you have.", - "slayer_task": "92", "melee_animation": "6559", "range_animation": "0", "defence_animation": "0", @@ -12591,7 +12848,6 @@ }, { "examine": "A scary, man eating ghoul.", - "slayer_task": "37", "melee_animation": "422", "range_animation": "422", "combat_audio": "442,444,443", @@ -12634,8 +12890,8 @@ "attack_level": "42" }, { - "examine": "Could his name be 'Lurch'?", - "melee_animation": "7183", + "examine": "It looks very hungry!", + "melee_animation": "1264", "slayer_exp": "50", "death_animation": "7185", "name": "Vampire", @@ -12649,6 +12905,8 @@ "attack_level": "52" }, { + "examine": "It looks very hungry!", + "melee_animation": "1264", "slayer_exp": "40", "name": "Vampire", "defence_level": "1", @@ -12661,6 +12919,7 @@ "attack_level": "1" }, { + "examine": "It looks very hungry!", "name": "Vampire", "defence_level": "1", "safespot": null, @@ -12907,7 +13166,6 @@ }, { "examine": "A shadowy sort of entity", - "slayer_task": "73", "melee_animation": "0", "range_animation": "0", "magic_level": "1", @@ -12928,7 +13186,6 @@ }, { "examine": "The shadowy remains of a long departed soul.", - "slayer_task": "73", "melee_animation": "1283", "range_animation": "0", "magic_level": "1", @@ -12949,7 +13206,6 @@ }, { "examine": "A shadowy sort of entity", - "slayer_task": "73", "melee_animation": "0", "range_animation": "0", "magic_level": "1", @@ -12970,7 +13226,6 @@ }, { "examine": "The shadowy remains of a long departed soul.", - "slayer_task": "73", "melee_animation": "1283", "range_animation": "0", "magic_level": "1", @@ -12991,7 +13246,6 @@ }, { "examine": "A shadowy sort of entity", - "slayer_task": "73", "melee_animation": "0", "range_animation": "0", "magic_level": "1", @@ -13012,7 +13266,6 @@ }, { "examine": "The shadowy remains of a long departed soul.", - "slayer_task": "73", "melee_animation": "1283", "range_animation": "0", "magic_level": "1", @@ -13033,7 +13286,6 @@ }, { "examine": "A shadowy sort of entity", - "slayer_task": "73", "melee_animation": "0", "range_animation": "0", "magic_level": "1", @@ -13054,7 +13306,6 @@ }, { "examine": "The shadowy remains of a long departed soul.", - "slayer_task": "73", "melee_animation": "1283", "range_animation": "0", "magic_level": "1", @@ -13075,7 +13326,6 @@ }, { "examine": "A shadowy sort of entity", - "slayer_task": "73", "melee_animation": "0", "range_animation": "0", "magic_level": "1", @@ -13096,7 +13346,6 @@ }, { "examine": "The shadowy remains of a long departed soul.", - "slayer_task": "73", "melee_animation": "1283", "range_animation": "0", "magic_level": "1", @@ -13946,6 +14195,7 @@ "examine": "Keeps the stalls secure.", "melee_animation": "390", "range_animation": "0", + "combat_audio": "", "defence_animation": "0", "weakness": "9", "magic_animation": "0", @@ -14027,7 +14277,6 @@ }, { "examine": "Cute. But deadly.", - "slayer_task": "6", "range_animation": "0", "combat_audio": "498,500,499", "respawn_delay": "60", @@ -14102,7 +14351,6 @@ }, { "examine": "Fearsome predator found only in the Fremennik Province", - "slayer_task": "92", "melee_animation": "6559", "range_animation": "0", "combat_audio": "481,491,490", @@ -14281,7 +14529,6 @@ }, { "examine": "A horror from the ocean depths.", - "slayer_task": "24", "melee_animation": "1341", "range_animation": "0", "magic_level": "60", @@ -14380,6 +14627,16 @@ "range_level": "1", "attack_level": "1" }, + { + "examine": "Pretty barmaid.", + "name": "Sam", + "id": "1357" + }, + { + "examine": "Pretty barmaid.", + "name": "Rachael", + "id": "1358" + }, { "examine": "He seems happy to see you.", "melee_animation": "0", @@ -14974,7 +15231,6 @@ }, { "examine": "A scimitar wielding ninja monkey.", - "slayer_task": "61", "melee_animation": "1392", "range_animation": "0", "combat_audio": "629,631,630", @@ -14993,7 +15249,6 @@ }, { "examine": "A bow wielding ninja monkey.", - "slayer_task": "61", "combat_style": "1", "melee_animation": "0", "range_animation": "1394", @@ -15049,7 +15304,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", @@ -15069,7 +15323,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", @@ -15091,7 +15344,6 @@ }, { "examine": "A huge brutish gorilla stands here", - "slayer_task": "61", "melee_animation": "0", "range_animation": "0", "defence_animation": "0", @@ -15131,7 +15383,6 @@ }, { "examine": "A large and lumbering undead monkey.", - "slayer_task": "61", "melee_animation": "1383", "range_animation": "0", "combat_audio": "629,631,630", @@ -15247,7 +15498,6 @@ }, { "examine": "It's a brightly coloured bird of the jungle.", - "slayer_task": "7", "melee_animation": "6775", "range_animation": "0", "defence_animation": "0", @@ -15265,7 +15515,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", @@ -15295,7 +15544,6 @@ }, { "examine": "A very dangerous looking spider", - "slayer_task": "76", "melee_animation": "5327", "range_animation": "0", "combat_audio": "537,539,538", @@ -15570,7 +15818,6 @@ }, { "examine": "Well", - "slayer_task": "75", "melee_animation": "0", "range_animation": "0", "defence_animation": "0", @@ -15589,7 +15836,6 @@ }, { "examine": "I don't think the pickaxe is for hitting rocks.", - "slayer_task": "75", "melee_animation": "5485", "range_animation": "0", "respawn_delay": "60", @@ -15655,7 +15901,6 @@ }, { "examine": "A large boisterous bird, a delicacy for ogres.", - "slayer_task": "7", "melee_animation": "6761", "range_animation": "0", "combat_audio": "1449,1451,1450", @@ -15723,7 +15968,6 @@ }, { "examine": "Not man's best friend.", - "slayer_task": "92", "melee_animation": "6559", "range_animation": "0", "respawn_delay": "60", @@ -15743,12 +15987,13 @@ }, { "examine": "Brrrrr...he must be cold!", - "slayer_task": "83", + "melee_animation": "284", "range_animation": "0", "respawn_delay": "60", - "defence_animation": "0", + "defence_animation": "285", "weakness": "9", "magic_animation": "0", + "death_animation": "287", "name": "Ice Troll", "defence_level": "60", "safespot": null, @@ -15761,11 +16006,13 @@ }, { "examine": "Brrrrr...he must be cold!", - "slayer_task": "83", + "melee_animation": "284", "range_animation": "0", - "defence_animation": "0", + "respawn_delay": "60", + "defence_animation": "285", "weakness": "9", "magic_animation": "0", + "death_animation": "287", "name": "Ice Troll", "defence_level": "60", "safespot": null, @@ -15777,12 +16024,13 @@ }, { "examine": "Brrrrr...he must be cold!", - "slayer_task": "83", + "melee_animation": "284", "range_animation": "0", "respawn_delay": "60", - "defence_animation": "0", + "defence_animation": "285", "weakness": "9", "magic_animation": "0", + "death_animation": "287", "name": "Ice Troll", "defence_level": "60", "safespot": null, @@ -15795,11 +16043,13 @@ }, { "examine": "Brrrrr...he must be cold!", - "slayer_task": "83", + "melee_animation": "284", "range_animation": "0", - "defence_animation": "0", + "respawn_delay": "60", + "defence_animation": "285", "weakness": "9", "magic_animation": "0", + "death_animation": "287", "name": "Ice Troll", "defence_level": "60", "safespot": null, @@ -15811,12 +16061,13 @@ }, { "examine": "Brrrrr...he must be cold!", - "slayer_task": "83", + "melee_animation": "284", "range_animation": "0", "respawn_delay": "60", - "defence_animation": "0", + "defence_animation": "285", "weakness": "9", "magic_animation": "0", + "death_animation": "287", "name": "Ice Troll", "defence_level": "60", "safespot": null, @@ -15829,12 +16080,13 @@ }, { "examine": "Brrrrr...he must be cold!", - "slayer_task": "83", + "melee_animation": "284", "range_animation": "0", "respawn_delay": "60", - "defence_animation": "0", + "defence_animation": "285", "weakness": "9", "magic_animation": "0", + "death_animation": "287", "name": "Ice Troll", "defence_level": "60", "safespot": null, @@ -15847,11 +16099,13 @@ }, { "examine": "Brrrrr...he must be cold!", - "slayer_task": "83", + "melee_animation": "284", "range_animation": "0", - "defence_animation": "0", + "respawn_delay": "60", + "defence_animation": "285", "weakness": "9", "magic_animation": "0", + "death_animation": "287", "name": "Ice Troll", "defence_level": "60", "safespot": null, @@ -15879,7 +16133,6 @@ }, { "examine": "A very large elemental adversary.", - "slayer_task": "33", "melee_animation": "4666", "range_animation": "4666", "combat_audio": "447,451,450", @@ -15904,7 +16157,6 @@ }, { "examine": "A very large elemental adversary.", - "slayer_task": "33", "melee_animation": "4666", "range_animation": "4666", "combat_audio": "447,451,450", @@ -15929,7 +16181,6 @@ }, { "examine": "A very large elemental adversary.", - "slayer_task": "33", "melee_animation": "4666", "range_animation": "4666", "combat_audio": "447,451,450", @@ -15954,7 +16205,6 @@ }, { "examine": "A very large elemental adversary.", - "slayer_task": "33", "melee_animation": "4666", "range_animation": "4666", "combat_audio": "447,451,450", @@ -15979,7 +16229,6 @@ }, { "examine": "A very large elemental adversary.", - "slayer_task": "33", "melee_animation": "4666", "range_animation": "4666", "combat_audio": "447,451,450", @@ -16004,7 +16253,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", @@ -16029,7 +16277,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", @@ -16054,7 +16301,6 @@ }, { "examine": "Young but still dangerous.", - "slayer_task": "68", "melee_animation": "25", "range_animation": "25", "combat_audio": "405,407,406", @@ -16078,7 +16324,6 @@ }, { "examine": "Its scales seem to be made of bronze.", - "slayer_task": "13", "melee_animation": "80", "range_animation": "80", "combat_audio": "408,410,409", @@ -16104,7 +16349,6 @@ }, { "examine": "Its scales seem to be made of iron.", - "slayer_task": "50", "melee_animation": "80", "range_animation": "80", "combat_audio": "408,410,409", @@ -16130,7 +16374,6 @@ }, { "examine": "Its scales seem to be made of steel.", - "slayer_task": "80", "melee_animation": "80", "range_animation": "80", "combat_audio": "408,410,409", @@ -16156,7 +16399,6 @@ }, { "examine": "An unsuitable pet.", - "slayer_task": "27", "melee_animation": "6562", "range_animation": "6562", "magic_level": "20", @@ -16196,9 +16438,13 @@ "range_level": "1", "attack_level": "25" }, + { + "examine": "She looks dangerous!", + "name": "Chaeldar", + "id": "1598" + }, { "examine": "A spiky crawling critter. ", - "slayer_task": "16", "melee_animation": "266", "range_animation": "266", "combat_audio": "341,343,342", @@ -16288,7 +16534,6 @@ }, { "examine": "A very smelly ghost.", - "slayer_task": "0", "start_gfx": "334", "combat_style": "2", "melee_animation": "9466", @@ -16317,7 +16562,6 @@ }, { "examine": "A very smelly ghost.", - "slayer_task": "0", "start_gfx": "334", "combat_style": "2", "melee_animation": "9466", @@ -16346,7 +16590,6 @@ }, { "examine": "A very smelly ghost.", - "slayer_task": "0", "start_gfx": "334", "combat_style": "2", "melee_animation": "9466", @@ -16375,7 +16618,6 @@ }, { "examine": "A very smelly ghost.", - "slayer_task": "0", "start_gfx": "334", "combat_style": "2", "melee_animation": "9466", @@ -16473,7 +16715,6 @@ }, { "examine": "Flies like a rock.", - "slayer_task": "84", "melee_animation": "1516", "range_animation": "1516", "attack_speed": "5", @@ -16561,7 +16802,6 @@ }, { "examine": "A denizen of the Abyss!", - "slayer_task": "1", "melee_animation": "1537", "range_animation": "1537", "combat_audio": "276,278,277", @@ -16675,7 +16915,6 @@ }, { "examine": "The winged reptile.", - "slayer_task": "19", "melee_animation": "7762", "range_animation": "7762", "combat_audio": "363,365,364", @@ -16763,7 +17002,6 @@ }, { "examine": "The vacuumed face of evil.", - "slayer_task": "28", "melee_animation": "1557", "range_animation": "1557", "combat_audio": "414,416,415", @@ -16784,7 +17022,6 @@ }, { "examine": "The cave-dwelling cousin of the dust devils.", - "slayer_task": "28", "melee_animation": "1557", "range_animation": "0", "defence_animation": "0", @@ -16913,7 +17150,6 @@ { "spawn_animation": "8081", "examine": "A small fire demon.", - "slayer_task": "66", "melee_animation": "8080", "combat_audio": "696,698,697", "attack_speed": "5", @@ -16937,7 +17173,6 @@ { "spawn_animation": "8081", "examine": "A small fire demon.", - "slayer_task": "66", "melee_animation": "8080", "combat_audio": "696,698,697", "attack_speed": "5", @@ -16961,7 +17196,6 @@ { "spawn_animation": "8081", "examine": "A small fire demon.", - "slayer_task": "66", "melee_animation": "8080", "combat_audio": "696,698,697", "attack_speed": "5", @@ -16985,7 +17219,6 @@ { "spawn_animation": "8081", "examine": "A small fire demon.", - "slayer_task": "66", "melee_animation": "8080", "combat_audio": "696,698,697", "attack_speed": "5", @@ -17008,7 +17241,6 @@ }, { "examine": "It's a Jelly.", - "slayer_task": "51", "melee_animation": "8569", "range_animation": "8569", "combat_audio": "547,550,549", @@ -17031,7 +17263,6 @@ }, { "examine": "Doesn't look so tough...", - "slayer_task": "51", "melee_animation": "8569", "range_animation": "0", "combat_audio": "547,550,549", @@ -17054,7 +17285,6 @@ }, { "examine": "Wibbly.", - "slayer_task": "51", "melee_animation": "8569", "range_animation": "0", "combat_audio": "547,550,549", @@ -17077,7 +17307,6 @@ }, { "examine": "There's always room for jelly.", - "slayer_task": "51", "melee_animation": "8569", "range_animation": "0", "combat_audio": "547,550,549", @@ -17134,7 +17363,6 @@ }, { "examine": "An evil magic user.", - "slayer_task": "49", "start_gfx": "99", "combat_style": "2", "melee_animation": "811", @@ -17158,7 +17386,6 @@ "attack_level": "1" }, { - "slayer_task": "49", "start_gfx": "99", "combat_style": "2", "melee_animation": "811", @@ -17183,7 +17410,6 @@ }, { "examine": "An evil magic user.", - "slayer_task": "49", "start_gfx": "99", "combat_style": "2", "melee_animation": "811", @@ -17208,7 +17434,6 @@ }, { "examine": "An evil magic user.", - "slayer_task": "49", "start_gfx": "99", "combat_style": "2", "melee_animation": "811", @@ -17233,7 +17458,6 @@ }, { "examine": "An evil magic user.", - "slayer_task": "49", "start_gfx": "99", "combat_style": "2", "melee_animation": "811", @@ -17258,7 +17482,6 @@ }, { "examine": "Now THAT'S handy.", - "slayer_task": "21", "melee_animation": "9125", "range_animation": "9125", "combat_audio": "377,379,378", @@ -17281,7 +17504,6 @@ }, { "examine": "Gimmie five. Actually", - "slayer_task": "21", "melee_animation": "9125", "range_animation": "0", "combat_audio": "377,379,378", @@ -17303,7 +17525,6 @@ }, { "examine": "Gimmie five. Actually", - "slayer_task": "21", "melee_animation": "9125", "range_animation": "0", "combat_audio": "377,379,378", @@ -17324,7 +17545,6 @@ }, { "examine": "Gimmie five. Actually", - "slayer_task": "21", "melee_animation": "9125", "range_animation": "0", "combat_audio": "377,379,378", @@ -17346,7 +17566,6 @@ }, { "examine": "Gimmie five. Actually", - "slayer_task": "21", "melee_animation": "9125", "range_animation": "0", "combat_audio": "377,379,378", @@ -17368,7 +17587,6 @@ }, { "examine": "Now THAT's handy.", - "slayer_task": "21", "melee_animation": "9444", "range_animation": "0", "combat_audio": "377,379,378", @@ -17390,7 +17608,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", @@ -17412,7 +17629,6 @@ }, { "examine": "A big severed hand.", - "slayer_task": "21", "melee_animation": "9444", "range_animation": "0", "combat_audio": "377,379,378", @@ -17433,7 +17649,6 @@ }, { "examine": "Give the guy a big hand.....", - "slayer_task": "21", "melee_animation": "9444", "range_animation": "0", "combat_audio": "377,379,378", @@ -17455,7 +17670,6 @@ }, { "examine": "A big severed hand.", - "slayer_task": "21", "melee_animation": "9444", "range_animation": "0", "combat_audio": "377,379,378", @@ -17644,7 +17858,6 @@ }, { "examine": "It's an undead cow.", - "slayer_task": "20", "melee_animation": "5849", "range_animation": "0", "attack_speed": "4", @@ -17667,7 +17880,6 @@ }, { "examine": "Yup, definitely a chicken... an undead chicken.", - "slayer_task": "7", "melee_animation": "5387", "range_animation": "0", "combat_audio": "355,357,356", @@ -17691,7 +17903,6 @@ }, { "examine": "An extremely vicious lobster.", - "slayer_task": "71", "melee_animation": "6265", "range_animation": "0", "defence_animation": "0", @@ -17758,7 +17969,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", @@ -18267,7 +18477,6 @@ }, { "examine": "These gnomes know how to get around!", - "slayer_task": "7", "melee_animation": "6790", "range_animation": "0", "respawn_delay": "60", @@ -18286,7 +18495,6 @@ }, { "examine": "A farmer's enemy.", - "slayer_task": "7", "melee_animation": "0", "range_animation": "0", "defence_animation": "0", @@ -18424,7 +18632,6 @@ }, { "examine": "Where beef comes from.", - "slayer_task": "20", "melee_animation": "5849", "range_animation": "5849", "combat_audio": "369,371,370", @@ -18464,7 +18671,6 @@ }, { "examine": "An ugly green creature.", - "slayer_task": "38", "melee_animation": "6185", "range_animation": "6185", "combat_audio": "469,472,471", @@ -18486,7 +18692,6 @@ }, { "examine": "An ugly green creature.", - "slayer_task": "38", "melee_animation": "6185", "range_animation": "6185", "combat_audio": "469,472,471", @@ -18508,7 +18713,6 @@ }, { "examine": "An ugly green creature.", - "slayer_task": "38", "melee_animation": "6185", "range_animation": "6185", "combat_audio": "469,472,471", @@ -18530,7 +18734,6 @@ }, { "examine": "An ugly green creature.", - "slayer_task": "38", "melee_animation": "6185", "range_animation": "6185", "combat_audio": "469,472,471", @@ -18552,7 +18755,6 @@ }, { "examine": "An ugly green creature.", - "slayer_task": "38", "melee_animation": "6185", "range_animation": "6185", "combat_audio": "469,472,471", @@ -18574,7 +18776,6 @@ }, { "examine": "An ugly green creature.", - "slayer_task": "38", "melee_animation": "6185", "range_animation": "6185", "combat_audio": "469,472,471", @@ -18596,7 +18797,6 @@ }, { "examine": "An ugly green creature.", - "slayer_task": "38", "melee_animation": "6185", "range_animation": "6185", "combat_audio": "469,472,471", @@ -18618,7 +18818,6 @@ }, { "examine": "An ugly green creature.", - "slayer_task": "38", "melee_animation": "6185", "range_animation": "6185", "combat_audio": "469,472,471", @@ -18657,7 +18856,6 @@ }, { "examine": "A short stout menacing fellow.", - "slayer_task": "29", "melee_animation": "99", "range_animation": "0", "attack_speed": "6", @@ -18677,7 +18875,6 @@ }, { "examine": "A short stout menacing fellow.", - "slayer_task": "29", "melee_animation": "99", "range_animation": "0", "attack_speed": "6", @@ -18697,7 +18894,6 @@ }, { "examine": "A short stout menacing fellow.", - "slayer_task": "29", "melee_animation": "99", "range_animation": "0", "attack_speed": "6", @@ -18956,7 +19152,6 @@ }, { "examine": "A goblin with big bulging eyes.", - "slayer_task": "38", "melee_animation": "6001", "range_animation": "0", "combat_audio": "469,472,471", @@ -18976,7 +19171,6 @@ }, { "examine": "A goblin with big bulging eyes.", - "slayer_task": "38", "melee_animation": "6001", "range_animation": "0", "combat_audio": "469,472,471", @@ -18996,7 +19190,6 @@ }, { "examine": "A goblin with big bulging eyes.", - "slayer_task": "38", "melee_animation": "6001", "range_animation": "0", "combat_audio": "469,472,471", @@ -19016,7 +19209,6 @@ }, { "examine": "A goblin with big bulging eyes.", - "slayer_task": "38", "melee_animation": "6001", "range_animation": "0", "combat_audio": "469,472,471", @@ -19034,6 +19226,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", @@ -19096,37 +19303,36 @@ }, { "examine": "A foul-smelling blob of protoplasm.", - "slayer_task": "18", "melee_animation": "1789", + "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": "3", "magic_animation": "0", "death_animation": "1792", - "lifepoints": "25", - "id": "1831", - "aggressive": "false", - "range_animation": "0", - "combat_audio": "784,786,785", - "magic_level": "13", - "defence_animation": "0", "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": "5", + "attack_speed": "4", + "magic_level": "1", "respawn_delay": "60", "defence_animation": "0", "weakness": "2", @@ -19136,11 +19342,12 @@ "name": "Cave bug", "defence_level": "6", "safespot": null, - "lifepoints": "8", - "strength_level": "1", + "lifepoints": "5", + "strength_level": "5", "id": "1832", - "range_level": "6", - "attack_level": "1" + "bonuses": "0,0,0,0,0,10,10,5,5,10,0,0,0,0,0", + "range_level": "1", + "attack_level": "6" }, { "examine": "A little", @@ -19238,6 +19445,22 @@ "range_level": "1", "attack_level": "1" }, + { + "examine": "He seems to be in charge of the ship on the river.", + "melee_animation": "0", + "range_animation": "0", + "defence_animation": "0", + "magic_animation": "0", + "death_animation": "0", + "name": "Dwarven Boatman", + "defence_level": "1", + "safespot": null, + "lifepoints": "10", + "strength_level": "1", + "id": "1846", + "range_level": "1", + "attack_level": "1" + }, { "examine": "It is the avatar of the Arzinian Being of Bordanzan", "melee_animation": "1840", @@ -19635,6 +19858,7 @@ "melee_animation": "0", "range_animation": "0", "combat_audio": "703,705,704", + "attack_speed": "4", "defence_animation": "0", "magic_animation": "0", "death_animation": "0", @@ -19644,8 +19868,9 @@ "lifepoints": "10", "strength_level": "1", "id": "1902", + "bonuses": "0,0,0,0,0,10,10,5,5,10,0,0,0,0,0", "range_level": "1", - "attack_level": "1" + "attack_level": "6" }, { "examine": "Menaphite thug.", @@ -19726,49 +19951,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" }, @@ -19822,6 +20068,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", @@ -19892,12 +20154,13 @@ }, { "examine": "Brrrrr...he must be cold!", - "slayer_task": "83", + "melee_animation": "284", "range_animation": "0", "respawn_delay": "60", - "defence_animation": "0", + "defence_animation": "285", "weakness": "9", "magic_animation": "0", + "death_animation": "287", "name": "Ice troll", "defence_level": "64", "safespot": null, @@ -19910,11 +20173,13 @@ }, { "examine": "Brrrrr...he must be cold!", - "slayer_task": "83", + "melee_animation": "284", "range_animation": "0", - "defence_animation": "0", + "respawn_delay": "60", + "defence_animation": "285", "weakness": "9", "magic_animation": "0", + "death_animation": "287", "name": "Ice troll", "defence_level": "64", "safespot": null, @@ -19926,12 +20191,13 @@ }, { "examine": "Brrrrr...he must be cold!", - "slayer_task": "83", + "melee_animation": "284", "range_animation": "0", "respawn_delay": "60", - "defence_animation": "0", + "defence_animation": "285", "weakness": "9", "magic_animation": "0", + "death_animation": "287", "name": "Ice troll", "defence_level": "64", "safespot": null, @@ -19944,11 +20210,13 @@ }, { "examine": "Brrrrr...he must be cold!", - "slayer_task": "83", + "melee_animation": "284", "range_animation": "0", - "defence_animation": "0", + "respawn_delay": "60", + "defence_animation": "285", "weakness": "9", "magic_animation": "0", + "death_animation": "287", "name": "Ice troll", "defence_level": "64", "safespot": null, @@ -19960,12 +20228,13 @@ }, { "examine": "Brrrrr...he must be cold!", - "slayer_task": "83", + "melee_animation": "284", "range_animation": "0", "respawn_delay": "60", - "defence_animation": "0", + "defence_animation": "285", "weakness": "9", "magic_animation": "0", + "death_animation": "287", "name": "Ice troll", "defence_level": "64", "safespot": null, @@ -19978,12 +20247,13 @@ }, { "examine": "Brrrrr...he must be cold!", - "slayer_task": "83", + "melee_animation": "284", "range_animation": "0", "respawn_delay": "60", - "defence_animation": "0", + "defence_animation": "285", "weakness": "9", "magic_animation": "0", + "death_animation": "287", "name": "Ice troll", "defence_level": "64", "safespot": null, @@ -19996,11 +20266,13 @@ }, { "examine": "Brrrrr...he must be cold!", - "slayer_task": "83", + "melee_animation": "284", "range_animation": "0", - "defence_animation": "0", + "respawn_delay": "60", + "defence_animation": "285", "weakness": "9", "magic_animation": "0", + "death_animation": "287", "name": "Ice troll", "defence_level": "64", "safespot": null, @@ -20010,6 +20282,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", @@ -20156,17 +20520,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", @@ -20174,14 +20538,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.", @@ -20192,14 +20557,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?", @@ -20210,13 +20576,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.", @@ -20254,22 +20621,22 @@ }, { "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", "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.", @@ -20284,7 +20651,6 @@ }, { "examine": "A giant skeleton.", - "slayer_task": "75", "melee_animation": "5499", "range_animation": "0", "defence_animation": "0", @@ -20300,9 +20666,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", @@ -20320,19 +20724,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.", @@ -20454,7 +20861,6 @@ }, { "examine": "He has had his day.", - "slayer_task": "27", "melee_animation": "6568", "range_animation": "0", "combat_audio": "544,546,545", @@ -20517,7 +20923,6 @@ }, { "examine": "I might give the burgers a miss in this town.", - "slayer_task": "20", "melee_animation": "0", "range_animation": "0", "defence_animation": "0", @@ -20535,7 +20940,6 @@ }, { "examine": "I don't fancy eating any part of this.", - "slayer_task": "20", "melee_animation": "0", "range_animation": "0", "defence_animation": "0", @@ -20553,7 +20957,6 @@ }, { "examine": "I think they're some kind of beetle.", - "slayer_task": "70", "melee_animation": "1948", "range_animation": "0", "attack_speed": "2", @@ -20867,7 +21270,6 @@ }, { "examine": "A nasty little rodent.", - "slayer_task": "67", "melee_animation": "240", "range_animation": "240", "combat_audio": "703,705,704", @@ -20891,7 +21293,6 @@ }, { "examine": "A nasty overgrown rodent.", - "slayer_task": "67", "melee_animation": "4933", "range_animation": "4933", "combat_audio": "703,705,704", @@ -20915,7 +21316,6 @@ }, { "examine": "Incey wincey.", - "slayer_task": "76", "melee_animation": "6249", "range_animation": "6249", "combat_audio": "537,539,538", @@ -20939,7 +21339,6 @@ }, { "examine": "Not very incey wincey.", - "slayer_task": "76", "melee_animation": "5327", "range_animation": "5327", "combat_audio": "537,539,538", @@ -20962,177 +21361,319 @@ "attack_level": "65" }, { + "examine": "A partially decomposing zombie ogre.", + "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": "Zogre", - "defence_level": "1", + "defence_level": "35", "safespot": null, "lifepoints": "71", - "combat_audio": "", - "strength_level": "1", + "strength_level": "36", "id": "2044", "aggressive": "true", "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" + "attack_level": "20" }, { + "examine": "A partially decomposing zombie ogre.", + "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": "Zogre", - "defence_level": "1", + "defence_level": "35", "safespot": null, "lifepoints": "71", - "strength_level": "1", + "strength_level": "36", "id": "2045", "aggressive": "true", "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" + "attack_level": "20" }, { + "examine": "A partially decomposing zombie ogre.", + "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": "Zogre", - "defence_level": "1", + "defence_level": "35", "safespot": null, "lifepoints": "71", - "strength_level": "1", + "strength_level": "36", "id": "2046", "aggressive": "true", "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" + "attack_level": "20" }, { + "examine": "A partially decomposing zombie ogre.", + "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": "Zogre", - "defence_level": "1", + "defence_level": "35", "safespot": null, "lifepoints": "71", - "strength_level": "1", + "strength_level": "36", "id": "2047", "aggressive": "true", "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" + "attack_level": "20" }, { + "examine": "A partially decomposing zombie ogre.", + "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": "Zogre", - "defence_level": "1", + "defence_level": "35", "safespot": null, "lifepoints": "71", - "strength_level": "1", + "strength_level": "36", "id": "2048", "aggressive": "true", "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" + "attack_level": "20" }, { + "examine": "A partially decomposing zombie ogre.", + "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": "Zogre", - "defence_level": "1", + "defence_level": "35", "safespot": null, "lifepoints": "71", - "strength_level": "1", + "strength_level": "36", "id": "2049", "aggressive": "true", "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" + "attack_level": "20" }, { + "examine": "It's falling apart!", + "combat_audio": "", + "range_animation": "359", + "melee_animation": "359", + "attack_speed": "6", + "magic_level": "1", + "respawn_delay": "60", + "defence_animation": "360", + "weakness": "7", + "slayer_exp": "", + "magic_animation": "359", + "death_animation": "361", "name": "Skogre", - "defence_level": "1", + "defence_level": "35", "safespot": null, "lifepoints": "71", - "strength_level": "1", + "strength_level": "36", "id": "2050", "aggressive": "true", "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" + "attack_level": "20" }, { + "examine": "A partially decomposing zombie ogre.", + "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": "Zogre", - "defence_level": "1", + "defence_level": "35", "safespot": null, "lifepoints": "71", - "strength_level": "1", + "strength_level": "36", "id": "2051", "aggressive": "true", "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" + "attack_level": "20" }, { + "examine": "A partially decomposing zombie ogre.", + "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": "Zogre", - "defence_level": "1", + "defence_level": "35", "safespot": null, "lifepoints": "71", - "strength_level": "1", + "strength_level": "36", "id": "2052", "aggressive": "true", "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" + "attack_level": "20" }, { + "examine": "A partially decomposing zombie ogre.", + "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": "Zogre", - "defence_level": "1", + "defence_level": "35", "safespot": null, "lifepoints": "71", - "strength_level": "1", + "strength_level": "36", "id": "2053", "aggressive": "true", "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" + "attack_level": "20" }, { + "examine": "A partially decomposing zombie ogre.", + "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": "Zogre", - "defence_level": "1", + "defence_level": "35", "safespot": null, "lifepoints": "71", - "strength_level": "1", + "strength_level": "36", "id": "2054", "aggressive": "true", "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" + "attack_level": "20" }, { + "examine": "A partially decomposing zombie ogre.", + "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": "Zogre", - "defence_level": "1", + "defence_level": "35", "safespot": null, "lifepoints": "71", - "strength_level": "1", + "strength_level": "36", "id": "2055", "aggressive": "true", "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" + "attack_level": "20" }, { + "examine": "It's falling apart!", + "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": "Skogre", - "defence_level": "1", + "defence_level": "35", "safespot": null, "lifepoints": "71", - "strength_level": "1", + "strength_level": "36", "id": "2056", "aggressive": "true", "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" + "attack_level": "20" }, { + "examine": "It's falling apart!", + "combat_audio": "", + "range_animation": "359", + "melee_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": "Skogre", - "defence_level": "1", + "defence_level": "35", "safespot": null, "lifepoints": "71", - "strength_level": "1", + "strength_level": "36", "id": "2057", "aggressive": "true", "range_level": "1", - "respawn_delay": "60", - "attack_level": "1" + "attack_level": "20" }, { "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", @@ -21154,16 +21695,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.", @@ -21183,7 +21736,6 @@ }, { "examine": "This one is slacking off.", - "slayer_task": "38", "melee_animation": "6007", "range_animation": "0", "combat_audio": "469,472,471", @@ -21202,7 +21754,6 @@ }, { "examine": "This one is slacking off.", - "slayer_task": "38", "melee_animation": "6007", "range_animation": "0", "combat_audio": "469,472,471", @@ -21221,7 +21772,6 @@ }, { "examine": "This one is slacking off.", - "slayer_task": "38", "melee_animation": "6007", "range_animation": "0", "combat_audio": "469,472,471", @@ -21240,7 +21790,6 @@ }, { "examine": "This one is slacking off.", - "slayer_task": "38", "melee_animation": "6007", "range_animation": "0", "combat_audio": "469,472,471", @@ -21259,7 +21808,6 @@ }, { "examine": "He protects the miners.", - "slayer_task": "38", "melee_animation": "6007", "range_animation": "0", "combat_audio": "469,472,471", @@ -21279,7 +21827,6 @@ }, { "examine": "He protects the miners.", - "slayer_task": "38", "melee_animation": "6007", "range_animation": "0", "combat_audio": "469,472,471", @@ -21855,7 +22402,6 @@ }, { "examine": "A member of the Black Guard", - "slayer_task": "29", "melee_animation": "99", "range_animation": "0", "defence_animation": "0", @@ -21874,7 +22420,6 @@ }, { "examine": "A member of the Black Guard", - "slayer_task": "29", "melee_animation": "99", "range_animation": "0", "defence_animation": "0", @@ -21893,7 +22438,6 @@ }, { "examine": "A member of the Black Guard", - "slayer_task": "29", "melee_animation": "99", "range_animation": "0", "defence_animation": "0", @@ -21911,19 +22455,25 @@ "attack_level": "34" }, { + "examine": "A member of the Black Guard", + "melee_animation": "99", + "range_animation": "0", + "defence_animation": "0", + "weakness": "6", "slayer_exp": "16", + "magic_animation": "0", + "death_animation": "102", "name": "Black Guard", - "defence_level": "1", + "defence_level": "34", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "lifepoints": "48", + "strength_level": "34", "id": "2133", "range_level": "1", - "attack_level": "1" + "attack_level": "34" }, { "examine": "An elite member of the Black Guard.", - "slayer_task": "29", "melee_animation": "99", "range_animation": "0", "respawn_delay": "60", @@ -21942,7 +22492,6 @@ }, { "examine": "An elite member of the Black Guard.", - "slayer_task": "29", "melee_animation": "99", "range_animation": "0", "respawn_delay": "60", @@ -21961,7 +22510,6 @@ }, { "examine": "An elite member of the Black Guard.", - "slayer_task": "29", "melee_animation": "99", "range_animation": "0", "respawn_delay": "60", @@ -21982,7 +22530,7 @@ "examine": "A gnome traveller, visiting Keldagrim.", "name": "Gnome traveller", "defence_level": "1", - "force_talk": "Buying low-level skilling items!", + "force_talk": "", "safespot": null, "lifepoints": "10", "strength_level": "1", @@ -21990,6 +22538,18 @@ "range_level": "1", "attack_level": "1" }, + { + "examine": "A gnome traveller, visiting Keldagrim.", + "name": "Gnome traveller", + "defence_level": "1", + "force_talk": "", + "safespot": null, + "lifepoints": "10", + "strength_level": "1", + "id": "2139", + "range_level": "1", + "attack_level": "1" + }, { "examine": "Very protective of his master... and his property.", "melee_animation": "0", @@ -22022,6 +22582,38 @@ "range_level": "1", "attack_level": "1" }, + { + "examine": "Proprietor of Gunslik's Assorted Items.", + "melee_animation": "0", + "range_animation": "0", + "defence_animation": "0", + "magic_animation": "0", + "death_animation": "0", + "name": "Gunslik", + "defence_level": "1", + "safespot": null, + "lifepoints": "10", + "strength_level": "1", + "id": "2154", + "range_level": "1", + "attack_level": "1" + }, + { + "examine": "An old mining dwarf, now he sells pickaxes and generally acts grumpy.", + "melee_animation": "0", + "range_animation": "0", + "defence_animation": "0", + "magic_animation": "0", + "death_animation": "0", + "name": "Tati", + "defence_level": "1", + "safespot": null, + "lifepoints": "10", + "strength_level": "1", + "id": "2160", + "range_level": "1", + "attack_level": "1" + }, { "examine": "A rather personable banker lady.", "melee_animation": "0", @@ -22327,7 +22919,7 @@ "attack_level": "1" }, { - "examine": "A loud", + "examine": "A loud, drunk and generally obnoxious dwarf.", "melee_animation": "0", "range_animation": "0", "defence_animation": "0", @@ -22339,12 +22931,155 @@ "lifepoints": "10", "strength_level": "1", "id": "2187", - "aggressive": "true", "range_level": "1", "attack_level": "1" }, { - "examine": "You cannot see his ship", + "examine": "A sad old dwarf living in a sad old home.", + "melee_animation": "0", + "range_animation": "0", + "defence_animation": "0", + "magic_animation": "0", + "death_animation": "0", + "name": "Runvastr", + "defence_level": "1", + "safespot": null, + "lifepoints": "10", + "strength_level": "1", + "id": "2190", + "range_level": "1", + "attack_level": "1" + }, + { + "examine": "He seems to always be busy.", + "melee_animation": "0", + "range_animation": "0", + "defence_animation": "0", + "magic_animation": "0", + "death_animation": "0", + "name": "Sune", + "defence_level": "1", + "safespot": null, + "lifepoints": "10", + "strength_level": "1", + "id": "2191", + "range_level": "1", + "attack_level": "1" + }, + { + "examine": "This dwarf doesn't seem to be very well off.", + "melee_animation": "0", + "range_animation": "0", + "defence_animation": "0", + "magic_animation": "0", + "death_animation": "0", + "name": "Bentamir", + "defence_level": "1", + "safespot": null, + "lifepoints": "10", + "strength_level": "1", + "id": "2192", + "range_level": "1", + "attack_level": "1" + }, + { + "examine": "A dwarf minding his own business.", + "melee_animation": "0", + "range_animation": "0", + "defence_animation": "0", + "magic_animation": "0", + "death_animation": "0", + "name": "Ulifed", + "defence_level": "1", + "safespot": null, + "lifepoints": "10", + "strength_level": "1", + "id": "2193", + "range_level": "1", + "attack_level": "1" + }, + { + "examine": "A short, merry guy.", + "melee_animation": "0", + "range_animation": "0", + "defence_animation": "0", + "magic_animation": "0", + "death_animation": "0", + "name": "Karl", + "defence_level": "1", + "safespot": null, + "lifepoints": "10", + "strength_level": "1", + "id": "2195", + "range_level": "1", + "attack_level": "1" + }, + { + "examine": "His kebabs certainly smell delicious.", + "melee_animation": "0", + "range_animation": "0", + "defence_animation": "0", + "magic_animation": "0", + "death_animation": "0", + "name": "Kjut", + "defence_level": "1", + "safespot": null, + "lifepoints": "10", + "strength_level": "1", + "id": "2198", + "range_level": "1", + "attack_level": "1" + }, + { + "examine": "He seems to be very well off.", + "melee_animation": "0", + "range_animation": "0", + "defence_animation": "0", + "magic_animation": "0", + "death_animation": "0", + "name": "Audmann", + "defence_level": "1", + "safespot": null, + "lifepoints": "10", + "strength_level": "1", + "id": "2201", + "range_level": "1", + "attack_level": "1" + }, + { + "examine": "He's had a fair bit to drink...", + "melee_animation": "0", + "range_animation": "0", + "defence_animation": "0", + "magic_animation": "0", + "death_animation": "0", + "name": "Drunken Dwarf", + "defence_level": "1", + "safespot": null, + "lifepoints": "10", + "strength_level": "1", + "id": "2202", + "range_level": "1", + "attack_level": "1" + }, + { + "examine": "He's had a fair bit to drink...", + "melee_animation": "0", + "range_animation": "0", + "defence_animation": "0", + "magic_animation": "0", + "death_animation": "0", + "name": "Drunken Dwarf", + "defence_level": "1", + "safespot": null, + "lifepoints": "10", + "strength_level": "1", + "id": "2203", + "range_level": "1", + "attack_level": "1" + }, + { + "examine": "You cannot see his ship, but presumably he has one.", "melee_animation": "0", "range_animation": "0", "defence_animation": "0", @@ -22359,6 +23094,22 @@ "range_level": "1", "attack_level": "1" }, + { + "examine": "You cannot see his ship, but presumably he has one.", + "melee_animation": "0", + "range_animation": "0", + "defence_animation": "0", + "magic_animation": "0", + "death_animation": "0", + "name": "Dwarven Boatman", + "defence_level": "1", + "safespot": null, + "lifepoints": "10", + "strength_level": "1", + "id": "2206", + "range_level": "1", + "attack_level": "1" + }, { "examine": "He carries a heavy load.", "melee_animation": "0", @@ -22375,6 +23126,22 @@ "range_level": "1", "attack_level": "1" }, + { + "examine": "An elite member of the Black Guard.", + "melee_animation": "0", + "range_animation": "0", + "defence_animation": "0", + "magic_animation": "0", + "death_animation": "0", + "name": "Black Guard Berserker", + "defence_level": "1", + "safespot": null, + "lifepoints": "10", + "strength_level": "1", + "id": "2232", + "range_level": "1", + "attack_level": "1" + }, { "examine": "A master at farming.", "melee_animation": "0", @@ -22411,6 +23178,7 @@ "examine": "He guards the Draynor Market stalls from thieves.", "melee_animation": "390", "range_animation": "0", + "combat_audio": "2547,1979,512", "respawn_delay": "60", "defence_animation": "0", "weakness": "6", @@ -22893,6 +23661,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", @@ -22900,13 +23716,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" }, { @@ -22941,6 +23789,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", @@ -23024,7 +23888,6 @@ }, { "examine": "Young, but still beefy.", - "slayer_task": "20", "melee_animation": "5849", "range_animation": "5849", "combat_audio": "366,368,367", @@ -23212,6 +24075,22 @@ "range_level": "1", "attack_level": "1" }, + { + "examine": "An expert on the brewing of ales and cider.", + "melee_animation": "0", + "range_animation": "0", + "defence_animation": "0", + "magic_animation": "0", + "death_animation": "0", + "name": "Blandebir", + "defence_level": "1", + "safespot": null, + "lifepoints": "10", + "strength_level": "1", + "id": "2321", + "range_level": "1", + "attack_level": "1" + }, { "examine": "Only the Grim Reaper would have a pet like this.", "melee_animation": "0", @@ -23312,7 +24191,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", @@ -23334,7 +24212,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", @@ -23356,7 +24233,6 @@ }, { "examine": "He looks pretty handy with that bow.", - "slayer_task": "31", "start_gfx": "250", "combat_style": "1", "melee_animation": "428", @@ -23380,7 +24256,6 @@ }, { "examine": "He looks pretty handy with that bow.", - "slayer_task": "31", "start_gfx": "250", "combat_style": "1", "melee_animation": "428", @@ -23404,40 +24279,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", - "slayer_task": "31", + "examine": "A mourner, or plague healer.", "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, @@ -23449,6 +24517,7 @@ "attack_level": "1" }, { + "examine": "Seems to flitter in and out of existence...", "name": "Mysterious ghost", "defence_level": "1", "safespot": null, @@ -23460,6 +24529,7 @@ "attack_level": "1" }, { + "examine": "Seems to flitter in and out of existence...", "name": "Mysterious ghost", "defence_level": "1", "safespot": null, @@ -23471,6 +24541,7 @@ "attack_level": "1" }, { + "examine": "Seems to flitter in and out of existence...", "name": "Mysterious ghost", "defence_level": "1", "safespot": null, @@ -23482,6 +24553,7 @@ "attack_level": "1" }, { + "examine": "Seems to flitter in and out of existence...", "name": "Mysterious ghost", "defence_level": "1", "safespot": null, @@ -23493,6 +24565,7 @@ "attack_level": "1" }, { + "examine": "Seems to flitter in and out of existence...", "name": "Mysterious ghost", "defence_level": "1", "safespot": null, @@ -23504,17 +24577,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" }, @@ -23606,7 +24678,6 @@ }, { "examine": "A teeny-tiny horror from the ocean depths...", - "slayer_task": "24", "melee_animation": "1341", "range_animation": "0", "respawn_delay": "60", @@ -23688,12 +24759,88 @@ "range_level": "1", "attack_level": "1" }, + { + "examine": "A brightly coloured game bird.", + "melee_animation": "2372", + "range_animation": "2372", + "combat_audio": "690,692,691", + "attack_speed": "6", + "respawn_delay": "18", + "defence_animation": "2371", + "magic_animation": "2372", + "death_animation": "2373", + "name": "Pheasant", + "defence_level": "1", + "safespot": null, + "lifepoints": "5", + "strength_level": "1", + "id": "2459", + "range_level": "1", + "attack_level": "1" + }, + { + "examine": "A brightly coloured game bird.", + "melee_animation": "2372", + "range_animation": "2372", + "combat_audio": "690,692,691", + "attack_speed": "6", + "respawn_delay": "18", + "defence_animation": "2371", + "magic_animation": "2372", + "death_animation": "2373", + "name": "Pheasant", + "defence_level": "1", + "safespot": null, + "lifepoints": "5", + "strength_level": "1", + "id": "2460", + "range_level": "1", + "attack_level": "1" + }, + { + "examine": "A brightly coloured game bird.", + "melee_animation": "2372", + "range_animation": "2372", + "combat_audio": "690,692,691", + "attack_speed": "6", + "respawn_delay": "18", + "defence_animation": "2371", + "magic_animation": "2372", + "death_animation": "2373", + "name": "Pheasant", + "defence_level": "1", + "safespot": null, + "lifepoints": "5", + "strength_level": "1", + "id": "2461", + "range_level": "1", + "attack_level": "1" + }, + { + "examine": "A brightly coloured game bird.", + "melee_animation": "2372", + "range_animation": "2372", + "combat_audio": "690,692,691", + "attack_speed": "6", + "respawn_delay": "18", + "defence_animation": "2371", + "magic_animation": "2372", + "death_animation": "2373", + "name": "Pheasant", + "defence_level": "1", + "safespot": null, + "lifepoints": "5", + "strength_level": "1", + "id": "2462", + "range_level": "1", + "attack_level": "1" + }, { "examine": "A fowl beast.", "combat_style": "2", "melee_animation": "2302", "range_animation": "2302", - "combat_audio": "355,357,356", + "combat_audio": "2288,357,356", "magic_level": "5", "respawn_delay": "25", "defence_animation": "2300", @@ -23716,7 +24863,7 @@ "combat_style": "2", "melee_animation": "2302", "range_animation": "2302", - "combat_audio": "355,357,356", + "combat_audio": "2288,357,356", "magic_level": "10", "respawn_delay": "25", "defence_animation": "2300", @@ -23739,7 +24886,7 @@ "combat_style": "2", "melee_animation": "2302", "range_animation": "2302", - "combat_audio": "355,357,356", + "combat_audio": "2288,357,356", "magic_level": "15", "respawn_delay": "25", "defence_animation": "2300", @@ -23762,7 +24909,7 @@ "combat_style": "2", "melee_animation": "2302", "range_animation": "2302", - "combat_audio": "355,357,356", + "combat_audio": "2288,357,356", "magic_level": "25", "respawn_delay": "25", "defence_animation": "2300", @@ -23785,7 +24932,7 @@ "combat_style": "2", "melee_animation": "2302", "range_animation": "2302", - "combat_audio": "355,357,356", + "combat_audio": "2288,357,356", "magic_level": "35", "respawn_delay": "25", "defence_animation": "2300", @@ -23808,7 +24955,7 @@ "combat_style": "2", "melee_animation": "2302", "range_animation": "2302", - "combat_audio": "355,357,356", + "combat_audio": "2288,357,356", "magic_level": "45", "respawn_delay": "25", "defence_animation": "2300", @@ -23832,6 +24979,7 @@ "combat_style": "1", "melee_animation": "4230", "range_animation": "4230", + "combat_audio": "2695,513,512", "attack_speed": "5", "magic_level": "30", "defence_animation": "404", @@ -23845,7 +24993,9 @@ "id": "2476", "bonuses": "30,20,10,20,20,30,30,10,20,10,30,30,20,30,20", "range_level": "20", - "attack_level": "8" + "projectile": "27", + "attack_level": "8", + "prj_height": "" }, { "examine": "Apparently a master of quizzes!", @@ -23864,7 +25014,7 @@ "attack_level": "1" }, { - "examine": "Hey", + "examine": "Hey, it's Bob the cat! Or... is it?", "melee_animation": "0", "range_animation": "0", "defence_animation": "0", @@ -24209,6 +25359,16 @@ "range_level": "1", "attack_level": "1" }, + { + "examine": "He smells of rock dust.", + "name": "Ordan", + "id": "2564" + }, + { + "examine": "He buys stuff.", + "name": "Jorzik", + "id": "2565" + }, { "death_animation": "836", "name": "Market Guard", @@ -24864,6 +26024,11 @@ "range_level": "1", "attack_level": "1" }, + { + "examine": "Nice but dim.", + "name": "Unferth", + "id": "2655" + }, { "examine": "Known for his light-fingered qualities.", "melee_animation": "422", @@ -25170,7 +26335,6 @@ }, { "examine": "She quackers.", - "slayer_task": "7", "melee_animation": "0", "range_animation": "0", "respawn_delay": "60", @@ -25190,7 +26354,6 @@ }, { "examine": "Mini quackers.", - "slayer_task": "7", "melee_animation": "0", "range_animation": "0", "defence_animation": "0", @@ -26006,7 +27169,6 @@ }, { "examine": "From a darker dimension.", - "slayer_task": "25", "melee_animation": "2731", "range_animation": "2731", "combat_audio": "389,391,390", @@ -26031,51 +27193,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!", @@ -26110,7 +27272,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", @@ -26118,14 +27280,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'.", @@ -26145,7 +27307,6 @@ }, { "examine": "A cold-blooded creature, partial to warmth.", - "slayer_task": "26", "melee_animation": "2776", "range_animation": "2776", "attack_speed": "5", @@ -26166,7 +27327,6 @@ }, { "examine": "A cold-blooded creature, partial to warmth.", - "slayer_task": "26", "melee_animation": "2776", "range_animation": "2776", "attack_speed": "5", @@ -26187,7 +27347,6 @@ }, { "examine": "A cold-blooded creature, partial to warmth.", - "slayer_task": "26", "melee_animation": "2776", "range_animation": "2776", "attack_speed": "5", @@ -26227,7 +27386,6 @@ }, { "examine": "A cold-blooded creature, partial to warmth.", - "slayer_task": "26", "melee_animation": "2776", "range_animation": "2776", "attack_speed": "5", @@ -26247,7 +27405,6 @@ }, { "examine": "A cold-blooded creature, partial to warmth.", - "slayer_task": "26", "melee_animation": "2776", "range_animation": "2776", "attack_speed": "5", @@ -26525,7 +27682,6 @@ }, { "examine": "An undead sea scoundrel.", - "slayer_task": "93", "melee_animation": "5647", "range_animation": "0", "combat_audio": "703,705,704", @@ -26546,7 +27702,6 @@ }, { "examine": "An undead sea scoundrel.", - "slayer_task": "93", "melee_animation": "5651", "range_animation": "0", "combat_audio": "703,705,704", @@ -26567,7 +27722,6 @@ }, { "examine": "An undead sea scoundrel.", - "slayer_task": "93", "melee_animation": "5651", "range_animation": "0", "combat_audio": "703,705,704", @@ -26588,7 +27742,6 @@ }, { "examine": "An undead sea scoundrel.", - "slayer_task": "93", "melee_animation": "5651", "range_animation": "0", "combat_audio": "703,705,704", @@ -26609,7 +27762,6 @@ }, { "examine": "An undead sea scoundrel.", - "slayer_task": "93", "melee_animation": "5647", "range_animation": "0", "combat_audio": "703,705,704", @@ -26630,7 +27782,6 @@ }, { "examine": "An undead sea scoundrel.", - "slayer_task": "93", "melee_animation": "5647", "range_animation": "0", "combat_audio": "703,705,704", @@ -26651,7 +27802,6 @@ }, { "examine": "He talks a good fight.", - "slayer_task": "93", "melee_animation": "5647", "range_animation": "0", "defence_animation": "0", @@ -26670,7 +27820,6 @@ }, { "examine": "He talks a good fight.", - "slayer_task": "93", "melee_animation": "5647", "range_animation": "0", "defence_animation": "0", @@ -26689,7 +27838,6 @@ }, { "examine": "He talks a good fight.", - "slayer_task": "93", "melee_animation": "5651", "range_animation": "0", "defence_animation": "0", @@ -26708,7 +27856,6 @@ }, { "examine": "He talks a good fight.", - "slayer_task": "93", "melee_animation": "5651", "range_animation": "0", "defence_animation": "0", @@ -26727,7 +27874,6 @@ }, { "examine": "He talks a good fight.", - "slayer_task": "93", "melee_animation": "5647", "range_animation": "0", "defence_animation": "0", @@ -26746,7 +27892,6 @@ }, { "examine": "He talks a good fight.", - "slayer_task": "93", "melee_animation": "5647", "range_animation": "0", "defence_animation": "0", @@ -26785,7 +27930,6 @@ }, { "examine": "A bunch of legs, eyes and teeth.", - "slayer_task": "76", "melee_animation": "5319", "range_animation": "0", "combat_audio": "537,539,538", @@ -26984,7 +28128,6 @@ }, { "examine": "A knee-high horror from the ocean depths...", - "slayer_task": "24", "melee_animation": "1579", "range_animation": "0", "defence_animation": "0", @@ -27607,6 +28750,7 @@ "examine": "A not-so friendly, not-so little cat.", "melee_animation": "0", "range_animation": "0", + "attack_speed": "4", "defence_animation": "0", "magic_animation": "0", "death_animation": "0", @@ -27618,7 +28762,7 @@ "id": "2986", "aggressive": "true", "range_level": "1", - "attack_level": "1" + "attack_level": "10" }, { "examine": "A fully grown feline.", @@ -28300,19 +29444,20 @@ "magic_animation": "0", "death_animation": "2946", "name": "Earth Warrior Champion", - "defence_level": "51", + "defence_level": "84", "safespot": null, - "lifepoints": "72", - "strength_level": "51", + "lifepoints": "108", + "strength_level": "84", "id": "3057", "aggressive": "true", "range_level": "1", - "attack_level": "51" + "attack_level": "84" }, { "examine": "Champion of the giants.", "melee_animation": "6368", "range_animation": "0", + "attack_speed": "4", "respawn_delay": "60", "defence_animation": "0", "weakness": "8", @@ -28320,14 +29465,14 @@ "magic_animation": "0", "death_animation": "6369", "name": "Giant Champion", - "defence_level": "28", + "defence_level": "52", "safespot": null, - "lifepoints": "40", - "strength_level": "28", + "lifepoints": "70", + "strength_level": "44", "id": "3058", "aggressive": "true", "range_level": "1", - "attack_level": "28" + "attack_level": "36" }, { "examine": "Champion of the ghouls.", @@ -28358,86 +29503,89 @@ "melee_animation": "6188", "range_animation": "0", "combat_audio": "469,472,471", - "magic_level": "12", + "attack_speed": "4", + "magic_level": "26", "defence_animation": "0", "weakness": "10", "magic_animation": "0", "death_animation": "6190", "name": "Goblin Champion", - "defence_level": "12", + "defence_level": "14", "safespot": null, - "lifepoints": "17", - "strength_level": "6", + "lifepoints": "32", + "strength_level": "1", "id": "3060", "range_level": "1", - "attack_level": "6" + "attack_level": "1" }, { "examine": "Champion of the hobgoblins.", "combat_style": "1", - "range_animation": "0", + "melee_animation": "164", + "range_animation": "164", "combat_audio": "469,472,471", + "attack_speed": "4", "respawn_delay": "60", - "defence_animation": "0", + "defence_animation": "165", "weakness": "0", - "magic_animation": "0", - "death_animation": "2958", + "magic_animation": "164", + "death_animation": "167", "name": "Hobgoblin Champion", - "defence_level": "28", + "defence_level": "48", "safespot": null, - "lifepoints": "40", - "strength_level": "14", + "lifepoints": "58", + "strength_level": "48", "id": "3061", "aggressive": "true", - "range_level": "28", - "attack_level": "14" + "range_level": "44", + "attack_level": "44" }, { "examine": "Champion of the imps.", "melee_animation": "5285", "range_animation": "0", - "attack_speed": "5", + "attack_speed": "4", "respawn_delay": "60", "defence_animation": "0", "weakness": "7", "magic_animation": "0", "death_animation": "172", "name": "Imp Champion", - "defence_level": "7", + "defence_level": "5", "safespot": null, - "lifepoints": "10", - "strength_level": "7", + "lifepoints": "40", + "strength_level": "5", "id": "3062", "aggressive": "true", - "range_level": "7", - "attack_level": "7" + "range_level": "5", + "attack_level": "5" }, { "examine": "Champion of the jogres.", - "melee_animation": "2100", - "range_animation": "0", + "melee_animation": "2936", + "range_animation": "2936", "poisonous": "true", "respawn_delay": "60", - "defence_animation": "0", + "defence_animation": "2937", "weakness": "9", - "magic_animation": "0", - "death_animation": "8576", + "magic_animation": "2936", + "death_animation": "2938", "name": "Jogre Champion", - "defence_level": "54", + "defence_level": "86", "safespot": null, - "lifepoints": "77", - "strength_level": "54", + "lifepoints": "120", + "strength_level": "86", "id": "3063", "aggressive": "true", "range_level": "1", - "attack_level": "54" + "attack_level": "86" }, { "examine": "Champion of the lesser demons.", "melee_animation": "64", "range_animation": "0", "combat_audio": "396,402,401", - "magic_level": "81", + "magic_level": "136", "respawn_delay": "60", "defence_animation": "65", "weakness": "5", @@ -28445,13 +29593,33 @@ "magic_animation": "0", "death_animation": "67", "name": "Lesser Demon Champion", - "defence_level": "81", + "defence_level": "142", "safespot": null, - "lifepoints": "115", - "strength_level": "1", + "lifepoints": "148", + "strength_level": "140", "id": "3064", "aggressive": "true", "range_level": "1", + "attack_level": "136" + }, + { + "examine": "Champion of the jogres.", + "melee_animation": "5485", + "range_animation": "5493", + "poisonous": "true", + "respawn_delay": "60", + "defence_animation": "5493", + "weakness": "9", + "magic_animation": "0", + "death_animation": "1444", + "name": "Skeleton Champion", + "defence_level": "54", + "safespot": null, + "lifepoints": "58", + "strength_level": "1", + "id": "3065", + "aggressive": "true", + "range_level": "36", "attack_level": "1" }, { @@ -28491,7 +29659,6 @@ }, { "examine": "A very dangerous pile of animated Wyvern bones.", - "slayer_task": "95", "start_gfx": "499", "melee_animation": "2985", "attack_speed": "6", @@ -28520,7 +29687,6 @@ }, { "examine": "A very dangerous pile of animated Wyvern bones.", - "slayer_task": "95", "start_gfx": "499", "melee_animation": "2985", "attack_speed": "6", @@ -28549,7 +29715,6 @@ }, { "examine": "A very dangerous pile of animated Wyvern bones.", - "slayer_task": "95", "start_gfx": "499", "melee_animation": "2985", "attack_speed": "6", @@ -28578,7 +29743,6 @@ }, { "examine": "A very dangerous pile of animated Wyvern bones.", - "slayer_task": "95", "start_gfx": "499", "melee_animation": "2985", "attack_speed": "6", @@ -28607,7 +29771,6 @@ }, { "examine": "He's got icicles in his beard.", - "slayer_task": "47", "melee_animation": "4672", "range_animation": "0", "combat_audio": "448,451,450", @@ -28632,7 +29795,6 @@ }, { "examine": "A cold-hearted elemental warrior.", - "slayer_task": "48", "melee_animation": "451", "range_animation": "451", "combat_audio": "2500,530,529", @@ -29012,7 +30174,6 @@ }, { "examine": "A swarm of bugs.", - "slayer_task": "42", "melee_animation": "1584", "range_animation": "0", "combat_audio": "2743,2745,2744", @@ -29558,25 +30719,8 @@ "range_level": "1", "attack_level": "1" }, - { - "examine": "A mourner", - "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", @@ -29595,7 +30739,6 @@ }, { "examine": "Loves mining.", - "slayer_task": "29", "melee_animation": "99", "range_animation": "0", "defence_animation": "0", @@ -29614,7 +30757,6 @@ }, { "examine": "Loves mining.", - "slayer_task": "29", "melee_animation": "99", "range_animation": "0", "defence_animation": "0", @@ -29719,7 +30861,7 @@ "combat_audio": "511,513,512", "attack_speed": "5", "respawn_delay": "60", - "defence_animation": "0", + "defence_animation": "404", "weakness": "7", "magic_animation": "0", "death_animation": "836", @@ -29735,12 +30877,12 @@ }, { "examine": "He tries to keep order around here.", - "melee_animation": "0", + "melee_animation": "4230", "range_animation": "2075", "combat_audio": "511,513,512", "attack_speed": "5", "respawn_delay": "60", - "defence_animation": "0", + "defence_animation": "404", "weakness": "0", "magic_animation": "0", "death_animation": "836", @@ -29761,7 +30903,7 @@ "combat_audio": "511,513,512", "attack_speed": "5", "respawn_delay": "60", - "defence_animation": "0", + "defence_animation": "404", "weakness": "7", "magic_animation": "0", "death_animation": "836", @@ -29888,53 +31030,73 @@ "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", "combat_audio": "511,513,512", "attack_speed": "5", "respawn_delay": "60", - "defence_animation": "425", + "defence_animation": "404", "death_animation": "836", "name": "Guard", "defence_level": "1", @@ -30425,7 +31587,6 @@ }, { "examine": "A dwarven worker.", - "slayer_task": "29", "melee_animation": "99", "range_animation": "0", "defence_animation": "0", @@ -30444,7 +31605,6 @@ }, { "examine": "A dwarven worker.", - "slayer_task": "29", "melee_animation": "99", "range_animation": "0", "defence_animation": "0", @@ -30463,7 +31623,6 @@ }, { "examine": "A dwarven worker.", - "slayer_task": "29", "melee_animation": "99", "range_animation": "0", "defence_animation": "0", @@ -30482,7 +31641,6 @@ }, { "examine": "A dwarven worker.", - "slayer_task": "29", "melee_animation": "99", "range_animation": "0", "defence_animation": "0", @@ -30501,7 +31659,6 @@ }, { "examine": "A dwarven worker.", - "slayer_task": "29", "melee_animation": "99", "range_animation": "0", "defence_animation": "0", @@ -30520,7 +31677,6 @@ }, { "examine": "A dwarven worker.", - "slayer_task": "29", "melee_animation": "99", "range_animation": "0", "defence_animation": "0", @@ -30539,7 +31695,6 @@ }, { "examine": "A dwarven worker.", - "slayer_task": "29", "melee_animation": "99", "range_animation": "0", "defence_animation": "0", @@ -30558,7 +31713,6 @@ }, { "examine": "A dwarven worker.", - "slayer_task": "29", "melee_animation": "99", "range_animation": "0", "defence_animation": "0", @@ -30577,7 +31731,6 @@ }, { "examine": "A member of the Black Guard", - "slayer_task": "29", "melee_animation": "99", "range_animation": "0", "defence_animation": "0", @@ -30596,7 +31749,6 @@ }, { "examine": "A member of the Black Guard", - "slayer_task": "29", "melee_animation": "99", "range_animation": "0", "defence_animation": "0", @@ -30615,7 +31767,6 @@ }, { "examine": "A member of the Black Guard", - "slayer_task": "29", "melee_animation": "99", "range_animation": "0", "defence_animation": "0", @@ -30633,15 +31784,22 @@ "attack_level": "30" }, { + "examine": "A member of the Black Guard", + "melee_animation": "99", + "range_animation": "0", + "defence_animation": "0", + "weakness": "6", "slayer_exp": "16", + "magic_animation": "0", + "death_animation": "102", "name": "Black Guard", - "defence_level": "1", + "defence_level": "30", "safespot": null, - "lifepoints": "10", - "strength_level": "1", + "lifepoints": "42", + "strength_level": "30", "id": "3279", "range_level": "1", - "attack_level": "1" + "attack_level": "30" }, { "examine": "He looks busy.", @@ -30873,7 +32031,6 @@ }, { "examine": "Converts grass to beef.", - "slayer_task": "20", "melee_animation": "5849", "range_animation": "5849", "combat_audio": "369,371,370", @@ -30892,6 +32049,11 @@ "range_level": "1", "attack_level": "1" }, + { + "examine": "White and fluffy", + "name": "Sheep", + "id": "3310" + }, { "name": "Tanglefoot", "defence_level": "1", @@ -31056,7 +32218,6 @@ }, { "examine": "A bouncy fungus.", - "slayer_task": "94", "melee_animation": "2776", "range_animation": "0", "attack_speed": "5", @@ -31206,7 +32367,6 @@ }, { "examine": "A fowl beast.", - "slayer_task": "7", "melee_animation": "2299", "range_animation": "0", "combat_audio": "355,357,356", @@ -31228,7 +32388,6 @@ }, { "examine": "Young but still dangerous.", - "slayer_task": "68", "melee_animation": "25", "range_animation": "25", "combat_audio": "405,407,406", @@ -31269,7 +32428,6 @@ }, { "examine": "Vermin from the underworld.", - "slayer_task": "67", "melee_animation": "0", "range_animation": "0", "combat_audio": "703,705,704", @@ -31401,7 +32559,6 @@ }, { "examine": "A small ice demon.", - "slayer_task": "46", "range_animation": "0", "combat_audio": "531,533,532", "attack_speed": "5", @@ -31627,16 +32784,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" }, { @@ -32027,7 +33189,6 @@ }, { "examine": "A large boisterous bird", - "slayer_task": "7", "melee_animation": "6800", "range_animation": "0", "defence_animation": "0", @@ -32352,7 +33513,6 @@ }, { "examine": "An initiate juvenile vampyre", - "slayer_task": "86", "melee_animation": "6016", "range_animation": "0", "defence_animation": "0", @@ -32387,7 +33547,6 @@ }, { "examine": "He looks really hungry!", - "slayer_task": "86", "melee_animation": "6276", "range_animation": "0", "respawn_delay": "60", @@ -32406,7 +33565,6 @@ }, { "examine": "She looks really hungry!", - "slayer_task": "86", "melee_animation": "6276", "range_animation": "0", "respawn_delay": "60", @@ -32438,7 +33596,6 @@ }, { "examine": "He looks really hungry!", - "slayer_task": "86", "melee_animation": "0", "range_animation": "0", "defence_animation": "0", @@ -32456,7 +33613,6 @@ }, { "examine": "An initiate juvenile vampyre", - "slayer_task": "86", "melee_animation": "6016", "range_animation": "0", "defence_animation": "0", @@ -32537,7 +33693,6 @@ }, { "examine": "Good doggy...", - "slayer_task": "43", "melee_animation": "6562", "range_animation": "0", "combat_audio": "3717,3719,3718", @@ -32718,7 +33873,6 @@ }, { "examine": "A large snake that thrives in swamps.", - "slayer_task": "86", "melee_animation": "3538", "range_animation": "3538", "combat_audio": "3609,3608,3610", @@ -32878,11 +34032,13 @@ "examine": "He looks a little on the cross side!", "range_animation": "0", "combat_audio": "297,299,298", + "melee_animation": "4927", "attack_speed": "4", "magic_level": "1", - "defence_animation": "0", + "defence_animation": "4927", "weakness": "9", "magic_animation": "0", + "death_animation": "4929", "name": "Angry bear", "defence_level": "38", "safespot": null, @@ -32918,7 +34074,7 @@ "melee_animation": "4933", "range_animation": "0", "combat_audio": "703,705,704", - "defence_animation": "0", + "defence_animation": "4934", "weakness": "9", "magic_animation": "0", "death_animation": "4935", @@ -32936,7 +34092,7 @@ "melee_animation": "6185", "range_animation": "0", "combat_audio": "469,472,471", - "defence_animation": "0", + "defence_animation": "6183", "weakness": "9", "magic_animation": "0", "death_animation": "6182", @@ -32953,18 +34109,20 @@ "examine": "AHHHHH!", "melee_animation": "3812", "range_animation": "0", - "defence_animation": "0", - "weakness": "9", + "combat_audio": "297,299,298", + "attack_speed": "4", + "defence_animation": "3811", + "weakness": "", "magic_animation": "0", "death_animation": "3813", "name": "Fear reaper", - "defence_level": "27", + "defence_level": "40", "safespot": null, - "lifepoints": "38", - "strength_level": "27", + "lifepoints": "25", + "strength_level": "41", "id": "3649", "range_level": "1", - "attack_level": "27" + "attack_level": "41" }, { "examine": "What on Gielinor is that?!?", @@ -33002,13 +34160,19 @@ }, { "examine": "He looks a little on the cross side!", + "melee_animation": "6376", + "range_animation": "0", + "combat_audio": "876,878,877", + "attack_speed": "4", + "defence_animation": "0", + "weakness": "9", + "magic_animation": "0", + "death_animation": "6377", "name": "Angry unicorn", "defence_level": "38", "safespot": null, "lifepoints": "50", - "combat_audio": "876,878,877", "strength_level": "41", - "attack_speed": "4", "id": "3661", "aggressive": "false", "range_level": "1", @@ -33016,40 +34180,45 @@ }, { "examine": "He looks a little on the cross side!", - "name": "Angry giant rat", - "defence_level": "1", - "safespot": null, - "lifepoints": "10", "combat_audio": "703,705,704", - "strength_level": "1", + "melee_animation": "4933", + "defence_animation": "4934", + "death_animation": "4935", + "name": "Angry giant rat", + "defence_level": "38", + "safespot": null, + "lifepoints": "50", + "strength_level": "41", "id": "3662", "range_level": "1", - "attack_level": "1" + "attack_level": "38" }, { "examine": "He looks a little on the cross side!", - "slayer_task": "38", "melee_animation": "6185", "range_animation": "0", "combat_audio": "469,472,471", - "defence_animation": "0", + "defence_animation": "6183", "weakness": "9", "magic_animation": "0", "death_animation": "6182", "name": "Angry goblin", - "defence_level": "25", + "defence_level": "38", "safespot": null, - "lifepoints": "35", - "strength_level": "25", + "lifepoints": "50", + "strength_level": "41", "id": "3663", "range_level": "1", - "attack_level": "25" + "attack_level": "38" }, { "examine": "He looks a little on the cross side!", "combat_audio": "297,299,298", + "melee_animation": "4927", "attack_speed": "4", "magic_level": "1", + "defence_animation": "4927", + "death_animation": "4929", "name": "Angry bear", "defence_level": "38", "safespot": null, @@ -33060,43 +34229,108 @@ "range_level": "1", "attack_level": "38" }, + { + "examine": "AHHHHH!", + "melee_animation": "3812", + "combat_audio": "297,299,298", + "attack_speed": "4", + "magic_level": "1", + "defence_animation": "3811", + "death_animation": "3813", + "name": "Fear reaper", + "defence_level": "45", + "safespot": null, + "lifepoints": "57", + "strength_level": "48", + "id": "3665", + "bonuses": "", + "range_level": "1", + "attack_level": "45" + }, + { + "examine": "What on Gielinor is that?!?", + "melee_animation": "3818", + "combat_audio": "297,299,298", + "attack_speed": "4", + "magic_level": "1", + "defence_animation": "3820", + "death_animation": "3821", + "name": "Confusion beast", + "defence_level": "52", + "safespot": null, + "lifepoints": "64", + "strength_level": "55", + "id": "3666", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "range_level": "1", + "attack_level": "52" + }, + { + "examine": "He looks a little on the cross side!", + "melee_animation": "3823", + "combat_audio": "297,299,298", + "attack_speed": "4", + "magic_level": "1", + "defence_animation": "0", + "death_animation": "3827", + "name": "Hopeless creature", + "defence_level": "59", + "safespot": null, + "lifepoints": "71", + "strength_level": "62", + "id": "3667", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "range_level": "1", + "attack_level": "59" + }, + { + "examine": "That's a lot of bones on his back...", + "name": "Odd Old Man", + "id": "3670" + }, { "examine": "Freshly sheared.", "melee_animation": "5341", "range_animation": "0", + "combat_audio": "757,759,758", "attack_speed": "5", "respawn_delay": "60", - "defence_animation": "0", + "defence_animation": "5337", "weakness": "9", "magic_animation": "0", "death_animation": "5343", "name": "Ram", - "defence_level": "2", + "defence_level": "1", "safespot": null, - "lifepoints": "2", - "strength_level": "2", + "lifepoints": "8", + "strength_level": "1", "id": "3672", "range_level": "1", - "attack_level": "2" + "attack_level": "1" }, { - "examine": "White and shaggy.", + "examine": "White and fluffy.", "melee_animation": "5338", "range_animation": "0", + "combat_audio": "757,759,758", "attack_speed": "5", "respawn_delay": "60", - "defence_animation": "0", + "defence_animation": "5337", "weakness": "9", "magic_animation": "0", "death_animation": "5336", "name": "Ram", - "defence_level": "2", + "defence_level": "1", "safespot": null, - "lifepoints": "2", - "strength_level": "2", + "lifepoints": "8", + "strength_level": "1", "id": "3673", "range_level": "1", - "attack_level": "2" + "attack_level": "1" + }, + { + "name": "Sack", + "id": "3674" }, { "examine": "If you see them circling, run.", @@ -33170,7 +34404,6 @@ }, { "examine": "Eww", - "slayer_task": "67", "melee_animation": "6117", "range_animation": "0", "combat_audio": "703,705,704", @@ -33208,7 +34441,6 @@ }, { "examine": "An annoying flappy thing.", - "slayer_task": "5", "melee_animation": "4915", "range_animation": "4915", "combat_audio": "292,294,293", @@ -35077,7 +36309,6 @@ }, { "examine": "Snake", - "slayer_task": "72", "melee_animation": "3538", "range_animation": "0", "combat_audio": "3609,3608,3610", @@ -35098,7 +36329,6 @@ }, { "examine": "A baby sea snake. Snaaaaaaake!", - "slayer_task": "72", "melee_animation": "3538", "range_animation": "0", "combat_audio": "3609,3608,3610", @@ -35216,7 +36446,6 @@ }, { "examine": "A denizen of the Abyss!", - "slayer_task": "1", "melee_animation": "1537", "range_animation": "1537", "combat_audio": "276,278,277", @@ -35377,7 +36606,6 @@ }, { "examine": "A dwarven guard.", - "slayer_task": "29", "melee_animation": "99", "range_animation": "0", "combat_audio": "511,513,512", @@ -35744,6 +36972,7 @@ "examine": "Animated bronze armour.", "melee_animation": "386", "range_animation": "386", + "combat_audio": "2549,1979,512", "attack_speed": "4", "respawn_delay": "20", "defence_animation": "388", @@ -35766,6 +36995,7 @@ "examine": "Animated iron armour.", "melee_animation": "386", "range_animation": "386", + "combat_audio": "2549,1979,512", "attack_speed": "4", "respawn_delay": "20", "defence_animation": "388", @@ -35788,6 +37018,7 @@ "examine": "Animated steel armour.", "melee_animation": "386", "range_animation": "386", + "combat_audio": "2549,1979,512", "attack_speed": "4", "respawn_delay": "20", "defence_animation": "388", @@ -35810,6 +37041,7 @@ "examine": "Animated black armour.", "melee_animation": "386", "range_animation": "386", + "combat_audio": "2549,1979,512", "attack_speed": "4", "respawn_delay": "20", "defence_animation": "388", @@ -35832,6 +37064,7 @@ "examine": "Animated mithril armour.", "melee_animation": "386", "range_animation": "386", + "combat_audio": "2549,1979,512", "attack_speed": "4", "respawn_delay": "20", "defence_animation": "388", @@ -35854,6 +37087,7 @@ "examine": "Animated adamant armour.", "melee_animation": "386", "range_animation": "386", + "combat_audio": "2549,1979,512", "attack_speed": "4", "respawn_delay": "20", "defence_animation": "388", @@ -35876,6 +37110,7 @@ "examine": "Animated rune armour.", "melee_animation": "386", "range_animation": "386", + "combat_audio": "2549,1979,512", "attack_speed": "4", "respawn_delay": "20", "defence_animation": "388", @@ -35894,11 +37129,41 @@ "range_level": "1", "attack_level": "120" }, + { + "examine": "The big door man.", + "name": "Ghommal", + "id": "4285" + }, + { + "examine": "Guild Master.", + "name": "Harrallak Menarous", + "id": "4286" + }, + { + "examine": "The guild engineer.", + "name": "Gamfred", + "id": "4287" + }, + { + "examine": "Used to be a black knight.", + "name": "Ajjat", + "id": "4288" + }, + { + "examine": "Blademistress, skillful female warrior.", + "name": "Kamfreena", + "id": "4289" + }, + { + "examine": "A warrior mage from the east.", + "name": "Shanomi", + "id": "4290" + }, { "examine": "A one-eyed man eater.", - "slayer_task": "44", "melee_animation": "4652", "range_animation": "0", + "combat_audio": "448,451,450", "attack_speed": "4", "magic_level": "1", "respawn_delay": "30", @@ -35921,9 +37186,9 @@ }, { "examine": "A one-eyed woman eater.", - "slayer_task": "44", "melee_animation": "4652", "range_animation": "0", + "combat_audio": "448,451,450", "attack_speed": "4", "magic_level": "1", "respawn_delay": "30", @@ -35944,6 +37209,41 @@ "range_level": "1", "attack_level": "65" }, + { + "examine": "Food shopkeeper.", + "name": "Lidio", + "id": "4293" + }, + { + "examine": "Potion shopkeeper.", + "name": "Lilly", + "id": "4294" + }, + { + "examine": "Armour shopkeeper.", + "name": "Anton", + "id": "4295" + }, + { + "examine": "Bank staff.", + "name": "Jade", + "id": "4296" + }, + { + "examine": "He looks like a strong warrior.", + "name": "Sloane", + "id": "4297" + }, + { + "examine": "He looks a bit drunk.", + "name": "Jimmy", + "id": "4298" + }, + { + "examine": "He looks fair and reliable.", + "name": "Ref", + "id": "4299" + }, { "examine": "He looks fair and reliable.", "melee_animation": "0", @@ -36212,7 +37512,6 @@ }, { "examine": "It's all white by me.", - "slayer_task": "5", "melee_animation": "4915", "range_animation": "0", "respawn_delay": "60", @@ -36250,7 +37549,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", @@ -36298,7 +37596,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", @@ -36368,7 +37665,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", @@ -36394,7 +37690,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", @@ -36420,7 +37715,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", @@ -36446,7 +37740,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", @@ -36472,7 +37765,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", @@ -36610,10 +37902,11 @@ "attack_level": "1" }, { - "examine": "A bony ghost.", + "examine": "A boney ghost.", "melee_animation": "422", "range_animation": "422", - "attack_speed": "6", + "combat_audio": "924,926,925", + "attack_speed": "4", "defence_animation": "404", "slayer_exp": "60", "magic_animation": "422", @@ -36630,10 +37923,11 @@ "attack_level": "50" }, { - "examine": "A bony ghost.", + "examine": "A boney ghost.", "melee_animation": "422", "range_animation": "422", - "attack_speed": "6", + "combat_audio": "924,926,925", + "attack_speed": "4", "defence_animation": "404", "slayer_exp": "60", "magic_animation": "422", @@ -36650,10 +37944,11 @@ "attack_level": "50" }, { - "examine": "A bony ghost.", + "examine": "A boney ghost.", "melee_animation": "422", "range_animation": "422", - "attack_speed": "6", + "combat_audio": "924,926,925", + "attack_speed": "4", "defence_animation": "404", "slayer_exp": "60", "magic_animation": "422", @@ -36689,7 +37984,6 @@ }, { "examine": "Eeek! A ghost!", - "slayer_task": "36", "melee_animation": "5532", "range_animation": "0", "combat_audio": "436,439,438", @@ -36712,9 +38006,9 @@ }, { "examine": "I don't think insect repellent will work.", - "slayer_task": "34", "melee_animation": "1184", "range_animation": "1184", + "combat_audio": "571,573,572", "attack_speed": "3", "respawn_delay": "30", "defence_animation": "1186", @@ -36737,6 +38031,7 @@ "examine": "I don't think insect repellent will work.", "melee_animation": "1184", "range_animation": "1184", + "combat_audio": "571,573,572", "attack_speed": "3", "respawn_delay": "30", "defence_animation": "1186", @@ -36758,6 +38053,7 @@ "examine": "I don't think insect repellent will work.", "melee_animation": "1184", "range_animation": "1184", + "combat_audio": "571,573,572", "attack_speed": "3", "respawn_delay": "30", "defence_animation": "1186", @@ -36776,10 +38072,10 @@ "attack_level": "100" }, { - "examine": "The walking dead.", + "examine": "Dead man walking.", "melee_animation": "5568", "range_animation": "5568", - "combat_audio": "931,923,922", + "combat_audio": "918,923,922", "attack_speed": "5", "respawn_delay": "30", "defence_animation": "5567", @@ -36792,15 +38088,16 @@ "lifepoints": "23", "strength_level": "1", "id": "4392", + "aggressive": "true", "bonuses": "15,25,25,25,25,25,25,15,15,25,25,25,25,25,25", "range_level": "1", "attack_level": "24" }, { - "examine": "Dead (wo)man walking.", + "examine": "Dead man walking.", "melee_animation": "5578", "range_animation": "5578", - "combat_audio": "931,923,922", + "combat_audio": "918,923,922", "attack_speed": "5", "respawn_delay": "30", "defence_animation": "5579", @@ -36819,10 +38116,10 @@ "attack_level": "35" }, { - "examine": "Could his name be \"Lurch\"?", + "examine": "Dead man walking.", "melee_animation": "5578", "range_animation": "5578", - "combat_audio": "931,923,922", + "combat_audio": "918,923,922", "attack_speed": "5", "respawn_delay": "30", "defence_animation": "5579", @@ -36835,16 +38132,19 @@ "lifepoints": "40", "strength_level": "1", "id": "4394", + "aggressive": "true", "bonuses": "35,45,45,45,45,45,45,35,35,45,45,45,45,45,45", "range_level": "1", "attack_level": "47" }, { "examine": "Overgrown vermin.", - "slayer_task": "67", "combat_audio": "703,705,704", + "melee_animation": "4933", "attack_speed": "5", + "defence_animation": "4934", "weakness": "9", + "death_animation": "4935", "name": "Giant rat", "defence_level": "23", "safespot": null, @@ -36860,7 +38160,7 @@ "examine": "A popular dwarven delicacy.", "melee_animation": "2705", "range_animation": "2705", - "combat_audio": "3102,3104,3103", + "combat_audio": "710,713,711", "attack_speed": "5", "defence_animation": "2706", "magic_animation": "2705", @@ -36877,6 +38177,7 @@ { "examine": "Big Cow-like... But cows don't have serpent tails!", "melee_animation": "4271", + "combat_audio": "511,330,329", "attack_speed": "5", "magic_level": "38", "spell_id": "7", @@ -36900,6 +38201,7 @@ { "examine": "Big Cow-like... But cows don't have serpent tails!", "melee_animation": "4271", + "combat_audio": "511,330,329", "attack_speed": "5", "magic_level": "38", "spell_id": "7", @@ -36923,6 +38225,7 @@ { "examine": "Big Cow-like... But cows don't have serpent tails!", "melee_animation": "4271", + "combat_audio": "511,330,329", "attack_speed": "5", "magic_level": "45", "spell_id": "7", @@ -36964,6 +38267,7 @@ }, { "melee_animation": "6249", + "combat_audio": "3604,3609,3608", "respawn_delay": "60", "defence_animation": "6250", "death_animation": "6251", @@ -36973,7 +38277,7 @@ "lifepoints": "22", "strength_level": "1", "id": "4401", - "aggressive": "true", + "aggressive": "false", "range_level": "1", "attack_level": "1" }, @@ -37021,7 +38325,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", @@ -37096,7 +38399,7 @@ "examine": "An ugly green creature.", "melee_animation": "6185", "range_animation": "6185", - "combat_audio": "469,472,471", + "combat_audio": "3520,472,471", "attack_speed": "5", "defence_animation": "6183", "slayer_exp": "5", @@ -37117,7 +38420,7 @@ "examine": "An ugly green creature.", "melee_animation": "6185", "range_animation": "6185", - "combat_audio": "469,472,471", + "combat_audio": "3520,472,471", "attack_speed": "5", "defence_animation": "6183", "slayer_exp": "16", @@ -37138,7 +38441,7 @@ "examine": "An ugly green creature.", "melee_animation": "6185", "range_animation": "6185", - "combat_audio": "469,472,471", + "combat_audio": "3520,472,471", "attack_speed": "5", "defence_animation": "6183", "slayer_exp": "7", @@ -37159,7 +38462,7 @@ "examine": "An ugly green creature.", "melee_animation": "6185", "range_animation": "6185", - "combat_audio": "469,472,471", + "combat_audio": "3520,472,471", "attack_speed": "5", "defence_animation": "6183", "magic_animation": "6185", @@ -37179,7 +38482,7 @@ "examine": "An ugly green creature.", "melee_animation": "6185", "range_animation": "6185", - "combat_audio": "469,472,471", + "combat_audio": "3520,472,471", "attack_speed": "5", "defence_animation": "6183", "magic_animation": "6185", @@ -37199,7 +38502,7 @@ "examine": "An ugly green creature.", "melee_animation": "6185", "range_animation": "6185", - "combat_audio": "469,472,471", + "combat_audio": "3520,472,471", "attack_speed": "5", "defence_animation": "6183", "magic_animation": "6185", @@ -37259,10 +38562,9 @@ }, { "examine": "A popular dwarven delicacy.", - "slayer_task": "67", "melee_animation": "2705", "range_animation": "2705", - "combat_audio": "3102,3104,3103", + "combat_audio": "710,713,711", "attack_speed": "4", "magic_level": "1", "respawn_delay": "3", @@ -37312,6 +38614,11 @@ "range_level": "1", "attack_level": "1" }, + { + "examine": "A healing fairy.", + "name": "Fairy Nuff", + "id": "4434" + }, { "examine": "Horseplay.", "melee_animation": "0", @@ -38093,7 +39400,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", @@ -39116,7 +40422,6 @@ }, { "examine": "Young but still dangerous.", - "slayer_task": "11", "melee_animation": "25", "range_animation": "25", "combat_audio": "405,407,406", @@ -39141,7 +40446,6 @@ }, { "examine": "Young but still dangerous.", - "slayer_task": "11", "combat_audio": "405,407,406", "melee_animation": "25", "attack_speed": "4", @@ -39193,7 +40497,6 @@ }, { "examine": "A big powerful dragon.", - "slayer_task": "68", "melee_animation": "80", "range_animation": "80", "combat_audio": "408,410,409", @@ -39219,7 +40522,6 @@ }, { "examine": "A big powerful dragon.", - "slayer_task": "68", "melee_animation": "80", "range_animation": "80", "combat_audio": "408,410,409", @@ -39245,7 +40547,6 @@ }, { "examine": "A big powerful dragon.", - "slayer_task": "68", "melee_animation": "80", "range_animation": "80", "combat_audio": "408,410,409", @@ -39271,7 +40572,6 @@ }, { "examine": "A big powerful dragon.", - "slayer_task": "68", "melee_animation": "80", "range_animation": "80", "combat_audio": "408,410,409", @@ -39297,7 +40597,6 @@ }, { "examine": "A fierce dragon with black scales!", - "slayer_task": "9", "melee_animation": "80", "range_animation": "80", "combat_audio": "408,410,409", @@ -39323,7 +40622,6 @@ }, { "examine": "A fierce dragon with black scales!", - "slayer_task": "9", "melee_animation": "80", "range_animation": "80", "combat_audio": "408,410,409", @@ -39349,7 +40647,6 @@ }, { "examine": "A fierce dragon with black scales!", - "slayer_task": "9", "melee_animation": "80", "range_animation": "80", "combat_audio": "408,410,409", @@ -39375,7 +40672,6 @@ }, { "examine": "A fierce dragon with black scales!", - "slayer_task": "9", "melee_animation": "80", "range_animation": "80", "combat_audio": "408,410,409", @@ -39401,7 +40697,6 @@ }, { "examine": "Must be related to Elvarg.", - "slayer_task": "41", "melee_animation": "80", "range_animation": "80", "combat_audio": "408,410,409", @@ -39427,7 +40722,6 @@ }, { "examine": "Must be related to Elvarg.", - "slayer_task": "41", "melee_animation": "80", "range_animation": "80", "combat_audio": "408,410,409", @@ -39453,7 +40747,6 @@ }, { "examine": "Must be related to Elvarg.", - "slayer_task": "41", "melee_animation": "80", "range_animation": "80", "combat_audio": "408,410,409", @@ -39479,7 +40772,6 @@ }, { "examine": "Must be related to Elvarg.", - "slayer_task": "41", "melee_animation": "80", "range_animation": "80", "combat_audio": "408,410,409", @@ -39505,7 +40797,6 @@ }, { "examine": "A mother dragon.", - "slayer_task": "11", "melee_animation": "80", "range_animation": "80", "combat_audio": "408,410,409", @@ -39531,7 +40822,6 @@ }, { "examine": "A mother dragon.", - "slayer_task": "11", "melee_animation": "80", "range_animation": "80", "combat_audio": "408,410,409", @@ -39557,7 +40847,6 @@ }, { "examine": "A mother dragon.", - "slayer_task": "11", "melee_animation": "80", "range_animation": "80", "combat_audio": "408,410,409", @@ -39583,7 +40872,6 @@ }, { "examine": "A mother dragon.", - "slayer_task": "11", "melee_animation": "80", "range_animation": "80", "combat_audio": "408,410,409", @@ -39609,7 +40897,6 @@ }, { "examine": "He's got icicles in his beard.", - "slayer_task": "47", "melee_animation": "4672", "range_animation": "0", "combat_audio": "448,451,450", @@ -39634,7 +40921,6 @@ }, { "examine": "He's got icicles in his beard.", - "slayer_task": "47", "melee_animation": "4672", "range_animation": "0", "combat_audio": "448,451,450", @@ -39659,7 +40945,6 @@ }, { "examine": "He's got icicles in his beard.", - "slayer_task": "47", "melee_animation": "4672", "range_animation": "0", "combat_audio": "448,451,450", @@ -39684,7 +40969,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", @@ -39709,7 +40993,6 @@ }, { "examine": "A very large foe.", - "slayer_task": "44", "melee_animation": "4652", "range_animation": "4652", "combat_audio": "448,451,450", @@ -39734,7 +41017,6 @@ }, { "examine": "A very large foe.", - "slayer_task": "44", "melee_animation": "4652", "range_animation": "4652", "combat_audio": "448,451,450", @@ -39759,7 +41041,6 @@ }, { "examine": "A very large foe.", - "slayer_task": "44", "melee_animation": "4652", "range_animation": "4652", "combat_audio": "448,451,450", @@ -39784,7 +41065,6 @@ }, { "examine": "A very large foe.", - "slayer_task": "44", "melee_animation": "4652", "range_animation": "4652", "combat_audio": "448,451,450", @@ -39809,7 +41089,6 @@ }, { "examine": "A very large foe.", - "slayer_task": "44", "melee_animation": "4652", "range_animation": "4652", "combat_audio": "448,451,450", @@ -39834,7 +41113,6 @@ }, { "examine": "Lesser, but still pretty big.", - "slayer_task": "56", "melee_animation": "4630", "range_animation": "4630", "combat_audio": "400,404,403", @@ -39859,7 +41137,6 @@ }, { "examine": "Lesser, but still pretty big.", - "slayer_task": "56", "melee_animation": "4630", "range_animation": "4630", "combat_audio": "400,404,403", @@ -39884,7 +41161,6 @@ }, { "examine": "Lesser, but still pretty big.", - "slayer_task": "56", "melee_animation": "4630", "range_animation": "4630", "combat_audio": "400,404,403", @@ -39909,7 +41185,6 @@ }, { "examine": "Lesser, but still pretty big.", - "slayer_task": "56", "melee_animation": "4630", "range_animation": "4630", "combat_audio": "400,404,403", @@ -39934,7 +41209,6 @@ }, { "examine": "Big, red, and incredibly evil.", - "slayer_task": "40", "melee_animation": "64", "range_animation": "64", "combat_audio": "400,404,403", @@ -39960,7 +41234,6 @@ }, { "examine": "Big, red, and incredibly evil.", - "slayer_task": "40", "melee_animation": "64", "range_animation": "64", "combat_audio": "400,404,403", @@ -39986,7 +41259,6 @@ }, { "examine": "Big, red, and incredibly evil.", - "slayer_task": "40", "melee_animation": "64", "range_animation": "64", "combat_audio": "400,404,403", @@ -40012,7 +41284,6 @@ }, { "examine": "Big, red, and incredibly evil.", - "slayer_task": "40", "melee_animation": "64", "range_animation": "64", "combat_audio": "400,404,403", @@ -40038,7 +41309,6 @@ }, { "examine": "A big, scary, jet-black demon.", - "slayer_task": "8", "melee_animation": "64", "range_animation": "64", "combat_audio": "397,399,398", @@ -40063,7 +41333,6 @@ }, { "examine": "A big, scary, jet-black demon.", - "slayer_task": "8", "melee_animation": "64", "range_animation": "64", "combat_audio": "397,399,398", @@ -40088,7 +41357,6 @@ }, { "examine": "A big, scary, jet-black demon.", - "slayer_task": "8", "melee_animation": "64", "range_animation": "64", "combat_audio": "397,399,398", @@ -40113,7 +41381,6 @@ }, { "examine": "A big, scary, jet-black demon.", - "slayer_task": "8", "melee_animation": "64", "range_animation": "64", "combat_audio": "397,399,398", @@ -40138,7 +41405,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", @@ -40925,7 +42191,6 @@ }, { "examine": "An enraged vampyre!", - "slayer_task": "86", "melee_animation": "6016", "range_animation": "0", "defence_animation": "0", @@ -41808,7 +43073,6 @@ }, { "examine": "An extremely vicious lobster.", - "slayer_task": "71", "melee_animation": "6265", "range_animation": "0", "respawn_delay": "60", @@ -42040,7 +43304,6 @@ }, { "examine": "A nasty overgrown rodent.", - "slayer_task": "67", "melee_animation": "4933", "range_animation": "4933", "combat_audio": "703,705,704", @@ -42064,7 +43327,6 @@ }, { "examine": "A nasty overgrown rodent.", - "slayer_task": "67", "melee_animation": "4933", "range_animation": "4933", "combat_audio": "703,705,704", @@ -42088,7 +43350,6 @@ }, { "examine": "Overgrown vermin.", - "slayer_task": "67", "melee_animation": "4933", "range_animation": "0", "combat_audio": "703,705,704", @@ -42110,7 +43371,6 @@ }, { "examine": "Overgrown vermin.", - "slayer_task": "67", "melee_animation": "4933", "range_animation": "0", "combat_audio": "703,705,704", @@ -42132,7 +43392,6 @@ }, { "examine": "Overgrown vermin.", - "slayer_task": "67", "melee_animation": "4933", "range_animation": "0", "combat_audio": "703,705,704", @@ -42169,7 +43428,6 @@ }, { "examine": "Overgrown vermin.", - "slayer_task": "67", "melee_animation": "4933", "range_animation": "0", "combat_audio": "703,705,704", @@ -42191,7 +43449,6 @@ }, { "examine": "Overgrown vermin.", - "slayer_task": "67", "melee_animation": "4933", "range_animation": "0", "combat_audio": "703,705,704", @@ -42213,7 +43470,6 @@ }, { "examine": "Overgrown vermin.", - "slayer_task": "67", "melee_animation": "0", "range_animation": "0", "combat_audio": "703,705,704", @@ -42235,7 +43491,6 @@ }, { "examine": "Overgrown vermin.", - "slayer_task": "67", "melee_animation": "0", "range_animation": "0", "combat_audio": "703,705,704", @@ -42353,7 +43608,6 @@ }, { "examine": "A dirty rat.", - "slayer_task": "67", "melee_animation": "4933", "range_animation": "0", "combat_audio": "703,705,704", @@ -42375,7 +43629,6 @@ }, { "examine": "A dirty rat.", - "slayer_task": "67", "melee_animation": "4933", "range_animation": "0", "combat_audio": "703,705,704", @@ -42419,7 +43672,6 @@ }, { "examine": "He looks a little on the cross side!", - "slayer_task": "67", "melee_animation": "4933", "range_animation": "0", "combat_audio": "703,705,704", @@ -42438,7 +43690,6 @@ }, { "examine": "It's one of Iban's pet vermin.", - "slayer_task": "67", "range_animation": "0", "combat_audio": "703,705,704", "defence_animation": "0", @@ -42490,7 +43741,10 @@ { "examine": "Overgrown vermin.", "combat_audio": "703,705,704", + "melee_animation": "4933", "attack_speed": "5", + "defence_animation": "4934", + "death_animation": "4935", "name": "Giant rat", "defence_level": "23", "safespot": null, @@ -42504,10 +43758,12 @@ }, { "examine": "Overgrown vermin.", - "slayer_task": "67", "combat_audio": "703,705,704", + "melee_animation": "4933", "attack_speed": "5", + "defence_animation": "4934", "weakness": "9", + "death_animation": "4935", "name": "Giant rat", "defence_level": "23", "safespot": null, @@ -42521,7 +43777,6 @@ }, { "examine": "Trollish.", - "slayer_task": "83", "melee_animation": "0", "range_animation": "0", "defence_animation": "0", @@ -42570,7 +43825,6 @@ }, { "examine": "A mountain-dwelling bird. Cute", - "slayer_task": "7", "melee_animation": "5031", "range_animation": "0", "respawn_delay": "60", @@ -42590,7 +43844,6 @@ }, { "examine": "A very", - "slayer_task": "7", "melee_animation": "5024", "range_animation": "5025", "magic_level": "55", @@ -43203,7 +44456,6 @@ }, { "examine": "It blends in very well with its surroundings.", - "slayer_task": "7", "melee_animation": "0", "range_animation": "0", "defence_animation": "0", @@ -43220,7 +44472,6 @@ }, { "examine": "This bird obviously doesn't believe in subtlety.", - "slayer_task": "7", "melee_animation": "0", "range_animation": "0", "defence_animation": "0", @@ -43237,7 +44488,6 @@ }, { "examine": "Best served ice cold.", - "slayer_task": "7", "melee_animation": "0", "range_animation": "0", "defence_animation": "0", @@ -43254,7 +44504,6 @@ }, { "examine": "Actually", - "slayer_task": "7", "melee_animation": "0", "range_animation": "0", "defence_animation": "0", @@ -43271,7 +44520,6 @@ }, { "examine": "Nothing much to get in a flap about.", - "slayer_task": "7", "melee_animation": "0", "range_animation": "0", "defence_animation": "0", @@ -43900,6 +45148,11 @@ "range_level": "1", "attack_level": "1" }, + { + "examine": "White and fluffy", + "name": "Sheep", + "id": "5164" + }, { "examine": "This goat belongs to the mountain camp people.", "melee_animation": "0", @@ -43933,41 +45186,46 @@ "attack_level": "1" }, { - "examine": "White and shaggy.", + "examine": "White and fluffy.", "melee_animation": "5338", + "combat_audio": "757,759,758", "attack_speed": "5", "respawn_delay": "60", "defence_animation": "5337", + "weakness": "9", "death_animation": "5336", "name": "Ram", "defence_level": "1", "safespot": null, - "lifepoints": "4", + "lifepoints": "8", "strength_level": "1", "id": "5168", "range_level": "1", "attack_level": "1" }, { - "examine": "White and shaggy.", + "examine": "White and fluffy.", "melee_animation": "5338", + "combat_audio": "757,759,758", "attack_speed": "5", "respawn_delay": "60", "defence_animation": "5337", + "weakness": "9", "death_animation": "5336", "name": "Ram", "defence_level": "1", "safespot": null, - "lifepoints": "4", + "lifepoints": "8", "strength_level": "1", "id": "5169", "range_level": "1", "attack_level": "1" }, { - "examine": "White and shaggy.", + "examine": "White and fluffy.", "melee_animation": "5338", "range_animation": "0", + "combat_audio": "757,759,758", "attack_speed": "5", "respawn_delay": "60", "defence_animation": "5337", @@ -43975,13 +45233,13 @@ "magic_animation": "0", "death_animation": "5336", "name": "Ram", - "defence_level": "2", + "defence_level": "1", "safespot": null, - "lifepoints": "2", - "strength_level": "2", + "lifepoints": "8", + "strength_level": "1", "id": "5170", "range_level": "1", - "attack_level": "2" + "attack_level": "1" }, { "examine": "This beast doesn't need climbing boots.", @@ -44033,7 +45291,6 @@ }, { "examine": "Seems intelligent... for an ogre.", - "slayer_task": "64", "combat_style": "2", "range_animation": "0", "respawn_delay": "60", @@ -44052,7 +45309,6 @@ }, { "examine": "A mother dragon.", - "slayer_task": "11", "melee_animation": "80", "range_animation": "80", "combat_audio": "408,410,409", @@ -44078,7 +45334,6 @@ }, { "examine": "Seems intelligent... for an ogre.", - "slayer_task": "64", "range_animation": "0", "defence_animation": "0", "weakness": "7", @@ -44094,7 +45349,6 @@ }, { "examine": "Seems intelligent... for an ogre.", - "slayer_task": "64", "combat_style": "2", "range_animation": "0", "respawn_delay": "60", @@ -44113,7 +45367,6 @@ }, { "examine": "Seems intelligent... for an ogre.", - "slayer_task": "64", "combat_style": "2", "range_animation": "0", "respawn_delay": "60", @@ -44357,7 +45610,6 @@ }, { "examine": "Part scarab, part man.", - "slayer_task": "70", "combat_style": "2", "melee_animation": "7615", "range_animation": "0", @@ -44379,7 +45631,6 @@ }, { "examine": "A mounted lancer.", - "slayer_task": "70", "melee_animation": "7584", "range_animation": "0", "defence_animation": "0", @@ -44398,7 +45649,6 @@ }, { "examine": "A mounted archer.", - "slayer_task": "70", "melee_animation": "5451", "range_animation": "5451", "defence_animation": "0", @@ -44417,7 +45667,6 @@ }, { "examine": "A huge scarab beast.", - "slayer_task": "70", "melee_animation": "5457", "range_animation": "0", "magic_level": "62", @@ -45223,6 +46472,7 @@ "attack_level": "1" }, { + "examine": "", "melee_animation": "5568", "combat_audio": "931,923,922", "respawn_delay": "60", @@ -45258,7 +46508,6 @@ }, { "examine": "Eeek! A ghost!", - "slayer_task": "36", "melee_animation": "5532", "range_animation": "0", "combat_audio": "436,439,438", @@ -45301,7 +46550,6 @@ }, { "examine": "Eeek! A ghost!", - "slayer_task": "36", "melee_animation": "5532", "range_animation": "0", "combat_audio": "436,439,438", @@ -45325,7 +46573,6 @@ }, { "examine": "Eeek! A ghost!", - "slayer_task": "36", "melee_animation": "5532", "range_animation": "0", "combat_audio": "436,439,438", @@ -45368,7 +46615,6 @@ }, { "examine": "Eeek! A ghost!", - "slayer_task": "36", "melee_animation": "5532", "range_animation": "0", "combat_audio": "436,439,438", @@ -45392,7 +46638,6 @@ }, { "examine": "Eeek! A ghost!", - "slayer_task": "36", "melee_animation": "5532", "range_animation": "0", "combat_audio": "436,439,438", @@ -45492,7 +46737,6 @@ }, { "examine": "A minion of Rashiliyia.", - "slayer_task": "75", "melee_animation": "5485", "range_animation": "0", "attack_speed": "5", @@ -45513,7 +46757,6 @@ }, { "examine": "A minion of Rashiliyia.", - "slayer_task": "75", "melee_animation": "5487", "range_animation": "0", "defence_animation": "0", @@ -45531,7 +46774,6 @@ }, { "examine": "The animated dead; one of Rashiliyia's minions.", - "slayer_task": "93", "melee_animation": "5568", "range_animation": "0", "attack_speed": "5", @@ -45552,7 +46794,6 @@ }, { "examine": "The animated dead; one of Rashiliyia's minions.", - "slayer_task": "93", "melee_animation": "5568", "range_animation": "0", "attack_speed": "5", @@ -45573,7 +46814,6 @@ }, { "examine": "The animated dead; one of Rashiliyia's minions.", - "slayer_task": "93", "melee_animation": "5571", "range_animation": "0", "attack_speed": "5", @@ -45594,7 +46834,6 @@ }, { "examine": "The animated dead; one of Rashiliyia's minions.", - "slayer_task": "93", "melee_animation": "5571", "range_animation": "0", "attack_speed": "5", @@ -45615,7 +46854,6 @@ }, { "examine": "A giant skeleton.", - "slayer_task": "75", "melee_animation": "5499", "range_animation": "0", "defence_animation": "0", @@ -45633,7 +46871,6 @@ }, { "examine": "A fiendish embodiment of water.", - "slayer_task": "90", "combat_style": "1", "melee_animation": "1582", "range_animation": "1582", @@ -45659,7 +46896,6 @@ }, { "examine": "It appears to be intelligent and savage.", - "slayer_task": "41", "melee_animation": "91", "range_animation": "91", "combat_audio": "408,410,409", @@ -45684,7 +46920,6 @@ }, { "examine": "Experimenting with mithril gone bad!", - "slayer_task": "57", "melee_animation": "91", "range_animation": "91", "combat_audio": "408,410,409", @@ -45842,10 +47077,10 @@ "attack_level": "80" }, { - "examine": "Examine not added", + "examine": "Dead man walking.", "melee_animation": "5568", "range_animation": "5568", - "combat_audio": "931,923,922", + "combat_audio": "918,923,922", "attack_speed": "5", "respawn_delay": "30", "defence_animation": "5567", @@ -45858,15 +47093,16 @@ "lifepoints": "23", "strength_level": "1", "id": "5375", + "aggressive": "true", "bonuses": "15,25,25,25,25,25,25,15,15,25,25,25,25,25,25", "range_level": "1", "attack_level": "25" }, { - "examine": "Examine not added", + "examine": "Dead man walking.", "melee_animation": "5568", "range_animation": "5568", - "combat_audio": "931,923,922", + "combat_audio": "918,923,922", "attack_speed": "5", "respawn_delay": "30", "defence_animation": "5567", @@ -45879,15 +47115,16 @@ "lifepoints": "23", "strength_level": "1", "id": "5376", + "aggressive": "true", "bonuses": "15,25,25,25,25,25,25,15,15,25,25,25,25,25,25", "range_level": "1", "attack_level": "25" }, { - "examine": "Examine not added", + "examine": "Dead man walking.", "melee_animation": "5578", "range_animation": "5578", - "combat_audio": "931,923,922", + "combat_audio": "918,923,922", "attack_speed": "5", "respawn_delay": "30", "defence_animation": "5579", @@ -45900,15 +47137,16 @@ "lifepoints": "30", "strength_level": "1", "id": "5377", + "aggressive": "true", "bonuses": "15,25,28,28,25,28,28,15,15,28,25,25,25,25,25", "range_level": "1", "attack_level": "38" }, { - "examine": "Examine not added", + "examine": "Dead man walking.", "melee_animation": "5578", "range_animation": "5578", - "combat_audio": "931,923,922", + "combat_audio": "918,923,922", "attack_speed": "5", "respawn_delay": "30", "defence_animation": "5579", @@ -45921,15 +47159,16 @@ "lifepoints": "30", "strength_level": "1", "id": "5378", + "aggressive": "true", "bonuses": "15,25,28,28,25,28,28,15,15,28,25,25,25,25,25", "range_level": "1", "attack_level": "38" }, { - "examine": "Examine not added", + "examine": "The walking dead.", "melee_animation": "5578", "range_animation": "5578", - "combat_audio": "931,923,922", + "combat_audio": "918,923,922", "attack_speed": "5", "respawn_delay": "30", "defence_animation": "5579", @@ -45942,15 +47181,16 @@ "lifepoints": "40", "strength_level": "1", "id": "5379", + "aggressive": "true", "bonuses": "35,45,45,45,45,45,45,35,35,45,45,45,45,45,45", "range_level": "1", "attack_level": "47" }, { - "examine": "Examine not added", + "examine": "The walking dead.", "melee_animation": "5578", "range_animation": "5578", - "combat_audio": "931,923,922", + "combat_audio": "918,923,922", "attack_speed": "5", "respawn_delay": "30", "defence_animation": "5579", @@ -45963,6 +47203,7 @@ "lifepoints": "40", "strength_level": "1", "id": "5380", + "aggressive": "true", "bonuses": "35,45,45,45,45,45,45,35,35,45,45,45,45,45,45", "range_level": "1", "attack_level": "47" @@ -46271,7 +47512,6 @@ }, { "examine": "A terrifying dog beast.", - "slayer_task": "82", "melee_animation": "5625", "range_animation": "0", "attack_speed": "5", @@ -46408,7 +47648,6 @@ }, { "examine": "Sturdy cold being.", - "slayer_task": "48", "melee_animation": "5724", "range_animation": "5725", "respawn_delay": "60", @@ -46428,7 +47667,6 @@ }, { "examine": "Sturdy cold being.", - "slayer_task": "48", "melee_animation": "5724", "range_animation": "0", "respawn_delay": "60", @@ -46448,7 +47686,6 @@ }, { "examine": "Sturdy cold being.", - "slayer_task": "48", "melee_animation": "5724", "range_animation": "0", "respawn_delay": "60", @@ -46468,7 +47705,6 @@ }, { "examine": "Sturdy cold being.", - "slayer_task": "48", "melee_animation": "5724", "range_animation": "0", "respawn_delay": "60", @@ -46488,7 +47724,6 @@ }, { "examine": "An impressive-looking troll.", - "slayer_task": "83", "melee_animation": "5374", "range_animation": "0", "magic_level": "65", @@ -47380,7 +48615,6 @@ }, { "examine": "Bedside manner is a little lacking", - "slayer_task": "93", "melee_animation": "5643", "range_animation": "0", "defence_animation": "0", @@ -47398,7 +48632,6 @@ }, { "examine": "I hope his hands don't shake.", - "slayer_task": "93", "melee_animation": "5643", "range_animation": "0", "defence_animation": "0", @@ -47416,7 +48649,6 @@ }, { "examine": "Aaaarg", - "slayer_task": "93", "melee_animation": "5647", "range_animation": "0", "combat_audio": "703,705,704", @@ -47453,7 +48685,6 @@ }, { "examine": "Aaaarg", - "slayer_task": "93", "melee_animation": "5647", "range_animation": "0", "combat_audio": "703,705,704", @@ -47490,7 +48721,6 @@ }, { "examine": "Aaaarg", - "slayer_task": "93", "melee_animation": "5647", "range_animation": "0", "combat_audio": "703,705,704", @@ -47511,7 +48741,6 @@ }, { "examine": "Aaaarg", - "slayer_task": "93", "melee_animation": "5647", "range_animation": "0", "combat_audio": "703,705,704", @@ -47532,7 +48761,6 @@ }, { "examine": "Aaaarg", - "slayer_task": "93", "melee_animation": "5647", "range_animation": "0", "combat_audio": "703,705,704", @@ -47569,7 +48797,6 @@ }, { "examine": "Aaaarg", - "slayer_task": "93", "melee_animation": "5651", "range_animation": "0", "combat_audio": "703,705,704", @@ -47590,7 +48817,6 @@ }, { "examine": "Aaaarg", - "slayer_task": "93", "melee_animation": "5651", "range_animation": "0", "combat_audio": "703,705,704", @@ -47611,7 +48837,6 @@ }, { "examine": "Aaaarg", - "slayer_task": "93", "melee_animation": "5651", "range_animation": "0", "combat_audio": "703,705,704", @@ -47632,7 +48857,6 @@ }, { "examine": "Aaaarg", - "slayer_task": "93", "melee_animation": "5651", "range_animation": "0", "combat_audio": "703,705,704", @@ -47653,7 +48877,6 @@ }, { "examine": "Aaaarg", - "slayer_task": "93", "melee_animation": "5651", "range_animation": "0", "combat_audio": "703,705,704", @@ -47674,7 +48897,6 @@ }, { "examine": "Aaaarg", - "slayer_task": "93", "melee_animation": "5880", "range_animation": "0", "combat_audio": "703,705,704", @@ -47695,7 +48917,6 @@ }, { "examine": "Aaaarg", - "slayer_task": "93", "melee_animation": "5880", "range_animation": "0", "combat_audio": "703,705,704", @@ -47716,7 +48937,6 @@ }, { "examine": "Aaaarg", - "slayer_task": "93", "melee_animation": "5880", "range_animation": "0", "combat_audio": "703,705,704", @@ -47737,7 +48957,6 @@ }, { "examine": "Aaaarg", - "slayer_task": "93", "melee_animation": "5880", "range_animation": "0", "combat_audio": "703,705,704", @@ -47758,7 +48977,6 @@ }, { "examine": "Aaaarg", - "slayer_task": "93", "melee_animation": "5880", "range_animation": "0", "combat_audio": "703,705,704", @@ -47779,7 +48997,6 @@ }, { "examine": "Aaaarg", - "slayer_task": "93", "melee_animation": "5884", "range_animation": "0", "combat_audio": "703,705,704", @@ -47800,7 +49017,6 @@ }, { "examine": "Aaaarg", - "slayer_task": "93", "melee_animation": "5884", "range_animation": "0", "combat_audio": "703,705,704", @@ -47837,7 +49053,6 @@ }, { "examine": "Aaaarg", - "slayer_task": "93", "melee_animation": "5884", "range_animation": "0", "combat_audio": "703,705,704", @@ -47874,7 +49089,6 @@ }, { "examine": "Aaaarg", - "slayer_task": "93", "melee_animation": "5884", "range_animation": "0", "combat_audio": "703,705,704", @@ -47911,7 +49125,6 @@ }, { "examine": "Aaaarg", - "slayer_task": "93", "melee_animation": "5884", "range_animation": "0", "combat_audio": "703,705,704", @@ -47948,7 +49161,6 @@ }, { "examine": "Aaaarg", - "slayer_task": "93", "melee_animation": "5889", "range_animation": "0", "combat_audio": "703,705,704", @@ -47985,7 +49197,6 @@ }, { "examine": "Aaaarg", - "slayer_task": "93", "melee_animation": "5889", "range_animation": "0", "combat_audio": "703,705,704", @@ -48006,7 +49217,6 @@ }, { "examine": "Aaaarg", - "slayer_task": "93", "melee_animation": "5889", "range_animation": "0", "combat_audio": "703,705,704", @@ -48043,7 +49253,6 @@ }, { "examine": "Aaaarg", - "slayer_task": "93", "melee_animation": "5889", "range_animation": "0", "combat_audio": "703,705,704", @@ -48080,7 +49289,6 @@ }, { "examine": "Aaaarg", - "slayer_task": "93", "melee_animation": "5889", "range_animation": "0", "combat_audio": "703,705,704", @@ -48117,7 +49325,6 @@ }, { "examine": "Aaaarg", - "slayer_task": "93", "melee_animation": "5889", "range_animation": "0", "combat_audio": "703,705,704", @@ -48174,10 +49381,10 @@ }, { "examine": "A big", - "slayer_task": "15", "melee_animation": "6079", "range_animation": "0", - "attack_speed": "5", + "attack_speed": "4", + "magic_level": "1", "respawn_delay": "60", "defence_animation": "0", "weakness": "2", @@ -48185,13 +49392,14 @@ "magic_animation": "0", "death_animation": "6081", "name": "Cave bug", - "defence_level": "9", + "defence_level": "84", "safespot": null, "lifepoints": "93", - "strength_level": "1", + "strength_level": "80", "id": "5750", - "range_level": "9", - "attack_level": "1" + "bonuses": "0,0,0,0,0,72,59,35,25,95,0,0,0,0,0", + "range_level": "1", + "attack_level": "82" }, { "examine": "A strange mole-like being.", @@ -48606,7 +49814,6 @@ }, { "examine": "He keeps order in the city.", - "slayer_task": "38", "melee_animation": "6007", "range_animation": "0", "combat_audio": "511,513,512", @@ -48626,7 +49833,6 @@ }, { "examine": "He keeps order in the city.", - "slayer_task": "38", "melee_animation": "6007", "range_animation": "0", "combat_audio": "511,513,512", @@ -49301,7 +50507,6 @@ }, { "examine": "A goblin with big", - "slayer_task": "38", "melee_animation": "0", "range_animation": "0", "combat_audio": "469,472,471", @@ -49319,7 +50524,6 @@ }, { "examine": "A goblin with big", - "slayer_task": "38", "melee_animation": "0", "range_animation": "0", "combat_audio": "469,472,471", @@ -49370,7 +50574,6 @@ }, { "examine": "He sells tickets to Keldagrim.", - "slayer_task": "38", "melee_animation": "0", "range_animation": "0", "combat_audio": "469,472,471", @@ -49439,6 +50642,7 @@ }, { "slayer_exp": "16", + "examine": "A dwarf from Keldagrim.", "name": "Dwarf", "defence_level": "1", "safespot": null, @@ -49450,6 +50654,7 @@ }, { "slayer_exp": "16", + "examine": "A dwarf from Keldagrim.", "name": "Dwarf", "defence_level": "1", "safespot": null, @@ -49461,6 +50666,7 @@ }, { "slayer_exp": "16", + "examine": "A dwarf from Keldagrim.", "name": "Dwarf", "defence_level": "1", "safespot": null, @@ -49708,7 +50914,7 @@ "examine": "He tries to keep order around here. His bizarre uniform isn't helping.", "melee_animation": "6489", "range_animation": "0", - "combat_audio": "511,513,512", + "combat_audio": "2548,1979,512", "attack_speed": "5", "respawn_delay": "60", "defence_animation": "0", @@ -49729,7 +50935,7 @@ "examine": "He tries to keep order around here. His bizarre uniform isn't helping.", "melee_animation": "6489", "range_animation": "0", - "combat_audio": "511,513,512", + "combat_audio": "2548,1979,512", "attack_speed": "5", "respawn_delay": "60", "defence_animation": "0", @@ -50459,710 +51665,848 @@ "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", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" + "bonuses": "0,0,0,0,0,-21,-21,-21,-21,-21,0,0,0,0,0", + "range_level": "1", + "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", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" + "bonuses": "0,0,0,0,0,-21,-21,-21,-21,-21,0,0,0,0,0", + "range_level": "1", + "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", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" + "bonuses": "0,0,0,0,0,-21,-21,-21,-21,-21,0,0,0,0,0", + "range_level": "1", + "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", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" + "bonuses": "0,0,0,0,0,-21,-21,-21,-21,-21,0,0,0,0,0", + "range_level": "1", + "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", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" + "bonuses": "0,0,0,0,0,-21,-21,-21,-21,-21,0,0,0,0,0", + "range_level": "1", + "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", - "range_level": "1", - "attack_level": "1" + "bonuses": "0,0,0,0,0,-21,-21,-21,-21,-21,0,0,0,0,0", + "range_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", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" + "bonuses": "0,0,0,0,0,-21,-21,-21,-21,-21,0,0,0,0,0", + "range_level": "1", + "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", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" + "bonuses": "0,0,0,0,0,-21,-21,-21,-21,-21,0,0,0,0,0", + "range_level": "1", + "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", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" + "bonuses": "0,0,0,0,0,-21,-21,-21,-21,-21,0,0,0,0,0", + "range_level": "1", + "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", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" + "bonuses": "0,0,0,0,0,-21,-21,-21,-21,-21,0,0,0,0,0", + "range_level": "1", + "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", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" + "bonuses": "0,0,0,0,0,-21,-21,-21,-21,-21,0,0,0,0,0", + "range_level": "1", + "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", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" + "bonuses": "0,0,0,0,0,-21,-21,-21,-21,-21,0,0,0,0,0", + "range_level": "1", + "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", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" + "bonuses": "0,0,0,0,0,-21,-21,-21,-21,-21,0,0,0,0,0", + "range_level": "1", + "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", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" + "bonuses": "0,0,0,0,0,-21,-21,-21,-21,-21,0,0,0,0,0", + "range_level": "1", + "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", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" + "bonuses": "0,0,0,0,0,-21,-21,-21,-21,-21,0,0,0,0,0", + "range_level": "1", + "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", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" + "bonuses": "0,0,0,0,0,-21,-21,-21,-21,-21,0,0,0,0,0", + "range_level": "1", + "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", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" + "bonuses": "0,0,0,0,0,-21,-21,-21,-21,-21,0,0,0,0,0", + "range_level": "1", + "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", - "range_level": "1", - "respawn_delay": "60", - "attack_level": "1", - "defence_animation": "425" + "bonuses": "0,0,0,0,0,-21,-21,-21,-21,-21,0,0,0,0,0", + "range_level": "1", + "attack_level": "10" }, { "melee_animation": "6559", + "combat_audio": "909,912,911", "respawn_delay": "60", "defence_animation": "6557", "slayer_exp": "74", @@ -51181,7 +52525,7 @@ "examine": "A vicious mountain wolf.", "melee_animation": "6579", "range_animation": "6579", - "combat_audio": "481,491,490", + "combat_audio": "909,912,911", "attack_speed": "6", "defence_animation": "6578", "slayer_exp": "34", @@ -51236,7 +52580,6 @@ }, { "examine": "A vicious desert wolf.", - "slayer_task": "92", "melee_animation": "6579", "range_animation": "6579", "attack_speed": "5", @@ -51361,7 +52704,6 @@ }, { "examine": "A one-eyed man eater.", - "slayer_task": "44", "melee_animation": "4652", "range_animation": "0", "attack_speed": "4", @@ -51386,7 +52728,6 @@ }, { "examine": "A one-eyed woman eater.", - "slayer_task": "44", "melee_animation": "4652", "range_animation": "0", "attack_speed": "4", @@ -51411,7 +52752,6 @@ }, { "examine": "A one-eyed man eater.", - "slayer_task": "44", "melee_animation": "4652", "range_animation": "0", "attack_speed": "4", @@ -51436,7 +52776,6 @@ }, { "examine": "A one-eyed woman eater.", - "slayer_task": "44", "melee_animation": "4652", "range_animation": "0", "attack_speed": "4", @@ -51461,7 +52800,6 @@ }, { "examine": "Overgrown undead vermin.", - "slayer_task": "67", "melee_animation": "4933", "range_animation": "0", "combat_audio": "703,705,704", @@ -51481,7 +52819,6 @@ }, { "examine": "Overgrown undead vermin.", - "slayer_task": "67", "melee_animation": "4933", "range_animation": "0", "combat_audio": "703,705,704", @@ -51501,7 +52838,6 @@ }, { "examine": "Overgrown undead vermin.", - "slayer_task": "67", "melee_animation": "4933", "range_animation": "0", "combat_audio": "703,705,704", @@ -51645,7 +52981,6 @@ }, { "examine": "Lesser, but still pretty big.", - "slayer_task": "56", "melee_animation": "4630", "range_animation": "4630", "combat_audio": "400,404,403", @@ -51688,7 +53023,6 @@ }, { "examine": "Probably not a chicken.", - "slayer_task": "7", "melee_animation": "6811", "range_animation": "0", "respawn_delay": "60", @@ -51699,7 +53033,7 @@ "name": "Entrana firebird", "defence_level": "1", "safespot": null, - "lifepoints": "1", + "lifepoints": "5", "strength_level": "1", "id": "6108", "range_level": "1", @@ -51707,7 +53041,6 @@ }, { "examine": "These gnomes know how to get around!", - "slayer_task": "7", "melee_animation": "6790", "range_animation": "0", "respawn_delay": "60", @@ -51756,7 +53089,6 @@ }, { "examine": "Aww, cute.", - "slayer_task": "7", "melee_animation": "0", "range_animation": "0", "defence_animation": "0", @@ -51790,7 +53122,6 @@ }, { "examine": "A messy bird.", - "slayer_task": "7", "melee_animation": "3467", "range_animation": "3467", "combat_audio": "309,311,310", @@ -52502,7 +53833,6 @@ }, { "examine": "A servant of the god Zamorak. ", - "slayer_task": "40", "melee_animation": "6945", "attack_speed": "6", "poisonous": "true", @@ -52532,7 +53862,6 @@ { "agg_radius": "64", "examine": "Destroyer of 1000 planes!", - "slayer_task": "40", "melee_animation": "6945", "range_animation": "6945", "attack_speed": "5", @@ -52567,7 +53896,6 @@ }, { "examine": "Scourge of light.", - "slayer_task": "56", "start_gfx": "1208", "combat_style": "1", "melee_animation": "7033", @@ -52643,7 +53971,6 @@ }, { "examine": "From the maws of hell.", - "slayer_task": "43", "melee_animation": "6579", "range_animation": "6579", "combat_audio": "3717,3719,3718", @@ -52689,7 +54016,6 @@ }, { "examine": "He doesn't look pleased to see me.", - "slayer_task": "91", "melee_animation": "6536", "range_animation": "6536", "respawn_delay": "25", @@ -52711,7 +54037,6 @@ }, { "examine": "He doesn't look pleased to see me.", - "slayer_task": "91", "melee_animation": "6536", "range_animation": "6536", "respawn_delay": "25", @@ -52775,7 +54100,6 @@ }, { "examine": "A small fire demon.", - "slayer_task": "66", "melee_animation": "1582", "range_animation": "1582", "combat_audio": "696,698,697", @@ -52818,7 +54142,6 @@ }, { "examine": "Those horns look pretty sharp...", - "slayer_task": "39", "melee_animation": "4300", "range_animation": "4300", "respawn_delay": "25", @@ -52840,7 +54163,6 @@ }, { "examine": "Warrior of Zamorak.", - "slayer_task": "79", "melee_animation": "390", "range_animation": "390", "attack_speed": "5", @@ -52863,7 +54185,6 @@ }, { "examine": "A ranger spirit dedicated to Zamorak.", - "slayer_task": "78", "start_gfx": "18", "combat_style": "1", "start_height": "96", @@ -52889,7 +54210,6 @@ }, { "examine": "A deadly servant of Zamorak.", - "slayer_task": "77", "combat_style": "2", "melee_animation": "811", "attack_speed": "5", @@ -53056,7 +54376,6 @@ }, { "examine": "A servant of Armadyl.", - "slayer_task": "79", "combat_style": "1", "melee_animation": "6954", "range_animation": "6954", @@ -53079,7 +54398,6 @@ }, { "examine": "Armadyl's favourite servant.", - "slayer_task": "78", "combat_style": "1", "melee_animation": "6953", "range_animation": "6953", @@ -53104,29 +54422,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" }, { @@ -53642,7 +54959,6 @@ }, { "examine": "Warrior of Saradomin.", - "slayer_task": "79", "melee_animation": "390", "range_animation": "390", "attack_speed": "5", @@ -53665,7 +54981,6 @@ }, { "examine": "A ranger spirit dedicated to Saradomin.", - "slayer_task": "78", "start_gfx": "18", "combat_style": "1", "start_height": "96", @@ -53691,7 +55006,6 @@ }, { "examine": "Saradomin's holy mage.", - "slayer_task": "77", "combat_style": "2", "melee_animation": "811", "attack_speed": "5", @@ -53786,7 +55100,6 @@ { "agg_radius": "64", "examine": "A battle-honed goblin. ", - "slayer_task": "38", "melee_animation": "6154", "range_animation": "6154", "attack_speed": "5", @@ -53819,28 +55132,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" @@ -53859,7 +55171,6 @@ { "agg_radius": "64", "examine": "A battle-honed goblin. ", - "slayer_task": "38", "combat_style": "1", "melee_animation": "6154", "range_animation": "6154", @@ -53938,7 +55249,6 @@ }, { "examine": "A one-eyed man-eater.", - "slayer_task": "44", "melee_animation": "4652", "range_animation": "0", "attack_speed": "4", @@ -53963,7 +55273,6 @@ }, { "examine": "A one-eyed man-eater.", - "slayer_task": "44", "melee_animation": "4652", "range_animation": "0", "attack_speed": "4", @@ -54065,7 +55374,6 @@ }, { "examine": "An ugly, smelly creature.", - "slayer_task": "45", "melee_animation": "164", "range_animation": "164", "combat_audio": "469,472,471", @@ -54087,7 +55395,6 @@ }, { "examine": "The spirit of a ranger, serving bandos beyond death.", - "slayer_task": "78", "start_gfx": "95", "combat_style": "1", "melee_animation": "4320", @@ -54112,7 +55419,6 @@ }, { "examine": "A true servant of Bandos.", - "slayer_task": "79", "melee_animation": "4320", "range_animation": "4320", "respawn_delay": "25", @@ -54134,7 +55440,6 @@ }, { "examine": "One of Bandos' chosen.", - "slayer_task": "77", "combat_style": "2", "melee_animation": "4320", "range_animation": "4320", @@ -54265,7 +55570,6 @@ }, { "examine": "You don't think you can harm this terrorbird.", - "slayer_task": "88", "combat_style": "1", "melee_animation": "7108", "range_animation": "0", @@ -54288,7 +55592,6 @@ }, { "examine": "You don't think you can harm this terrorbird.", - "slayer_task": "88", "combat_style": "1", "melee_animation": "7108", "range_animation": "0", @@ -54311,7 +55614,6 @@ }, { "examine": "You don't think you can harm this terrorbird.", - "slayer_task": "88", "combat_style": "1", "melee_animation": "7108", "range_animation": "0", @@ -54334,7 +55636,6 @@ }, { "examine": "You don't think you can harm this terrorbird.", - "slayer_task": "88", "combat_style": "1", "melee_animation": "7108", "range_animation": "0", @@ -54357,7 +55658,6 @@ }, { "examine": "You don't think you can harm this terrorbird.", - "slayer_task": "88", "combat_style": "1", "melee_animation": "7108", "range_animation": "0", @@ -54397,7 +55697,6 @@ }, { "examine": "You don't think you can harm this terrorbird.", - "slayer_task": "88", "combat_style": "1", "melee_animation": "7108", "range_animation": "0", @@ -54420,7 +55719,6 @@ }, { "examine": "You don't think you can harm this terrorbird.", - "slayer_task": "88", "combat_style": "1", "melee_animation": "7108", "range_animation": "0", @@ -54460,7 +55758,6 @@ }, { "examine": "You don't think you can harm this terrorbird.", - "slayer_task": "88", "combat_style": "1", "melee_animation": "7108", "range_animation": "0", @@ -54483,7 +55780,6 @@ }, { "examine": "You don't think you can harm this terrorbird.", - "slayer_task": "88", "combat_style": "1", "melee_animation": "7108", "range_animation": "0", @@ -54506,7 +55802,6 @@ }, { "examine": "You don't think you can harm this creature.", - "slayer_task": "89", "melee_animation": "7093", "range_animation": "0", "attack_speed": "5", @@ -54542,7 +55837,6 @@ }, { "examine": "Annoyingly easy to squish.", - "slayer_task": "73", "melee_animation": "0", "range_animation": "0", "magic_level": "2", @@ -55318,7 +56612,6 @@ }, { "examine": "I wouldn't want to be footing that bill.", - "slayer_task": "7", "melee_animation": "6775", "range_animation": "0", "attack_speed": "5", @@ -55395,7 +56688,6 @@ }, { "examine": "Polynomial - a parrot that goes without breakfast", - "slayer_task": "7", "melee_animation": "6775", "range_animation": "0", "attack_speed": "5", @@ -56278,7 +57570,6 @@ }, { "examine": "A goblin high priest", - "slayer_task": "75", "melee_animation": "7317", "range_animation": "0", "respawn_delay": "60", @@ -56298,7 +57589,6 @@ }, { "examine": "A goblin high priest", - "slayer_task": "75", "melee_animation": "7317", "range_animation": "0", "respawn_delay": "60", @@ -56318,7 +57608,6 @@ }, { "examine": "A goblin high priest", - "slayer_task": "75", "melee_animation": "7317", "range_animation": "0", "magic_level": "22", @@ -56338,7 +57627,6 @@ }, { "examine": "A goblin high priest", - "slayer_task": "75", "melee_animation": "7317", "range_animation": "0", "magic_level": "26", @@ -56358,7 +57646,6 @@ }, { "examine": "A goblin high priest", - "slayer_task": "75", "melee_animation": "7317", "range_animation": "0", "magic_level": "26", @@ -59521,22 +60808,30 @@ "attack_level": "1" }, { + "examine": "A snowman in fake dragon armour.", + "combat_audio": "29,3295,3287", + "melee_animation": "7558", + "defence_animation": "7559", + "death_animation": "7560", "name": "Dragon snowman", "defence_level": "1", "safespot": null, - "lifepoints": "10", - "combat_audio": "408,410,409", + "lifepoints": "20", "strength_level": "1", "id": "6743", "range_level": "1", "attack_level": "1" }, { + "examine": "Arr!", + "combat_audio": "29,3295,3287", + "melee_animation": "7558", + "defence_animation": "7559", + "death_animation": "7560", "name": "Pirate snowman", "defence_level": "1", "safespot": null, - "lifepoints": "10", - "combat_audio": "703,705,704", + "lifepoints": "20", "strength_level": "1", "id": "6745", "range_level": "1", @@ -59578,7 +60873,6 @@ }, { "examine": "Cut-down and dried.", - "slayer_task": "93", "melee_animation": "5568", "range_animation": "0", "attack_speed": "5", @@ -59598,7 +60892,6 @@ }, { "examine": "Needs moisturising.", - "slayer_task": "93", "melee_animation": "5568", "range_animation": "0", "attack_speed": "5", @@ -59618,7 +60911,6 @@ }, { "examine": "I bet it's parched.", - "slayer_task": "93", "melee_animation": "5568", "range_animation": "0", "attack_speed": "5", @@ -59654,7 +60946,6 @@ }, { "examine": "A huge beast of a beetle.", - "slayer_task": "70", "melee_animation": "7596", "range_animation": "0", "defence_animation": "0", @@ -59690,7 +60981,6 @@ }, { "examine": "Bred to shoot you down.", - "slayer_task": "70", "start_gfx": "18", "combat_style": "1", "melee_animation": "7607", @@ -59713,7 +61003,6 @@ }, { "examine": "It might be angry...it's hard to tell.", - "slayer_task": "70", "melee_animation": "7612", "range_animation": "0", "respawn_delay": "60", @@ -59752,7 +61041,6 @@ }, { "examine": "Bouncy", - "slayer_task": "70", "melee_animation": "7584", "range_animation": "0", "respawn_delay": "60", @@ -59772,7 +61060,6 @@ }, { "examine": "He'll have someone's eye out with that.", - "slayer_task": "70", "start_gfx": "18", "combat_style": "1", "melee_animation": "5451", @@ -59813,7 +61100,6 @@ }, { "examine": "Part scarab, part man.", - "slayer_task": "70", "combat_style": "2", "melee_animation": "7615", "range_animation": "0", @@ -59916,7 +61202,6 @@ }, { "examine": "He exudes something like holiness.", - "slayer_task": "70", "melee_animation": "7612", "range_animation": "0", "magic_level": "66", @@ -59935,7 +61220,6 @@ }, { "examine": "He exudes something like holiness.", - "slayer_task": "70", "melee_animation": "7607", "range_animation": "7607", "magic_level": "66", @@ -59955,17 +61239,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.", @@ -60028,18 +61316,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", @@ -60061,18 +61354,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.", @@ -60094,18 +61392,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.", @@ -60127,18 +61430,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!", @@ -60209,19 +61517,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!", @@ -60302,23 +61615,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", @@ -60348,19 +61666,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.", @@ -60383,18 +61706,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.", @@ -60521,19 +61849,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!", @@ -60595,18 +61928,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.", @@ -60630,6 +61969,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", @@ -60647,18 +62006,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!", @@ -60680,19 +62044,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.", @@ -60749,6 +62118,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" }, @@ -60794,18 +62164,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.", @@ -60827,18 +62202,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.", @@ -60924,6 +62304,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" }, @@ -60953,60 +62334,65 @@ }, { "examine": "He's just a big bully.", - "combat_style": "1", + "combat_style": "0", "melee_animation": "8024", - "magic_level": "31", + "magic_level": "1", "respawn_delay": "0", "defence_animation": "8023", "death_animation": "8025", "name": "Bronze minotaur", - "defence_level": "31", + "defence_level": "19", "safespot": null, - "lifepoints": "15", - "strength_level": "1", + "lifepoints": "133", + "strength_level": "19", "id": "6853", - "bonuses": "15,20,10,15,10,5,35,0,0,0,0,0,0,0,0", - "range_level": "31", - "attack_level": "31" + "bonuses": "-2,6,3,0,0,15,14,9,-6,14,5,90,0,0,0", + "range_level": "1", + "attack_level": "19" }, { "examine": "He's just a big bully.", - "combat_style": "1", + "combat_style": "0", "melee_animation": "8024", "range_animation": "0", - "magic_level": "15", + "magic_level": "1", "respawn_delay": "0", "defence_animation": "0", "weakness": "10", "magic_animation": "0", "death_animation": "8025", "name": "Bronze minotaur", - "defence_level": "15", + "defence_level": "19", "safespot": null, - "lifepoints": "51", - "strength_level": "15", + "lifepoints": "133", + "strength_level": "19", "id": "6854", - "range_level": "15", - "attack_level": "15" - }, - { - "combat_style": "1", - "melee_animation": "8024", - "respawn_delay": "0", - "defence_animation": "8023", - "death_animation": "8025", - "name": "Iron minotaur", - "defence_level": "1", - "safespot": null, - "lifepoints": "24", - "strength_level": "1", - "id": "6855", + "bonuses": "15,20,10,15,10,15,15,15,0,0,0,90,0,0,0", "range_level": "1", - "attack_level": "1" + "attack_level": "19" }, { "examine": "He's just a big bully.", - "combat_style": "1", + "combat_style": "0", + "melee_animation": "8024", + "magic_level": "25", + "respawn_delay": "0", + "defence_animation": "8023", + "magic_animation": "", + "death_animation": "8025", + "name": "Iron minotaur", + "defence_level": "25", + "safespot": null, + "lifepoints": "193", + "strength_level": "25", + "id": "6855", + "bonuses": "-2,8,5,0,0,21,17,9,-6,20,5,100,0,100,0", + "range_level": "1", + "attack_level": "25" + }, + { + "examine": "He's just a big bully.", + "combat_style": "0", "melee_animation": "8024", "range_animation": "0", "magic_level": "25", @@ -61018,32 +62404,116 @@ "name": "Iron minotaur", "defence_level": "25", "safespot": null, - "lifepoints": "65", + "lifepoints": "193", "strength_level": "25", "id": "6856", - "range_level": "25", + "bonuses": "25,25,25,25,0,25,25,25,25,0,0,100,0,100,0", + "clue_level": "", + "range_level": "1", "attack_level": "25" }, { "examine": "He's just a big bully!", - "combat_style": "1", + "combat_style": "0", "melee_animation": "8024", + "magic_level": "28", "respawn_delay": "0", "defence_animation": "8023", "death_animation": "8025", "name": "Steel minotaur", - "defence_level": "70", + "defence_level": "28", "safespot": null, - "lifepoints": "44", - "strength_level": "30", + "lifepoints": "260", + "strength_level": "28", "id": "6857", - "bonuses": "40,30,30,30,30,50,30,60,60,60,20,80,40,20,0", + "bonuses": "-2,16,11,0,0,32,31,24,-6,31,5,122,0,122,0", "range_level": "1", - "attack_level": "70" + "attack_level": "28" }, { "examine": "He's just a big bully.", - "combat_style": "1", + "combat_style": "0", + "melee_animation": "8024", + "range_animation": "0", + "magic_level": "28", + "respawn_delay": "0", + "defence_animation": "0", + "weakness": "10", + "magic_animation": "0", + "death_animation": "8025", + "name": "Steel minotaur", + "defence_level": "28", + "safespot": null, + "lifepoints": "260", + "strength_level": "28", + "id": "6858", + "bonuses": "40,30,30,30,30,30,30,30,30,30,0,127,0,0,0", + "range_level": "1", + "attack_level": "28" + }, + { + "examine": "He's just a big bully", + "combat_style": "0", + "melee_animation": "8024", + "magic_level": "30", + "respawn_delay": "0", + "defence_animation": "8023", + "death_animation": "8025", + "name": "Mithril minotaur", + "defence_level": "30", + "safespot": null, + "lifepoints": "340", + "can_tolerate": "", + "strength_level": "30", + "id": "6859", + "bonuses": "-2,22,17,0,0,46,44,38,-6,44,20,146,0,147,0", + "range_level": "0", + "attack_level": "30" + }, + { + "examine": "He's just a big bully.", + "combat_style": "0", + "melee_animation": "8024", + "range_animation": "0", + "magic_level": "30", + "respawn_delay": "0", + "defence_animation": "8023", + "weakness": "10", + "magic_animation": "0", + "death_animation": "8025", + "name": "Mithril minotaur", + "defence_level": "30", + "safespot": null, + "lifepoints": "340", + "strength_level": "30", + "id": "6860", + "bonuses": "30,30,30,30,0,30,30,30,30,0,30,146,0,147,0", + "range_level": "0", + "attack_level": "30" + }, + { + "examine": "He's just a big bully.", + "combat_style": "0", + "melee_animation": "8024", + "attack_speed": "", + "magic_level": "35", + "respawn_delay": "0", + "defence_animation": "8023", + "death_animation": "8025", + "name": "Adamant minotaur", + "defence_level": "35", + "safespot": null, + "poison_immune": "", + "lifepoints": "441", + "strength_level": "35", + "id": "6861", + "bonuses": "-2,31,26,-3,-17,65,63,55,-6,63,30,146,0,181,0", + "range_level": "1", + "attack_level": "35" + }, + { + "examine": "He's just a big bully.", + "combat_style": "0", "melee_animation": "8024", "range_animation": "0", "magic_level": "35", @@ -61052,139 +62522,69 @@ "weakness": "10", "magic_animation": "0", "death_animation": "8025", - "name": "Steel minotaur", + "name": "Adamant minotaur", "defence_level": "35", "safespot": null, - "lifepoints": "80", + "lifepoints": "441", "strength_level": "35", - "id": "6858", - "range_level": "35", + "id": "6862", + "bonuses": "40,50,40,45,35,20,50,36,30,40,15,180,30,10,0", + "range_level": "1", "attack_level": "35" }, { - "examine": "He's just a big bully", - "combat_style": "1", + "examine": "He's just a big bully.", "melee_animation": "8024", "magic_level": "40", "respawn_delay": "0", "defence_animation": "8023", "death_animation": "8025", - "name": "Mithril minotaur", + "name": "Rune minotaur", "defence_level": "40", "safespot": null, - "lifepoints": "37", + "lifepoints": "570", "strength_level": "40", - "id": "6859", - "bonuses": "56,35,30,30,30,40,10,12,30,40,30,35,40,0,0", - "range_level": "40", + "id": "6863", + "bonuses": "-2,48,43,0,-1,207,209,192,-12,205,102,157,0,195,0", + "range_level": "1", "attack_level": "40" }, { "examine": "He's just a big bully.", - "combat_style": "1", "melee_animation": "8024", - "range_animation": "0", - "magic_level": "45", - "respawn_delay": "0", - "defence_animation": "0", - "weakness": "10", - "magic_animation": "0", - "death_animation": "8025", - "name": "Mithril minotaur", - "defence_level": "45", - "safespot": null, - "lifepoints": "94", - "strength_level": "45", - "id": "6860", - "range_level": "45", - "attack_level": "45" - }, - { - "examine": "He's just a big bully.", - "combat_style": "1", - "melee_animation": "8024", - "magic_level": "55", - "respawn_delay": "0", - "defence_animation": "8023", - "death_animation": "8025", - "name": "Adamant minotaur", - "defence_level": "55", - "safespot": null, - "lifepoints": "43", - "strength_level": "1", - "id": "6861", - "bonuses": "40,50,40,45,35,20,50,36,30,40,15,18,30,10,0", - "range_level": "55", - "attack_level": "55" - }, - { - "examine": "He's just a big bully.", - "combat_style": "1", - "melee_animation": "8024", - "range_animation": "0", - "magic_level": "55", - "respawn_delay": "0", - "defence_animation": "0", - "weakness": "10", - "magic_animation": "0", - "death_animation": "8025", - "name": "Adamant minotaur", - "defence_level": "55", - "safespot": null, - "lifepoints": "108", - "strength_level": "55", - "id": "6862", - "range_level": "55", - "attack_level": "55" - }, - { - "examine": "He's just a big bully.", - "melee_animation": "8024", - "magic_level": "65", - "respawn_delay": "0", - "defence_animation": "8023", - "death_animation": "8025", - "name": "Rune minotaur", - "defence_level": "65", - "safespot": null, - "lifepoints": "57", - "strength_level": "65", - "id": "6863", - "bonuses": "50,40,40,56,40,30,50,30,60,30,40,30,50,40,40", - "range_level": "65", - "attack_level": "65" - }, - { - "examine": "He's just a big bully.", - "melee_animation": "8024", - "magic_level": "65", + "magic_level": "40", "respawn_delay": "0", "defence_animation": "8023", "slayer_exp": "0", "death_animation": "8025", "name": "Rune minotaur", - "defence_level": "65", + "defence_level": "40", "safespot": null, - "lifepoints": "57", - "strength_level": "65", + "lifepoints": "570", + "strength_level": "40", "id": "6864", - "bonuses": "50,40,40,56,40,30,50,30,60,30,40,30,50,40,40", - "range_level": "65", - "attack_level": "65" + "bonuses": "50,40,40,56,40,30,50,30,60,30,40,219,50,40,40", + "range_level": "1", + "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", @@ -61206,18 +62606,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.", @@ -61281,19 +62686,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", @@ -61357,21 +62767,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", @@ -61396,21 +62811,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", @@ -61435,22 +62855,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", @@ -61476,21 +62901,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", @@ -61515,21 +62945,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", @@ -61554,21 +62989,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", @@ -61593,21 +63033,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", @@ -61632,20 +63077,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.", @@ -61909,6 +63357,7 @@ "combat_style": "2", "melee_animation": "8304", "attack_speed": "5", + "magic_level": "28", "respawn_delay": "0", "defence_animation": "8306", "death_animation": "8305", @@ -62257,18 +63706,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", @@ -62290,19 +63744,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", @@ -62384,7 +63843,6 @@ }, { "examine": "A very large elemental adversary.", - "slayer_task": "33", "melee_animation": "4666", "range_animation": "4666", "combat_audio": "447,451,450", @@ -62408,7 +63866,6 @@ }, { "examine": "A very large elemental adversary.", - "slayer_task": "33", "melee_animation": "4666", "range_animation": "4666", "combat_audio": "447,451,450", @@ -62432,7 +63889,6 @@ }, { "examine": "Must be the pack leader.", - "slayer_task": "92", "melee_animation": "6559", "range_animation": "0", "respawn_delay": "60", @@ -62566,7 +64022,6 @@ }, { "examine": "An ogre snack with wings.", - "slayer_task": "7", "melee_animation": "0", "range_animation": "0", "defence_animation": "0", @@ -62633,7 +64088,6 @@ }, { "examine": "A fearsome adversary with no sense of humour.", - "slayer_task": "64", "melee_animation": "8636", "range_animation": "0", "respawn_delay": "60", @@ -62653,7 +64107,6 @@ }, { "examine": "Irascible, belligerent and stupefyingly impolite.", - "slayer_task": "64", "melee_animation": "8636", "range_animation": "0", "respawn_delay": "60", @@ -62673,7 +64126,6 @@ }, { "examine": "Irascible, beliggerent and stupefyingly impolite.", - "slayer_task": "64", "melee_animation": "8637", "range_animation": "0", "respawn_delay": "60", @@ -62693,7 +64145,6 @@ }, { "examine": "A large and contentious lady ogre.", - "slayer_task": "64", "melee_animation": "8636", "range_animation": "0", "attack_speed": "5", @@ -62714,7 +64165,6 @@ }, { "examine": "A large and contentious lady ogre.", - "slayer_task": "64", "melee_animation": "8637", "range_animation": "0", "attack_speed": "5", @@ -64076,7 +65526,7 @@ "attack_level": "1" }, { - "combat_style": "1", + "combat_style": "0", "melee_animation": "8222", "magic_level": "70", "respawn_delay": "0", @@ -64094,7 +65544,7 @@ }, { "examine": "Do you hear duelling banjos?", - "combat_style": "1", + "combat_style": "0", "melee_animation": "8222", "range_animation": "0", "magic_level": "65", @@ -64154,6 +65604,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", @@ -64173,21 +65641,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", @@ -64206,18 +65681,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.", @@ -64240,6 +65720,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", @@ -64261,6 +65742,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", @@ -64282,24 +65764,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", @@ -64320,6 +65807,7 @@ }, { "examine": "The King of the Titans!", + "combat_style": "1", "melee_animation": "8183", "range_animation": "8183", "magic_level": "70", @@ -64342,6 +65830,7 @@ }, { "examine": "The King of the Titans!", + "combat_style": "1", "melee_animation": "8183", "range_animation": "8183", "magic_level": "70", @@ -64400,18 +65889,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", @@ -64433,18 +65927,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", @@ -64467,6 +65966,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", @@ -64484,21 +66003,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", @@ -64518,6 +66044,7 @@ }, { "examine": "Scorching!", + "combat_style": "2", "melee_animation": "7834", "range_animation": "7834", "attack_speed": "5", @@ -64541,6 +66068,7 @@ }, { "examine": "Scorching!", + "combat_style": "2", "melee_animation": "7834", "range_animation": "7834", "attack_speed": "5", @@ -64655,24 +66183,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", @@ -64692,19 +66225,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!", @@ -64727,19 +66265,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!", @@ -64761,6 +66304,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", @@ -64779,6 +66340,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", @@ -64817,22 +66396,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", @@ -64851,7 +66435,7 @@ }, { "examine": "He is an iron man!", - "combat_style": "1", + "combat_style": "0", "melee_animation": "7946", "combat_audio": "0,0,0", "magic_level": "65", @@ -64870,6 +66454,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", @@ -65230,7 +66832,6 @@ }, { "examine": "You clearly can't live on treasure alone.", - "slayer_task": "75", "range_animation": "0", "defence_animation": "0", "weakness": "6", @@ -65247,7 +66848,6 @@ }, { "examine": "A testament to the effect of greed.", - "slayer_task": "75", "range_animation": "0", "defence_animation": "0", "weakness": "6", @@ -65264,7 +66864,6 @@ }, { "examine": "He wanted loot", - "slayer_task": "75", "range_animation": "0", "defence_animation": "0", "weakness": "6", @@ -65281,7 +66880,6 @@ }, { "examine": "Eternally looking for that big payday.", - "slayer_task": "75", "range_animation": "0", "defence_animation": "0", "weakness": "6", @@ -65298,7 +66896,6 @@ }, { "examine": "No more tea-breaks for this one.", - "slayer_task": "75", "range_animation": "0", "defence_animation": "0", "weakness": "6", @@ -65315,7 +66912,6 @@ }, { "examine": "Even in death you can smell his feet.", - "slayer_task": "75", "range_animation": "0", "defence_animation": "0", "weakness": "6", @@ -65332,7 +66928,6 @@ }, { "examine": "An anatomist's dream.", - "slayer_task": "75", "range_animation": "0", "defence_animation": "0", "weakness": "6", @@ -65349,7 +66944,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", @@ -65467,7 +67061,6 @@ }, { "examine": "This dung beetle has mistaken you for its staple diet.", - "slayer_task": "70", "range_animation": "0", "respawn_delay": "60", "defence_animation": "0", @@ -65485,7 +67078,6 @@ }, { "examine": "It's looking unwell - probably something it ate.", - "slayer_task": "70", "range_animation": "0", "respawn_delay": "60", "defence_animation": "0", @@ -65503,7 +67095,6 @@ }, { "examine": "Once a valuable contributor to the ecosystem.", - "slayer_task": "70", "range_animation": "0", "respawn_delay": "60", "defence_animation": "0", @@ -65521,7 +67112,6 @@ }, { "examine": "This is what happens if you play with your food.", - "slayer_task": "70", "range_animation": "0", "respawn_delay": "60", "defence_animation": "0", @@ -66504,7 +68094,6 @@ }, { "examine": "Ew it's still alive.", - "slayer_task": "21", "melee_animation": "9125", "range_animation": "9125", "attack_speed": "5", @@ -66526,7 +68115,6 @@ }, { "examine": "Ew it's still alive.", - "slayer_task": "21", "melee_animation": "9125", "range_animation": "9125", "attack_speed": "5", @@ -66548,7 +68136,6 @@ }, { "examine": "A bloodveld with a very mixed heritage.", - "slayer_task": "10", "melee_animation": "9102", "range_animation": "0", "attack_speed": "5", @@ -66570,7 +68157,6 @@ }, { "examine": "A bloodveld with a very mixed heritage.", - "slayer_task": "10", "melee_animation": "9102", "range_animation": "0", "attack_speed": "5", @@ -67018,7 +68604,6 @@ }, { "examine": "Looks like a big ugly dog.", - "slayer_task": "27", "melee_animation": "6565", "range_animation": "0", "respawn_delay": "60", @@ -67053,7 +68638,6 @@ }, { "examine": "A small ice demon.", - "slayer_task": "46", "melee_animation": "8080", "range_animation": "0", "combat_audio": "531,533,532", @@ -67077,7 +68661,6 @@ }, { "examine": "The icefiend seems to be melting.", - "slayer_task": "46", "melee_animation": "8080", "range_animation": "0", "combat_audio": "531,533,532", @@ -67101,7 +68684,6 @@ }, { "examine": "The icefiend seems to be melting.", - "slayer_task": "46", "melee_animation": "8080", "range_animation": "0", "attack_speed": "5", @@ -67335,6 +68917,28 @@ "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", @@ -67509,6 +69113,7 @@ "defence_level": "85", "poison_immune": "true", "safespot": "true", + "movement_radius": "40", "lifepoints": "326", "strength_level": "85", "id": "8349", @@ -67534,6 +69139,7 @@ "defence_level": "85", "poison_immune": "true", "safespot": "true", + "movement_radius": "40", "lifepoints": "326", "strength_level": "85", "id": "8350", @@ -67559,6 +69165,7 @@ "defence_level": "85", "poison_immune": "true", "safespot": "true", + "movement_radius": "40", "lifepoints": "326", "strength_level": "85", "id": "8351", @@ -67584,6 +69191,7 @@ "defence_level": "85", "poison_immune": "true", "safespot": "true", + "movement_radius": "40", "lifepoints": "326", "strength_level": "85", "id": "8352", @@ -67609,6 +69217,7 @@ "defence_level": "85", "poison_immune": "true", "safespot": "true", + "movement_radius": "40", "lifepoints": "326", "strength_level": "85", "id": "8353", @@ -67634,6 +69243,7 @@ "defence_level": "85", "poison_immune": "true", "safespot": "true", + "movement_radius": "40", "lifepoints": "326", "strength_level": "85", "id": "8354", @@ -67659,6 +69269,7 @@ "defence_level": "85", "poison_immune": "true", "safespot": "true", + "movement_radius": "40", "lifepoints": "326", "strength_level": "85", "id": "8355", @@ -67684,6 +69295,7 @@ "defence_level": "85", "poison_immune": "true", "safespot": "true", + "movement_radius": "40", "lifepoints": "326", "strength_level": "85", "id": "8356", @@ -67709,6 +69321,7 @@ "defence_level": "85", "poison_immune": "true", "safespot": "true", + "movement_radius": "40", "lifepoints": "326", "strength_level": "85", "id": "8357", @@ -67734,6 +69347,7 @@ "defence_level": "85", "poison_immune": "true", "safespot": "true", + "movement_radius": "40", "lifepoints": "326", "strength_level": "85", "id": "8358", @@ -67759,6 +69373,7 @@ "defence_level": "85", "poison_immune": "true", "safespot": "true", + "movement_radius": "40", "lifepoints": "326", "strength_level": "85", "id": "8359", @@ -67784,6 +69399,7 @@ "defence_level": "85", "poison_immune": "true", "safespot": "true", + "movement_radius": "40", "lifepoints": "326", "strength_level": "85", "id": "8360", @@ -67809,6 +69425,7 @@ "defence_level": "85", "poison_immune": "true", "safespot": "true", + "movement_radius": "40", "lifepoints": "326", "strength_level": "85", "id": "8361", @@ -67834,6 +69451,7 @@ "defence_level": "85", "poison_immune": "true", "safespot": "true", + "movement_radius": "40", "lifepoints": "326", "strength_level": "85", "id": "8362", @@ -67859,6 +69477,7 @@ "defence_level": "85", "poison_immune": "true", "safespot": "true", + "movement_radius": "40", "lifepoints": "326", "strength_level": "85", "id": "8363", @@ -67884,6 +69503,7 @@ "defence_level": "85", "poison_immune": "true", "safespot": "true", + "movement_radius": "40", "lifepoints": "326", "strength_level": "85", "id": "8364", @@ -67909,6 +69529,7 @@ "defence_level": "85", "poison_immune": "true", "safespot": "true", + "movement_radius": "40", "lifepoints": "326", "strength_level": "85", "id": "8365", @@ -67934,6 +69555,7 @@ "defence_level": "85", "poison_immune": "true", "safespot": "true", + "movement_radius": "40", "lifepoints": "326", "strength_level": "85", "id": "8366", @@ -68623,7 +70245,6 @@ }, { "examine": "Could do with gaining a few pounds.", - "slayer_task": "75", "melee_animation": "5485", "range_animation": "5485", "combat_audio": "774,775,777", @@ -68645,7 +70266,6 @@ }, { "examine": "Could do with gaining a few pounds.", - "slayer_task": "75", "melee_animation": "5485", "range_animation": "5485", "combat_audio": "774,775,777", @@ -68666,7 +70286,6 @@ }, { "examine": "Could do with gaining a few pounds.", - "slayer_task": "75", "melee_animation": "5485", "range_animation": "5485", "combat_audio": "774,775,777", @@ -68687,7 +70306,6 @@ }, { "examine": "Could do with gaining a few pounds.", - "slayer_task": "75", "melee_animation": "5485", "range_animation": "5485", "combat_audio": "774,775,777", @@ -68727,7 +70345,6 @@ }, { "examine": "It rattles when it moves.", - "slayer_task": "75", "combat_style": "2", "melee_animation": "5485", "range_animation": "5485", @@ -68750,7 +70367,6 @@ }, { "examine": "It looks just a bit... underfed.", - "slayer_task": "75", "melee_animation": "5519", "range_animation": "5519", "combat_audio": "774,775,777", @@ -68775,7 +70391,6 @@ }, { "examine": "Could do with gaining a few pounds.", - "slayer_task": "75", "melee_animation": "5487", "range_animation": "5487", "combat_audio": "774,775,777", @@ -68799,7 +70414,6 @@ }, { "examine": "Could do with gaining a few pounds.", - "slayer_task": "75", "melee_animation": "5487", "range_animation": "5487", "combat_audio": "774,775,777", @@ -68874,7 +70488,6 @@ }, { "examine": "Could do with gaining a few pounds.", - "slayer_task": "75", "melee_animation": "5485", "range_animation": "0", "combat_audio": "774,775,777", @@ -68896,7 +70509,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", @@ -68919,7 +70531,6 @@ }, { "examine": "Could use a good meal.", - "slayer_task": "75", "melee_animation": "6128", "range_animation": "0", "combat_audio": "774,775,777", @@ -68969,7 +70580,6 @@ }, { "examine": "He seems a little underweight.", - "slayer_task": "75", "melee_animation": "6128", "range_animation": "0", "combat_audio": "774,775,777", @@ -68989,7 +70599,6 @@ }, { "examine": "He seems a little underweight.", - "slayer_task": "75", "melee_animation": "6128", "range_animation": "0", "combat_audio": "774,775,777", @@ -69024,7 +70633,6 @@ }, { "examine": "He seems a little underweight.", - "slayer_task": "75", "melee_animation": "6128", "range_animation": "0", "combat_audio": "774,775,777", @@ -69044,7 +70652,6 @@ }, { "examine": "He seems a little underweight.", - "slayer_task": "75", "melee_animation": "6128", "range_animation": "0", "combat_audio": "774,775,777", @@ -69068,6 +70675,7 @@ "safespot": null, "lifepoints": "10", "combat_audio": "774,775,777", + "melee_animation": "5485", "strength_level": "1", "id": "3705", "range_level": "1", @@ -69075,6 +70683,7 @@ }, { "combat_audio": "774,775,777", + "melee_animation": "5485", "attack_speed": "5", "respawn_delay": "60", "defence_animation": "5513", @@ -69091,7 +70700,6 @@ }, { "examine": "Could do with gaining a few pounds.", - "slayer_task": "75", "melee_animation": "5485", "range_animation": "0", "combat_audio": "774,775,777", @@ -69113,6 +70721,7 @@ }, { "combat_audio": "774,775,777", + "melee_animation": "5485", "attack_speed": "5", "respawn_delay": "60", "defence_animation": "5513", @@ -69147,7 +70756,6 @@ }, { "examine": "Could do with gaining a few pounds.", - "slayer_task": "75", "combat_audio": "774,775,777", "melee_animation": "5485", "attack_speed": "4", @@ -69168,7 +70776,6 @@ }, { "examine": "Could do with gaining a few pounds.", - "slayer_task": "75", "range_animation": "0", "combat_audio": "774,775,777", "melee_animation": "5485", @@ -69191,7 +70798,6 @@ }, { "examine": "Could do with gaining a few pounds.", - "slayer_task": "75", "range_animation": "0", "combat_audio": "774,775,777", "melee_animation": "5485", @@ -69250,7 +70856,6 @@ }, { "examine": "Could do with gaining a few pounds.", - "slayer_task": "75", "melee_animation": "5485", "range_animation": "5485", "combat_audio": "774,775,777", @@ -69273,7 +70878,6 @@ }, { "examine": "Could do with gaining a few pounds.", - "slayer_task": "75", "melee_animation": "5485", "range_animation": "5485", "combat_audio": "774,775,777", @@ -69293,7 +70897,6 @@ }, { "examine": "Could do with gaining a few pounds.", - "slayer_task": "75", "melee_animation": "5485", "range_animation": "5485", "combat_audio": "774,775,777", @@ -69313,7 +70916,6 @@ }, { "examine": "Could do with gaining a few pounds.", - "slayer_task": "75", "melee_animation": "5485", "range_animation": "5485", "combat_audio": "774,775,777", @@ -69333,7 +70935,6 @@ }, { "examine": "Could do with gaining a few pounds.", - "slayer_task": "75", "melee_animation": "5499", "range_animation": "0", "combat_audio": "774,775,777", @@ -69355,7 +70956,6 @@ }, { "examine": "Could do with gaining a few pounds.", - "slayer_task": "75", "melee_animation": "5485", "range_animation": "0", "combat_audio": "774,775,777", @@ -69378,6 +70978,7 @@ { "examine": "Could do with gaining a few pounds.", "combat_audio": "774,775,777", + "melee_animation": "5485", "attack_speed": "5", "respawn_delay": "60", "defence_animation": "5513", @@ -69395,6 +70996,7 @@ { "examine": "Could do with gaining a few pounds.", "combat_audio": "774,775,777", + "melee_animation": "5485", "attack_speed": "5", "respawn_delay": "60", "defence_animation": "5513", @@ -69428,7 +71030,6 @@ }, { "examine": "Could do with gaining a few pounds.", - "slayer_task": "75", "melee_animation": "5485", "range_animation": "0", "combat_audio": "774,775,777", @@ -69450,7 +71051,6 @@ }, { "examine": "A skeleton in a dress!", - "slayer_task": "75", "melee_animation": "5487", "range_animation": "0", "combat_audio": "774,775,777", @@ -69473,7 +71073,6 @@ }, { "examine": "Achingly thin.", - "slayer_task": "75", "melee_animation": "5487", "range_animation": "0", "combat_audio": "774,775,777", @@ -69495,7 +71094,6 @@ }, { "examine": "Look: another skeleton.", - "slayer_task": "75", "melee_animation": "5485", "range_animation": "0", "combat_audio": "774,775,777", @@ -69517,7 +71115,6 @@ }, { "examine": "That skeleton's grinning at me.", - "slayer_task": "75", "melee_animation": "5487", "range_animation": "0", "combat_audio": "774,775,777", @@ -69539,7 +71136,6 @@ }, { "examine": "He needs a tan.", - "slayer_task": "75", "melee_animation": "5487", "range_animation": "0", "combat_audio": "774,775,777", @@ -69561,7 +71157,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", @@ -69582,7 +71177,6 @@ }, { "examine": "He obviously hasn't realised he's dead.", - "slayer_task": "75", "melee_animation": "5485", "range_animation": "0", "combat_audio": "774,775,777", @@ -69604,7 +71198,6 @@ }, { "examine": "Put some meat on those bones!", - "slayer_task": "75", "melee_animation": "5485", "range_animation": "0", "combat_audio": "774,775,777", @@ -69626,7 +71219,6 @@ }, { "examine": "An angry skeleton.", - "slayer_task": "75", "melee_animation": "5487", "range_animation": "0", "combat_audio": "774,775,777", @@ -69648,7 +71240,6 @@ }, { "examine": "Cross bones.", - "slayer_task": "75", "melee_animation": "5485", "range_animation": "0", "combat_audio": "774,775,777", @@ -69670,7 +71261,6 @@ }, { "examine": "Could do with gaining a few pounds.", - "slayer_task": "75", "melee_animation": "5485", "range_animation": "0", "combat_audio": "774,775,777", @@ -69726,7 +71316,6 @@ }, { "examine": "Could do with gaining a few pounds.", - "slayer_task": "75", "melee_animation": "5487", "range_animation": "0", "combat_audio": "774,775,777", @@ -69748,7 +71337,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", @@ -69769,7 +71357,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", @@ -69790,7 +71377,6 @@ }, { "examine": "He's heartless.", - "slayer_task": "75", "melee_animation": "2067", "range_animation": "0", "combat_audio": "774,775,777", @@ -69811,7 +71397,6 @@ }, { "examine": "He's less heavy now.", - "slayer_task": "75", "melee_animation": "390", "range_animation": "0", "combat_audio": "774,775,777", @@ -69832,7 +71417,6 @@ }, { "examine": "Floats like an anvil, hits like a hammer.", - "slayer_task": "75", "melee_animation": "422", "range_animation": "0", "combat_audio": "774,775,777", @@ -70333,6 +71917,21 @@ "name": "Dreven", "id": "2335" }, + { + "examine": "Founder and Director of the Piscatoris Fishing Colony.", + "name": "Herman Caranos", + "id": "3822" + }, + { + "examine": "Engineer for the Piscatoris Fishing Colony.", + "name": "Franklin Caranos", + "id": "3823" + }, + { + "examine": "Secretary and Storemaster for the Piscatoris Fishing Colony.", + "name": "Arnold Lydspor", + "id": "3824" + }, { "examine": "Perhaps this gardener might look after your crops for you.", "name": "Frizzy Skernip", @@ -70508,11 +72107,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", @@ -70551,6 +72145,7 @@ { "examine": "A sea bird.", "name": "Gull", + "water_npc": "true", "id": "2726" }, { @@ -71211,5 +72806,15091 @@ "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", "range_level": "1", "attack_level": "1" + }, + { + "name": "Puro Spawn (Low)", + "id": "6065", + "respawn_delay": "1" + }, + { + "name": "Puro Spawn (Mid)", + "id": "6066", + "respawn_delay": "1" + }, + { + "name": "Puro Spawn (High)", + "id": "6067", + "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" + }, + { + "examine": "He looks dangerous!", + "name": "Turael", + "id": "8273" + }, + { + "examine": "Doesn't seem to want to go away.", + "combat_style": "2", + "melee_animation": "811", + "range_animation": "811", + "attack_speed": "4", + "magic_level": "100", + "defence_animation": "0", + "magic_animation": "811", + "death_animation": "836", + "name": "Skeleton Mage", + "defence_level": "60", + "lifepoints": "80", + "strength_level": "10", + "id": "3851", + "bonuses": "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0", + "range_level": "1", + "attack_level": "10" + }, + { + "examine": "Blaec seems to be covered in sand.", + "name": "Blaec", + "id": "3115" + }, + { + "examine": "Looks otherworldy...", + "name": "Fairy Queen", + "id": "4437" + }, + { + "examine": "A delicate creature from this strange realm.", + "name": "Fairy", + "id": "4443" + }, + { + "examine": "A very strange plant.", + "death_animation": "355", + "name": "Strange Plant", + "defence_level": "300", + "melee_animation": "353", + "lifepoints": "100", + "id": "408", + "magic_level": "200", + "defence_animation": "354" + }, + { + "examine": "A very strange plant.", + "name": "Strange Plant", + "id": "407" + }, + { + "agg_radius": "", + "examine": "A helmetted, barbarian snowman.", + "death_animation": "7560", + "name": "Barbarian Snowman", + "defence_level": "1", + "melee_animation": "7565", + "lifepoints": "20", + "combat_audio": "12,3295,3287", + "id": "6742", + "defence_animation": "7559" + }, + { + "examine": "A snow dwarf.", + "death_animation": "7560", + "name": "Dwarf Snowman", + "defence_level": "1", + "melee_animation": "7565", + "lifepoints": "20", + "combat_audio": "12,3295,3287", + "id": "6744", + "defence_animation": "7559" + }, + { + "examine": "Distinctly heroic.", + "name": "Achietties", + "id": "796" + }, + { + "name": "Wizard Cromperty", + "id": "2328", + "examine": "The hat is a dead give away." + }, + { + "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" + }, + { + "id": "17", + "name": "Schoolgirl" + }, + { + "id": "70", + "name": "Orc" + }, + { + "id": "94", + "name": "Skeleton Mage" + }, + { + "id": "129", + "name": "Skavid" + }, + { + "id": "135", + "name": "Mammoth" + }, + { + "id": "136", + "name": "Mounted terrorchick gnome" + }, + { + "id": "149", + "name": "Gull" + }, + { + "id": "150", + "name": "Gull" + }, + { + "id": "151", + "name": "Fly trap" + }, + { + "id": "152", + "name": "Butterfly" + }, + { + "id": "155", + "name": "Butterfly" + }, + { + "id": "156", + "name": "Butterfly" + }, + { + "id": "157", + "name": "Butterfly" + }, + { + "id": "165", + "name": "Gnome shop keeper" + }, + { + "id": "167", + "name": "Gnome baller" + }, + { + "id": "171", + "name": "Brimstail" + }, + { + "id": "177", + "name": "Witch" + }, + { + "id": "195", + "name": "Bandit" + }, + { + "id": "197", + "name": "Barbarian guard" + }, + { + "id": "200", + "name": "Lord Daquarius" + }, + { + "id": "207", + "name": "Lollk" + }, + { + "id": "209", + "name": "Nulodion" + }, + { + "id": "211", + "name": "Sir Percival" + }, + { + "id": "213", + "name": "Merlin" + }, + { + "id": "215", + "name": "Peasant" + }, + { + "id": "217", + "name": "Crone" + }, + { + "id": "218", + "name": "Galahad" + }, + { + "id": "225", + "name": "Bonzo" + }, + { + "id": "226", + "name": "Morris" + }, + { + "id": "228", + "name": "Big Dave" + }, + { + "id": "229", + "name": "Joshua" + }, + { + "id": "232", + "name": "Austri" + }, + { + "id": "234", + "name": "Fishing spot" + }, + { + "id": "235", + "name": "Fishing spot" + }, + { + "id": "236", + "name": "Fishing spot" + }, + { + "id": "239", + "name": "Sir Lancelot" + }, + { + "id": "240", + "name": "Sir Gawain" + }, + { + "id": "241", + "name": "Sir Kay" + }, + { + "id": "242", + "name": "Sir Bedivere" + }, + { + "id": "243", + "name": "Sir Tristram" + }, + { + "id": "244", + "name": "Sir Pelleas" + }, + { + "id": "245", + "name": "Sir Lucan" + }, + { + "id": "248", + "name": "Morgan Le Faye" + }, + { + "id": "249", + "name": "Merlin" + }, + { + "id": "250", + "name": "The Lady of the Lake" + }, + { + "id": "252", + "name": "Beggar" + }, + { + "id": "260", + "name": "Kelvin" + }, + { + "id": "261", + "name": "Joe" + }, + { + "id": "263", + "name": "Hengrad" + }, + { + "id": "276", + "name": "Winelda" + }, + { + "id": "287", + "name": "Ernest" + }, + { + "id": "292", + "name": "Guard" + }, + { + "id": "301", + "name": "Twig" + }, + { + "id": "302", + "name": "Hadley" + }, + { + "id": "303", + "name": "Gerald" + }, + { + "id": "304", + "name": "Almera" + }, + { + "id": "305", + "name": "Hudon" + }, + { + "id": "306", + "name": "Golrie" + }, + { + "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": "Fishing spot" + }, + { + "id": "337", + "name": "Da Vinci" + }, + { + "id": "343", + "name": "Guidor" + }, + { + "id": "349", + "name": "Kilron" + }, + { + "id": "350", + "name": "Omart" + }, + { + "id": "366", + "name": "Jerico" + }, + { + "id": "379", + "name": "Luthas" + }, + { + "id": "381", + "name": "Dwarf" + }, + { + "id": "383", + "name": "Stankers" + }, + { + "id": "399", + "name": "Legends guard" + }, + { + "id": "400", + "name": "Radimus Erkle" + }, + { + "id": "403", + "name": "Fishing spot" + }, + { + "id": "404", + "name": "Fishing spot" + }, + { + "id": "405", + "name": "Fishing spot" + }, + { + "id": "406", + "name": "Fishing spot" + }, + { + "id": "409", + "name": "Genie" + }, + { + "id": "410", + "name": "Mysterious Old Man" + }, + { + "id": "431", + "name": "Cyrisus" + }, + { + "id": "433", + "name": "Cyrisus" + }, + { + "id": "434", + "name": "Cyrisus" + }, + { + "id": "436", + "name": "Fallen Man" + }, + { + "id": "463", + "name": "Murphy" + }, + { + "id": "464", + "name": "Murphy" + }, + { + "id": "465", + "name": "Murphy" + }, + { + "id": "466", + "name": "Murphy" + }, + { + "id": "467", + "name": "Shark" + }, + { + "id": "468", + "name": "Shark" + }, + { + "id": "471", + "name": "Bolkoy" + }, + { + "id": "472", + "name": "Remsai" + }, + { + "id": "473", + "name": "Khazard trooper" + }, + { + "id": "480", + "name": "Gnome troop" + }, + { + "id": "484", + "name": "Local Gnome" + }, + { + "id": "486", + "name": "Kalron" + }, + { + "id": "488", + "name": "Observatory professor" + }, + { + "id": "510", + "name": "Hajedy" + }, + { + "id": "511", + "name": "Vigroy" + }, + { + "id": "513", + "name": "Yohnus" + }, + { + "id": "514", + "name": "Seravel" + }, + { + "id": "536", + "name": "Valaine" + }, + { + "id": "537", + "name": "Scavvo" + }, + { + "id": "553", + "name": "Aubury" + }, + { + "id": "560", + "name": "Jiminua" + }, + { + "id": "563", + "name": "Arhein" + }, + { + "id": "565", + "name": "Lunderwin" + }, + { + "id": "568", + "name": "Zambo" + }, + { + "id": "575", + "name": "Hickton" + }, + { + "id": "576", + "name": "Harry" + }, + { + "id": "578", + "name": "Frincos" + }, + { + "id": "584", + "name": "Herquin" + }, + { + "id": "585", + "name": "Rommik" + }, + { + "id": "588", + "name": "Davon" + }, + { + "id": "589", + "name": "Zenesha" + }, + { + "id": "590", + "name": "Aemad" + }, + { + "id": "591", + "name": "Kortan" + }, + { + "id": "592", + "name": "Roachey" + }, + { + "id": "593", + "name": "Frenita" + }, + { + "id": "594", + "name": "Nurmof" + }, + { + "id": "597", + "name": "Noterazzo" + }, + { + "id": "600", + "name": "Hudo" + }, + { + "id": "601", + "name": "Rometti" + }, + { + "id": "602", + "name": "Gulluck" + }, + { + "id": "603", + "name": "Heckel Funch" + }, + { + "id": "607", + "name": "Gunnjorn" + }, + { + "id": "608", + "name": "Sir Amik Varze" + }, + { + "id": "612", + "name": "Greldo" + }, + { + "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 baller" + }, + { + "id": "634", + "name": "Gnome winger" + }, + { + "id": "636", + "name": "Cheerleader" + }, + { + "id": "642", + "name": "Katrine" + }, + { + "id": "644", + "name": "Straven" + }, + { + "id": "647", + "name": "King Roald" + }, + { + "id": "653", + "name": "Fairy Queen" + }, + { + "id": "654", + "name": "Shamus" + }, + { + "id": "661", + "name": "Megan" + }, + { + "id": "662", + "name": "Lucy" + }, + { + "id": "664", + "name": "Boot" + }, + { + "id": "666", + "name": "Caleb" + }, + { + "id": "668", + "name": "Johnathon" + }, + { + "id": "669", + "name": "Hazelmere" + }, + { + "id": "671", + "name": "Glough" + }, + { + "id": "672", + "name": "Anita" + }, + { + "id": "673", + "name": "Charlie" + }, + { + "id": "675", + "name": "Shipyard worker" + }, + { + "id": "676", + "name": "Femi" + }, + { + "id": "685", + "name": "Tower Advisor" + }, + { + "id": "686", + "name": "Tower Advisor" + }, + { + "id": "687", + "name": "Tower Advisor" + }, + { + "id": "693", + "name": "Competition Judge" + }, + { + "id": "695", + "name": "Bailey" + }, + { + "id": "697", + "name": "Holgart" + }, + { + "id": "699", + "name": "Holgart" + }, + { + "id": "700", + "name": "Holgart" + }, + { + "id": "701", + "name": "Kent" + }, + { + "id": "702", + "name": "Fisherman" + }, + { + "id": "703", + "name": "Fisherman" + }, + { + "id": "704", + "name": "Fisherman" + }, + { + "id": "710", + "name": "Alrena" + }, + { + "id": "711", + "name": "Bravek" + }, + { + "id": "712", + "name": "Carla" + }, + { + "id": "721", + "name": "Ted Rehnison" + }, + { + "id": "722", + "name": "Martha Rehnison" + }, + { + "id": "723", + "name": "Billy Rehnison" + }, + { + "id": "724", + "name": "Milli Rehnison" + }, + { + "id": "725", + "name": "Jethick" + }, + { + "id": "740", + "name": "Trufitus" + }, + { + "id": "746", + "name": "Oracle" + }, + { + "id": "748", + "name": "Angry barbarian spirit" + }, + { + "id": "754", + "name": "Peaceful barbarian spirit" + }, + { + "id": "759", + "name": "Kittens" + }, + { + "id": "761", + "name": "Kitten" + }, + { + "id": "762", + "name": "Kitten" + }, + { + "id": "763", + "name": "Kitten" + }, + { + "id": "764", + "name": "Kitten" + }, + { + "id": "765", + "name": "Kitten" + }, + { + "id": "766", + "name": "Kitten" + }, + { + "id": "768", + "name": "Cat" + }, + { + "id": "769", + "name": "Cat" + }, + { + "id": "770", + "name": "Cat" + }, + { + "id": "771", + "name": "Cat" + }, + { + "id": "772", + "name": "Cat" + }, + { + "id": "773", + "name": "Cat" + }, + { + "id": "774", + "name": "Overgrown cat" + }, + { + "id": "775", + "name": "Overgrown cat" + }, + { + "id": "776", + "name": "Overgrown cat" + }, + { + "id": "777", + "name": "Overgrown cat" + }, + { + "id": "779", + "name": "Overgrown cat" + }, + { + "id": "780", + "name": "Gertrude" + }, + { + "id": "782", + "name": "Philop" + }, + { + "id": "784", + "name": "Kanel" + }, + { + "id": "788", + "name": "Garv" + }, + { + "id": "789", + "name": "Grubor" + }, + { + "id": "791", + "name": "Seth" + }, + { + "id": "792", + "name": "Grip" + }, + { + "id": "797", + "name": "Helemos" + }, + { + "id": "807", + "name": "Pierre" + }, + { + "id": "808", + "name": "Hobbes" + }, + { + "id": "809", + "name": "Louisa" + }, + { + "id": "810", + "name": "Mary" + }, + { + "id": "811", + "name": "Stanford" + }, + { + "id": "814", + "name": "Anna" + }, + { + "id": "815", + "name": "Bob" + }, + { + "id": "816", + "name": "Carol" + }, + { + "id": "817", + "name": "David" + }, + { + "id": "818", + "name": "Elizabeth" + }, + { + "id": "819", + "name": "Frank" + }, + { + "id": "822", + "name": "Ana" + }, + { + "id": "823", + "name": "Ana" + }, + { + "id": "824", + "name": "Female slave" + }, + { + "id": "826", + "name": "Escaping slave" + }, + { + "id": "828", + "name": "Shanty Claws" + }, + { + "id": "829", + "name": "Mercenary Captain" + }, + { + "id": "832", + "name": "Al Shabim" + }, + { + "id": "844", + "name": "Horacio" + }, + { + "id": "846", + "name": "Kangai Mau" + }, + { + "id": "848", + "name": "Blurberry" + }, + { + "id": "849", + "name": "Barman" + }, + { + "id": "853", + "name": "Og" + }, + { + "id": "854", + "name": "Grew" + }, + { + "id": "855", + "name": "Toban" + }, + { + "id": "857", + "name": "Ogre guard" + }, + { + "id": "860", + "name": "Ogre guard" + }, + { + "id": "861", + "name": "Ogre guard" + }, + { + "id": "865", + "name": "Skavid" + }, + { + "id": "866", + "name": "Skavid" + }, + { + "id": "867", + "name": "Skavid" + }, + { + "id": "868", + "name": "Skavid" + }, + { + "id": "869", + "name": "Skavid" + }, + { + "id": "871", + "name": "Watchtower Wizard" + }, + { + "id": "876", + "name": "Ogre trader" + }, + { + "id": "880", + "name": "Weakened Delrith" + }, + { + "id": "886", + "name": "Claus the chef" + }, + { + "id": "888", + "name": "Philipe Carnillean" + }, + { + "id": "889", + "name": "Henryeta Carnillean" + }, + { + "id": "890", + "name": "Butler Jones" + }, + { + "id": "891", + "name": "Alomone" + }, + { + "id": "892", + "name": "Hazeel" + }, + { + "id": "896", + "name": "Nora T. Hagg" + }, + { + "id": "901", + "name": "Mouse" + }, + { + "id": "902", + "name": "Gundai" + }, + { + "id": "903", + "name": "Lundail" + }, + { + "id": "906", + "name": "Kolodion" + }, + { + "id": "918", + "name": "Ned" + }, + { + "id": "921", + "name": "Prince Ali" + }, + { + "id": "926", + "name": "Border Guard" + }, + { + "id": "927", + "name": "Fishing spot" + }, + { + "id": "928", + "name": "Gujuo" + }, + { + "id": "929", + "name": "Ungadulu" + }, + { + "id": "930", + "name": "Ungadulu" + }, + { + "id": "933", + "name": "Siegfried Erkle" + }, + { + "id": "935", + "name": "Viyeldi" + }, + { + "id": "936", + "name": "San Tojalon" + }, + { + "id": "937", + "name": "Irvig Senay" + }, + { + "id": "940", + "name": "Echned Zekin" + }, + { + "id": "952", + "name": "Fishing spot" + }, + { + "id": "953", + "name": "Banker" + }, + { + "id": "992", + "name": "Kardia" + }, + { + "id": "994", + "name": "Niloof" + }, + { + "id": "1003", + "name": "Lord Iban" + }, + { + "id": "1008", + "name": "Hamid" + }, + { + "id": "1011", + "name": "Fycie" + }, + { + "id": "1012", + "name": "Bugs" + }, + { + "id": "1014", + "name": "Bloated Toad" + }, + { + "id": "1016", + "name": "Chompy bird" + }, + { + "id": "1024", + "name": "Baby impling" + }, + { + "id": "1036", + "name": "Banker" + }, + { + "id": "1038", + "name": "Rufus" + }, + { + "id": "1039", + "name": "Barker" + }, + { + "id": "1040", + "name": "Fidelio" + }, + { + "id": "1041", + "name": "Sbott" + }, + { + "id": "1042", + "name": "Roavar" + }, + { + "id": "1043", + "name": "Will o' the wisp" + }, + { + "id": "1047", + "name": "Drezel" + }, + { + "id": "1056", + "name": "Mime" + }, + { + "id": "1070", + "name": "Saba" + }, + { + "id": "1071", + "name": "Tenzing" + }, + { + "id": "1075", + "name": "Archer" + }, + { + "id": "1091", + "name": "Bob" + }, + { + "id": "1093", + "name": "Billy" + }, + { + "id": "1094", + "name": "Mountain goat" + }, + { + "id": "1113", + "name": "Eadgar" + }, + { + "id": "1114", + "name": "Godric" + }, + { + "id": "1159", + "name": "Kalphite Queen" + }, + { + "id": "1162", + "name": "Timfraku" + }, + { + "id": "1163", + "name": "Tiadeche" + }, + { + "id": "1164", + "name": "Tiadeche" + }, + { + "id": "1165", + "name": "Tinsay" + }, + { + "id": "1166", + "name": "Tinsay" + }, + { + "id": "1167", + "name": "Tamayu" + }, + { + "id": "1168", + "name": "Tamayu" + }, + { + "id": "1169", + "name": "Tamayu" + }, + { + "id": "1170", + "name": "Tamayu" + }, + { + "id": "1171", + "name": "Lubufu" + }, + { + "id": "1173", + "name": "The Shaikahan" + }, + { + "id": "1174", + "name": "Fishing spot" + }, + { + "id": "1175", + "name": "Fishing spot" + }, + { + "id": "1177", + "name": "Fishing spot" + }, + { + "id": "1178", + "name": "Fishing spot" + }, + { + "id": "1180", + "name": "Cormorant" + }, + { + "id": "1181", + "name": "Pelican" + }, + { + "id": "1182", + "name": "Lord Iorwerth" + }, + { + "id": "1186", + "name": "Idris" + }, + { + "id": "1187", + "name": "Essyllt" + }, + { + "id": "1188", + "name": "Morvran" + }, + { + "id": "1189", + "name": "Fishing spot" + }, + { + "id": "1190", + "name": "Fishing spot" + }, + { + "id": "1191", + "name": "Fishing spot" + }, + { + "id": "1202", + "name": "Arianwyn" + }, + { + "id": "1205", + "name": "Tyras guard" + }, + { + "id": "1207", + "name": "Quartermaster" + }, + { + "id": "1221", + "name": "Spider" + }, + { + "id": "1222", + "name": "Mist" + }, + { + "id": "1224", + "name": "Vampyric hound" + }, + { + "id": "1226", + "name": "Tree" + }, + { + "id": "1236", + "name": "Fishing spot" + }, + { + "id": "1237", + "name": "Fishing spot" + }, + { + "id": "1238", + "name": "Fishing spot" + }, + { + "id": "1242", + "name": "Shade Spirit" + }, + { + "id": "1251", + "name": "Afflicted(Ulsquire)" + }, + { + "id": "1252", + "name": "Ulsquire Shauncy" + }, + { + "id": "1253", + "name": "Afflicted(Razmire)" + }, + { + "id": "1254", + "name": "Razmire Keelgan" + }, + { + "id": "1255", + "name": "Mort'ton Local" + }, + { + "id": "1256", + "name": "Mort'ton Local" + }, + { + "id": "1259", + "name": "Mort'ton local" + }, + { + "id": "1260", + "name": "Mort'ton local" + }, + { + "id": "1280", + "name": "Butterfly" + }, + { + "id": "1284", + "name": "Bjorn" + }, + { + "id": "1285", + "name": "Eldgrim" + }, + { + "id": "1295", + "name": "Askeladden" + }, + { + "id": "1299", + "name": "Town Guard" + }, + { + "id": "1310", + "name": "Freygerd" + }, + { + "id": "1311", + "name": "Lensa" + }, + { + "id": "1312", + "name": "Jennella" + }, + { + "id": "1322", + "name": "Gull" + }, + { + "id": "1323", + "name": "Gull" + }, + { + "id": "1324", + "name": "Gull" + }, + { + "id": "1325", + "name": "Gull" + }, + { + "id": "1331", + "name": "Fishing spot" + }, + { + "id": "1332", + "name": "Fishing spot" + }, + { + "id": "1333", + "name": "Fishing spot" + }, + { + "id": "1334", + "name": "Jossik" + }, + { + "id": "1335", + "name": "Jossik" + }, + { + "id": "1336", + "name": "Larrissa" + }, + { + "id": "1337", + "name": "Larrissa" + }, + { + "id": "1348", + "name": "Dagannoth mother" + }, + { + "id": "1349", + "name": "Dagannoth mother" + }, + { + "id": "1350", + "name": "Dagannoth mother" + }, + { + "id": "1359", + "name": "Queen Sigrid" + }, + { + "id": "1361", + "name": "Arnor" + }, + { + "id": "1362", + "name": "Haming" + }, + { + "id": "1363", + "name": "Moldof" + }, + { + "id": "1364", + "name": "Helga" + }, + { + "id": "1365", + "name": "Matilda" + }, + { + "id": "1366", + "name": "Ashild" + }, + { + "id": "1371", + "name": "Prince Brand" + }, + { + "id": "1372", + "name": "Princess Astrid" + }, + { + "id": "1373", + "name": "King Vargas" + }, + { + "id": "1375", + "name": "Advisor Ghrim" + }, + { + "id": "1383", + "name": "Halla" + }, + { + "id": "1384", + "name": "Yrsa" + }, + { + "id": "1387", + "name": "Thora" + }, + { + "id": "1399", + "name": "Fishing spot" + }, + { + "id": "1400", + "name": "Gull" + }, + { + "id": "1405", + "name": "Fishing spot" + }, + { + "id": "1406", + "name": "Fishing spot" + }, + { + "id": "1407", + "name": "Daero" + }, + { + "id": "1408", + "name": "Waydar" + }, + { + "id": "1409", + "name": "Waydar" + }, + { + "id": "1410", + "name": "Waydar" + }, + { + "id": "1411", + "name": "Garkor" + }, + { + "id": "1412", + "name": "Garkor" + }, + { + "id": "1413", + "name": "Lumo" + }, + { + "id": "1414", + "name": "Lumo" + }, + { + "id": "1415", + "name": "Bunkdo" + }, + { + "id": "1416", + "name": "Bunkdo" + }, + { + "id": "1417", + "name": "Carado" + }, + { + "id": "1418", + "name": "Carado" + }, + { + "id": "1420", + "name": "Karam" + }, + { + "id": "1421", + "name": "Karam" + }, + { + "id": "1422", + "name": "Karam" + }, + { + "id": "1423", + "name": "Bunkwicket" + }, + { + "id": "1424", + "name": "Waymottin" + }, + { + "id": "1425", + "name": "Zooknock" + }, + { + "id": "1426", + "name": "Zooknock" + }, + { + "id": "1428", + "name": "G.L.O. Caranock" + }, + { + "id": "1429", + "name": "Dugopul" + }, + { + "id": "1430", + "name": "Salenab" + }, + { + "id": "1431", + "name": "Trefaji" + }, + { + "id": "1432", + "name": "Aberab" + }, + { + "id": "1433", + "name": "Solihib" + }, + { + "id": "1434", + "name": "Daga" + }, + { + "id": "1435", + "name": "Tutab" + }, + { + "id": "1436", + "name": "Ifaba" + }, + { + "id": "1437", + "name": "Hamab" + }, + { + "id": "1438", + "name": "Hafuba" + }, + { + "id": "1439", + "name": "Denadu" + }, + { + "id": "1440", + "name": "Lofu" + }, + { + "id": "1441", + "name": "Kruk" + }, + { + "id": "1448", + "name": "Awowogei" + }, + { + "id": "1449", + "name": "Uwogo" + }, + { + "id": "1450", + "name": "Muruwoi" + }, + { + "id": "1462", + "name": "Elder Guard" + }, + { + "id": "1468", + "name": "Bonzara" + }, + { + "id": "1470", + "name": "Foreman" + }, + { + "id": "1474", + "name": "Spider" + }, + { + "id": "1482", + "name": "Gorilla" + }, + { + "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": "Dummy" + }, + { + "id": "1508", + "name": "Forester" + }, + { + "id": "1509", + "name": "Woman-at-arms" + }, + { + "id": "1510", + "name": "Apprentice" + }, + { + "id": "1511", + "name": "Ranger" + }, + { + "id": "1512", + "name": "Adventurer" + }, + { + "id": "1513", + "name": "Mage" + }, + { + "id": "1515", + "name": "Nail beast" + }, + { + "id": "1522", + "name": "Nail beast" + }, + { + "id": "1523", + "name": "Nail beast" + }, + { + "id": "1525", + "name": "Undead Lumberjack" + }, + { + "id": "1526", + "name": "Lanthus" + }, + { + "id": "1527", + "name": "Mine cart" + }, + { + "id": "1529", + "name": "Sheep" + }, + { + "id": "1530", + "name": "Rabbit" + }, + { + "id": "1542", + "name": "Loading crane" + }, + { + "id": "1543", + "name": "Innocent-looking key" + }, + { + "id": "1544", + "name": "Mine cart" + }, + { + "id": "1545", + "name": "Mine cart" + }, + { + "id": "1546", + "name": "Mine cart" + }, + { + "id": "1547", + "name": "Mine cart" + }, + { + "id": "1548", + "name": "Mine cart" + }, + { + "id": "1552", + "name": "Santa" + }, + { + "id": "1554", + "name": "Aga" + }, + { + "id": "1555", + "name": "Arrg" + }, + { + "id": "1559", + "name": "Ice wolf" + }, + { + "id": "1568", + "name": "Curpile Fyod" + }, + { + "id": "1569", + "name": "Veliaf Hurtz" + }, + { + "id": "1570", + "name": "Sani Piliu" + }, + { + "id": "1571", + "name": "Harold Evans" + }, + { + "id": "1572", + "name": "Radigad Ponfit" + }, + { + "id": "1573", + "name": "Polmafi Ferdygris" + }, + { + "id": "1574", + "name": "Ivan Strom" + }, + { + "id": "1575", + "name": "Skeleton Hellhound" + }, + { + "id": "1576", + "name": "Stranger" + }, + { + "id": "1577", + "name": "Vanstrom Klause" + }, + { + "id": "1578", + "name": "Mist" + }, + { + "id": "1579", + "name": "Vanstrom Klause" + }, + { + "id": "1580", + "name": "Vanstrom Klause" + }, + { + "id": "1581", + "name": "Vanstrom Klause" + }, + { + "id": "1595", + "name": "Saniboch" + }, + { + "id": "1596", + "name": "Vannaka" + }, + { + "id": "1599", + "name": "Cave crawler" + }, + { + "id": "1659", + "name": "Skullball" + }, + { + "id": "1664", + "name": "Agility Trainer" + }, + { + "id": "1666", + "name": "Dr Fenkenstrain" + }, + { + "id": "1671", + "name": "Fenkenstrain's Monster" + }, + { + "id": "1674", + "name": "Lord Rologarth" + }, + { + "id": "1679", + "name": "Eluned" + }, + { + "id": "1680", + "name": "Islwyn" + }, + { + "id": "1682", + "name": "Golrie" + }, + { + "id": "1683", + "name": "Velorina" + }, + { + "id": "1685", + "name": "Gravingas" + }, + { + "id": "1687", + "name": "Ak-Haranu" + }, + { + "id": "1689", + "name": "Undead cow" + }, + { + "id": "1694", + "name": "Robin" + }, + { + "id": "1709", + "name": "Johanhus Ulsbrecht" + }, + { + "id": "1719", + "name": "Tree" + }, + { + "id": "1720", + "name": "Tree" + }, + { + "id": "1721", + "name": "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": "Dead tree" + }, + { + "id": "1732", + "name": "Dramen tree" + }, + { + "id": "1734", + "name": "Magic tree" + }, + { + "id": "1735", + "name": "Maple tree" + }, + { + "id": "1736", + "name": "Willow" + }, + { + "id": "1737", + "name": "Willow" + }, + { + "id": "1738", + "name": "Willow" + }, + { + "id": "1749", + "name": "Hollow tree" + }, + { + "id": "1750", + "name": "Hollow tree" + }, + { + "id": "1753", + "name": "Mounted terrorbird gnome" + }, + { + "id": "1756", + "name": "Crow" + }, + { + "id": "1762", + "name": "Sheep" + }, + { + "id": "1764", + "name": "Sheep" + }, + { + "id": "1765", + "name": "Sheep" + }, + { + "id": "1777", + "name": "Ilfeen" + }, + { + "id": "1778", + "name": "William" + }, + { + "id": "1779", + "name": "Ian" + }, + { + "id": "1780", + "name": "Larry" + }, + { + "id": "1781", + "name": "Darren" + }, + { + "id": "1782", + "name": "Edward" + }, + { + "id": "1784", + "name": "Neil" + }, + { + "id": "1786", + "name": "Simon" + }, + { + "id": "1787", + "name": "Sam" + }, + { + "id": "1788", + "name": "Lumdo" + }, + { + "id": "1789", + "name": "Bunkwicket" + }, + { + "id": "1790", + "name": "Waymottin" + }, + { + "id": "1791", + "name": "Jungle Tree" + }, + { + "id": "1792", + "name": "Jungle Tree" + }, + { + "id": "1793", + "name": "Tassie Slipcast" + }, + { + "id": "1798", + "name": "Phantuwti Fanstuwi Farsight" + }, + { + "id": "1799", + "name": "Tindel Marchant" + }, + { + "id": "1800", + "name": "Gnormadium Avlafrim" + }, + { + "id": "1801", + "name": "Petra Fiyed" + }, + { + "id": "1804", + "name": "Slagilith" + }, + { + "id": "1808", + "name": "Ragnar" + }, + { + "id": "1809", + "name": "Svidi" + }, + { + "id": "1810", + "name": "Jokul" + }, + { + "id": "1811", + "name": "The Kendal" + }, + { + "id": "1821", + "name": "Bald Headed Eagle" + }, + { + "id": "1826", + "name": "Zombie" + }, + { + "id": "1830", + "name": "Frog" + }, + { + "id": "1835", + "name": "Easter Bunny" + }, + { + "id": "1836", + "name": "Dondakan the Dwarf" + }, + { + "id": "1838", + "name": "Dondakan the Dwarf" + }, + { + "id": "1839", + "name": "Dondakan the Dwarf" + }, + { + "id": "1841", + "name": "Rolad" + }, + { + "id": "1845", + "name": "Dwarven Boatman" + }, + { + "id": "1847", + "name": "Miodvetnir" + }, + { + "id": "1848", + "name": "Dernu" + }, + { + "id": "1849", + "name": "Derni" + }, + { + "id": "1860", + "name": "Brian" + }, + { + "id": "1862", + "name": "Ali Morrisane" + }, + { + "id": "1869", + "name": "Ali the Mayor" + }, + { + "id": "1876", + "name": "Bandit Leader" + }, + { + "id": "1879", + "name": "Bandit" + }, + { + "id": "1881", + "name": "Bandit" + }, + { + "id": "1882", + "name": "Sir Palomedes" + }, + { + "id": "1883", + "name": "Sir Palomedes" + }, + { + "id": "1884", + "name": "Trobert" + }, + { + "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": "Villager" + }, + { + "id": "1899", + "name": "Menaphite Leader" + }, + { + "id": "1903", + "name": "Menaphite Thug" + }, + { + "id": "1907", + "name": "Broken clay golem" + }, + { + "id": "1909", + "name": "Damaged clay golem" + }, + { + "id": "1910", + "name": "Clay golem" + }, + { + "id": "1912", + "name": "Elissa" + }, + { + "id": "1922", + "name": "Eblis" + }, + { + "id": "1924", + "name": "Eblis" + }, + { + "id": "1927", + "name": "Bandit" + }, + { + "id": "1928", + "name": "Bandit" + }, + { + "id": "1929", + "name": "Bandit" + }, + { + "id": "1930", + "name": "Bandit" + }, + { + "id": "1932", + "name": "Troll child" + }, + { + "id": "1934", + "name": "Troll child" + }, + { + "id": "1943", + "name": "Ice block" + }, + { + "id": "1945", + "name": "Ice block" + }, + { + "id": "1957", + "name": "Mummy" + }, + { + "id": "1959", + "name": "Mummy" + }, + { + "id": "1960", + "name": "Mummy ashes" + }, + { + "id": "1966", + "name": "Mummy" + }, + { + "id": "1968", + "name": "Mummy" + }, + { + "id": "1970", + "name": "Azzanadra" + }, + { + "id": "1982", + "name": "Raetul" + }, + { + "id": "1983", + "name": "Siamun" + }, + { + "id": "1984", + "name": "High Priest" + }, + { + "id": "1987", + "name": "Priest" + }, + { + "id": "1989", + "name": "Priest" + }, + { + "id": "1990", + "name": "Sphinx" + }, + { + "id": "1992", + "name": "Neite" + }, + { + "id": "1996", + "name": "Vulture" + }, + { + "id": "2000", + "name": "Plague cow" + }, + { + "id": "2002", + "name": "Wanderer" + }, + { + "id": "2006", + "name": "Wanderer" + }, + { + "id": "2007", + "name": "Het" + }, + { + "id": "2008", + "name": "Apmeken" + }, + { + "id": "2009", + "name": "Scabaras" + }, + { + "id": "2010", + "name": "Crondis" + }, + { + "id": "2011", + "name": "Icthlarin" + }, + { + "id": "2013", + "name": "Klenter" + }, + { + "id": "2020", + "name": "Light creature" + }, + { + "id": "2022", + "name": "Light creature" + }, + { + "id": "2023", + "name": "Juna" + }, + { + "id": "2038", + "name": "Grish" + }, + { + "id": "2039", + "name": "Uglug Nar" + }, + { + "id": "2040", + "name": "Pilg" + }, + { + "id": "2041", + "name": "Grug" + }, + { + "id": "2042", + "name": "Ogre guard" + }, + { + "id": "2043", + "name": "Ogre guard" + }, + { + "id": "2059", + "name": "Zavistic Rarve" + }, + { + "id": "2061", + "name": "Sithik Ints" + }, + { + "id": "2062", + "name": "Sithik Ints" + }, + { + "id": "2063", + "name": "Gargh" + }, + { + "id": "2064", + "name": "Scarg" + }, + { + "id": "2065", + "name": "Gruh" + }, + { + "id": "2066", + "name": "Irwin Feaselbaum" + }, + { + "id": "2068", + "name": "Fishing spot" + }, + { + "id": "2079", + "name": "Sigmund" + }, + { + "id": "2083", + "name": "Sigmund" + }, + { + "id": "2084", + "name": "Mistag" + }, + { + "id": "2085", + "name": "Kazgar" + }, + { + "id": "2087", + "name": "Ur-tag" + }, + { + "id": "2088", + "name": "Duke Horacio" + }, + { + "id": "2089", + "name": "Mistag" + }, + { + "id": "2090", + "name": "Sigmund" + }, + { + "id": "2099", + "name": "Red Axe Secretary" + }, + { + "id": "2107", + "name": "Red Axe Director" + }, + { + "id": "2108", + "name": "Red Axe Cat" + }, + { + "id": "2123", + "name": "Trader" + }, + { + "id": "2125", + "name": "Trader" + }, + { + "id": "2128", + "name": "Supreme Commander" + }, + { + "id": "2129", + "name": "Commander Veldaban" + }, + { + "id": "2137", + "name": "Gnome emissary" + }, + { + "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": "Riki the sculptor's model" + }, + { + "id": "2151", + "name": "Vigr" + }, + { + "id": "2152", + "name": "Santiri" + }, + { + "id": "2153", + "name": "Saro" + }, + { + "id": "2155", + "name": "Wemund" + }, + { + "id": "2156", + "name": "Randivor" + }, + { + "id": "2157", + "name": "Hervi" + }, + { + "id": "2159", + "name": "Gulldamar" + }, + { + "id": "2161", + "name": "Agmundi" + }, + { + "id": "2162", + "name": "Vermundi" + }, + { + "id": "2166", + "name": "Assistant" + }, + { + "id": "2169", + "name": "Dromund" + }, + { + "id": "2176", + "name": "Inn Keeper" + }, + { + "id": "2179", + "name": "Barman" + }, + { + "id": "2188", + "name": "Hegir" + }, + { + "id": "2189", + "name": "Haera" + }, + { + "id": "2194", + "name": "Reinald" + }, + { + "id": "2196", + "name": "Gauss" + }, + { + "id": "2197", + "name": "Myndill" + }, + { + "id": "2199", + "name": "Tombar" + }, + { + "id": "2200", + "name": "Odmar" + }, + { + "id": "2204", + "name": "Drunken Dwarf" + }, + { + "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": "Dwarven Miner" + }, + { + "id": "2219", + "name": "Purple Pewter Director" + }, + { + "id": "2220", + "name": "Purple Pewter Director" + }, + { + "id": "2221", + "name": "Blue Opal Director" + }, + { + "id": "2222", + "name": "Yellow Fortune Director" + }, + { + "id": "2223", + "name": "Green Gemstone Director" + }, + { + "id": "2224", + "name": "White Chisel Director" + }, + { + "id": "2225", + "name": "Silver Cog Director" + }, + { + "id": "2226", + "name": "Brown Engine Director" + }, + { + "id": "2227", + "name": "Red Axe Director" + }, + { + "id": "2228", + "name": "Commander Veldaban" + }, + { + "id": "2229", + "name": "Red Axe Cat" + }, + { + "id": "2230", + "name": "Red Axe Cat" + }, + { + "id": "2231", + "name": "Black Guard Berserker" + }, + { + "id": "2233", + "name": "Olivia" + }, + { + "id": "2238", + "name": "Donie" + }, + { + "id": "2239", + "name": "Pig" + }, + { + "id": "2251", + "name": "Gnome" + }, + { + "id": "2252", + "name": "Crow" + }, + { + "id": "2254", + "name": "Bed" + }, + { + "id": "2255", + "name": "Thing under the bed" + }, + { + "id": "2257", + "name": "Mage of Zamorak" + }, + { + "id": "2259", + "name": "Mage of Zamorak" + }, + { + "id": "2260", + "name": "Mage of Zamorak" + }, + { + "id": "2266", + "name": "Brian O'Richard" + }, + { + "id": "2268", + "name": "Rogue Guard" + }, + { + "id": "2270", + "name": "Martin Thwait" + }, + { + "id": "2273", + "name": "Spin Blades" + }, + { + "id": "2295", + "name": "Rug Merchant" + }, + { + "id": "2297", + "name": "Rug Merchant" + }, + { + "id": "2299", + "name": "Rug Station Attendant" + }, + { + "id": "2302", + "name": "Sarah" + }, + { + "id": "2305", + "name": "Vanessa" + }, + { + "id": "2306", + "name": "Richard" + }, + { + "id": "2307", + "name": "Alice" + }, + { + "id": "2308", + "name": "Capt' Arnav" + }, + { + "id": "2322", + "name": "Metarialus" + }, + { + "id": "2345", + "name": "Sick-looking sheep (1)" + }, + { + "id": "2346", + "name": "Sick-looking sheep (2)" + }, + { + "id": "2347", + "name": "Sick-looking sheep (3)" + }, + { + "id": "2348", + "name": "Sick-looking sheep (4)" + }, + { + "id": "2349", + "name": "Mourner" + }, + { + "id": "2350", + "name": "Mourner" + }, + { + "id": "2351", + "name": "Mourner" + }, + { + "id": "2352", + "name": "Eudav" + }, + { + "id": "2353", + "name": "Oronwen" + }, + { + "id": "2356", + "name": "Dalldav" + }, + { + "id": "2357", + "name": "Gethin" + }, + { + "id": "2358", + "name": "Arianwyn" + }, + { + "id": "2363", + "name": "Goreu" + }, + { + "id": "2364", + "name": "Ysgawyn" + }, + { + "id": "2365", + "name": "Arvel" + }, + { + "id": "2366", + "name": "Mawrth" + }, + { + "id": "2367", + "name": "Kelyn" + }, + { + "id": "2368", + "name": "Eoin" + }, + { + "id": "2369", + "name": "Iona" + }, + { + "id": "2370", + "name": "Gnome" + }, + { + "id": "2375", + "name": "Eluned" + }, + { + "id": "2377", + "name": "Sick-looking sheep (1)" + }, + { + "id": "2378", + "name": "Sick-looking sheep (2)" + }, + { + "id": "2379", + "name": "Sick-looking sheep (3)" + }, + { + "id": "2380", + "name": "Sick-looking sheep (4)" + }, + { + "id": "2381", + "name": "Mysterious ghost" + }, + { + "id": "2403", + "name": "Cart conductor" + }, + { + "id": "2410", + "name": "Red Axe Director" + }, + { + "id": "2411", + "name": "Red Axe Director" + }, + { + "id": "2412", + "name": "Red Axe Henchman" + }, + { + "id": "2413", + "name": "Red Axe Henchman" + }, + { + "id": "2414", + "name": "Red Axe Henchman" + }, + { + "id": "2415", + "name": "Colonel Grimsson" + }, + { + "id": "2416", + "name": "Colonel Grimsson" + }, + { + "id": "2417", + "name": "Ogre shaman" + }, + { + "id": "2418", + "name": "Ogre shaman" + }, + { + "id": "2419", + "name": "Grunsh" + }, + { + "id": "2420", + "name": "Gnome emissary" + }, + { + "id": "2421", + "name": "Gnome companion" + }, + { + "id": "2422", + "name": "Gnome companion" + }, + { + "id": "2424", + "name": "Gunslik" + }, + { + "id": "2425", + "name": "Nolar" + }, + { + "id": "2426", + "name": "Factory Worker" + }, + { + "id": "2427", + "name": "Cart conductor" + }, + { + "id": "2428", + "name": "Gauss" + }, + { + "id": "2429", + "name": "Drunken Dwarf" + }, + { + "id": "2430", + "name": "Rowdy dwarf" + }, + { + "id": "2431", + "name": "Ulifed" + }, + { + "id": "2432", + "name": "Red Axe Henchman" + }, + { + "id": "2433", + "name": "Red Axe Henchman" + }, + { + "id": "2434", + "name": "Ogre shaman" + }, + { + "id": "2435", + "name": "Jarvald" + }, + { + "id": "2437", + "name": "Jarvald" + }, + { + "id": "2438", + "name": "Jarvald" + }, + { + "id": "2440", + "name": "Door-support" + }, + { + "id": "2441", + "name": "Door" + }, + { + "id": "2442", + "name": "Door" + }, + { + "id": "2444", + "name": "Door" + }, + { + "id": "2445", + "name": "Door" + }, + { + "id": "2446", + "name": "Door-support" + }, + { + "id": "2447", + "name": "Door" + }, + { + "id": "2448", + "name": "Door" + }, + { + "id": "2449", + "name": "Egg" + }, + { + "id": "2450", + "name": "Egg" + }, + { + "id": "2451", + "name": "Egg" + }, + { + "id": "2458", + "name": "Freaky Forester" + }, + { + "id": "2469", + "name": "Frog" + }, + { + "id": "2470", + "name": "Frog" + }, + { + "id": "2471", + "name": "Frog" + }, + { + "id": "2472", + "name": "Frog" + }, + { + "id": "2473", + "name": "Frog" + }, + { + "id": "2474", + "name": "Frog prince" + }, + { + "id": "2475", + "name": "Frog princess" + }, + { + "id": "2478", + "name": "Evil Bob" + }, + { + "id": "2480", + "name": "Servant" + }, + { + "id": "2483", + "name": "Bush snake" + }, + { + "id": "2498", + "name": "Broodoo victim" + }, + { + "id": "2500", + "name": "Broodoo victim" + }, + { + "id": "2502", + "name": "Broodoo victim" + }, + { + "id": "2504", + "name": "Sharimika" + }, + { + "id": "2507", + "name": "Mamma Bufetta" + }, + { + "id": "2510", + "name": "Layleen" + }, + { + "id": "2513", + "name": "Karaday" + }, + { + "id": "2516", + "name": "Safta Doc" + }, + { + "id": "2519", + "name": "Gabooty" + }, + { + "id": "2522", + "name": "Fanellaman" + }, + { + "id": "2525", + "name": "Jagbakoba" + }, + { + "id": "2528", + "name": "Murcaily" + }, + { + "id": "2531", + "name": "Rionasta" + }, + { + "id": "2534", + "name": "Mahogany" + }, + { + "id": "2535", + "name": "Teak" + }, + { + "id": "2536", + "name": "Niles" + }, + { + "id": "2537", + "name": "Miles" + }, + { + "id": "2538", + "name": "Giles" + }, + { + "id": "2540", + "name": "Dr Jekyll" + }, + { + "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": "Mr Hyde" + }, + { + "id": "2548", + "name": "Blackjack seller" + }, + { + "id": "2550", + "name": "Dwarven Miner" + }, + { + "id": "2551", + "name": "Dwarven Miner" + }, + { + "id": "2552", + "name": "Dwarven Miner" + }, + { + "id": "2554", + "name": "Tin ore" + }, + { + "id": "2555", + "name": "Copper ore" + }, + { + "id": "2556", + "name": "Iron ore" + }, + { + "id": "2557", + "name": "Mithril ore" + }, + { + "id": "2558", + "name": "Adamantite ore" + }, + { + "id": "2559", + "name": "Runite ore" + }, + { + "id": "2560", + "name": "Silver ore" + }, + { + "id": "2561", + "name": "Gold ore" + }, + { + "id": "2562", + "name": "Coal" + }, + { + "id": "2563", + "name": "Perfect gold ore" + }, + { + "id": "2566", + "name": "Wise Old Man" + }, + { + "id": "2567", + "name": "Wise Old Man" + }, + { + "id": "2568", + "name": "Banker" + }, + { + "id": "2569", + "name": "Banker" + }, + { + "id": "2570", + "name": "Banker" + }, + { + "id": "2573", + "name": "Pillory Guard" + }, + { + "id": "2575", + "name": "Purepker895" + }, + { + "id": "2576", + "name": "Qutiedoll" + }, + { + "id": "2577", + "name": "1337sp34kr" + }, + { + "id": "2578", + "name": "Elfinlocks" + }, + { + "id": "2579", + "name": "Cool Mom227" + }, + { + "id": "2581", + "name": "Ellamaria" + }, + { + "id": "2582", + "name": "Trolley" + }, + { + "id": "2583", + "name": "Trolley" + }, + { + "id": "2584", + "name": "Trolley" + }, + { + "id": "2585", + "name": "Billy, a guard of Falador" + }, + { + "id": "2587", + "name": "Bob, another guard of Falador" + }, + { + "id": "2589", + "name": "PKMaster0036" + }, + { + "id": "2590", + "name": "King Roald" + }, + { + "id": "2617", + "name": "TzHaar-Mej-Jal" + }, + { + "id": "2618", + "name": "TzHaar-Mej-Kah" + }, + { + "id": "2619", + "name": "TzHaar-Ket-Zuh" + }, + { + "id": "2620", + "name": "TzHaar-Hur-Tel" + }, + { + "id": "2622", + "name": "TzHaar-Hur-Lek" + }, + { + "id": "2623", + "name": "TzHaar-Mej-Roh" + }, + { + "id": "2624", + "name": "TzHaar-Ket" + }, + { + "id": "2625", + "name": "TzHaar-Ket" + }, + { + "id": "2626", + "name": "Rocks" + }, + { + "id": "2632", + "name": "Tok-Xil" + }, + { + "id": "2633", + "name": "Wise Old Man" + }, + { + "id": "2635", + "name": "Bob" + }, + { + "id": "2636", + "name": "Bob" + }, + { + "id": "2637", + "name": "Sphinx" + }, + { + "id": "2638", + "name": "Neite" + }, + { + "id": "2639", + "name": "Robert the Strong" + }, + { + "id": "2640", + "name": "Odysseus" + }, + { + "id": "2642", + "name": "King Black Dragon" + }, + { + "id": "2643", + "name": "R4ng3rNo0b889" + }, + { + "id": "2644", + "name": "Love Cats" + }, + { + "id": "2645", + "name": "Love Cats" + }, + { + "id": "2646", + "name": "Neite" + }, + { + "id": "2647", + "name": "Bob" + }, + { + "id": "2648", + "name": "Beite" + }, + { + "id": "2649", + "name": "Gnome" + }, + { + "id": "2650", + "name": "Gnome" + }, + { + "id": "2651", + "name": "Odysseus" + }, + { + "id": "2652", + "name": "Neite" + }, + { + "id": "2654", + "name": "Unferth" + }, + { + "id": "2656", + "name": "Unferth" + }, + { + "id": "2657", + "name": "Unferth" + }, + { + "id": "2658", + "name": "Unferth" + }, + { + "id": "2659", + "name": "Unferth" + }, + { + "id": "2661", + "name": "Reldo" + }, + { + "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": "Lazy 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": "Wily cat" + }, + { + "id": "2676", + "name": "Make-over Mage" + }, + { + "examine": "So what can one do with a drunken sailor?", + "id": "2692", + "name": "Ahab" + }, + { + "id": "2708", + "name": "Seagull" + }, + { + "id": "2719", + "name": "Grum" + }, + { + "id": "2720", + "name": "Gerrant" + }, + { + "id": "2721", + "name": "Wydin" + }, + { + "id": "2722", + "name": "Fishing spot" + }, + { + "id": "2723", + "name": "Fishing spot" + }, + { + "id": "2724", + "name": "Fishing spot" + }, + { + "id": "2727", + "name": "Gull" + }, + { + "id": "2730", + "name": "Monk of Entrana" + }, + { + "id": "2747", + "name": "Solus Dellagar" + }, + { + "id": "2748", + "name": "Savant" + }, + { + "id": "2749", + "name": "Lord Daquarius" + }, + { + "id": "2750", + "name": "Solus Dellagar" + }, + { + "id": "2751", + "name": "Black Knight" + }, + { + "id": "2752", + "name": "Lord Daquarius" + }, + { + "id": "2753", + "name": "Mage of Zamorak" + }, + { + "id": "2754", + "name": "Mage of Zamorak" + }, + { + "id": "2755", + "name": "Mage of Zamorak" + }, + { + "id": "2756", + "name": "Woman" + }, + { + "id": "2777", + "name": "Black Knight" + }, + { + "id": "2778", + "name": "Black Knight" + }, + { + "id": "2780", + "name": "Solus Dellagar" + }, + { + "id": "2781", + "name": "Gnome guard" + }, + { + "id": "2788", + "name": "Thorgel" + }, + { + "id": "2791", + "name": "Pillory Guard" + }, + { + "id": "2793", + "name": "Tramp" + }, + { + "id": "2794", + "name": "Tramp" + }, + { + "id": "2795", + "name": "Skippy" + }, + { + "id": "2797", + "name": "Skippy" + }, + { + "id": "2798", + "name": "Skippy" + }, + { + "id": "2799", + "name": "Skippy" + }, + { + "id": "2800", + "name": "A pile of broken glass" + }, + { + "id": "2813", + "name": "Alice the Camel" + }, + { + "id": "2816", + "name": "Ali the Smith" + }, + { + "id": "2821", + "name": "Ali the Farmer" + }, + { + "id": "2822", + "name": "Ali the Tailor" + }, + { + "id": "2823", + "name": "Ali the Guard" + }, + { + "id": "2829", + "name": "Davey" + }, + { + "id": "2859", + "name": "Fishing spot" + }, + { + "id": "2862", + "name": "Death" + }, + { + "id": "2864", + "name": "Most of a Zombie" + }, + { + "id": "2865", + "name": "Most of a Zombie" + }, + { + "id": "2867", + "name": "Most of a Zombie" + }, + { + "id": "2868", + "name": "Zombie Head" + }, + { + "id": "2870", + "name": "Half-Zombie" + }, + { + "id": "2871", + "name": "Other Half-Zombie" + }, + { + "id": "2872", + "name": "Child" + }, + { + "id": "2873", + "name": "Child" + }, + { + "id": "2874", + "name": "Child" + }, + { + "id": "2875", + "name": "Child" + }, + { + "id": "2876", + "name": "Child" + }, + { + "id": "2877", + "name": "Child" + }, + { + "id": "2879", + "name": "Bardur" + }, + { + "id": "2884", + "name": "Wallasalki" + }, + { + "id": "2891", + "name": "Suspicious water" + }, + { + "id": "2893", + "name": "Suspicious water" + }, + { + "id": "2895", + "name": "Suspicious water" + }, + { + "id": "2897", + "name": "Father Reen" + }, + { + "id": "2900", + "name": "Father Reen" + }, + { + "id": "2901", + "name": "Father Badden" + }, + { + "id": "2903", + "name": "Father Badden" + }, + { + "id": "2904", + "name": "Denath" + }, + { + "id": "2905", + "name": "Denath" + }, + { + "id": "2906", + "name": "Eric" + }, + { + "id": "2907", + "name": "Eric" + }, + { + "id": "2908", + "name": "Evil Dave" + }, + { + "id": "2910", + "name": "Evil Dave" + }, + { + "id": "2911", + "name": "Matthew" + }, + { + "id": "2912", + "name": "Matthew" + }, + { + "id": "2913", + "name": "Jennifer" + }, + { + "id": "2914", + "name": "Jennifer" + }, + { + "id": "2915", + "name": "Tanya" + }, + { + "id": "2916", + "name": "Tanya" + }, + { + "id": "2917", + "name": "Patrick" + }, + { + "id": "2918", + "name": "Patrick" + }, + { + "id": "2920", + "name": "Sand storm" + }, + { + "id": "2922", + "name": "Clay golem" + }, + { + "id": "2928", + "name": "Clay golem" + }, + { + "id": "2929", + "name": "Ghost" + }, + { + "id": "2932", + "name": "Jorral" + }, + { + "id": "2933", + "name": "Melina" + }, + { + "id": "2935", + "name": "Melina" + }, + { + "id": "2936", + "name": "Droalak" + }, + { + "id": "2938", + "name": "Droalak" + }, + { + "id": "2939", + "name": "Dron" + }, + { + "id": "2940", + "name": "Blanin" + }, + { + "id": "2943", + "name": "Pox" + }, + { + "id": "2944", + "name": "Pox" + }, + { + "examine": "Cracking personality.", + "id": "2946", + "name": "Grimesquit" + }, + { + "examine": "Lovely girl, shame about the smell.", + "id": "2947", + "name": "Phingspet" + }, + { + "id": "2951", + "name": "Felkrash" + }, + { + "id": "2953", + "name": "Ceril Carnillean" + }, + { + "id": "2954", + "name": "Councillor Halgrive" + }, + { + "id": "2955", + "name": "Spice seller" + }, + { + "id": "2956", + "name": "Fur trader" + }, + { + "id": "2957", + "name": "Gem merchant" + }, + { + "id": "2959", + "name": "Silk merchant" + }, + { + "id": "2960", + "name": "Zenesha" + }, + { + "id": "2961", + "name": "Ali Morrisane" + }, + { + "id": "2974", + "name": "Rat" + }, + { + "id": "2983", + "name": "Turbogroomer" + }, + { + "id": "2985", + "name": "Loki" + }, + { + "id": "2987", + "name": "Treacle" + }, + { + "id": "2989", + "name": "Claude" + }, + { + "id": "2991", + "name": "Rauborn" + }, + { + "id": "2992", + "name": "Vaeringk" + }, + { + "id": "2993", + "name": "Oxi" + }, + { + "id": "2994", + "name": "Fior" + }, + { + "id": "2995", + "name": "Sagira" + }, + { + "id": "2996", + "name": "Anleif" + }, + { + "id": "2999", + "name": "Gambler" + }, + { + "id": "3000", + "name": "Barman" + }, + { + "id": "3019", + "name": "Fishing spot" + }, + { + "id": "3020", + "name": "Rug Merchant" + }, + { + "id": "3023", + "name": "Nirrie" + }, + { + "id": "3024", + "name": "Tirrie" + }, + { + "id": "3025", + "name": "Hallak" + }, + { + "id": "3031", + "name": "Usi" + }, + { + "id": "3032", + "name": "Nkuku" + }, + { + "id": "3033", + "name": "Garai" + }, + { + "id": "3034", + "name": "Habibah" + }, + { + "id": "3035", + "name": "Meskhenet" + }, + { + "id": "3036", + "name": "Zahra" + }, + { + "id": "3037", + "name": "Zahur" + }, + { + "id": "3038", + "name": "Seddu" + }, + { + "id": "3039", + "name": "Kazemde" + }, + { + "id": "3041", + "name": "Tarik" + }, + { + "id": "3045", + "name": "Rokuh" + }, + { + "id": "3047", + "name": "Target" + }, + { + "id": "3048", + "name": "Target" + }, + { + "id": "3049", + "name": "Larxus" + }, + { + "id": "3076", + "name": "Dead Monk" + }, + { + "id": "3078", + "name": "High Priest" + }, + { + "id": "3081", + "name": "Assassin" + }, + { + "id": "3082", + "name": "Rosie" + }, + { + "id": "3083", + "name": "Sorcha" + }, + { + "id": "3084", + "name": "Cait" + }, + { + "id": "3085", + "name": "Cormac" + }, + { + "id": "3086", + "name": "Fionn" + }, + { + "id": "3087", + "name": "Donnacha" + }, + { + "id": "3088", + "name": "Ronan" + }, + { + "id": "3093", + "name": "Flying Book" + }, + { + "id": "3095", + "name": "Flying Book" + }, + { + "id": "3096", + "name": "Pizzaz Hat" + }, + { + "id": "3107", + "name": "Charmed Warrior" + }, + { + "id": "3108", + "name": "Bert" + }, + { + "id": "3110", + "name": "Sandy" + }, + { + "id": "3113", + "name": "Sandy" + }, + { + "id": "3114", + "name": "Mazion" + }, + { + "id": "3116", + "name": "Reeso" + }, + { + "id": "3118", + "name": "Prison Pete" + }, + { + "id": "3119", + "name": "Balloon Animal" + }, + { + "id": "3120", + "name": "Balloon Animal" + }, + { + "id": "3124", + "name": "Pyramid block" + }, + { + "id": "3125", + "name": "Pyramid block" + }, + { + "id": "3126", + "name": "Pentyn" + }, + { + "id": "3127", + "name": "Aristarchus" + }, + { + "id": "3128", + "name": "Boneguard" + }, + { + "id": "3130", + "name": "Pile of bones" + }, + { + "id": "3131", + "name": "Desert Spirit" + }, + { + "id": "3132", + "name": "Crust of ice" + }, + { + "id": "3134", + "name": "Furnace grate" + }, + { + "id": "3136", + "name": "Enakhra" + }, + { + "id": "3138", + "name": "Enakhra" + }, + { + "id": "3139", + "name": "Boneguard" + }, + { + "id": "3141", + "name": "Akthanakos" + }, + { + "id": "3142", + "name": "Akthanakos" + }, + { + "id": "3143", + "name": "Lazim" + }, + { + "id": "3148", + "name": "Enakhra" + }, + { + "id": "3149", + "name": "Akthanakos" + }, + { + "id": "3152", + "name": "Harpie Bug Swarm" + }, + { + "id": "3154", + "name": "Count Draynor" + }, + { + "id": "3156", + "name": "Bill Teach" + }, + { + "id": "3157", + "name": "Bill Teach" + }, + { + "id": "3158", + "name": "Bill Teach" + }, + { + "id": "3159", + "name": "Bill Teach" + }, + { + "id": "3160", + "name": "Bill Teach" + }, + { + "id": "3161", + "name": "Charley" + }, + { + "id": "3162", + "name": "Smith" + }, + { + "id": "3163", + "name": "Joe" + }, + { + "id": "3164", + "name": "Mama" + }, + { + "id": "3165", + "name": "Mama" + }, + { + "id": "3197", + "name": "Gull" + }, + { + "id": "3205", + "name": "Romily Weaklax" + }, + { + "id": "3206", + "name": "Priest" + }, + { + "id": "3207", + "name": "Pious Pete" + }, + { + "id": "3208", + "name": "Taper" + }, + { + "id": "3210", + "name": "Alrena" + }, + { + "id": "3211", + "name": "Alrena" + }, + { + "id": "3212", + "name": "Bravek" + }, + { + "id": "3218", + "name": "Tina" + }, + { + "id": "3254", + "name": "Hunding" + }, + { + "id": "3281", + "name": "Engineering assistant" + }, + { + "id": "3284", + "name": "Squirrel" + }, + { + "id": "3285", + "name": "Squirrel" + }, + { + "id": "3287", + "name": "Raccoon" + }, + { + "id": "3289", + "name": "Skeleton" + }, + { + "id": "3292", + "name": "Witch" + }, + { + "id": "3300", + "name": "Frog" + }, + { + "id": "3301", + "name": "Storm cloud" + }, + { + "id": "3303", + "name": "Fairy Nuff" + }, + { + "id": "3305", + "name": "Slim Louie" + }, + { + "id": "3306", + "name": "Fat Rocco" + }, + { + "id": "3308", + "name": "Zandar Horfyre" + }, + { + "id": "3311", + "name": "Sheep" + }, + { + "id": "3312", + "name": "Zanaris choir" + }, + { + "id": "3314", + "name": "Baby tanglefoot" + }, + { + "id": "3321", + "name": "Gatekeeper" + }, + { + "id": "3323", + "name": "Draul Leptoc" + }, + { + "id": "3326", + "name": "Martina Scorsby" + }, + { + "id": "3328", + "name": "Tarquin" + }, + { + "id": "3329", + "name": "Sigurd" + }, + { + "id": "3330", + "name": "Hari" + }, + { + "id": "3332", + "name": "Trees" + }, + { + "id": "3333", + "name": "Trees" + }, + { + "id": "3335", + "name": "Bullrush" + }, + { + "id": "3336", + "name": "Bullrush" + }, + { + "id": "3337", + "name": "Cave scenery" + }, + { + "id": "3338", + "name": "Cave scenery" + }, + { + "id": "3339", + "name": "Cave scenery" + }, + { + "id": "3351", + "name": "Genie" + }, + { + "id": "3352", + "name": "Mysterious Old Man" + }, + { + "id": "3353", + "name": "Swarm" + }, + { + "id": "3354", + "name": "Cap'n Hand" + }, + { + "id": "3355", + "name": "Rick Turpentine" + }, + { + "id": "3356", + "name": "Niles" + }, + { + "id": "3357", + "name": "Miles" + }, + { + "id": "3358", + "name": "Giles" + }, + { + "id": "3359", + "name": "Dr Jekyll" + }, + { + "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": "Mr Hyde" + }, + { + "id": "3372", + "name": "Sir Amik Varze" + }, + { + "id": "3373", + "name": "Sir Amik Varze" + }, + { + "id": "3377", + "name": "K'klik" + }, + { + "id": "3379", + "name": "Evil Dave" + }, + { + "id": "3381", + "name": "Doris" + }, + { + "id": "3383", + "name": "Gypsy" + }, + { + "id": "3386", + "name": "Gypsy" + }, + { + "id": "3387", + "name": "Culinaromancer" + }, + { + "id": "3388", + "name": "Osman" + }, + { + "id": "3395", + "name": "Sir Amik Varze" + }, + { + "id": "3396", + "name": "Awowogei" + }, + { + "id": "3397", + "name": "Awowogei" + }, + { + "id": "3398", + "name": "Skrach Uglogwee" + }, + { + "id": "3399", + "name": "Culinaromancer" + }, + { + "id": "3401", + "name": "An old Dwarf" + }, + { + "id": "3403", + "name": "Rohak" + }, + { + "id": "3417", + "name": "Pirate Pete" + }, + { + "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": "Fish" + }, + { + "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": "? ? ? ?" + }, + { + "id": "3462", + "name": "Skrach Uglogwee" + }, + { + "id": "3464", + "name": "Skrach Uglogwee" + }, + { + "id": "3465", + "name": "Nung" + }, + { + "id": "3466", + "name": "Ogre" + }, + { + "id": "3467", + "name": "Rantz" + }, + { + "id": "3468", + "name": "Rantz" + }, + { + "id": "3469", + "name": "Ogre boat" + }, + { + "id": "3472", + "name": "Ogre boat" + }, + { + "id": "3473", + "name": "Balloon Toad" + }, + { + "id": "3474", + "name": "Balloon Toad" + }, + { + "id": "3475", + "name": "Balloon Toad" + }, + { + "id": "3477", + "name": "Jubbly bird" + }, + { + "id": "3479", + "name": "King Awowogei" + }, + { + "id": "3481", + "name": "Mizaru" + }, + { + "id": "3482", + "name": "Kikazaru" + }, + { + "id": "3483", + "name": "Iwazaru" + }, + { + "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": "Culinaromancer" + }, + { + "id": "3503", + "name": "Overgrown hellcat" + }, + { + "id": "3504", + "name": "Hellcat" + }, + { + "id": "3505", + "name": "Hell-kitten" + }, + { + "id": "3506", + "name": "Lazy hellcat" + }, + { + "id": "3507", + "name": "Wily hellcat" + }, + { + "id": "3508", + "name": "Leo" + }, + { + "id": "3510", + "name": "Wiskit" + }, + { + "id": "3512", + "name": "Vampyre Juvinate" + }, + { + "id": "3515", + "name": "Vampyre Juvinate" + }, + { + "id": "3516", + "name": "Gadderanks" + }, + { + "id": "3518", + "name": "Gadderanks" + }, + { + "id": "3519", + "name": "Gadderanks" + }, + { + "id": "3520", + "name": "Vampyre Juvinate" + }, + { + "id": "3528", + "name": "Vampyre Juvinate" + }, + { + "id": "3529", + "name": "Vampyre Juvinate" + }, + { + "id": "3530", + "name": "Mist" + }, + { + "id": "3535", + "name": "Ivan Strom" + }, + { + "id": "3536", + "name": "Ivan Strom" + }, + { + "id": "3537", + "name": "Vampyre Juvinate" + }, + { + "id": "3538", + "name": "Vampyre Juvinate" + }, + { + "id": "3539", + "name": "Veliaf Hurtz" + }, + { + "id": "3540", + "name": "Elisabeta" + }, + { + "id": "3541", + "name": "Aurel" + }, + { + "id": "3542", + "name": "Sorin" + }, + { + "id": "3543", + "name": "Luscion" + }, + { + "id": "3544", + "name": "Sergiu" + }, + { + "id": "3545", + "name": "Radu" + }, + { + "id": "3546", + "name": "Grigore" + }, + { + "id": "3547", + "name": "Ileana" + }, + { + "id": "3548", + "name": "Valeria" + }, + { + "id": "3549", + "name": "Emilia" + }, + { + "id": "3550", + "name": "Florin" + }, + { + "id": "3551", + "name": "Catalina" + }, + { + "id": "3552", + "name": "Ivan" + }, + { + "id": "3553", + "name": "Victor" + }, + { + "id": "3554", + "name": "Helena" + }, + { + "id": "3555", + "name": "Teodor" + }, + { + "id": "3556", + "name": "Marius" + }, + { + "id": "3557", + "name": "Gabriela" + }, + { + "id": "3558", + "name": "Vladimir" + }, + { + "id": "3559", + "name": "Calin" + }, + { + "id": "3560", + "name": "Mihail" + }, + { + "id": "3561", + "name": "Nicoleta" + }, + { + "id": "3562", + "name": "Simona" + }, + { + "id": "3563", + "name": "Vasile" + }, + { + "id": "3564", + "name": "Razvan" + }, + { + "id": "3565", + "name": "Luminata" + }, + { + "id": "3566", + "name": "Cornelius" + }, + { + "id": "3569", + "name": "Cornelius" + }, + { + "id": "3570", + "name": "Benjamin" + }, + { + "id": "3571", + "name": "Liam" + }, + { + "id": "3572", + "name": "Miala" + }, + { + "id": "3573", + "name": "Verak" + }, + { + "id": "3574", + "name": "Fishing spot" + }, + { + "id": "3575", + "name": "Fishing spot" + }, + { + "id": "3576", + "name": "Juvinate" + }, + { + "id": "3578", + "name": "Juvinate" + }, + { + "id": "3580", + "name": "Tentacle" + }, + { + "id": "3584", + "name": "Troll" + }, + { + "id": "3592", + "name": "Tok-Xil" + }, + { + "id": "3594", + "name": "Rocnar" + }, + { + "id": "3595", + "name": "Toy Soldier" + }, + { + "id": "3596", + "name": "Toy Doll" + }, + { + "id": "3597", + "name": "Toy Mouse" + }, + { + "id": "3598", + "name": "Clockwork cat" + }, + { + "id": "3606", + "name": "Ghast" + }, + { + "id": "3607", + "name": "Ghast" + }, + { + "id": "3608", + "name": "Ghast" + }, + { + "id": "3609", + "name": "Ghast" + }, + { + "id": "3610", + "name": "Ghast" + }, + { + "id": "3611", + "name": "Ghast" + }, + { + "id": "3612", + "name": "Giant snail" + }, + { + "id": "3613", + "name": "Giant snail" + }, + { + "id": "3614", + "name": "Giant snail" + }, + { + "id": "3615", + "name": "Riyl shadow" + }, + { + "id": "3616", + "name": "Asyn shadow" + }, + { + "id": "3617", + "name": "Shade" + }, + { + "id": "3621", + "name": "Tentacle" + }, + { + "id": "3623", + "name": "Smiddi Ryak" + }, + { + "id": "3625", + "name": "Rolayne Twickit" + }, + { + "id": "3627", + "name": "Jayene Kliyn" + }, + { + "id": "3629", + "name": "Valantay Eppel" + }, + { + "id": "3631", + "name": "Dalcian Fang" + }, + { + "id": "3633", + "name": "Fyiona Fray" + }, + { + "id": "3635", + "name": "Abidor Crank" + }, + { + "id": "3636", + "name": "Spirit tree" + }, + { + "id": "3637", + "name": "Spirit tree" + }, + { + "id": "3638", + "name": "Launa" + }, + { + "id": "3640", + "name": "Launa" + }, + { + "id": "3641", + "name": "Brana" + }, + { + "id": "3642", + "name": "Mawnis Burowgar" + }, + { + "id": "3643", + "name": "Tolna" + }, + { + "id": "3644", + "name": "Tolna" + }, + { + "id": "3651", + "name": "Confusion beast" + }, + { + "id": "3652", + "name": "Confusion beast" + }, + { + "id": "3653", + "name": "Confusion beast" + }, + { + "id": "3654", + "name": "Confusion beast" + }, + { + "id": "3656", + "name": "Hopeless creature" + }, + { + "id": "3657", + "name": "Hopeless creature" + }, + { + "id": "3658", + "name": "Tolna" + }, + { + "id": "3659", + "name": "Tolna" + }, + { + "id": "3660", + "name": "Tolna" + }, + { + "id": "3668", + "name": "Hopeless beast" + }, + { + "id": "3669", + "name": "Hopeless beast" + }, + { + "id": "3677", + "name": "Sinister Stranger" + }, + { + "id": "3678", + "name": "Sinister Stranger" + }, + { + "id": "3679", + "name": "Vestri" + }, + { + "id": "3681", + "name": "Nigel" + }, + { + "id": "3682", + "name": "Egg" + }, + { + "id": "3683", + "name": "Egg" + }, + { + "id": "3684", + "name": "Egg" + }, + { + "id": "3685", + "name": "Egg" + }, + { + "id": "3686", + "name": "Chocolate kebbit" + }, + { + "id": "3687", + "name": "Chocolate kebbit" + }, + { + "id": "3688", + "name": "Easter Bunny" + }, + { + "id": "3689", + "name": "Egg" + }, + { + "id": "3690", + "name": "Egg" + }, + { + "id": "3691", + "name": "Egg" + }, + { + "id": "3692", + "name": "Egg" + }, + { + "id": "3693", + "name": "Egg" + }, + { + "id": "3694", + "name": "Egg" + }, + { + "id": "3695", + "name": "Volf Olafson" + }, + { + "id": "3696", + "name": "Ingrid Hradson" + }, + { + "id": "3708", + "name": "Boulder" + }, + { + "id": "3710", + "name": "Ulfric" + }, + { + "id": "3712", + "name": "Zanik" + }, + { + "id": "3713", + "name": "Sigmund" + }, + { + "id": "3714", + "name": "Zanik" + }, + { + "id": "3716", + "name": "Sigmund" + }, + { + "id": "3717", + "name": "Sigmund" + }, + { + "id": "3718", + "name": "Sigmund" + }, + { + "id": "3719", + "name": "Sigmund" + }, + { + "id": "3720", + "name": "Sigmund" + }, + { + "id": "3721", + "name": "Zanik" + }, + { + "id": "3722", + "name": "Zanik" + }, + { + "id": "3723", + "name": "General Bentnoze" + }, + { + "id": "3724", + "name": "General Wartface" + }, + { + "id": "3725", + "name": "Grubfoot" + }, + { + "id": "3778", + "name": "Arthur" + }, + { + "id": "3780", + "name": "Squire" + }, + { + "id": "3787", + "name": "Sir Palomedes" + }, + { + "id": "3789", + "name": "Void Knight" + }, + { + "id": "3803", + "name": "Fishing spot" + }, + { + "id": "3817", + "name": "Lieutenant Schepbur" + }, + { + "id": "3820", + "name": "Wise Old Man" + }, + { + "id": "3825", + "name": "Devin Mendelberg" + }, + { + "id": "3826", + "name": "George Laxmeister" + }, + { + "id": "3827", + "name": "Ramara du Croissant" + }, + { + "id": "3828", + "name": "Kathy Corkat" + }, + { + "id": "3831", + "name": "Kathy Corkat" + }, + { + "id": "3832", + "name": "Kalphite Queen" + }, + { + "id": "3837", + "name": "Drunken Dwarf" + }, + { + "id": "3838", + "name": "Wise Old Man" + }, + { + "id": "3839", + "name": "Wise Old Man" + }, + { + "id": "3841", + "name": "Sea troll" + }, + { + "id": "3842", + "name": "Sea troll" + }, + { + "id": "3844", + "name": "Skeleton Mage" + }, + { + "id": "3845", + "name": "Sea troll" + }, + { + "id": "3846", + "name": "Sea Troll General" + }, + { + "id": "3848", + "name": "Fishing spot" + }, + { + "id": "3850", + "name": "Skeleton Mage" + }, + { + "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": "Suspect" + }, + { + "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": "Molly" + }, + { + "id": "3912", + "name": "Flippa" + }, + { + "id": "3913", + "name": "Tilt" + }, + { + "id": "3914", + "name": "Gardener" + }, + { + "id": "3918", + "name": "Prince Brand" + }, + { + "id": "3919", + "name": "Princess Astrid" + }, + { + "id": "3920", + "name": "Runa" + }, + { + "id": "3923", + "name": "Osvald" + }, + { + "id": "3924", + "name": "Runolf" + }, + { + "id": "3926", + "name": "Ingrid" + }, + { + "id": "3928", + "name": "Signy" + }, + { + "id": "3929", + "name": "Hild" + }, + { + "id": "3930", + "name": "Armod" + }, + { + "id": "3931", + "name": "Beigarth" + }, + { + "id": "3932", + "name": "Reinn" + }, + { + "id": "3936", + "name": "Thorodin" + }, + { + "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": "Hangman game" + }, + { + "id": "3954", + "name": "Treasure fairy" + }, + { + "id": "3955", + "name": "Jacky Jester" + }, + { + "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": "Combat stone" + }, + { + "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": "Elemental balance" + }, + { + "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": "Combat stone" + }, + { + "id": "4226", + "name": "Crawling hand" + }, + { + "id": "4231", + "name": "Left head" + }, + { + "id": "4232", + "name": "Middle head" + }, + { + "id": "4233", + "name": "Right head" + }, + { + "id": "4234", + "name": "Kalphite Queen" + }, + { + "id": "4235", + "name": "Rick" + }, + { + "id": "4236", + "name": "Maid" + }, + { + "id": "4238", + "name": "Cook" + }, + { + "id": "4240", + "name": "Butler" + }, + { + "id": "4242", + "name": "Demon butler" + }, + { + "id": "4244", + "name": "Chief servant" + }, + { + "id": "4253", + "name": "Guard" + }, + { + "id": "4277", + "name": "Buinn" + }, + { + "id": "4312", + "name": "Nardok" + }, + { + "id": "4313", + "name": "Dartog" + }, + { + "id": "4315", + "name": "Dwarf" + }, + { + "id": "4317", + "name": "H.A.M. Member" + }, + { + "id": "4319", + "name": "H.A.M. Member" + }, + { + "id": "4321", + "name": "Zanik" + }, + { + "id": "4323", + "name": "Zanik" + }, + { + "id": "4324", + "name": "Zanik" + }, + { + "id": "4325", + "name": "Light creature" + }, + { + "id": "4326", + "name": "Zanik" + }, + { + "id": "4327", + "name": "HAM member" + }, + { + "id": "4328", + "name": "Sigmund" + }, + { + "id": "4330", + "name": "Johanhus Ulsbrecht" + }, + { + "id": "4331", + "name": "Sigmund" + }, + { + "id": "4332", + "name": "Sigmund" + }, + { + "id": "4333", + "name": "Sigmund" + }, + { + "id": "4334", + "name": "Sigmund" + }, + { + "id": "4335", + "name": "Sigmund" + }, + { + "id": "4337", + "name": "Zanik" + }, + { + "id": "4338", + "name": "Zanik" + }, + { + "id": "4340", + "name": "Zanik" + }, + { + "id": "4341", + "name": "Zanik" + }, + { + "id": "4342", + "name": "Zanik" + }, + { + "id": "4346", + "name": "Crab" + }, + { + "id": "4358", + "name": "Cavey Davey" + }, + { + "id": "4360", + "name": "San Fan" + }, + { + "id": "4364", + "name": "Swarm" + }, + { + "id": "4365", + "name": "Blue Monkey" + }, + { + "id": "4374", + "name": "Parrot" + }, + { + "id": "4377", + "name": "Gate of War" + }, + { + "id": "4378", + "name": "Ricketty door" + }, + { + "id": "4379", + "name": "Oozing barrier" + }, + { + "id": "4380", + "name": "Portal of Death" + }, + { + "id": "4416", + "name": "Bee keeper" + }, + { + "id": "4417", + "name": "Bees!" + }, + { + "id": "4420", + "name": "Fairy Godfather" + }, + { + "id": "4435", + "name": "Fairy Queen" + }, + { + "id": "4442", + "name": "Fairy Very Wise" + }, + { + "id": "4444", + "name": "Fairy" + }, + { + "id": "4445", + "name": "Fairy" + }, + { + "id": "4446", + "name": "Fairy" + }, + { + "id": "4447", + "name": "Rabbit" + }, + { + "id": "4448", + "name": "Rabbit" + }, + { + "id": "4449", + "name": "Butterfly" + }, + { + "id": "4450", + "name": "Butterfly" + }, + { + "id": "4451", + "name": "Starflower" + }, + { + "id": "4452", + "name": "Starflower" + }, + { + "id": "4453", + "name": "Fairy Fixit" + }, + { + "id": "4456", + "name": "Ork" + }, + { + "id": "4460", + "name": "Fake Man" + }, + { + "id": "4461", + "name": "Held vampyre juvinate" + }, + { + "id": "4462", + "name": "Held vampyre juvinate" + }, + { + "id": "4463", + "name": "Angry juvinate" + }, + { + "id": "4464", + "name": "Angry juvinate" + }, + { + "id": "4465", + "name": "Angry juvinate" + }, + { + "id": "4466", + "name": "Benjamin" + }, + { + "id": "4467", + "name": "Liam" + }, + { + "id": "4468", + "name": "Miala" + }, + { + "id": "4469", + "name": "Verak" + }, + { + "id": "4471", + "name": "Bogrog" + }, + { + "id": "4473", + "name": "Woman" + }, + { + "id": "4475", + "name": "Ned" + }, + { + "id": "4477", + "name": "Annoyed guardian mummy" + }, + { + "id": "4478", + "name": "Tarik" + }, + { + "id": "4493", + "name": "General Bentnoze" + }, + { + "id": "4495", + "name": "Grubfoot" + }, + { + "id": "4497", + "name": "Grubfoot" + }, + { + "id": "4498", + "name": "Grubfoot" + }, + { + "id": "4500", + "name": "Scarab swarm" + }, + { + "id": "4508", + "name": "Ethereal Mimic" + }, + { + "id": "4513", + "name": "Baba Yaga" + }, + { + "id": "4514", + "name": "Pauline Polaris" + }, + { + "id": "4515", + "name": "Meteora" + }, + { + "id": "4516", + "name": "Melana Moonlander" + }, + { + "id": "4517", + "name": "Selene" + }, + { + "id": "4518", + "name": "Rimae Sirsalis" + }, + { + "id": "4519", + "name": "Sirsal Banker" + }, + { + "id": "4520", + "name": "Clan Guard" + }, + { + "id": "4522", + "name": "Enchanted Broom" + }, + { + "id": "4523", + "name": "Enchanted Broom" + }, + { + "id": "4525", + "name": "Enchanted Bucket" + }, + { + "id": "4526", + "name": "Bouquet Mac Hyacinth" + }, + { + "id": "4535", + "name": "Parrot" + }, + { + "id": "4536", + "name": "Lokar Searunner" + }, + { + "id": "4538", + "name": "Cabin boy" + }, + { + "id": "4544", + "name": "'Bird's-Eye' Jack" + }, + { + "id": "4552", + "name": "Palmer" + }, + { + "id": "4553", + "name": "'Betty' B.Boppin" + }, + { + "id": "4558", + "name": "Hirko" + }, + { + "id": "4559", + "name": "Holoy" + }, + { + "id": "4563", + "name": "Hura" + }, + { + "id": "4569", + "name": "Nick" + }, + { + "id": "4570", + "name": "Crow" + }, + { + "id": "4571", + "name": "Crow" + }, + { + "id": "4572", + "name": "Gianne jnr." + }, + { + "id": "4573", + "name": "Timble" + }, + { + "id": "4574", + "name": "Tamble" + }, + { + "id": "4575", + "name": "Spang" + }, + { + "id": "4576", + "name": "Brambickle" + }, + { + "id": "4577", + "name": "Wingstone" + }, + { + "id": "4578", + "name": "Penwie" + }, + { + "id": "4583", + "name": "Professor Manglethorp" + }, + { + "id": "4584", + "name": "Damwin" + }, + { + "id": "4586", + "name": "Professor Imblewyn" + }, + { + "id": "4587", + "name": "Perrdur" + }, + { + "id": "4588", + "name": "Dalila" + }, + { + "id": "4591", + "name": "Eebel" + }, + { + "id": "4592", + "name": "Ermin" + }, + { + "id": "4596", + "name": "Captain Lamdoo" + }, + { + "id": "4597", + "name": "Meegle" + }, + { + "id": "4598", + "name": "Wurbel" + }, + { + "id": "4599", + "name": "Sarble" + }, + { + "id": "4601", + "name": "Burkor" + }, + { + "id": "4602", + "name": "Froono" + }, + { + "id": "4609", + "name": "Brimstail" + }, + { + "id": "4612", + "name": "Gnome shop keeper" + }, + { + "id": "4613", + "name": "Cute creature" + }, + { + "id": "4616", + "name": "Cute creature" + }, + { + "id": "4618", + "name": "Evil creature" + }, + { + "id": "4619", + "name": "Cute creature" + }, + { + "id": "4621", + "name": "Evil creature" + }, + { + "id": "4622", + "name": "Cute creature" + }, + { + "id": "4624", + "name": "Evil creature" + }, + { + "id": "4625", + "name": "Cute creature" + }, + { + "id": "4627", + "name": "Evil creature" + }, + { + "id": "4628", + "name": "Cute creature" + }, + { + "id": "4630", + "name": "Evil creature" + }, + { + "id": "4631", + "name": "fluffie" + }, + { + "id": "4632", + "name": "fluffie" + }, + { + "id": "4644", + "name": "Oaknock the Engineer" + }, + { + "id": "4646", + "name": "King Healthorg" + }, + { + "id": "4647", + "name": "Hazelmere" + }, + { + "id": "4648", + "name": "Nisha" + }, + { + "id": "4649", + "name": "Tyras guard" + }, + { + "id": "4657", + "name": "Sir Prysin" + }, + { + "id": "4658", + "name": "Dark wizard" + }, + { + "id": "4662", + "name": "Denath" + }, + { + "id": "4663", + "name": "Denath" + }, + { + "id": "4664", + "name": "Wally" + }, + { + "id": "4709", + "name": "Vertida Sefalatis" + }, + { + "id": "4710", + "name": "Aeonisig Raispher" + }, + { + "id": "4711", + "name": "Safalaan" + }, + { + "id": "4714", + "name": "Sarius Guile" + }, + { + "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 citizen" + }, + { + "id": "4758", + "name": "Meiyerditch miner" + }, + { + "id": "4760", + "name": "Meiyerditch miner" + }, + { + "id": "4761", + "name": "Meiyerditch miner" + }, + { + "id": "4762", + "name": "Shadowy figure" + }, + { + "id": "4763", + "name": "Shadowy figure" + }, + { + "id": "4764", + "name": "Shadowy figure" + }, + { + "id": "4767", + "name": "Stray dog" + }, + { + "id": "4769", + "name": "Cat" + }, + { + "id": "4770", + "name": "Boat" + }, + { + "id": "4771", + "name": "Boat" + }, + { + "id": "4781", + "name": "Held vampyre juvinate" + }, + { + "id": "4782", + "name": "Vampyre juvinate" + }, + { + "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": "Former vampyre" + }, + { + "id": "4790", + "name": "Angry vampyre" + }, + { + "id": "4791", + "name": "Vanstrom Klause" + }, + { + "id": "4792", + "name": "Vanstrom Klause" + }, + { + "id": "4794", + "name": "Vanstrom Klause" + }, + { + "id": "4795", + "name": "Vanstrom Klause" + }, + { + "id": "4797", + "name": "Vanescula Drakan" + }, + { + "id": "4798", + "name": "Vanescula Drakan" + }, + { + "id": "4799", + "name": "Vanescula Drakan" + }, + { + "id": "4800", + "name": "Vanescula Drakan" + }, + { + "id": "4801", + "name": "Ranis Drakan" + }, + { + "id": "4802", + "name": "Ranis Drakan" + }, + { + "id": "4803", + "name": "Ranis Drakan" + }, + { + "id": "4804", + "name": "Ranis Drakan" + }, + { + "id": "4809", + "name": "Flying female vampire" + }, + { + "id": "4846", + "name": "Flying female vampire" + }, + { + "id": "4853", + "name": "Ezekial Lovecraft" + }, + { + "id": "4857", + "name": "Sarius Guile" + }, + { + "id": "4862", + "name": "Vanstrom Klause" + }, + { + "id": "4863", + "name": "Kennith" + }, + { + "id": "4864", + "name": "Kennith" + }, + { + "id": "4865", + "name": "Holgart" + }, + { + "id": "4867", + "name": "Holgart" + }, + { + "id": "4869", + "name": "Fisherman" + }, + { + "id": "4871", + "name": "Col. O'Niall" + }, + { + "id": "4873", + "name": "Col. O'Niall" + }, + { + "id": "4875", + "name": "Mayor Hobb" + }, + { + "id": "4876", + "name": "Brother Maledict" + }, + { + "id": "4879", + "name": "Brother Maledict" + }, + { + "id": "4880", + "name": "Witchaven villager" + }, + { + "id": "4884", + "name": "Witchaven villager" + }, + { + "id": "4886", + "name": "Witchaven villager" + }, + { + "id": "4888", + "name": "Witchaven villager" + }, + { + "id": "4889", + "name": "Mother Mallum" + }, + { + "id": "4891", + "name": "Giant lobster" + }, + { + "id": "4896", + "name": "Jeb" + }, + { + "id": "4897", + "name": "Sir Tinley" + }, + { + "id": "4905", + "name": "Smithing Tutor" + }, + { + "id": "4908", + "name": "Fishing spot" + }, + { + "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": "Jig cart" + }, + { + "id": "4919", + "name": "Abidor Crank" + }, + { + "id": "4946", + "name": "Ignatius Vulcan" + }, + { + "id": "4948", + "name": "My Arm" + }, + { + "id": "4949", + "name": "My Arm" + }, + { + "id": "4950", + "name": "My Arm" + }, + { + "id": "4958", + "name": "My Arm" + }, + { + "id": "4959", + "name": "My Arm" + }, + { + "id": "4960", + "name": "Adventurer" + }, + { + "id": "4961", + "name": "Captain Barnaby" + }, + { + "id": "4963", + "name": "Murcaily" + }, + { + "id": "4964", + "name": "Jagbakoba" + }, + { + "id": "4966", + "name": "Flies" + }, + { + "id": "4968", + "name": "Unnamed troll child" + }, + { + "id": "4969", + "name": "Drunken dwarf's leg" + }, + { + "id": "4970", + "name": "Baby Roc" + }, + { + "id": "4973", + "name": "Shadow" + }, + { + "id": "4974", + "name": "Captain Barnaby" + }, + { + "id": "4976", + "name": "Male slave" + }, + { + "id": "4978", + "name": "Female slave" + }, + { + "id": "4979", + "name": "Cart Camel" + }, + { + "id": "4980", + "name": "Mine Cart" + }, + { + "id": "4981", + "name": "Mine Cart" + }, + { + "id": "4982", + "name": "Ana" + }, + { + "id": "4983", + "name": "Mercenary" + }, + { + "id": "4984", + "name": "Irena" + }, + { + "id": "5003", + "name": "Gublinch" + }, + { + "id": "5018", + "name": "Gublinch" + }, + { + "id": "5019", + "name": "Gublinch" + }, + { + "id": "5020", + "name": "Jack" + }, + { + "id": "5024", + "name": "Jill" + }, + { + "id": "5025", + "name": "Jeff" + }, + { + "id": "5044", + "name": "Penance Fighter" + }, + { + "id": "5045", + "name": "Penance Fighter" + }, + { + "id": "5046", + "name": "Jack" + }, + { + "id": "5047", + "name": "Jill" + }, + { + "id": "5049", + "name": "Auguste" + }, + { + "id": "5050", + "name": "Auguste" + }, + { + "id": "5051", + "name": "Auguste" + }, + { + "id": "5052", + "name": "Auguste" + }, + { + "id": "5053", + "name": "Assistant Serf" + }, + { + "id": "5054", + "name": "Assistant Brock" + }, + { + "id": "5055", + "name": "Assistant Marrow" + }, + { + "id": "5056", + "name": "Assistant Le Smith" + }, + { + "id": "5057", + "name": "Assistant Stan" + }, + { + "id": "5058", + "name": "Bob" + }, + { + "id": "5059", + "name": "Curly" + }, + { + "id": "5060", + "name": "Moe" + }, + { + "id": "5061", + "name": "Larry" + }, + { + "id": "5062", + "name": "Shark" + }, + { + "id": "5068", + "name": "Shark" + }, + { + "id": "5069", + "name": "Shark" + }, + { + "id": "5070", + "name": "Tropical wagtail" + }, + { + "id": "5077", + "name": "Chinchompa" + }, + { + "id": "5090", + "name": "Matthias" + }, + { + "id": "5093", + "name": "Matthias" + }, + { + "id": "5094", + "name": "Gyr Falcon" + }, + { + "id": "5095", + "name": "Gyr Falcon" + }, + { + "id": "5096", + "name": "Gyr Falcon" + }, + { + "id": "5101", + "name": "Sabre-toothed kyatt" + }, + { + "id": "5110", + "name": "Aleck" + }, + { + "id": "5118", + "name": "Eagle" + }, + { + "id": "5121", + "name": "Eagle" + }, + { + "id": "5122", + "name": "Eagle" + }, + { + "id": "5123", + "name": "Eagle" + }, + { + "id": "5124", + "name": "Nickolaus" + }, + { + "id": "5127", + "name": "Nickolaus" + }, + { + "id": "5128", + "name": "Nickolaus" + }, + { + "id": "5129", + "name": "Nickolaus" + }, + { + "id": "5134", + "name": "Kebbit" + }, + { + "id": "5138", + "name": "Charlie" + }, + { + "id": "5139", + "name": "Boulder" + }, + { + "id": "5141", + "name": "Uri" + }, + { + "id": "5142", + "name": "Uri" + }, + { + "id": "5143", + "name": "Uri" + }, + { + "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": "Sheep" + }, + { + "id": "5174", + "name": "Ogre chieftain" + }, + { + "id": "5175", + "name": "Ogre shaman" + }, + { + "id": "5177", + "name": "Ogre shaman" + }, + { + "id": "5179", + "name": "Elkoy" + }, + { + "id": "5180", + "name": "Ogre shaman" + }, + { + "id": "5182", + "name": "Elkoy" + }, + { + "id": "5183", + "name": "Ogre shaman" + }, + { + "id": "5185", + "name": "Biggleswade" + }, + { + "id": "5186", + "name": "Ogre shaman" + }, + { + "id": "5189", + "name": "Ogre shaman" + }, + { + "id": "5191", + "name": "Blaze Sharpeye" + }, + { + "id": "5192", + "name": "Ogre shaman" + }, + { + "id": "5194", + "name": "Blaze Sharpeye" + }, + { + "id": "5199", + "name": "Witch" + }, + { + "id": "5201", + "name": "Alice's husband" + }, + { + "id": "5203", + "name": "Alice's husband" + }, + { + "id": "5205", + "name": "Alice's husband" + }, + { + "id": "5206", + "name": "Tree" + }, + { + "id": "5208", + "name": "Undead tree" + }, + { + "id": "5209", + "name": " Sneaky undead fowl" + }, + { + "id": "5210", + "name": "Cow1337killr" + }, + { + "id": "5212", + "name": "Alice" + }, + { + "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 Fighter" + }, + { + "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 Runner" + }, + { + "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 Ranger" + }, + { + "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": "Penance Healer" + }, + { + "id": "5255", + "name": "Locust rider" + }, + { + "id": "5256", + "name": "Locust rider" + }, + { + "id": "5257", + "name": "Banker" + }, + { + "id": "5259", + "name": "Banker" + }, + { + "id": "5261", + "name": "Stonemason" + }, + { + "id": "5263", + "name": "Nathifa" + }, + { + "id": "5265", + "name": "Urbi" + }, + { + "id": "5267", + "name": "Jamila" + }, + { + "id": "5269", + "name": "Sophanem guard" + }, + { + "id": "5271", + "name": "Sophanem guard" + }, + { + "id": "5273", + "name": "Sophanem guard" + }, + { + "id": "5275", + "name": "Sophanem guard" + }, + { + "id": "5278", + "name": "Coenus" + }, + { + "id": "5279", + "name": "Jex" + }, + { + "id": "5280", + "name": "Maisa" + }, + { + "id": "5282", + "name": "Osman" + }, + { + "id": "5286", + "name": "Osman" + }, + { + "id": "5287", + "name": "Osman" + }, + { + "id": "5288", + "name": "Embalmer" + }, + { + "id": "5289", + "name": "Carpenter" + }, + { + "id": "5290", + "name": "Linen worker" + }, + { + "id": "5291", + "name": "Priest" + }, + { + "id": "5292", + "name": "Giant scarab" + }, + { + "id": "5360", + "name": "Mummy ashes" + }, + { + "id": "5383", + "name": "Odovacar" + }, + { + "id": "5415", + "name": "Terror dog statue" + }, + { + "id": "5416", + "name": "Terror dog statue" + }, + { + "id": "5419", + "name": "Tarn" + }, + { + "id": "5423", + "name": "Larry" + }, + { + "id": "5425", + "name": "Larry" + }, + { + "id": "5426", + "name": "Larry" + }, + { + "id": "5427", + "name": "Penguin" + }, + { + "id": "5429", + "name": "Penguin" + }, + { + "id": "5430", + "name": "Penguin" + }, + { + "id": "5431", + "name": "KGP Guard" + }, + { + "id": "5432", + "name": "Pescaling Pax" + }, + { + "id": "5433", + "name": "Ping" + }, + { + "id": "5434", + "name": "Ping" + }, + { + "id": "5435", + "name": "Pong" + }, + { + "id": "5436", + "name": "Pong" + }, + { + "id": "5437", + "name": "Ping" + }, + { + "id": "5438", + "name": "Pong" + }, + { + "id": "5443", + "name": "Noodle" + }, + { + "id": "5445", + "name": "Penguin" + }, + { + "id": "5446", + "name": "Penguin suit" + }, + { + "id": "5449", + "name": "Penguin" + }, + { + "id": "5450", + "name": "Penguin" + }, + { + "id": "5451", + "name": "Penguin" + }, + { + "id": "5456", + "name": "Crusher" + }, + { + "id": "5457", + "name": "Crusher" + }, + { + "id": "5458", + "name": "Crusher" + }, + { + "id": "5459", + "name": "Crusher" + }, + { + "id": "5460", + "name": "Tree" + }, + { + "id": "5461", + "name": "Jungle Tree" + }, + { + "id": "5462", + "name": "Tolna" + }, + { + "id": "5463", + "name": "Honour guard" + }, + { + "id": "5464", + "name": "Honour guard" + }, + { + "id": "5465", + "name": "Fridleif Shieldson" + }, + { + "id": "5466", + "name": "Thakkrad Sigmundson" + }, + { + "id": "5467", + "name": "Iceberg" + }, + { + "id": "5468", + "name": "Iceberg" + }, + { + "id": "5469", + "name": "Arctic Pine" + }, + { + "id": "5470", + "name": "Fishing spot" + }, + { + "id": "5471", + "name": "Fishing spot" + }, + { + "id": "5477", + "name": "Bork Sigmundson" + }, + { + "id": "5481", + "name": "Mord Gunnars" + }, + { + "id": "5482", + "name": "Mord Gunnars" + }, + { + "id": "5498", + "name": "Miner" + }, + { + "id": "5502", + "name": "Grundt" + }, + { + "id": "5503", + "name": "Mawnis Burowgar" + }, + { + "id": "5504", + "name": "Mawnis Burowgar" + }, + { + "id": "5505", + "name": "Fridleif Shieldson" + }, + { + "id": "5506", + "name": "Thakkrad Sigmundson" + }, + { + "id": "5507", + "name": "Maria Gunnars" + }, + { + "id": "5508", + "name": "Maria Gunnars" + }, + { + "id": "5509", + "name": "Jofridr Mordstatter" + }, + { + "id": "5510", + "name": "Morten Holdstrom" + }, + { + "id": "5511", + "name": "Gunnar Holdstrom" + }, + { + "id": "5512", + "name": "Anne Isaakson" + }, + { + "id": "5513", + "name": "Lisse Isaakson" + }, + { + "id": "5518", + "name": "Kjedelig Uppsen" + }, + { + "id": "5519", + "name": "Trogen Konungarde" + }, + { + "id": "5520", + "name": "Slug Hemligssen" + }, + { + "id": "5528", + "name": "Ice troll grunt" + }, + { + "id": "5530", + "name": "Sorceress" + }, + { + "id": "5560", + "name": "Osman" + }, + { + "id": "5562", + "name": "Del-Monty" + }, + { + "id": "5564", + "name": "Bouncer" + }, + { + "id": "5565", + "name": "Bouncer" + }, + { + "id": "5566", + "name": "General Khazard" + }, + { + "id": "5567", + "name": "Scout" + }, + { + "id": "5568", + "name": "Scout" + }, + { + "id": "5569", + "name": "Scout" + }, + { + "id": "5570", + "name": "Scout" + }, + { + "id": "5573", + "name": "Effigy" + }, + { + "id": "5579", + "name": "Effigy" + }, + { + "id": "5580", + "name": "Bonafido" + }, + { + "id": "5582", + "name": "Homunculus" + }, + { + "id": "5583", + "name": "Homunculus" + }, + { + "id": "5585", + "name": "'Transmute' The Alchemist" + }, + { + "id": "5586", + "name": "'Transmute' The Alchemist" + }, + { + "id": "5587", + "name": "'Currency' The Alchemist" + }, + { + "id": "5588", + "name": "'Currency' The Alchemist" + }, + { + "id": "5592", + "name": "'The Guns'" + }, + { + "id": "5598", + "name": "Unicow" + }, + { + "id": "5604", + "name": "Elfinlocks" + }, + { + "id": "5605", + "name": "Clockwork cat" + }, + { + "id": "5606", + "name": "Clockwork cat" + }, + { + "id": "5612", + "name": "Rufus" + }, + { + "id": "5613", + "name": "Mi-Gor" + }, + { + "id": "5615", + "name": "Puffin" + }, + { + "id": "5616", + "name": "Brother Tranquility" + }, + { + "id": "5617", + "name": "Brother Tranquility" + }, + { + "id": "5618", + "name": "Brother Tranquility" + }, + { + "id": "5623", + "name": "Zombie monk" + }, + { + "id": "5624", + "name": "Zombie monk" + }, + { + "id": "5625", + "name": "Zombie monk" + }, + { + "id": "5626", + "name": "Zombie monk" + }, + { + "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": "Undead Lumberjack" + }, + { + "id": "5748", + "name": "Fishing spot" + }, + { + "id": "5749", + "name": "Fishing spot" + }, + { + "id": "5770", + "name": "Ur-zek" + }, + { + "id": "5771", + "name": "Ur-vass" + }, + { + "id": "5772", + "name": "Ur-taal" + }, + { + "id": "5773", + "name": "Ur-meg" + }, + { + "id": "5774", + "name": "Ur-lun" + }, + { + "id": "5775", + "name": "Ur-pel" + }, + { + "id": "5778", + "name": "Bartak" + }, + { + "id": "5779", + "name": "Turgall" + }, + { + "id": "5780", + "name": "Reldak" + }, + { + "id": "5781", + "name": "Miltog" + }, + { + "id": "5782", + "name": "Mernik" + }, + { + "id": "5787", + "name": "Gourmet" + }, + { + "id": "5789", + "name": "Gourmet" + }, + { + "id": "5790", + "name": "Gourmet" + }, + { + "id": "5791", + "name": "Gourmet" + }, + { + "id": "5792", + "name": "Turgok" + }, + { + "id": "5793", + "name": "Markog" + }, + { + "id": "5794", + "name": "Durgok" + }, + { + "id": "5795", + "name": "Tindar" + }, + { + "id": "5796", + "name": "Gundik" + }, + { + "id": "5797", + "name": "Zenkog" + }, + { + "id": "5798", + "name": "Lurgon" + }, + { + "id": "5799", + "name": "Ur-tag" + }, + { + "id": "5802", + "name": "Young 'un" + }, + { + "id": "5804", + "name": "Tyke" + }, + { + "id": "5806", + "name": "Nipper" + }, + { + "id": "5825", + "name": "Movario" + }, + { + "id": "5826", + "name": "Darve" + }, + { + "id": "5828", + "name": "Barlak" + }, + { + "id": "5830", + "name": "Rat Burgiss" + }, + { + "id": "5835", + "name": "Surok Magis" + }, + { + "id": "5836", + "name": "Zaff" + }, + { + "id": "5837", + "name": "Anna Jones" + }, + { + "id": "5838", + "name": "King Roald" + }, + { + "id": "5839", + "name": "Mishkal'un Dorn" + }, + { + "id": "5840", + "name": "Dakh'thoulan Aegis" + }, + { + "id": "5841", + "name": "Sil'as Dahcsnu" + }, + { + "id": "5853", + "name": "Bench" + }, + { + "id": "5857", + "name": "Zanik" + }, + { + "id": "5858", + "name": "Ur-tag" + }, + { + "id": "5862", + "name": "Sigmund and Zanik" + }, + { + "id": "5863", + "name": "Ambassador Alvijar" + }, + { + "id": "5868", + "name": "Tegdak" + }, + { + "id": "5869", + "name": "Zanik" + }, + { + "id": "5871", + "name": "Sergeant Mossfists" + }, + { + "id": "5872", + "name": "Sergeant Slimetoes" + }, + { + "id": "5887", + "name": "Cyrisus" + }, + { + "id": "5894", + "name": "Cyrisus" + }, + { + "id": "5895", + "name": "Cyrisus" + }, + { + "id": "5896", + "name": "Cyrisus" + }, + { + "id": "5897", + "name": "Cyrisus" + }, + { + "id": "5898", + "name": "'Bird's-Eye' Jack" + }, + { + "id": "5899", + "name": "The Inadequacy" + }, + { + "id": "5907", + "name": "The Illusive" + }, + { + "id": "5912", + "name": "Banker" + }, + { + "id": "5913", + "name": "Banker" + }, + { + "id": "5915", + "name": "Elsie" + }, + { + "id": "5918", + "name": "Stray dog" + }, + { + "id": "5932", + "name": "Barnabus Hurma" + }, + { + "id": "5933", + "name": "Marius Giste" + }, + { + "id": "5934", + "name": "Caden Azro" + }, + { + "id": "5935", + "name": "Thias Leacke" + }, + { + "id": "5936", + "name": "Sinco Doar" + }, + { + "id": "5937", + "name": "Tinse Torpe" + }, + { + "id": "5939", + "name": "Torrcs" + }, + { + "id": "5940", + "name": "Marfet" + }, + { + "id": "5941", + "name": "Museum guard" + }, + { + "id": "5942", + "name": "Museum guard" + }, + { + "id": "5943", + "name": "Museum guard" + }, + { + "id": "5946", + "name": "Schoolboy" + }, + { + "id": "5948", + "name": "Teacher and pupil" + }, + { + "id": "5949", + "name": "Schoolboy" + }, + { + "id": "5950", + "name": "Teacher" + }, + { + "id": "5951", + "name": "Schoolgirl" + }, + { + "id": "5953", + "name": "Workman" + }, + { + "id": "5955", + "name": "Workman" + }, + { + "id": "5957", + "name": "Schoolgirl" + }, + { + "id": "5964", + "name": "Ed Wood" + }, + { + "id": "5965", + "name": "Orlando Smith" + }, + { + "id": "5967", + "name": "Natural historian" + }, + { + "id": "5968", + "name": "Natural historian" + }, + { + "id": "5969", + "name": "Natural historian" + }, + { + "id": "5970", + "name": "Natural historian" + }, + { + "id": "5983", + "name": "Schoolgirl" + }, + { + "id": "5984", + "name": "Schoolgirl" + }, + { + "id": "5985", + "name": "Schoolgirl" + }, + { + "id": "5988", + "name": "Miazrqa" + }, + { + "id": "5989", + "name": "Grimgnash" + }, + { + "id": "5991", + "name": "Drain pipe" + }, + { + "id": "5995", + "name": "Mouse" + }, + { + "id": "5997", + "name": "Rupert the Beard" + }, + { + "id": "6000", + "name": "Rupert the Beard" + }, + { + "id": "6002", + "name": "Gnome" + }, + { + "id": "6003", + "name": "Winkin" + }, + { + "id": "6004", + "name": "Gnome" + }, + { + "id": "6005", + "name": "Cage" + }, + { + "id": "6071", + "name": "Immenizz" + }, + { + "id": "6075", + "name": "Cyclops" + }, + { + "id": "6082", + "name": "Captain Ned" + }, + { + "id": "6085", + "name": "Cabin boy Jenkins" + }, + { + "id": "6086", + "name": "Cabin boy Jenkins" + }, + { + "id": "6087", + "name": "Elvarg" + }, + { + "id": "6114", + "name": "Drake" + }, + { + "id": "6119", + "name": "Observatory professor" + }, + { + "id": "6130", + "name": "Clothears" + }, + { + "id": "6138", + "name": "Town crier" + }, + { + "id": "6139", + "name": "Town crier" + }, + { + "id": "6158", + "name": "Sir Lucan" + }, + { + "id": "6160", + "name": "Sir Lancelot" + }, + { + "id": "6161", + "name": "Sir Bedivere" + }, + { + "id": "6162", + "name": "Sir Tristram" + }, + { + "id": "6163", + "name": "Sir Pelleas" + }, + { + "id": "6164", + "name": "Sir Gawain" + }, + { + "id": "6165", + "name": "Sir Kay" + }, + { + "id": "6166", + "name": "Sir Pelleas" + }, + { + "id": "6167", + "name": "Sir Gawain" + }, + { + "id": "6168", + "name": "Sir Kay" + }, + { + "id": "6171", + "name": "Sir Kay" + }, + { + "id": "6172", + "name": "Sir Gawain" + }, + { + "id": "6173", + "name": "Sir Lucan" + }, + { + "id": "6174", + "name": "Bandit" + }, + { + "id": "6175", + "name": "Sir Tristram" + }, + { + "id": "6176", + "name": "Sir Pelleas" + }, + { + "id": "6177", + "name": "Sir Bedivere" + }, + { + "id": "6178", + "name": "Anna" + }, + { + "id": "6179", + "name": "David" + }, + { + "id": "6180", + "name": "Anna" + }, + { + "id": "6181", + "name": "Court judge" + }, + { + "id": "6182", + "name": "Jury" + }, + { + "id": "6185", + "name": "Prosecutor" + }, + { + "id": "6186", + "name": "Morgan Le Faye" + }, + { + "id": "6191", + "name": "Sinclair" + }, + { + "id": "6199", + "name": "Banker" + }, + { + "id": "6202", + "name": "K'ril Tsutsaroth" + }, + { + "id": "6284", + "name": "Warped terrorbird" + }, + { + "id": "6298", + "name": "Jeffery" + }, + { + "id": "6299", + "name": "Big monolith" + }, + { + "id": "6300", + "name": "Small monolith" + }, + { + "id": "6301", + "name": "Cute creature" + }, + { + "id": "6302", + "name": "Evil creature" + }, + { + "id": "6303", + "name": "Yewnock the engineer" + }, + { + "id": "6304", + "name": "Bolrie" + }, + { + "id": "6305", + "name": "Hazelmere" + }, + { + "id": "6307", + "name": "Advisor" + }, + { + "id": "6308", + "name": "King Argenthorg" + }, + { + "id": "6309", + "name": "Prince Argenthorg" + }, + { + "id": "6310", + "name": "Cute creature" + }, + { + "id": "6312", + "name": "Evil creature" + }, + { + "id": "6313", + "name": "Terrorbird servant" + }, + { + "id": "6316", + "name": "Guard no. 21" + }, + { + "id": "6317", + "name": "Guard no. 72" + }, + { + "id": "6318", + "name": "Longramble" + }, + { + "id": "6319", + "name": "Spirit tree" + }, + { + "id": "6321", + "name": "Spirit tree" + }, + { + "id": "6333", + "name": "Child" + }, + { + "id": "6340", + "name": "Child" + }, + { + "id": "6341", + "name": "Child" + }, + { + "id": "6342", + "name": "Child" + }, + { + "id": "6343", + "name": "Child" + }, + { + "id": "6345", + "name": "Child" + }, + { + "id": "6357", + "name": "Street urchin" + }, + { + "id": "6359", + "name": "Street urchin" + }, + { + "id": "6360", + "name": "Street urchin" + }, + { + "id": "6361", + "name": "Street urchin" + }, + { + "id": "6362", + "name": "Eniola" + }, + { + "id": "6373", + "name": "Kennith" + }, + { + "id": "6375", + "name": "Wizard Cromperty" + }, + { + "id": "6384", + "name": "Karamjan Jungle Eagle" + }, + { + "id": "6386", + "name": "Bandit" + }, + { + "id": "6391", + "name": "Glough" + }, + { + "id": "6392", + "name": "Oldak" + }, + { + "id": "6393", + "name": "Zanik" + }, + { + "id": "6394", + "name": "Zanik" + }, + { + "id": "6396", + "name": "Zanik" + }, + { + "id": "6398", + "name": "Zanik" + }, + { + "id": "6400", + "name": "Oldak" + }, + { + "id": "6468", + "name": "Skoblin" + }, + { + "id": "6474", + "name": "Snothead" + }, + { + "id": "6475", + "name": "Snailfeet" + }, + { + "id": "6476", + "name": "Mosschin" + }, + { + "id": "6477", + "name": "Redeyes" + }, + { + "id": "6478", + "name": "Strongbones" + }, + { + "id": "6479", + "name": "Grubfoot" + }, + { + "id": "6480", + "name": "Grubfoot" + }, + { + "id": "6483", + "name": "Priest" + }, + { + "id": "6484", + "name": "Priest" + }, + { + "id": "6485", + "name": "Priest" + }, + { + "id": "6486", + "name": "Priest" + }, + { + "id": "6487", + "name": "Priest" + }, + { + "id": "6489", + "name": "High priest" + }, + { + "id": "6505", + "name": "Registrar 1" + }, + { + "id": "6511", + "name": "Jury" + }, + { + "id": "6513", + "name": "Spectator" + }, + { + "id": "6515", + "name": "Spectator" + }, + { + "id": "6517", + "name": "Spectator" + }, + { + "id": "6519", + "name": "Spectator" + }, + { + "id": "6520", + "name": "Head Registrar" + }, + { + "id": "6537", + "name": "Mandrith" + }, + { + "id": "6538", + "name": "Banker" + }, + { + "id": "6539", + "name": "Charley the Cleaner" + }, + { + "id": "6540", + "name": "Scotty the Cleaner" + }, + { + "id": "6541", + "name": "Sanchez the Cleaner" + }, + { + "id": "6542", + "name": "Summer Bonde" + }, + { + "id": "6566", + "name": "Broken grave marker" + }, + { + "id": "6567", + "name": "Collapsing grave marker" + }, + { + "id": "6568", + "name": "Grave marker" + }, + { + "id": "6569", + "name": "Broken grave marker" + }, + { + "id": "6570", + "name": "Collapsing grave marker" + }, + { + "id": "6571", + "name": "Gravestone" + }, + { + "id": "6572", + "name": "Broken gravestone" + }, + { + "id": "6573", + "name": "Collapsing gravestone" + }, + { + "id": "6574", + "name": "Gravestone" + }, + { + "id": "6575", + "name": "Broken gravestone" + }, + { + "id": "6576", + "name": "Collapsing gravestone" + }, + { + "id": "6577", + "name": "Gravestone" + }, + { + "id": "6578", + "name": "Broken gravestone" + }, + { + "id": "6579", + "name": "Collapsing gravestone" + }, + { + "id": "6580", + "name": "Stele" + }, + { + "id": "6581", + "name": "Broken stele" + }, + { + "id": "6582", + "name": "Collapsing stele" + }, + { + "id": "6583", + "name": "Saradomin symbol" + }, + { + "id": "6584", + "name": "Broken Saradomin symbol" + }, + { + "id": "6585", + "name": "Collapsing Saradomin symbol" + }, + { + "id": "6586", + "name": "Zamorak symbol" + }, + { + "id": "6587", + "name": "Broken Zamorak symbol" + }, + { + "id": "6588", + "name": "Collapsing Zamorak symbol" + }, + { + "id": "6589", + "name": "Guthix symbol" + }, + { + "id": "6590", + "name": "Broken Guthix symbol" + }, + { + "id": "6591", + "name": "Collapsing Guthix symbol" + }, + { + "id": "6592", + "name": "Bandos symbol" + }, + { + "id": "6593", + "name": "Broken Bandos symbol" + }, + { + "id": "6594", + "name": "Collapsing Bandos symbol" + }, + { + "id": "6595", + "name": "Armadyl symbol" + }, + { + "id": "6596", + "name": "Broken Armadyl symbol" + }, + { + "id": "6597", + "name": "Collapsing Armadyl symbol" + }, + { + "id": "6598", + "name": "Memorial stone" + }, + { + "id": "6599", + "name": "Broken memorial stone" + }, + { + "id": "6600", + "name": "Collapsing memorial stone" + }, + { + "id": "6601", + "name": "Memorial stone" + }, + { + "id": "6602", + "name": "Broken memorial stone" + }, + { + "id": "6603", + "name": "Collapsing memorial stone" + }, + { + "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 imp" + }, + { + "id": "6741", + "name": "Snow" + }, + { + "id": "6746", + "name": "Snowman" + }, + { + "id": "6751", + "name": "Bulldog" + }, + { + "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": "Mummy" + }, + { + "id": "6771", + "name": "Giant scarab" + }, + { + "id": "6775", + "name": "Scabaras priest" + }, + { + "id": "6782", + "name": "Maisa" + }, + { + "id": "6783", + "name": "Maisa" + }, + { + "id": "6784", + "name": "Priest" + }, + { + "id": "6791", + "name": "High Priest of Scabaras" + }, + { + "id": "6792", + "name": "Vulture" + }, + { + "id": "6793", + "name": "Spirit terrorbird" + }, + { + "id": "6808", + "name": "Beaver" + }, + { + "id": "6898", + "name": "Pet shop owner" + }, + { + "id": "6899", + "name": "Bulldog" + }, + { + "id": "6908", + "name": "Baby penguin" + }, + { + "id": "6909", + "name": "Penguin" + }, + { + "id": "6910", + "name": "Penguin" + }, + { + "id": "6911", + "name": "Raven chick" + }, + { + "id": "6912", + "name": "Raven" + }, + { + "id": "6913", + "name": "Baby raccoon" + }, + { + "id": "6914", + "name": "Raccoon" + }, + { + "id": "6915", + "name": "Baby gecko" + }, + { + "id": "6916", + "name": "Gecko" + }, + { + "id": "6918", + "name": "Gecko" + }, + { + "id": "6919", + "name": "Baby squirrel" + }, + { + "id": "6920", + "name": "Squirrel" + }, + { + "id": "6922", + "name": "Baby chameleon" + }, + { + "id": "6923", + "name": "Chameleon" + }, + { + "id": "6924", + "name": "Baby chameleon" + }, + { + "id": "6925", + "name": "Chameleon" + }, + { + "id": "6926", + "name": "Baby chameleon" + }, + { + "id": "6927", + "name": "Chameleon" + }, + { + "id": "6928", + "name": "Baby chameleon" + }, + { + "id": "6929", + "name": "Chameleon" + }, + { + "id": "6930", + "name": "Baby chameleon" + }, + { + "id": "6931", + "name": "Chameleon" + }, + { + "id": "6932", + "name": "Baby chameleon" + }, + { + "id": "6933", + "name": "Chameleon" + }, + { + "id": "6934", + "name": "Baby chameleon" + }, + { + "id": "6935", + "name": "Chameleon" + }, + { + "id": "6936", + "name": "Baby chameleon" + }, + { + "id": "6937", + "name": "Chameleon" + }, + { + "id": "6938", + "name": "Baby chameleon" + }, + { + "id": "6939", + "name": "Chameleon" + }, + { + "id": "6940", + "name": "Baby chameleon" + }, + { + "id": "6941", + "name": "Chameleon" + }, + { + "id": "6945", + "name": "Vulture chick" + }, + { + "id": "6946", + "name": "Vulture" + }, + { + "id": "6947", + "name": "Baby giant crab" + }, + { + "id": "6948", + "name": "Giant crab" + }, + { + "id": "6949", + "name": "Saradomin chick" + }, + { + "id": "6950", + "name": "Saradomin bird" + }, + { + "id": "6951", + "name": "Saradomin owl" + }, + { + "id": "6952", + "name": "Zamorak chick" + }, + { + "id": "6953", + "name": "Zamorak bird" + }, + { + "id": "6954", + "name": "Zamorak hawk" + }, + { + "id": "6955", + "name": "Guthix chick" + }, + { + "id": "6956", + "name": "Guthix bird" + }, + { + "id": "6957", + "name": "Guthix raptor" + }, + { + "id": "6970", + "name": "Pikkupstix" + }, + { + "id": "6988", + "name": "Giant wolpertinger" + }, + { + "id": "6991", + "name": "Ibis" + }, + { + "id": "6996", + "name": "Fishing spot" + }, + { + "id": "7000", + "name": "Dark Squall" + }, + { + "id": "7002", + "name": "Surok Magis" + }, + { + "id": "7006", + "name": "Grenwall" + }, + { + "id": "7015", + "name": "Platypus" + }, + { + "id": "7016", + "name": "Platypus" + }, + { + "id": "7017", + "name": "Platypus" + }, + { + "id": "7018", + "name": "Baby platypus" + }, + { + "id": "7019", + "name": "Baby platypus" + }, + { + "id": "7020", + "name": "Baby platypus" + }, + { + "id": "7022", + "name": "Platypus" + }, + { + "id": "7023", + "name": "Platypus" + }, + { + "id": "7025", + "name": "Baby platypus" + }, + { + "id": "7026", + "name": "Baby platypus" + }, + { + "id": "7027", + "name": "Patrick" + }, + { + "id": "7028", + "name": "Penelope" + }, + { + "id": "7029", + "name": "Peter" + }, + { + "id": "7030", + "name": "Peanut" + }, + { + "id": "7032", + "name": "Diseased kebbit" + }, + { + "id": "7040", + "name": "Fishing spot" + }, + { + "id": "7045", + "name": "Fishing spot" + }, + { + "id": "7046", + "name": "Fishing spot" + }, + { + "id": "7048", + "name": "Frawd" + }, + { + "id": "7050", + "name": "Ogress banker" + }, + { + "id": "7052", + "name": "Seegud" + }, + { + "id": "7053", + "name": "Chargurr" + }, + { + "id": "7054", + "name": "Chargurr" + }, + { + "id": "7055", + "name": "Snurgh" + }, + { + "id": "7058", + "name": "Kringk" + }, + { + "id": "7059", + "name": "Thump" + }, + { + "id": "7060", + "name": "Massage table" + }, + { + "id": "7061", + "name": "Thump" + }, + { + "id": "7062", + "name": "Muggh" + }, + { + "id": "7063", + "name": "Kringk" + }, + { + "id": "7064", + "name": "Hairdryer" + }, + { + "id": "7065", + "name": "Snert" + }, + { + "id": "7068", + "name": "Tyke" + }, + { + "id": "7069", + "name": "Snarrl" + }, + { + "id": "7070", + "name": "Snarrk" + }, + { + "id": "7071", + "name": "I'rk" + }, + { + "id": "7072", + "name": "Thuddley" + }, + { + "id": "7073", + "name": "Grr'bah" + }, + { + "id": "7074", + "name": "Chomp" + }, + { + "id": "7075", + "name": "Grubb" + }, + { + "id": "7076", + "name": "Grunther" + }, + { + "id": "7077", + "name": "Glum" + }, + { + "id": "7083", + "name": "Flying bugs" + }, + { + "id": "7087", + "name": "Balnea" + }, + { + "id": "7088", + "name": "Chief Tess" + }, + { + "id": "7089", + "name": "Chargurr" + }, + { + "id": "7090", + "name": "Wise Old Man" + }, + { + "id": "7091", + "name": "Dawg" + }, + { + "id": "7116", + "name": "Drunken sailor" + }, + { + "id": "7117", + "name": "Drunken sailor" + }, + { + "id": "7118", + "name": "Catapult engineer" + }, + { + "id": "7119", + "name": "Tyras guard" + }, + { + "id": "7121", + "name": "General Hining" + }, + { + "id": "7122", + "name": "Githan" + }, + { + "id": "7123", + "name": "Amulet of Nature" + }, + { + "id": "7124", + "name": "Mud" + }, + { + "id": "7136", + "name": "Surok Magis" + }, + { + "id": "7143", + "name": "Professor Henry" + }, + { + "id": "7163", + "name": "Villager" + }, + { + "id": "7165", + "name": "Villager" + }, + { + "id": "7166", + "name": "Villager" + }, + { + "id": "7167", + "name": "Villager" + }, + { + "id": "7169", + "name": "Kimberly" + }, + { + "id": "7170", + "name": "Kimberly" + }, + { + "id": "7171", + "name": "Kennith" + }, + { + "id": "7172", + "name": "Kennith" + }, + { + "id": "7173", + "name": "Kennith" + }, + { + "id": "7174", + "name": "Kennith" + }, + { + "id": "7175", + "name": "Kennith" + }, + { + "id": "7176", + "name": "Villager" + }, + { + "id": "7184", + "name": "Ezekial Lovecraft" + }, + { + "id": "7185", + "name": "Clive" + }, + { + "id": "7186", + "name": "Katherine" + }, + { + "id": "7187", + "name": "Katherine" + }, + { + "id": "7188", + "name": "Clive" + }, + { + "id": "7189", + "name": "Kent" + }, + { + "id": "7190", + "name": "Rabbit hole" + }, + { + "id": "7197", + "name": "Easter Bunny" + }, + { + "id": "7198", + "name": "Bunny" + }, + { + "id": "7199", + "name": "Bunny" + }, + { + "id": "7200", + "name": "Guard bunny" + }, + { + "id": "7201", + "name": "Chocatrice" + }, + { + "id": "7203", + "name": "Void Knight" + }, + { + "id": "7205", + "name": "Mouse" + }, + { + "id": "7208", + "name": "Rabbit" + }, + { + "id": "7261", + "name": "Raven chick" + }, + { + "id": "7262", + "name": "Raven" + }, + { + "id": "7263", + "name": "Raven chick" + }, + { + "id": "7264", + "name": "Raven" + }, + { + "id": "7265", + "name": "Raven chick" + }, + { + "id": "7266", + "name": "Raven" + }, + { + "id": "7267", + "name": "Raven chick" + }, + { + "id": "7268", + "name": "Raven" + }, + { + "id": "7269", + "name": "Raven chick" + }, + { + "id": "7270", + "name": "Raven" + }, + { + "id": "7271", + "name": "Baby raccoon" + }, + { + "id": "7272", + "name": "Raccoon" + }, + { + "id": "7273", + "name": "Baby raccoon" + }, + { + "id": "7274", + "name": "Raccoon" + }, + { + "id": "7276", + "name": "Baby raccoon" + }, + { + "id": "7277", + "name": "Baby gecko" + }, + { + "id": "7278", + "name": "Baby gecko" + }, + { + "id": "7279", + "name": "Baby gecko" + }, + { + "id": "7280", + "name": "Baby gecko" + }, + { + "id": "7281", + "name": "Gecko" + }, + { + "id": "7282", + "name": "Gecko" + }, + { + "id": "7283", + "name": "Gecko" + }, + { + "id": "7284", + "name": "Gecko" + }, + { + "id": "7286", + "name": "Baby gecko" + }, + { + "id": "7287", + "name": "Baby gecko" + }, + { + "id": "7288", + "name": "Baby gecko" + }, + { + "id": "7289", + "name": "Gecko" + }, + { + "id": "7290", + "name": "Gecko" + }, + { + "id": "7291", + "name": "Gecko" + }, + { + "id": "7292", + "name": "Gecko" + }, + { + "id": "7293", + "name": "Baby giant crab" + }, + { + "id": "7294", + "name": "Giant crab" + }, + { + "id": "7295", + "name": "Baby giant crab" + }, + { + "id": "7296", + "name": "Giant crab" + }, + { + "id": "7297", + "name": "Baby giant crab" + }, + { + "id": "7298", + "name": "Giant crab" + }, + { + "id": "7299", + "name": "Baby giant crab" + }, + { + "id": "7300", + "name": "Giant crab" + }, + { + "id": "7301", + "name": "Baby squirrel" + }, + { + "id": "7302", + "name": "Squirrel" + }, + { + "id": "7303", + "name": "Baby squirrel" + }, + { + "id": "7304", + "name": "Squirrel" + }, + { + "id": "7305", + "name": "Baby squirrel" + }, + { + "id": "7306", + "name": "Squirrel" + }, + { + "id": "7307", + "name": "Baby squirrel" + }, + { + "id": "7308", + "name": "Squirrel" + }, + { + "id": "7309", + "name": "Baby squirrel" + }, + { + "id": "7310", + "name": "Baby squirrel" + }, + { + "id": "7311", + "name": "Baby squirrel" + }, + { + "id": "7312", + "name": "Baby squirrel" + }, + { + "id": "7313", + "name": "Baby penguin" + }, + { + "id": "7314", + "name": "Penguin" + }, + { + "id": "7315", + "name": "Penguin" + }, + { + "id": "7316", + "name": "Baby penguin" + }, + { + "id": "7317", + "name": "Penguin" + }, + { + "id": "7318", + "name": "Penguin" + }, + { + "id": "7319", + "name": "Vulture chick" + }, + { + "id": "7320", + "name": "Vulture" + }, + { + "id": "7321", + "name": "Vulture chick" + }, + { + "id": "7322", + "name": "Vulture" + }, + { + "id": "7323", + "name": "Vulture chick" + }, + { + "id": "7324", + "name": "Vulture" + }, + { + "id": "7325", + "name": "Vulture chick" + }, + { + "id": "7326", + "name": "Vulture" + }, + { + "id": "7327", + "name": "Vulture chick" + }, + { + "id": "7328", + "name": "Vulture" + }, + { + "id": "7369", + "name": "Void shifter" + }, + { + "id": "7372", + "name": "Ravenous locust" + }, + { + "id": "7383", + "name": "Uberlass" + }, + { + "id": "7384", + "name": "Uberlass" + }, + { + "id": "7385", + "name": "Uberlass" + }, + { + "id": "7386", + "name": "Uberlass" + }, + { + "id": "7387", + "name": "Sannytea" + }, + { + "id": "7388", + "name": "Sannytea" + }, + { + "id": "7389", + "name": "Irollsixes" + }, + { + "id": "7390", + "name": "Irollsixes" + }, + { + "id": "7391", + "name": "Foxyhunter" + }, + { + "id": "7392", + "name": "Foxyhunter" + }, + { + "id": "7393", + "name": "Foxyhunter" + }, + { + "id": "7394", + "name": "Gabriela" + }, + { + "id": "7395", + "name": "Teodor" + }, + { + "id": "7396", + "name": "Aurel" + }, + { + "id": "7397", + "name": "Cornelius" + }, + { + "id": "7399", + "name": "Sorin" + }, + { + "id": "7400", + "name": "Luscion" + }, + { + "id": "7401", + "name": "Sergiu" + }, + { + "id": "7402", + "name": "Radu" + }, + { + "id": "7403", + "name": "Grigore" + }, + { + "id": "7404", + "name": "Ileana" + }, + { + "id": "7405", + "name": "Valeria" + }, + { + "id": "7406", + "name": "Emilia" + }, + { + "id": "7407", + "name": "Florin" + }, + { + "id": "7408", + "name": "Catalina" + }, + { + "id": "7409", + "name": "Ivan" + }, + { + "id": "7410", + "name": "Victor" + }, + { + "id": "7411", + "name": "Helena" + }, + { + "id": "7412", + "name": "Mihail" + }, + { + "id": "7413", + "name": "Nicoleta" + }, + { + "id": "7414", + "name": "Vasile" + }, + { + "id": "7415", + "name": "Razvan" + }, + { + "id": "7416", + "name": "Luminata" + }, + { + "id": "7418", + "name": "Juvinate" + }, + { + "id": "7419", + "name": "Held vampyre juvinate" + }, + { + "id": "7420", + "name": "Hieronymus Avlafrim" + }, + { + "id": "7421", + "name": "Sasquine Huburns" + }, + { + "id": "7422", + "name": "Sasquine Huburns" + }, + { + "id": "7423", + "name": "Lollery" + }, + { + "id": "7424", + "name": "Wigglewoo" + }, + { + "id": "7427", + "name": "Orangeowns" + }, + { + "id": "7428", + "name": "I like m0m" + }, + { + "id": "7429", + "name": "I like m0m" + }, + { + "id": "7430", + "name": "Qutiedoll" + }, + { + "id": "7431", + "name": "Goreu" + }, + { + "id": "7432", + "name": "Ysgawyn" + }, + { + "id": "7433", + "name": "Arvel" + }, + { + "id": "7434", + "name": "Mawrth" + }, + { + "id": "7435", + "name": "Kelyn" + }, + { + "id": "7436", + "name": "Eoin" + }, + { + "id": "7437", + "name": "Iona" + }, + { + "id": "7442", + "name": "Arianwyn" + }, + { + "id": "7444", + "name": "Oronwen" + }, + { + "id": "7445", + "name": "Banker" + }, + { + "id": "7446", + "name": "Banker" + }, + { + "id": "7447", + "name": "Dalldav" + }, + { + "id": "7448", + "name": "Gethin" + }, + { + "id": "7449", + "name": "Amaethwr" + }, + { + "id": "7450", + "name": "Teclyn" + }, + { + "id": "7451", + "name": "Butterfly" + }, + { + "id": "7452", + "name": "Lapsang" + }, + { + "id": "7453", + "name": "Lapsang" + }, + { + "id": "7454", + "name": "Souchong" + }, + { + "id": "7455", + "name": "Souchong" + }, + { + "id": "7456", + "name": "Earlgrey" + }, + { + "id": "7457", + "name": "Earlgrey" + }, + { + "id": "7458", + "name": "Fairtrade" + }, + { + "id": "7464", + "name": "Sliceoflemon" + }, + { + "id": "7465", + "name": "Sliceoflemon" + }, + { + "id": "7466", + "name": "Milknosugar" + }, + { + "id": "7467", + "name": "Milknosugar" + }, + { + "id": "7468", + "name": "Randomdood" + }, + { + "id": "7469", + "name": "Employedman" + }, + { + "id": "7470", + "name": "Heidiggle" + }, + { + "id": "7471", + "name": "Dodgy Penny" + }, + { + "id": "7472", + "name": "Shadydude98" + }, + { + "id": "7473", + "name": "Madam C" + }, + { + "id": "7474", + "name": "M0m Online" + }, + { + "id": "7478", + "name": "Learking" + }, + { + "id": "7489", + "name": "Moglewee" + }, + { + "id": "7490", + "name": "Moglewee" + }, + { + "id": "7491", + "name": "Sarah Domin" + }, + { + "id": "7498", + "name": "Snowy knight" + }, + { + "id": "7499", + "name": "Sapphire glacialis" + }, + { + "id": "7509", + "name": "Evil Wibbler" + }, + { + "id": "7516", + "name": "Heresjohnny" + }, + { + "id": "7517", + "name": "Mogglewump" + }, + { + "id": "7521", + "name": "Renderorder" + }, + { + "id": "7522", + "name": "Boolean" + }, + { + "id": "7523", + "name": "Stress Diva" + }, + { + "id": "7524", + "name": "Treadsoftly" + }, + { + "id": "7525", + "name": "Helphelphelp" + }, + { + "id": "7526", + "name": "Doorbellpl0x" + }, + { + "id": "7527", + "name": "Fixmydoorup" + }, + { + "id": "7529", + "name": "Wallscaler" + }, + { + "id": "7530", + "name": "2scompany" + }, + { + "id": "7531", + "name": "2scompany" + }, + { + "id": "7533", + "name": "4sjustsilly" + }, + { + "id": "7534", + "name": "Roadblocked" + }, + { + "id": "7535", + "name": "Barricade" + }, + { + "id": "7536", + "name": "Barricade" + }, + { + "id": "7537", + "name": "Yoinker" + }, + { + "id": "7538", + "name": "Yoinker" + }, + { + "id": "7539", + "name": "Stopthief" + }, + { + "id": "7540", + "name": "Stopthief" + }, + { + "id": "7541", + "name": "Knickknack" + }, + { + "id": "7542", + "name": "Paddywhack" + }, + { + "id": "7543", + "name": "Giveadog" + }, + { + "id": "7544", + "name": "Spacebadgers" + }, + { + "id": "7545", + "name": "Freakypeaky" + }, + { + "id": "7546", + "name": "Rollinghome" + }, + { + "id": "7547", + "name": "Nullpointer" + }, + { + "id": "7548", + "name": "Badgerfreak" + }, + { + "id": "7549", + "name": "Windstrike32" + }, + { + "id": "7550", + "name": "Void Knight" + }, + { + "id": "7567", + "name": "Wraithboss" + }, + { + "id": "7568", + "name": "What Goudron" + }, + { + "id": "7574", + "name": "Lvzyoda" + }, + { + "id": "7575", + "name": "Airstriker" + }, + { + "id": "7576", + "name": "Droepedoff" + }, + { + "id": "7577", + "name": "Plzpudding" + }, + { + "id": "7578", + "name": "Assassin10" + }, + { + "id": "7579", + "name": "Steallake" + }, + { + "id": "7581", + "name": "Deepkiwi" + }, + { + "id": "7584", + "name": "Bigface Oz" + }, + { + "id": "7587", + "name": "Torcher" + }, + { + "id": "7588", + "name": "Torcher" + }, + { + "id": "7589", + "name": "Defiler" + }, + { + "id": "7590", + "name": "Defiler" + }, + { + "id": "7591", + "name": "Shifter" + }, + { + "id": "7592", + "name": "Shifter" + }, + { + "id": "7594", + "name": "Shifter" + }, + { + "id": "7595", + "name": "Splatter" + }, + { + "id": "7596", + "name": "Splatter" + }, + { + "id": "7597", + "name": "Splatter" + }, + { + "id": "7598", + "name": "Spinner" + }, + { + "id": "7599", + "name": "Ravager" + }, + { + "id": "7600", + "name": "Fiara" + }, + { + "id": "7601", + "name": "Reggie" + }, + { + "id": "7602", + "name": "Getorix" + }, + { + "id": "7603", + "name": "Pontimer" + }, + { + "id": "7604", + "name": "Alran" + }, + { + "id": "7610", + "name": "Flying female vampire" + }, + { + "id": "7611", + "name": "Flying female vampire" + }, + { + "id": "7612", + "name": "Flying female vampire" + }, + { + "id": "7613", + "name": "Flying female vampire" + }, + { + "id": "7622", + "name": "Vyrewatch" + }, + { + "id": "7623", + "name": "Vyrewatch" + }, + { + "id": "7624", + "name": "Vyrewatch" + }, + { + "id": "7625", + "name": "Vyrewatch" + }, + { + "id": "7630", + "name": "Vyrewatch" + }, + { + "id": "7633", + "name": "Vyrewatch" + }, + { + "id": "7636", + "name": "Fishing spot" + }, + { + "id": "7637", + "name": "Skeletal hand" + }, + { + "id": "7652", + "name": "Zaromark Sliver" + }, + { + "id": "7653", + "name": "Zaromark Sliver" + }, + { + "id": "7662", + "name": "Fistandantilus" + }, + { + "id": "7663", + "name": "Fistandantilus" + }, + { + "id": "7664", + "name": "Mercenary Adventurer" + }, + { + "id": "7665", + "name": "Ivan Strom" + }, + { + "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": "Mysterious person" + }, + { + "id": "7672", + "name": "Flaygian Screwte" + }, + { + "id": "7673", + "name": "Mekritus A'hara" + }, + { + "id": "7674", + "name": "Andiess Juip" + }, + { + "id": "7675", + "name": "Kael Forshaw" + }, + { + "id": "7676", + "name": "Andiess Juip" + }, + { + "id": "7677", + "name": "Kael Forshaw" + }, + { + "id": "7678", + "name": "Safalaan" + }, + { + "id": "7679", + "name": "Andiess Juip" + }, + { + "id": "7680", + "name": "Kael Forshaw" + }, + { + "id": "7681", + "name": "Safalaan" + }, + { + "id": "7684", + "name": "Vyrewatch" + }, + { + "id": "7685", + "name": "Vyrewatch" + }, + { + "id": "7686", + "name": "Safalaan" + }, + { + "id": "7687", + "name": "Spectral Vyrewatch" + }, + { + "id": "7688", + "name": "Drezel" + }, + { + "id": "7708", + "name": "Temple guardian" + }, + { + "id": "7712", + "name": "Baby icefiend" + }, + { + "id": "7718", + "name": "Brother Bordiss" + }, + { + "id": "7719", + "name": "Brother Althric" + }, + { + "id": "7720", + "name": "Drorkar" + }, + { + "id": "7721", + "name": "Nurmof" + }, + { + "id": "7722", + "name": "Lakki the delivery dwarf" + }, + { + "id": "7723", + "name": "Drorkar" + }, + { + "id": "7724", + "name": "Brother Bordiss" + }, + { + "id": "7725", + "name": "Professor Arblenap" + }, + { + "id": "7726", + "name": "Assistant" + }, + { + "id": "7728", + "name": "Baby icefiend" + }, + { + "id": "7737", + "name": "Will" + }, + { + "id": "7738", + "name": "Will" + }, + { + "id": "7739", + "name": "Phil" + }, + { + "id": "7742", + "name": "Fluffs" + }, + { + "id": "7743", + "name": "Fluffs" + }, + { + "id": "7744", + "name": "Kittens" + }, + { + "id": "7746", + "name": "TzHaar-Mej-Malk" + }, + { + "id": "7748", + "name": "TzHaar-Hur-Frok" + }, + { + "id": "7749", + "name": "TzHaar-Ket-Grol" + }, + { + "id": "7750", + "name": "TzHaar-Ket-Rok" + }, + { + "id": "7751", + "name": "TzHaar-Ket-Lurk" + }, + { + "id": "7753", + "name": "TzHaar-Mej" + }, + { + "id": "7754", + "name": "TzHaar-Hur" + }, + { + "id": "7755", + "name": "TzHaar-Xil" + }, + { + "id": "7757", + "name": "TzHaar-Hur-Brekt" + }, + { + "id": "7758", + "name": "TzHaar-Hur-Brekt" + }, + { + "id": "7759", + "name": "TzHaar-Ket-Jok" + }, + { + "id": "7760", + "name": "TzHaar-Ket-Jok" + }, + { + "id": "7761", + "name": "TzHaar-Xil-Mor" + }, + { + "id": "7762", + "name": "TzHaar-Xil-Mor" + }, + { + "id": "7763", + "name": "TzHaar-Mej-Kol" + }, + { + "id": "7764", + "name": "TzHaar-Mej-Kol" + }, + { + "id": "7765", + "name": "TzHaar-Hur-Klag" + }, + { + "id": "7766", + "name": "TzHaar-Hur-Klag" + }, + { + "id": "7770", + "name": "TokTz-Ket-Dill" + }, + { + "id": "7771", + "name": "TokTz-Ket-Dill" + }, + { + "id": "7774", + "name": "Skeleton" + }, + { + "id": "7775", + "name": "Skeleton" + }, + { + "id": "7776", + "name": "Skeleton" + }, + { + "id": "7777", + "name": "Skeleton" + }, + { + "id": "7778", + "name": "Skeleton" + }, + { + "id": "7779", + "name": "Sumona" + }, + { + "id": "7781", + "name": "Jesmona" + }, + { + "id": "7782", + "name": "Catolax" + }, + { + "id": "7783", + "name": "Ali Cat" + }, + { + "id": "7787", + "name": "Cave crawler" + }, + { + "id": "7788", + "name": "Skeleton" + }, + { + "id": "7789", + "name": "Zombie" + }, + { + "id": "7790", + "name": "Zombie" + }, + { + "id": "7791", + "name": "Zombie" + }, + { + "id": "7792", + "name": "Zombie" + }, + { + "id": "7793", + "name": "Banshee mistress" + }, + { + "id": "7794", + "name": "Banshee mistress" + }, + { + "id": "7795", + "name": "Insectoid assassin" + }, + { + "id": "7796", + "name": "Insectoid assassin" + }, + { + "id": "7797", + "name": "Kurask overlord" + }, + { + "id": "7798", + "name": "Monstrous cave crawler" + }, + { + "id": "7799", + "name": "Basilisk boss" + }, + { + "id": "7800", + "name": "Mightiest turoth" + }, + { + "id": "7801", + "name": "Aberrant spectre" + }, + { + "id": "7802", + "name": "Aberrant spectre" + }, + { + "id": "7803", + "name": "Aberrant spectre" + }, + { + "id": "7804", + "name": "Aberrant spectre" + }, + { + "id": "7805", + "name": "Kurask minion" + }, + { + "id": "7806", + "name": "Mummy warrior" + }, + { + "id": "7807", + "name": "Mummy warrior" + }, + { + "id": "7808", + "name": "Mummy warrior" + }, + { + "id": "7809", + "name": "Cat" + }, + { + "id": "7810", + "name": "Banshee" + }, + { + "id": "7811", + "name": "Kurask" + }, + { + "id": "7812", + "name": "Cave crawler" + }, + { + "id": "7813", + "name": "Basilisk" + }, + { + "id": "7814", + "name": "Turoth" + }, + { + "id": "7815", + "name": "Skeleton" + }, + { + "id": "7816", + "name": "Master" + }, + { + "id": "7817", + "name": "Zombie" + }, + { + "id": "7818", + "name": "Zombie" + }, + { + "id": "7819", + "name": "Zombie" + }, + { + "id": "7820", + "name": "Zombie" + }, + { + "id": "7821", + "name": "A wanderer." + }, + { + "id": "7822", + "name": "A wanderer." + }, + { + "id": "7824", + "name": "Osman" + }, + { + "id": "7825", + "name": "50 Ships Mufassah" + }, + { + "id": "7826", + "name": "Karamthulhu" + }, + { + "id": "7827", + "name": "Karamthulhu" + }, + { + "id": "7828", + "name": "Giant lobster" + }, + { + "id": "7829", + "name": "Giant crab" + }, + { + "id": "7830", + "name": "Customs Sergeant" + }, + { + "id": "7831", + "name": "Customs Sergeant" + }, + { + "id": "7832", + "name": "Young Ralph" + }, + { + "id": "7833", + "name": "Young Ralph" + }, + { + "id": "7834", + "name": "Young Ralph" + }, + { + "id": "7835", + "name": "Young Ralph" + }, + { + "id": "7836", + "name": "Young Ralph" + }, + { + "id": "7837", + "name": "Customs Officer" + }, + { + "id": "7838", + "name": "Customs Officer" + }, + { + "id": "7839", + "name": "Customs Officer" + }, + { + "id": "7840", + "name": "Heavy-Handed Harry" + }, + { + "id": "7841", + "name": "Player" + }, + { + "id": "7842", + "name": "Locker Officer" + }, + { + "id": "7843", + "name": "Locker Officer" + }, + { + "id": "7844", + "name": "Ex-ex-parrot" + }, + { + "id": "7845", + "name": "Pirate impling" + }, + { + "id": "7846", + "name": "Pirate impling" + }, + { + "id": "7847", + "name": "Captain Rabid Jack" + }, + { + "id": "7848", + "name": "Jack" + }, + { + "id": "7849", + "name": "Bosun Giles" + }, + { + "id": "7850", + "name": "Pirate" + }, + { + "id": "7851", + "name": "Lizzie" + }, + { + "id": "7852", + "name": "Cap'n Izzy No-Beard" + }, + { + "id": "7853", + "name": "Ralph" + }, + { + "id": "7854", + "name": "Brass Hand Harry" + }, + { + "id": "7855", + "name": "Bill Teach" + }, + { + "id": "7856", + "name": "Pirate" + }, + { + "id": "7857", + "name": "Brass Hand Harry" + }, + { + "id": "7858", + "name": "Gull" + }, + { + "id": "7861", + "name": "Jack" + }, + { + "id": "7862", + "name": "Fishing spot" + }, + { + "id": "7863", + "name": "Fishing spot" + }, + { + "id": "7864", + "name": "Fishing spot" + }, + { + "id": "7865", + "name": "Guardsman Dante" + }, + { + "id": "7866", + "name": "Guardsman DeShawn" + }, + { + "id": "7867", + "name": "Guardsman Brawn" + }, + { + "id": "7868", + "name": "Iain" + }, + { + "id": "7869", + "name": "Julian" + }, + { + "id": "7870", + "name": "Lachtopher" + }, + { + "id": "7871", + "name": "Samuel" + }, + { + "id": "7872", + "name": "Victoria" + }, + { + "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": "Man" + }, + { + "id": "7880", + "name": "Woman" + }, + { + "id": "7881", + "name": "Woman" + }, + { + "id": "7882", + "name": "Woman" + }, + { + "id": "7883", + "name": "Woman" + }, + { + "id": "7884", + "name": "Woman" + }, + { + "id": "7885", + "name": "Guardsman Dante" + }, + { + "id": "7886", + "name": "Guardsman DeShawn" + }, + { + "id": "7887", + "name": "Guardsman Brawn" + }, + { + "id": "7888", + "name": "Sergeant Abram" + }, + { + "id": "7889", + "name": "Guardsman Pazel" + }, + { + "id": "7890", + "name": "Guardsman Peale" + }, + { + "id": "7892", + "name": "General Wartface" + }, + { + "id": "7893", + "name": "General Bentnoze" + }, + { + "id": "7894", + "name": "General Wartface" + }, + { + "id": "7896", + "name": "General Bentnoze" + }, + { + "id": "7898", + "name": "Loar Shadow" + }, + { + "id": "7901", + "name": "Loar Shadow" + }, + { + "id": "7902", + "name": "Sergeant Abram" + }, + { + "id": "7903", + "name": "Guardsman Pazel" + }, + { + "id": "7904", + "name": "Guardsman Peale" + }, + { + "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": "Barricade guard" + }, + { + "id": "7912", + "name": "Border guard" + }, + { + "id": "7913", + "name": "Iain" + }, + { + "id": "7914", + "name": "Julian" + }, + { + "id": "7915", + "name": "Lachtopher" + }, + { + "id": "7916", + "name": "Samuel" + }, + { + "id": "7917", + "name": "Victoria" + }, + { + "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": "Man" + }, + { + "id": "7925", + "name": "Woman" + }, + { + "id": "7926", + "name": "Woman" + }, + { + "id": "7927", + "name": "Woman" + }, + { + "id": "7928", + "name": "Woman" + }, + { + "id": "7929", + "name": "Lumbridge Guide" + }, + { + "id": "7930", + "name": "Doomsayer" + }, + { + "id": "7931", + "name": "Donie" + }, + { + "id": "7932", + "name": "Gee" + }, + { + "id": "7933", + "name": "Duke Horacio" + }, + { + "id": "7934", + "name": "Sigmund" + }, + { + "id": "7935", + "name": "Hans" + }, + { + "id": "7936", + "name": "Cook" + }, + { + "id": "7937", + "name": "Father Aereck" + }, + { + "id": "7938", + "name": "Sir Vant" + }, + { + "id": "7939", + "name": "Sir Vant" + }, + { + "id": "7940", + "name": "Sir Vant" + }, + { + "id": "7941", + "name": "Sir Vant" + }, + { + "id": "7942", + "name": "Sir Vant" + }, + { + "id": "7943", + "name": "Dragon" + }, + { + "id": "7944", + "name": "Dragon" + }, + { + "id": "7945", + "name": "Dragon" + }, + { + "id": "7946", + "name": "Dragon" + }, + { + "id": "7947", + "name": "Dragon" + }, + { + "id": "7948", + "name": "Dragon" + }, + { + "id": "7949", + "name": "Lumbridge Guide" + }, + { + "id": "7950", + "name": "Melee Tutor" + }, + { + "id": "7951", + "name": "Ranged Tutor" + }, + { + "id": "7952", + "name": "Magic Tutor" + }, + { + "id": "7953", + "name": "Cooking Tutor" + }, + { + "id": "7954", + "name": "Crafting Tutor" + }, + { + "id": "7955", + "name": "Fishing Tutor" + }, + { + "id": "7956", + "name": "Mining Tutor" + }, + { + "id": "7957", + "name": "Prayer Tutor" + }, + { + "id": "7960", + "name": "Woodcutting Tutor" + }, + { + "id": "7961", + "name": "Bank Tutor" + }, + { + "id": "7962", + "name": "Gee" + }, + { + "id": "7963", + "name": "Spirit" + }, + { + "id": "7964", + "name": "Goblin" + }, + { + "id": "7965", + "name": "Goblin" + }, + { + "id": "7966", + "name": "Chaos druid" + }, + { + "id": "7967", + "name": "Shopkeeper" + }, + { + "id": "7968", + "name": "Explorer Jack" + }, + { + "id": "7970", + "name": "Smithing Tutor" + }, + { + "id": "7971", + "name": "Mining Tutor" + }, + { + "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": "Spirit" + }, + { + "id": "7985", + "name": "Summer Bonde" + }, + { + "id": "7987", + "name": "Spirit" + }, + { + "id": "7988", + "name": "Spirit" + }, + { + "id": "7989", + "name": "Erik Bonde" + }, + { + "id": "7990", + "name": "Spirit" + }, + { + "id": "7991", + "name": "Jallek Lenkin" + }, + { + "id": "7992", + "name": "Spirit" + }, + { + "id": "7993", + "name": "Meranek Thanatos" + }, + { + "id": "7994", + "name": "Ghostly warrior" + }, + { + "id": "7995", + "name": "Spirit Beast" + }, + { + "id": "7996", + "name": "Spirit Beast" + }, + { + "id": "7997", + "name": "Spirit Beast" + }, + { + "id": "7998", + "name": "Ghost" + }, + { + "id": "7999", + "name": "Ghost" + }, + { + "id": "8000", + "name": "Goth leprechaun" + }, + { + "id": "8001", + "name": "Jack" + }, + { + "id": "8003", + "name": "Sarah" + }, + { + "id": "8004", + "name": "Laura" + }, + { + "id": "8005", + "name": "Laura" + }, + { + "id": "8006", + "name": "Roger" + }, + { + "id": "8007", + "name": "Jorral" + }, + { + "id": "8008", + "name": "Guthix" + }, + { + "id": "8009", + "name": "Max the traveller" + }, + { + "id": "8010", + "name": "Man" + }, + { + "id": "8011", + "name": "Man" + }, + { + "id": "8012", + "name": "Woman" + }, + { + "id": "8013", + "name": "Woman" + }, + { + "id": "8014", + "name": "Haluned" + }, + { + "id": "8015", + "name": "Jack" + }, + { + "id": "8016", + "name": "Baby Sarah" + }, + { + "id": "8017", + "name": "Snowy" + }, + { + "id": "8018", + "name": "Roger" + }, + { + "id": "8019", + "name": "Portal" + }, + { + "id": "8020", + "name": "Portal" + }, + { + "id": "8021", + "name": "Yellow orb" + }, + { + "id": "8023", + "name": "Yellow orb" + }, + { + "id": "8024", + "name": "Yellow orb" + }, + { + "id": "8025", + "name": "Green orb" + }, + { + "id": "8027", + "name": "Green orb" + }, + { + "id": "8028", + "name": "Green orb" + }, + { + "id": "8029", + "name": "Wizard Korvak" + }, + { + "id": "8030", + "name": "Wizard Vief" + }, + { + "id": "8031", + "name": "Wizard Acantha" + }, + { + "id": "8032", + "name": "Wizard Elriss" + }, + { + "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": "Wizard" + }, + { + "id": "8042", + "name": "Squire Fyre" + }, + { + "id": "8043", + "name": "Squire Fyre" + }, + { + "id": "8044", + "name": "Orry" + }, + { + "id": "8045", + "name": "Orry" + }, + { + "id": "8046", + "name": "Ube" + }, + { + "id": "8047", + "name": "Ube" + }, + { + "id": "8048", + "name": "Waldo" + }, + { + "id": "8049", + "name": "Waldo" + }, + { + "id": "8050", + "name": "Gjalp" + }, + { + "id": "8051", + "name": "Gjalp" + }, + { + "id": "8052", + "name": "Brother Fintan" + }, + { + "id": "8053", + "name": "Brother Fintan" + }, + { + "id": "8054", + "name": "Stubthumb" + }, + { + "id": "8055", + "name": "Stubthumb" + }, + { + "id": "8056", + "name": "Stubthumb" + }, + { + "id": "8057", + "name": "Doronbol" + }, + { + "id": "8058", + "name": "Doronbol" + }, + { + "id": "8059", + "name": "Crate" + }, + { + "id": "8060", + "name": "Crate" + }, + { + "id": "8061", + "name": "Egg" + }, + { + "id": "8062", + "name": "Egg" + }, + { + "id": "8063", + "name": "Nanuq" + }, + { + "id": "8064", + "name": "Nanuq" + }, + { + "id": "8065", + "name": "Pumpkin" + }, + { + "id": "8078", + "name": "Maggie" + }, + { + "id": "8079", + "name": "Circus barker" + }, + { + "id": "8080", + "name": "Circus barker" + }, + { + "id": "8081", + "name": "Circus barker" + }, + { + "id": "8082", + "name": "Agility assistant" + }, + { + "id": "8083", + "name": "Magic assistant" + }, + { + "id": "8084", + "name": "Ranged assistant" + }, + { + "id": "8085", + "name": "Ringmaster" + }, + { + "id": "8086", + "name": "Audience" + }, + { + "id": "8087", + "name": "Audience" + }, + { + "id": "8088", + "name": "Ticket vendor" + }, + { + "id": "8089", + "name": "Rock" + }, + { + "id": "8091", + "name": "Star sprite" + }, + { + "id": "8092", + "name": "Shooting star shadow" + }, + { + "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": "Penguin" + }, + { + "id": "8104", + "name": "Barrel" + }, + { + "id": "8105", + "name": "Bush" + }, + { + "id": "8106", + "name": "Bush" + }, + { + "id": "8107", + "name": "Cactus" + }, + { + "id": "8108", + "name": "Crate" + }, + { + "id": "8109", + "name": "Rock" + }, + { + "id": "8110", + "name": "Toadstool" + }, + { + "id": "8111", + "name": "Calladin" + }, + { + "id": "8112", + "name": "Summer Bonde" + }, + { + "id": "8114", + "name": "Summer Bonde" + }, + { + "id": "8115", + "name": "Erik Bonde" + }, + { + "id": "8116", + "name": "Erik Bonde" + }, + { + "id": "8117", + "name": "Jallek Lenkin" + }, + { + "id": "8118", + "name": "Jallek Lenkin" + }, + { + "id": "8119", + "name": "Meranek Thanatos" + }, + { + "id": "8120", + "name": "Meranek Thanatos" + }, + { + "id": "8121", + "name": "Spirit" + }, + { + "id": "8122", + "name": "Rogue" + }, + { + "id": "8123", + "name": "Tormented wraith" + }, + { + "id": "8126", + "name": "Dark energy core" + }, + { + "id": "8127", + "name": "Dark energy core" + }, + { + "id": "8128", + "name": "Spirit Beast" + }, + { + "id": "8129", + "name": "Spirit Beast" + }, + { + "id": "8130", + "name": "Spirit Beast" + }, + { + "id": "8131", + "name": "Spirit Beast" + }, + { + "id": "8132", + "name": "Spirit Beast" + }, + { + "id": "8134", + "name": "Spirit" + }, + { + "id": "8135", + "name": "Summer Bonde" + }, + { + "id": "8136", + "name": "Erik Bonde" + }, + { + "id": "8137", + "name": "Jallek Lenkin" + }, + { + "id": "8138", + "name": "Meranek Thanatos" + }, + { + "id": "8139", + "name": "Hartwin" + }, + { + "id": "8140", + "name": "Hartwin" + }, + { + "id": "8141", + "name": "Zombie" + }, + { + "id": "8143", + "name": "Zombie" + }, + { + "id": "8144", + "name": "Zombie" + }, + { + "id": "8145", + "name": "Zombie" + }, + { + "id": "8146", + "name": "Zemouregal" + }, + { + "id": "8147", + "name": "Zemouregal" + }, + { + "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": "Armoured zombie" + }, + { + "id": "8165", + "name": "Sharathteerk" + }, + { + "id": "8166", + "name": "Sharathteerk" + }, + { + "id": "8168", + "name": "Arrav" + }, + { + "id": "8169", + "name": "Arrav" + }, + { + "id": "8170", + "name": "Ramarno" + }, + { + "id": "8171", + "name": "Dimintheis" + }, + { + "id": "8172", + "name": "Dimintheis" + }, + { + "id": "8173", + "name": "Guard" + }, + { + "id": "8174", + "name": "Clive" + }, + { + "id": "8175", + "name": "Ellamaria" + }, + { + "id": "8176", + "name": "Fish" + }, + { + "id": "8177", + "name": "Fish" + }, + { + "id": "8178", + "name": "Monk of Zamorak" + }, + { + "id": "8179", + "name": "Ellamaria" + }, + { + "id": "8180", + "name": "Traiborn" + }, + { + "id": "8181", + "name": "Fenkenstrain's Monster" + }, + { + "id": "8182", + "name": "Wise Old Man" + }, + { + "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": "Acrobat" + }, + { + "id": "8201", + "name": "Wendy" + }, + { + "id": "8202", + "name": "Trogs" + }, + { + "id": "8203", + "name": "Norman" + }, + { + "id": "8204", + "name": "Babe" + }, + { + "id": "8205", + "name": "Gus" + }, + { + "id": "8206", + "name": "Lottie" + }, + { + "id": "8207", + "name": "Aggie" + }, + { + "id": "8208", + "name": "Bat" + }, + { + "id": "8209", + "name": "Rat" + }, + { + "id": "8210", + "name": "Lizard" + }, + { + "id": "8211", + "name": "Blackbird" + }, + { + "id": "8212", + "name": "Spider" + }, + { + "id": "8213", + "name": "Snail" + }, + { + "id": "8214", + "name": "Cat" + }, + { + "id": "8215", + "name": "Lazy cat" + }, + { + "id": "8216", + "name": "Overgrown cat" + }, + { + "id": "8217", + "name": "Kitten" + }, + { + "id": "8218", + "name": "Wily cat" + }, + { + "id": "8219", + "name": "Guardian of Armadyl" + }, + { + "id": "8227", + "name": "Head mystic" + }, + { + "id": "8228", + "name": "Rewards 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": "Mystic" + }, + { + "id": "8239", + "name": "Head mystic" + }, + { + "id": "8240", + "name": "Clay familiar (class 1)" + }, + { + "id": "8241", + "name": "Clay familiar (class 1)" + }, + { + "id": "8242", + "name": "Clay familiar (class 2)" + }, + { + "id": "8243", + "name": "Clay familiar (class 2)" + }, + { + "id": "8244", + "name": "Clay familiar (class 3)" + }, + { + "id": "8245", + "name": "Clay familiar (class 3)" + }, + { + "id": "8246", + "name": "Clay familiar (class 4)" + }, + { + "id": "8247", + "name": "Clay familiar (class 4)" + }, + { + "id": "8248", + "name": "Clay familiar (class 5)" + }, + { + "id": "8249", + "name": "Clay familiar (class 5)" + }, + { + "id": "8250", + "name": "Grindxplox" + }, + { + "id": "8251", + "name": "Grindxplox" + }, + { + "id": "8252", + "name": "Stealing Creation team" + }, + { + "id": "8253", + "name": "Resource NPC" + }, + { + "id": "8254", + "name": "Game NPC" + }, + { + "id": "8255", + "name": "Resource NPC" + }, + { + "id": "8257", + "name": "Harrallak Menarous" + }, + { + "id": "8269", + "name": "Yadech Strongarm" + }, + { + "id": "8271", + "name": "Turael" + }, + { + "id": "8274", + "name": "Mazchna" + }, + { + "id": "8275", + "name": "Duradel" + }, + { + "id": "8276", + "name": "Cyrisus" + }, + { + "id": "8280", + "name": "Sloane" + }, + { + "id": "8281", + "name": "Balance Elemental" + }, + { + "id": "8282", + "name": "Balance Elemental" + }, + { + "id": "8283", + "name": "Balance Elemental" + }, + { + "id": "8284", + "name": "Balance Elemental" + }, + { + "id": "8285", + "name": "Balance Elemental" + }, + { + "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": "Undead hero" + }, + { + "id": "8298", + "name": "Stone block" + }, + { + "id": "8299", + "name": "Harrallak Menarous" + }, + { + "id": "8300", + "name": "Harrallak Menarous" + }, + { + "id": "8301", + "name": "Harrallak Menarous" + }, + { + "id": "8302", + "name": "Turael" + }, + { + "id": "8303", + "name": "Turael" + }, + { + "id": "8304", + "name": "Cyrisus" + }, + { + "id": "8305", + "name": "Cyrisus" + }, + { + "id": "8306", + "name": "Ghommal" + }, + { + "id": "8307", + "name": "Mazchna" + }, + { + "id": "8308", + "name": "Mazchna" + }, + { + "id": "8309", + "name": "Mazchna" + }, + { + "id": "8310", + "name": "Duradel" + }, + { + "id": "8311", + "name": "Sloane" + }, + { + "id": "8317", + "name": "Elite Dark Ranger" + }, + { + "id": "8318", + "name": "Elite Dark Ranger" + }, + { + "id": "8319", + "name": "Elite Dark Ranger" + }, + { + "id": "8320", + "name": "Elite Dark Mage" + }, + { + "id": "8321", + "name": "Elite Dark Mage" + }, + { + "id": "8322", + "name": "Elite Dark Mage" + }, + { + "id": "8323", + "name": "Elite Dark Mage" + }, + { + "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": "Elite Black Knight" + }, + { + "id": "8331", + "name": "Guardian of Armadyl" + }, + { + "id": "8332", + "name": "Guardian of Armadyl" + }, + { + "id": "8333", + "name": "Guardian of Armadyl" + }, + { + "id": "8334", + "name": "Mercenary axeman" + }, + { + "id": "8335", + "name": "Mercenary mage" + }, + { + "id": "8336", + "name": "Idria" + }, + { + "id": "8337", + "name": "Idria" + }, + { + "id": "8338", + "name": "Akrisae" + }, + { + "id": "8339", + "name": "Shady stranger" + }, + { + "id": "8340", + "name": "Shady stranger" + }, + { + "id": "8341", + "name": "Shady stranger" + }, + { + "id": "8342", + "name": "Guardian of Armadyl" + }, + { + "id": "8344", + "name": "Guardian of Armadyl" + }, + { + "id": "8345", + "name": "Local thug" + }, + { + "id": "8347", + "name": "Local mage" + }, + { + "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": "Undead troll" + }, + { + "id": "8393", + "name": "Vasador" + }, + { + "id": "8394", + "name": "Valator" + }, + { + "id": "8395", + "name": "Vaasdor" + }, + { + "id": "8396", + "name": "Varosad" + }, + { + "id": "8397", + "name": "Verisad" + }, + { + "id": "8398", + "name": "Vudisor" + }, + { + "id": "8399", + "name": "Vlatoad" + }, + { + "id": "8400", + "name": "Vedolas" + }, + { + "id": "8401", + "name": "Vundiar" + }, + { + "id": "8402", + "name": "Versita" + }, + { + "id": "8403", + "name": "Vislota" + }, + { + "id": "8404", + "name": "Vanjuta" + }, + { + "id": "8405", + "name": "Vasador" + }, + { + "id": "8406", + "name": "Valator" + }, + { + "id": "8407", + "name": "Vaasdor" + }, + { + "id": "8408", + "name": "Varosad" + }, + { + "id": "8409", + "name": "Verisad" + }, + { + "id": "8410", + "name": "Vudisor" + }, + { + "id": "8411", + "name": "Vlatoad" + }, + { + "id": "8412", + "name": "Vedolas" + }, + { + "id": "8413", + "name": "Vundiar" + }, + { + "id": "8414", + "name": "Versita" + }, + { + "id": "8415", + "name": "Vislota" + }, + { + "id": "8416", + "name": "Vanjuta" + }, + { + "id": "8417", + "name": "Ivy Sophista" + }, + { + "id": "8418", + "name": "Thaerisk Cemphier" + }, + { + "id": "8419", + "name": "Thaerisk Cemphier" + }, + { + "id": "8420", + "name": "Assassin" + }, + { + "id": "8423", + "name": "Assassin" + }, + { + "id": "8424", + "name": "Mithril dragon" + }, + { + "id": "8425", + "name": "Dragon head" + }, + { + "id": "8426", + "name": "Dragon head" + }, + { + "id": "8427", + "name": "Dragon head" + }, + { + "id": "8428", + "name": "Khazard launderer" + }, + { + "id": "8429", + "name": "Khazard cook" + }, + { + "id": "8430", + "name": "Silif" + }, + { + "id": "8431", + "name": "Silif" + }, + { + "id": "8432", + "name": "Silif" + }, + { + "id": "8433", + "name": "Silif" + }, + { + "id": "8434", + "name": "Silif" + }, + { + "id": "8435", + "name": "Shady stranger" + }, + { + "id": "8436", + "name": "Suspicious outsider" + }, + { + "id": "8437", + "name": "Elite Khazard guard" + }, + { + "id": "8438", + "name": "Elite Khazard guard" + }, + { + "id": "8439", + "name": "Elite Khazard guard" + }, + { + "id": "8440", + "name": "Elite Khazard guard" + }, + { + "id": "8441", + "name": "Dark Squall" + }, + { + "id": "8442", + "name": "Surok Magis" + }, + { + "id": "8443", + "name": "Lucien" + }, + { + "id": "8444", + "name": "Druid" + }, + { + "id": "8445", + "name": "Druid" + }, + { + "id": "8446", + "name": "Druid" + }, + { + "id": "8447", + "name": "Druid bodyguard" + }, + { + "id": "8448", + "name": "Druid bodyguard" + }, + { + "id": "8449", + "name": "Movario" + }, + { + "id": "8450", + "name": "Darve" + }, + { + "id": "8451", + "name": "Cave goblin" + }, + { + "id": "8452", + "name": "Movario" + }, + { + "id": "8453", + "name": "Darve" + }, + { + "id": "8454", + "name": "Light creature" + }, + { + "id": "8455", + "name": "Light creature" + }, + { + "id": "8456", + "name": "Druid spirit" + }, + { + "id": "8457", + "name": "Druid spirit" + }, + { + "id": "8458", + "name": "Druid spirit" + }, + { + "id": "8459", + "name": "Druid spirit" + }, + { + "id": "8460", + "name": "Turael" + }, + { + "id": "8462", + "name": "Spria" + }, + { + "id": "8463", + "name": "Mazchna" + }, + { + "id": "8464", + "name": "Mazchna" + }, + { + "id": "8465", + "name": "Achtryn" + }, + { + "id": "8466", + "name": "Duradel" + }, + { + "id": "8467", + "name": "Lapalok" + }, + { + "id": "8468", + "name": "Laidee Gnonock" + }, + { + "id": "8485", + "name": "Hazelmere" + }, + { + "id": "8486", + "name": "Undead mage" + }, + { + "id": "8487", + "name": "Wild broav" + }, + { + "id": "8488", + "name": "Hazelmere" + }, + { + "id": "8490", + "name": "Hazelmere" + }, + { + "id": "8491", + "name": "Broav" + }, + { + "id": "8492", + "name": "Sithaph" + }, + { + "id": "8494", + "name": "Strisath" + }, + { + "id": "8495", + "name": "Sakirth" + }, + { + "id": "8496", + "name": "Hazelmere's hat" + }, + { + "id": "8497", + "name": "Lucien" + }, + { + "id": "8498", + "name": "A lazy Khazard guard" + }, + { + "id": "8499", + "name": "Thanksgiving Turkey" + }, + { + "id": "8500", + "name": "Cook's brother" + }, + { + "id": "8501", + "name": "Turkey" + }, + { + "id": "8502", + "name": "Cactus" + }, + { + "id": "8503", + "name": "Cactus" + }, + { + "id": "8504", + "name": "Bush" + }, + { + "id": "8505", + "name": "Toadstool" + }, + { + "id": "8506", + "name": "Barrel" + }, + { + "id": "8507", + "name": "Bush" + }, + { + "id": "8508", + "name": "Crate" + }, + { + "id": "8509", + "name": "Bush" + }, + { + "id": "8510", + "name": "Rock" + }, + { + "id": "8511", + "name": "Bush" + }, + { + "id": "8512", + "name": "Marvin" + }, + { + "id": "8513", + "name": "Marius" + }, + { + "id": "8514", + "name": "Benny" + }, + { + "id": "8515", + "name": "Yeti" + }, + { + "id": "8516", + "name": "Yeti" + }, + { + "id": "8517", + "name": "Jack Frost" + }, + { + "id": "8518", + "name": "Jack Frost" + }, + { + "id": "8519", + "name": "Jack Frost" + }, + { + "id": "8520", + "name": "Jack Frost" + }, + { + "id": "8521", + "name": "Snow imp" + }, + { + "id": "8537", + "name": "Snow imp" + }, + { + "id": "8538", + "name": "Snow imp" + }, + { + "id": "8539", + "name": "Queen of Snow" + }, + { + "id": "8540", + "name": "Santa Claus" + }, + { + "id": "8541", + "name": "Head snow imp" + }, + { + "id": "8542", + "name": "Head snow imp" + }, + { + "id": "8543", + "name": "Head snow imp" + }, + { + "id": "8544", + "name": "Isidor" + }, + { + "id": "8545", + "name": "Mystic" + }, + { + "id": "8546", + "name": "Balance Elemental" + }, + { + "id": "8547", + "name": "Wounded phoenix" + }, + { + "id": "8548", + "name": "Phoenix" + }, + { + "id": "8549", + "name": "Phoenix" + }, + { + "id": "8550", + "name": "Phoenix eggling" + }, + { + "id": "8551", + "name": "Phoenix eggling" + }, + { + "id": "8552", + "name": "Large egg" + }, + { + "id": "8553", + "name": "Priest of Guthix" + }, + { + "id": "8556", + "name": "Brian Twitcher" + }, + { + "id": "8557", + "name": "Lesser reborn warrior" + }, + { + "id": "8558", + "name": "Lesser reborn warrior" + }, + { + "id": "8559", + "name": "Greater reborn warrior" + }, + { + "id": "8560", + "name": "Greater reborn warrior" + }, + { + "id": "8561", + "name": "Lesser reborn ranger" + }, + { + "id": "8562", + "name": "Lesser reborn ranger" + }, + { + "id": "8563", + "name": "Greater reborn ranger" + }, + { + "id": "8564", + "name": "Greater reborn ranger" + }, + { + "id": "8565", + "name": "Lesser reborn mage" + }, + { + "id": "8566", + "name": "Lesser reborn mage" + }, + { + "id": "8567", + "name": "Greater reborn mage" + }, + { + "id": "8568", + "name": "Greater reborn mage" + }, + { + "id": "8569", + "name": "Lesser reborn warrior" + }, + { + "id": "8570", + "name": "Greater reborn warrior" + }, + { + "id": "8571", + "name": "Lesser reborn ranger" + }, + { + "id": "8572", + "name": "Greater reborn ranger" + }, + { + "id": "8573", + "name": "Lesser reborn mage" + }, + { + "id": "8574", + "name": "Greater reborn mage" + }, + { + "id": "8575", + "name": "Phoenix" + }, + { + "id": "8576", + "name": "Phoenix" + }, + { + "id": "8577", + "name": "Phoenix eggling" + }, + { + "id": "8578", + "name": "Phoenix eggling" + }, + { + "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": "Karma the chameleon" + }, + { + "id": "8590", + "name": "Geoffrey" + }, + { + "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" + }, + { + "name": "Elena", + "id": "335", + "examine": "She doesn't look too happy." + }, + { + "examine": "Use 1344 to change me", + "name": "Skippy Varbit", + "id": "2795" + }, + { + "examine": "He looks angry and smells drunk.", + "name": "Skippy", + "id": "2796" + }, + { + "name": "Skippy", + "id": "2797", + "examine": "Skippy, just looking a little 'tender'." + }, + { + "name": "Skippy", + "id": "2798", + "examine": "He seems a lot less angry and smells a great deal fresher." + }, + { + "name": "Skippy", + "id": "2799", + "examine": "He seems a bit confused." } ] \ No newline at end of file diff --git a/Server/data/configs/npc_spawns.json b/Server/data/configs/npc_spawns.json index 74f4d1946..8899eea83 100644 --- a/Server/data/configs/npc_spawns.json +++ b/Server/data/configs/npc_spawns.json @@ -1,11070 +1,12306 @@ -[ - { - "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}-{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}-{2893,3569,1,1,3}" - }, - { - "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}-{3231,3308,0,1,3}-{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}-{3160,3082,0,1,3}-{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}-{3189,3278,0,1,3}-{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}" - }, - { - "npc_id": "43", - "loc_data": "{3241,3354,0,1,3}-{3200,3263,0,0,0}-{3212,3262,0,1,1}-{2926,3323,0,1,0}-{2922,3325,0,1,3}" - }, - { - "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": "{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}" - }, - { - "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}-{3104,3680,0,1,3}-{3114,3688,0,1,1}-{3121,3675,0,1,1}-{3127,3695,0,1,3}-{3129,3678,0,1,2}-{3130,3689,0,1,2}-{3128,3665,0,1,3}-{3135,3701,0,1,6}-{3085,10134,0,1,0}-{3090,10123,0,1,0}-{3094,10133,0,1,0}-{3095,10120,0,1,0}-{3095,10128,0,1,0}-{3098,10122,0,1,0}-{3104,10127,0,1,0}-{3105,10125,0,1,0}-{3177,3905,0,1,4}-{3180,3908,0,1,2}-{3189,3914,0,1,6}-{3193,3906,0,1,2}-{2402,9785,0,1,1}-{2403,9782,0,1,1}-{2408,9782,0,1,1}-{2408,9785,0,1,1}-{2413,9776,0,1,1}-{2417,9777,0,1,1}-{2412,9773,0,1,1}-{2416,9773,0,1,1}-{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}-{3232,3790,0,0,4}" - }, - { - "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}-{3188,3216,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}-{3169,3257,0,1,1}-{3164,3236,0,1,6}-{3148,3226,0,1,5}-{3162,3223,0,1,7}-{3151,3221,0,1,0}-{3160,3221,0,1,0}-{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}" - }, - { - "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}-{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": "{2945,3910,0,1,0}-{2956,3923,0,1,0}-{2961,3919,0,1,0}-{2968,3915,0,1,0}-{2972,3927,0,1,0}-{2975,3949,0,1,0}-{2961,3945,0,1,0}-{2950,3927,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}-{3069,3901,0,1,4}-{3079,3955,0,1,3}-{3076,3953,0,1,0}-{3079,3952,0,1,1}-{3072,3952,0,1,0}-{3075,3948,0,1,2}-{3072,3956,0,1,1}-{3085,3951,0,1,6}-{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": "{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": "{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}-{3058,10128,0,1,0}-{3046,10139,0,1,0}-{3110,3157,2,1,5}" - }, - { - "npc_id": "83", - "loc_data": "{3169,3711,0,1,7}-{3095,10095,0,1,0}-{3095,10086,0,1,0}-{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}-{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}-{3199,3209,0,1,1}-{3193,3211,0,1,5}-{3197,3203,0,1,1}-{3201,3200,0,1,4}-{3199,3210,0,1,0}-{3191,3207,0,1,4}-{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}" - }, - { - "npc_id": "87", - "loc_data": "{3241,3531,0,1,7}-{3260,3540,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}" - }, - { - "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}-{3003,3593,0,1,5}-{3007,3596,0,1,4}-{3025,3595,0,1,4}-{3022,3594,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}-{2993,3940,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}" - }, - { - "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}" - }, - { - "npc_id": "94", - "loc_data": "{2589,9881,0,1,6}-{2591,9882,0,1,4}" - }, - { - "npc_id": "95", - "loc_data": "{2616,3283,0,1,3}-{2620,3283,0,1,3}-{2502,2969,0,1,6}-{2507,2971,0,1,2}-{2600,2959,0,1,6}-{2606,2971,0,1,6}-{2606,2963,0,1,4}-{2612,2965,0,1,6}-{2611,2973,0,1,5}-{2613,2963,0,1,3}-{2459,2884,0,1,7}-{2496,2907,0,1,6}-{2489,2887,0,1,7}-{2473,2885,0,1,7}-{2466,2872,0,1,4}-{2452,2883,0,1,5}-{2440,2869,0,1,5}-{2460,2849,0,1,1}-{2451,2842,0,1,4}-{2467,2838,0,1,6}-{2444,2832,0,1,6}-{2498,2845,0,1,3}-{2615,2957,0,1,4}-{2599,2960,0,1,2}-{2609,2966,0,1,0}-{2605,2967,0,1,5}-{2609,2963,0,1,2}-{2496,2857,0,1,4}-{2493,2849,0,1,4}-{2474,2860,0,1,1}-{2470,2865,0,1,4}-{2462,2874,0,1,1}-{2458,2853,0,1,4}-{2454,2876,0,1,5}-{2478,2884,0,1,6}-{2469,2886,0,1,5}-{2442,2876,0,1,1}-{2490,2903,0,1,1}-{2477,2928,0,1,4}-{2492,2956,0,1,1}-{2502,2956,0,1,4}-{2499,2965,0,1,0}-{2508,2968,0,1,4}-{2500,2974,0,1,0}-{3175,3948,0,1,1}-{3200,3931,0,1,1}-{3220,3921,0,1,7}-{2478,3272,0,1,1}-{2465,3276,0,1,1}-{2468,3277,0,1,4}-{2466,3271,0,1,5}-{2468,3277,0,1,0}" - }, - { - "npc_id": "96", - "loc_data": "{2842,3450,0,0,0}-{2872,3437,0,0,0}-{2873,3449,0,0,0}-{2868,3456,0,0,0}-{2860,3490,0,0,0}-{2861,3492,0,0,0}-{2855,3511,0,0,0}-{2857,3508,0,0,0}-{3004,3920,0,1,7}-{3007,3925,0,1,3}-{3005,3927,0,1,3}-{3004,3924,0,1,3}-{2988,3918,0,1,2}-{2992,3921,0,1,2}-{2990,3923,0,1,3}-{2988,3925,0,1,6}-{2987,3921,0,1,6}" - }, - { - "npc_id": "97", - "loc_data": "{2847,3485,0,0,0}-{2847,3489,0,0,0}-{2833,3511,0,0,0}-{2831,3506,0,0,0}-{2834,3514,0,0,0}-{3003,3921,0,1,6}-{3009,3920,0,1,3}-{3001,3923,0,1,1}-{3001,3927,0,1,4}-{2993,3919,0,1,4}-{2994,3924,0,1,1}-{2988,3918,0,1,4}" - }, - { - "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}" - }, - { - "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}" - }, - { - "npc_id": "102", - "loc_data": "{2622,3390,0,1,7}-{2622,3390,0,1,7}" - }, - { - "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}-{3277,3659,0,1,1}-{3280,3658,0,1,1}-{3277,3661,0,1,3}-{3276,3657,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}-{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}" - }, - { - "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": "{2632,3280,0,1,1}-{2633,3274,0,1,3}-{3094,3601,0,1,6}-{3109,3600,0,1,2}-{3091,3604,0,1,0}-{2497,3164,0,1,2}-{3023,4508,0,1,7}-{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}" - }, - { - "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}-{2955,3901,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}" - }, - { - "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": "{2583,2967,0,1,0}-{2572,3028,0,1,2}-{2550,3046,0,1,4}-{2572,3034,0,1,3}-{2576,3026,0,1,4}-{2534,2975,0,1,1}-{2518,2974,0,1,1}-{2573,2987,0,1,1}-{2592,2967,0,1,3}-{2589,2961,0,0,2}-{2575,2989,0,1,0}-{2574,2992,0,1,4}-{2521,2974,0,1,4}-{2500,3092,0,1,6}-{2524,3375,0,1,3}-{2524,3374,0,1,3}-{2529,3374,0,1,3}-{2532,3375,0,1,1}-{2532,3373,0,1,1}-{2527,3376,0,1,6}-{2525,3373,0,1,7}-{2525,3373,0,1,5}-{2511,3086,0,1,1}-{2511,3084,0,1,3}-{2503,3114,0,1,6}-{2509,3120,0,1,4}-{2506,3107,0,1,4}-{2500,3112,0,1,4}-{2521,3045,0,1,6}-{2517,3043,0,1,5}-{2500,3095,0,1,3}-{2497,3090,0,1,4}-{2502,3099,0,1,4}-{2504,3097,0,1,4}-{2503,3134,0,1,3}-{2554,3196,0,1,7}-{2519,3039,0,1,4}-{2722,9701,0,1,1}-{2720,9715,0,1,1}-{2721,9669,0,1,1}-{2722,9707,0,1,1}-{2723,9716,0,1,1}-{2717,9669,0,1,1}" - }, - { - "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}" - }, - { - "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": "{3248,3794,0,1,1}-{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}-{3244,3797,0,1,3}-{3245,3790,0,1,4}-{3239,3781,0,1,5}-{3253,3791,0,1,1}-{3245,3802,0,1,1}-{3250,3808,0,1,3}-{3246,3800,0,1,6}-{3246,3795,0,1,2}" - }, - { - "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}" - }, - { - "npc_id": "123", - "loc_data": "{3023,3801,0,1,4}-{3021,3805,0,1,0}-{3026,3805,0,1,1}-{3029,3801,0,1,3}-{3020,3808,0,1,3}-{3027,3810,0,1,6}-{3032,3805,0,1,1}-{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}" - }, - { - "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": "{2969,3916,0,1,0}-{2963,3928,0,1,0}-{2973,3941,0,1,0}-{2951,3908,0,1,0}-{2950,3912,0,1,0}-{2952,3915,0,1,0}-{2948,3917,0,1,0}-{2951,3923,0,1,0}-{2956,3924,0,1,0}-{2957,3931,0,1,0}-{2950,3939,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,0,0}-{3188,3960,0,0,0}-{3190,3962,0,0,0}-{3187,3959,0,0,0}-{3192,3961,0,0,0}-{3187,3960,0,0,0}-{3193,3959,0,0,0}-{3187,3961,0,0,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}-{3193,3958,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,3660,0,1,7}-{2709,3672,0,1,7}-{3079,3618,0,0,7}-{3070,3629,0,0,3}-{3073,3634,0,0,6}-{3088,3636,0,0,3}-{3092,3637,0,0,2}-{3073,3636,0,0,0}-{3070,3657,0,0,5}-{3109,3634,0,0,2}-{3114,3628,0,0,1}-{3107,3626,0,0,2}-{3118,3639,0,0,5}-{3119,3627,0,0,3}-{3119,3604,0,0,1}-{3136,3612,0,0,5}-{3124,3586,0,0,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": "{3043,9779,0,1,4}-{3047,9769,0,1,1}-{2847,3282,0,1,6}-{2846,3293,0,1,6}-{2850,3301,0,1,4}-{2852,3308,0,1,4}-{2861,3297,0,1,1}-{2863,3283,0,1,5}" - }, - { - "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": "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}" - }, - { - "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}-{3039,3855,0,1,4}-{3031,3852,0,1,1}-{3032,3849,0,1,3}-{3023,3849,0,1,6}-{3022,3847,0,1,3}-{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": "{3041,3958,0,0,0}-{3043,3958,0,0,0}-{3039,3958,0,0,0}-{3039,3950,0,0,0}-{3041,3958,0,0,0}-{3041,3954,0,0,0}-{3038,3957,0,0,0}-{3041,3954,0,0,0}-{3038,3955,0,0,0}-{3690,2966,0,1,4}" - }, - { - "npc_id": "183", - "loc_data": "{3043,3958,0,0,0}-{3043,3950,0,0,0}-{3042,3950,0,0,0}-{3039,3951,0,0,0}-{3044,3949,0,0,0}-{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}" - }, - { - "npc_id": "186", - "loc_data": "{3158,3571,0,0,7}-{3170,3575,0,0,0}-{3172,3578,0,0,5}-{3175,3564,0,0,4}-{3176,3570,0,0,2}-{3180,3565,0,0,5}-{3189,3565,0,0,7}-{3189,3580,0,0,0}-{3192,3568,0,0,7}-{3159,3563,0,0,1}-{3163,3572,0,0,6}-{3173,3574,0,0,3}-{3179,3561,0,0,2}" - }, - { - "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}" - }, - { - "npc_id": "188", - "loc_data": "{2933,3516,0,1,3}-{2935,3515,0,1,3}-{2932,3514,0,1,2}-{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": "{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": "{3027,3612,0,1,6}-{3021,3611,0,1,1}-{3036,3613,0,1,4}-{3024,3606,0,1,1}-{3023,3634,0,1,3}-{3026,3637,0,1,5}-{3031,3637,0,1,2}-{3035,3637,0,1,6}-{3034,3632,0,1,6}-{3035,3629,0,1,6}-{3035,3625,0,1,5}-{3029,3626,0,1,3}-{3032,3626,0,1,4}-{3022,3626,0,1,1}-{3023,3623,0,1,6}" - }, - { - "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": "{3042,3708,0,0,6}-{3037,3702,0,0,5}" - }, - { - "npc_id": "196", - "loc_data": "{3038,3670,0,1,1}-{3034,3674,0,1,2}" - }, - { - "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": "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": "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": "{2616,3172,0,1,1}-{2583,3142,0,1,6}" - }, - { - "npc_id": "254", - "loc_data": "{2565,3150,0,1,6}-{2564,3146,0,1,5}-{2572,3143,0,1,4}" - }, - { - "npc_id": "255", - "loc_data": "{2601,3195,0,1,3}-{2615,3188,0,1,6}-{2606,3186,0,1,3}" - }, - { - "npc_id": "256", - "loc_data": "{2568,3144,0,1,3}-{2606,3180,0,1,5}-{2600,3184,0,1,4}-{2598,3185,0,1,6}-{2593,3175,0,1,2}-{2594,3182,0,1,3}-{2594,3188,0,1,3}-{2593,3193,0,1,3}-{2592,3179,0,1,1}-{2605,3171,0,1,3}-{2581,3170,0,1,6}-{2580,3166,0,1,6}-{2577,3163,0,1,1}-{2585,3150,0,1,4}" - }, - { - "npc_id": "259", - "loc_data": "{2567,3140,0,0,0}" - }, - { - "npc_id": "260", - "loc_data": "{2589,3142,0,0,0}" - }, - { - "npc_id": "261", - "loc_data": "{2587,3142,0,0,0}" - }, - { - "npc_id": "262", - "loc_data": "{2616,3162,0,0,0}-{2616,3161,0,0,0}-{2596,3142,0,0,0}" - }, - { - "npc_id": "263", - "loc_data": "{2597,3143,0,0,0}" - }, - { - "npc_id": "265", - "loc_data": "{2616,3167,0,0,0}" - }, - { - "npc_id": "267", - "loc_data": "{2601,3168,0,0,0}" - }, - { - "npc_id": "268", - "loc_data": "{2583,3171,0,0,0}" - }, - { - "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}-{2689,3598,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}-{3259,3204,0,0,3}-{3248,3161,0,0,4}-{3259,3206,0,0,3}-{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}-{3169,3266,0,0,3}-{3173,3274,0,0,3}-{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,0,0}-{2536,3294,0,0,0}-{2538,3321,0,0,0}" - }, - { - "npc_id": "348", - "loc_data": "{2501,3315,0,0,0}-{2513,3325,0,0,0}-{2526,3279,0,0,0}-{2528,3297,0,0,0}-{2535,3288,0,0,0}-{2548,3287,0,0,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,0,0}" - }, - { - "npc_id": "356", - "loc_data": "{2523,3307,0,0,0}" - }, - { - "npc_id": "357", - "loc_data": "{2518,3309,0,0,0}-{2526,3303,0,0,0}-{2543,3309,0,0,0}-{2550,3319,0,0,0}-{2552,3319,0,0,0}" - }, - { - "npc_id": "358", - "loc_data": "{2529,3285,0,0,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,0,0}-{2545,3324,0,0,0}-{2545,3327,0,0,0}-{2552,3326,0,0,0}-{2553,3322,0,0,0}-{2553,3324,0,0,0}" - }, - { - "npc_id": "370", - "loc_data": "{2551,3324,1,0,0}-{2551,3327,1,0,0}" - }, - { - "npc_id": "371", - "loc_data": "{2547,3326,0,0,0}-{2549,3322,0,0,0}-{2550,3326,0,0,0}-{2552,3323,0,0,0}" - }, - { - "npc_id": "372", - "loc_data": "{2559,3303,0,0,6}-{2561,3303,0,0,6}-{2561,3305,0,0,4}-{2559,3305,0,0,1}" - }, - { - "npc_id": "373", - "loc_data": "{2516,3274,0,0,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}-{2880,3248,0,1,0}-{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}-{2760,3402,0,1,4}-{2759,3400,0,1,1}-{2756,3403,0,1,1}-{2759,3400,0,1,3}-{2755,3402,0,1,1}" - }, - { - "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": "{3203,3397,0,1,3}" - }, - { - "npc_id": "552", - "loc_data": "{3205,3399,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": "619", - "loc_data": "{3353,3333,0,1,3}" - }, - { - "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": "670", - "loc_data": "{2465,3496,0,1,6}" - }, - { - "npc_id": "674", - "loc_data": "{3001,3041,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}-{2976,3176,0,1,6}-{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": "711", - "loc_data": "{2536,3314,1,0,0}" - }, - { - "npc_id": "713", - "loc_data": "{2526,3319,0,0,0}" - }, - { - "npc_id": "716", - "loc_data": "{2540,3286,0,0,0}" - }, - { - "npc_id": "717", - "loc_data": "{2513,3294,0,0,0}-{2518,3320,0,0,0}-{2530,3274,0,0,0}-{2535,3296,0,0,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,0,0}" - }, - { - "npc_id": "725", - "loc_data": "{2540,3305,0,0,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": "{2450,3056,0,0,0}" - }, - { - "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": "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": "853", - "loc_data": "{2506,3115,0,1,3}" - }, - { - "npc_id": "854", - "loc_data": "{2511,3085,0,1,4}" - }, - { - "npc_id": "858", - "loc_data": "{2550,3032,0,0,0}-{2552,3033,0,0,0}-{2550,3030,0,0,0}-{2550,3032,0,0,0}" - }, - { - "npc_id": "860", - "loc_data": "{2503,3013,0,0,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": "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": "{3101,3940,0,0,0}-{3109,3931,0,0,0}-{3109,3939,0,0,0}-{3097,3931,0,1,4}-{3098,3938,0,1,6}-{3113,3938,0,1,7}" - }, - { - "npc_id": "913", - "loc_data": "{3105,3931,0,0,0}-{3101,3929,0,0,1}-{3111,3939,0,1,6}-{3109,3926,0,1,1}" - }, - { - "npc_id": "914", - "loc_data": "{3099,3936,0,0,7}-{3104,3929,0,0,0}-{3110,3936,0,1,2}-{3096,3932,0,1,4}-{3113,3928,0,1,1}" - }, - { - "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": "{2974,3620,0,1,3}-{2975,3609,0,1,3}-{2983,3614,0,1,3}-{3340,3678,0,1,1}-{3339,3695,0,1,6}-{3344,3705,0,1,3}-{3307,5462,0,1,0}-{3121,10085,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": "{3082,3248,0,1,4}" - }, - { - "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": "{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}" - }, - { - "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": "1028", - "loc_data": "{2778,3436,0,0,0}" - }, - { - "npc_id": "1029", - "loc_data": "{2920,3499,0,0,0}-{2784,3480,0,0,0}" - }, - { - "npc_id": "1030", - "loc_data": "{2768,3470,0,1,0}-{2843,3155,0,1,0}" - }, - { - "npc_id": "1032", - "loc_data": "{2865,3504,0,0,0}" - }, - { - "npc_id": "1033", - "loc_data": "{2606,2967,0,0,0}" - }, - { - "npc_id": "1035", - "loc_data": "{2599,2954,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,5}-{2894,3537,0,0,5}-{2893,3537,0,0,5}-{2892,3537,0,0,5}-{2891,3537,0,0,3}-{2891,3538,0,0,3}-{2892,3538,0,0,3}-{2893,3538,0,0,3}-{2894,3538,0,0,3}-{2895,3538,0,0,3}" - }, - { - "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}" - }, - { - "npc_id": "1074", - "loc_data": "{2897,3549,0,0,4}" - }, - { - "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,0,0}" - }, - { - "npc_id": "1085", - "loc_data": "{2921,3558,0,1,6}" - }, - { - "npc_id": "1086", - "loc_data": "{2965,3262,0,1,4}" - }, - { - "npc_id": "1087", - "loc_data": "{2911,3540,0,0,7}" - }, - { - "npc_id": "1088", - "loc_data": "{2918,3545,0,0,0}" - }, - { - "npc_id": "1089", - "loc_data": "{2919,3529,0,0,0}" - }, - { - "npc_id": "1090", - "loc_data": "{2931,3566,0,1,1}" - }, - { - "npc_id": "1093", - "loc_data": "{2817,3560,0,1,3}" - }, - { - "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}-{2857,3589,0,0,0}" - }, - { - "npc_id": "1100", - "loc_data": "{2859,3589,0,0,0}-{2859,3589,0,0,0}" - }, - { - "npc_id": "1101", - "loc_data": "{2851,3598,0,0,0}-{2854,3600,0,0,0}" - }, - { - "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}-{2925,3626,0,1,0}-{2843,3682,0,1,1}" - }, - { - "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}" - }, - { - "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}" - }, - { - "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}-{2920,3627,0,1,0}-{2923,10029,0,1,7}-{2851,3667,0,1,3}" - }, - { - "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}" - }, - { - "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}" - }, - { - "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": "{2831,10083,0,0,0}" - }, - { - "npc_id": "1114", - "loc_data": "{2826,10078,0,0,0}" - }, - { - "npc_id": "1115", - "loc_data": "{2832,10088,2,0,0}" - }, - { - "npc_id": "1116", - "loc_data": "{2824,10073,2,0,0}" - }, - { - "npc_id": "1117", - "loc_data": "{2829,10101,2,0,0}" - }, - { - "npc_id": "1118", - "loc_data": "{2920,3613,0,0,0}" - }, - { - "npc_id": "1119", - "loc_data": "{2930,3614,0,0,0}" - }, - { - "npc_id": "1120", - "loc_data": "{2927,3608,0,0,0}" - }, - { - "npc_id": "1121", - "loc_data": "{2930,3600,0,0,0}" - }, - { - "npc_id": "1122", - "loc_data": "{2918,3604,0,0,0}" - }, - { - "npc_id": "1123", - "loc_data": "{2914,3603,0,0,0}" - }, - { - "npc_id": "1124", - "loc_data": "{2925,3600,0,0,0}" - }, - { - "npc_id": "1125", - "loc_data": "{2916,3610,0,0,0}" - }, - { - "npc_id": "1128", - "loc_data": "{2833,10080,0,0,0}" - }, - { - "npc_id": "1129", - "loc_data": "{2833,10084,0,0,0}" - }, - { - "npc_id": "1131", - "loc_data": "{2905,3694,0,0,1}" - }, - { - "npc_id": "1132", - "loc_data": "{2901,3697,0,0,1}" - }, - { - "npc_id": "1133", - "loc_data": "{2895,3699,0,0,1}" - }, - { - "npc_id": "1134", - "loc_data": "{2891,3699,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}-{2909,3639,0,1,3}-{2923,10031,0,1,3}" - }, - { - "npc_id": "1140", - "loc_data": "{2856,10054,1,0,0}-{2860,10057,1,0,0}-{2898,3688,0,0,0}-{2819,3586,0,1,4}" - }, - { - "npc_id": "1141", - "loc_data": "{2857,10059,1,0,0}-{2857,10057,1,0,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": "{3228,3501,0,1,5}-{3023,4504,0,1,4}-{2216,3174,0,1,4}-{2294,3155,0,1,3}" - }, - { - "npc_id": "1196", - "loc_data": "{3025,4506,0,1,5}-{3023,4504,0,1,4}-{2212,3174,0,1,3}-{2294,3152,0,1,1}" - }, - { - "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": "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": "{3292,5455,0,1,3}-{3295,5452,0,1,0}-{3290,5448,0,1,4}" - }, - { - "npc_id": "1220", - "loc_data": "{3600,3491,0,1,2}-{3598,3498,0,1,4}-{3600,3496,0,1,0}-{3600,3496,0,1,6}-{3602,3494,0,1,1}-{3597,3490,0,1,6}-{3595,3497,0,1,4}-{3605,3491,0,1,4}-{3604,3495,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}-{2525,3763,0,0,7}-{2508,3762,0,0,0}-{2510,3755,0,0,2}-{2523,3756,0,0,1}-{2532,3752,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}-{2537,3761,0,0,7}-{2537,3761,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}-{2527,3755,0,0,1}-{2532,3758,0,0,6}-{2532,3751,0,0,3}-{2530,3738,0,0,2}-{2531,3729,0,0,0}-{2523,3723,0,0,1}-{2520,3722,0,0,3}-{2515,3720,0,0,3}-{2505,3724,0,0,4}-{2505,3729,0,0,6}-{2535,3717,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": "{3073,3640,0,0,0}" - }, - { - "npc_id": "1334", - "loc_data": "{2510,3644,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": "1359", - "loc_data": "{2612,3875,1,1,6}" - }, - { - "npc_id": "1360", - "loc_data": "{2620,3896,0,0,3}" - }, - { - "npc_id": "1369", - "loc_data": "{2165,3276,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": "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": "1560", - "loc_data": "{3150,5454,0,1,4}-{3144,5446,0,1,7}-{3166,5446,0,1,3}-{3158,5451,0,1,7}" - }, - { - "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}" - }, - { - "npc_id": "1562", - "loc_data": "{3147,5448,0,1,4}" - }, - { - "npc_id": "1563", - "loc_data": "{3152,5453,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}-{2352,9755,0,1,1}-{2343,9775,0,1,1}-{2340,9777,0,1,1}-{2334,9775,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}-{2352,9760,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}-{2350,9776,0,1,1}-{2337,9773,0,1,1}" - }, - { - "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}-{2355,9762,0,1,1}-{2354,9768,0,1,1}-{2351,9770,0,1,1}-{2354,9773,0,1,1}" - }, - { - "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}-{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}-{2340,9733,0,1,3}-{2348,9733,0,1,3}-{2348,9743,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}-{2891,3919,0,1,0}-{2901,3914,0,1,0}-{2911,3914,0,1,0}-{2920,3919,0,1,0}-{2929,3919,0,1,0}-{2929,3935,0,1,0}-{2929,3944,0,1,0}-{2702,9443,0,0,3}-{2700,9428,0,0,3}-{2705,9419,0,0,7}-{2311,9744,0,1,3}-{2313,9733,0,1,3}-{2317,9735,0,1,3}-{2329,9736,0,1,3}" - }, - { - "npc_id": "1592", - "loc_data": "{2893,3929,0,1,0}-{2893,3942,0,1,0}-{2897,3951,0,1,0}-{2903,3954,0,1,0}-{2910,3954,0,1,0}-{2920,3952,0,1,0}-{2929,3952,0,1,0}-{2728,9426,0,0,4}-{2714,9432,0,0,0}-{2707,9450,0,0,6}-{2724,9451,0,0,6}-{2311,9778,0,1,3}-{2313,9772,0,1,2}-{2318,9776,0,1,3}-{2318,9764,0,1,3}-{2318,9760,0,1,3}-{2313,9752,0,1,3}" - }, - { - "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}-{2373,9778,0,1,1}-{2372,9768,0,1,1}-{2371,9774,0,1,1}-{2375,9770,0,1,1}-{2383,9777,0,1,1}-{2387,9774,0,1,1}-{2384,9772,0,1,1}-{2387,9770,0,1,1}-{2392,9780,0,1,1}-{2396,9775,0,1,1}-{2399,9777,0,1,1}" - }, - { - "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}-{2428,9745,0,1,1}-{2428,9742,0,1,1}-{2428,9735,0,1,1}-{2425,9737,0,1,1}-{2422,9735,0,1,1}-{2419,9738,0,1,1}" - }, - { - "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}-{2426,9751,0,1,1}-{2427,9754,0,1,1}-{2425,9757,0,1,1}-{2422,9756,0,1,1}-{2426,9753,0,1,1}-{2420,9755,0,1,1}-{2417,9755,0,1,1}-{2406,9763,0,1,1}-{2404,9765,0,1,1}-{2407,9766,0,1,1}-{2410,9764,0,1,1}-{2416,9765,0,1,1}-{2417,9763,0,1,1}-{2420,9764,0,1,1}-{2418,9765,0,1,1}" - }, - { - "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}-{2402,9739,0,1,1}-{2402,9742,0,1,1}-{2402,9751,0,1,1}-{2406,9753,0,1,1}-{2408,9750,0,1,1}-{2404,9748,0,1,1}-{2407,9748,0,1,1}-{2416,9748,0,1,1}-{2415,9745,0,1,1}-{2418,9745,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}-{2425,9774,0,1,1}-{2425,9776,0,1,1}-{2423,9779,0,1,1}-{2419,9785,0,1,1}-{2415,9785,0,1,1}-{2416,9788,0,1,1}-{2430,9788,0,1,1}-{2434,9789,0,1,1}-{2436,9794,0,1,1}" - }, - { - "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": "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,0,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}" - }, - { - "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}" - }, - { - "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}" - }, - { - "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}" - }, - { - "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": "{2765,3652,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": "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": "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": "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": "{2392,2984,0,0,0}" - }, - { - "npc_id": "2039", - "loc_data": "{2392,2984,0,0,0}" - }, - { - "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": "{2455,3048,0,0,0}" - }, - { - "npc_id": "2043", - "loc_data": "{2451,3030,0,0,0}-{2446,3040,0,0,0}" - }, - { - "npc_id": "2050", - "loc_data": "{2460,3046,0,0,0}-{2465,3052,0,0,0}" - }, - { - "npc_id": "2051", - "loc_data": "{2463,3053,0,0,0}" - }, - { - "npc_id": "2052", - "loc_data": "{2468,3045,0,0,0}" - }, - { - "npc_id": "2053", - "loc_data": "{2467,3051,0,0,0}" - }, - { - "npc_id": "2054", - "loc_data": "{2466,3046,0,0,0}" - }, - { - "npc_id": "2055", - "loc_data": "{2471,3056,0,0,0}" - }, - { - "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": "2180", - "loc_data": "{2997,9837,0,1,3}" - }, - { - "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}" - }, - { - "npc_id": "2279", - "loc_data": "{3244,3247,0,1,3}" - }, - { - "npc_id": "2280", - "loc_data": "{3245,3244,0,1,4}" - }, - { - "npc_id": "2281", - "loc_data": "{3246,3245,0,1,4}" - }, - { - "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": "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": "2424", - "loc_data": "{2871,10187,0,1,0}" - }, - { - "npc_id": "1843", - "loc_data": "{2841,10129,0,1,0}" - }, - { - "npc_id": "1844", - "loc_data": "{2840,10127,0,1,0}" - }, - { - "npc_id": "2205", - "loc_data": "{2839,10128,0,1,0}" - }, - { - "npc_id": "2154", - "loc_data": "{2839,10128,0,1,0}" - }, - { - "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": "2482", - "loc_data": "{3057,3902,0,1,3}" - }, - { - "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": "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": "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": "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": "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": "3216", - "loc_data": "{2534,3273,0,0,0}-{2539,3273,0,0,0}" - }, - { - "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": "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": "{3265,3331,0,1,7}-{3146,3460,0,1,6}-{3115,3414,0,1,3}-{3195,3240,0,1,6}-{2565,9843,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}-{3194,3240,0,1,0}-{3178,3257,0,1,1}-{3170,3255,0,1,7}-{3192,3243,0,1,1}-{3175,3249,0,1,1}-{3170,3242,0,1,2}-{3193,3243,0,1,7}-{3189,3258,0,1,1}-{3172,3245,0,1,4}-{3172,3238,0,1,6}-{3187,3246,0,1,4}-{3186,3243,0,1,7}-{3222,3238,0,1,1}-{3144,3235,0,1,6}-{3147,3224,0,1,1}-{3131,3243,0,1,7}-{3142,3242,0,1,4}-{3157,3228,0,1,6}-{3151,3217,0,1,6}-{3152,3246,0,1,1}-{3145,3266,0,1,4}-{3148,3240,0,1,1}-{3135,3258,0,1,2}-{3140,3260,0,1,3}" - }, - { - "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}" - }, - { - "npc_id": "3384", - "loc_data": "{3211,3225,0,0,0}" - }, - { - "npc_id": "3402", - "loc_data": "{2560,2842,0,0,0}" - }, - { - "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": "{2540,3020,0,0,0}-{2555,2953,0,0,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": "{2587,9741,0,1,4}-{2585,9739,0,1,7}-{2582,9736,0,1,3}-{2589,9734,0,1,4}-{2582,9743,0,1,3}-{2576,9746,0,1,7}-{2579,9741,0,1,6}" - }, - { - "npc_id": "3638", - "loc_data": "{3309,3453,0,0,1}" - }, - { - "npc_id": "3671", - "loc_data": "{3084,3250,0,1,1}" - }, - { - "npc_id": "3673", - "loc_data": "{2925,3321,0,1,4}-{2924,3319,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": "{3281,3331,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": "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": "3823", - "loc_data": "{2343,3668,0,0,0}" - }, - { - "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": "4286", - "loc_data": "{2866,3543,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": "{3024,10152,0,0,0}-{3020,10148,0,0,0}-{3023,10145,0,0,0}-{3018,10140,0,0,0}-{3033,10139,0,0,0}-{3028,10137,0,0,0}-{2361,5241,0,0,1}-{2358,5240,0,0,1}-{2362,5239,0,0,1}-{2364,5240,0,0,1}-{2364,5242,0,0,1}-{2372,9743,0,1,1}-{2373,9745,0,1,1}-{2374,9742,0,1,1}-{2377,9750,0,1,1}-{2378,9752,0,1,1}-{2377,9754,0,1,1}" - }, - { - "npc_id": "4382", - "loc_data": "{3162,3716,0,0,0}-{3175,3717,0,0,0}-{3161,3727,0,0,0}-{2304,5160,0,0,2}-{2324,5210,0,0,2}-{2322,5201,0,0,7}-{2324,5210,0,0,2}-{2322,5201,0,0,7}-{2327,5205,0,0,7}-{2328,5200,0,0,7}-{2326,5198,0,0,7}-{2331,5198,0,0,4}-{2333,5201,0,0,4}-{2333,5199,0,0,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": "{2312,5188,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,0,2}-{2312,5225,0,0,2}-{2314,5240,0,0,2}-{2310,5241,0,0,2}-{2308,5246,0,0,2}-{2309,5245,0,0,2}-{2315,5237,0,0,2}" - }, - { - "npc_id": "4389", - "loc_data": "{2013,5238,0,1,0}" - }, - { - "npc_id": "4390", - "loc_data": "{1984,5160,0,1,2}-{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}" - }, - { - "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}" - }, - { - "npc_id": "4392", - "loc_data": "{2033,5233,0,1,4}-{2033,5233,0,1,7}-{2026,5236,0,1,1}-{1984,5160,0,1,7}" - }, - { - "npc_id": "4393", - "loc_data": "{2045,5222,0,1,4}-{1944,5152,0,1,2}-{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}" - }, - { - "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}" - }, - { - "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}-{1944,5152,0,1,2}-{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}" - }, - { - "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}-{1808,5152,0,1,2}-{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": "{1808,5152,0,1,2}-{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}-{1808,5152,0,1,2}-{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": "4568", - "loc_data": "{3364,3338,0,1,6}" - }, - { - "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": "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": "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": "{3192,3832,0,1,6}-{3211,3813,0,1,6}" - }, - { - "npc_id": "4671", - "loc_data": "{3189,3836,0,1,6}-{3181,3815,0,1,6}" - }, - { - "npc_id": "4672", - "loc_data": "{3212,3847,0,1,6}-{3221,3827,0,1,6}" - }, - { - "npc_id": "4673", - "loc_data": "{2829,9827,0,1,0}" - }, - { - "npc_id": "4675", - "loc_data": "{3048,10266,0,1,4}" - }, - { - "npc_id": "4676", - "loc_data": "{3054,10269,0,1,4}" - }, - { - "npc_id": "4677", - "loc_data": "{3335,3687,0,1,4}-{3338,3700,0,1,2}-{3316,5448,0,1,2}-{3107,10065,0,1,0}-{3299,5545,0,1,6}" - }, - { - "npc_id": "4678", - "loc_data": "{3335,3701,0,1,6}-{3350,3687,0,1,5}-{3305,5454,0,1,6}-{3110,10076,0,1,0}" - }, - { - "npc_id": "4679", - "loc_data": "{3333,3694,0,1,6}-{3341,3685,0,1,4}-{3118,10068,0,1,0}-{3312,5558,0,1,1}" - }, - { - "npc_id": "4680", - "loc_data": "{3349,3696,0,1,5}-{3120,10080,0,1,0}" - }, - { - "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": "{2957,3895,0,1,4}-{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}-{2957,3898,0,1,6}-{2946,3898,0,1,7}-{3213,5548,0,1,4}" - }, - { - "npc_id": "4688", - "loc_data": "{2700,3212,0,1,6}" - }, - { - "npc_id": "4689", - "loc_data": "{2902,9736,0,1,0}" - }, - { - "npc_id": "4690", - "loc_data": "{3256,3624,0,1,5}-{3105,3876,0,1,2}-{2912,9731,0,1,0}" - }, - { - "npc_id": "4693", - "loc_data": "{2912,9741,0,1,0}-{2906,9736,0,1,0}" - }, - { - "npc_id": "4694", - "loc_data": "{3098,3870,0,1,1}-{3020,3854,0,1,7}-{2843,9558,0,1,1}-{2840,9605,0,1,4}-{3053,10124,0,1,0}-{3056,10140,0,1,0}" - }, - { - "npc_id": "4695", - "loc_data": "{3097,3696,0,1,4}-{3017,3851,0,1,2}-{2839,9565,0,1,3}-{2837,9601,0,1,4}-{2840,3279,0,1,6}-{3048,10134,0,1,0}-{3061,10138,0,1,0}" - }, - { - "npc_id": "4696", - "loc_data": "{3118,3695,0,1,6}-{2931,9808,0,1,0}-{2930,9799,0,1,0}-{3010,3851,0,1,1}-{2839,9560,0,1,4}-{2846,9613,0,1,6}-{3062,10131,0,1,0}" - }, - { - "npc_id": "4697", - "loc_data": "{3125,3706,0,1,5}-{3331,3853,0,1,2}-{3016,3841,0,1,4}-{2845,9557,0,1,2}-{2839,9624,0,1,6}-{2825,3277,0,1,3}-{3065,10125,0,1,0}" - }, - { - "npc_id": "4698", - "loc_data": "{3139,3712,0,1,5}-{3306,3890,0,1,6}-{3085,10093,0,1,0}-{3090,10084,0,1,0}-{2861,9752,0,1,4}-{2363,9776,0,1,1}-{2361,9755,0,1,1}-{2365,9754,0,1,1}-{2362,9751,0,1,1}" - }, - { - "npc_id": "4699", - "loc_data": "{3035,10245,0,1,4}-{3159,3707,0,1,3}-{3301,3871,0,1,5}-{3097,10092,0,1,0}-{3082,10083,0,1,0}-{2629,9477,2,0,0}-{2640,9502,2,0,1}-{2359,9741,0,1,1}" - }, - { - "npc_id": "4700", - "loc_data": "{3030,10259,0,1,3}-{3144,3701,0,1,4}-{3279,3876,0,1,4}-{3094,10092,0,1,0}-{3089,10081,0,1,0}-{2633,9488,2,0,6}-{2862,9750,0,1,7}-{2358,9737,0,1,1}-{2366,9763,0,1,1}" - }, - { - "npc_id": "4701", - "loc_data": "{3028,10250,0,1,4}-{3149,3690,0,1,4}-{3286,3901,0,1,3}-{3089,10089,0,1,0}-{3085,10081,0,1,0}-{2628,9489,2,0,0}-{2631,9504,2,0,5}-{2361,9755,0,1,1}-{2363,9743,0,1,1}-{2363,9772,0,1,1}" - }, - { - "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": "{3242,5555,0,1,3}-{3206,5493,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": "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": "{3225,3551,0,1,1}-{3248,3524,0,1,1}" - }, - { - "npc_id": "4927", - "loc_data": "{3227,3529,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": "4944", - "loc_data": "{2042,5213,0,1,5}-{2020,5236,0,1,2}-{1944,5152,0,1,2}-{2041,5216,0,1,0}" - }, - { - "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": "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,0}-{2325,3483,0,1,0}-{2315,3482,0,1,0}-{2336,3491,0,1,0}" - }, - { - "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}" - }, - { - "npc_id": "5157", - "loc_data": "{3205,3262,0,1,1}" - }, - { - "npc_id": "5160", - "loc_data": "{3205,3270,0,1,4}-{3249,3345,0,1,5}" - }, - { - "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": "5168", - "loc_data": "{3207,3260,0,1,3}-{3197,3268,0,1,1}" - }, - { - "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": "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": "{3125,3627,0,1,6}-{3232,3698,0,1,7}" - }, - { - "npc_id": "5326", - "loc_data": "{3229,3683,0,1,0}" - }, - { - "npc_id": "5327", - "loc_data": "{3224,3671,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}" - }, - { - "npc_id": "5333", - "loc_data": "{2885,9815,0,1,0}-{3100,3560,0,1,4}-{3108,3545,0,1,2}-{2860,9571,0,1,7}" - }, - { - "npc_id": "5334", - "loc_data": "{2884,9817,0,1,0}-{3108,3533,0,1,1}-{2860,9569,0,1,6}" - }, - { - "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}-{2995,3938,0,1,4}" - }, - { - "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}" - }, - { - "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}" - }, - { - "npc_id": "5341", - "loc_data": "{2840,9633,0,1,1}-{2844,9634,0,1,6}-{2842,9637,0,1,3}" - }, - { - "npc_id": "5345", - "loc_data": "{2501,3294,0,1,3}" - }, - { - "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}-{2386,9745,0,1,1}-{2385,9748,0,1,1}-{2389,9746,0,1,1}-{2390,9751,0,1,1}-{2306,9760,0,1,1}-{2395,9757,0,1,1}-{2397,9756,0,1,1}-{2399,9758,0,1,1}-{2385,9762,0,1,1}-{2382,9759,0,1,1}-{2385,9757,0,1,1}-{2388,9759,0,1,1}-{2393,9768,0,1,1}-{2394,9741,0,1,1}" - }, - { - "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": "{2312,5189,0,1,7}" - }, - { - "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}" - }, - { - "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}-{1944,5152,0,1,2}-{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}" - }, - { - "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": "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": "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": "5956", - "loc_data": "{2659,2664,0,1,3}" - }, - { - "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": "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,3,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}-{2925,5295,3,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,3,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,3,1,1}-{2925,5292,3,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,3,1,1}-{2921,5292,3,1,5}-{2924,5288,3,1,0}-{2925,5287,3,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,3,1,1}-{2923,5298,3,1,4}-{2924,5298,3,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,5284,2,1,0}-{2900,5287,2,1,0}-{2905,5302,2,1,1}-{2898,5307,2,1,6}-{2918,5292,3,1,3}-{2924,5286,3,1,0}-{2924,5292,3,1,1}-{2924,5297,3,1,3}-{2926,5285,3,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,3,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,3,1,6}-{2924,5295,3,1,2}-{2905,5297,0,1,6}-{2920,5291,1,1,5}-{2930,5290,0,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,0,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": "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": "6773", - "loc_data": "{3439,2808,0,0,0}" - }, - { - "npc_id": "6774", - "loc_data": "{3435,2808,0,0,0}" - }, - { - "npc_id": "6776", - "loc_data": "{3441,2810,0,0,0}-{3445,2813,0,0,0}" - }, - { - "npc_id": "6777", - "loc_data": "{3432,2810,0,0,0}" - }, - { - "npc_id": "6778", - "loc_data": "{3440,2806,0,0,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": "6786", - "loc_data": "{3409,2812,1,0,0}" - }, - { - "npc_id": "6787", - "loc_data": "{3408,2813,1,0,0}" - }, - { - "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": "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}-{2555,2826,0,1,5}-{2550,2821,0,1,1}" - }, - { - "npc_id": "7024", - "loc_data": "{2523,2823,0,0,0}-{2525,2821,0,0,0}-{2525,2820,0,0,0}" - }, - { - "npc_id": "7025", - "loc_data": "{2537,2820,0,0,0}-{2539,2819,0,0,0}-{2543,2817,0,0,0}" - }, - { - "npc_id": "7026", - "loc_data": "{2543,2822,0,0,0}-{2542,2818,0,0,0}-{2549,2822,0,0,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,0,0}-{2542,2832,0,0,0}-{2532,2836,0,0,0}" - }, - { - "npc_id": "7033", - "loc_data": "{2541,2837,0,0,0}-{2535,2832,0,0,0}-{2543,2832,0,0,0}" - }, - { - "npc_id": "7034", - "loc_data": "{2536,2839,0,0,0}-{2537,2840,0,0,0}-{2533,2836,0,0,0}" - }, - { - "npc_id": "7035", - "loc_data": "{2542,2839,0,0,0}-{2549,2835,0,0,0}-{2541,2839,0,0,0}" - }, - { - "npc_id": "7036", - "loc_data": "{2538,2841,0,0,0}-{2537,2836,0,0,0}-{2531,2836,0,0,0}" - }, - { - "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}-{2503,2825,0,1,4}-{2493,2828,0,1,3}-{2515,2823,0,1,6}-{2525,2827,0,1,4}-{2589,2808,0,1,2}-{2562,2825,0,1,7}-{2568,2820,0,1,4}-{2577,2821,0,1,4}-{2582,2832,0,1,6}-{2500,2818,0,1,1}-{2525,2827,0,1,7}-{2536,2826,0,1,2}-{2541,2819,0,1,4}-{2503,2822,0,1,3}" - }, - { - "npc_id": "7040", - "loc_data": "{2563,2862,0,0,0}-{2563,2856,0,0,0}" - }, - { - "npc_id": "7041", - "loc_data": "{2562,2863,0,0,0}-{2553,2861,0,0,0}" - }, - { - "npc_id": "7042", - "loc_data": "{2551,2863,0,0,0}-{2552,2859,0,0,0}" - }, - { - "npc_id": "7043", - "loc_data": "{2553,2862,0,0,0}-{2561,2861,0,0,0}" - }, - { - "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,0,0}-{2559,2838,0,0,0}" - }, - { - "npc_id": "7048", - "loc_data": "{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,0,0}-{2549,2854,0,0,0}" - }, - { - "npc_id": "7052", - "loc_data": "{2571,2855,0,0,0}" - }, - { - "npc_id": "7054", - "loc_data": "{2561,2850,0,0,0}" - }, - { - "npc_id": "7055", - "loc_data": "{2559,2851,0,0,0}-{2560,2852,0,0,0}" - }, - { - "npc_id": "7056", - "loc_data": "{2560,2851,0,0,0}-{2561,2852,0,0,0}" - }, - { - "npc_id": "7057", - "loc_data": "{2596,2845,0,0,0}" - }, - { - "npc_id": "7062", - "loc_data": "{2571,2840,0,0,0}-{2572,2840,0,0,0}" - }, - { - "npc_id": "7065", - "loc_data": "{2621,2857,1,0,0}-{2622,2857,0,0,0}" - }, - { - "npc_id": "7066", - "loc_data": "{2619,2856,1,0,0}-{2620,2857,0,0,0}" - }, - { - "npc_id": "7067", - "loc_data": "{2518,2842,0,0,0}" - }, - { - "npc_id": "7068", - "loc_data": "{2531,2850,0,0,0}" - }, - { - "npc_id": "7069", - "loc_data": "{2559,2858,0,0,0}" - }, - { - "npc_id": "7070", - "loc_data": "{2581,2866,0,0,0}" - }, - { - "npc_id": "7071", - "loc_data": "{2598,2864,0,0,0}" - }, - { - "npc_id": "7072", - "loc_data": "{2517,2840,0,0,0}" - }, - { - "npc_id": "7073", - "loc_data": "{2533,2848,0,0,0}" - }, - { - "npc_id": "7074", - "loc_data": "{2556,2858,0,0,0}" - }, - { - "npc_id": "7075", - "loc_data": "{2583,2865,0,0,0}" - }, - { - "npc_id": "7076", - "loc_data": "{2596,2865,0,0,0}" - }, - { - "npc_id": "7077", - "loc_data": "{2617,2856,1,0,0}-{2618,2857,0,0,0}" - }, - { - "npc_id": "7078", - "loc_data": "{2502,2838,0,0,0}-{2478,2884,0,0,0}-{2476,2895,0,0,0}-{2472,2870,0,0,0}-{2455,2873,0,0,0}-{2455,2857,0,0,0}-{2474,2833,0,0,0}-{2505,2842,0,0,0}-{2507,2843,0,0,0}-{2481,2833,0,0,0}-{2476,2865,0,0,0}-{2456,2856,0,0,0}-{2454,2879,0,0,0}-{2472,2884,0,0,0}-{2475,2894,0,0,0}" - }, - { - "npc_id": "7079", - "loc_data": "{2501,2851,0,0,0}-{2466,2878,0,0,0}-{2454,2847,0,0,0}-{2471,2857,0,0,0}-{2483,2840,0,0,0}-{2498,2854,0,0,0}-{2504,2851,0,0,0}-{2491,2838,0,0,0}-{2475,2852,0,0,0}-{2464,2873,0,0,0}-{2474,2903,0,0,0}-{2479,2906,0,0,0}" - }, - { - "npc_id": "7080", - "loc_data": "{2508,2853,0,0,0}-{2492,2893,0,0,0}-{2483,2876,0,0,0}-{2456,2868,0,0,0}-{2459,2836,0,0,0}-{2514,2851,0,0,0}-{2506,2852,0,0,0}-{2487,2877,0,0,0}-{2486,2860,0,0,0}-{2458,2866,0,0,0}-{2416,2856,0,0,0}-{2495,2884,0,0,0}" - }, - { - "npc_id": "7081", - "loc_data": "{2499,2860,0,0,0}-{2485,2892,0,0,0}-{2451,2870,0,0,0}-{2466,2864,0,0,0}-{2449,2826,0,0,0}-{2487,2842,0,0,0}-{2497,2863,0,0,0}-{2489,2843,0,0,0}-{2471,2860,0,0,0}-{2448,2870,0,0,0}-{2478,2888,0,0,0}-{2485,2922,0,0,0}" - }, - { - "npc_id": "7082", - "loc_data": "{2516,2858,0,0,0}-{2493,2903,0,0,0}-{2485,2869,0,0,0}-{2484,2888,0,0,0}-{2444,2861,0,0,0}-{2457,2863,0,0,0}-{2474,2837,0,0,0}-{2521,2862,0,0,0}-{2483,2865,0,0,0}-{2474,2844,0,0,0}-{2460,2859,0,0,0}-{2492,2901,0,0,0}-{2486,2889,0,0,0}" - }, - { - "npc_id": "7091", - "loc_data": "{2525,2852,0,0,0}-{2529,2844,0,0,0}" - }, - { - "npc_id": "7092", - "loc_data": "{2526,2854,0,0,0}-{2528,2850,0,0,0}" - }, - { - "npc_id": "7093", - "loc_data": "{2527,2853,0,0,0}-{2528,2849,0,0,0}" - }, - { - "npc_id": "7094", - "loc_data": "{2527,2851,0,0,0}-{2523,2850,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,0,0}-{2581,2845,0,0,0}" - }, - { - "npc_id": "7100", - "loc_data": "{2586,2840,0,0,0}-{2587,2841,0,0,0}" - }, - { - "npc_id": "7101", - "loc_data": "{2585,2842,0,0,0}" - }, - { - "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,0,0}-{2547,2856,0,0,0}" - }, - { - "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": "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": "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": "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": "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": "2160", - "loc_data": "{2925,10211,0,1,0}" - }, - { - "npc_id": "2191", - "loc_data": "{2925,10213,0,1,0}" - }, - { - "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": "2158", - "loc_data": "{2885,10207,0,1,0}" - }, - { - "npc_id": "2157", - "loc_data": "{2887,10212,0,1,0}" - }, - { - "npc_id": "2156", - "loc_data": "{2892,10212,0,1,0}" - }, - { - "npc_id": "2159", - "loc_data": "{2885,10196,0,1,0}" - }, - { - "npc_id": "2565", - "loc_data": "{1937,4972,0,1,0}" - }, - { - "npc_id": "2564", - "loc_data": "{1935,4964,0,0,4}" - }, - { - "npc_id": "2321", - "loc_data": "{2918,10194,0,0,3}" - }, - { - "npc_id": "2198", - "loc_data": "{2905,10213,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": "2130", - "loc_data": "{2892,10191,0,1,0}-{2919,10206,0,1,0}-{2923,10230,0,1,0}-{2865,10193,0,1,0}" - }, - { - "npc_id": "2131", - "loc_data": "{2893,10205,0,1,0}-{2895,10223,0,1,0}-{2871,10199,0,1,0}-{2890,10199,0,1,0}" - }, - { - "npc_id": "2132", - "loc_data": "{2913,10211,0,1,0}-{2901,10232,0,1,0}-{2823,10193,0,1,0}" - }, - { - "npc_id": "5880", - "loc_data": "{2888,10226,0,1,0}-{2920,10196,0,1,0}-{2844,10214,0,1,0}" - }, - { - "npc_id": "5881", - "loc_data": "{2847,10200,0,1,0}-{2863,10233,0,1,0}-{2920,10216,0,1,0}" - }, - { - "npc_id": "5882", - "loc_data": "{2916,10191,0,1,0}-{2841,10195,0,1,0}-{2831,10219,0,1,0}" - }, - { - "npc_id": "2138", - "loc_data": "{2890,10232,0,1,0}" - }, - { - "npc_id": "2178", - "loc_data": "{2916,10192,0,1,0}-{2839,10196,0,1,0}" - }, - { - "npc_id": "2187", - "loc_data": "{2913,10200,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": "2193", - "loc_data": "{2887,10223,0,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": "2171", - "loc_data": "{2930,10184,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": "2192", - "loc_data": "{2933,10219,0,1,0}" - }, - { - "npc_id": "2141", - "loc_data": "{2906,10204,0,1,0}" - }, - { - "npc_id": "2202", - "loc_data": "{2912,10222,0,1,0}" - }, - { - "npc_id": "2195", - "loc_data": "{2901,10227,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": "2190", - "loc_data": "{2922,10201,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,0,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": "3848", - "loc_data": "{2410,3780,0,1,0}-{2414,3780,0,1,0}" - }, - { - "npc_id": "313", - "loc_data":"{2401,3781,0,1,0}" - }, - { - "npc_id": "312", - "loc_data": "{2421,3789,0,1,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,1,0}-{2890,9766,0,1,0}-{2894,9764,0,1,0}" - }, - { - "npc_id": "3322", - "loc_data": "{2384,4439,0,1,0}" - }, - { - "npc_id": "223", - "loc_data": "{2569,3250,0,1,0}" - }, - { - "npc_id": "224", - "loc_data": "{2582,9656,0,1,0}-{2589,9657,0,1,0}-{2587,9658,0,1,0}-{2584,9657,0,1,0}-{2585,9656,0,1,0}-{2582,9658,0,1,0}-{2585,9653,0,1,0}-{2590,9658,0,1,0}-{2590,9655,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": "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}-{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}-{2648,9766,0,1,4}-{2653,9761,0,1,3}-{2483,2877,0,1,3}-{2481,2876,0,1,4}-{2457,2867,0,1,4}-{2481,2847,0,1,0}-{2475,2854,0,1,3}-{2482,2873,0,1,3}-{2485,2876,0,1,2}-{2449,2865,0,1,1}-{2461,2880,0,1,5}-{2489,2935,0,1,7}-{2492,2907,0,1,1}-{2487,2894,0,1,2}-{2487,2888,0,1,7}-{2478,2916,0,1,5}-{2485,2902,0,1,3}-{2487,2902,0,1,2}-{2489,2894,0,1,2}-{2490,2905,0,1,7}-{2490,2917,0,1,5}-{2484,2890,0,1,1}-{2491,2927,0,1,1}-{3250,3239,0,1,1}-{3241,3241,0,1,1}-{3249,3249,0,1,1}-{3218,9890,0,1,3}-{3220,9887,0,1,0}-{3218,9887,0,1,1}-{3218,9889,0,1,5}-{3213,9890,0,1,1}-{2496,2890,0,1,4}-{2503,2889,0,1,1}-{2497,2939,0,1,2}-{2369,3374,0,1,0}-{2372,3379,0,1,0}-{2378,3366,0,1,0}-{2379,3378,0,1,0}-{2387,3370,0,1,0}-{2394,3365,0,1,0}-{2402,3386,0,1,0}-{2405,3381,0,1,0}-{2407,3387,0,1,0}-{2412,3384,0,1,0}-" + }, + { + "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": "{2417,3493,1,1,0}-{2418,3495,1,1,0}-{2383,3452,0,1,0}-{2401,3417,0,1,0}-{2402,3422,0,1,0}-{2402,3441,0,1,0}-{2403,3430,0,1,0}-{2408,3441,0,1,0}-{2409,3430,0,1,0}-{2423,3426,0,1,0}-{2427,3428,0,1,0}-{2427,3440,0,1,0}-{2526,3168,0,1,0}-{2529,3163,0,1,0}-{2530,3172,0,1,0}-{2536,3169,0,1,0}-{2435,3460,0,1,0}-{2440,3470,0,1,0}-{2447,3502,0,1,0}-{2457,3462,0,1,0}-{2478,3502,0,1,0}-{2449,3492,1,1,0}-{2450,3490,1,1,0}-{2474,3490,1,1,0}-{2482,3492,1,1,0}-{2482,3498,1,1,0}-{2399,3356,0,1,0}-" + }, + { + "npc_id": "67", + "loc_data": "{2556,3226,0,1,0}-{2456,3425,0,1,0}-{2459,3421,0,1,0}-{2462,3431,0,1,0}-{2394,3500,1,1,0}-{2417,3483,1,1,0}-{2377,3442,0,1,0}-{2380,3425,0,1,0}-{2383,3433,0,1,0}-{2405,3447,0,1,0}-{2406,3440,0,1,0}-{2393,3451,1,1,0}-{2408,3437,1,1,0}-{2521,3169,0,1,0}-{2521,3171,0,1,0}-{2033,5530,1,1,0}-{2437,3478,0,1,0}-{2442,3465,0,1,0}-{2456,3467,0,1,0}-{2471,3496,0,1,0}-{2474,3508,0,1,0}-{2475,3471,0,1,0}-{2479,3468,0,1,0}-{2486,3470,0,1,0}-{2492,3474,0,1,0}-{2443,3464,1,1,0}-{2443,3502,1,1,0}-{2448,3496,1,1,0}-{2449,3506,1,1,0}-{2457,3498,1,1,0}-{2474,3498,1,1,0}-{2480,3502,1,1,0}-{2481,3482,1,1,0}-{2490,3503,1,1,0}-{2400,3356,0,1,0}-" + }, + { + "npc_id": "68", + "loc_data": "{2400,3514,1,1,0}-{2418,3485,1,1,0}-{2378,3423,0,1,0}-{2393,3435,0,1,0}-{2394,3425,0,1,0}-{2395,3449,0,1,0}-{2403,3433,0,1,0}-{2420,3429,0,1,0}-{2420,3438,0,1,0}-{2522,3172,0,1,0}-{2433,3474,0,1,0}-{2433,3492,0,1,0}-{2458,3497,0,1,0}-{2464,3505,0,1,0}-{2476,3458,0,1,0}-{2445,3502,1,1,0}-{2453,3488,1,1,0}-{2475,3502,1,1,0}-{2465,3490,2,1,0}-{2401,3357,0,1,0}-" + }, + { + "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}-{2564,9653,0,1,0}-{2565,9655,0,1,0}-{2566,9626,0,1,0}-{2567,9653,0,1,0}-{2569,9633,0,1,0}-{2607,9621,0,1,0}-{2551,3408,0,1,6}-{2553,3405,0,1,3}-{2559,3452,0,1,3}-" + }, + { + "npc_id": "101", + "loc_data": "{2622,3389,0,1,4}-{2619,3390,0,1,4}-{2573,3437,0,1,3}-{2562,9659,0,1,0}-{2563,9661,0,1,0}-{2599,9626,0,1,0}-{2601,9627,0,1,0}-{2605,9621,0,1,0}-{2610,9620,0,1,0}-{2624,3391,0,1,1}-{2555,3407,0,1,3}-{2553,3457,0,1,3}-" + }, + { + "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}-{2696,3329,0,1,4}-{2708,3336,0,1,4}-{3230,3500,0,1,5}-{2988,3671,0,1,0}-{3001,3674,0,1,0}-{2497,3164,0,1,2}-{2387,3376,0,1,0}-{2398,3366,0,1,0}-{2412,3378,0,1,0}-{2419,3372,0,1,0}-" + }, + { + "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}-{3305,9400,0,1,1}-{3244,9356,0,1,1}-{3252,9370,0,1,1}-{3294,9375,0,1,3}-{3050,10337,0,1,4}-{3047,10340,0,1,4}-{3048,10346,0,1,4}-{3048,10346,0,1,3}-" + }, + { + "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": "{2369,3404,0,1,0}-{3118,9845,0,1,4}-{3111,9844,0,1,6}-{3123,9845,0,1,3}-{3114,9833,0,1,3}-{3110,9841,0,1,1}-{3119,9839,0,1,1}-{3097,9832,0,1,0}-{3101,9832,0,1,1}-{3107,9829,0,1,3}-{3115,9831,0,1,4}-{3109,9835,0,1,4}-{2904,9734,0,1,0}-{2548,3146,0,1,1}-{2542,3145,0,1,4}-{2503,3150,0,1,4}-{3300,3649,0,1,0}-{3044,10321,0,1,2}-{3044,10316,0,1,4}-{3045,10308,0,1,4}-{3048,10317,0,1,2}-" + }, + { + "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": "{2540,9820,0,1,0}-{2543,9813,0,1,0}-{2546,9817,0,1,0}-{2464,4421,0,1,0}-{2466,4423,0,1,0}-{2486,4464,0,1,0}-{2556,3444,0,1,0}-{3082,3077,0,1,0}-{3092,3082,0,1,0}-{3109,3090,0,1,0}-{3112,3093,0,1,0}-{2249,3260,0,1,0}-{2250,3257,0,1,0}-{2250,3259,0,1,0}-{2252,3261,0,1,0}-{2253,3256,0,1,0}-{2262,3226,0,1,0}-{2264,3225,0,1,0}-{2266,3229,0,1,0}-{2268,3227,0,1,0}-{2269,3222,0,1,0}-{2269,3224,0,1,0}-{2270,3223,0,1,0}-{2270,3226,0,1,0}-{3200,5954,0,1,0}-{3202,5958,0,1,0}-{3204,5955,0,1,0}-{3204,5959,0,1,0}-{3231,5967,0,1,0}-{3236,5974,0,1,0}-{3237,5968,0,1,0}-{3240,5992,0,1,0}-{3240,5994,0,1,0}-{3242,5990,0,1,0}-{3242,5993,0,1,0}-{3244,5994,0,1,0}-{3246,5989,0,1,0}-{2196,3191,0,1,0}-{2197,3189,0,1,0}-{2198,3181,0,1,0}-{2198,3187,0,1,0}-{2199,3183,0,1,0}-{2199,3187,0,1,0}-{2200,3185,0,1,0}-{2201,3185,0,1,0}-{2204,3180,0,1,0}-{2324,4599,0,1,0}-{2325,4597,0,1,0}-{2326,4589,0,1,0}-{2326,4595,0,1,0}-{2327,4591,0,1,0}-{2327,4595,0,1,0}-{2328,4593,0,1,0}-{2329,4593,0,1,0}-{2332,4588,0,1,0}-{2176,3202,0,1,0}-{2178,3206,0,1,0}-{2180,3203,0,1,0}-{2180,3207,0,1,0}-{2207,3215,0,1,0}-{2212,3222,0,1,0}-{2213,3216,0,1,0}-{2216,3240,0,1,0}-{2216,3242,0,1,0}-{2218,3238,0,1,0}-{2218,3241,0,1,0}-{2220,3242,0,1,0}-{2222,3237,0,1,0}-{2437,3425,0,1,0}-{2438,3422,0,1,0}-{2450,3419,0,1,0}-{2415,4466,0,1,0}-{2419,4427,0,1,0}-{2421,4468,0,1,0}-{2426,4431,0,1,0}-{3175,3226,0,1,0}-{3178,3228,0,1,0}-{3179,3226,0,1,0}-{3182,3251,0,1,0}-{3183,3254,0,1,0}-{2697,6204,0,1,0}-{2698,6201,0,1,0}-{2698,6203,0,1,0}-{2700,6205,0,1,0}-{2701,6200,0,1,0}-{2710,6170,0,1,0}-{2712,6169,0,1,0}-{2714,6173,0,1,0}-{2716,6171,0,1,0}-{2717,6166,0,1,0}-{2717,6168,0,1,0}-{2718,6167,0,1,0}-{2718,6170,0,1,0}-{2478,3373,0,1,0}-{2479,3381,0,1,0}-{2481,3384,0,1,0}-{2490,3371,0,1,0}-{2689,6083,0,1,0}-{2689,6088,0,1,0}-{2690,6094,0,1,0}-{2691,6084,0,1,0}-{2692,6081,0,1,0}-{2692,6090,0,1,0}-{2693,6083,0,1,0}-{2693,6085,0,1,0}-{2693,6090,0,1,0}-{2694,6083,0,1,0}-{2694,6086,0,1,0}-{2695,6082,0,1,0}-{2695,6085,0,1,0}-{2695,6088,0,1,0}-{2695,6091,0,1,0}-{2695,6121,0,1,0}-{2698,6121,0,1,0}-{2699,6082,0,1,0}-{2699,6085,0,1,0}-{2700,6084,0,1,0}-{2701,6085,0,1,0}-{2702,6081,0,1,0}-{2702,6083,0,1,0}-{2703,6119,0,1,0}-{2704,6117,0,1,0}-{2705,6121,0,1,0}-{2730,6091,0,1,0}-{2732,6095,0,1,0}-{2734,6090,0,1,0}-{2735,6094,0,1,0}-{2735,6097,0,1,0}-{2738,6098,0,1,0}-{2416,3404,0,1,0}-{2424,3410,0,1,0}-{2430,3405,0,1,0}-{1906,5223,0,1,0}-{1907,5221,0,1,0}-{3273,6012,0,1,0}-{3274,6009,0,1,0}-{3274,6011,0,1,0}-{3276,6013,0,1,0}-{3277,6008,0,1,0}-{3286,5978,0,1,0}-{3288,5977,0,1,0}-{3290,5981,0,1,0}-{3292,5979,0,1,0}-{3293,5974,0,1,0}-{3293,5976,0,1,0}-{3294,5975,0,1,0}-{3294,5978,0,1,0}-{2828,5091,0,1,0}-{2832,5108,0,1,0}-{2835,5079,0,1,0}-{2846,5067,0,1,0}-{2850,5111,0,1,0}-{2853,5086,0,1,0}-{2864,5071,0,1,0}-{2549,3139,0,1,0}-{2241,3139,0,1,0}-{2241,3144,0,1,0}-{2242,3150,0,1,0}-{2243,3140,0,1,0}-{2244,3137,0,1,0}-{2244,3146,0,1,0}-{2245,3139,0,1,0}-{2245,3141,0,1,0}-{2245,3146,0,1,0}-{2246,3139,0,1,0}-{2246,3142,0,1,0}-{2247,3138,0,1,0}-{2247,3141,0,1,0}-{2247,3144,0,1,0}-{2247,3147,0,1,0}-{2247,3177,0,1,0}-{2250,3177,0,1,0}-{2251,3138,0,1,0}-{2251,3141,0,1,0}-{2252,3140,0,1,0}-{2253,3141,0,1,0}-{2254,3137,0,1,0}-{2254,3139,0,1,0}-{2255,3175,0,1,0}-{2256,3173,0,1,0}-{2257,3177,0,1,0}-{2282,3147,0,1,0}-{2284,3151,0,1,0}-{2286,3146,0,1,0}-{2287,3150,0,1,0}-{2287,3153,0,1,0}-{2290,3154,0,1,0}-" + }, + { + "npc_id": "154", + "loc_data": "{2475,4459,0,1,0}-{2480,4458,0,1,0}-{2481,4454,0,1,0}-{2488,4465,0,1,0}-{3125,3084,0,1,0}-{2268,3228,0,1,0}-{3202,5955,0,1,0}-{3206,5953,0,1,0}-{3231,5976,0,1,0}-{3233,5974,0,1,0}-{3235,5970,0,1,0}-{3241,5996,0,1,0}-{3242,5988,0,1,0}-{3244,5992,0,1,0}-{2180,3173,0,1,0}-{2193,3187,0,1,0}-{2196,3184,0,1,0}-{2200,3181,0,1,0}-{2200,3188,0,1,0}-{2203,3189,0,1,0}-{2308,4581,0,1,0}-{2321,4595,0,1,0}-{2324,4592,0,1,0}-{2328,4589,0,1,0}-{2328,4596,0,1,0}-{2331,4597,0,1,0}-{2178,3203,0,1,0}-{2182,3201,0,1,0}-{2207,3224,0,1,0}-{2209,3222,0,1,0}-{2211,3218,0,1,0}-{2217,3244,0,1,0}-{2218,3236,0,1,0}-{2220,3240,0,1,0}-{2446,3396,0,1,0}-{2470,3397,0,1,0}-{2394,4453,0,1,0}-{2394,4457,0,1,0}-{2406,4447,0,1,0}-{2422,3489,0,1,0}-{2716,6172,0,1,0}-{2688,6092,0,1,0}-{2690,6087,0,1,0}-{2690,6095,0,1,0}-{2692,6082,0,1,0}-{2692,6088,0,1,0}-{2692,6123,0,1,0}-{2693,6081,0,1,0}-{2694,6125,0,1,0}-{2695,6087,0,1,0}-{2697,6086,0,1,0}-{2697,6087,0,1,0}-{2698,6081,0,1,0}-{2698,6124,0,1,0}-{2700,6083,0,1,0}-{2702,6084,0,1,0}-{2726,6085,0,1,0}-{2727,6081,0,1,0}-{2729,6083,0,1,0}-{2732,6084,0,1,0}-{2378,3418,0,1,0}-{2380,3422,0,1,0}-{2393,3442,0,1,0}-{2397,3440,0,1,0}-{2423,3399,0,1,0}-{2425,3406,0,1,0}-{2427,3402,0,1,0}-{1908,5222,0,1,0}-{3292,5980,0,1,0}-{2831,5070,0,1,0}-{2843,5106,0,1,0}-{2844,5080,0,1,0}-{2860,5079,0,1,0}-{2866,5104,0,1,0}-{2868,5096,0,1,0}-{2240,3148,0,1,0}-{2242,3143,0,1,0}-{2242,3151,0,1,0}-{2244,3138,0,1,0}-{2244,3144,0,1,0}-{2244,3179,0,1,0}-{2245,3137,0,1,0}-{2246,3181,0,1,0}-{2247,3143,0,1,0}-{2249,3142,0,1,0}-{2249,3143,0,1,0}-{2250,3137,0,1,0}-{2250,3180,0,1,0}-{2252,3139,0,1,0}-{2254,3140,0,1,0}-{2278,3141,0,1,0}-{2279,3137,0,1,0}-{2281,3139,0,1,0}-{2284,3140,0,1,0}-" + }, + { + "npc_id": "155", + "loc_data": "{3093,3086,0,1,0}-{3097,3085,0,1,0}-{3129,3091,0,1,0}-{3135,3084,0,1,0}-{2190,3180,0,1,0}-{2318,4588,0,1,0}-{2374,3469,0,1,0}-{2376,3466,0,1,0}-{3254,3230,0,1,0}-{1986,5564,0,1,0}-{2434,3516,0,1,0}-{2479,3501,0,1,0}-" + }, + { + "npc_id": "156", + "loc_data": "{2371,3460,0,1,0}-{2372,3456,0,1,0}-{2422,3467,0,1,0}-{2725,6127,0,1,0}-{2444,3491,0,1,0}-{2277,3183,0,1,0}-" + }, + { + "npc_id": "157", + "loc_data": "{2180,2798,0,1,0}-{2209,2812,0,1,0}-{2217,2780,0,1,0}-{1921,5931,0,1,0}-{1947,5908,0,1,0}-{1955,5949,0,1,0}-{1973,5888,0,1,0}-{1975,5918,0,1,0}-{1988,5870,0,1,0}-{2017,5884,0,1,0}-{2025,5852,0,1,0}-{2182,2979,0,1,0}-{2211,2963,0,1,0}-{1931,6038,0,1,0}-{1983,6037,0,1,0}-{2153,2810,0,1,0}-{2172,2806,0,1,0}-{1961,5882,0,1,0}-{1980,5878,0,1,0}-{2242,2788,0,1,0}-{2248,2858,0,1,0}-{2256,2828,0,1,0}-{2284,2874,0,1,0}-{2255,3216,0,1,0}-{2262,3210,0,1,0}-{2271,3204,0,1,0}-{2294,3219,0,1,0}-{2296,3206,0,1,0}-{2298,3201,0,1,0}-{2056,5930,0,1,0}-{2064,5900,0,1,0}-{2092,5946,0,1,0}-{2050,5860,0,1,0}-{3201,5986,0,1,0}-{3254,5972,0,1,0}-{3255,5986,0,1,0}-{3256,6008,0,1,0}-{3257,5973,0,1,0}-{3257,5975,0,1,0}-{3260,5974,0,1,0}-{3261,5977,0,1,0}-{3262,5957,0,1,0}-{2194,3158,0,1,0}-{2216,3188,0,1,0}-{2217,3190,0,1,0}-{2220,3158,0,1,0}-{2222,3141,0,1,0}-{2229,3138,0,1,0}-{2231,3181,0,1,0}-{2236,3155,0,1,0}-{2278,2956,0,1,0}-{2322,4566,0,1,0}-{2344,4596,0,1,0}-{2345,4598,0,1,0}-{2348,4566,0,1,0}-{2350,4549,0,1,0}-{2357,4546,0,1,0}-{2359,4589,0,1,0}-{2364,4563,0,1,0}-{2177,3234,0,1,0}-{2230,3220,0,1,0}-{2231,3234,0,1,0}-{2232,3256,0,1,0}-{2233,3221,0,1,0}-{2233,3223,0,1,0}-{2236,3222,0,1,0}-{2237,3225,0,1,0}-{2238,3205,0,1,0}-{2479,3396,0,1,0}-{2102,2942,0,1,0}-{2108,2919,0,1,0}-{2104,2873,0,1,0}-{2122,6032,0,1,0}-{1912,5945,0,1,0}-{3143,3210,0,1,0}-{3155,3253,0,1,0}-{3163,3261,0,1,0}-{3168,3258,0,1,0}-{2086,6028,0,1,0}-{2703,6160,0,1,0}-{2710,6154,0,1,0}-{2719,6148,0,1,0}-{2742,6163,0,1,0}-{2744,6150,0,1,0}-{2746,6145,0,1,0}-{2314,2960,0,1,0}-{2723,6105,0,1,0}-{2739,6123,0,1,0}-{2744,6108,0,1,0}-{2326,2894,0,1,0}-{2333,2920,0,1,0}-{3279,5968,0,1,0}-{3286,5962,0,1,0}-{3295,5956,0,1,0}-{3318,5971,0,1,0}-{3320,5958,0,1,0}-{3322,5953,0,1,0}-{2134,5966,0,1,0}-{2141,5992,0,1,0}-{2123,2966,0,1,0}-{2175,2965,0,1,0}-{1910,6014,0,1,0}-{1916,5991,0,1,0}-{2540,3167,0,1,0}-{2110,2955,0,1,0}-{1990,6051,0,1,0}-{2019,6035,0,1,0}-{2447,3467,0,1,0}-{2448,3469,0,1,0}-{1918,6027,0,1,0}-{2275,3161,0,1,0}-{2291,3179,0,1,0}-{2296,3164,0,1,0}-{2113,2859,0,1,0}-{2139,2836,0,1,0}-{2147,2877,0,1,0}-{2165,2816,0,1,0}-{2167,2846,0,1,0}-" + }, + { + "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": "{2392,3475,0,1,0}-{2394,3506,0,1,0}-{2396,3471,0,1,0}-{2405,3499,0,1,0}-{2416,3487,0,1,0}-{2413,3445,1,1,0}-{2415,3435,1,1,0}-{2416,3416,1,1,0}-{2424,3434,1,1,0}-{2483,3500,1,1,0}-" + }, + { + "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": "{2451,3414,0,1,0}-{2459,3395,0,1,0}-{2459,3438,0,1,0}-{2462,3395,0,1,0}-{2445,3429,1,1,0}-{2409,3470,1,1,0}-{2416,3466,1,1,0}-{2420,3466,1,1,0}-{2461,9895,0,1,0}-{2465,9894,0,1,0}-{2465,9899,0,1,0}-{2459,3385,0,1,0}-{2463,3385,0,1,0}-{2392,3454,0,1,0}-{2394,3436,0,1,0}-{2408,3452,0,1,0}-{2421,3413,0,1,0}-{2442,3489,0,1,0}-{2450,3485,0,1,0}-{2451,3507,0,1,0}-{2453,3490,0,1,0}-{2459,3503,0,1,0}-{2461,3509,0,1,0}-{2464,3465,0,1,0}-{2464,3468,0,1,0}-{2468,3465,0,1,0}-{2468,3468,0,1,0}-{2468,3506,0,1,0}-{2472,3484,0,1,0}-{2475,3490,0,1,0}-{2475,3502,0,1,0}-{2477,3512,0,1,0}-{2481,3485,0,1,0}-{2482,3501,0,1,0}-{2467,3495,1,1,0}-{2473,3495,1,1,0}-{2448,3497,2,1,0}-{2463,3480,2,1,0}-{2465,3497,2,1,0}-{2465,3510,2,1,0}-{2466,3480,2,1,0}-" + }, + { + "npc_id": "164", + "loc_data": "{2459,3392,0,1,0}-{2461,3422,0,1,0}-{2462,3392,0,1,0}-{2460,3382,0,1,0}-{2462,3382,0,1,0}-{2410,3416,0,1,0}-{2420,3435,0,1,0}-{2420,3447,0,1,0}-{2427,3410,0,1,0}-{2012,5535,2,1,0}-{2023,5544,2,1,0}-{2441,3497,0,1,0}-{2447,3510,0,1,0}-{2461,3487,0,1,0}-{2464,3472,0,1,0}-{2464,3489,0,1,0}-{2467,3489,0,1,0}-{2468,3472,0,1,0}-{2473,3503,0,1,0}-{2478,3497,0,1,0}-{2464,3496,1,1,0}-{2448,3495,2,1,0}-{2460,3487,2,1,0}-{2467,3494,2,1,0}-{2467,3510,2,1,0}-{2471,3496,2,1,0}-{2483,3495,2,1,0}-{2483,3497,2,1,0}-" + }, + { + "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": "{2434,3436,0,1,0}-{2437,3451,0,1,0}-{2438,3427,0,1,0}-{2441,3411,0,1,0}-{2466,3449,0,1,0}-{2470,3399,0,1,0}-{2472,3400,0,1,0}-{2473,3412,0,1,0}-{2476,3454,0,1,0}-{2482,3397,0,1,0}-{2489,3401,0,1,0}-{2479,3407,1,1,0}-{2379,3482,0,1,0}-{2381,3496,0,1,0}-{2384,3497,0,1,0}-{2391,3476,0,1,0}-{2410,3496,0,1,0}-{2421,3481,0,1,0}-{2397,3514,1,1,0}-{2398,3451,1,1,0}-{2414,3447,1,1,0}-{2438,3465,0,1,0}-{2442,3505,0,1,0}-{2448,3486,0,1,0}-{2449,3487,0,1,0}-{2450,3489,0,1,0}-{2450,3505,0,1,0}-{2454,3465,0,1,0}-{2449,3486,1,1,0}-{2457,3488,1,1,0}-{2476,3488,1,1,0}-{2450,3496,2,1,0}-{2467,3488,2,1,0}-{2470,3503,2,1,0}-{2481,3498,2,1,0}-" + }, + { + "npc_id": "169", + "loc_data": "{2439,3433,0,1,0}-{2441,3449,0,1,0}-{2442,3428,0,1,0}-{2446,3403,0,1,0}-{2450,3416,0,1,0}-{2468,3441,0,1,0}-{2480,3408,0,1,0}-{2480,3406,1,1,0}-{2486,3400,1,1,0}-{2378,3482,0,1,0}-{2383,3496,0,1,0}-{2402,3507,0,1,0}-{2406,3476,0,1,0}-{2422,3485,0,1,0}-{2382,3506,1,1,0}-{2418,3472,1,1,0}-{2392,3450,1,1,0}-{2415,3415,1,1,0}-{2416,3434,1,1,0}-{2423,3425,1,1,0}-{2424,3442,1,1,0}-{2448,3489,0,1,0}-{2450,3488,0,1,0}-{2463,3508,0,1,0}-{2473,3489,0,1,0}-{2474,3457,0,1,0}-{2479,3503,0,1,0}-{2486,3467,0,1,0}-{2437,3463,1,1,0}-{2448,3489,1,1,0}-{2482,3508,1,1,0}-" + }, + { + "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": "{1970,5522,3,1,0}-{2384,3481,0,1,0}-{2388,3473,0,1,0}-{2418,3474,3,1,0}-{2415,3433,3,1,0}-{2466,3500,2,1,0}-" + }, + { + "npc_id": "480", + "loc_data": "{1964,5522,3,1,0}-{2458,3417,1,1,0}-{2460,3417,1,1,0}-{2409,3507,0,1,0}-{2412,3474,3,1,0}-{2463,3504,2,1,0}-" + }, + { + "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": "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": "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": "{3265,3066,0,1,0}-{3267,3066,0,1,0}-{3310,3068,0,1,0}-{3322,3011,0,1,0}-{3322,3052,0,1,0}-{3324,3030,0,1,0}-{3150,3044,0,1,0}-{3172,3009,0,1,0}-{3197,3012,0,1,0}-{3198,3040,0,1,0}-{3217,3092,0,1,0}-{3235,3074,0,1,0}-{3252,3125,0,1,0}-{3258,3078,0,1,0}-{3237,2968,0,1,0}-{3224,3013,0,1,0}-{3225,3034,0,1,0}-{3226,3060,0,1,0}-{3250,3057,0,1,0}-{3283,3108,0,1,0}-{3323,3094,0,1,0}-" + }, + { + "npc_id": "840", + "loc_data": "{3268,3052,0,1,0}-{3281,3056,0,1,0}-{3307,3055,0,1,0}-{3318,3020,0,1,0}-{3318,3040,0,1,0}-{3190,3054,0,1,0}-{3192,3016,0,1,0}-{3217,3111,0,1,0}-{3222,3086,0,1,0}-{3238,3101,0,1,0}-{3244,3080,0,1,0}-{3253,3116,0,1,0}-{3255,3095,0,1,0}-{3237,3000,0,1,0}-{3245,2960,0,1,0}-{3208,3032,0,1,0}-{3217,3064,0,1,0}-{3238,3015,0,1,0}-{3258,3063,0,1,0}-{3267,3077,0,1,0}-{3269,3110,0,1,0}-{3275,3094,0,1,0}-{3291,3078,0,1,0}-{3291,3100,0,1,0}-{3305,3089,0,1,0}-{3318,3078,0,1,0}-{3321,3104,0,1,0}-" + }, + { + "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": "{3726,3381,0,0,0}-{3745,3359,0,0,0}-{3749,3365,0,0,0}-{3750,3354,0,0,0}-{3757,3363,0,0,0}-{3763,3337,0,0,0}-{3459,3457,0,0,0}-{3460,3462,0,0,0}-{3462,3459,0,0,0}-{3463,3490,0,0,0}-{3464,3511,0,0,0}-{3465,3466,0,0,0}-{3466,3495,0,0,0}-{3466,3497,0,0,0}-{3467,3485,0,0,0}-{3467,3497,0,0,0}-{3467,3509,0,0,0}-{3469,3470,0,0,0}-{3470,3469,0,0,0}-{3471,3477,0,0,0}-{3474,3505,0,0,0}-{3476,3507,0,0,0}-{3479,3511,0,0,0}-{3480,3468,0,0,0}-{3480,3470,0,0,0}-{3482,3469,0,0,0}-{3483,3511,0,0,0}-{3484,3464,0,0,0}-{3485,3509,0,0,0}-{3490,3461,0,0,0}-{3491,3460,0,0,0}-{3494,3461,0,0,0}-{3494,3512,0,0,0}-{3498,3461,0,0,0}-{3499,3513,0,0,0}-{3501,3513,0,0,0}-{3502,3464,0,0,0}-{3504,3463,0,0,0}-{3506,3512,0,0,0}-{3506,3515,0,0,0}-{3507,3515,0,0,0}-{3509,3512,0,0,0}-{3511,3488,0,0,0}-{3513,3467,0,0,0}-{3513,3487,0,0,0}-{3513,3489,0,0,0}-{3513,3503,0,0,0}-{3514,3490,0,0,0}-{3515,3495,0,0,0}-{3515,3499,0,0,0}-{3516,3469,0,0,0}-{3409,3369,0,0,0}-{3420,3349,0,0,0}-{3430,3340,0,0,0}-{3435,3330,0,0,0}-{3435,3359,0,0,0}-{3437,3374,0,0,0}-{3446,3385,0,0,0}-{3447,3388,0,0,0}-{3450,3370,0,0,0}-{3450,3387,0,0,0}-{3451,3336,0,0,0}-{3452,3355,0,0,0}-{3726,3289,0,0,0}-{3732,3291,0,0,0}-{3737,3280,0,0,0}-{3745,3285,0,0,0}-{3418,3420,0,0,0}-{3424,3452,0,0,0}-{3428,3409,0,0,0}-{3433,3416,0,0,0}-{3434,3394,0,0,0}-{3436,3407,0,0,0}-{3438,3414,0,0,0}-{3439,3449,0,0,0}-{3446,3439,0,0,0}-{3454,3410,0,0,0}-{3454,3423,0,0,0}-{2852,4561,0,0,0}-{2853,4566,0,0,0}-{2856,4568,0,0,0}-{2864,4561,0,0,0}-{2864,4563,0,0,0}-" + }, + { + "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": "{2247,3226,0,0,0}-{2249,3227,0,0,0}-{2249,3258,0,0,0}-{2251,3260,0,0,0}-{2259,3211,0,0,0}-{2267,3223,0,0,0}-{2267,3225,0,0,0}-{2298,3262,0,0,0}-{3202,5990,0,0,0}-{3232,5977,0,0,0}-{3234,5969,0,0,0}-{3237,5995,0,0,0}-{3242,5997,0,0,0}-{3244,5995,0,0,0}-{3245,5995,0,0,0}-{2194,3189,0,0,0}-{2196,3187,0,0,0}-{2196,3189,0,0,0}-{2206,3164,0,0,0}-{2209,3164,0,0,0}-{2216,3142,0,0,0}-{2218,3139,0,0,0}-{2178,3238,0,0,0}-{2208,3225,0,0,0}-{2210,3217,0,0,0}-{2213,3243,0,0,0}-{2218,3245,0,0,0}-{2220,3243,0,0,0}-{2221,3243,0,0,0}-{2695,6170,0,0,0}-{2697,6171,0,0,0}-{2697,6202,0,0,0}-{2699,6204,0,0,0}-{2707,6155,0,0,0}-{2715,6167,0,0,0}-{2715,6169,0,0,0}-{2746,6206,0,0,0}-{2729,6086,0,0,0}-{2731,6080,0,0,0}-{2731,6089,0,0,0}-{2744,6139,0,0,0}-{3271,5978,0,0,0}-{3273,5979,0,0,0}-{3273,6010,0,0,0}-{3275,6012,0,0,0}-{3283,5963,0,0,0}-{3291,5975,0,0,0}-{3291,5977,0,0,0}-{3322,6014,0,0,0}-{2281,3142,0,0,0}-{2283,3136,0,0,0}-{2283,3145,0,0,0}-{2296,3195,0,0,0}-" + }, + { + "npc_id": "1213", + "loc_data": "{2248,3225,0,0,0}-{2248,3227,0,0,0}-{2251,3257,0,0,0}-{2253,3259,0,0,0}-{2268,3224,0,0,0}-{2291,3262,0,0,0}-{3204,5988,0,0,0}-{3232,5979,0,0,0}-{3234,5967,0,0,0}-{3234,5976,0,0,0}-{3238,5996,0,0,0}-{3239,5997,0,0,0}-{3243,5996,0,0,0}-{2192,3188,0,0,0}-{2192,3191,0,0,0}-{2194,3190,0,0,0}-{2208,3163,0,0,0}-{2209,3163,0,0,0}-{2216,3136,0,0,0}-{2216,3140,0,0,0}-{2180,3236,0,0,0}-{2208,3227,0,0,0}-{2210,3215,0,0,0}-{2210,3224,0,0,0}-{2214,3244,0,0,0}-{2215,3245,0,0,0}-{2219,3244,0,0,0}-{2696,6169,0,0,0}-{2696,6171,0,0,0}-{2699,6201,0,0,0}-{2701,6203,0,0,0}-{2716,6168,0,0,0}-{2739,6206,0,0,0}-{2730,6084,0,0,0}-{3272,5977,0,0,0}-{3272,5979,0,0,0}-{3275,6009,0,0,0}-{3277,6011,0,0,0}-{3292,5976,0,0,0}-{3315,6014,0,0,0}-{2282,3140,0,0,0}-" + }, + { + "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": "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}-{3274,9397,0,1,1}-{3275,9393,0,1,6}-{3271,9384,0,1,0}-{3270,9380,0,1,2}-{3283,9378,0,1,6}-{3285,9386,0,1,5}-{3284,9401,0,1,4}-{3278,9353,0,1,1}-{3299,9380,0,1,1}-{3319,9402,0,1,1}-{3305,9350,0,1,4}-{3248,9374,0,1,3}-{3253,9363,0,1,3}-{3249,9355,0,1,3}-{2761,10007,0,1,0}-{2757,10010,0,1,3}-{2763,10000,0,1,5}-{2760,10011,0,1,0}-{2761,9997,0,1,4}-" + }, + { + "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": "{2437,3442,0,1,0}-{2449,3421,0,1,0}-{2457,3394,0,1,0}-{2464,3394,0,1,0}-{2480,3406,0,1,0}-{2432,3387,0,1,0}-{2442,3388,0,1,0}-{2453,3363,0,1,0}-{2463,3375,0,1,0}-{2381,3428,0,1,0}-{2392,3405,0,1,0}-{2413,3397,0,1,0}-{2415,3408,0,1,0}-{2463,3456,0,1,0}-{2468,3456,0,1,0}-" + }, + { + "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": "{3371,9301,0,1,0}-{3371,9303,0,1,0}-{3371,9305,0,1,0}-{3371,9307,0,1,0}-{3371,9309,0,1,0}-{3373,9301,0,1,0}-{3373,9303,0,1,0}-{3373,9306,0,1,0}-{3373,9308,0,1,0}-{3375,9301,0,1,0}-{3375,9303,0,1,0}-{3375,9305,0,1,0}-{3375,9307,0,1,0}-{3375,9309,0,1,0}-{3403,2963,0,1,0}-{3429,2976,0,1,0}-{3328,2952,0,1,0}-{3328,2957,0,1,0}-{3331,2955,0,1,0}-{3331,2961,0,1,0}-{3266,2955,0,1,0}-{3271,2967,0,1,0}-{3279,2957,0,1,0}-{3280,2975,0,1,0}-{3284,2959,0,1,0}-{3294,2964,0,1,0}-{3295,2978,0,1,0}-{3305,2966,0,1,0}-{3307,2959,0,1,0}-{3309,2973,0,1,0}-{3396,3029,0,1,0}-{3397,3044,0,1,0}-{3398,3038,0,1,0}-" + }, + { + "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": "{3156,5477,0,1,5}-{3162,5479,0,1,4}-{3159,5477,0,1,6}-{3150,5474,0,1,3}-{3147,5480,0,1,1}-{3159,5484,0,1,3}-{2764,4944,1,1,0}-{2796,4976,1,1,0}-{2798,4950,1,1,0}-{2806,4939,1,1,0}-{2832,4959,2,1,0}-{3201,9293,0,1,0}-{3205,9303,0,1,0}-{3206,9328,0,1,0}-{3207,9310,0,1,0}-{3211,9284,0,1,0}-{3220,9289,0,1,0}-{3224,9334,0,1,0}-{3249,9286,0,1,0}-{3251,9303,0,1,0}-{3252,9327,0,1,0}-{3252,9332,0,1,0}-{3255,9315,0,1,0}-{3261,9296,0,1,0}-{3261,9306,0,1,0}-" + }, + { + "npc_id": "1962", + "loc_data": "{3168,5458,0,1,2}-{2900,4948,3,1,0}-{2762,4962,1,1,0}-{2763,4973,1,1,0}-{2780,4977,1,1,0}-{2787,4967,1,1,0}-{2796,4959,1,1,0}-{2838,4948,2,1,0}-{3205,9329,0,1,0}-{3210,9292,0,1,0}-{3221,9310,0,1,0}-{3225,9323,0,1,0}-{3243,9310,0,1,0}-{3246,9290,0,1,0}-{3255,9301,0,1,0}-{3255,9321,0,1,0}-{3261,9331,0,1,0}-" + }, + { + "npc_id": "1963", + "loc_data": "{3166,5465,0,1,7}-{3168,5462,0,1,0}-{3167,5467,0,1,1}-{3167,5467,0,1,7}-{2926,4965,3,1,0}-{2771,4947,1,1,0}-{2775,4963,1,1,0}-{2781,4948,1,1,0}-{2792,4942,1,1,0}-{2798,4954,1,1,0}-{2858,4964,2,1,0}-{3204,9309,0,1,0}-{3214,9333,0,1,0}-{3219,9297,0,1,0}-{3239,9300,0,1,0}-{3240,9286,0,1,0}-{3240,9330,0,1,0}-{3245,9306,0,1,0}-{3250,9316,0,1,0}-{3250,9317,0,1,0}-{3257,9290,0,1,0}-" + }, + { + "npc_id": "1964", + "loc_data": "{2761,4950,1,1,0}-{2765,4938,1,1,0}-{2770,4955,1,1,0}-{2772,4940,1,1,0}-{2777,4943,1,1,0}-{2782,4967,1,1,0}-{2797,4965,1,1,0}-{2799,4937,1,1,0}-{2799,4941,1,1,0}-{2807,4968,1,1,0}-{2808,4975,1,1,0}-{2809,4953,1,1,0}-{2864,4946,2,1,0}-{3202,9283,0,1,0}-{3206,9333,0,1,0}-{3209,9299,0,1,0}-{3219,9301,0,1,0}-{3226,9285,0,1,0}-{3227,9303,0,1,0}-{3228,9293,0,1,0}-{3229,9333,0,1,0}-{3237,9293,0,1,0}-{3246,9321,0,1,0}-{3256,9296,0,1,0}-{3260,9285,0,1,0}-{3262,9317,0,1,0}-" + }, + { + "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": "{2693,5075,0,1,0}-{2695,5089,0,1,0}-{2697,5096,0,1,0}-{2703,5064,0,1,0}-{2710,5105,0,1,0}-{2719,5078,0,1,0}-{2719,5112,0,1,0}-{2720,5096,0,1,0}-{2726,5086,0,1,0}-{2729,5096,0,1,0}-{2735,5061,0,1,0}-{2740,5069,0,1,0}-{2740,5085,0,1,0}-{2746,5092,0,1,0}-{2746,5114,0,1,0}-{2626,5065,0,1,0}-{2637,5099,0,1,0}-{2638,5058,0,1,0}-{2644,5090,0,1,0}-{2658,5082,0,1,0}-{2666,5097,0,1,0}-{2680,5074,0,1,0}-" + }, + { + "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": "{3264,2886,0,1,0}-{3267,2891,0,1,0}-{3268,2881,0,1,0}-{3269,2885,0,1,0}-{3296,2912,0,1,0}-{3297,2919,0,1,0}-{3301,2920,0,1,0}-{3308,2916,0,1,0}-{3258,2868,0,1,0}-{3259,2823,0,1,0}-{3259,2845,0,1,0}-{3260,2830,0,1,0}-{3260,2859,0,1,0}-{3261,2819,0,1,0}-{3261,2851,0,1,0}-{3337,2922,0,1,0}-{3343,2931,0,1,0}-{3349,2922,0,1,0}-{3351,2927,0,1,0}-{3357,2922,0,1,0}-{3359,2927,0,1,0}-{3364,2937,0,1,0}-{3268,2854,0,1,0}-{3269,2827,0,1,0}-{3269,2866,0,1,0}-{3269,2875,0,1,0}-{3270,2818,0,1,0}-{3272,2841,0,1,0}-{3276,2842,0,1,0}-{3282,2839,0,1,0}-{3290,2847,0,1,0}-" + }, + { + "npc_id": "1994", + "loc_data": "{3265,2931,0,1,0}-{3265,2935,0,1,0}-{3268,2932,0,1,0}-{3268,2935,0,1,0}-{3316,2900,0,1,0}-{3319,2897,0,1,0}-{3320,2901,0,1,0}-{3321,2899,0,1,0}-{3212,2863,0,1,0}-{3213,2866,0,1,0}-{3215,2863,0,1,0}-{3216,2865,0,1,0}-{3216,2868,0,1,0}-{3218,2831,0,1,0}-{3220,2830,0,1,0}-{3220,2833,0,1,0}-{3221,2831,0,1,0}-{3238,2845,0,1,0}-{3240,2845,0,1,0}-{3241,2843,0,1,0}-{3241,2847,0,1,0}-{3400,2997,0,1,0}-{3402,2999,0,1,0}-{3403,2997,0,1,0}-{3445,2993,0,1,0}-{3446,2992,0,1,0}-{3448,2991,0,1,0}-{3448,2994,0,1,0}-{3329,2933,0,1,0}-{3330,2931,0,1,0}-{3331,2933,0,1,0}-{3343,2895,0,1,0}-{3345,2896,0,1,0}-{3346,2894,0,1,0}-{3348,2894,0,1,0}-{3376,2934,0,1,0}-{3378,2933,0,1,0}-{3378,2935,0,1,0}-{3381,2907,0,1,0}-{3383,2905,0,1,0}-{3383,2908,0,1,0}-{3384,2907,0,1,0}-{3306,2817,0,1,0}-{3308,2818,0,1,0}-{3309,2816,0,1,0}-{3312,2817,0,1,0}-{3314,2861,0,1,0}-{3319,2873,0,1,0}-{3321,2871,0,1,0}-{3324,2870,0,1,0}-{3327,2858,0,1,0}-{3406,3014,0,1,0}-{3408,3013,0,1,0}-{3408,3016,0,1,0}-{3410,3015,0,1,0}-" + }, + { + "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": "{2442,9436,2,1,0}-{2458,9413,2,1,0}-{2470,9435,2,1,0}-{2472,9460,2,1,0}-{2483,9413,2,1,0}-{2485,9447,2,1,0}-{2480,3046,0,1,0}-{2453,9393,2,1,0}-{2461,9403,2,1,0}-{2464,9380,2,1,0}-" + }, + { + "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": "{3387,3068,0,1,0}-{3387,3017,0,1,0}-{3404,3062,0,1,0}-{3441,3061,0,1,0}-{3445,3034,0,1,0}-{3338,2804,0,1,0}-{3339,2815,0,1,0}-{3342,2809,0,1,0}-{3344,2800,0,1,0}-{3348,2809,0,1,0}-{3349,2814,0,1,0}-{3357,2813,0,1,0}-{3357,2806,0,1,0}-{3365,2815,0,1,0}-" + }, + { + "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": "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": "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": "{3333,2864,0,1,1}-{3335,2860,0,1,2}-{3338,2864,0,1,4}-{3215,2841,0,1,0}-{3217,2845,0,1,0}-{3220,2841,0,1,0}-{3295,2866,0,1,0}-{3297,2863,0,1,0}-{3301,2864,0,1,0}-{3336,2778,0,1,0}-{3343,2793,0,1,0}-{3367,2791,0,1,0}-" + }, + { + "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": "{2372,3401,0,1,0}-{3104,3875,0,1,2}-{2912,9731,0,1,0}-{3256,3624,0,1,5}-{3308,3661,0,1,2}-" + }, + { + "npc_id": "4691", + "loc_data": "{2371,3398,0,1,0}-{3110,3854,0,1,0}-" + }, + { + "npc_id": "4692", + "loc_data": "{2372,3395,0,1,0}-{3116,3858,0,1,0}-" + }, + { + "npc_id": "4693", + "loc_data": "{2369,3394,0,1,0}-{3094,3849,0,1,0}-{2912,9741,0,1,0}-{2906,9736,0,1,0}-" + }, + { + "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": "{2693,5065,0,1,0}-{2693,5089,0,1,0}-{2704,5091,0,1,0}-{2710,5095,0,1,0}-{2712,5076,0,1,0}-{2714,5108,0,1,0}-{2716,5092,0,1,0}-{2719,5071,0,1,0}-{2722,5061,0,1,0}-{2732,5087,0,1,0}-{2739,5078,0,1,0}-{2740,5104,0,1,0}-{2747,5083,0,1,0}-{2627,5093,0,1,0}-{2629,5061,0,1,0}-{2638,5090,0,1,0}-{2651,5105,0,1,0}-{2654,5079,0,1,0}-{2663,5116,0,1,0}-{2683,5059,0,1,0}-{2685,5111,0,1,0}-" + }, + { + "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": "{3264,3008,0,1,0}-{3266,3008,0,1,0}-{3285,3066,0,1,0}-{3322,3032,0,1,0}-{3323,3055,0,1,0}-{3325,3010,0,1,0}-{3174,3010,0,1,0}-{3198,3061,0,1,0}-{3199,3016,0,1,0}-{3219,3125,0,1,0}-{3233,3075,0,1,0}-{3250,3126,0,1,0}-{3223,3011,0,1,0}-{3228,3060,0,1,0}-{3251,3059,0,1,0}-{3310,3076,0,1,0}-" + }, + { + "npc_id": "6051", + "loc_data": "{3266,3010,0,1,0}-{3267,3068,0,1,0}-{3287,3065,0,1,0}-{3287,3067,0,1,0}-{3310,3070,0,1,0}-{3312,3068,0,1,0}-{3320,3054,0,1,0}-{3324,3013,0,1,0}-{3325,3033,0,1,0}-{3153,3047,0,1,0}-{3196,3063,0,1,0}-{3199,3036,0,1,0}-{3215,3092,0,1,0}-{3217,3124,0,1,0}-{3260,3077,0,1,0}-{3223,3035,0,1,0}-{3225,3011,0,1,0}-{3283,3084,0,1,0}-" + }, + { + "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": "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": "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": "7823", + "loc_data": "{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}-" + }, + { + "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}-{2949,3819,1,1,0}-{2949,3822,1,1,0}-{2955,3822,1,1,0}-{2957,3822,1,1,0}-{3016,9977,1,1,0}-{3021,9939,1,1,0}-{3024,9954,1,1,0}-{3025,9943,1,1,0}-{3025,9954,1,1,0}-{3026,9966,1,1,0}-{3032,9952,1,1,0}-{3039,9954,1,1,0}-{3044,9967,1,1,0}-{3044,9971,1,1,0}-{3057,9936,1,1,0}-{3059,9953,1,1,0}-{3041,9975,2,1,0}-{3043,9975,2,1,0}-{3045,9975,2,1,0}-{3016,10022,2,1,0}-{3017,10039,2,1,0}-{3027,10028,2,1,0}-{3029,10013,2,1,0}-{3036,10038,2,1,0}-{3037,10006,2,1,0}-{3041,10024,2,1,0}-{3043,10032,2,1,0}-{3057,10002,2,1,0}-{3058,10021,2,1,0}-{3065,10006,2,1,0}-{3027,10092,1,1,0}-{3035,10097,1,1,0}-{3051,10099,1,1,0}-{3427,5102,0,1,0}-{3428,5099,0,1,0}-{3428,5102,0,1,0}-{3430,5099,0,1,0}-" + }, + { + "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 354bfc6de..54f50f614 100644 --- a/Server/data/configs/object_configs.json +++ b/Server/data/configs/object_configs.json @@ -145,7 +145,7 @@ }, { "examine": "Used for storage.", - "ids": "63,64" + "ids": "63,64,36351,36352" }, { "examine": "Surprisingly sturdy looking.", @@ -968,7 +968,7 @@ "ids": "573" }, { - "examine": "A carving of a figure from the history of RuneScape.", + "examine": "A carving of a figure from the history of 2009Scape.", "ids": "574,575,576,577,578" }, { @@ -1753,7 +1753,7 @@ }, { "examine": "Danger!", - "ids": "1032,5127,29373" + "ids": "1032,5127" }, { "examine": "This tells you which way is which.", @@ -2148,7 +2148,7 @@ "ids": "1231" }, { - "examine": "One of the most common trees in RuneScape.", + "examine": "One of the most common trees in 2009Scape.", "ids": "1276" }, { @@ -2601,7 +2601,7 @@ }, { "examine": "I can climb this.", - "ids": "1729" + "ids": "1729,36347,36363,36644" }, { "examine": "Dare I go up?", @@ -3584,7 +3584,7 @@ "ids": "2375" }, { - "examine": "Soil", + "examine": "Soil. Dug up!", "ids": "2376,2377,2378" }, { @@ -3641,7 +3641,7 @@ }, { "examine": "I can climb down this.", - "ids": "2408" + "ids": "2408,36693,36694" }, { "examine": "A commonly found tree.", @@ -4964,7 +4964,7 @@ "ids": "3032" }, { - "examine": "One of the most common trees in RuneScape.", + "examine": "One of the most common trees in 2009Scape.", "ids": "3033,3034,3035,3036" }, { @@ -6384,12 +6384,12 @@ "ids": "4131" }, { - "examine": "These doors look very ominous. A sign says 'To the tombs'.", - "ids": "4132,4133" + "examine": "This tells you which way is which.", + "ids": "4132,4133,4134,4135" }, { - "examine": "Large doors set into the hillside.", - "ids": "4134,4135,4136,4137" + "examine": "These doors look very ominous. A sign says 'To the tombs'.", + "ids": "4136,4137" }, { "examine": "Items are for sale here.", @@ -6777,7 +6777,7 @@ }, { "examine": "A solid stone staircase.", - "ids": "4415,4416,4417,4418,4419,4420" + "ids": "4415,4416,4417,4418,4419,4420,36480,36481,36484,36495,36521,36523,36532,36540" }, { "examine": "A spiky barricade.", @@ -7609,7 +7609,7 @@ }, { "examine": "I can climb down this.", - "ids": "4911,4912" + "ids": "4911,4912,36349,36365" }, { "examine": "It looks cramped and dark.", @@ -8592,7 +8592,7 @@ "ids": "5789,5790" }, { - "examine": "A carving of a figure from the history of RuneScape.", + "examine": "A carving of a figure from the history of 2009Scape.", "ids": "5791,36749,36751" }, { @@ -12788,7 +12788,7 @@ "ids": "8972" }, { - "examine": "One of the most common trees in RuneScape.", + "examine": "One of the most common trees in 2009Scape.", "ids": "8973" }, { @@ -14100,7 +14100,7 @@ "ids": "10080" }, { - "examine": "One of the most common trees in RuneScape.", + "examine": "One of the most common trees in 2009Scape.", "ids": "10081" }, { @@ -15964,7 +15964,7 @@ "ids": "11354,11355" }, { - "examine": "A gateway back to RuneScape.", + "examine": "A gateway back to 2009Scape.", "ids": "11356" }, { @@ -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" }, { @@ -16564,7 +16564,7 @@ "ids": "11759" }, { - "examine": "A carving of a figure from the history of RuneScape.", + "examine": "A carving of a figure from the history of 2009Scape.", "ids": "11761" }, { @@ -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.", @@ -18728,7 +18728,7 @@ "ids": "13409,13410" }, { - "examine": "One of the most common trees in RuneScape.", + "examine": "One of the most common trees in 2009Scape.", "ids": "13411,13412" }, { @@ -18996,11 +18996,11 @@ "ids": "13525" }, { - "examine": "A map of RuneScape.", + "examine": "A map of 2009Scape.", "ids": "13526" }, { - "examine": "A map of RuneScape including major cave systems.", + "examine": "A map of 2009Scape including major cave systems.", "ids": "13527" }, { @@ -20007,6 +20007,46 @@ "examine": "Why do I never take time to look at the grass?", "ids": "14003" }, + { + "examine": "Well, I have the logs in place, now to get something to boil.", + "ids": "14005" + }, + { + "examine": "Covered in a thick layer of grease.", + "ids": "14006" + }, + { + "examine": "I'm glad this isn't for dinner.", + "ids": "14007" + }, + { + "examine": "This smells so very, very bad...", + "ids": "14008" + }, + { + "examine": "It seems to have all boiled away.", + "ids": "14009" + }, + { + "examine": "Stop examining signs! You're in the Wilderness now!", + "ids": "14503" + }, + { + "examine": "Danger, Wilderness up ahead. Last chance to turn back...", + "ids": "14504" + }, + { + "examine": "Danger, Wilderness up ahead. Watch out for the Beast!", + "ids": "14505" + }, + { + "examine": "Danger, Wilderness up ahead. Beware of stray magma.", + "ids": "14506" + }, + { + "examine": "Danger, Wilderness up ahead. Almost certain death!", + "ids": "14507" + }, { "examine": "Home sweet home?", "ids": "15477" @@ -20019,10 +20059,30 @@ "examine": "Home sweet home?", "ids": "15748" }, + { + "examine": "Danger - Possibly deadly creatures below!", + "ids": "16083,16151,16117" + }, + { + "examine": "Large urn.", + "ids": "17362" + }, { "examine": "A large fish.", "ids": "18482" }, + { + "examine": "A sign on a cactus.", + "ids": "18876" + }, + { + "examine": "Dead and half-buried.", + "ids": "19996" + }, + { + "examine": "Dead animal head. Lovely.", + "ids": "19997" + }, { "examine": "Might be worth opening?", "ids": "21299" @@ -20127,10 +20187,18 @@ "examine": "Contains traces of summoning energy.", "ids": "29939,29943,29944,29945,29947,29951,29952,29953,29954" }, + { + "examine": "An interesting sign.", + "ids": "31297,31298,31299,31300,31301" + }, { "examine": "I wonder what this spooky contains.", "ids": "37051" }, + { + "examine": "The mine has collapsed.", + "ids": "37634" + }, { "examine": "It really was this big!", "ids": "40043" @@ -20195,6 +20263,14 @@ "examine": "A wooden crate.", "ids": "15031" }, + { + "examine": "A sealed barrel with a warning sign on it.", + "ids": "17296" + }, + { + "examine": "A barrel with a warning sign on it.", + "ids": "17297" + }, { "examine": "A beautiful landscape.", "ids": "24184" @@ -20275,6 +20351,10 @@ "examine": "Technically a bed.", "ids": "32031" }, + { + "examine": "Now that's what I call slimline!", + "ids": "32072" + }, { "examine": "A really bad portrait of the King.", "ids": "32326" @@ -20293,7 +20373,7 @@ }, { "examine": "A wooden barrel for storage.", - "ids": "31136,36798" + "ids": "17308,31136,36798" }, { "examine": "A home for baby creatures.", @@ -20519,10 +20599,6 @@ "examine": "Popular on sandy beaches where fruity cocktails may be found.", "ids": "18856" }, - { - "examine": "Stop examining signs! You're in the Wilderness now!", - "ids": "14503" - }, { "examine": "Looks like part of the entrance is blocked by rubble.", "ids": "15767" @@ -20602,5 +20678,41 @@ { "examine": "Keeps the puppies out of trouble.", "ids": "28341" + }, + { + "examine": "Warning! Danger of death.", + "ids": "29373" + }, + { + "examine": "There are some toolboxes on this table.", + "ids": "36573,36580" + }, + { + "examine": "There's some rock I can use with the catapult here.", + "ids": "36574,36581" + }, + { + "examine": "There are some barricades here.", + "ids": "36575,36582" + }, + { + "examine": "There is some rope here.", + "ids": "36576,36583" + }, + { + "examine": "There are some explosive potions here.", + "ids": "36577,36584" + }, + { + "examine": "There are some pickaxes on this table.", + "ids": "36578,36585" + }, + { + "examine": "There are some bandages on this table.", + "ids": "36579,36586" + }, + { + "examine": "Rough but adequate.", + "ids": "14918" } ] \ No newline at end of file diff --git a/Server/data/configs/ranged_weapon_configs.json b/Server/data/configs/ranged_weapon_configs.json index a2790b612..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", @@ -1502,69 +1502,6 @@ "drop_ammo": "true", "ammunition": "882,883,5616,5622,598,884,885,5617,5623,2532,886,887,5618,5624,2534,888,889,5619,5625,2536,890,891,5620,5626,2538,892,893,5621,5627,2540,78,4160" }, - { - "itemId": "14671", - "name": "Armadyl crossbow", - "ammo_slot": "13", - "weapon_type": "1", - "animation": "4230", - "drop_ammo": "true", - "ammunition": "877,878,6061,6062,879,9236,9141,9288,9295,9302,9336,9239,9236,9237,9238,9239,9240,9241,9242,9243,9244,9245,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" - }, - { - "itemId": "14684", - "name": "Odium ward", - "ammo_slot": "13", - "weapon_type": "1", - "animation": "4230", - "drop_ammo": "true", - "ammunition": "8882,877,878,6061,6062,879,9236,9141,9288,9295,9302,9336,9239,13083,13084,13085,13086,9142,9289,9296,9303,9337,9240,9338,9241,9143,9290,9297,9304,9339,9242,9340,9243" - }, - { - "itemId": "14685", - "name": "Odium shard 1", - "ammo_slot": "13", - "weapon_type": "1", - "animation": "4230", - "drop_ammo": "true", - "ammunition": "877,878,6061,6062,879,9236" - }, - { - "itemId": "14803", - "name": "Dark bow", - "ammo_slot": "13", - "weapon_type": "2", - "animation": "426", - "drop_ammo": "true", - "ammunition": "882,883,5616,5622,598,884,885,5617,5623,2532,886,887,5618,5624,2534,888,889,5619,5625,2536,890,891,5620,5626,2538,892,893,5621,5627,2540,11212,11227,11228,11229,11217,78,4160" - }, - { - "itemId": "14804", - "name": "Dark bow", - "ammo_slot": "13", - "weapon_type": "2", - "animation": "426", - "drop_ammo": "true", - "ammunition": "882,883,5616,5622,598,884,885,5617,5623,2532,886,887,5618,5624,2534,888,889,5619,5625,2536,890,891,5620,5626,2538,892,893,5621,5627,2540,11212,11227,11228,11229,11217,78,4160" - }, - { - "itemId": "14805", - "name": "Dark bow", - "ammo_slot": "13", - "weapon_type": "2", - "animation": "426", - "drop_ammo": "true", - "ammunition": "882,883,5616,5622,598,884,885,5617,5623,2532,886,887,5618,5624,2534,888,889,5619,5625,2536,890,891,5620,5626,2538,892,893,5621,5627,2540,11212,11227,11228,11229,11217,78,4160" - }, - { - "itemId": "14806", - "name": "Dark bow", - "ammo_slot": "13", - "weapon_type": "2", - "animation": "426", - "drop_ammo": "true", - "ammunition": "882,883,5616,5622,598,884,885,5617,5623,2532,886,887,5618,5624,2534,888,889,5619,5625,2536,890,891,5620,5626,2538,892,893,5621,5627,2540,11212,11227,11228,11229,11217,78,4160" - }, { "itemId": "14192", "name": "Bow (class 1)", diff --git a/Server/data/configs/shops.json b/Server/data/configs/shops.json index 39f14cafa..d1c888651 100644 --- a/Server/data/configs/shops.json +++ b/Server/data/configs/shops.json @@ -45,22 +45,13 @@ "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}" }, { - "npcs": "590", + "npcs": "590,591", "high_alch": "0", "currency": "995", "general_store": "true", "id": "6", - "title": "Aemad's Adventuring Supplies Store", - "stock": "{227,300,100}-{1265,10,100}-{1349,10,100}-{2142,10,100}-{590,10,100}-{1759,100,100}-{882,1000,100}-{954,10,100}-{970,10,100}-{946,10,100}-{1935,10,100}" - }, - { - "npcs": "591", - "high_alch": "0", - "currency": "995", - "general_store": "true", - "id": "7", - "title": "Kortan General Store", - "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}" + "title": "Aemad's Adventuring Supplies", + "stock": "{227,10,100}-{1265,10,100}-{1349,10,100}-{2142,10,100}-{590,10,100}-{1759,100,100}-{882,1000,100}-{954,10,100}-{970,10,100}-{946,10,100}-{1935,10,100}" }, { "npcs": "971", @@ -222,7 +213,7 @@ "general_store": "true", "id": "25", "title": "Lumbridge General Store", - "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": "{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}-{1939,1000,1}" }, { "npcs": "4716", @@ -519,7 +510,7 @@ "general_store": "false", "id": "59", "title": "Fancy Clothes Store", - "stock": "{1949,10,100}-{958,10,100}-{948,10,100}-{1733,10,100}-{1734,1000,100}-{1059,30,100}-{1061,10,100}-{1757,10,100}-{1013,10,100}-{1025,10,100}-{10088,0,100}-{10090,0,100}-{10091,0,100}-{10089,0,100}-{10087,0,100}-{11525,0,100}" + "stock": "{1949,0,100}-{579,10,100}-{1023,10,100}-{958,10,100}-{948,10,100}-{1733,10,100}-{1734,1000,100}-{1059,30,100}-{1061,10,100}-{428,10,100}-{426,10,100}-{1757,10,100}-{1013,10,100}-{1015,10,100}-{1011,10,100}-{1007,10,100}-{1025,10,100}-{10088,0,100}-{10090,0,100}-{10091,0,100}-{10089,0,100}-{10087,0,100}-{11525,0,100}" }, { "npcs": "548", @@ -573,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", @@ -1112,7 +1103,7 @@ "currency": "995", "general_store": "false", "id": "126", - "title": "Pickaxe-is-mine", + "title": "Pickaxe-Is-Mine", "stock": "{1265,10,100}-{1269,10,100}-{1273,10,100}-{1271,10,100}-{1275,10,100}" }, { @@ -1251,13 +1242,13 @@ "stock": "{12204,10,100}-{12207,10,100}-{12210,10,100}-{12213,10,100}-{12216,10,100}-{12219,10,100}-{12222,10,100}-{12183,125000,100}-{12155,5000,100}" }, { - "npcs": "6996", + "npcs": "4472", "high_alch": "0", "currency": "995", "general_store": "false", "id": "142", "title": "Bogrog's Summoning Shop", - "stock": "{12204,10,100}-{12207,10,100}-{12210,10,100}-{12213,10,100}-{12216,10,100}-{12219,10,100}-{12222,10,100}-{12183,125000,100}-{12155,5000,100}" + "stock": "{12204,10,100}-{12207,10,100}-{12210,0,100}-{12213,0,100}-{12216,0,100}-{12219,0,100}-{12222,0,100}-{12183,65000,100}-{12155,5000,100}" }, { "npcs": "3671", @@ -1266,7 +1257,7 @@ "general_store": "false", "id": "143", "title": "Fortunato's Fine Wine", - "stock": "{1993,10,100}-{1937,10,100}-{7919,10,100}-{1935,10,100}" + "stock": "{1993,10,100}-{7810,10,100}-{7919,10,100}-{1935,10,100}" }, { "npcs": "558", @@ -1476,7 +1467,7 @@ "stock": "{8794,10,100}-{8790,300,100}-{4819,300,100}-{4820,300,100}-{1539,300,100}" }, { - "npcs": "1079", + "npcs": "1079,1357,1358", "high_alch": "0", "currency": "995", "general_store": "false", @@ -1608,7 +1599,7 @@ "general_store": "false", "id": "181", "title": "MoonClan Fine Clothes", - "stock": "{9068,10,100}-{9069,10,100}-{9070,10,100}-{9071,10,100}-{9072,10,100}-{9073,10,100}-{9074,10,100}-{1733,50,100}-{1734,500,100}" + "stock": "{9068,10,100}-{9069,10,100}-{9070,10,100}-{9071,10,100}-{9072,10,100}-{9073,10,100}-{9074,10,100}-{1733,50,100}-{1734,500,100}-{9084,10,100}-{9096,10,100}-{9097,10,100}-{9098,10,100}-{9099,10,100}-{9100,10,100}-{9101,10,100}-{9102,10,100}-{9104,10,100}" }, { "npcs": "", @@ -1662,7 +1653,7 @@ "general_store": "false", "id": "187", "title": "William's Wilderness Cape Shop", - "stock": "{4315,100,100}-{4335,100,100}-{4375,100,100}-{4395,100,100}" + "stock": "{4315,100,100}-{4335,100,100}-{4355,100,100}-{4375,100,100}-{4395,100,100}" }, { "npcs": "1779", @@ -1842,7 +1833,7 @@ "general_store": "false", "id": "216", "title": "Ore Store", - "stock": "{436,20,100}-{438,10,100}-{440,10,100}-{442,5,100}-{453,10,100}-{444,5,100}-{447,0,100}-{449,0,100}" + "stock": "{436,10,100}-{438,10,100}-{440,0,100}-{442,0,100}-{453,0,100}-{444,0,100}-{447,0,100}-{449,0,100}" }, { "npcs": "596", @@ -1891,12 +1882,13 @@ }, { "npcs": "2564", + "force_shared": "true", "high_alch": "0", "currency": "995", "general_store": "false", "id": "222", "title": "Ore Shop\b", - "stock": "{436,1500,100}-{438,1500,100}-{440,1500,100}-{442,1500,100}-{444,1500,100}-{447,1500,100}-{453,1500,100}" + "stock": "{436,0,0}-{438,0,0}-{440,0,0}-{442,0,0}-{444,0,0}-{447,0,0}-{453,0,0}" }, { "npcs": "603", @@ -2176,5 +2168,32 @@ "id": "254", "title": "Gabooty's Tai Bwo Wannai Drinky Store", "stock": "{2084,0,100}-{2092,0,100}-{2048,0,100}-{2054,0,100}-{2064,0,100}-{2074,0,100}" + }, + { + "npcs": "1526", + "high_alch": "0", + "currency": "4067", + "general_store": "false", + "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 e8b3049ed..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" @@ -25,16 +13,8 @@ "keys": "779607549,1253385498,-2146148208,-1240685073" }, { - "regionId": "6484", - "keys": "0,0,0,0" - }, - { - "regionId": "6485", - "keys": "0,0,0,0" - }, - { - "regionId": "6486", - "keys": "0,0,0,0" + "regionId": "6483", + "keys": "14881828,-6662814,58238456,146761213" }, { "regionId": "6487", @@ -60,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" @@ -117,33 +57,13 @@ "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": "6988", - "keys": "0,0,0,0" + "regionId": "6985", + "keys": "1193572731,-1288765743,-386862979,351599890" }, { "regionId": "6989", "keys": "-891749173,-113432144,-1228601828,695082558" }, - { - "regionId": "6990", - "keys": "0,0,0,0" - }, { "regionId": "6991", "keys": "-1448188175,626198836,1985989165,-1062391664" @@ -164,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" @@ -212,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" @@ -236,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" @@ -268,17 +128,9 @@ "regionId": "7496", "keys": "-1890766195,-149577199,919306397,-954784877" }, - { - "regionId": "7497", - "keys": "0,0,0,0" - }, - { - "regionId": "7498", - "keys": "0,0,0,0" - }, { "regionId": "7499", - "keys": "1187151706,718898064,1304406549,-1997198061" + "keys": "-2007696984,-1215288615,-1572330641,-2125382604" }, { "regionId": "7500", @@ -304,10 +156,6 @@ "regionId": "7505", "keys": "-1177354806,-1864641630,23736540,750869235" }, - { - "regionId": "7506", - "keys": "0,0,0,0" - }, { "regionId": "7507", "keys": "-1476331852,1093075535,-2003580146,734573751" @@ -324,6 +172,10 @@ "regionId": "7510", "keys": "2073894230,-553352720,274325814,-1461052837" }, + { + "regionId": "7511", + "keys": "14881828,-6662814,58238456,146761213" + }, { "regionId": "7513", "keys": "1066375195,548376104,-1771348528,1096433429" @@ -332,10 +184,6 @@ "regionId": "7745", "keys": "1212560091,-662504525,-1213494614,-1245629284" }, - { - "regionId": "7747", - "keys": "0,0,0,0" - }, { "regionId": "7748", "keys": "-850453392,-249468744,1635462867,100471784" @@ -344,10 +192,6 @@ "regionId": "7749", "keys": "369356966,-1038198212,282112206,802717032" }, - { - "regionId": "7750", - "keys": "0,0,0,0" - }, { "regionId": "7752", "keys": "1894158557,20416613,-1434347651,-1209846311" @@ -384,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" @@ -432,10 +244,6 @@ "regionId": "8002", "keys": "1597309277,469931771,1619035792,663887748" }, - { - "regionId": "8003", - "keys": "0,0,0,0" - }, { "regionId": "8004", "keys": "609107948,-755432209,-723012522,1175820911" @@ -480,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" @@ -520,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" @@ -544,6 +348,10 @@ "regionId": "8260", "keys": "-357122149,-546918388,1659548066,2023267334" }, + { + "regionId": "8261", + "keys": "14881828,-6662814,58238456,146761213" + }, { "regionId": "8262", "keys": "937196178,1968517712,1697292009,-2036678778" @@ -568,10 +376,6 @@ "regionId": "8267", "keys": "-474856962,-1295973639,789657650,-1769058462" }, - { - "regionId": "8268", - "keys": "0,0,0,0" - }, { "regionId": "8269", "keys": "-497435078,2028988145,486156819,-1178938803" @@ -593,20 +397,8 @@ "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" }, { "regionId": "8282", @@ -628,10 +420,6 @@ "regionId": "8499", "keys": "1158999293,2131770333,-861122128,1149793504" }, - { - "regionId": "8500", - "keys": "0,0,0,0" - }, { "regionId": "8506", "keys": "-1227747958,1491928866,1293676120,2065870654" @@ -708,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" @@ -748,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" @@ -788,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" @@ -824,10 +588,6 @@ "regionId": "8779", "keys": "-1680845199,-1149954104,-1177525481,-576614197" }, - { - "regionId": "8780", - "keys": "0,0,0,0" - }, { "regionId": "8781", "keys": "-2133721491,843228961,1706232103,-934369964" @@ -848,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" @@ -896,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" @@ -932,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" @@ -960,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" @@ -1012,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" @@ -1032,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" @@ -1109,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" @@ -1140,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" @@ -1160,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" @@ -1200,10 +864,6 @@ "regionId": "9362", "keys": "-253025041,376209206,1663580032,1115549308" }, - { - "regionId": "9363", - "keys": "0,0,0,0" - }, { "regionId": "9364", "keys": "1350964684,-1141243546,-158288919,1853454502" @@ -1216,10 +876,6 @@ "regionId": "9366", "keys": "-1691974727,-1591730841,1506117200,822346809" }, - { - "regionId": "9367", - "keys": "0,0,0,0" - }, { "regionId": "9368", "keys": "-911562498,-1433720091,257702084,1652121807" @@ -1240,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" @@ -1328,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" @@ -1344,10 +988,6 @@ "regionId": "9541", "keys": "1352006827,1096305726,805420370,-1426885864" }, - { - "regionId": "9542", - "keys": "0,0,0,0" - }, { "regionId": "9543", "keys": "1852273964,-104833248,-2069080639,-188650169" @@ -1368,10 +1008,6 @@ "regionId": "9547", "keys": "-1998382641,30700550,1548517068,-855708219" }, - { - "regionId": "9548", - "keys": "0,0,0,0" - }, { "regionId": "9549", "keys": "-1648136735,1402264941,639592027,348907048" @@ -1392,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" @@ -1424,10 +1048,6 @@ "regionId": "9562", "keys": "-1047298777,1197811918,663745433,-2098029011" }, - { - "regionId": "9619", - "keys": "0,0,0,0" - }, { "regionId": "9620", "keys": "1941157140,-1630547377,-1114648519,87057467" @@ -1456,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" @@ -1476,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" @@ -1560,6 +1156,10 @@ "regionId": "9789", "keys": "-620749099,528872615,1976629038,667155751" }, + { + "regionId": "9791", + "keys": "-2105683947,1913166182,-1697741730,-25818496" + }, { "regionId": "9792", "keys": "1500471532,1729075639,-304984137,1117230148" @@ -1568,10 +1168,6 @@ "regionId": "9794", "keys": "1788083183,2097372895,1633476682,-589223611" }, - { - "regionId": "9795", - "keys": "0,0,0,0" - }, { "regionId": "9796", "keys": "517509106,-2019799796,1066150754,-676371780" @@ -1580,10 +1176,6 @@ "regionId": "9797", "keys": "672333395,-284721288,262558790,-344798491" }, - { - "regionId": "9798", - "keys": "0,0,0,0" - }, { "regionId": "9799", "keys": "181886191,758691911,810524169,1456599388" @@ -1616,10 +1208,6 @@ "regionId": "9806", "keys": "368995667,940146211,957819918,-761335591" }, - { - "regionId": "9807", - "keys": "0,0,0,0" - }, { "regionId": "9808", "keys": "308766586,-214665927,-1255727041,-1034716346" @@ -1640,10 +1228,6 @@ "regionId": "9812", "keys": "50571970,-1762921782,-1667424270,524742325" }, - { - "regionId": "9813", - "keys": "0,0,0,0" - }, { "regionId": "9814", "keys": "-34428628,1940609264,-1638642159,-295486604" @@ -1688,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" @@ -1744,10 +1304,6 @@ "regionId": "10034", "keys": "-1435464304,-836439389,-788586899,1579582706" }, - { - "regionId": "10035", - "keys": "0,0,0,0" - }, { "regionId": "10036", "keys": "689954852,-1377739344,-847433475,-1883927289" @@ -1788,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" @@ -1828,10 +1376,6 @@ "regionId": "10059", "keys": "1185163869,-1047826729,-1461057337,139139001" }, - { - "regionId": "10060", - "keys": "0,0,0,0" - }, { "regionId": "10061", "keys": "1683880127,1851133137,-567572662,-1477799312" @@ -1840,10 +1384,6 @@ "regionId": "10062", "keys": "2024717410,-1807032706,1106507805,1426160484" }, - { - "regionId": "10063", - "keys": "0,0,0,0" - }, { "regionId": "10064", "keys": "1978019309,1433546802,-1989346624,-1611361553" @@ -1856,10 +1396,6 @@ "regionId": "10066", "keys": "105954939,-1741244981,-1491777272,207112205" }, - { - "regionId": "10067", - "keys": "0,0,0,0" - }, { "regionId": "10068", "keys": "-452138979,-1929816521,364996963,-1315825295" @@ -1936,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" @@ -1972,10 +1496,6 @@ "regionId": "10283", "keys": "-2133080221,1327669620,173304076,-151662318" }, - { - "regionId": "10284", - "keys": "0,0,0,0" - }, { "regionId": "10285", "keys": "-1317120534,1321149083,-1700628824,1028203235" @@ -2000,10 +1520,6 @@ "regionId": "10290", "keys": "-587752684,-318735232,336724431,-1931735346" }, - { - "regionId": "10291", - "keys": "0,0,0,0" - }, { "regionId": "10292", "keys": "933507835,1135929795,-1932059890,1492191263" @@ -2092,10 +1608,6 @@ "regionId": "10315", "keys": "-1034094191,1810212216,618808107,1905492210" }, - { - "regionId": "10316", - "keys": "0,0,0,0" - }, { "regionId": "10317", "keys": "216403117,1910251124,144162918,224240414" @@ -2104,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" @@ -2120,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" @@ -2192,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" @@ -2256,10 +1736,6 @@ "regionId": "10546", "keys": "192659093,263441429,29086759,670094037" }, - { - "regionId": "10547", - "keys": "0,0,0,0" - }, { "regionId": "10548", "keys": "-1157394711,-1306553144,2105373539,798383108" @@ -2316,10 +1792,6 @@ "regionId": "10564", "keys": "1279522133,380240348,604239496,1759916207" }, - { - "regionId": "10565", - "keys": "0,0,0,0" - }, { "regionId": "10566", "keys": "221004409,1368318650,-1237341956,25411913" @@ -2348,10 +1820,6 @@ "regionId": "10572", "keys": "-1375061166,-1411427241,-294467489,-2106748278" }, - { - "regionId": "10573", - "keys": "0,0,0,0" - }, { "regionId": "10574", "keys": "-1609693854,991245740,148787249,-1011766477" @@ -2360,10 +1828,6 @@ "regionId": "10575", "keys": "434036980,-780702800,-1042242727,-354464628" }, - { - "regionId": "10576", - "keys": "0,0,0,0" - }, { "regionId": "10577", "keys": "824897954,-1807466644,2079017570,1299975025" @@ -2377,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", @@ -2432,10 +1892,6 @@ "regionId": "10650", "keys": "-908578223,-911827513,1137573080,-419444146" }, - { - "regionId": "10651", - "keys": "0,0,0,0" - }, { "regionId": "10652", "keys": "1580722385,609745227,1407021351,1098485033" @@ -2444,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" @@ -2500,10 +1940,6 @@ "regionId": "10801", "keys": "850125851,1199386038,-1494136472,-1487577933" }, - { - "regionId": "10802", - "keys": "0,0,0,0" - }, { "regionId": "10803", "keys": "-1028081880,964144306,174845257,1906118817" @@ -2556,6 +1992,10 @@ "regionId": "10815", "keys": "2102925673,1133406202,-1885710196,165240951" }, + { + "regionId": "10816", + "keys": "-1909517714,218137505,744896894,831968418" + }, { "regionId": "10819", "keys": "-904401587,-2054970579,55120674,-1889562804" @@ -2596,10 +2036,6 @@ "regionId": "10828", "keys": "1665018619,-1607107877,-1997272567,1342325223" }, - { - "regionId": "10829", - "keys": "0,0,0,0" - }, { "regionId": "10830", "keys": "-608096960,58771202,1174291927,-1613596249" @@ -2620,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" @@ -2728,10 +2156,6 @@ "regionId": "10911", "keys": "948965123,-545528654,-459191491,-860302360" }, - { - "regionId": "10912", - "keys": "0,0,0,0" - }, { "regionId": "11049", "keys": "-621090199,-1004888612,-749707219,2052444337" @@ -2856,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" @@ -2888,10 +2296,6 @@ "regionId": "11091", "keys": "977844365,-245927723,99303766,-735341354" }, - { - "regionId": "11092", - "keys": "0,0,0,0" - }, { "regionId": "11093", "keys": "-379710910,-2088433559,1180928189,-54938349" @@ -2956,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" @@ -2992,10 +2388,6 @@ "regionId": "11167", "keys": "-1735216162,2141149053,57787230,-1263840909" }, - { - "regionId": "11168", - "keys": "0,0,0,0" - }, { "regionId": "11305", "keys": "-1429346525,-118378130,1002549662,1383588995" @@ -3072,10 +2464,6 @@ "regionId": "11323", "keys": "-1710808226,725482732,1863997544,5983834" }, - { - "regionId": "11324", - "keys": "0,0,0,0" - }, { "regionId": "11326", "keys": "1862093150,1696245244,1320714102,1241887579" @@ -3092,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" @@ -3136,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" @@ -3168,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" @@ -3192,10 +2544,6 @@ "regionId": "11410", "keys": "1509299526,-892391942,1485831814,1255407038" }, - { - "regionId": "11411", - "keys": "0,0,0,0" - }, { "regionId": "11412", "keys": "-1379772393,621580805,-316850307,1026773740" @@ -3300,10 +2648,6 @@ "regionId": "11572", "keys": "892185929,686162443,1793819023,-992991976" }, - { - "regionId": "11573", - "keys": "0,0,0,0" - }, { "regionId": "11574", "keys": "-1406155502,13961326,1071473330,1528558089" @@ -3320,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" @@ -3360,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" @@ -3384,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" @@ -3416,10 +2728,6 @@ "regionId": "11603", "keys": "-254635141,-1757221941,-2072225761,-752948578" }, - { - "regionId": "11604", - "keys": "0,0,0,0" - }, { "regionId": "11605", "keys": "57037316,1119657363,2040370510,801474589" @@ -3428,10 +2736,6 @@ "regionId": "11606", "keys": "-1790275662,-1050505330,-1496410655,679769180" }, - { - "regionId": "11607", - "keys": "0,0,0,0" - }, { "regionId": "11608", "keys": "248039728,1861834308,1335200688,317723667" @@ -3440,10 +2744,6 @@ "regionId": "11609", "keys": "-1581925228,-215021003,-1695323698,-1853661356" }, - { - "regionId": "11610", - "keys": "0,0,0,0" - }, { "regionId": "11612", "keys": "-1751954695,954770889,-337304771,986015401" @@ -3548,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" @@ -3616,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" @@ -3660,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" @@ -3728,10 +2968,6 @@ "regionId": "11926", "keys": "-501586879,235710413,-1122707717,-354940885" }, - { - "regionId": "11927", - "keys": "0,0,0,0" - }, { "regionId": "11928", "keys": "-445378710,-788377286,1363332226,627739779" @@ -3744,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" @@ -3764,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" @@ -3860,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" @@ -3908,10 +3104,6 @@ "regionId": "12111", "keys": "-746445771,1759120362,1253848335,1433199232" }, - { - "regionId": "12112", - "keys": "0,0,0,0" - }, { "regionId": "12113", "keys": "-1749952613,1569535680,1900425482,-1993748284" @@ -3920,10 +3112,6 @@ "regionId": "12115", "keys": "2146362785,1996258091,-613445101,539756591" }, - { - "regionId": "12116", - "keys": "0,0,0,0" - }, { "regionId": "12117", "keys": "-97063310,1962833557,-849078816,-1560293275" @@ -3960,10 +3148,6 @@ "regionId": "12182", "keys": "-499997333,1910872683,-234510566,-361238419" }, - { - "regionId": "12183", - "keys": "0,0,0,0" - }, { "regionId": "12184", "keys": "-1993918550,1977224805,-1048356116,-1578331413" @@ -3976,10 +3160,6 @@ "regionId": "12186", "keys": "-259674875,-949495016,1835821953,1740556829" }, - { - "regionId": "12187", - "keys": "0,0,0,0" - }, { "regionId": "12188", "keys": "22801476,-1237575758,2139201694,-163382547" @@ -3988,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" @@ -4096,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" @@ -4188,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" @@ -4308,10 +3408,6 @@ "regionId": "12614", "keys": "1621048325,1179863793,357058137,-1896311701" }, - { - "regionId": "12615", - "keys": "0,0,0,0" - }, { "regionId": "12616", "keys": "1951523008,-2131232747,-381301931,2007660709" @@ -4324,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" @@ -4364,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" @@ -4392,10 +3464,6 @@ "regionId": "12691", "keys": "-998717750,-178246764,973015517,-1732934591" }, - { - "regionId": "12692", - "keys": "0,0,0,0" - }, { "regionId": "12693", "keys": "1339198001,799729854,-750687990,1703141753" @@ -4404,10 +3472,6 @@ "regionId": "12694", "keys": "631068451,-510806123,2026757696,207332608" }, - { - "regionId": "12695", - "keys": "0,0,0,0" - }, { "regionId": "12696", "keys": "690970369,-466418236,-666391415,1091047140" @@ -4428,10 +3492,6 @@ "regionId": "12700", "keys": "-1802430094,1220107446,-1897971895,-1437873968" }, - { - "regionId": "12842", - "keys": "0,0,0,0" - }, { "regionId": "12843", "keys": "-203885401,22803731,234561826,140244374" @@ -4460,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" @@ -4525,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", @@ -4620,10 +3652,6 @@ "regionId": "12956", "keys": "-1383918319,-1190517730,-1037058957,534994622" }, - { - "regionId": "13098", - "keys": "0,0,0,0" - }, { "regionId": "13099", "keys": "919393388,-1011543647,454982828,-809340931" @@ -4648,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" @@ -4716,33 +3736,21 @@ "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" }, { "regionId": "13140", @@ -4780,10 +3788,6 @@ "regionId": "13203", "keys": "-1342258113,-2106603709,395365404,-973258459" }, - { - "regionId": "13204", - "keys": "0,0,0,0" - }, { "regionId": "13205", "keys": "1250202258,-2009534633,1981493488,10068968" @@ -4792,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" @@ -4909,24 +3905,8 @@ "keys": "50769406,1445159687,1657343198,-1977600563" }, { - "regionId": "13388", - "keys": "0,0,0,0" - }, - { - "regionId": "13389", - "keys": "0,0,0,0" - }, - { - "regionId": "13397", - "keys": "0,0,0,0" - }, - { - "regionId": "13398", - "keys": "0,0,0,0" - }, - { - "regionId": "13399", - "keys": "0,0,0,0" + "regionId": "13393", + "keys": "14881828,-6662814,58238456,146761213" }, { "regionId": "13456", @@ -4972,10 +3952,6 @@ "regionId": "13466", "keys": "-460228398,-76181685,2062962515,1167974062" }, - { - "regionId": "13467", - "keys": "0,0,0,0" - }, { "regionId": "13610", "keys": "-1259340500,2094890289,-1255134464,1578867778" @@ -5024,10 +4000,6 @@ "regionId": "13621", "keys": "-1291167236,-1205011126,-1949699235,1530879202" }, - { - "regionId": "13622", - "keys": "0,0,0,0" - }, { "regionId": "13623", "keys": "-1700397822,-1427942931,1638091118,438962861" @@ -5072,10 +4044,6 @@ "regionId": "13643", "keys": "1113875885,-236852397,193142585,-854043613" }, - { - "regionId": "13644", - "keys": "0,0,0,0" - }, { "regionId": "13650", "keys": "-1281992532,-252178835,-207196406,32900092" @@ -5088,10 +4056,6 @@ "regionId": "13713", "keys": "676853493,-1887443315,-998811191,1947183065" }, - { - "regionId": "13715", - "keys": "0,0,0,0" - }, { "regionId": "13716", "keys": "-913580283,340446282,893998558,589419388" @@ -5116,10 +4080,6 @@ "regionId": "13721", "keys": "-1188425330,1307846097,888707126,-1843934128" }, - { - "regionId": "13722", - "keys": "0,0,0,0" - }, { "regionId": "13723", "keys": "-310574621,110640497,1685108265,-1931806505" @@ -5136,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" @@ -5172,10 +4120,6 @@ "regionId": "13877", "keys": "1498185652,2064367906,152992439,585271079" }, - { - "regionId": "13878", - "keys": "0,0,0,0" - }, { "regionId": "13879", "keys": "-2101514338,-1139447054,51825149,-512907347" @@ -5196,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" @@ -5216,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" @@ -5256,10 +4180,6 @@ "regionId": "13980", "keys": "-12957944,-743456598,2041601862,618819565" }, - { - "regionId": "14127", - "keys": "0,0,0,0" - }, { "regionId": "14128", "keys": "1907787432,-241593923,1152767085,-541422403" @@ -5296,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" @@ -5324,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" @@ -5364,6 +4256,10 @@ "regionId": "14236", "keys": "816141215,-2114312227,1578950035,1471842311" }, + { + "regionId": "14380", + "keys": "1663518625,-107913441,-1625821936,330353351" + }, { "regionId": "14381", "keys": "-534048527,1614741928,-469012711,1064350100" @@ -5396,10 +4292,6 @@ "regionId": "14388", "keys": "2048238173,2003642395,1658667134,411519881" }, - { - "regionId": "14389", - "keys": "0,0,0,0" - }, { "regionId": "14390", "keys": "-1631704360,1281079278,-1549067757,-2034268623" @@ -5412,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" @@ -5432,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" @@ -5452,6 +4324,10 @@ "regionId": "14492", "keys": "420310205,1004898846,889537686,13961361" }, + { + "regionId": "14636", + "keys": "-1554034534,-545576842,-416921234,1887723532" + }, { "regionId": "14637", "keys": "112774068,-1867270219,-442076728,-1436438131" @@ -5472,10 +4348,6 @@ "regionId": "14641", "keys": "-310201697,1724559662,90126688,-664432561" }, - { - "regionId": "14645", - "keys": "0,0,0,0" - }, { "regionId": "14646", "keys": "-2069357113,-2117603296,1620573021,-239596308" @@ -5488,10 +4360,6 @@ "regionId": "14648", "keys": "-1757352130,-1996516705,263017190,1647356258" }, - { - "regionId": "14745", - "keys": "0,0,0,0" - }, { "regionId": "14746", "keys": "2081288932,-143900986,-1978370486,-544919360" @@ -5524,10 +4392,6 @@ "regionId": "14896", "keys": "548765086,801664009,-1716752945,-1492954302" }, - { - "regionId": "14901", - "keys": "0,0,0,0" - }, { "regionId": "14902", "keys": "1158948283,-12013143,1242958376,-1265402384" @@ -5548,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/data/eco/.gitignore b/Server/data/eco/.gitignore new file mode 100644 index 000000000..5e7d2734c --- /dev/null +++ b/Server/data/eco/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore diff --git a/Server/data/eco/autostock.json b/Server/data/eco/autostock.json deleted file mode 100644 index 09fa93713..000000000 --- a/Server/data/eco/autostock.json +++ /dev/null @@ -1,69 +0,0 @@ -{ - "offers": [ - { - "__comment" : "coal", - "item" : "453", - "qty" : "1000" - }, - { - "__comment" : "vial", - "item" : "227", - "qty" : "10000" - }, - { - "__comment" : "water vial", - "item" : "229", - "qty" : "10000" - }, - { - "__comment" : "cooked lobster", - "item" : "379", - "qty" : "10000" - }, - { - "__comment" : "Ashes", - "item" : "592", - "qty" : "1000" - }, - { - "__comment" : "Mort myre fungi", - "item" : "2970", - "qty" : "1000" - }, - { - "__comment" : "Poison ivy berries", - "item" : "6018", - "qty" : "1000" - }, - { - "__comment" : "Potato cactus", - "item" : "3138", - "qty" : "1000" - }, - { - "__comment" : "Unicorn horn", - "item" : "237", - "qty" : "1000" - }, - { - "__comment" : "Red spider eggs", - "item" : "223", - "qty" : "1000" - }, - { - "__comment" : "Bird nest", - "item" : "6693", - "qty" : "1000" - }, - { - "__comment" : "Chocolate dust", - "item" : "1975", - "qty" : "1000" - }, - { - "__comment" : "Limpwurt root", - "item" : "225", - "qty" : "10000" - } - ] -} diff --git a/Server/data/eco/ge_resource.emp b/Server/data/eco/ge_resource.emp deleted file mode 100644 index f96c401f3..000000000 --- a/Server/data/eco/ge_resource.emp +++ /dev/null @@ -1 +0,0 @@ -ÿÿ \ No newline at end of file diff --git a/Server/data/eco/gedb.xml b/Server/data/eco/gedb.xml deleted file mode 100644 index 4f89c9973..000000000 --- a/Server/data/eco/gedb.xml +++ /dev/null @@ -1,102497 +0,0 @@ - - - - 0 - 1 - 0 - 0 - 0 - - - 1 - 1 - 0 - 0 - 0 - - - 2 - 5 - 0 - 0 - 0 - - - 3 - 1 - 0 - 0 - 0 - - - 4 - 5 - 0 - 0 - 0 - - - 5 - 10 - 0 - 0 - 0 - - - 6 - 187500 - 0 - 0 - 0 - - - 7 - 187500 - 0 - 0 - 0 - - - 8 - 187500 - 0 - 0 - 0 - - - 9 - 187500 - 0 - 0 - 0 - - - 10 - 187500 - 0 - 0 - 0 - - - 11 - 187500 - 0 - 0 - 0 - - - 12 - 187500 - 0 - 0 - 0 - - - 13 - 187500 - 0 - 0 - 0 - - - 14 - 1 - 0 - 0 - 0 - - - 15 - 10 - 0 - 0 - 0 - - - 16 - 10 - 0 - 0 - 0 - - - 17 - 10 - 0 - 0 - 0 - - - 18 - 2 - 0 - 0 - 0 - - - 19 - 3 - 0 - 0 - 0 - - - 20 - 1 - 0 - 0 - 0 - - - 21 - 1 - 0 - 0 - 0 - - - 22 - 1 - 0 - 0 - 0 - - - 23 - 1 - 0 - 0 - 0 - - - 24 - 1 - 0 - 0 - 0 - - - 25 - 1 - 0 - 0 - 0 - - - 26 - 1 - 0 - 0 - 0 - - - 27 - 1 - 0 - 0 - 0 - - - 28 - 3 - 0 - 0 - 0 - - - 29 - 1 - 0 - 0 - 0 - - - 30 - 6 - 0 - 0 - 0 - - - 31 - 1 - 0 - 0 - 0 - - - 32 - 3 - 0 - 0 - 0 - - - 33 - 3 - 0 - 0 - 0 - - - 34 - 3 - 0 - 0 - 0 - - - 35 - 200 - 0 - 0 - 0 - - - 36 - 3 - 0 - 0 - 0 - - - 37 - 3 - 0 - 0 - 0 - - - 38 - 3 - 0 - 0 - 0 - - - 39 - 10 - 0 - 0 - 0 - - - 40 - 26 - 0 - 0 - 0 - - - 41 - 36 - 0 - 0 - 0 - - - 42 - 68 - 0 - 0 - 0 - - - 43 - 160 - 0 - 0 - 0 - - - 44 - 460 - 0 - 0 - 0 - - - 45 - 7 - 0 - 0 - 0 - - - 46 - 12 - 0 - 0 - 0 - - - 47 - 95 - 0 - 0 - 0 - - - 48 - 60 - 0 - 0 - 0 - - - 49 - 60 - 0 - 0 - 0 - - - 50 - 23 - 0 - 0 - 0 - - - 51 - 23 - 0 - 0 - 0 - - - 52 - 12 - 0 - 0 - 0 - - - 53 - 1 - 0 - 0 - 0 - - - 54 - 50 - 0 - 0 - 0 - - - 55 - 50 - 0 - 0 - 0 - - - 56 - 80 - 0 - 0 - 0 - - - 57 - 80 - 0 - 0 - 0 - - - 58 - 160 - 0 - 0 - 0 - - - 59 - 160 - 0 - 0 - 0 - - - 60 - 100 - 0 - 0 - 0 - - - 61 - 100 - 0 - 0 - 0 - - - 62 - 320 - 0 - 0 - 0 - - - 63 - 320 - 0 - 0 - 0 - - - 64 - 200 - 0 - 0 - 0 - - - 65 - 200 - 0 - 0 - 0 - - - 66 - 640 - 0 - 0 - 0 - - - 67 - 640 - 0 - 0 - 0 - - - 68 - 400 - 0 - 0 - 0 - - - 69 - 400 - 0 - 0 - 0 - - - 70 - 1280 - 0 - 0 - 0 - - - 71 - 1280 - 0 - 0 - 0 - - - 72 - 800 - 0 - 0 - 0 - - - 73 - 800 - 0 - 0 - 0 - - - 74 - 10 - 0 - 0 - 0 - - - 75 - 12 - 0 - 0 - 0 - - - 76 - 1 - 0 - 0 - 0 - - - 77 - 5 - 0 - 0 - 0 - - - 78 - 2 - 0 - 0 - 0 - - - 79 - 1 - 0 - 0 - 0 - - - 80 - 1 - 0 - 0 - 0 - - - 81 - 1 - 0 - 0 - 0 - - - 82 - 1 - 0 - 0 - 0 - - - 83 - 15 - 0 - 0 - 0 - - - 84 - 15 - 0 - 0 - 0 - - - 85 - 1 - 0 - 0 - 0 - - - 86 - 12 - 0 - 0 - 0 - - - 87 - 12 - 0 - 0 - 0 - - - 88 - 6 - 0 - 0 - 0 - - - 89 - 6 - 0 - 0 - 0 - - - 90 - 5 - 0 - 0 - 0 - - - 91 - 3 - 0 - 0 - 0 - - - 92 - 3 - 0 - 0 - 0 - - - 93 - 5 - 0 - 0 - 0 - - - 94 - 5 - 0 - 0 - 0 - - - 95 - 11 - 0 - 0 - 0 - - - 96 - 11 - 0 - 0 - 0 - - - 97 - 20 - 0 - 0 - 0 - - - 98 - 20 - 0 - 0 - 0 - - - 99 - 25 - 0 - 0 - 0 - - - 100 - 25 - 0 - 0 - 0 - - - 101 - 40 - 0 - 0 - 0 - - - 102 - 40 - 0 - 0 - 0 - - - 103 - 48 - 0 - 0 - 0 - - - 104 - 48 - 0 - 0 - 0 - - - 105 - 54 - 0 - 0 - 0 - - - 106 - 54 - 0 - 0 - 0 - - - 107 - 65 - 0 - 0 - 0 - - - 108 - 65 - 0 - 0 - 0 - - - 109 - 70 - 0 - 0 - 0 - - - 110 - 70 - 0 - 0 - 0 - - - 111 - 25 - 0 - 0 - 0 - - - 112 - 25 - 0 - 0 - 0 - - - 113 - 1500 - 0 - 0 - 0 - - - 114 - 1500 - 0 - 0 - 0 - - - 115 - 1200 - 0 - 0 - 0 - - - 116 - 1200 - 0 - 0 - 0 - - - 117 - 900 - 0 - 0 - 0 - - - 118 - 900 - 0 - 0 - 0 - - - 119 - 600 - 0 - 0 - 0 - - - 120 - 600 - 0 - 0 - 0 - - - 121 - 560 - 0 - 0 - 0 - - - 122 - 560 - 0 - 0 - 0 - - - 123 - 420 - 0 - 0 - 0 - - - 124 - 420 - 0 - 0 - 0 - - - 125 - 280 - 0 - 0 - 0 - - - 126 - 280 - 0 - 0 - 0 - - - 127 - 88 - 0 - 0 - 0 - - - 128 - 88 - 0 - 0 - 0 - - - 129 - 66 - 0 - 0 - 0 - - - 130 - 66 - 0 - 0 - 0 - - - 131 - 44 - 0 - 0 - 0 - - - 132 - 44 - 0 - 0 - 0 - - - 133 - 880 - 0 - 0 - 0 - - - 134 - 880 - 0 - 0 - 0 - - - 135 - 660 - 0 - 0 - 0 - - - 136 - 660 - 0 - 0 - 0 - - - 137 - 440 - 0 - 0 - 0 - - - 138 - 440 - 0 - 0 - 0 - - - 139 - 152 - 0 - 0 - 0 - - - 140 - 152 - 0 - 0 - 0 - - - 141 - 114 - 0 - 0 - 0 - - - 142 - 114 - 0 - 0 - 0 - - - 143 - 76 - 0 - 0 - 0 - - - 144 - 76 - 0 - 0 - 0 - - - 145 - 180 - 0 - 0 - 0 - - - 146 - 180 - 0 - 0 - 0 - - - 147 - 135 - 0 - 0 - 0 - - - 148 - 135 - 0 - 0 - 0 - - - 149 - 90 - 0 - 0 - 0 - - - 150 - 90 - 0 - 0 - 0 - - - 151 - 200 - 0 - 0 - 0 - - - 152 - 200 - 0 - 0 - 0 - - - 153 - 150 - 0 - 0 - 0 - - - 154 - 150 - 0 - 0 - 0 - - - 155 - 100 - 0 - 0 - 0 - - - 156 - 100 - 0 - 0 - 0 - - - 157 - 220 - 0 - 0 - 0 - - - 158 - 220 - 0 - 0 - 0 - - - 159 - 165 - 0 - 0 - 0 - - - 160 - 165 - 0 - 0 - 0 - - - 161 - 110 - 0 - 0 - 0 - - - 162 - 110 - 0 - 0 - 0 - - - 163 - 264 - 0 - 0 - 0 - - - 164 - 264 - 0 - 0 - 0 - - - 165 - 198 - 0 - 0 - 0 - - - 166 - 198 - 0 - 0 - 0 - - - 167 - 132 - 0 - 0 - 0 - - - 168 - 132 - 0 - 0 - 0 - - - 169 - 288 - 0 - 0 - 0 - - - 170 - 288 - 0 - 0 - 0 - - - 171 - 216 - 0 - 0 - 0 - - - 172 - 216 - 0 - 0 - 0 - - - 173 - 144 - 0 - 0 - 0 - - - 174 - 144 - 0 - 0 - 0 - - - 175 - 288 - 0 - 0 - 0 - - - 176 - 288 - 0 - 0 - 0 - - - 177 - 216 - 0 - 0 - 0 - - - 178 - 216 - 0 - 0 - 0 - - - 179 - 144 - 0 - 0 - 0 - - - 180 - 144 - 0 - 0 - 0 - - - 181 - 288 - 0 - 0 - 0 - - - 182 - 288 - 0 - 0 - 0 - - - 183 - 216 - 0 - 0 - 0 - - - 184 - 216 - 0 - 0 - 0 - - - 185 - 144 - 0 - 0 - 0 - - - 186 - 144 - 0 - 0 - 0 - - - 187 - 144 - 0 - 0 - 0 - - - 188 - 144 - 0 - 0 - 0 - - - 189 - 175 - 0 - 0 - 0 - - - 190 - 175 - 0 - 0 - 0 - - - 191 - 150 - 0 - 0 - 0 - - - 192 - 150 - 0 - 0 - 0 - - - 193 - 125 - 0 - 0 - 0 - - - 194 - 125 - 0 - 0 - 0 - - - 195 - 1 - 0 - 0 - 0 - - - 196 - 1 - 0 - 0 - 0 - - - 197 - 20 - 0 - 0 - 0 - - - 198 - 20 - 0 - 0 - 0 - - - 199 - 1 - 0 - 0 - 0 - - - 200 - 1 - 0 - 0 - 0 - - - 201 - 1 - 0 - 0 - 0 - - - 202 - 1 - 0 - 0 - 0 - - - 203 - 1 - 0 - 0 - 0 - - - 204 - 1 - 0 - 0 - 0 - - - 205 - 1 - 0 - 0 - 0 - - - 206 - 1 - 0 - 0 - 0 - - - 207 - 1 - 0 - 0 - 0 - - - 208 - 1 - 0 - 0 - 0 - - - 209 - 1 - 0 - 0 - 0 - - - 210 - 1 - 0 - 0 - 0 - - - 211 - 1 - 0 - 0 - 0 - - - 212 - 1 - 0 - 0 - 0 - - - 213 - 1 - 0 - 0 - 0 - - - 214 - 1 - 0 - 0 - 0 - - - 215 - 1 - 0 - 0 - 0 - - - 216 - 1 - 0 - 0 - 0 - - - 217 - 1 - 0 - 0 - 0 - - - 218 - 1 - 0 - 0 - 0 - - - 219 - 1 - 0 - 0 - 0 - - - 220 - 1 - 0 - 0 - 0 - - - 221 - 3 - 0 - 0 - 0 - - - 222 - 3 - 0 - 0 - 0 - - - 223 - 7 - 0 - 0 - 0 - - - 224 - 7 - 0 - 0 - 0 - - - 225 - 7 - 0 - 0 - 0 - - - 226 - 7 - 0 - 0 - 0 - - - 227 - 10 - 0 - 0 - 0 - - - 228 - 10 - 0 - 0 - 0 - - - 229 - 5 - 0 - 0 - 0 - - - 230 - 5 - 0 - 0 - 0 - - - 231 - 10 - 0 - 0 - 0 - - - 232 - 10 - 0 - 0 - 0 - - - 233 - 4 - 0 - 0 - 0 - - - 234 - 4 - 0 - 0 - 0 - - - 235 - 20 - 0 - 0 - 0 - - - 236 - 20 - 0 - 0 - 0 - - - 237 - 20 - 0 - 0 - 0 - - - 238 - 20 - 0 - 0 - 0 - - - 239 - 10 - 0 - 0 - 0 - - - 240 - 10 - 0 - 0 - 0 - - - 241 - 52 - 0 - 0 - 0 - - - 242 - 52 - 0 - 0 - 0 - - - 243 - 50 - 0 - 0 - 0 - - - 244 - 50 - 0 - 0 - 0 - - - 245 - 1 - 0 - 0 - 0 - - - 246 - 1 - 0 - 0 - 0 - - - 247 - 1 - 0 - 0 - 0 - - - 248 - 1 - 0 - 0 - 0 - - - 249 - 3 - 0 - 0 - 0 - - - 250 - 3 - 0 - 0 - 0 - - - 251 - 5 - 0 - 0 - 0 - - - 252 - 5 - 0 - 0 - 0 - - - 253 - 11 - 0 - 0 - 0 - - - 254 - 11 - 0 - 0 - 0 - - - 255 - 20 - 0 - 0 - 0 - - - 256 - 20 - 0 - 0 - 0 - - - 257 - 25 - 0 - 0 - 0 - - - 258 - 25 - 0 - 0 - 0 - - - 259 - 40 - 0 - 0 - 0 - - - 260 - 40 - 0 - 0 - 0 - - - 261 - 48 - 0 - 0 - 0 - - - 262 - 48 - 0 - 0 - 0 - - - 263 - 54 - 0 - 0 - 0 - - - 264 - 54 - 0 - 0 - 0 - - - 265 - 65 - 0 - 0 - 0 - - - 266 - 65 - 0 - 0 - 0 - - - 267 - 70 - 0 - 0 - 0 - - - 268 - 70 - 0 - 0 - 0 - - - 269 - 75 - 0 - 0 - 0 - - - 270 - 75 - 0 - 0 - 0 - - - 271 - 1 - 0 - 0 - 0 - - - 272 - 1 - 0 - 0 - 0 - - - 273 - 1 - 0 - 0 - 0 - - - 274 - 1 - 0 - 0 - 0 - - - 275 - 1 - 0 - 0 - 0 - - - 276 - 3 - 0 - 0 - 0 - - - 277 - 3 - 0 - 0 - 0 - - - 278 - 3 - 0 - 0 - 0 - - - 279 - 3 - 0 - 0 - 0 - - - 280 - 1 - 0 - 0 - 0 - - - 281 - 1 - 0 - 0 - 0 - - - 282 - 1 - 0 - 0 - 0 - - - 283 - 1 - 0 - 0 - 0 - - - 284 - 50 - 0 - 0 - 0 - - - 285 - 50 - 0 - 0 - 0 - - - 286 - 40 - 0 - 0 - 0 - - - 287 - 40 - 0 - 0 - 0 - - - 288 - 40 - 0 - 0 - 0 - - - 289 - 40 - 0 - 0 - 0 - - - 290 - 2 - 0 - 0 - 0 - - - 291 - 5 - 0 - 0 - 0 - - - 292 - 2 - 0 - 0 - 0 - - - 293 - 1 - 0 - 0 - 0 - - - 294 - 1 - 0 - 0 - 0 - - - 295 - 1 - 0 - 0 - 0 - - - 296 - 1 - 0 - 0 - 0 - - - 297 - 1 - 0 - 0 - 0 - - - 298 - 1 - 0 - 0 - 0 - - - 299 - 300 - 0 - 0 - 0 - - - 300 - 3 - 0 - 0 - 0 - - - 301 - 20 - 0 - 0 - 0 - - - 302 - 20 - 0 - 0 - 0 - - - 303 - 5 - 0 - 0 - 0 - - - 304 - 5 - 0 - 0 - 0 - - - 305 - 20 - 0 - 0 - 0 - - - 306 - 20 - 0 - 0 - 0 - - - 307 - 5 - 0 - 0 - 0 - - - 308 - 5 - 0 - 0 - 0 - - - 309 - 5 - 0 - 0 - 0 - - - 310 - 5 - 0 - 0 - 0 - - - 311 - 5 - 0 - 0 - 0 - - - 312 - 5 - 0 - 0 - 0 - - - 313 - 3 - 0 - 0 - 0 - - - 314 - 6 - 0 - 0 - 0 - - - 315 - 5 - 0 - 0 - 0 - - - 316 - 5 - 0 - 0 - 0 - - - 317 - 6 - 0 - 0 - 0 - - - 318 - 6 - 0 - 0 - 0 - - - 319 - 15 - 0 - 0 - 0 - - - 320 - 15 - 0 - 0 - 0 - - - 321 - 16 - 0 - 0 - 0 - - - 322 - 16 - 0 - 0 - 0 - - - 323 - 1 - 0 - 0 - 0 - - - 324 - 1 - 0 - 0 - 0 - - - 325 - 10 - 0 - 0 - 0 - - - 326 - 10 - 0 - 0 - 0 - - - 327 - 10 - 0 - 0 - 0 - - - 328 - 10 - 0 - 0 - 0 - - - 329 - 88 - 0 - 0 - 0 - - - 330 - 88 - 0 - 0 - 0 - - - 331 - 92 - 0 - 0 - 0 - - - 332 - 92 - 0 - 0 - 0 - - - 333 - 68 - 0 - 0 - 0 - - - 334 - 68 - 0 - 0 - 0 - - - 335 - 20 - 0 - 0 - 0 - - - 336 - 20 - 0 - 0 - 0 - - - 337 - 50 - 0 - 0 - 0 - - - 338 - 50 - 0 - 0 - 0 - - - 339 - 26 - 0 - 0 - 0 - - - 340 - 26 - 0 - 0 - 0 - - - 341 - 34 - 0 - 0 - 0 - - - 342 - 34 - 0 - 0 - 0 - - - 343 - 1 - 0 - 0 - 0 - - - 344 - 1 - 0 - 0 - 0 - - - 345 - 16 - 0 - 0 - 0 - - - 346 - 16 - 0 - 0 - 0 - - - 347 - 15 - 0 - 0 - 0 - - - 348 - 15 - 0 - 0 - 0 - - - 349 - 25 - 0 - 0 - 0 - - - 350 - 25 - 0 - 0 - 0 - - - 351 - 25 - 0 - 0 - 0 - - - 352 - 25 - 0 - 0 - 0 - - - 353 - 26 - 0 - 0 - 0 - - - 354 - 26 - 0 - 0 - 0 - - - 355 - 17 - 0 - 0 - 0 - - - 356 - 17 - 0 - 0 - 0 - - - 357 - 1 - 0 - 0 - 0 - - - 358 - 1 - 0 - 0 - 0 - - - 359 - 120 - 0 - 0 - 0 - - - 360 - 120 - 0 - 0 - 0 - - - 361 - 120 - 0 - 0 - 0 - - - 362 - 120 - 0 - 0 - 0 - - - 363 - 280 - 0 - 0 - 0 - - - 364 - 280 - 0 - 0 - 0 - - - 365 - 270 - 0 - 0 - 0 - - - 366 - 270 - 0 - 0 - 0 - - - 367 - 1 - 0 - 0 - 0 - - - 368 - 1 - 0 - 0 - 0 - - - 369 - 1 - 0 - 0 - 0 - - - 370 - 1 - 0 - 0 - 0 - - - 371 - 340 - 0 - 0 - 0 - - - 372 - 340 - 0 - 0 - 0 - - - 373 - 400 - 0 - 0 - 0 - - - 374 - 400 - 0 - 0 - 0 - - - 375 - 1 - 0 - 0 - 0 - - - 376 - 1 - 0 - 0 - 0 - - - 377 - 284 - 0 - 0 - 0 - - - 378 - 284 - 0 - 0 - 0 - - - 379 - 268 - 173 - 57782 - 0 - - - 380 - 268 - 0 - 0 - 0 - - - 381 - 1 - 0 - 0 - 0 - - - 382 - 1 - 0 - 0 - 0 - - - 383 - 300 - 0 - 0 - 0 - - - 384 - 300 - 0 - 0 - 0 - - - 385 - 300 - 0 - 0 - 0 - - - 386 - 300 - 0 - 0 - 0 - - - 387 - 1 - 0 - 0 - 0 - - - 388 - 1 - 0 - 0 - 0 - - - 389 - 500 - 0 - 0 - 0 - - - 390 - 500 - 0 - 0 - 0 - - - 391 - 500 - 0 - 0 - 0 - - - 392 - 500 - 0 - 0 - 0 - - - 393 - 1 - 0 - 0 - 0 - - - 394 - 1 - 0 - 0 - 0 - - - 395 - 500 - 0 - 0 - 0 - - - 396 - 500 - 0 - 0 - 0 - - - 397 - 500 - 0 - 0 - 0 - - - 398 - 500 - 0 - 0 - 0 - - - 399 - 1 - 0 - 0 - 0 - - - 400 - 1 - 0 - 0 - 0 - - - 401 - 2 - 0 - 0 - 0 - - - 402 - 2 - 0 - 0 - 0 - - - 403 - 2 - 0 - 0 - 0 - - - 404 - 2 - 0 - 0 - 0 - - - 405 - 50 - 0 - 0 - 0 - - - 406 - 50 - 0 - 0 - 0 - - - 407 - 200 - 0 - 0 - 0 - - - 408 - 200 - 0 - 0 - 0 - - - 409 - 5 - 0 - 0 - 0 - - - 410 - 5 - 0 - 0 - 0 - - - 411 - 112 - 0 - 0 - 0 - - - 412 - 112 - 0 - 0 - 0 - - - 413 - 1400 - 0 - 0 - 0 - - - 414 - 1400 - 0 - 0 - 0 - - - 415 - 10 - 0 - 0 - 0 - - - 416 - 1 - 0 - 0 - 0 - - - 417 - 1 - 0 - 0 - 0 - - - 418 - 1 - 0 - 0 - 0 - - - 419 - 1 - 0 - 0 - 0 - - - 420 - 1 - 0 - 0 - 0 - - - 421 - 10 - 0 - 0 - 0 - - - 422 - 1 - 0 - 0 - 0 - - - 423 - 1 - 0 - 0 - 0 - - - 424 - 1 - 0 - 0 - 0 - - - 425 - 1 - 0 - 0 - 0 - - - 426 - 5 - 0 - 0 - 0 - - - 427 - 5 - 0 - 0 - 0 - - - 428 - 5 - 0 - 0 - 0 - - - 429 - 5 - 0 - 0 - 0 - - - 430 - 40 - 0 - 0 - 0 - - - 431 - 30 - 0 - 0 - 0 - - - 432 - 1 - 0 - 0 - 0 - - - 433 - 1 - 0 - 0 - 0 - - - 434 - 1 - 0 - 0 - 0 - - - 435 - 1 - 0 - 0 - 0 - - - 436 - 20 - 0 - 0 - 0 - - - 437 - 20 - 0 - 0 - 0 - - - 438 - 20 - 0 - 0 - 0 - - - 439 - 20 - 0 - 0 - 0 - - - 440 - 17 - 0 - 0 - 0 - - - 441 - 17 - 0 - 0 - 0 - - - 442 - 75 - 0 - 0 - 0 - - - 443 - 75 - 0 - 0 - 0 - - - 444 - 150 - 0 - 0 - 0 - - - 445 - 150 - 0 - 0 - 0 - - - 446 - 150 - 0 - 0 - 0 - - - 447 - 162 - 0 - 0 - 0 - - - 448 - 162 - 0 - 0 - 0 - - - 449 - 400 - 0 - 0 - 0 - - - 450 - 400 - 0 - 0 - 0 - - - 451 - 3200 - 0 - 0 - 0 - - - 452 - 3200 - 0 - 0 - 0 - - - 453 - 45 - 0 - 0 - 0 - - - 454 - 45 - 0 - 0 - 0 - - - 455 - 10 - 0 - 0 - 0 - - - 456 - 10 - 0 - 0 - 0 - - - 457 - 10 - 0 - 0 - 0 - - - 458 - 10 - 0 - 0 - 0 - - - 459 - 10 - 0 - 0 - 0 - - - 460 - 10 - 0 - 0 - 0 - - - 461 - 10 - 0 - 0 - 0 - - - 462 - 10 - 0 - 0 - 0 - - - 463 - 10 - 0 - 0 - 0 - - - 464 - 1 - 0 - 0 - 0 - - - 465 - 1 - 0 - 0 - 0 - - - 466 - 1 - 0 - 0 - 0 - - - 467 - 1 - 0 - 0 - 0 - - - 468 - 1 - 0 - 0 - 0 - - - 469 - 1 - 0 - 0 - 0 - - - 470 - 1 - 0 - 0 - 0 - - - 471 - 1 - 0 - 0 - 0 - - - 472 - 17 - 0 - 0 - 0 - - - 473 - 17 - 0 - 0 - 0 - - - 474 - 43 - 0 - 0 - 0 - - - 475 - 43 - 0 - 0 - 0 - - - 476 - 107 - 0 - 0 - 0 - - - 477 - 107 - 0 - 0 - 0 - - - 478 - 1100 - 0 - 0 - 0 - - - 479 - 1100 - 0 - 0 - 0 - - - 480 - 1 - 0 - 0 - 0 - - - 481 - 1 - 0 - 0 - 0 - - - 482 - 139 - 0 - 0 - 0 - - - 483 - 139 - 0 - 0 - 0 - - - 484 - 499 - 0 - 0 - 0 - - - 485 - 499 - 0 - 0 - 0 - - - 486 - 1299 - 0 - 0 - 0 - - - 487 - 1299 - 0 - 0 - 0 - - - 488 - 3199 - 0 - 0 - 0 - - - 489 - 3199 - 0 - 0 - 0 - - - 490 - 31999 - 0 - 0 - 0 - - - 491 - 31999 - 0 - 0 - 0 - - - 492 - 1 - 0 - 0 - 0 - - - 493 - 1 - 0 - 0 - 0 - - - 494 - 1 - 0 - 0 - 0 - - - 495 - 1 - 0 - 0 - 0 - - - 496 - 1 - 0 - 0 - 0 - - - 497 - 1 - 0 - 0 - 0 - - - 498 - 1 - 0 - 0 - 0 - - - 499 - 1 - 0 - 0 - 0 - - - 500 - 10 - 0 - 0 - 0 - - - 501 - 10 - 0 - 0 - 0 - - - 502 - 18 - 0 - 0 - 0 - - - 503 - 18 - 0 - 0 - 0 - - - 504 - 43 - 0 - 0 - 0 - - - 505 - 43 - 0 - 0 - 0 - - - 506 - 427 - 0 - 0 - 0 - - - 507 - 427 - 0 - 0 - 0 - - - 508 - 15 - 0 - 0 - 0 - - - 509 - 15 - 0 - 0 - 0 - - - 510 - 55 - 0 - 0 - 0 - - - 511 - 55 - 0 - 0 - 0 - - - 512 - 199 - 0 - 0 - 0 - - - 513 - 199 - 0 - 0 - 0 - - - 514 - 383 - 0 - 0 - 0 - - - 515 - 383 - 0 - 0 - 0 - - - 516 - 519 - 0 - 0 - 0 - - - 517 - 519 - 0 - 0 - 0 - - - 518 - 1279 - 0 - 0 - 0 - - - 519 - 1279 - 0 - 0 - 0 - - - 520 - 12799 - 0 - 0 - 0 - - - 521 - 12799 - 0 - 0 - 0 - - - 522 - 1 - 0 - 0 - 0 - - - 523 - 1 - 0 - 0 - 0 - - - 524 - 1 - 0 - 0 - 0 - - - 525 - 1 - 0 - 0 - 0 - - - 526 - 1 - 0 - 0 - 0 - - - 527 - 1 - 0 - 0 - 0 - - - 528 - 1 - 0 - 0 - 0 - - - 529 - 1 - 0 - 0 - 0 - - - 530 - 1 - 0 - 0 - 0 - - - 531 - 1 - 0 - 0 - 0 - - - 532 - 1 - 0 - 0 - 0 - - - 533 - 1 - 0 - 0 - 0 - - - 534 - 1 - 0 - 0 - 0 - - - 535 - 1 - 0 - 0 - 0 - - - 536 - 1 - 0 - 0 - 0 - - - 537 - 1 - 0 - 0 - 0 - - - 538 - 30 - 0 - 0 - 0 - - - 539 - 30 - 0 - 0 - 0 - - - 540 - 40 - 0 - 0 - 0 - - - 541 - 40 - 0 - 0 - 0 - - - 542 - 30 - 0 - 0 - 0 - - - 543 - 30 - 0 - 0 - 0 - - - 544 - 40 - 0 - 0 - 0 - - - 545 - 40 - 0 - 0 - 0 - - - 546 - 40 - 0 - 0 - 0 - - - 547 - 1 - 0 - 0 - 0 - - - 548 - 30 - 0 - 0 - 0 - - - 549 - 1 - 0 - 0 - 0 - - - 550 - 1 - 0 - 0 - 0 - - - 551 - 1 - 0 - 0 - 0 - - - 552 - 35 - 0 - 0 - 0 - - - 553 - 1 - 0 - 0 - 0 - - - 554 - 17 - 0 - 0 - 0 - - - 555 - 17 - 0 - 0 - 0 - - - 556 - 17 - 0 - 0 - 0 - - - 557 - 17 - 0 - 0 - 0 - - - 558 - 17 - 0 - 0 - 0 - - - 559 - 16 - 0 - 0 - 0 - - - 560 - 310 - 0 - 0 - 0 - - - 561 - 372 - 0 - 0 - 0 - - - 562 - 140 - 0 - 0 - 0 - - - 563 - 378 - 0 - 0 - 0 - - - 564 - 232 - 0 - 0 - 0 - - - 565 - 550 - 0 - 0 - 0 - - - 566 - 410 - 0 - 0 - 0 - - - 567 - 100 - 0 - 0 - 0 - - - 568 - 100 - 0 - 0 - 0 - - - 569 - 300 - 0 - 0 - 0 - - - 570 - 300 - 0 - 0 - 0 - - - 571 - 300 - 0 - 0 - 0 - - - 572 - 300 - 0 - 0 - 0 - - - 573 - 300 - 0 - 0 - 0 - - - 574 - 300 - 0 - 0 - 0 - - - 575 - 300 - 0 - 0 - 0 - - - 576 - 300 - 0 - 0 - 0 - - - 577 - 15 - 0 - 0 - 0 - - - 578 - 15 - 0 - 0 - 0 - - - 579 - 2 - 0 - 0 - 0 - - - 580 - 2 - 0 - 0 - 0 - - - 581 - 13 - 0 - 0 - 0 - - - 582 - 13 - 0 - 0 - 0 - - - 583 - 10 - 0 - 0 - 0 - - - 584 - 10 - 0 - 0 - 0 - - - 585 - 10 - 0 - 0 - 0 - - - 586 - 10 - 0 - 0 - 0 - - - 587 - 1 - 0 - 0 - 0 - - - 588 - 1 - 0 - 0 - 0 - - - 589 - 1 - 0 - 0 - 0 - - - 590 - 1 - 0 - 0 - 0 - - - 591 - 1 - 0 - 0 - 0 - - - 592 - 2 - 0 - 0 - 0 - - - 593 - 2 - 0 - 0 - 0 - - - 594 - 4 - 0 - 0 - 0 - - - 595 - 4 - 0 - 0 - 0 - - - 596 - 4 - 0 - 0 - 0 - - - 597 - 4 - 0 - 0 - 0 - - - 598 - 10 - 0 - 0 - 0 - - - 599 - 1 - 0 - 0 - 0 - - - 600 - 3 - 0 - 0 - 0 - - - 601 - 1 - 0 - 0 - 0 - - - 602 - 1 - 0 - 0 - 0 - - - 603 - 3 - 0 - 0 - 0 - - - 604 - 1 - 0 - 0 - 0 - - - 605 - 1 - 0 - 0 - 0 - - - 606 - 1 - 0 - 0 - 0 - - - 607 - 1 - 0 - 0 - 0 - - - 608 - 1 - 0 - 0 - 0 - - - 609 - 1 - 0 - 0 - 0 - - - 610 - 1 - 0 - 0 - 0 - - - 611 - 1 - 0 - 0 - 0 - - - 612 - 1 - 0 - 0 - 0 - - - 613 - 1 - 0 - 0 - 0 - - - 614 - 1 - 0 - 0 - 0 - - - 615 - 1 - 0 - 0 - 0 - - - 616 - 1 - 0 - 0 - 0 - - - 617 - 1 - 0 - 0 - 0 - - - 618 - 1 - 0 - 0 - 0 - - - 619 - 5 - 0 - 0 - 0 - - - 620 - 5 - 0 - 0 - 0 - - - 621 - 5 - 0 - 0 - 0 - - - 622 - 1 - 0 - 0 - 0 - - - 623 - 1 - 0 - 0 - 0 - - - 624 - 1 - 0 - 0 - 0 - - - 625 - 5 - 0 - 0 - 0 - - - 626 - 200 - 0 - 0 - 0 - - - 627 - 200 - 0 - 0 - 0 - - - 628 - 200 - 0 - 0 - 0 - - - 629 - 200 - 0 - 0 - 0 - - - 630 - 200 - 0 - 0 - 0 - - - 631 - 200 - 0 - 0 - 0 - - - 632 - 200 - 0 - 0 - 0 - - - 633 - 200 - 0 - 0 - 0 - - - 634 - 200 - 0 - 0 - 0 - - - 635 - 200 - 0 - 0 - 0 - - - 636 - 180 - 0 - 0 - 0 - - - 637 - 180 - 0 - 0 - 0 - - - 638 - 180 - 0 - 0 - 0 - - - 639 - 180 - 0 - 0 - 0 - - - 640 - 180 - 0 - 0 - 0 - - - 641 - 180 - 0 - 0 - 0 - - - 642 - 180 - 0 - 0 - 0 - - - 643 - 180 - 0 - 0 - 0 - - - 644 - 180 - 0 - 0 - 0 - - - 645 - 180 - 0 - 0 - 0 - - - 646 - 180 - 0 - 0 - 0 - - - 647 - 180 - 0 - 0 - 0 - - - 648 - 180 - 0 - 0 - 0 - - - 649 - 180 - 0 - 0 - 0 - - - 650 - 180 - 0 - 0 - 0 - - - 651 - 180 - 0 - 0 - 0 - - - 652 - 180 - 0 - 0 - 0 - - - 653 - 180 - 0 - 0 - 0 - - - 654 - 180 - 0 - 0 - 0 - - - 655 - 180 - 0 - 0 - 0 - - - 656 - 160 - 0 - 0 - 0 - - - 657 - 160 - 0 - 0 - 0 - - - 658 - 160 - 0 - 0 - 0 - - - 659 - 160 - 0 - 0 - 0 - - - 660 - 160 - 0 - 0 - 0 - - - 661 - 160 - 0 - 0 - 0 - - - 662 - 160 - 0 - 0 - 0 - - - 663 - 160 - 0 - 0 - 0 - - - 664 - 160 - 0 - 0 - 0 - - - 665 - 160 - 0 - 0 - 0 - - - 666 - 3 - 0 - 0 - 0 - - - 667 - 200 - 0 - 0 - 0 - - - 668 - 3 - 0 - 0 - 0 - - - 669 - 1 - 0 - 0 - 0 - - - 670 - 1 - 0 - 0 - 0 - - - 671 - 1 - 0 - 0 - 0 - - - 672 - 1 - 0 - 0 - 0 - - - 673 - 1 - 0 - 0 - 0 - - - 674 - 1 - 0 - 0 - 0 - - - 675 - 1 - 0 - 0 - 0 - - - 676 - 1 - 0 - 0 - 0 - - - 677 - 1 - 0 - 0 - 0 - - - 678 - 1 - 0 - 0 - 0 - - - 679 - 1 - 0 - 0 - 0 - - - 680 - 1 - 0 - 0 - 0 - - - 681 - 1 - 0 - 0 - 0 - - - 682 - 1 - 0 - 0 - 0 - - - 683 - 1 - 0 - 0 - 0 - - - 684 - 1 - 0 - 0 - 0 - - - 685 - 1 - 0 - 0 - 0 - - - 686 - 1 - 0 - 0 - 0 - - - 687 - 1 - 0 - 0 - 0 - - - 688 - 1 - 0 - 0 - 0 - - - 689 - 1 - 0 - 0 - 0 - - - 690 - 1 - 0 - 0 - 0 - - - 691 - 1 - 0 - 0 - 0 - - - 692 - 1 - 0 - 0 - 0 - - - 693 - 1 - 0 - 0 - 0 - - - 694 - 1 - 0 - 0 - 0 - - - 695 - 1 - 0 - 0 - 0 - - - 696 - 1 - 0 - 0 - 0 - - - 697 - 1 - 0 - 0 - 0 - - - 698 - 1 - 0 - 0 - 0 - - - 699 - 1 - 0 - 0 - 0 - - - 700 - 1 - 0 - 0 - 0 - - - 701 - 1 - 0 - 0 - 0 - - - 702 - 1 - 0 - 0 - 0 - - - 703 - 1 - 0 - 0 - 0 - - - 704 - 1 - 0 - 0 - 0 - - - 705 - 1 - 0 - 0 - 0 - - - 706 - 1 - 0 - 0 - 0 - - - 707 - 1 - 0 - 0 - 0 - - - 708 - 1 - 0 - 0 - 0 - - - 709 - 1 - 0 - 0 - 0 - - - 710 - 1 - 0 - 0 - 0 - - - 711 - 1 - 0 - 0 - 0 - - - 712 - 1 - 0 - 0 - 0 - - - 713 - 1 - 0 - 0 - 0 - - - 714 - 1 - 0 - 0 - 0 - - - 715 - 1 - 0 - 0 - 0 - - - 716 - 1 - 0 - 0 - 0 - - - 717 - 1 - 0 - 0 - 0 - - - 718 - 1 - 0 - 0 - 0 - - - 719 - 1 - 0 - 0 - 0 - - - 720 - 10 - 0 - 0 - 0 - - - 721 - 700 - 0 - 0 - 0 - - - 722 - 700 - 0 - 0 - 0 - - - 723 - 700 - 0 - 0 - 0 - - - 724 - 700 - 0 - 0 - 0 - - - 725 - 700 - 0 - 0 - 0 - - - 726 - 700 - 0 - 0 - 0 - - - 727 - 2 - 0 - 0 - 0 - - - 728 - 1 - 0 - 0 - 0 - - - 729 - 1 - 0 - 0 - 0 - - - 730 - 1 - 0 - 0 - 0 - - - 731 - 1 - 0 - 0 - 0 - - - 732 - 1 - 0 - 0 - 0 - - - 733 - 1 - 0 - 0 - 0 - - - 734 - 1 - 0 - 0 - 0 - - - 735 - 1 - 0 - 0 - 0 - - - 736 - 1 - 0 - 0 - 0 - - - 737 - 1 - 0 - 0 - 0 - - - 738 - 1 - 0 - 0 - 0 - - - 739 - 1 - 0 - 0 - 0 - - - 740 - 2 - 0 - 0 - 0 - - - 741 - 1 - 0 - 0 - 0 - - - 742 - 1 - 0 - 0 - 0 - - - 743 - 1 - 0 - 0 - 0 - - - 744 - 1 - 0 - 0 - 0 - - - 745 - 1 - 0 - 0 - 0 - - - 746 - 1 - 0 - 0 - 0 - - - 747 - 1 - 0 - 0 - 0 - - - 748 - 1 - 0 - 0 - 0 - - - 749 - 1 - 0 - 0 - 0 - - - 750 - 1 - 0 - 0 - 0 - - - 751 - 1 - 0 - 0 - 0 - - - 752 - 1 - 0 - 0 - 0 - - - 753 - 1 - 0 - 0 - 0 - - - 754 - 1 - 0 - 0 - 0 - - - 755 - 5 - 0 - 0 - 0 - - - 756 - 1 - 0 - 0 - 0 - - - 757 - 1 - 0 - 0 - 0 - - - 758 - 1 - 0 - 0 - 0 - - - 759 - 1 - 0 - 0 - 0 - - - 760 - 1 - 0 - 0 - 0 - - - 761 - 5 - 0 - 0 - 0 - - - 762 - 1 - 0 - 0 - 0 - - - 763 - 1 - 0 - 0 - 0 - - - 764 - 1 - 0 - 0 - 0 - - - 765 - 1 - 0 - 0 - 0 - - - 766 - 1 - 0 - 0 - 0 - - - 767 - 4 - 0 - 0 - 0 - - - 768 - 4 - 0 - 0 - 0 - - - 769 - 1 - 0 - 0 - 0 - - - 770 - 1 - 0 - 0 - 0 - - - 771 - 15 - 0 - 0 - 0 - - - 772 - 15 - 0 - 0 - 0 - - - 773 - 2025 - 0 - 0 - 0 - - - 774 - 2175 - 0 - 0 - 0 - - - 775 - 1 - 0 - 0 - 0 - - - 776 - 1 - 0 - 0 - 0 - - - 777 - 1 - 0 - 0 - 0 - - - 778 - 1 - 0 - 0 - 0 - - - 779 - 1 - 0 - 0 - 0 - - - 780 - 1 - 0 - 0 - 0 - - - 781 - 1 - 0 - 0 - 0 - - - 782 - 1 - 0 - 0 - 0 - - - 783 - 2 - 0 - 0 - 0 - - - 784 - 2 - 0 - 0 - 0 - - - 785 - 2 - 0 - 0 - 0 - - - 786 - 2 - 0 - 0 - 0 - - - 787 - 2 - 0 - 0 - 0 - - - 788 - 1 - 0 - 0 - 0 - - - 789 - 1 - 0 - 0 - 0 - - - 790 - 1 - 0 - 0 - 0 - - - 791 - 1 - 0 - 0 - 0 - - - 792 - 1 - 0 - 0 - 0 - - - 793 - 1 - 0 - 0 - 0 - - - 794 - 2 - 0 - 0 - 0 - - - 795 - 2 - 0 - 0 - 0 - - - 796 - 1 - 0 - 0 - 0 - - - 797 - 1 - 0 - 0 - 0 - - - 798 - 1 - 0 - 0 - 0 - - - 799 - 1 - 0 - 0 - 0 - - - 800 - 4 - 0 - 0 - 0 - - - 801 - 13 - 0 - 0 - 0 - - - 802 - 38 - 0 - 0 - 0 - - - 803 - 105 - 0 - 0 - 0 - - - 804 - 262 - 0 - 0 - 0 - - - 805 - 698 - 0 - 0 - 0 - - - 806 - 1 - 0 - 0 - 0 - - - 807 - 2 - 0 - 0 - 0 - - - 808 - 10 - 0 - 0 - 0 - - - 809 - 25 - 0 - 0 - 0 - - - 810 - 65 - 0 - 0 - 0 - - - 811 - 350 - 0 - 0 - 0 - - - 812 - 1 - 0 - 0 - 0 - - - 813 - 2 - 0 - 0 - 0 - - - 814 - 10 - 0 - 0 - 0 - - - 815 - 25 - 0 - 0 - 0 - - - 816 - 65 - 0 - 0 - 0 - - - 817 - 350 - 0 - 0 - 0 - - - 818 - 1 - 0 - 0 - 0 - - - 819 - 1 - 0 - 0 - 0 - - - 820 - 3 - 0 - 0 - 0 - - - 821 - 5 - 0 - 0 - 0 - - - 822 - 12 - 0 - 0 - 0 - - - 823 - 36 - 0 - 0 - 0 - - - 824 - 175 - 0 - 0 - 0 - - - 825 - 6 - 0 - 0 - 0 - - - 826 - 8 - 0 - 0 - 0 - - - 827 - 37 - 0 - 0 - 0 - - - 828 - 98 - 0 - 0 - 0 - - - 829 - 246 - 0 - 0 - 0 - - - 830 - 624 - 0 - 0 - 0 - - - 831 - 4 - 0 - 0 - 0 - - - 832 - 6 - 0 - 0 - 0 - - - 833 - 24 - 0 - 0 - 0 - - - 834 - 64 - 0 - 0 - 0 - - - 835 - 160 - 0 - 0 - 0 - - - 836 - 400 - 0 - 0 - 0 - - - 837 - 70 - 0 - 0 - 0 - - - 838 - 70 - 0 - 0 - 0 - - - 839 - 80 - 0 - 0 - 0 - - - 840 - 80 - 0 - 0 - 0 - - - 841 - 50 - 0 - 0 - 0 - - - 842 - 50 - 0 - 0 - 0 - - - 843 - 100 - 0 - 0 - 0 - - - 844 - 100 - 0 - 0 - 0 - - - 845 - 160 - 0 - 0 - 0 - - - 846 - 160 - 0 - 0 - 0 - - - 847 - 320 - 0 - 0 - 0 - - - 848 - 320 - 0 - 0 - 0 - - - 849 - 200 - 0 - 0 - 0 - - - 850 - 200 - 0 - 0 - 0 - - - 851 - 640 - 0 - 0 - 0 - - - 852 - 640 - 0 - 0 - 0 - - - 853 - 400 - 0 - 0 - 0 - - - 854 - 400 - 0 - 0 - 0 - - - 855 - 1280 - 0 - 0 - 0 - - - 856 - 1280 - 0 - 0 - 0 - - - 857 - 800 - 0 - 0 - 0 - - - 858 - 800 - 0 - 0 - 0 - - - 859 - 2560 - 0 - 0 - 0 - - - 860 - 2560 - 0 - 0 - 0 - - - 861 - 1600 - 0 - 0 - 0 - - - 862 - 1600 - 0 - 0 - 0 - - - 863 - 58 - 0 - 0 - 0 - - - 864 - 14 - 0 - 0 - 0 - - - 865 - 68 - 0 - 0 - 0 - - - 866 - 27 - 0 - 0 - 0 - - - 867 - 66 - 0 - 0 - 0 - - - 868 - 167 - 0 - 0 - 0 - - - 869 - 19 - 0 - 0 - 0 - - - 870 - 1 - 0 - 0 - 0 - - - 871 - 3 - 0 - 0 - 0 - - - 872 - 10 - 0 - 0 - 0 - - - 873 - 27 - 0 - 0 - 0 - - - 874 - 18 - 0 - 0 - 0 - - - 875 - 66 - 0 - 0 - 0 - - - 876 - 166 - 0 - 0 - 0 - - - 877 - 3 - 0 - 0 - 0 - - - 878 - 3 - 0 - 0 - 0 - - - 879 - 7 - 0 - 0 - 0 - - - 880 - 13 - 0 - 0 - 0 - - - 881 - 200 - 0 - 0 - 0 - - - 882 - 7 - 0 - 0 - 0 - - - 883 - 1 - 0 - 0 - 0 - - - 884 - 20 - 0 - 0 - 0 - - - 885 - 3 - 0 - 0 - 0 - - - 886 - 46 - 0 - 0 - 0 - - - 887 - 12 - 0 - 0 - 0 - - - 888 - 76 - 0 - 0 - 0 - - - 889 - 32 - 0 - 0 - 0 - - - 890 - 172 - 0 - 0 - 0 - - - 891 - 80 - 0 - 0 - 0 - - - 892 - 510 - 0 - 0 - 0 - - - 893 - 400 - 0 - 0 - 0 - - - 894 - 1 - 0 - 0 - 0 - - - 895 - 1 - 0 - 0 - 0 - - - 896 - 1 - 0 - 0 - 0 - - - 897 - 1 - 0 - 0 - 0 - - - 898 - 1 - 0 - 0 - 0 - - - 899 - 1 - 0 - 0 - 0 - - - 900 - 1 - 0 - 0 - 0 - - - 901 - 1 - 0 - 0 - 0 - - - 902 - 1 - 0 - 0 - 0 - - - 903 - 1 - 0 - 0 - 0 - - - 904 - 1 - 0 - 0 - 0 - - - 905 - 1 - 0 - 0 - 0 - - - 906 - 1 - 0 - 0 - 0 - - - 907 - 1 - 0 - 0 - 0 - - - 908 - 1 - 0 - 0 - 0 - - - 909 - 1 - 0 - 0 - 0 - - - 910 - 1 - 0 - 0 - 0 - - - 911 - 1 - 0 - 0 - 0 - - - 912 - 1 - 0 - 0 - 0 - - - 913 - 1 - 0 - 0 - 0 - - - 914 - 1 - 0 - 0 - 0 - - - 915 - 1 - 0 - 0 - 0 - - - 916 - 1 - 0 - 0 - 0 - - - 917 - 1 - 0 - 0 - 0 - - - 918 - 1 - 0 - 0 - 0 - - - 919 - 1 - 0 - 0 - 0 - - - 920 - 1 - 0 - 0 - 0 - - - 921 - 1 - 0 - 0 - 0 - - - 922 - 1 - 0 - 0 - 0 - - - 923 - 1 - 0 - 0 - 0 - - - 924 - 1 - 0 - 0 - 0 - - - 925 - 1 - 0 - 0 - 0 - - - 926 - 1 - 0 - 0 - 0 - - - 927 - 1 - 0 - 0 - 0 - - - 928 - 1 - 0 - 0 - 0 - - - 929 - 1 - 0 - 0 - 0 - - - 930 - 1 - 0 - 0 - 0 - - - 931 - 1 - 0 - 0 - 0 - - - 932 - 1 - 0 - 0 - 0 - - - 933 - 1 - 0 - 0 - 0 - - - 934 - 1 - 0 - 0 - 0 - - - 935 - 1 - 0 - 0 - 0 - - - 936 - 1 - 0 - 0 - 0 - - - 937 - 1 - 0 - 0 - 0 - - - 938 - 1 - 0 - 0 - 0 - - - 939 - 1 - 0 - 0 - 0 - - - 940 - 1 - 0 - 0 - 0 - - - 941 - 1 - 0 - 0 - 0 - - - 942 - 10 - 0 - 0 - 0 - - - 943 - 1 - 0 - 0 - 0 - - - 944 - 1 - 0 - 0 - 0 - - - 945 - 1 - 0 - 0 - 0 - - - 946 - 6 - 0 - 0 - 0 - - - 947 - 6 - 0 - 0 - 0 - - - 948 - 10 - 0 - 0 - 0 - - - 949 - 10 - 0 - 0 - 0 - - - 950 - 55 - 0 - 0 - 0 - - - 951 - 55 - 0 - 0 - 0 - - - 952 - 3 - 0 - 0 - 0 - - - 953 - 3 - 0 - 0 - 0 - - - 954 - 18 - 0 - 0 - 0 - - - 955 - 18 - 0 - 0 - 0 - - - 956 - 1 - 0 - 0 - 0 - - - 957 - 1 - 0 - 0 - 0 - - - 958 - 150 - 0 - 0 - 0 - - - 959 - 150 - 0 - 0 - 0 - - - 960 - 100 - 0 - 0 - 0 - - - 961 - 100 - 0 - 0 - 0 - - - 962 - 1 - 0 - 0 - 0 - - - 963 - 1 - 0 - 0 - 0 - - - 964 - 1 - 0 - 0 - 0 - - - 965 - 1 - 0 - 0 - 0 - - - 966 - 1 - 0 - 0 - 0 - - - 967 - 1 - 0 - 0 - 0 - - - 968 - 1 - 0 - 0 - 0 - - - 969 - 1 - 0 - 0 - 0 - - - 970 - 10 - 0 - 0 - 0 - - - 971 - 10 - 0 - 0 - 0 - - - 972 - 1 - 0 - 0 - 0 - - - 973 - 45 - 0 - 0 - 0 - - - 974 - 45 - 0 - 0 - 0 - - - 975 - 52 - 0 - 0 - 0 - - - 976 - 52 - 0 - 0 - 0 - - - 977 - 1 - 0 - 0 - 0 - - - 978 - 1 - 0 - 0 - 0 - - - 979 - 1 - 0 - 0 - 0 - - - 980 - 1 - 0 - 0 - 0 - - - 981 - 12 - 0 - 0 - 0 - - - 982 - 12 - 0 - 0 - 0 - - - 983 - 1 - 0 - 0 - 0 - - - 984 - 1 - 0 - 0 - 0 - - - 985 - 1 - 0 - 0 - 0 - - - 986 - 1 - 0 - 0 - 0 - - - 987 - 1 - 0 - 0 - 0 - - - 988 - 1 - 0 - 0 - 0 - - - 989 - 1 - 0 - 0 - 0 - - - 990 - 1 - 0 - 0 - 0 - - - 991 - 1 - 0 - 0 - 0 - - - 992 - 1 - 0 - 0 - 0 - - - 993 - 1 - 0 - 0 - 0 - - - 994 - 1 - 0 - 0 - 0 - - - 995 - 1 - 0 - 0 - 0 - - - 996 - 1 - 0 - 0 - 0 - - - 997 - 1 - 0 - 0 - 0 - - - 998 - 1 - 0 - 0 - 0 - - - 999 - 1 - 0 - 0 - 0 - - - 1000 - 1 - 0 - 0 - 0 - - - 1001 - 1 - 0 - 0 - 0 - - - 1002 - 1 - 0 - 0 - 0 - - - 1003 - 1 - 0 - 0 - 0 - - - 1004 - 1 - 0 - 0 - 0 - - - 1005 - 2 - 0 - 0 - 0 - - - 1006 - 2 - 0 - 0 - 0 - - - 1007 - 20 - 0 - 0 - 0 - - - 1008 - 20 - 0 - 0 - 0 - - - 1009 - 30 - 0 - 0 - 0 - - - 1010 - 30 - 0 - 0 - 0 - - - 1011 - 2 - 0 - 0 - 0 - - - 1012 - 2 - 0 - 0 - 0 - - - 1013 - 2 - 0 - 0 - 0 - - - 1014 - 2 - 0 - 0 - 0 - - - 1015 - 2 - 0 - 0 - 0 - - - 1016 - 2 - 0 - 0 - 0 - - - 1017 - 2 - 0 - 0 - 0 - - - 1018 - 2 - 0 - 0 - 0 - - - 1019 - 20 - 0 - 0 - 0 - - - 1020 - 20 - 0 - 0 - 0 - - - 1021 - 20 - 0 - 0 - 0 - - - 1022 - 20 - 0 - 0 - 0 - - - 1023 - 20 - 0 - 0 - 0 - - - 1024 - 20 - 0 - 0 - 0 - - - 1025 - 2 - 0 - 0 - 0 - - - 1026 - 2 - 0 - 0 - 0 - - - 1027 - 20 - 0 - 0 - 0 - - - 1028 - 20 - 0 - 0 - 0 - - - 1029 - 20 - 0 - 0 - 0 - - - 1030 - 20 - 0 - 0 - 0 - - - 1031 - 20 - 0 - 0 - 0 - - - 1032 - 20 - 0 - 0 - 0 - - - 1033 - 30 - 0 - 0 - 0 - - - 1034 - 30 - 0 - 0 - 0 - - - 1035 - 40 - 0 - 0 - 0 - - - 1036 - 40 - 0 - 0 - 0 - - - 1037 - 1 - 0 - 0 - 0 - - - 1038 - 1 - 0 - 0 - 0 - - - 1039 - 1 - 0 - 0 - 0 - - - 1040 - 1 - 0 - 0 - 0 - - - 1041 - 1 - 0 - 0 - 0 - - - 1042 - 1 - 0 - 0 - 0 - - - 1043 - 1 - 0 - 0 - 0 - - - 1044 - 1 - 0 - 0 - 0 - - - 1045 - 1 - 0 - 0 - 0 - - - 1046 - 1 - 0 - 0 - 0 - - - 1047 - 1 - 0 - 0 - 0 - - - 1048 - 1 - 0 - 0 - 0 - - - 1049 - 1 - 0 - 0 - 0 - - - 1050 - 160 - 0 - 0 - 0 - - - 1051 - 160 - 0 - 0 - 0 - - - 1052 - 450 - 0 - 0 - 0 - - - 1053 - 15 - 0 - 0 - 0 - - - 1054 - 15 - 0 - 0 - 0 - - - 1055 - 15 - 0 - 0 - 0 - - - 1056 - 15 - 0 - 0 - 0 - - - 1057 - 15 - 0 - 0 - 0 - - - 1058 - 15 - 0 - 0 - 0 - - - 1059 - 6 - 0 - 0 - 0 - - - 1060 - 6 - 0 - 0 - 0 - - - 1061 - 6 - 0 - 0 - 0 - - - 1062 - 6 - 0 - 0 - 0 - - - 1063 - 18 - 0 - 0 - 0 - - - 1064 - 18 - 0 - 0 - 0 - - - 1065 - 2500 - 0 - 0 - 0 - - - 1066 - 2500 - 0 - 0 - 0 - - - 1067 - 280 - 0 - 0 - 0 - - - 1068 - 280 - 0 - 0 - 0 - - - 1069 - 1000 - 0 - 0 - 0 - - - 1070 - 1000 - 0 - 0 - 0 - - - 1071 - 2600 - 0 - 0 - 0 - - - 1072 - 2600 - 0 - 0 - 0 - - - 1073 - 6400 - 0 - 0 - 0 - - - 1074 - 6400 - 0 - 0 - 0 - - - 1075 - 80 - 0 - 0 - 0 - - - 1076 - 80 - 0 - 0 - 0 - - - 1077 - 1920 - 0 - 0 - 0 - - - 1078 - 1920 - 0 - 0 - 0 - - - 1079 - 64000 - 0 - 0 - 0 - - - 1080 - 64000 - 0 - 0 - 0 - - - 1081 - 280 - 0 - 0 - 0 - - - 1082 - 280 - 0 - 0 - 0 - - - 1083 - 1000 - 0 - 0 - 0 - - - 1084 - 1000 - 0 - 0 - 0 - - - 1085 - 2600 - 0 - 0 - 0 - - - 1086 - 2600 - 0 - 0 - 0 - - - 1087 - 80 - 0 - 0 - 0 - - - 1088 - 80 - 0 - 0 - 0 - - - 1089 - 1920 - 0 - 0 - 0 - - - 1090 - 1920 - 0 - 0 - 0 - - - 1091 - 6400 - 0 - 0 - 0 - - - 1092 - 6400 - 0 - 0 - 0 - - - 1093 - 64000 - 0 - 0 - 0 - - - 1094 - 64000 - 0 - 0 - 0 - - - 1095 - 20 - 0 - 0 - 0 - - - 1096 - 20 - 0 - 0 - 0 - - - 1097 - 750 - 0 - 0 - 0 - - - 1098 - 750 - 0 - 0 - 0 - - - 1099 - 3900 - 0 - 0 - 0 - - - 1100 - 3900 - 0 - 0 - 0 - - - 1101 - 210 - 0 - 0 - 0 - - - 1102 - 210 - 0 - 0 - 0 - - - 1103 - 60 - 0 - 0 - 0 - - - 1104 - 60 - 0 - 0 - 0 - - - 1105 - 750 - 0 - 0 - 0 - - - 1106 - 750 - 0 - 0 - 0 - - - 1107 - 1440 - 0 - 0 - 0 - - - 1108 - 1440 - 0 - 0 - 0 - - - 1109 - 1950 - 0 - 0 - 0 - - - 1110 - 1950 - 0 - 0 - 0 - - - 1111 - 4800 - 0 - 0 - 0 - - - 1112 - 4800 - 0 - 0 - 0 - - - 1113 - 50000 - 0 - 0 - 0 - - - 1114 - 50000 - 0 - 0 - 0 - - - 1115 - 560 - 0 - 0 - 0 - - - 1116 - 560 - 0 - 0 - 0 - - - 1117 - 160 - 0 - 0 - 0 - - - 1118 - 160 - 0 - 0 - 0 - - - 1119 - 2000 - 0 - 0 - 0 - - - 1120 - 2000 - 0 - 0 - 0 - - - 1121 - 5200 - 0 - 0 - 0 - - - 1122 - 5200 - 0 - 0 - 0 - - - 1123 - 16640 - 0 - 0 - 0 - - - 1124 - 16640 - 0 - 0 - 0 - - - 1125 - 3840 - 0 - 0 - 0 - - - 1126 - 3840 - 0 - 0 - 0 - - - 1127 - 65000 - 0 - 0 - 0 - - - 1128 - 65000 - 0 - 0 - 0 - - - 1129 - 21 - 0 - 0 - 0 - - - 1130 - 21 - 0 - 0 - 0 - - - 1131 - 170 - 0 - 0 - 0 - - - 1132 - 170 - 0 - 0 - 0 - - - 1133 - 850 - 0 - 0 - 0 - - - 1134 - 850 - 0 - 0 - 0 - - - 1135 - 7800 - 0 - 0 - 0 - - - 1136 - 7800 - 0 - 0 - 0 - - - 1137 - 84 - 0 - 0 - 0 - - - 1138 - 84 - 0 - 0 - 0 - - - 1139 - 24 - 0 - 0 - 0 - - - 1140 - 24 - 0 - 0 - 0 - - - 1141 - 300 - 0 - 0 - 0 - - - 1142 - 300 - 0 - 0 - 0 - - - 1143 - 780 - 0 - 0 - 0 - - - 1144 - 780 - 0 - 0 - 0 - - - 1145 - 1920 - 0 - 0 - 0 - - - 1146 - 1920 - 0 - 0 - 0 - - - 1147 - 19200 - 0 - 0 - 0 - - - 1148 - 19200 - 0 - 0 - 0 - - - 1149 - 100000 - 0 - 0 - 0 - - - 1150 - 100000 - 0 - 0 - 0 - - - 1151 - 576 - 0 - 0 - 0 - - - 1152 - 576 - 0 - 0 - 0 - - - 1153 - 154 - 0 - 0 - 0 - - - 1154 - 154 - 0 - 0 - 0 - - - 1155 - 44 - 0 - 0 - 0 - - - 1156 - 44 - 0 - 0 - 0 - - - 1157 - 550 - 0 - 0 - 0 - - - 1158 - 550 - 0 - 0 - 0 - - - 1159 - 1430 - 0 - 0 - 0 - - - 1160 - 1430 - 0 - 0 - 0 - - - 1161 - 3520 - 0 - 0 - 0 - - - 1162 - 3520 - 0 - 0 - 0 - - - 1163 - 35200 - 0 - 0 - 0 - - - 1164 - 35200 - 0 - 0 - 0 - - - 1165 - 1372 - 0 - 0 - 0 - - - 1166 - 1372 - 0 - 0 - 0 - - - 1167 - 24 - 0 - 0 - 0 - - - 1168 - 24 - 0 - 0 - 0 - - - 1169 - 200 - 0 - 0 - 0 - - - 1170 - 200 - 0 - 0 - 0 - - - 1171 - 20 - 0 - 0 - 0 - - - 1172 - 20 - 0 - 0 - 0 - - - 1173 - 48 - 0 - 0 - 0 - - - 1174 - 48 - 0 - 0 - 0 - - - 1175 - 168 - 0 - 0 - 0 - - - 1176 - 168 - 0 - 0 - 0 - - - 1177 - 600 - 0 - 0 - 0 - - - 1178 - 600 - 0 - 0 - 0 - - - 1179 - 1152 - 0 - 0 - 0 - - - 1180 - 1152 - 0 - 0 - 0 - - - 1181 - 1560 - 0 - 0 - 0 - - - 1182 - 1560 - 0 - 0 - 0 - - - 1183 - 3840 - 0 - 0 - 0 - - - 1184 - 3840 - 0 - 0 - 0 - - - 1185 - 38400 - 0 - 0 - 0 - - - 1186 - 38400 - 0 - 0 - 0 - - - 1187 - 500000 - 0 - 0 - 0 - - - 1188 - 500000 - 0 - 0 - 0 - - - 1189 - 68 - 0 - 0 - 0 - - - 1190 - 68 - 0 - 0 - 0 - - - 1191 - 238 - 0 - 0 - 0 - - - 1192 - 238 - 0 - 0 - 0 - - - 1193 - 850 - 0 - 0 - 0 - - - 1194 - 850 - 0 - 0 - 0 - - - 1195 - 2121 - 0 - 0 - 0 - - - 1196 - 2121 - 0 - 0 - 0 - - - 1197 - 2210 - 0 - 0 - 0 - - - 1198 - 2210 - 0 - 0 - 0 - - - 1199 - 5440 - 0 - 0 - 0 - - - 1200 - 5440 - 0 - 0 - 0 - - - 1201 - 54400 - 0 - 0 - 0 - - - 1202 - 54400 - 0 - 0 - 0 - - - 1203 - 35 - 0 - 0 - 0 - - - 1204 - 35 - 0 - 0 - 0 - - - 1205 - 10 - 0 - 0 - 0 - - - 1206 - 10 - 0 - 0 - 0 - - - 1207 - 125 - 0 - 0 - 0 - - - 1208 - 125 - 0 - 0 - 0 - - - 1209 - 325 - 0 - 0 - 0 - - - 1210 - 325 - 0 - 0 - 0 - - - 1211 - 800 - 0 - 0 - 0 - - - 1212 - 800 - 0 - 0 - 0 - - - 1213 - 8000 - 0 - 0 - 0 - - - 1214 - 8000 - 0 - 0 - 0 - - - 1215 - 30000 - 0 - 0 - 0 - - - 1216 - 30000 - 0 - 0 - 0 - - - 1217 - 240 - 0 - 0 - 0 - - - 1218 - 240 - 0 - 0 - 0 - - - 1219 - 35 - 0 - 0 - 0 - - - 1220 - 35 - 0 - 0 - 0 - - - 1221 - 10 - 0 - 0 - 0 - - - 1222 - 10 - 0 - 0 - 0 - - - 1223 - 125 - 0 - 0 - 0 - - - 1224 - 125 - 0 - 0 - 0 - - - 1225 - 325 - 0 - 0 - 0 - - - 1226 - 325 - 0 - 0 - 0 - - - 1227 - 800 - 0 - 0 - 0 - - - 1228 - 800 - 0 - 0 - 0 - - - 1229 - 8000 - 0 - 0 - 0 - - - 1230 - 8000 - 0 - 0 - 0 - - - 1231 - 24000 - 0 - 0 - 0 - - - 1232 - 24000 - 0 - 0 - 0 - - - 1233 - 240 - 0 - 0 - 0 - - - 1234 - 240 - 0 - 0 - 0 - - - 1235 - 565 - 0 - 0 - 0 - - - 1236 - 565 - 0 - 0 - 0 - - - 1237 - 26 - 0 - 0 - 0 - - - 1238 - 26 - 0 - 0 - 0 - - - 1239 - 91 - 0 - 0 - 0 - - - 1240 - 91 - 0 - 0 - 0 - - - 1241 - 325 - 0 - 0 - 0 - - - 1242 - 325 - 0 - 0 - 0 - - - 1243 - 845 - 0 - 0 - 0 - - - 1244 - 845 - 0 - 0 - 0 - - - 1245 - 2080 - 0 - 0 - 0 - - - 1246 - 2080 - 0 - 0 - 0 - - - 1247 - 20800 - 0 - 0 - 0 - - - 1248 - 20800 - 0 - 0 - 0 - - - 1249 - 62400 - 0 - 0 - 0 - - - 1250 - 62400 - 0 - 0 - 0 - - - 1251 - 26 - 0 - 0 - 0 - - - 1252 - 26 - 0 - 0 - 0 - - - 1253 - 91 - 0 - 0 - 0 - - - 1254 - 91 - 0 - 0 - 0 - - - 1255 - 325 - 0 - 0 - 0 - - - 1256 - 325 - 0 - 0 - 0 - - - 1257 - 845 - 0 - 0 - 0 - - - 1258 - 845 - 0 - 0 - 0 - - - 1259 - 2080 - 0 - 0 - 0 - - - 1260 - 2080 - 0 - 0 - 0 - - - 1261 - 20800 - 0 - 0 - 0 - - - 1262 - 20800 - 0 - 0 - 0 - - - 1263 - 62400 - 0 - 0 - 0 - - - 1264 - 62400 - 0 - 0 - 0 - - - 1265 - 1 - 0 - 0 - 0 - - - 1266 - 1 - 0 - 0 - 0 - - - 1267 - 140 - 0 - 0 - 0 - - - 1268 - 140 - 0 - 0 - 0 - - - 1269 - 500 - 0 - 0 - 0 - - - 1270 - 500 - 0 - 0 - 0 - - - 1271 - 3200 - 0 - 0 - 0 - - - 1272 - 3200 - 0 - 0 - 0 - - - 1273 - 1300 - 0 - 0 - 0 - - - 1274 - 1300 - 0 - 0 - 0 - - - 1275 - 32000 - 0 - 0 - 0 - - - 1276 - 32000 - 0 - 0 - 0 - - - 1277 - 26 - 0 - 0 - 0 - - - 1278 - 26 - 0 - 0 - 0 - - - 1279 - 91 - 0 - 0 - 0 - - - 1280 - 91 - 0 - 0 - 0 - - - 1281 - 325 - 0 - 0 - 0 - - - 1282 - 325 - 0 - 0 - 0 - - - 1283 - 624 - 0 - 0 - 0 - - - 1284 - 624 - 0 - 0 - 0 - - - 1285 - 845 - 0 - 0 - 0 - - - 1286 - 845 - 0 - 0 - 0 - - - 1287 - 2080 - 0 - 0 - 0 - - - 1288 - 2080 - 0 - 0 - 0 - - - 1289 - 20800 - 0 - 0 - 0 - - - 1290 - 20800 - 0 - 0 - 0 - - - 1291 - 40 - 0 - 0 - 0 - - - 1292 - 40 - 0 - 0 - 0 - - - 1293 - 140 - 0 - 0 - 0 - - - 1294 - 140 - 0 - 0 - 0 - - - 1295 - 500 - 0 - 0 - 0 - - - 1296 - 500 - 0 - 0 - 0 - - - 1297 - 960 - 0 - 0 - 0 - - - 1298 - 960 - 0 - 0 - 0 - - - 1299 - 1300 - 0 - 0 - 0 - - - 1300 - 1300 - 0 - 0 - 0 - - - 1301 - 3200 - 0 - 0 - 0 - - - 1302 - 3200 - 0 - 0 - 0 - - - 1303 - 32000 - 0 - 0 - 0 - - - 1304 - 32000 - 0 - 0 - 0 - - - 1305 - 100000 - 0 - 0 - 0 - - - 1306 - 100000 - 0 - 0 - 0 - - - 1307 - 80 - 0 - 0 - 0 - - - 1308 - 80 - 0 - 0 - 0 - - - 1309 - 280 - 0 - 0 - 0 - - - 1310 - 280 - 0 - 0 - 0 - - - 1311 - 1000 - 0 - 0 - 0 - - - 1312 - 1000 - 0 - 0 - 0 - - - 1313 - 1920 - 0 - 0 - 0 - - - 1314 - 1920 - 0 - 0 - 0 - - - 1315 - 2600 - 0 - 0 - 0 - - - 1316 - 2600 - 0 - 0 - 0 - - - 1317 - 6400 - 0 - 0 - 0 - - - 1318 - 6400 - 0 - 0 - 0 - - - 1319 - 64000 - 0 - 0 - 0 - - - 1320 - 64000 - 0 - 0 - 0 - - - 1321 - 32 - 0 - 0 - 0 - - - 1322 - 32 - 0 - 0 - 0 - - - 1323 - 112 - 0 - 0 - 0 - - - 1324 - 112 - 0 - 0 - 0 - - - 1325 - 400 - 0 - 0 - 0 - - - 1326 - 400 - 0 - 0 - 0 - - - 1327 - 768 - 0 - 0 - 0 - - - 1328 - 768 - 0 - 0 - 0 - - - 1329 - 1040 - 0 - 0 - 0 - - - 1330 - 1040 - 0 - 0 - 0 - - - 1331 - 2560 - 0 - 0 - 0 - - - 1332 - 2560 - 0 - 0 - 0 - - - 1333 - 25600 - 0 - 0 - 0 - - - 1334 - 25600 - 0 - 0 - 0 - - - 1335 - 224 - 0 - 0 - 0 - - - 1336 - 224 - 0 - 0 - 0 - - - 1337 - 61 - 0 - 0 - 0 - - - 1338 - 61 - 0 - 0 - 0 - - - 1339 - 832 - 0 - 0 - 0 - - - 1340 - 832 - 0 - 0 - 0 - - - 1341 - 1274 - 0 - 0 - 0 - - - 1342 - 1274 - 0 - 0 - 0 - - - 1343 - 2158 - 0 - 0 - 0 - - - 1344 - 2158 - 0 - 0 - 0 - - - 1345 - 5356 - 0 - 0 - 0 - - - 1346 - 5356 - 0 - 0 - 0 - - - 1347 - 41500 - 0 - 0 - 0 - - - 1348 - 41500 - 0 - 0 - 0 - - - 1349 - 56 - 0 - 0 - 0 - - - 1350 - 56 - 0 - 0 - 0 - - - 1351 - 16 - 0 - 0 - 0 - - - 1352 - 16 - 0 - 0 - 0 - - - 1353 - 200 - 0 - 0 - 0 - - - 1354 - 200 - 0 - 0 - 0 - - - 1355 - 520 - 0 - 0 - 0 - - - 1356 - 520 - 0 - 0 - 0 - - - 1357 - 1280 - 0 - 0 - 0 - - - 1358 - 1280 - 0 - 0 - 0 - - - 1359 - 12800 - 0 - 0 - 0 - - - 1360 - 12800 - 0 - 0 - 0 - - - 1361 - 384 - 0 - 0 - 0 - - - 1362 - 384 - 0 - 0 - 0 - - - 1363 - 182 - 0 - 0 - 0 - - - 1364 - 182 - 0 - 0 - 0 - - - 1365 - 650 - 0 - 0 - 0 - - - 1366 - 650 - 0 - 0 - 0 - - - 1367 - 1248 - 0 - 0 - 0 - - - 1368 - 1248 - 0 - 0 - 0 - - - 1369 - 1690 - 0 - 0 - 0 - - - 1370 - 1690 - 0 - 0 - 0 - - - 1371 - 4160 - 0 - 0 - 0 - - - 1372 - 4160 - 0 - 0 - 0 - - - 1373 - 41600 - 0 - 0 - 0 - - - 1374 - 41600 - 0 - 0 - 0 - - - 1375 - 52 - 0 - 0 - 0 - - - 1376 - 52 - 0 - 0 - 0 - - - 1377 - 200000 - 0 - 0 - 0 - - - 1378 - 200000 - 0 - 0 - 0 - - - 1379 - 15 - 0 - 0 - 0 - - - 1380 - 15 - 0 - 0 - 0 - - - 1381 - 1500 - 0 - 0 - 0 - - - 1382 - 1500 - 0 - 0 - 0 - - - 1383 - 1500 - 0 - 0 - 0 - - - 1384 - 1500 - 0 - 0 - 0 - - - 1385 - 1500 - 0 - 0 - 0 - - - 1386 - 1500 - 0 - 0 - 0 - - - 1387 - 1500 - 0 - 0 - 0 - - - 1388 - 1500 - 0 - 0 - 0 - - - 1389 - 200 - 0 - 0 - 0 - - - 1390 - 200 - 0 - 0 - 0 - - - 1391 - 7000 - 0 - 0 - 0 - - - 1392 - 7000 - 0 - 0 - 0 - - - 1393 - 15500 - 0 - 0 - 0 - - - 1394 - 15500 - 0 - 0 - 0 - - - 1395 - 15500 - 0 - 0 - 0 - - - 1396 - 15500 - 0 - 0 - 0 - - - 1397 - 15500 - 0 - 0 - 0 - - - 1398 - 15500 - 0 - 0 - 0 - - - 1399 - 15500 - 0 - 0 - 0 - - - 1400 - 15500 - 0 - 0 - 0 - - - 1401 - 42500 - 0 - 0 - 0 - - - 1402 - 42500 - 0 - 0 - 0 - - - 1403 - 42500 - 0 - 0 - 0 - - - 1404 - 42500 - 0 - 0 - 0 - - - 1405 - 42500 - 0 - 0 - 0 - - - 1406 - 42500 - 0 - 0 - 0 - - - 1407 - 42500 - 0 - 0 - 0 - - - 1408 - 42500 - 0 - 0 - 0 - - - 1409 - 42500 - 0 - 0 - 0 - - - 1410 - 20 - 0 - 0 - 0 - - - 1411 - 1 - 0 - 0 - 0 - - - 1412 - 1 - 0 - 0 - 0 - - - 1413 - 1 - 0 - 0 - 0 - - - 1414 - 1 - 0 - 0 - 0 - - - 1415 - 1 - 0 - 0 - 0 - - - 1416 - 1 - 0 - 0 - 0 - - - 1417 - 1 - 0 - 0 - 0 - - - 1418 - 1 - 0 - 0 - 0 - - - 1419 - 1 - 0 - 0 - 0 - - - 1420 - 63 - 0 - 0 - 0 - - - 1421 - 63 - 0 - 0 - 0 - - - 1422 - 18 - 0 - 0 - 0 - - - 1423 - 18 - 0 - 0 - 0 - - - 1424 - 225 - 0 - 0 - 0 - - - 1425 - 225 - 0 - 0 - 0 - - - 1426 - 432 - 0 - 0 - 0 - - - 1427 - 432 - 0 - 0 - 0 - - - 1428 - 585 - 0 - 0 - 0 - - - 1429 - 585 - 0 - 0 - 0 - - - 1430 - 1440 - 0 - 0 - 0 - - - 1431 - 1440 - 0 - 0 - 0 - - - 1432 - 14400 - 0 - 0 - 0 - - - 1433 - 14400 - 0 - 0 - 0 - - - 1434 - 50000 - 0 - 0 - 0 - - - 1435 - 50000 - 0 - 0 - 0 - - - 1436 - 4 - 0 - 0 - 0 - - - 1437 - 4 - 0 - 0 - 0 - - - 1438 - 4 - 0 - 0 - 0 - - - 1439 - 4 - 0 - 0 - 0 - - - 1440 - 4 - 0 - 0 - 0 - - - 1441 - 4 - 0 - 0 - 0 - - - 1442 - 4 - 0 - 0 - 0 - - - 1443 - 4 - 0 - 0 - 0 - - - 1444 - 4 - 0 - 0 - 0 - - - 1445 - 4 - 0 - 0 - 0 - - - 1446 - 4 - 0 - 0 - 0 - - - 1447 - 4 - 0 - 0 - 0 - - - 1448 - 4 - 0 - 0 - 0 - - - 1449 - 4 - 0 - 0 - 0 - - - 1450 - 4 - 0 - 0 - 0 - - - 1451 - 4 - 0 - 0 - 0 - - - 1452 - 4 - 0 - 0 - 0 - - - 1453 - 4 - 0 - 0 - 0 - - - 1454 - 4 - 0 - 0 - 0 - - - 1455 - 4 - 0 - 0 - 0 - - - 1456 - 4 - 0 - 0 - 0 - - - 1457 - 4 - 0 - 0 - 0 - - - 1458 - 4 - 0 - 0 - 0 - - - 1459 - 4 - 0 - 0 - 0 - - - 1460 - 4 - 0 - 0 - 0 - - - 1461 - 4 - 0 - 0 - 0 - - - 1462 - 4 - 0 - 0 - 0 - - - 1463 - 4 - 0 - 0 - 0 - - - 1464 - 25 - 0 - 0 - 0 - - - 1465 - 11 - 0 - 0 - 0 - - - 1466 - 4 - 0 - 0 - 0 - - - 1467 - 1 - 0 - 0 - 0 - - - 1468 - 1 - 0 - 0 - 0 - - - 1469 - 1 - 0 - 0 - 0 - - - 1470 - 4 - 0 - 0 - 0 - - - 1471 - 4 - 0 - 0 - 0 - - - 1472 - 4 - 0 - 0 - 0 - - - 1473 - 4 - 0 - 0 - 0 - - - 1474 - 4 - 0 - 0 - 0 - - - 1475 - 4 - 0 - 0 - 0 - - - 1476 - 4 - 0 - 0 - 0 - - - 1477 - 4 - 0 - 0 - 0 - - - 1478 - 100 - 0 - 0 - 0 - - - 1479 - 100 - 0 - 0 - 0 - - - 1480 - 1 - 0 - 0 - 0 - - - 1481 - 10 - 0 - 0 - 0 - - - 1482 - 10 - 0 - 0 - 0 - - - 1483 - 10 - 0 - 0 - 0 - - - 1484 - 20 - 0 - 0 - 0 - - - 1485 - 10 - 0 - 0 - 0 - - - 1486 - 10 - 0 - 0 - 0 - - - 1487 - 20 - 0 - 0 - 0 - - - 1488 - 10 - 0 - 0 - 0 - - - 1489 - 10 - 0 - 0 - 0 - - - 1490 - 10 - 0 - 0 - 0 - - - 1491 - 1 - 0 - 0 - 0 - - - 1492 - 2 - 0 - 0 - 0 - - - 1493 - 1 - 0 - 0 - 0 - - - 1494 - 1 - 0 - 0 - 0 - - - 1495 - 6 - 0 - 0 - 0 - - - 1496 - 1 - 0 - 0 - 0 - - - 1497 - 1 - 0 - 0 - 0 - - - 1498 - 1 - 0 - 0 - 0 - - - 1499 - 1 - 0 - 0 - 0 - - - 1500 - 2 - 0 - 0 - 0 - - - 1501 - 2 - 0 - 0 - 0 - - - 1502 - 2 - 0 - 0 - 0 - - - 1503 - 5 - 0 - 0 - 0 - - - 1504 - 2 - 0 - 0 - 0 - - - 1505 - 1 - 0 - 0 - 0 - - - 1506 - 2 - 0 - 0 - 0 - - - 1507 - 1 - 0 - 0 - 0 - - - 1508 - 2 - 0 - 0 - 0 - - - 1509 - 1 - 0 - 0 - 0 - - - 1510 - 1 - 0 - 0 - 0 - - - 1511 - 4 - 0 - 0 - 0 - - - 1512 - 4 - 0 - 0 - 0 - - - 1513 - 320 - 0 - 0 - 0 - - - 1514 - 320 - 0 - 0 - 0 - - - 1515 - 160 - 0 - 0 - 0 - - - 1516 - 160 - 0 - 0 - 0 - - - 1517 - 80 - 0 - 0 - 0 - - - 1518 - 80 - 0 - 0 - 0 - - - 1519 - 40 - 0 - 0 - 0 - - - 1520 - 40 - 0 - 0 - 0 - - - 1521 - 20 - 0 - 0 - 0 - - - 1522 - 20 - 0 - 0 - 0 - - - 1523 - 20 - 0 - 0 - 0 - - - 1524 - 20 - 0 - 0 - 0 - - - 1525 - 1 - 0 - 0 - 0 - - - 1526 - 5 - 0 - 0 - 0 - - - 1527 - 1 - 0 - 0 - 0 - - - 1528 - 5 - 0 - 0 - 0 - - - 1529 - 1 - 0 - 0 - 0 - - - 1530 - 5 - 0 - 0 - 0 - - - 1531 - 1 - 0 - 0 - 0 - - - 1532 - 5 - 0 - 0 - 0 - - - 1533 - 1 - 0 - 0 - 0 - - - 1534 - 5 - 0 - 0 - 0 - - - 1535 - 1 - 0 - 0 - 0 - - - 1536 - 1 - 0 - 0 - 0 - - - 1537 - 1 - 0 - 0 - 0 - - - 1538 - 1 - 0 - 0 - 0 - - - 1539 - 52 - 0 - 0 - 0 - - - 1540 - 20 - 0 - 0 - 0 - - - 1541 - 20 - 0 - 0 - 0 - - - 1542 - 1 - 0 - 0 - 0 - - - 1543 - 1 - 0 - 0 - 0 - - - 1544 - 1 - 0 - 0 - 0 - - - 1545 - 1 - 0 - 0 - 0 - - - 1546 - 1 - 0 - 0 - 0 - - - 1547 - 1 - 0 - 0 - 0 - - - 1548 - 1 - 0 - 0 - 0 - - - 1549 - 8 - 0 - 0 - 0 - - - 1550 - 3 - 0 - 0 - 0 - - - 1551 - 3 - 0 - 0 - 0 - - - 1552 - 10 - 0 - 0 - 0 - - - 1553 - 1 - 0 - 0 - 0 - - - 1554 - 1 - 0 - 0 - 0 - - - 1555 - 1 - 0 - 0 - 0 - - - 1556 - 1 - 0 - 0 - 0 - - - 1557 - 1 - 0 - 0 - 0 - - - 1558 - 1 - 0 - 0 - 0 - - - 1559 - 1 - 0 - 0 - 0 - - - 1560 - 1 - 0 - 0 - 0 - - - 1561 - 1 - 0 - 0 - 0 - - - 1562 - 1 - 0 - 0 - 0 - - - 1563 - 1 - 0 - 0 - 0 - - - 1564 - 1 - 0 - 0 - 0 - - - 1565 - 1 - 0 - 0 - 0 - - - 1566 - 1 - 0 - 0 - 0 - - - 1567 - 1 - 0 - 0 - 0 - - - 1568 - 1 - 0 - 0 - 0 - - - 1569 - 1 - 0 - 0 - 0 - - - 1570 - 1 - 0 - 0 - 0 - - - 1571 - 1 - 0 - 0 - 0 - - - 1572 - 1 - 0 - 0 - 0 - - - 1573 - 2 - 0 - 0 - 0 - - - 1574 - 1 - 0 - 0 - 0 - - - 1575 - 350 - 0 - 0 - 0 - - - 1576 - 1 - 0 - 0 - 0 - - - 1577 - 5 - 0 - 0 - 0 - - - 1578 - 5 - 0 - 0 - 0 - - - 1579 - 2 - 0 - 0 - 0 - - - 1580 - 6 - 0 - 0 - 0 - - - 1581 - 5 - 0 - 0 - 0 - - - 1582 - 10 - 0 - 0 - 0 - - - 1583 - 2 - 0 - 0 - 0 - - - 1584 - 1 - 0 - 0 - 0 - - - 1585 - 15 - 0 - 0 - 0 - - - 1586 - 1 - 0 - 0 - 0 - - - 1587 - 1 - 0 - 0 - 0 - - - 1588 - 1 - 0 - 0 - 0 - - - 1589 - 1 - 0 - 0 - 0 - - - 1590 - 1 - 0 - 0 - 0 - - - 1591 - 1 - 0 - 0 - 0 - - - 1592 - 5 - 0 - 0 - 0 - - - 1593 - 5 - 0 - 0 - 0 - - - 1594 - 200 - 0 - 0 - 0 - - - 1595 - 5 - 0 - 0 - 0 - - - 1596 - 5 - 0 - 0 - 0 - - - 1597 - 5 - 0 - 0 - 0 - - - 1598 - 5 - 0 - 0 - 0 - - - 1599 - 5 - 0 - 0 - 0 - - - 1600 - 5 - 0 - 0 - 0 - - - 1601 - 2000 - 0 - 0 - 0 - - - 1602 - 2000 - 0 - 0 - 0 - - - 1603 - 1000 - 0 - 0 - 0 - - - 1604 - 1000 - 0 - 0 - 0 - - - 1605 - 500 - 0 - 0 - 0 - - - 1606 - 500 - 0 - 0 - 0 - - - 1607 - 250 - 0 - 0 - 0 - - - 1608 - 250 - 0 - 0 - 0 - - - 1609 - 100 - 0 - 0 - 0 - - - 1610 - 100 - 0 - 0 - 0 - - - 1611 - 150 - 0 - 0 - 0 - - - 1612 - 150 - 0 - 0 - 0 - - - 1613 - 200 - 0 - 0 - 0 - - - 1614 - 200 - 0 - 0 - 0 - - - 1615 - 10000 - 0 - 0 - 0 - - - 1616 - 10000 - 0 - 0 - 0 - - - 1617 - 200 - 0 - 0 - 0 - - - 1618 - 200 - 0 - 0 - 0 - - - 1619 - 100 - 0 - 0 - 0 - - - 1620 - 100 - 0 - 0 - 0 - - - 1621 - 50 - 0 - 0 - 0 - - - 1622 - 50 - 0 - 0 - 0 - - - 1623 - 25 - 0 - 0 - 0 - - - 1624 - 25 - 0 - 0 - 0 - - - 1625 - 20 - 0 - 0 - 0 - - - 1626 - 20 - 0 - 0 - 0 - - - 1627 - 30 - 0 - 0 - 0 - - - 1628 - 30 - 0 - 0 - 0 - - - 1629 - 40 - 0 - 0 - 0 - - - 1630 - 40 - 0 - 0 - 0 - - - 1631 - 1000 - 0 - 0 - 0 - - - 1632 - 1000 - 0 - 0 - 0 - - - 1633 - 1 - 0 - 0 - 0 - - - 1634 - 1 - 0 - 0 - 0 - - - 1635 - 350 - 0 - 0 - 0 - - - 1636 - 350 - 0 - 0 - 0 - - - 1637 - 900 - 0 - 0 - 0 - - - 1638 - 900 - 0 - 0 - 0 - - - 1639 - 1275 - 0 - 0 - 0 - - - 1640 - 1275 - 0 - 0 - 0 - - - 1641 - 2025 - 0 - 0 - 0 - - - 1642 - 2025 - 0 - 0 - 0 - - - 1643 - 3525 - 0 - 0 - 0 - - - 1644 - 3525 - 0 - 0 - 0 - - - 1645 - 17625 - 0 - 0 - 0 - - - 1646 - 17625 - 0 - 0 - 0 - - - 1647 - 1 - 0 - 0 - 0 - - - 1648 - 1 - 0 - 0 - 0 - - - 1649 - 1 - 0 - 0 - 0 - - - 1650 - 1 - 0 - 0 - 0 - - - 1651 - 1 - 0 - 0 - 0 - - - 1652 - 1 - 0 - 0 - 0 - - - 1653 - 1 - 0 - 0 - 0 - - - 1654 - 450 - 0 - 0 - 0 - - - 1655 - 450 - 0 - 0 - 0 - - - 1656 - 1050 - 0 - 0 - 0 - - - 1657 - 1050 - 0 - 0 - 0 - - - 1658 - 1425 - 0 - 0 - 0 - - - 1659 - 1425 - 0 - 0 - 0 - - - 1660 - 2175 - 0 - 0 - 0 - - - 1661 - 2175 - 0 - 0 - 0 - - - 1662 - 3675 - 0 - 0 - 0 - - - 1663 - 3675 - 0 - 0 - 0 - - - 1664 - 18375 - 0 - 0 - 0 - - - 1665 - 18375 - 0 - 0 - 0 - - - 1666 - 1 - 0 - 0 - 0 - - - 1667 - 1 - 0 - 0 - 0 - - - 1668 - 1 - 0 - 0 - 0 - - - 1669 - 1 - 0 - 0 - 0 - - - 1670 - 1 - 0 - 0 - 0 - - - 1671 - 1 - 0 - 0 - 0 - - - 1672 - 1 - 0 - 0 - 0 - - - 1673 - 350 - 0 - 0 - 0 - - - 1674 - 350 - 0 - 0 - 0 - - - 1675 - 900 - 0 - 0 - 0 - - - 1676 - 900 - 0 - 0 - 0 - - - 1677 - 1275 - 0 - 0 - 0 - - - 1678 - 1275 - 0 - 0 - 0 - - - 1679 - 2025 - 0 - 0 - 0 - - - 1680 - 2025 - 0 - 0 - 0 - - - 1681 - 3525 - 0 - 0 - 0 - - - 1682 - 3525 - 0 - 0 - 0 - - - 1683 - 17625 - 0 - 0 - 0 - - - 1684 - 17625 - 0 - 0 - 0 - - - 1685 - 1 - 0 - 0 - 0 - - - 1686 - 1 - 0 - 0 - 0 - - - 1687 - 1 - 0 - 0 - 0 - - - 1688 - 1 - 0 - 0 - 0 - - - 1689 - 1 - 0 - 0 - 0 - - - 1690 - 1 - 0 - 0 - 0 - - - 1691 - 1 - 0 - 0 - 0 - - - 1692 - 350 - 0 - 0 - 0 - - - 1693 - 350 - 0 - 0 - 0 - - - 1694 - 900 - 0 - 0 - 0 - - - 1695 - 900 - 0 - 0 - 0 - - - 1696 - 1275 - 0 - 0 - 0 - - - 1697 - 1275 - 0 - 0 - 0 - - - 1698 - 2025 - 0 - 0 - 0 - - - 1699 - 2025 - 0 - 0 - 0 - - - 1700 - 3525 - 0 - 0 - 0 - - - 1701 - 3525 - 0 - 0 - 0 - - - 1702 - 17625 - 0 - 0 - 0 - - - 1703 - 17625 - 0 - 0 - 0 - - - 1704 - 17625 - 0 - 0 - 0 - - - 1705 - 17625 - 0 - 0 - 0 - - - 1706 - 17625 - 0 - 0 - 0 - - - 1707 - 17625 - 0 - 0 - 0 - - - 1708 - 17625 - 0 - 0 - 0 - - - 1709 - 17625 - 0 - 0 - 0 - - - 1710 - 17625 - 0 - 0 - 0 - - - 1711 - 17625 - 0 - 0 - 0 - - - 1712 - 17625 - 0 - 0 - 0 - - - 1713 - 17625 - 0 - 0 - 0 - - - 1714 - 200 - 0 - 0 - 0 - - - 1715 - 200 - 0 - 0 - 0 - - - 1716 - 200 - 0 - 0 - 0 - - - 1717 - 200 - 0 - 0 - 0 - - - 1718 - 300 - 0 - 0 - 0 - - - 1719 - 300 - 0 - 0 - 0 - - - 1720 - 200 - 0 - 0 - 0 - - - 1721 - 200 - 0 - 0 - 0 - - - 1722 - 200 - 0 - 0 - 0 - - - 1723 - 200 - 0 - 0 - 0 - - - 1724 - 200 - 0 - 0 - 0 - - - 1725 - 2025 - 0 - 0 - 0 - - - 1726 - 2025 - 0 - 0 - 0 - - - 1727 - 900 - 0 - 0 - 0 - - - 1728 - 900 - 0 - 0 - 0 - - - 1729 - 1275 - 0 - 0 - 0 - - - 1730 - 1275 - 0 - 0 - 0 - - - 1731 - 3525 - 0 - 0 - 0 - - - 1732 - 3525 - 0 - 0 - 0 - - - 1733 - 1 - 0 - 0 - 0 - - - 1734 - 4 - 0 - 0 - 0 - - - 1735 - 1 - 0 - 0 - 0 - - - 1736 - 1 - 0 - 0 - 0 - - - 1737 - 1 - 0 - 0 - 0 - - - 1738 - 1 - 0 - 0 - 0 - - - 1739 - 1 - 0 - 0 - 0 - - - 1740 - 1 - 0 - 0 - 0 - - - 1741 - 1 - 0 - 0 - 0 - - - 1742 - 1 - 0 - 0 - 0 - - - 1743 - 1 - 0 - 0 - 0 - - - 1744 - 1 - 0 - 0 - 0 - - - 1745 - 50 - 0 - 0 - 0 - - - 1746 - 50 - 0 - 0 - 0 - - - 1747 - 80 - 0 - 0 - 0 - - - 1748 - 80 - 0 - 0 - 0 - - - 1749 - 60 - 0 - 0 - 0 - - - 1750 - 60 - 0 - 0 - 0 - - - 1751 - 40 - 0 - 0 - 0 - - - 1752 - 40 - 0 - 0 - 0 - - - 1753 - 20 - 0 - 0 - 0 - - - 1754 - 20 - 0 - 0 - 0 - - - 1755 - 1 - 0 - 0 - 0 - - - 1756 - 1 - 0 - 0 - 0 - - - 1757 - 2 - 0 - 0 - 0 - - - 1758 - 2 - 0 - 0 - 0 - - - 1759 - 5 - 0 - 0 - 0 - - - 1760 - 5 - 0 - 0 - 0 - - - 1761 - 2 - 0 - 0 - 0 - - - 1762 - 2 - 0 - 0 - 0 - - - 1763 - 6 - 0 - 0 - 0 - - - 1764 - 6 - 0 - 0 - 0 - - - 1765 - 6 - 0 - 0 - 0 - - - 1766 - 6 - 0 - 0 - 0 - - - 1767 - 6 - 0 - 0 - 0 - - - 1768 - 6 - 0 - 0 - 0 - - - 1769 - 6 - 0 - 0 - 0 - - - 1770 - 6 - 0 - 0 - 0 - - - 1771 - 6 - 0 - 0 - 0 - - - 1772 - 6 - 0 - 0 - 0 - - - 1773 - 6 - 0 - 0 - 0 - - - 1774 - 6 - 0 - 0 - 0 - - - 1775 - 3 - 0 - 0 - 0 - - - 1776 - 3 - 0 - 0 - 0 - - - 1777 - 10 - 0 - 0 - 0 - - - 1778 - 10 - 0 - 0 - 0 - - - 1779 - 5 - 0 - 0 - 0 - - - 1780 - 5 - 0 - 0 - 0 - - - 1781 - 2 - 0 - 0 - 0 - - - 1782 - 2 - 0 - 0 - 0 - - - 1783 - 2 - 0 - 0 - 0 - - - 1784 - 2 - 0 - 0 - 0 - - - 1785 - 2 - 0 - 0 - 0 - - - 1786 - 2 - 0 - 0 - 0 - - - 1787 - 1 - 0 - 0 - 0 - - - 1788 - 1 - 0 - 0 - 0 - - - 1789 - 3 - 0 - 0 - 0 - - - 1790 - 3 - 0 - 0 - 0 - - - 1791 - 2 - 0 - 0 - 0 - - - 1792 - 2 - 0 - 0 - 0 - - - 1793 - 1 - 0 - 0 - 0 - - - 1794 - 20 - 0 - 0 - 0 - - - 1795 - 20 - 0 - 0 - 0 - - - 1796 - 1 - 0 - 0 - 0 - - - 1797 - 1 - 0 - 0 - 0 - - - 1798 - 1 - 0 - 0 - 0 - - - 1799 - 1 - 0 - 0 - 0 - - - 1800 - 1 - 0 - 0 - 0 - - - 1801 - 1 - 0 - 0 - 0 - - - 1802 - 1 - 0 - 0 - 0 - - - 1803 - 1 - 0 - 0 - 0 - - - 1804 - 1 - 0 - 0 - 0 - - - 1805 - 1 - 0 - 0 - 0 - - - 1806 - 1 - 0 - 0 - 0 - - - 1807 - 1 - 0 - 0 - 0 - - - 1808 - 1 - 0 - 0 - 0 - - - 1809 - 1 - 0 - 0 - 0 - - - 1810 - 1 - 0 - 0 - 0 - - - 1811 - 1 - 0 - 0 - 0 - - - 1812 - 1 - 0 - 0 - 0 - - - 1813 - 1 - 0 - 0 - 0 - - - 1814 - 1 - 0 - 0 - 0 - - - 1815 - 1 - 0 - 0 - 0 - - - 1816 - 1 - 0 - 0 - 0 - - - 1817 - 1 - 0 - 0 - 0 - - - 1818 - 1 - 0 - 0 - 0 - - - 1819 - 1 - 0 - 0 - 0 - - - 1820 - 1 - 0 - 0 - 0 - - - 1821 - 1 - 0 - 0 - 0 - - - 1822 - 1 - 0 - 0 - 0 - - - 1823 - 30 - 0 - 0 - 0 - - - 1824 - 30 - 0 - 0 - 0 - - - 1825 - 27 - 0 - 0 - 0 - - - 1826 - 27 - 0 - 0 - 0 - - - 1827 - 24 - 0 - 0 - 0 - - - 1828 - 24 - 0 - 0 - 0 - - - 1829 - 18 - 0 - 0 - 0 - - - 1830 - 18 - 0 - 0 - 0 - - - 1831 - 15 - 0 - 0 - 0 - - - 1832 - 15 - 0 - 0 - 0 - - - 1833 - 40 - 0 - 0 - 0 - - - 1834 - 40 - 0 - 0 - 0 - - - 1835 - 40 - 0 - 0 - 0 - - - 1836 - 40 - 0 - 0 - 0 - - - 1837 - 20 - 0 - 0 - 0 - - - 1838 - 20 - 0 - 0 - 0 - - - 1839 - 1 - 0 - 0 - 0 - - - 1840 - 1 - 0 - 0 - 0 - - - 1841 - 1 - 0 - 0 - 0 - - - 1842 - 1 - 0 - 0 - 0 - - - 1843 - 1 - 0 - 0 - 0 - - - 1844 - 40 - 0 - 0 - 0 - - - 1845 - 40 - 0 - 0 - 0 - - - 1846 - 1 - 0 - 0 - 0 - - - 1847 - 10 - 0 - 0 - 0 - - - 1848 - 1 - 0 - 0 - 0 - - - 1849 - 2 - 0 - 0 - 0 - - - 1850 - 1 - 0 - 0 - 0 - - - 1851 - 1 - 0 - 0 - 0 - - - 1852 - 1 - 0 - 0 - 0 - - - 1853 - 1 - 0 - 0 - 0 - - - 1854 - 5 - 0 - 0 - 0 - - - 1855 - 1 - 0 - 0 - 0 - - - 1856 - 1 - 0 - 0 - 0 - - - 1857 - 10 - 0 - 0 - 0 - - - 1858 - 10 - 0 - 0 - 0 - - - 1859 - 2 - 0 - 0 - 0 - - - 1860 - 2 - 0 - 0 - 0 - - - 1861 - 5 - 0 - 0 - 0 - - - 1862 - 5 - 0 - 0 - 0 - - - 1863 - 4 - 0 - 0 - 0 - - - 1864 - 4 - 0 - 0 - 0 - - - 1865 - 10 - 0 - 0 - 0 - - - 1866 - 10 - 0 - 0 - 0 - - - 1867 - 1 - 0 - 0 - 0 - - - 1868 - 1 - 0 - 0 - 0 - - - 1869 - 3 - 0 - 0 - 0 - - - 1870 - 3 - 0 - 0 - 0 - - - 1871 - 3 - 0 - 0 - 0 - - - 1872 - 3 - 0 - 0 - 0 - - - 1873 - 5 - 0 - 0 - 0 - - - 1874 - 5 - 0 - 0 - 0 - - - 1875 - 5 - 0 - 0 - 0 - - - 1876 - 5 - 0 - 0 - 0 - - - 1877 - 7 - 0 - 0 - 0 - - - 1878 - 7 - 0 - 0 - 0 - - - 1879 - 7 - 0 - 0 - 0 - - - 1880 - 7 - 0 - 0 - 0 - - - 1881 - 9 - 0 - 0 - 0 - - - 1882 - 9 - 0 - 0 - 0 - - - 1883 - 20 - 0 - 0 - 0 - - - 1884 - 20 - 0 - 0 - 0 - - - 1885 - 20 - 0 - 0 - 0 - - - 1886 - 20 - 0 - 0 - 0 - - - 1887 - 10 - 0 - 0 - 0 - - - 1888 - 10 - 0 - 0 - 0 - - - 1889 - 20 - 0 - 0 - 0 - - - 1890 - 20 - 0 - 0 - 0 - - - 1891 - 50 - 0 - 0 - 0 - - - 1892 - 50 - 0 - 0 - 0 - - - 1893 - 30 - 0 - 0 - 0 - - - 1894 - 30 - 0 - 0 - 0 - - - 1895 - 10 - 0 - 0 - 0 - - - 1896 - 10 - 0 - 0 - 0 - - - 1897 - 70 - 0 - 0 - 0 - - - 1898 - 70 - 0 - 0 - 0 - - - 1899 - 50 - 0 - 0 - 0 - - - 1900 - 50 - 0 - 0 - 0 - - - 1901 - 30 - 0 - 0 - 0 - - - 1902 - 30 - 0 - 0 - 0 - - - 1903 - 1 - 0 - 0 - 0 - - - 1904 - 1 - 0 - 0 - 0 - - - 1905 - 2 - 0 - 0 - 0 - - - 1906 - 2 - 0 - 0 - 0 - - - 1907 - 2 - 0 - 0 - 0 - - - 1908 - 2 - 0 - 0 - 0 - - - 1909 - 2 - 0 - 0 - 0 - - - 1910 - 2 - 0 - 0 - 0 - - - 1911 - 2 - 0 - 0 - 0 - - - 1912 - 2 - 0 - 0 - 0 - - - 1913 - 2 - 0 - 0 - 0 - - - 1914 - 2 - 0 - 0 - 0 - - - 1915 - 3 - 0 - 0 - 0 - - - 1916 - 3 - 0 - 0 - 0 - - - 1917 - 2 - 0 - 0 - 0 - - - 1918 - 2 - 0 - 0 - 0 - - - 1919 - 2 - 0 - 0 - 0 - - - 1920 - 2 - 0 - 0 - 0 - - - 1921 - 4 - 0 - 0 - 0 - - - 1922 - 4 - 0 - 0 - 0 - - - 1923 - 4 - 0 - 0 - 0 - - - 1924 - 4 - 0 - 0 - 0 - - - 1925 - 2 - 0 - 0 - 0 - - - 1926 - 2 - 0 - 0 - 0 - - - 1927 - 12 - 0 - 0 - 0 - - - 1928 - 12 - 0 - 0 - 0 - - - 1929 - 6 - 0 - 0 - 0 - - - 1930 - 6 - 0 - 0 - 0 - - - 1931 - 1 - 0 - 0 - 0 - - - 1932 - 1 - 0 - 0 - 0 - - - 1933 - 14 - 0 - 0 - 0 - - - 1934 - 14 - 0 - 0 - 0 - - - 1935 - 1 - 0 - 0 - 0 - - - 1936 - 1 - 0 - 0 - 0 - - - 1937 - 1 - 0 - 0 - 0 - - - 1938 - 1 - 0 - 0 - 0 - - - 1939 - 1 - 0 - 0 - 0 - - - 1940 - 1 - 0 - 0 - 0 - - - 1941 - 31 - 0 - 0 - 0 - - - 1942 - 16 - 0 - 0 - 0 - - - 1943 - 16 - 0 - 0 - 0 - - - 1944 - 4 - 0 - 0 - 0 - - - 1945 - 4 - 0 - 0 - 0 - - - 1946 - 2 - 0 - 0 - 0 - - - 1947 - 2 - 0 - 0 - 0 - - - 1948 - 2 - 0 - 0 - 0 - - - 1949 - 2 - 0 - 0 - 0 - - - 1950 - 2 - 0 - 0 - 0 - - - 1951 - 3 - 0 - 0 - 0 - - - 1952 - 3 - 0 - 0 - 0 - - - 1953 - 1 - 0 - 0 - 0 - - - 1954 - 1 - 0 - 0 - 0 - - - 1955 - 36 - 0 - 0 - 0 - - - 1956 - 36 - 0 - 0 - 0 - - - 1957 - 12 - 0 - 0 - 0 - - - 1958 - 12 - 0 - 0 - 0 - - - 1959 - 30 - 0 - 0 - 0 - - - 1960 - 30 - 0 - 0 - 0 - - - 1961 - 10 - 0 - 0 - 0 - - - 1962 - 10 - 0 - 0 - 0 - - - 1963 - 2 - 0 - 0 - 0 - - - 1964 - 2 - 0 - 0 - 0 - - - 1965 - 1 - 0 - 0 - 0 - - - 1966 - 1 - 0 - 0 - 0 - - - 1967 - 1 - 0 - 0 - 0 - - - 1968 - 1 - 0 - 0 - 0 - - - 1969 - 1 - 0 - 0 - 0 - - - 1970 - 1 - 0 - 0 - 0 - - - 1971 - 3 - 0 - 0 - 0 - - - 1972 - 3 - 0 - 0 - 0 - - - 1973 - 20 - 0 - 0 - 0 - - - 1974 - 20 - 0 - 0 - 0 - - - 1975 - 20 - 0 - 0 - 0 - - - 1976 - 20 - 0 - 0 - 0 - - - 1977 - 2 - 0 - 0 - 0 - - - 1978 - 10 - 0 - 0 - 0 - - - 1979 - 10 - 0 - 0 - 0 - - - 1980 - 2 - 0 - 0 - 0 - - - 1981 - 2 - 0 - 0 - 0 - - - 1982 - 14 - 0 - 0 - 0 - - - 1983 - 14 - 0 - 0 - 0 - - - 1984 - 1 - 0 - 0 - 0 - - - 1985 - 8 - 0 - 0 - 0 - - - 1986 - 8 - 0 - 0 - 0 - - - 1987 - 1 - 0 - 0 - 0 - - - 1988 - 1 - 0 - 0 - 0 - - - 1989 - 1 - 0 - 0 - 0 - - - 1990 - 1 - 0 - 0 - 0 - - - 1991 - 1 - 0 - 0 - 0 - - - 1992 - 1 - 0 - 0 - 0 - - - 1993 - 128 - 0 - 0 - 0 - - - 1994 - 128 - 0 - 0 - 0 - - - 1995 - 10 - 0 - 0 - 0 - - - 1996 - 10 - 0 - 0 - 0 - - - 1997 - 4 - 0 - 0 - 0 - - - 1998 - 4 - 0 - 0 - 0 - - - 1999 - 4 - 0 - 0 - 0 - - - 2000 - 4 - 0 - 0 - 0 - - - 2001 - 10 - 0 - 0 - 0 - - - 2002 - 10 - 0 - 0 - 0 - - - 2003 - 100 - 0 - 0 - 0 - - - 2004 - 100 - 0 - 0 - 0 - - - 2005 - 1 - 0 - 0 - 0 - - - 2006 - 1 - 0 - 0 - 0 - - - 2007 - 230 - 0 - 0 - 0 - - - 2008 - 230 - 0 - 0 - 0 - - - 2009 - 10 - 0 - 0 - 0 - - - 2010 - 10 - 0 - 0 - 0 - - - 2011 - 20 - 0 - 0 - 0 - - - 2012 - 20 - 0 - 0 - 0 - - - 2013 - 1 - 0 - 0 - 0 - - - 2014 - 1 - 0 - 0 - 0 - - - 2015 - 5 - 0 - 0 - 0 - - - 2016 - 5 - 0 - 0 - 0 - - - 2017 - 5 - 0 - 0 - 0 - - - 2018 - 5 - 0 - 0 - 0 - - - 2019 - 5 - 0 - 0 - 0 - - - 2020 - 5 - 0 - 0 - 0 - - - 2021 - 5 - 0 - 0 - 0 - - - 2022 - 5 - 0 - 0 - 0 - - - 2023 - 2 - 0 - 0 - 0 - - - 2024 - 2 - 0 - 0 - 0 - - - 2025 - 2 - 0 - 0 - 0 - - - 2026 - 1 - 0 - 0 - 0 - - - 2027 - 1 - 0 - 0 - 0 - - - 2028 - 30 - 0 - 0 - 0 - - - 2029 - 30 - 0 - 0 - 0 - - - 2030 - 30 - 0 - 0 - 0 - - - 2031 - 30 - 0 - 0 - 0 - - - 2032 - 30 - 0 - 0 - 0 - - - 2033 - 30 - 0 - 0 - 0 - - - 2034 - 30 - 0 - 0 - 0 - - - 2035 - 30 - 0 - 0 - 0 - - - 2036 - 30 - 0 - 0 - 0 - - - 2037 - 30 - 0 - 0 - 0 - - - 2038 - 30 - 0 - 0 - 0 - - - 2039 - 30 - 0 - 0 - 0 - - - 2040 - 30 - 0 - 0 - 0 - - - 2041 - 30 - 0 - 0 - 0 - - - 2042 - 2 - 0 - 0 - 0 - - - 2043 - 2 - 0 - 0 - 0 - - - 2044 - 2 - 0 - 0 - 0 - - - 2045 - 2 - 0 - 0 - 0 - - - 2046 - 2 - 0 - 0 - 0 - - - 2047 - 2 - 0 - 0 - 0 - - - 2048 - 30 - 0 - 0 - 0 - - - 2049 - 30 - 0 - 0 - 0 - - - 2050 - 2 - 0 - 0 - 0 - - - 2051 - 2 - 0 - 0 - 0 - - - 2052 - 2 - 0 - 0 - 0 - - - 2053 - 2 - 0 - 0 - 0 - - - 2054 - 30 - 0 - 0 - 0 - - - 2055 - 30 - 0 - 0 - 0 - - - 2056 - 2 - 0 - 0 - 0 - - - 2057 - 2 - 0 - 0 - 0 - - - 2058 - 2 - 0 - 0 - 0 - - - 2059 - 2 - 0 - 0 - 0 - - - 2060 - 2 - 0 - 0 - 0 - - - 2061 - 2 - 0 - 0 - 0 - - - 2062 - 2 - 0 - 0 - 0 - - - 2063 - 2 - 0 - 0 - 0 - - - 2064 - 30 - 0 - 0 - 0 - - - 2065 - 30 - 0 - 0 - 0 - - - 2066 - 2 - 0 - 0 - 0 - - - 2067 - 2 - 0 - 0 - 0 - - - 2068 - 2 - 0 - 0 - 0 - - - 2069 - 2 - 0 - 0 - 0 - - - 2070 - 2 - 0 - 0 - 0 - - - 2071 - 2 - 0 - 0 - 0 - - - 2072 - 2 - 0 - 0 - 0 - - - 2073 - 2 - 0 - 0 - 0 - - - 2074 - 30 - 0 - 0 - 0 - - - 2075 - 30 - 0 - 0 - 0 - - - 2076 - 2 - 0 - 0 - 0 - - - 2077 - 2 - 0 - 0 - 0 - - - 2078 - 2 - 0 - 0 - 0 - - - 2079 - 2 - 0 - 0 - 0 - - - 2080 - 30 - 0 - 0 - 0 - - - 2081 - 30 - 0 - 0 - 0 - - - 2082 - 2 - 0 - 0 - 0 - - - 2083 - 2 - 0 - 0 - 0 - - - 2084 - 30 - 0 - 0 - 0 - - - 2085 - 30 - 0 - 0 - 0 - - - 2086 - 2 - 0 - 0 - 0 - - - 2087 - 2 - 0 - 0 - 0 - - - 2088 - 2 - 0 - 0 - 0 - - - 2089 - 2 - 0 - 0 - 0 - - - 2090 - 2 - 0 - 0 - 0 - - - 2091 - 2 - 0 - 0 - 0 - - - 2092 - 30 - 0 - 0 - 0 - - - 2093 - 30 - 0 - 0 - 0 - - - 2094 - 2 - 0 - 0 - 0 - - - 2095 - 2 - 0 - 0 - 0 - - - 2096 - 2 - 0 - 0 - 0 - - - 2097 - 2 - 0 - 0 - 0 - - - 2098 - 2 - 0 - 0 - 0 - - - 2099 - 2 - 0 - 0 - 0 - - - 2100 - 2 - 0 - 0 - 0 - - - 2101 - 2 - 0 - 0 - 0 - - - 2102 - 2 - 0 - 0 - 0 - - - 2103 - 2 - 0 - 0 - 0 - - - 2104 - 2 - 0 - 0 - 0 - - - 2105 - 2 - 0 - 0 - 0 - - - 2106 - 2 - 0 - 0 - 0 - - - 2107 - 2 - 0 - 0 - 0 - - - 2108 - 70 - 0 - 0 - 0 - - - 2109 - 70 - 0 - 0 - 0 - - - 2110 - 2 - 0 - 0 - 0 - - - 2111 - 2 - 0 - 0 - 0 - - - 2112 - 2 - 0 - 0 - 0 - - - 2113 - 2 - 0 - 0 - 0 - - - 2114 - 2 - 0 - 0 - 0 - - - 2115 - 2 - 0 - 0 - 0 - - - 2116 - 1 - 0 - 0 - 0 - - - 2117 - 1 - 0 - 0 - 0 - - - 2118 - 1 - 0 - 0 - 0 - - - 2119 - 1 - 0 - 0 - 0 - - - 2120 - 2 - 0 - 0 - 0 - - - 2121 - 2 - 0 - 0 - 0 - - - 2122 - 1 - 0 - 0 - 0 - - - 2123 - 1 - 0 - 0 - 0 - - - 2124 - 2 - 0 - 0 - 0 - - - 2125 - 2 - 0 - 0 - 0 - - - 2126 - 4 - 0 - 0 - 0 - - - 2127 - 4 - 0 - 0 - 0 - - - 2128 - 2 - 0 - 0 - 0 - - - 2129 - 2 - 0 - 0 - 0 - - - 2130 - 19 - 0 - 0 - 0 - - - 2131 - 19 - 0 - 0 - 0 - - - 2132 - 8 - 0 - 0 - 0 - - - 2133 - 8 - 0 - 0 - 0 - - - 2134 - 8 - 0 - 0 - 0 - - - 2135 - 8 - 0 - 0 - 0 - - - 2136 - 8 - 0 - 0 - 0 - - - 2137 - 8 - 0 - 0 - 0 - - - 2138 - 9 - 0 - 0 - 0 - - - 2139 - 9 - 0 - 0 - 0 - - - 2140 - 4 - 0 - 0 - 0 - - - 2141 - 4 - 0 - 0 - 0 - - - 2142 - 4 - 0 - 0 - 0 - - - 2143 - 4 - 0 - 0 - 0 - - - 2144 - 1 - 0 - 0 - 0 - - - 2145 - 1 - 0 - 0 - 0 - - - 2146 - 1 - 0 - 0 - 0 - - - 2147 - 1 - 0 - 0 - 0 - - - 2148 - 150 - 0 - 0 - 0 - - - 2149 - 150 - 0 - 0 - 0 - - - 2150 - 2 - 0 - 0 - 0 - - - 2151 - 2 - 0 - 0 - 0 - - - 2152 - 2 - 0 - 0 - 0 - - - 2153 - 2 - 0 - 0 - 0 - - - 2154 - 2 - 0 - 0 - 0 - - - 2155 - 2 - 0 - 0 - 0 - - - 2156 - 2 - 0 - 0 - 0 - - - 2157 - 2 - 0 - 0 - 0 - - - 2158 - 2 - 0 - 0 - 0 - - - 2159 - 2 - 0 - 0 - 0 - - - 2160 - 2 - 0 - 0 - 0 - - - 2161 - 2 - 0 - 0 - 0 - - - 2162 - 2 - 0 - 0 - 0 - - - 2163 - 2 - 0 - 0 - 0 - - - 2164 - 10 - 0 - 0 - 0 - - - 2165 - 10 - 0 - 0 - 0 - - - 2166 - 10 - 0 - 0 - 0 - - - 2167 - 2 - 0 - 0 - 0 - - - 2168 - 2 - 0 - 0 - 0 - - - 2169 - 2 - 0 - 0 - 0 - - - 2170 - 2 - 0 - 0 - 0 - - - 2171 - 2 - 0 - 0 - 0 - - - 2172 - 2 - 0 - 0 - 0 - - - 2173 - 2 - 0 - 0 - 0 - - - 2174 - 2 - 0 - 0 - 0 - - - 2175 - 1 - 0 - 0 - 0 - - - 2176 - 1 - 0 - 0 - 0 - - - 2177 - 2 - 0 - 0 - 0 - - - 2178 - 2 - 0 - 0 - 0 - - - 2179 - 1 - 0 - 0 - 0 - - - 2180 - 1 - 0 - 0 - 0 - - - 2181 - 1 - 0 - 0 - 0 - - - 2182 - 1 - 0 - 0 - 0 - - - 2183 - 1 - 0 - 0 - 0 - - - 2184 - 1 - 0 - 0 - 0 - - - 2185 - 2 - 0 - 0 - 0 - - - 2186 - 2 - 0 - 0 - 0 - - - 2187 - 2 - 0 - 0 - 0 - - - 2188 - 2 - 0 - 0 - 0 - - - 2189 - 1 - 0 - 0 - 0 - - - 2190 - 1 - 0 - 0 - 0 - - - 2191 - 2 - 0 - 0 - 0 - - - 2192 - 2 - 0 - 0 - 0 - - - 2193 - 1 - 0 - 0 - 0 - - - 2194 - 1 - 0 - 0 - 0 - - - 2195 - 2 - 0 - 0 - 0 - - - 2196 - 2 - 0 - 0 - 0 - - - 2197 - 2 - 0 - 0 - 0 - - - 2198 - 2 - 0 - 0 - 0 - - - 2199 - 1 - 0 - 0 - 0 - - - 2200 - 1 - 0 - 0 - 0 - - - 2201 - 2 - 0 - 0 - 0 - - - 2202 - 2 - 0 - 0 - 0 - - - 2203 - 1 - 0 - 0 - 0 - - - 2204 - 1 - 0 - 0 - 0 - - - 2205 - 2 - 0 - 0 - 0 - - - 2206 - 2 - 0 - 0 - 0 - - - 2207 - 1 - 0 - 0 - 0 - - - 2208 - 1 - 0 - 0 - 0 - - - 2209 - 2 - 0 - 0 - 0 - - - 2210 - 2 - 0 - 0 - 0 - - - 2211 - 1 - 0 - 0 - 0 - - - 2212 - 1 - 0 - 0 - 0 - - - 2213 - 2 - 0 - 0 - 0 - - - 2214 - 2 - 0 - 0 - 0 - - - 2215 - 1 - 0 - 0 - 0 - - - 2216 - 1 - 0 - 0 - 0 - - - 2217 - 2 - 0 - 0 - 0 - - - 2218 - 2 - 0 - 0 - 0 - - - 2219 - 195 - 0 - 0 - 0 - - - 2220 - 195 - 0 - 0 - 0 - - - 2221 - 195 - 0 - 0 - 0 - - - 2222 - 195 - 0 - 0 - 0 - - - 2223 - 195 - 0 - 0 - 0 - - - 2224 - 195 - 0 - 0 - 0 - - - 2225 - 195 - 0 - 0 - 0 - - - 2226 - 195 - 0 - 0 - 0 - - - 2227 - 195 - 0 - 0 - 0 - - - 2228 - 195 - 0 - 0 - 0 - - - 2229 - 450 - 0 - 0 - 0 - - - 2230 - 450 - 0 - 0 - 0 - - - 2231 - 450 - 0 - 0 - 0 - - - 2232 - 450 - 0 - 0 - 0 - - - 2233 - 270 - 0 - 0 - 0 - - - 2234 - 270 - 0 - 0 - 0 - - - 2235 - 270 - 0 - 0 - 0 - - - 2236 - 270 - 0 - 0 - 0 - - - 2237 - 80 - 0 - 0 - 0 - - - 2238 - 80 - 0 - 0 - 0 - - - 2239 - 70 - 0 - 0 - 0 - - - 2240 - 70 - 0 - 0 - 0 - - - 2241 - 70 - 0 - 0 - 0 - - - 2242 - 70 - 0 - 0 - 0 - - - 2243 - 80 - 0 - 0 - 0 - - - 2244 - 80 - 0 - 0 - 0 - - - 2245 - 2 - 0 - 0 - 0 - - - 2246 - 2 - 0 - 0 - 0 - - - 2247 - 1 - 0 - 0 - 0 - - - 2248 - 1 - 0 - 0 - 0 - - - 2249 - 2 - 0 - 0 - 0 - - - 2250 - 2 - 0 - 0 - 0 - - - 2251 - 1 - 0 - 0 - 0 - - - 2252 - 1 - 0 - 0 - 0 - - - 2253 - 1 - 0 - 0 - 0 - - - 2254 - 1 - 0 - 0 - 0 - - - 2255 - 2 - 0 - 0 - 0 - - - 2256 - 2 - 0 - 0 - 0 - - - 2257 - 1 - 0 - 0 - 0 - - - 2258 - 1 - 0 - 0 - 0 - - - 2259 - 2 - 0 - 0 - 0 - - - 2260 - 2 - 0 - 0 - 0 - - - 2261 - 1 - 0 - 0 - 0 - - - 2262 - 1 - 0 - 0 - 0 - - - 2263 - 1 - 0 - 0 - 0 - - - 2264 - 1 - 0 - 0 - 0 - - - 2265 - 1 - 0 - 0 - 0 - - - 2266 - 1 - 0 - 0 - 0 - - - 2267 - 1 - 0 - 0 - 0 - - - 2268 - 1 - 0 - 0 - 0 - - - 2269 - 1 - 0 - 0 - 0 - - - 2270 - 1 - 0 - 0 - 0 - - - 2271 - 1 - 0 - 0 - 0 - - - 2272 - 1 - 0 - 0 - 0 - - - 2273 - 1 - 0 - 0 - 0 - - - 2274 - 1 - 0 - 0 - 0 - - - 2275 - 1 - 0 - 0 - 0 - - - 2276 - 1 - 0 - 0 - 0 - - - 2277 - 2 - 0 - 0 - 0 - - - 2278 - 2 - 0 - 0 - 0 - - - 2279 - 1 - 0 - 0 - 0 - - - 2280 - 1 - 0 - 0 - 0 - - - 2281 - 2 - 0 - 0 - 0 - - - 2282 - 2 - 0 - 0 - 0 - - - 2283 - 4 - 0 - 0 - 0 - - - 2284 - 4 - 0 - 0 - 0 - - - 2285 - 10 - 0 - 0 - 0 - - - 2286 - 10 - 0 - 0 - 0 - - - 2287 - 25 - 0 - 0 - 0 - - - 2288 - 25 - 0 - 0 - 0 - - - 2289 - 200 - 0 - 0 - 0 - - - 2290 - 200 - 0 - 0 - 0 - - - 2291 - 20 - 0 - 0 - 0 - - - 2292 - 20 - 0 - 0 - 0 - - - 2293 - 50 - 0 - 0 - 0 - - - 2294 - 50 - 0 - 0 - 0 - - - 2295 - 25 - 0 - 0 - 0 - - - 2296 - 25 - 0 - 0 - 0 - - - 2297 - 60 - 0 - 0 - 0 - - - 2298 - 60 - 0 - 0 - 0 - - - 2299 - 30 - 0 - 0 - 0 - - - 2300 - 30 - 0 - 0 - 0 - - - 2301 - 100 - 0 - 0 - 0 - - - 2302 - 100 - 0 - 0 - 0 - - - 2303 - 50 - 0 - 0 - 0 - - - 2304 - 50 - 0 - 0 - 0 - - - 2305 - 1 - 0 - 0 - 0 - - - 2306 - 1 - 0 - 0 - 0 - - - 2307 - 4 - 0 - 0 - 0 - - - 2308 - 4 - 0 - 0 - 0 - - - 2309 - 12 - 0 - 0 - 0 - - - 2310 - 12 - 0 - 0 - 0 - - - 2311 - 1 - 0 - 0 - 0 - - - 2312 - 1 - 0 - 0 - 0 - - - 2313 - 3 - 0 - 0 - 0 - - - 2314 - 3 - 0 - 0 - 0 - - - 2315 - 4 - 0 - 0 - 0 - - - 2316 - 4 - 0 - 0 - 0 - - - 2317 - 16 - 0 - 0 - 0 - - - 2318 - 16 - 0 - 0 - 0 - - - 2319 - 8 - 0 - 0 - 0 - - - 2320 - 8 - 0 - 0 - 0 - - - 2321 - 6 - 0 - 0 - 0 - - - 2322 - 6 - 0 - 0 - 0 - - - 2323 - 140 - 0 - 0 - 0 - - - 2324 - 140 - 0 - 0 - 0 - - - 2325 - 150 - 0 - 0 - 0 - - - 2326 - 150 - 0 - 0 - 0 - - - 2327 - 160 - 0 - 0 - 0 - - - 2328 - 160 - 0 - 0 - 0 - - - 2329 - 1 - 0 - 0 - 0 - - - 2330 - 1 - 0 - 0 - 0 - - - 2331 - 8 - 0 - 0 - 0 - - - 2332 - 8 - 0 - 0 - 0 - - - 2333 - 6 - 0 - 0 - 0 - - - 2334 - 6 - 0 - 0 - 0 - - - 2335 - 15 - 0 - 0 - 0 - - - 2336 - 15 - 0 - 0 - 0 - - - 2337 - 10 - 0 - 0 - 0 - - - 2338 - 10 - 0 - 0 - 0 - - - 2339 - 5 - 0 - 0 - 0 - - - 2340 - 5 - 0 - 0 - 0 - - - 2341 - 16 - 0 - 0 - 0 - - - 2342 - 16 - 0 - 0 - 0 - - - 2343 - 35 - 0 - 0 - 0 - - - 2344 - 35 - 0 - 0 - 0 - - - 2345 - 1 - 0 - 0 - 0 - - - 2346 - 1 - 0 - 0 - 0 - - - 2347 - 1 - 0 - 0 - 0 - - - 2348 - 1 - 0 - 0 - 0 - - - 2349 - 8 - 0 - 0 - 0 - - - 2350 - 8 - 0 - 0 - 0 - - - 2351 - 28 - 0 - 0 - 0 - - - 2352 - 28 - 0 - 0 - 0 - - - 2353 - 100 - 0 - 0 - 0 - - - 2354 - 100 - 0 - 0 - 0 - - - 2355 - 150 - 0 - 0 - 0 - - - 2356 - 150 - 0 - 0 - 0 - - - 2357 - 300 - 0 - 0 - 0 - - - 2358 - 300 - 0 - 0 - 0 - - - 2359 - 300 - 0 - 0 - 0 - - - 2360 - 300 - 0 - 0 - 0 - - - 2361 - 640 - 0 - 0 - 0 - - - 2362 - 640 - 0 - 0 - 0 - - - 2363 - 5000 - 0 - 0 - 0 - - - 2364 - 5000 - 0 - 0 - 0 - - - 2365 - 300 - 0 - 0 - 0 - - - 2366 - 110000 - 0 - 0 - 0 - - - 2367 - 110000 - 0 - 0 - 0 - - - 2368 - 750000 - 0 - 0 - 0 - - - 2369 - 750000 - 0 - 0 - 0 - - - 2370 - 150 - 0 - 0 - 0 - - - 2371 - 150 - 0 - 0 - 0 - - - 2372 - 1 - 0 - 0 - 0 - - - 2373 - 1 - 0 - 0 - 0 - - - 2374 - 1 - 0 - 0 - 0 - - - 2375 - 1 - 0 - 0 - 0 - - - 2376 - 1 - 0 - 0 - 0 - - - 2377 - 1 - 0 - 0 - 0 - - - 2378 - 1 - 0 - 0 - 0 - - - 2379 - 1 - 0 - 0 - 0 - - - 2380 - 1 - 0 - 0 - 0 - - - 2381 - 1 - 0 - 0 - 0 - - - 2382 - 1 - 0 - 0 - 0 - - - 2383 - 1 - 0 - 0 - 0 - - - 2384 - 1 - 0 - 0 - 0 - - - 2385 - 1 - 0 - 0 - 0 - - - 2386 - 1 - 0 - 0 - 0 - - - 2387 - 1 - 0 - 0 - 0 - - - 2388 - 1 - 0 - 0 - 0 - - - 2389 - 1 - 0 - 0 - 0 - - - 2390 - 1 - 0 - 0 - 0 - - - 2391 - 20 - 0 - 0 - 0 - - - 2392 - 20 - 0 - 0 - 0 - - - 2393 - 300 - 0 - 0 - 0 - - - 2394 - 120 - 0 - 0 - 0 - - - 2395 - 120 - 0 - 0 - 0 - - - 2396 - 5 - 0 - 0 - 0 - - - 2397 - 40 - 0 - 0 - 0 - - - 2398 - 30 - 0 - 0 - 0 - - - 2399 - 100 - 0 - 0 - 0 - - - 2400 - 100 - 0 - 0 - 0 - - - 2401 - 100 - 0 - 0 - 0 - - - 2402 - 50 - 0 - 0 - 0 - - - 2403 - 1 - 0 - 0 - 0 - - - 2404 - 1 - 0 - 0 - 0 - - - 2405 - 65 - 0 - 0 - 0 - - - 2406 - 1 - 0 - 0 - 0 - - - 2407 - 1 - 0 - 0 - 0 - - - 2408 - 1 - 0 - 0 - 0 - - - 2409 - 1 - 0 - 0 - 0 - - - 2410 - 3 - 0 - 0 - 0 - - - 2411 - 1 - 0 - 0 - 0 - - - 2412 - 100 - 0 - 0 - 0 - - - 2413 - 100 - 0 - 0 - 0 - - - 2414 - 100 - 0 - 0 - 0 - - - 2415 - 80000 - 0 - 0 - 0 - - - 2416 - 80000 - 0 - 0 - 0 - - - 2417 - 80000 - 0 - 0 - 0 - - - 2418 - 1 - 0 - 0 - 0 - - - 2419 - 1 - 0 - 0 - 0 - - - 2420 - 1 - 0 - 0 - 0 - - - 2421 - 1 - 0 - 0 - 0 - - - 2422 - 1 - 0 - 0 - 0 - - - 2423 - 1 - 0 - 0 - 0 - - - 2424 - 5 - 0 - 0 - 0 - - - 2425 - 1 - 0 - 0 - 0 - - - 2426 - 1 - 0 - 0 - 0 - - - 2427 - 1 - 0 - 0 - 0 - - - 2428 - 700 - 0 - 0 - 0 - - - 2429 - 700 - 0 - 0 - 0 - - - 2430 - 110 - 0 - 0 - 0 - - - 2431 - 110 - 0 - 0 - 0 - - - 2432 - 1100 - 0 - 0 - 0 - - - 2433 - 1100 - 0 - 0 - 0 - - - 2434 - 190 - 0 - 0 - 0 - - - 2435 - 190 - 0 - 0 - 0 - - - 2436 - 225 - 0 - 0 - 0 - - - 2437 - 225 - 0 - 0 - 0 - - - 2438 - 250 - 0 - 0 - 0 - - - 2439 - 250 - 0 - 0 - 0 - - - 2440 - 275 - 0 - 0 - 0 - - - 2441 - 275 - 0 - 0 - 0 - - - 2442 - 330 - 0 - 0 - 0 - - - 2443 - 330 - 0 - 0 - 0 - - - 2444 - 360 - 0 - 0 - 0 - - - 2445 - 360 - 0 - 0 - 0 - - - 2446 - 360 - 0 - 0 - 0 - - - 2447 - 360 - 0 - 0 - 0 - - - 2448 - 360 - 0 - 0 - 0 - - - 2449 - 360 - 0 - 0 - 0 - - - 2450 - 200 - 0 - 0 - 0 - - - 2451 - 200 - 0 - 0 - 0 - - - 2452 - 330 - 0 - 0 - 0 - - - 2453 - 330 - 0 - 0 - 0 - - - 2454 - 264 - 0 - 0 - 0 - - - 2455 - 264 - 0 - 0 - 0 - - - 2456 - 198 - 0 - 0 - 0 - - - 2457 - 198 - 0 - 0 - 0 - - - 2458 - 132 - 0 - 0 - 0 - - - 2459 - 132 - 0 - 0 - 0 - - - 2460 - 100 - 0 - 0 - 0 - - - 2461 - 100 - 0 - 0 - 0 - - - 2462 - 100 - 0 - 0 - 0 - - - 2463 - 100 - 0 - 0 - 0 - - - 2464 - 100 - 0 - 0 - 0 - - - 2465 - 100 - 0 - 0 - 0 - - - 2466 - 100 - 0 - 0 - 0 - - - 2467 - 100 - 0 - 0 - 0 - - - 2468 - 100 - 0 - 0 - 0 - - - 2469 - 100 - 0 - 0 - 0 - - - 2470 - 100 - 0 - 0 - 0 - - - 2471 - 100 - 0 - 0 - 0 - - - 2472 - 100 - 0 - 0 - 0 - - - 2473 - 100 - 0 - 0 - 0 - - - 2474 - 100 - 0 - 0 - 0 - - - 2475 - 100 - 0 - 0 - 0 - - - 2476 - 100 - 0 - 0 - 0 - - - 2477 - 100 - 0 - 0 - 0 - - - 2478 - 1 - 0 - 0 - 0 - - - 2479 - 1 - 0 - 0 - 0 - - - 2480 - 1 - 0 - 0 - 0 - - - 2481 - 68 - 0 - 0 - 0 - - - 2482 - 68 - 0 - 0 - 0 - - - 2483 - 68 - 0 - 0 - 0 - - - 2484 - 68 - 0 - 0 - 0 - - - 2485 - 1 - 0 - 0 - 0 - - - 2486 - 1 - 0 - 0 - 0 - - - 2487 - 3000 - 0 - 0 - 0 - - - 2488 - 3000 - 0 - 0 - 0 - - - 2489 - 3600 - 0 - 0 - 0 - - - 2490 - 3600 - 0 - 0 - 0 - - - 2491 - 4320 - 0 - 0 - 0 - - - 2492 - 4320 - 0 - 0 - 0 - - - 2493 - 4320 - 0 - 0 - 0 - - - 2494 - 4320 - 0 - 0 - 0 - - - 2495 - 5180 - 0 - 0 - 0 - - - 2496 - 5180 - 0 - 0 - 0 - - - 2497 - 6220 - 0 - 0 - 0 - - - 2498 - 6220 - 0 - 0 - 0 - - - 2499 - 9360 - 0 - 0 - 0 - - - 2500 - 9360 - 0 - 0 - 0 - - - 2501 - 11230 - 0 - 0 - 0 - - - 2502 - 11230 - 0 - 0 - 0 - - - 2503 - 13480 - 0 - 0 - 0 - - - 2504 - 13480 - 0 - 0 - 0 - - - 2505 - 70 - 0 - 0 - 0 - - - 2506 - 70 - 0 - 0 - 0 - - - 2507 - 90 - 0 - 0 - 0 - - - 2508 - 90 - 0 - 0 - 0 - - - 2509 - 110 - 0 - 0 - 0 - - - 2510 - 110 - 0 - 0 - 0 - - - 2511 - 4 - 0 - 0 - 0 - - - 2512 - 1 - 0 - 0 - 0 - - - 2513 - 6 - 0 - 0 - 0 - - - 2514 - 5 - 0 - 0 - 0 - - - 2515 - 5 - 0 - 0 - 0 - - - 2516 - 10 - 0 - 0 - 0 - - - 2517 - 10 - 0 - 0 - 0 - - - 2518 - 1 - 0 - 0 - 0 - - - 2519 - 1 - 0 - 0 - 0 - - - 2520 - 150 - 0 - 0 - 0 - - - 2521 - 150 - 0 - 0 - 0 - - - 2522 - 150 - 0 - 0 - 0 - - - 2523 - 150 - 0 - 0 - 0 - - - 2524 - 150 - 0 - 0 - 0 - - - 2525 - 150 - 0 - 0 - 0 - - - 2526 - 150 - 0 - 0 - 0 - - - 2527 - 150 - 0 - 0 - 0 - - - 2528 - 1 - 0 - 0 - 0 - - - 2529 - 1 - 0 - 0 - 0 - - - 2530 - 1 - 0 - 0 - 0 - - - 2531 - 1 - 0 - 0 - 0 - - - 2532 - 3 - 0 - 0 - 0 - - - 2533 - 10 - 0 - 0 - 0 - - - 2534 - 3 - 0 - 0 - 0 - - - 2535 - 10 - 0 - 0 - 0 - - - 2536 - 3 - 0 - 0 - 0 - - - 2537 - 10 - 0 - 0 - 0 - - - 2538 - 3 - 0 - 0 - 0 - - - 2539 - 10 - 0 - 0 - 0 - - - 2540 - 3 - 0 - 0 - 0 - - - 2541 - 10 - 0 - 0 - 0 - - - 2542 - 1 - 0 - 0 - 0 - - - 2543 - 1 - 0 - 0 - 0 - - - 2544 - 1 - 0 - 0 - 0 - - - 2545 - 1 - 0 - 0 - 0 - - - 2546 - 1 - 0 - 0 - 0 - - - 2547 - 1 - 0 - 0 - 0 - - - 2548 - 1 - 0 - 0 - 0 - - - 2549 - 1 - 0 - 0 - 0 - - - 2550 - 900 - 0 - 0 - 0 - - - 2551 - 900 - 0 - 0 - 0 - - - 2552 - 1275 - 0 - 0 - 0 - - - 2553 - 1275 - 0 - 0 - 0 - - - 2554 - 1275 - 0 - 0 - 0 - - - 2555 - 1275 - 0 - 0 - 0 - - - 2556 - 1275 - 0 - 0 - 0 - - - 2557 - 1275 - 0 - 0 - 0 - - - 2558 - 1275 - 0 - 0 - 0 - - - 2559 - 1275 - 0 - 0 - 0 - - - 2560 - 1275 - 0 - 0 - 0 - - - 2561 - 1275 - 0 - 0 - 0 - - - 2562 - 1275 - 0 - 0 - 0 - - - 2563 - 1275 - 0 - 0 - 0 - - - 2564 - 1275 - 0 - 0 - 0 - - - 2565 - 1275 - 0 - 0 - 0 - - - 2566 - 1275 - 0 - 0 - 0 - - - 2567 - 1275 - 0 - 0 - 0 - - - 2568 - 2025 - 0 - 0 - 0 - - - 2569 - 2025 - 0 - 0 - 0 - - - 2570 - 3525 - 0 - 0 - 0 - - - 2571 - 3525 - 0 - 0 - 0 - - - 2573 - 17625 - 0 - 0 - 0 - - - 2574 - 50 - 0 - 0 - 0 - - - 2575 - 100 - 0 - 0 - 0 - - - 2576 - 2 - 0 - 0 - 0 - - - 2577 - 200 - 0 - 0 - 0 - - - 2578 - 200 - 0 - 0 - 0 - - - 2579 - 200 - 0 - 0 - 0 - - - 2580 - 200 - 0 - 0 - 0 - - - 2581 - 450 - 0 - 0 - 0 - - - 2582 - 450 - 0 - 0 - 0 - - - 2583 - 3840 - 0 - 0 - 0 - - - 2584 - 3840 - 0 - 0 - 0 - - - 2585 - 1920 - 0 - 0 - 0 - - - 2586 - 1920 - 0 - 0 - 0 - - - 2587 - 1056 - 0 - 0 - 0 - - - 2588 - 1056 - 0 - 0 - 0 - - - 2589 - 1632 - 0 - 0 - 0 - - - 2590 - 1632 - 0 - 0 - 0 - - - 2591 - 3840 - 0 - 0 - 0 - - - 2592 - 3840 - 0 - 0 - 0 - - - 2593 - 1920 - 0 - 0 - 0 - - - 2594 - 1920 - 0 - 0 - 0 - - - 2595 - 1056 - 0 - 0 - 0 - - - 2596 - 1056 - 0 - 0 - 0 - - - 2597 - 1632 - 0 - 0 - 0 - - - 2598 - 1632 - 0 - 0 - 0 - - - 2599 - 12800 - 0 - 0 - 0 - - - 2600 - 12800 - 0 - 0 - 0 - - - 2601 - 6400 - 0 - 0 - 0 - - - 2602 - 6400 - 0 - 0 - 0 - - - 2603 - 5440 - 0 - 0 - 0 - - - 2604 - 5440 - 0 - 0 - 0 - - - 2605 - 3520 - 0 - 0 - 0 - - - 2606 - 3520 - 0 - 0 - 0 - - - 2607 - 12800 - 0 - 0 - 0 - - - 2608 - 12800 - 0 - 0 - 0 - - - 2609 - 6400 - 0 - 0 - 0 - - - 2610 - 6400 - 0 - 0 - 0 - - - 2611 - 5440 - 0 - 0 - 0 - - - 2612 - 5440 - 0 - 0 - 0 - - - 2613 - 3520 - 0 - 0 - 0 - - - 2614 - 3520 - 0 - 0 - 0 - - - 2615 - 65000 - 0 - 0 - 0 - - - 2616 - 65000 - 0 - 0 - 0 - - - 2617 - 64000 - 0 - 0 - 0 - - - 2618 - 64000 - 0 - 0 - 0 - - - 2619 - 35200 - 0 - 0 - 0 - - - 2620 - 35200 - 0 - 0 - 0 - - - 2621 - 54400 - 0 - 0 - 0 - - - 2622 - 54400 - 0 - 0 - 0 - - - 2623 - 65000 - 0 - 0 - 0 - - - 2624 - 65000 - 0 - 0 - 0 - - - 2625 - 64000 - 0 - 0 - 0 - - - 2626 - 64000 - 0 - 0 - 0 - - - 2627 - 35200 - 0 - 0 - 0 - - - 2628 - 35200 - 0 - 0 - 0 - - - 2629 - 54400 - 0 - 0 - 0 - - - 2630 - 54400 - 0 - 0 - 0 - - - 2631 - 40 - 0 - 0 - 0 - - - 2632 - 40 - 0 - 0 - 0 - - - 2633 - 80 - 0 - 0 - 0 - - - 2634 - 80 - 0 - 0 - 0 - - - 2635 - 80 - 0 - 0 - 0 - - - 2636 - 80 - 0 - 0 - 0 - - - 2637 - 80 - 0 - 0 - 0 - - - 2638 - 80 - 0 - 0 - 0 - - - 2639 - 200 - 0 - 0 - 0 - - - 2640 - 200 - 0 - 0 - 0 - - - 2641 - 200 - 0 - 0 - 0 - - - 2642 - 200 - 0 - 0 - 0 - - - 2643 - 200 - 0 - 0 - 0 - - - 2644 - 200 - 0 - 0 - 0 - - - 2645 - 40 - 0 - 0 - 0 - - - 2646 - 40 - 0 - 0 - 0 - - - 2647 - 40 - 0 - 0 - 0 - - - 2648 - 40 - 0 - 0 - 0 - - - 2649 - 40 - 0 - 0 - 0 - - - 2650 - 40 - 0 - 0 - 0 - - - 2651 - 180 - 0 - 0 - 0 - - - 2652 - 180 - 0 - 0 - 0 - - - 2653 - 65000 - 0 - 0 - 0 - - - 2654 - 65000 - 0 - 0 - 0 - - - 2655 - 64000 - 0 - 0 - 0 - - - 2656 - 64000 - 0 - 0 - 0 - - - 2657 - 35200 - 0 - 0 - 0 - - - 2658 - 35200 - 0 - 0 - 0 - - - 2659 - 54400 - 0 - 0 - 0 - - - 2660 - 54400 - 0 - 0 - 0 - - - 2661 - 65000 - 0 - 0 - 0 - - - 2662 - 65000 - 0 - 0 - 0 - - - 2663 - 64000 - 0 - 0 - 0 - - - 2664 - 64000 - 0 - 0 - 0 - - - 2665 - 35200 - 0 - 0 - 0 - - - 2666 - 35200 - 0 - 0 - 0 - - - 2667 - 54400 - 0 - 0 - 0 - - - 2668 - 54400 - 0 - 0 - 0 - - - 2669 - 65000 - 0 - 0 - 0 - - - 2670 - 65000 - 0 - 0 - 0 - - - 2671 - 64000 - 0 - 0 - 0 - - - 2672 - 64000 - 0 - 0 - 0 - - - 2673 - 35200 - 0 - 0 - 0 - - - 2674 - 35200 - 0 - 0 - 0 - - - 2675 - 54400 - 0 - 0 - 0 - - - 2676 - 54400 - 0 - 0 - 0 - - - 2677 - 1 - 0 - 0 - 0 - - - 2678 - 1 - 0 - 0 - 0 - - - 2679 - 1 - 0 - 0 - 0 - - - 2680 - 1 - 0 - 0 - 0 - - - 2681 - 1 - 0 - 0 - 0 - - - 2682 - 1 - 0 - 0 - 0 - - - 2683 - 1 - 0 - 0 - 0 - - - 2684 - 1 - 0 - 0 - 0 - - - 2685 - 1 - 0 - 0 - 0 - - - 2686 - 1 - 0 - 0 - 0 - - - 2687 - 1 - 0 - 0 - 0 - - - 2688 - 1 - 0 - 0 - 0 - - - 2689 - 1 - 0 - 0 - 0 - - - 2690 - 1 - 0 - 0 - 0 - - - 2691 - 1 - 0 - 0 - 0 - - - 2692 - 1 - 0 - 0 - 0 - - - 2693 - 1 - 0 - 0 - 0 - - - 2694 - 1 - 0 - 0 - 0 - - - 2695 - 1 - 0 - 0 - 0 - - - 2696 - 1 - 0 - 0 - 0 - - - 2697 - 1 - 0 - 0 - 0 - - - 2698 - 1 - 0 - 0 - 0 - - - 2699 - 1 - 0 - 0 - 0 - - - 2700 - 1 - 0 - 0 - 0 - - - 2701 - 1 - 0 - 0 - 0 - - - 2702 - 1 - 0 - 0 - 0 - - - 2703 - 1 - 0 - 0 - 0 - - - 2704 - 1 - 0 - 0 - 0 - - - 2705 - 1 - 0 - 0 - 0 - - - 2706 - 1 - 0 - 0 - 0 - - - 2707 - 1 - 0 - 0 - 0 - - - 2708 - 1 - 0 - 0 - 0 - - - 2709 - 1 - 0 - 0 - 0 - - - 2710 - 1 - 0 - 0 - 0 - - - 2711 - 1 - 0 - 0 - 0 - - - 2712 - 1 - 0 - 0 - 0 - - - 2713 - 1 - 0 - 0 - 0 - - - 2714 - 50 - 0 - 0 - 0 - - - 2715 - 50 - 0 - 0 - 0 - - - 2716 - 1 - 0 - 0 - 0 - - - 2717 - 50 - 0 - 0 - 0 - - - 2718 - 50 - 0 - 0 - 0 - - - 2719 - 1 - 0 - 0 - 0 - - - 2720 - 50 - 0 - 0 - 0 - - - 2721 - 50 - 0 - 0 - 0 - - - 2722 - 1 - 0 - 0 - 0 - - - 2723 - 1 - 0 - 0 - 0 - - - 2724 - 50 - 0 - 0 - 0 - - - 2725 - 1 - 0 - 0 - 0 - - - 2726 - 50 - 0 - 0 - 0 - - - 2727 - 1 - 0 - 0 - 0 - - - 2728 - 50 - 0 - 0 - 0 - - - 2729 - 1 - 0 - 0 - 0 - - - 2730 - 50 - 0 - 0 - 0 - - - 2731 - 1 - 0 - 0 - 0 - - - 2732 - 50 - 0 - 0 - 0 - - - 2733 - 1 - 0 - 0 - 0 - - - 2734 - 50 - 0 - 0 - 0 - - - 2735 - 1 - 0 - 0 - 0 - - - 2736 - 50 - 0 - 0 - 0 - - - 2737 - 1 - 0 - 0 - 0 - - - 2738 - 50 - 0 - 0 - 0 - - - 2739 - 1 - 0 - 0 - 0 - - - 2740 - 50 - 0 - 0 - 0 - - - 2741 - 1 - 0 - 0 - 0 - - - 2742 - 50 - 0 - 0 - 0 - - - 2743 - 1 - 0 - 0 - 0 - - - 2744 - 50 - 0 - 0 - 0 - - - 2745 - 1 - 0 - 0 - 0 - - - 2746 - 50 - 0 - 0 - 0 - - - 2747 - 1 - 0 - 0 - 0 - - - 2748 - 50 - 0 - 0 - 0 - - - 2749 - 1 - 0 - 0 - 0 - - - 2750 - 1 - 0 - 0 - 0 - - - 2751 - 1 - 0 - 0 - 0 - - - 2752 - 1 - 0 - 0 - 0 - - - 2753 - 1 - 0 - 0 - 0 - - - 2754 - 1 - 0 - 0 - 0 - - - 2755 - 1 - 0 - 0 - 0 - - - 2756 - 1 - 0 - 0 - 0 - - - 2757 - 1 - 0 - 0 - 0 - - - 2758 - 1 - 0 - 0 - 0 - - - 2759 - 1 - 0 - 0 - 0 - - - 2760 - 1 - 0 - 0 - 0 - - - 2761 - 1 - 0 - 0 - 0 - - - 2762 - 1 - 0 - 0 - 0 - - - 2763 - 1 - 0 - 0 - 0 - - - 2764 - 1 - 0 - 0 - 0 - - - 2765 - 1 - 0 - 0 - 0 - - - 2766 - 1 - 0 - 0 - 0 - - - 2767 - 1 - 0 - 0 - 0 - - - 2768 - 1 - 0 - 0 - 0 - - - 2769 - 1 - 0 - 0 - 0 - - - 2770 - 1 - 0 - 0 - 0 - - - 2771 - 1 - 0 - 0 - 0 - - - 2772 - 1 - 0 - 0 - 0 - - - 2773 - 1 - 0 - 0 - 0 - - - 2774 - 1 - 0 - 0 - 0 - - - 2775 - 50 - 0 - 0 - 0 - - - 2776 - 1 - 0 - 0 - 0 - - - 2777 - 50 - 0 - 0 - 0 - - - 2778 - 1 - 0 - 0 - 0 - - - 2779 - 50 - 0 - 0 - 0 - - - 2780 - 1 - 0 - 0 - 0 - - - 2781 - 50 - 0 - 0 - 0 - - - 2782 - 1 - 0 - 0 - 0 - - - 2783 - 1 - 0 - 0 - 0 - - - 2784 - 50 - 0 - 0 - 0 - - - 2785 - 1 - 0 - 0 - 0 - - - 2786 - 1 - 0 - 0 - 0 - - - 2787 - 50 - 0 - 0 - 0 - - - 2788 - 1 - 0 - 0 - 0 - - - 2789 - 50 - 0 - 0 - 0 - - - 2790 - 1 - 0 - 0 - 0 - - - 2791 - 50 - 0 - 0 - 0 - - - 2792 - 1 - 0 - 0 - 0 - - - 2793 - 1 - 0 - 0 - 0 - - - 2794 - 1 - 0 - 0 - 0 - - - 2795 - 100 - 0 - 0 - 0 - - - 2796 - 1 - 0 - 0 - 0 - - - 2797 - 1 - 0 - 0 - 0 - - - 2798 - 100 - 0 - 0 - 0 - - - 2799 - 1 - 0 - 0 - 0 - - - 2800 - 100 - 0 - 0 - 0 - - - 2801 - 1 - 0 - 0 - 0 - - - 2802 - 50 - 0 - 0 - 0 - - - 2803 - 1 - 0 - 0 - 0 - - - 2804 - 50 - 0 - 0 - 0 - - - 2805 - 1 - 0 - 0 - 0 - - - 2806 - 50 - 0 - 0 - 0 - - - 2807 - 1 - 0 - 0 - 0 - - - 2808 - 50 - 0 - 0 - 0 - - - 2809 - 1 - 0 - 0 - 0 - - - 2810 - 50 - 0 - 0 - 0 - - - 2811 - 1 - 0 - 0 - 0 - - - 2812 - 50 - 0 - 0 - 0 - - - 2813 - 1 - 0 - 0 - 0 - - - 2814 - 50 - 0 - 0 - 0 - - - 2815 - 1 - 0 - 0 - 0 - - - 2816 - 50 - 0 - 0 - 0 - - - 2817 - 1 - 0 - 0 - 0 - - - 2818 - 50 - 0 - 0 - 0 - - - 2819 - 1 - 0 - 0 - 0 - - - 2820 - 50 - 0 - 0 - 0 - - - 2821 - 1 - 0 - 0 - 0 - - - 2822 - 50 - 0 - 0 - 0 - - - 2823 - 1 - 0 - 0 - 0 - - - 2824 - 50 - 0 - 0 - 0 - - - 2825 - 1 - 0 - 0 - 0 - - - 2826 - 50 - 0 - 0 - 0 - - - 2827 - 1 - 0 - 0 - 0 - - - 2828 - 50 - 0 - 0 - 0 - - - 2829 - 1 - 0 - 0 - 0 - - - 2830 - 50 - 0 - 0 - 0 - - - 2831 - 1 - 0 - 0 - 0 - - - 2832 - 1 - 0 - 0 - 0 - - - 2833 - 1 - 0 - 0 - 0 - - - 2834 - 1 - 0 - 0 - 0 - - - 2835 - 1 - 0 - 0 - 0 - - - 2836 - 1 - 0 - 0 - 0 - - - 2837 - 1 - 0 - 0 - 0 - - - 2838 - 1 - 0 - 0 - 0 - - - 2839 - 1 - 0 - 0 - 0 - - - 2840 - 1 - 0 - 0 - 0 - - - 2841 - 1 - 0 - 0 - 0 - - - 2842 - 1 - 0 - 0 - 0 - - - 2843 - 1 - 0 - 0 - 0 - - - 2844 - 1 - 0 - 0 - 0 - - - 2845 - 1 - 0 - 0 - 0 - - - 2846 - 1 - 0 - 0 - 0 - - - 2847 - 1 - 0 - 0 - 0 - - - 2848 - 1 - 0 - 0 - 0 - - - 2849 - 1 - 0 - 0 - 0 - - - 2850 - 1 - 0 - 0 - 0 - - - 2851 - 1 - 0 - 0 - 0 - - - 2852 - 1 - 0 - 0 - 0 - - - 2853 - 1 - 0 - 0 - 0 - - - 2854 - 1 - 0 - 0 - 0 - - - 2855 - 1 - 0 - 0 - 0 - - - 2856 - 1 - 0 - 0 - 0 - - - 2857 - 1 - 0 - 0 - 0 - - - 2858 - 1 - 0 - 0 - 0 - - - 2859 - 1 - 0 - 0 - 0 - - - 2860 - 1 - 0 - 0 - 0 - - - 2861 - 3 - 0 - 0 - 0 - - - 2862 - 4 - 0 - 0 - 0 - - - 2863 - 4 - 0 - 0 - 0 - - - 2864 - 1 - 0 - 0 - 0 - - - 2865 - 1 - 0 - 0 - 0 - - - 2866 - 25 - 0 - 0 - 0 - - - 2867 - 1 - 0 - 0 - 0 - - - 2868 - 1 - 0 - 0 - 0 - - - 2869 - 1 - 0 - 0 - 0 - - - 2870 - 1 - 0 - 0 - 0 - - - 2871 - 1 - 0 - 0 - 0 - - - 2872 - 1 - 0 - 0 - 0 - - - 2873 - 1 - 0 - 0 - 0 - - - 2874 - 1 - 0 - 0 - 0 - - - 2875 - 1 - 0 - 0 - 0 - - - 2876 - 85 - 0 - 0 - 0 - - - 2877 - 85 - 0 - 0 - 0 - - - 2878 - 130 - 0 - 0 - 0 - - - 2879 - 130 - 0 - 0 - 0 - - - 2880 - 1 - 0 - 0 - 0 - - - 2881 - 1 - 0 - 0 - 0 - - - 2882 - 10 - 0 - 0 - 0 - - - 2883 - 500 - 0 - 0 - 0 - - - 2884 - 1 - 0 - 0 - 0 - - - 2885 - 1 - 0 - 0 - 0 - - - 2886 - 10 - 0 - 0 - 0 - - - 2887 - 1 - 0 - 0 - 0 - - - 2888 - 1 - 0 - 0 - 0 - - - 2889 - 1 - 0 - 0 - 0 - - - 2890 - 20 - 0 - 0 - 0 - - - 2891 - 20 - 0 - 0 - 0 - - - 2892 - 5 - 0 - 0 - 0 - - - 2893 - 8 - 0 - 0 - 0 - - - 2894 - 650 - 0 - 0 - 0 - - - 2895 - 650 - 0 - 0 - 0 - - - 2896 - 650 - 0 - 0 - 0 - - - 2897 - 650 - 0 - 0 - 0 - - - 2898 - 650 - 0 - 0 - 0 - - - 2899 - 650 - 0 - 0 - 0 - - - 2900 - 650 - 0 - 0 - 0 - - - 2901 - 650 - 0 - 0 - 0 - - - 2902 - 650 - 0 - 0 - 0 - - - 2903 - 650 - 0 - 0 - 0 - - - 2904 - 650 - 0 - 0 - 0 - - - 2905 - 650 - 0 - 0 - 0 - - - 2906 - 650 - 0 - 0 - 0 - - - 2907 - 650 - 0 - 0 - 0 - - - 2908 - 650 - 0 - 0 - 0 - - - 2909 - 650 - 0 - 0 - 0 - - - 2910 - 650 - 0 - 0 - 0 - - - 2911 - 650 - 0 - 0 - 0 - - - 2912 - 650 - 0 - 0 - 0 - - - 2913 - 650 - 0 - 0 - 0 - - - 2914 - 650 - 0 - 0 - 0 - - - 2915 - 650 - 0 - 0 - 0 - - - 2916 - 650 - 0 - 0 - 0 - - - 2917 - 650 - 0 - 0 - 0 - - - 2918 - 650 - 0 - 0 - 0 - - - 2919 - 650 - 0 - 0 - 0 - - - 2920 - 650 - 0 - 0 - 0 - - - 2921 - 650 - 0 - 0 - 0 - - - 2922 - 650 - 0 - 0 - 0 - - - 2923 - 650 - 0 - 0 - 0 - - - 2924 - 650 - 0 - 0 - 0 - - - 2925 - 650 - 0 - 0 - 0 - - - 2926 - 650 - 0 - 0 - 0 - - - 2927 - 650 - 0 - 0 - 0 - - - 2928 - 650 - 0 - 0 - 0 - - - 2929 - 650 - 0 - 0 - 0 - - - 2930 - 650 - 0 - 0 - 0 - - - 2931 - 650 - 0 - 0 - 0 - - - 2932 - 650 - 0 - 0 - 0 - - - 2933 - 650 - 0 - 0 - 0 - - - 2934 - 650 - 0 - 0 - 0 - - - 2935 - 650 - 0 - 0 - 0 - - - 2936 - 650 - 0 - 0 - 0 - - - 2937 - 650 - 0 - 0 - 0 - - - 2938 - 650 - 0 - 0 - 0 - - - 2939 - 650 - 0 - 0 - 0 - - - 2940 - 650 - 0 - 0 - 0 - - - 2941 - 650 - 0 - 0 - 0 - - - 2942 - 650 - 0 - 0 - 0 - - - 2943 - 650 - 0 - 0 - 0 - - - 2944 - 300 - 0 - 0 - 0 - - - 2945 - 2 - 0 - 0 - 0 - - - 2946 - 300 - 0 - 0 - 0 - - - 2947 - 300 - 0 - 0 - 0 - - - 2948 - 300 - 0 - 0 - 0 - - - 2949 - 300 - 0 - 0 - 0 - - - 2950 - 300 - 0 - 0 - 0 - - - 2951 - 300 - 0 - 0 - 0 - - - 2952 - 300 - 0 - 0 - 0 - - - 2953 - 6 - 0 - 0 - 0 - - - 2954 - 6 - 0 - 0 - 0 - - - 2955 - 5 - 0 - 0 - 0 - - - 2956 - 5 - 0 - 0 - 0 - - - 2957 - 5 - 0 - 0 - 0 - - - 2958 - 5 - 0 - 0 - 0 - - - 2959 - 1 - 0 - 0 - 0 - - - 2960 - 1 - 0 - 0 - 0 - - - 2961 - 175 - 0 - 0 - 0 - - - 2962 - 175 - 0 - 0 - 0 - - - 2963 - 250 - 0 - 0 - 0 - - - 2964 - 4 - 0 - 0 - 0 - - - 2965 - 1 - 0 - 0 - 0 - - - 2966 - 1 - 0 - 0 - 0 - - - 2967 - 1 - 0 - 0 - 0 - - - 2968 - 1 - 0 - 0 - 0 - - - 2969 - 1 - 0 - 0 - 0 - - - 2970 - 1 - 0 - 0 - 0 - - - 2971 - 1 - 0 - 0 - 0 - - - 2972 - 1 - 0 - 0 - 0 - - - 2973 - 1 - 0 - 0 - 0 - - - 2974 - 1 - 0 - 0 - 0 - - - 2975 - 1 - 0 - 0 - 0 - - - 2976 - 10 - 0 - 0 - 0 - - - 2977 - 10 - 0 - 0 - 0 - - - 2978 - 1 - 0 - 0 - 0 - - - 2979 - 1 - 0 - 0 - 0 - - - 2980 - 1 - 0 - 0 - 0 - - - 2981 - 1 - 0 - 0 - 0 - - - 2982 - 1 - 0 - 0 - 0 - - - 2983 - 1 - 0 - 0 - 0 - - - 2984 - 1 - 0 - 0 - 0 - - - 2985 - 1 - 0 - 0 - 0 - - - 2986 - 1 - 0 - 0 - 0 - - - 2987 - 1 - 0 - 0 - 0 - - - 2988 - 1 - 0 - 0 - 0 - - - 2989 - 1 - 0 - 0 - 0 - - - 2990 - 1 - 0 - 0 - 0 - - - 2991 - 1 - 0 - 0 - 0 - - - 2992 - 1 - 0 - 0 - 0 - - - 2993 - 1 - 0 - 0 - 0 - - - 2994 - 1 - 0 - 0 - 0 - - - 2995 - 1 - 0 - 0 - 0 - - - 2996 - 1 - 0 - 0 - 0 - - - 2997 - 89 - 0 - 0 - 0 - - - 2998 - 48 - 0 - 0 - 0 - - - 2999 - 48 - 0 - 0 - 0 - - - 3000 - 59 - 0 - 0 - 0 - - - 3001 - 59 - 0 - 0 - 0 - - - 3002 - 48 - 0 - 0 - 0 - - - 3003 - 48 - 0 - 0 - 0 - - - 3004 - 59 - 0 - 0 - 0 - - - 3005 - 59 - 0 - 0 - 0 - - - 3006 - 60 - 0 - 0 - 0 - - - 3007 - 60 - 0 - 0 - 0 - - - 3008 - 146 - 0 - 0 - 0 - - - 3009 - 146 - 0 - 0 - 0 - - - 3010 - 110 - 0 - 0 - 0 - - - 3011 - 110 - 0 - 0 - 0 - - - 3012 - 72 - 0 - 0 - 0 - - - 3013 - 72 - 0 - 0 - 0 - - - 3014 - 36 - 0 - 0 - 0 - - - 3015 - 36 - 0 - 0 - 0 - - - 3016 - 300 - 0 - 0 - 0 - - - 3017 - 300 - 0 - 0 - 0 - - - 3018 - 230 - 0 - 0 - 0 - - - 3019 - 230 - 0 - 0 - 0 - - - 3020 - 160 - 0 - 0 - 0 - - - 3021 - 160 - 0 - 0 - 0 - - - 3022 - 90 - 0 - 0 - 0 - - - 3023 - 90 - 0 - 0 - 0 - - - 3024 - 300 - 0 - 0 - 0 - - - 3025 - 300 - 0 - 0 - 0 - - - 3026 - 240 - 0 - 0 - 0 - - - 3027 - 240 - 0 - 0 - 0 - - - 3028 - 180 - 0 - 0 - 0 - - - 3029 - 180 - 0 - 0 - 0 - - - 3030 - 120 - 0 - 0 - 0 - - - 3031 - 120 - 0 - 0 - 0 - - - 3032 - 200 - 0 - 0 - 0 - - - 3033 - 200 - 0 - 0 - 0 - - - 3034 - 150 - 0 - 0 - 0 - - - 3035 - 150 - 0 - 0 - 0 - - - 3036 - 100 - 0 - 0 - 0 - - - 3037 - 100 - 0 - 0 - 0 - - - 3038 - 50 - 0 - 0 - 0 - - - 3039 - 50 - 0 - 0 - 0 - - - 3040 - 300 - 0 - 0 - 0 - - - 3041 - 300 - 0 - 0 - 0 - - - 3042 - 250 - 0 - 0 - 0 - - - 3043 - 250 - 0 - 0 - 0 - - - 3044 - 200 - 0 - 0 - 0 - - - 3045 - 200 - 0 - 0 - 0 - - - 3046 - 150 - 0 - 0 - 0 - - - 3047 - 150 - 0 - 0 - 0 - - - 3048 - 89 - 0 - 0 - 0 - - - 3049 - 1 - 0 - 0 - 0 - - - 3050 - 1 - 0 - 0 - 0 - - - 3051 - 1 - 0 - 0 - 0 - - - 3052 - 1 - 0 - 0 - 0 - - - 3053 - 17000 - 0 - 0 - 0 - - - 3054 - 45000 - 0 - 0 - 0 - - - 3055 - 17000 - 0 - 0 - 0 - - - 3056 - 45000 - 0 - 0 - 0 - - - 3057 - 1 - 0 - 0 - 0 - - - 3058 - 1 - 0 - 0 - 0 - - - 3059 - 1 - 0 - 0 - 0 - - - 3060 - 1 - 0 - 0 - 0 - - - 3061 - 1 - 0 - 0 - 0 - - - 3062 - 1 - 0 - 0 - 0 - - - 3063 - 1 - 0 - 0 - 0 - - - 3064 - 1 - 0 - 0 - 0 - - - 3065 - 1 - 0 - 0 - 0 - - - 3066 - 1 - 0 - 0 - 0 - - - 3067 - 1 - 0 - 0 - 0 - - - 3068 - 1 - 0 - 0 - 0 - - - 3069 - 1 - 0 - 0 - 0 - - - 3070 - 1 - 0 - 0 - 0 - - - 3071 - 1 - 0 - 0 - 0 - - - 3072 - 1 - 0 - 0 - 0 - - - 3073 - 1 - 0 - 0 - 0 - - - 3074 - 1 - 0 - 0 - 0 - - - 3075 - 1 - 0 - 0 - 0 - - - 3076 - 1 - 0 - 0 - 0 - - - 3077 - 1 - 0 - 0 - 0 - - - 3078 - 1 - 0 - 0 - 0 - - - 3079 - 1 - 0 - 0 - 0 - - - 3080 - 1 - 0 - 0 - 0 - - - 3081 - 1 - 0 - 0 - 0 - - - 3082 - 1 - 0 - 0 - 0 - - - 3083 - 1 - 0 - 0 - 0 - - - 3084 - 1 - 0 - 0 - 0 - - - 3085 - 1 - 0 - 0 - 0 - - - 3086 - 1 - 0 - 0 - 0 - - - 3087 - 1 - 0 - 0 - 0 - - - 3088 - 1 - 0 - 0 - 0 - - - 3089 - 1 - 0 - 0 - 0 - - - 3090 - 1 - 0 - 0 - 0 - - - 3091 - 1 - 0 - 0 - 0 - - - 3092 - 1 - 0 - 0 - 0 - - - 3093 - 1 - 0 - 0 - 0 - - - 3094 - 18 - 0 - 0 - 0 - - - 3095 - 15 - 0 - 0 - 0 - - - 3096 - 50 - 0 - 0 - 0 - - - 3097 - 175 - 0 - 0 - 0 - - - 3098 - 360 - 0 - 0 - 0 - - - 3099 - 475 - 0 - 0 - 0 - - - 3100 - 1200 - 0 - 0 - 0 - - - 3101 - 12000 - 0 - 0 - 0 - - - 3102 - 1 - 0 - 0 - 0 - - - 3103 - 1 - 0 - 0 - 0 - - - 3104 - 1 - 0 - 0 - 0 - - - 3105 - 12 - 0 - 0 - 0 - - - 3106 - 12 - 0 - 0 - 0 - - - 3107 - 30 - 0 - 0 - 0 - - - 3108 - 1 - 0 - 0 - 0 - - - 3109 - 1 - 0 - 0 - 0 - - - 3110 - 1 - 0 - 0 - 0 - - - 3111 - 1 - 0 - 0 - 0 - - - 3112 - 1 - 0 - 0 - 0 - - - 3113 - 1 - 0 - 0 - 0 - - - 3114 - 1 - 0 - 0 - 0 - - - 3115 - 15 - 0 - 0 - 0 - - - 3116 - 50 - 0 - 0 - 0 - - - 3117 - 175 - 0 - 0 - 0 - - - 3118 - 360 - 0 - 0 - 0 - - - 3119 - 475 - 0 - 0 - 0 - - - 3120 - 1200 - 0 - 0 - 0 - - - 3121 - 12000 - 0 - 0 - 0 - - - 3122 - 56000 - 0 - 0 - 0 - - - 3123 - 1 - 0 - 0 - 0 - - - 3124 - 1 - 0 - 0 - 0 - - - 3125 - 1 - 0 - 0 - 0 - - - 3126 - 1 - 0 - 0 - 0 - - - 3127 - 1 - 0 - 0 - 0 - - - 3128 - 1 - 0 - 0 - 0 - - - 3129 - 1 - 0 - 0 - 0 - - - 3130 - 1 - 0 - 0 - 0 - - - 3131 - 1 - 0 - 0 - 0 - - - 3132 - 1 - 0 - 0 - 0 - - - 3133 - 1 - 0 - 0 - 0 - - - 3134 - 56000 - 0 - 0 - 0 - - - 3135 - 1 - 0 - 0 - 0 - - - 3136 - 1 - 0 - 0 - 0 - - - 3137 - 1 - 0 - 0 - 0 - - - 3138 - 1 - 0 - 0 - 0 - - - 3139 - 1 - 0 - 0 - 0 - - - 3140 - 250000 - 0 - 0 - 0 - - - 3141 - 250000 - 0 - 0 - 0 - - - 3142 - 440 - 0 - 0 - 0 - - - 3143 - 440 - 0 - 0 - 0 - - - 3144 - 460 - 0 - 0 - 0 - - - 3145 - 460 - 0 - 0 - 0 - - - 3146 - 250 - 0 - 0 - 0 - - - 3147 - 250 - 0 - 0 - 0 - - - 3148 - 1 - 0 - 0 - 0 - - - 3149 - 1 - 0 - 0 - 0 - - - 3150 - 10 - 0 - 0 - 0 - - - 3151 - 10 - 0 - 0 - 0 - - - 3152 - 15 - 0 - 0 - 0 - - - 3153 - 15 - 0 - 0 - 0 - - - 3154 - 15 - 0 - 0 - 0 - - - 3155 - 15 - 0 - 0 - 0 - - - 3156 - 15 - 0 - 0 - 0 - - - 3157 - 5 - 0 - 0 - 0 - - - 3158 - 5 - 0 - 0 - 0 - - - 3159 - 40 - 0 - 0 - 0 - - - 3160 - 40 - 0 - 0 - 0 - - - 3161 - 5 - 0 - 0 - 0 - - - 3162 - 2 - 0 - 0 - 0 - - - 3163 - 2 - 0 - 0 - 0 - - - 3164 - 30 - 0 - 0 - 0 - - - 3165 - 30 - 0 - 0 - 0 - - - 3166 - 1 - 0 - 0 - 0 - - - 3167 - 10 - 0 - 0 - 0 - - - 3168 - 1 - 0 - 0 - 0 - - - 3169 - 1 - 0 - 0 - 0 - - - 3170 - 26 - 0 - 0 - 0 - - - 3171 - 91 - 0 - 0 - 0 - - - 3172 - 325 - 0 - 0 - 0 - - - 3173 - 845 - 0 - 0 - 0 - - - 3174 - 2080 - 0 - 0 - 0 - - - 3175 - 20800 - 0 - 0 - 0 - - - 3176 - 62400 - 0 - 0 - 0 - - - 3177 - 2 - 0 - 0 - 0 - - - 3178 - 2 - 0 - 0 - 0 - - - 3179 - 1 - 0 - 0 - 0 - - - 3180 - 1 - 0 - 0 - 0 - - - 3181 - 1 - 0 - 0 - 0 - - - 3182 - 1 - 0 - 0 - 0 - - - 3183 - 1 - 0 - 0 - 0 - - - 3184 - 1 - 0 - 0 - 0 - - - 3185 - 1 - 0 - 0 - 0 - - - 3186 - 1 - 0 - 0 - 0 - - - 3187 - 1 - 0 - 0 - 0 - - - 3188 - 60 - 0 - 0 - 0 - - - 3189 - 60 - 0 - 0 - 0 - - - 3190 - 104 - 0 - 0 - 0 - - - 3191 - 104 - 0 - 0 - 0 - - - 3192 - 364 - 0 - 0 - 0 - - - 3193 - 364 - 0 - 0 - 0 - - - 3194 - 1300 - 0 - 0 - 0 - - - 3195 - 1300 - 0 - 0 - 0 - - - 3196 - 2496 - 0 - 0 - 0 - - - 3197 - 2496 - 0 - 0 - 0 - - - 3198 - 3380 - 0 - 0 - 0 - - - 3199 - 3380 - 0 - 0 - 0 - - - 3200 - 8320 - 0 - 0 - 0 - - - 3201 - 8320 - 0 - 0 - 0 - - - 3202 - 128000 - 0 - 0 - 0 - - - 3203 - 128000 - 0 - 0 - 0 - - - 3204 - 325000 - 0 - 0 - 0 - - - 3205 - 325000 - 0 - 0 - 0 - - - 3206 - 1 - 0 - 0 - 0 - - - 3207 - 1 - 0 - 0 - 0 - - - 3208 - 10 - 0 - 0 - 0 - - - 3209 - 1 - 0 - 0 - 0 - - - 3210 - 1 - 0 - 0 - 0 - - - 3211 - 10 - 0 - 0 - 0 - - - 3212 - 10 - 0 - 0 - 0 - - - 3213 - 4 - 0 - 0 - 0 - - - 3214 - 5 - 0 - 0 - 0 - - - 3215 - 5 - 0 - 0 - 0 - - - 3216 - 1 - 0 - 0 - 0 - - - 3217 - 1 - 0 - 0 - 0 - - - 3218 - 1 - 0 - 0 - 0 - - - 3219 - 1 - 0 - 0 - 0 - - - 3220 - 1 - 0 - 0 - 0 - - - 3221 - 1 - 0 - 0 - 0 - - - 3222 - 1 - 0 - 0 - 0 - - - 3223 - 1 - 0 - 0 - 0 - - - 3224 - 10 - 0 - 0 - 0 - - - 3225 - 1 - 0 - 0 - 0 - - - 3226 - 20 - 0 - 0 - 0 - - - 3227 - 20 - 0 - 0 - 0 - - - 3228 - 4 - 0 - 0 - 0 - - - 3229 - 4 - 0 - 0 - 0 - - - 3230 - 1 - 0 - 0 - 0 - - - 3231 - 1 - 0 - 0 - 0 - - - 3232 - 1 - 0 - 0 - 0 - - - 3233 - 1 - 0 - 0 - 0 - - - 3234 - 1 - 0 - 0 - 0 - - - 3235 - 1 - 0 - 0 - 0 - - - 3236 - 1 - 0 - 0 - 0 - - - 3237 - 1 - 0 - 0 - 0 - - - 3238 - 1 - 0 - 0 - 0 - - - 3239 - 1 - 0 - 0 - 0 - - - 3240 - 1 - 0 - 0 - 0 - - - 3241 - 1 - 0 - 0 - 0 - - - 3242 - 1 - 0 - 0 - 0 - - - 3243 - 1 - 0 - 0 - 0 - - - 3244 - 1 - 0 - 0 - 0 - - - 3245 - 1 - 0 - 0 - 0 - - - 3246 - 1 - 0 - 0 - 0 - - - 3247 - 1 - 0 - 0 - 0 - - - 3248 - 1 - 0 - 0 - 0 - - - 3249 - 1 - 0 - 0 - 0 - - - 3250 - 1 - 0 - 0 - 0 - - - 3251 - 1 - 0 - 0 - 0 - - - 3252 - 1 - 0 - 0 - 0 - - - 3253 - 1 - 0 - 0 - 0 - - - 3254 - 1 - 0 - 0 - 0 - - - 3255 - 1 - 0 - 0 - 0 - - - 3256 - 1 - 0 - 0 - 0 - - - 3257 - 1 - 0 - 0 - 0 - - - 3258 - 1 - 0 - 0 - 0 - - - 3259 - 1 - 0 - 0 - 0 - - - 3260 - 1 - 0 - 0 - 0 - - - 3261 - 1 - 0 - 0 - 0 - - - 3262 - 1 - 0 - 0 - 0 - - - 3263 - 1 - 0 - 0 - 0 - - - 3264 - 1 - 0 - 0 - 0 - - - 3265 - 1 - 0 - 0 - 0 - - - 3266 - 1 - 0 - 0 - 0 - - - 3267 - 1 - 0 - 0 - 0 - - - 3268 - 1 - 0 - 0 - 0 - - - 3269 - 1 - 0 - 0 - 0 - - - 3270 - 1 - 0 - 0 - 0 - - - 3271 - 1 - 0 - 0 - 0 - - - 3272 - 1 - 0 - 0 - 0 - - - 3273 - 1 - 0 - 0 - 0 - - - 3274 - 1 - 0 - 0 - 0 - - - 3275 - 1 - 0 - 0 - 0 - - - 3276 - 1 - 0 - 0 - 0 - - - 3277 - 1 - 0 - 0 - 0 - - - 3278 - 1 - 0 - 0 - 0 - - - 3279 - 1 - 0 - 0 - 0 - - - 3280 - 1 - 0 - 0 - 0 - - - 3281 - 1 - 0 - 0 - 0 - - - 3282 - 1 - 0 - 0 - 0 - - - 3283 - 1 - 0 - 0 - 0 - - - 3284 - 1 - 0 - 0 - 0 - - - 3285 - 1 - 0 - 0 - 0 - - - 3286 - 1 - 0 - 0 - 0 - - - 3287 - 1 - 0 - 0 - 0 - - - 3288 - 1 - 0 - 0 - 0 - - - 3289 - 1 - 0 - 0 - 0 - - - 3290 - 1 - 0 - 0 - 0 - - - 3291 - 1 - 0 - 0 - 0 - - - 3292 - 1 - 0 - 0 - 0 - - - 3293 - 1 - 0 - 0 - 0 - - - 3294 - 1 - 0 - 0 - 0 - - - 3295 - 1 - 0 - 0 - 0 - - - 3296 - 1 - 0 - 0 - 0 - - - 3297 - 1 - 0 - 0 - 0 - - - 3298 - 1 - 0 - 0 - 0 - - - 3299 - 1 - 0 - 0 - 0 - - - 3300 - 1 - 0 - 0 - 0 - - - 3301 - 1 - 0 - 0 - 0 - - - 3302 - 1 - 0 - 0 - 0 - - - 3303 - 1 - 0 - 0 - 0 - - - 3304 - 1 - 0 - 0 - 0 - - - 3305 - 1 - 0 - 0 - 0 - - - 3306 - 1 - 0 - 0 - 0 - - - 3307 - 1 - 0 - 0 - 0 - - - 3308 - 1 - 0 - 0 - 0 - - - 3309 - 1 - 0 - 0 - 0 - - - 3310 - 1 - 0 - 0 - 0 - - - 3311 - 1 - 0 - 0 - 0 - - - 3312 - 1 - 0 - 0 - 0 - - - 3313 - 1 - 0 - 0 - 0 - - - 3314 - 1 - 0 - 0 - 0 - - - 3315 - 1 - 0 - 0 - 0 - - - 3316 - 1 - 0 - 0 - 0 - - - 3317 - 1 - 0 - 0 - 0 - - - 3318 - 1 - 0 - 0 - 0 - - - 3319 - 1 - 0 - 0 - 0 - - - 3320 - 1 - 0 - 0 - 0 - - - 3321 - 1 - 0 - 0 - 0 - - - 3322 - 1 - 0 - 0 - 0 - - - 3323 - 1 - 0 - 0 - 0 - - - 3324 - 1 - 0 - 0 - 0 - - - 3325 - 2 - 0 - 0 - 0 - - - 3326 - 2 - 0 - 0 - 0 - - - 3327 - 300 - 0 - 0 - 0 - - - 3328 - 300 - 0 - 0 - 0 - - - 3329 - 300 - 0 - 0 - 0 - - - 3330 - 300 - 0 - 0 - 0 - - - 3331 - 300 - 0 - 0 - 0 - - - 3332 - 300 - 0 - 0 - 0 - - - 3333 - 300 - 0 - 0 - 0 - - - 3334 - 300 - 0 - 0 - 0 - - - 3335 - 300 - 0 - 0 - 0 - - - 3336 - 300 - 0 - 0 - 0 - - - 3337 - 300 - 0 - 0 - 0 - - - 3338 - 300 - 0 - 0 - 0 - - - 3339 - 300 - 0 - 0 - 0 - - - 3340 - 300 - 0 - 0 - 0 - - - 3341 - 300 - 0 - 0 - 0 - - - 3342 - 300 - 0 - 0 - 0 - - - 3343 - 300 - 0 - 0 - 0 - - - 3344 - 300 - 0 - 0 - 0 - - - 3345 - 150 - 0 - 0 - 0 - - - 3346 - 150 - 0 - 0 - 0 - - - 3347 - 150 - 0 - 0 - 0 - - - 3348 - 150 - 0 - 0 - 0 - - - 3349 - 150 - 0 - 0 - 0 - - - 3350 - 150 - 0 - 0 - 0 - - - 3351 - 150 - 0 - 0 - 0 - - - 3352 - 150 - 0 - 0 - 0 - - - 3353 - 150 - 0 - 0 - 0 - - - 3354 - 150 - 0 - 0 - 0 - - - 3355 - 150 - 0 - 0 - 0 - - - 3356 - 150 - 0 - 0 - 0 - - - 3357 - 150 - 0 - 0 - 0 - - - 3358 - 150 - 0 - 0 - 0 - - - 3359 - 150 - 0 - 0 - 0 - - - 3360 - 150 - 0 - 0 - 0 - - - 3361 - 150 - 0 - 0 - 0 - - - 3362 - 150 - 0 - 0 - 0 - - - 3363 - 60 - 0 - 0 - 0 - - - 3364 - 60 - 0 - 0 - 0 - - - 3365 - 10 - 0 - 0 - 0 - - - 3366 - 10 - 0 - 0 - 0 - - - 3367 - 15 - 0 - 0 - 0 - - - 3368 - 15 - 0 - 0 - 0 - - - 3369 - 10 - 0 - 0 - 0 - - - 3370 - 10 - 0 - 0 - 0 - - - 3371 - 20 - 0 - 0 - 0 - - - 3372 - 20 - 0 - 0 - 0 - - - 3373 - 30 - 0 - 0 - 0 - - - 3374 - 30 - 0 - 0 - 0 - - - 3375 - 10 - 0 - 0 - 0 - - - 3376 - 10 - 0 - 0 - 0 - - - 3377 - 5 - 0 - 0 - 0 - - - 3378 - 5 - 0 - 0 - 0 - - - 3379 - 1 - 0 - 0 - 0 - - - 3380 - 1 - 0 - 0 - 0 - - - 3381 - 1 - 0 - 0 - 0 - - - 3382 - 1 - 0 - 0 - 0 - - - 3383 - 1 - 0 - 0 - 0 - - - 3384 - 1 - 0 - 0 - 0 - - - 3385 - 10000 - 0 - 0 - 0 - - - 3386 - 10000 - 0 - 0 - 0 - - - 3387 - 45000 - 0 - 0 - 0 - - - 3388 - 45000 - 0 - 0 - 0 - - - 3389 - 40000 - 0 - 0 - 0 - - - 3390 - 40000 - 0 - 0 - 0 - - - 3391 - 5000 - 0 - 0 - 0 - - - 3392 - 5000 - 0 - 0 - 0 - - - 3393 - 5000 - 0 - 0 - 0 - - - 3394 - 5000 - 0 - 0 - 0 - - - 3395 - 1 - 0 - 0 - 0 - - - 3396 - 1 - 0 - 0 - 0 - - - 3397 - 1 - 0 - 0 - 0 - - - 3398 - 1 - 0 - 0 - 0 - - - 3399 - 1 - 0 - 0 - 0 - - - 3400 - 1 - 0 - 0 - 0 - - - 3401 - 1 - 0 - 0 - 0 - - - 3402 - 1 - 0 - 0 - 0 - - - 3403 - 1 - 0 - 0 - 0 - - - 3404 - 1 - 0 - 0 - 0 - - - 3405 - 1 - 0 - 0 - 0 - - - 3406 - 11 - 0 - 0 - 0 - - - 3407 - 11 - 0 - 0 - 0 - - - 3408 - 14 - 0 - 0 - 0 - - - 3409 - 1 - 0 - 0 - 0 - - - 3410 - 13 - 0 - 0 - 0 - - - 3411 - 1 - 0 - 0 - 0 - - - 3412 - 13 - 0 - 0 - 0 - - - 3413 - 1 - 0 - 0 - 0 - - - 3414 - 11 - 0 - 0 - 0 - - - 3415 - 1 - 0 - 0 - 0 - - - 3416 - 14 - 0 - 0 - 0 - - - 3417 - 13 - 0 - 0 - 0 - - - 3418 - 13 - 0 - 0 - 0 - - - 3419 - 11 - 0 - 0 - 0 - - - 3420 - 21 - 0 - 0 - 0 - - - 3421 - 21 - 0 - 0 - 0 - - - 3422 - 22 - 0 - 0 - 0 - - - 3423 - 22 - 0 - 0 - 0 - - - 3424 - 26 - 0 - 0 - 0 - - - 3425 - 26 - 0 - 0 - 0 - - - 3426 - 17 - 0 - 0 - 0 - - - 3427 - 17 - 0 - 0 - 0 - - - 3428 - 14 - 0 - 0 - 0 - - - 3429 - 14 - 0 - 0 - 0 - - - 3430 - 100 - 0 - 0 - 0 - - - 3431 - 100 - 0 - 0 - 0 - - - 3432 - 90 - 0 - 0 - 0 - - - 3433 - 90 - 0 - 0 - 0 - - - 3434 - 75 - 0 - 0 - 0 - - - 3435 - 75 - 0 - 0 - 0 - - - 3436 - 60 - 0 - 0 - 0 - - - 3437 - 60 - 0 - 0 - 0 - - - 3438 - 8 - 0 - 0 - 0 - - - 3439 - 8 - 0 - 0 - 0 - - - 3440 - 40 - 0 - 0 - 0 - - - 3441 - 40 - 0 - 0 - 0 - - - 3442 - 80 - 0 - 0 - 0 - - - 3443 - 80 - 0 - 0 - 0 - - - 3444 - 160 - 0 - 0 - 0 - - - 3445 - 160 - 0 - 0 - 0 - - - 3446 - 320 - 0 - 0 - 0 - - - 3447 - 320 - 0 - 0 - 0 - - - 3448 - 640 - 0 - 0 - 0 - - - 3449 - 640 - 0 - 0 - 0 - - - 3450 - 81 - 0 - 0 - 0 - - - 3451 - 82 - 0 - 0 - 0 - - - 3452 - 83 - 0 - 0 - 0 - - - 3453 - 84 - 0 - 0 - 0 - - - 3454 - 85 - 0 - 0 - 0 - - - 3455 - 86 - 0 - 0 - 0 - - - 3456 - 87 - 0 - 0 - 0 - - - 3457 - 88 - 0 - 0 - 0 - - - 3458 - 89 - 0 - 0 - 0 - - - 3459 - 90 - 0 - 0 - 0 - - - 3460 - 91 - 0 - 0 - 0 - - - 3461 - 92 - 0 - 0 - 0 - - - 3462 - 93 - 0 - 0 - 0 - - - 3463 - 94 - 0 - 0 - 0 - - - 3464 - 95 - 0 - 0 - 0 - - - 3465 - 96 - 0 - 0 - 0 - - - 3466 - 97 - 0 - 0 - 0 - - - 3467 - 98 - 0 - 0 - 0 - - - 3468 - 99 - 0 - 0 - 0 - - - 3469 - 100 - 0 - 0 - 0 - - - 3470 - 500 - 0 - 0 - 0 - - - 3471 - 500 - 0 - 0 - 0 - - - 3472 - 1920 - 0 - 0 - 0 - - - 3473 - 1920 - 0 - 0 - 0 - - - 3474 - 6400 - 0 - 0 - 0 - - - 3475 - 6400 - 0 - 0 - 0 - - - 3476 - 64000 - 0 - 0 - 0 - - - 3477 - 64000 - 0 - 0 - 0 - - - 3478 - 64000 - 0 - 0 - 0 - - - 3479 - 64000 - 0 - 0 - 0 - - - 3480 - 64000 - 0 - 0 - 0 - - - 3481 - 65000 - 0 - 0 - 0 - - - 3482 - 65000 - 0 - 0 - 0 - - - 3483 - 64000 - 0 - 0 - 0 - - - 3484 - 64000 - 0 - 0 - 0 - - - 3485 - 64000 - 0 - 0 - 0 - - - 3486 - 35200 - 0 - 0 - 0 - - - 3487 - 35200 - 0 - 0 - 0 - - - 3488 - 54400 - 0 - 0 - 0 - - - 3489 - 54400 - 0 - 0 - 0 - - - 3490 - 1 - 0 - 0 - 0 - - - 3491 - 1 - 0 - 0 - 0 - - - 3492 - 1 - 0 - 0 - 0 - - - 3493 - 1 - 0 - 0 - 0 - - - 3494 - 1 - 0 - 0 - 0 - - - 3495 - 1 - 0 - 0 - 0 - - - 3496 - 1 - 0 - 0 - 0 - - - 3497 - 1 - 0 - 0 - 0 - - - 3498 - 1 - 0 - 0 - 0 - - - 3499 - 1 - 0 - 0 - 0 - - - 3500 - 1 - 0 - 0 - 0 - - - 3501 - 1 - 0 - 0 - 0 - - - 3502 - 1 - 0 - 0 - 0 - - - 3503 - 1 - 0 - 0 - 0 - - - 3504 - 1 - 0 - 0 - 0 - - - 3505 - 1 - 0 - 0 - 0 - - - 3506 - 1 - 0 - 0 - 0 - - - 3507 - 1 - 0 - 0 - 0 - - - 3508 - 1 - 0 - 0 - 0 - - - 3509 - 1 - 0 - 0 - 0 - - - 3510 - 1 - 0 - 0 - 0 - - - 3511 - 50 - 0 - 0 - 0 - - - 3512 - 1 - 0 - 0 - 0 - - - 3513 - 1 - 0 - 0 - 0 - - - 3514 - 1 - 0 - 0 - 0 - - - 3515 - 1 - 0 - 0 - 0 - - - 3516 - 1 - 0 - 0 - 0 - - - 3517 - 50 - 0 - 0 - 0 - - - 3518 - 1 - 0 - 0 - 0 - - - 3519 - 50 - 0 - 0 - 0 - - - 3520 - 1 - 0 - 0 - 0 - - - 3521 - 50 - 0 - 0 - 0 - - - 3522 - 1 - 0 - 0 - 0 - - - 3523 - 50 - 0 - 0 - 0 - - - 3524 - 1 - 0 - 0 - 0 - - - 3525 - 1 - 0 - 0 - 0 - - - 3526 - 1 - 0 - 0 - 0 - - - 3527 - 50 - 0 - 0 - 0 - - - 3528 - 1 - 0 - 0 - 0 - - - 3529 - 50 - 0 - 0 - 0 - - - 3530 - 1 - 0 - 0 - 0 - - - 3531 - 50 - 0 - 0 - 0 - - - 3532 - 1 - 0 - 0 - 0 - - - 3533 - 50 - 0 - 0 - 0 - - - 3534 - 1 - 0 - 0 - 0 - - - 3535 - 50 - 0 - 0 - 0 - - - 3536 - 1 - 0 - 0 - 0 - - - 3537 - 50 - 0 - 0 - 0 - - - 3538 - 1 - 0 - 0 - 0 - - - 3539 - 50 - 0 - 0 - 0 - - - 3540 - 1 - 0 - 0 - 0 - - - 3541 - 50 - 0 - 0 - 0 - - - 3542 - 1 - 0 - 0 - 0 - - - 3543 - 50 - 0 - 0 - 0 - - - 3544 - 1 - 0 - 0 - 0 - - - 3545 - 50 - 0 - 0 - 0 - - - 3546 - 1 - 0 - 0 - 0 - - - 3547 - 50 - 0 - 0 - 0 - - - 3548 - 1 - 0 - 0 - 0 - - - 3549 - 50 - 0 - 0 - 0 - - - 3550 - 1 - 0 - 0 - 0 - - - 3551 - 50 - 0 - 0 - 0 - - - 3552 - 1 - 0 - 0 - 0 - - - 3553 - 50 - 0 - 0 - 0 - - - 3554 - 1 - 0 - 0 - 0 - - - 3555 - 50 - 0 - 0 - 0 - - - 3556 - 1 - 0 - 0 - 0 - - - 3557 - 50 - 0 - 0 - 0 - - - 3558 - 1 - 0 - 0 - 0 - - - 3559 - 50 - 0 - 0 - 0 - - - 3560 - 1 - 0 - 0 - 0 - - - 3561 - 50 - 0 - 0 - 0 - - - 3562 - 1 - 0 - 0 - 0 - - - 3563 - 50 - 0 - 0 - 0 - - - 3564 - 1 - 0 - 0 - 0 - - - 3565 - 100 - 0 - 0 - 0 - - - 3566 - 1 - 0 - 0 - 0 - - - 3567 - 100 - 0 - 0 - 0 - - - 3568 - 1 - 0 - 0 - 0 - - - 3569 - 100 - 0 - 0 - 0 - - - 3570 - 1 - 0 - 0 - 0 - - - 3571 - 100 - 0 - 0 - 0 - - - 3572 - 1 - 0 - 0 - 0 - - - 3573 - 1 - 0 - 0 - 0 - - - 3574 - 1 - 0 - 0 - 0 - - - 3575 - 1 - 0 - 0 - 0 - - - 3576 - 100 - 0 - 0 - 0 - - - 3577 - 1 - 0 - 0 - 0 - - - 3578 - 100 - 0 - 0 - 0 - - - 3579 - 1 - 0 - 0 - 0 - - - 3580 - 1 - 0 - 0 - 0 - - - 3581 - 50 - 0 - 0 - 0 - - - 3582 - 1 - 0 - 0 - 0 - - - 3583 - 50 - 0 - 0 - 0 - - - 3584 - 1 - 0 - 0 - 0 - - - 3585 - 50 - 0 - 0 - 0 - - - 3586 - 1 - 0 - 0 - 0 - - - 3587 - 50 - 0 - 0 - 0 - - - 3588 - 1 - 0 - 0 - 0 - - - 3589 - 50 - 0 - 0 - 0 - - - 3590 - 1 - 0 - 0 - 0 - - - 3591 - 50 - 0 - 0 - 0 - - - 3592 - 1 - 0 - 0 - 0 - - - 3593 - 50 - 0 - 0 - 0 - - - 3594 - 1 - 0 - 0 - 0 - - - 3595 - 50 - 0 - 0 - 0 - - - 3596 - 1 - 0 - 0 - 0 - - - 3597 - 50 - 0 - 0 - 0 - - - 3598 - 1 - 0 - 0 - 0 - - - 3599 - 1 - 0 - 0 - 0 - - - 3600 - 50 - 0 - 0 - 0 - - - 3601 - 1 - 0 - 0 - 0 - - - 3602 - 1 - 0 - 0 - 0 - - - 3603 - 50 - 0 - 0 - 0 - - - 3604 - 1 - 0 - 0 - 0 - - - 3605 - 1 - 0 - 0 - 0 - - - 3606 - 1 - 0 - 0 - 0 - - - 3607 - 1 - 0 - 0 - 0 - - - 3608 - 1 - 0 - 0 - 0 - - - 3609 - 1 - 0 - 0 - 0 - - - 3610 - 1 - 0 - 0 - 0 - - - 3611 - 1 - 0 - 0 - 0 - - - 3612 - 1 - 0 - 0 - 0 - - - 3613 - 1 - 0 - 0 - 0 - - - 3614 - 1 - 0 - 0 - 0 - - - 3615 - 1 - 0 - 0 - 0 - - - 3616 - 1 - 0 - 0 - 0 - - - 3617 - 1 - 0 - 0 - 0 - - - 3618 - 1 - 0 - 0 - 0 - - - 3619 - 1 - 0 - 0 - 0 - - - 3620 - 1 - 0 - 0 - 0 - - - 3621 - 1 - 0 - 0 - 0 - - - 3622 - 1 - 0 - 0 - 0 - - - 3623 - 1 - 0 - 0 - 0 - - - 3624 - 1 - 0 - 0 - 0 - - - 3625 - 1 - 0 - 0 - 0 - - - 3626 - 1 - 0 - 0 - 0 - - - 3627 - 1 - 0 - 0 - 0 - - - 3628 - 1 - 0 - 0 - 0 - - - 3629 - 1 - 0 - 0 - 0 - - - 3630 - 1 - 0 - 0 - 0 - - - 3631 - 1 - 0 - 0 - 0 - - - 3632 - 1 - 0 - 0 - 0 - - - 3633 - 1 - 0 - 0 - 0 - - - 3634 - 1 - 0 - 0 - 0 - - - 3635 - 1 - 0 - 0 - 0 - - - 3636 - 1 - 0 - 0 - 0 - - - 3637 - 1 - 0 - 0 - 0 - - - 3638 - 1 - 0 - 0 - 0 - - - 3639 - 1 - 0 - 0 - 0 - - - 3640 - 1 - 0 - 0 - 0 - - - 3641 - 1 - 0 - 0 - 0 - - - 3642 - 1 - 0 - 0 - 0 - - - 3643 - 1 - 0 - 0 - 0 - - - 3644 - 1 - 0 - 0 - 0 - - - 3645 - 1 - 0 - 0 - 0 - - - 3646 - 1 - 0 - 0 - 0 - - - 3647 - 1 - 0 - 0 - 0 - - - 3648 - 1 - 0 - 0 - 0 - - - 3649 - 1 - 0 - 0 - 0 - - - 3650 - 1 - 0 - 0 - 0 - - - 3651 - 1 - 0 - 0 - 0 - - - 3652 - 1 - 0 - 0 - 0 - - - 3653 - 1 - 0 - 0 - 0 - - - 3654 - 1 - 0 - 0 - 0 - - - 3655 - 1 - 0 - 0 - 0 - - - 3656 - 1 - 0 - 0 - 0 - - - 3657 - 1 - 0 - 0 - 0 - - - 3658 - 1 - 0 - 0 - 0 - - - 3659 - 1 - 0 - 0 - 0 - - - 3660 - 1 - 0 - 0 - 0 - - - 3661 - 1 - 0 - 0 - 0 - - - 3662 - 1 - 0 - 0 - 0 - - - 3663 - 1 - 0 - 0 - 0 - - - 3664 - 1 - 0 - 0 - 0 - - - 3665 - 1 - 0 - 0 - 0 - - - 3666 - 1 - 0 - 0 - 0 - - - 3667 - 1 - 0 - 0 - 0 - - - 3668 - 1920 - 0 - 0 - 0 - - - 3669 - 1920 - 0 - 0 - 0 - - - 3670 - 6400 - 0 - 0 - 0 - - - 3671 - 6400 - 0 - 0 - 0 - - - 3672 - 64000 - 0 - 0 - 0 - - - 3673 - 64000 - 0 - 0 - 0 - - - 3674 - 64000 - 0 - 0 - 0 - - - 3675 - 64000 - 0 - 0 - 0 - - - 3676 - 64000 - 0 - 0 - 0 - - - 3677 - 64000 - 0 - 0 - 0 - - - 3678 - 13000 - 0 - 0 - 0 - - - 3679 - 1 - 0 - 0 - 0 - - - 3680 - 1 - 0 - 0 - 0 - - - 3681 - 1 - 0 - 0 - 0 - - - 3682 - 1 - 0 - 0 - 0 - - - 3683 - 1 - 0 - 0 - 0 - - - 3684 - 1 - 0 - 0 - 0 - - - 3685 - 1 - 0 - 0 - 0 - - - 3686 - 1 - 0 - 0 - 0 - - - 3687 - 1 - 0 - 0 - 0 - - - 3688 - 1 - 0 - 0 - 0 - - - 3689 - 1 - 0 - 0 - 0 - - - 3690 - 1 - 0 - 0 - 0 - - - 3691 - 1000 - 0 - 0 - 0 - - - 3692 - 1 - 0 - 0 - 0 - - - 3693 - 1 - 0 - 0 - 0 - - - 3694 - 1 - 0 - 0 - 0 - - - 3695 - 1 - 0 - 0 - 0 - - - 3696 - 4 - 0 - 0 - 0 - - - 3697 - 4 - 0 - 0 - 0 - - - 3698 - 1 - 0 - 0 - 0 - - - 3699 - 1 - 0 - 0 - 0 - - - 3700 - 1 - 0 - 0 - 0 - - - 3701 - 1 - 0 - 0 - 0 - - - 3702 - 1 - 0 - 0 - 0 - - - 3703 - 1 - 0 - 0 - 0 - - - 3704 - 1 - 0 - 0 - 0 - - - 3705 - 1 - 0 - 0 - 0 - - - 3706 - 1 - 0 - 0 - 0 - - - 3707 - 1 - 0 - 0 - 0 - - - 3708 - 1 - 0 - 0 - 0 - - - 3709 - 1 - 0 - 0 - 0 - - - 3710 - 1 - 0 - 0 - 0 - - - 3711 - 1 - 0 - 0 - 0 - - - 3712 - 1 - 0 - 0 - 0 - - - 3713 - 1 - 0 - 0 - 0 - - - 3714 - 1 - 0 - 0 - 0 - - - 3715 - 1 - 0 - 0 - 0 - - - 3716 - 1 - 0 - 0 - 0 - - - 3717 - 1 - 0 - 0 - 0 - - - 3718 - 3 - 0 - 0 - 0 - - - 3719 - 1 - 0 - 0 - 0 - - - 3720 - 1 - 0 - 0 - 0 - - - 3721 - 2 - 0 - 0 - 0 - - - 3722 - 1 - 0 - 0 - 0 - - - 3723 - 1 - 0 - 0 - 0 - - - 3724 - 1 - 0 - 0 - 0 - - - 3725 - 1 - 0 - 0 - 0 - - - 3726 - 1 - 0 - 0 - 0 - - - 3727 - 1 - 0 - 0 - 0 - - - 3728 - 1 - 0 - 0 - 0 - - - 3729 - 1 - 0 - 0 - 0 - - - 3730 - 1 - 0 - 0 - 0 - - - 3731 - 1 - 0 - 0 - 0 - - - 3732 - 1 - 0 - 0 - 0 - - - 3733 - 1 - 0 - 0 - 0 - - - 3734 - 1 - 0 - 0 - 0 - - - 3735 - 1 - 0 - 0 - 0 - - - 3736 - 1 - 0 - 0 - 0 - - - 3737 - 1 - 0 - 0 - 0 - - - 3738 - 1 - 0 - 0 - 0 - - - 3739 - 1 - 0 - 0 - 0 - - - 3740 - 1 - 0 - 0 - 0 - - - 3741 - 1 - 0 - 0 - 0 - - - 3742 - 1 - 0 - 0 - 0 - - - 3743 - 1 - 0 - 0 - 0 - - - 3744 - 1 - 0 - 0 - 0 - - - 3745 - 1 - 0 - 0 - 0 - - - 3746 - 1 - 0 - 0 - 0 - - - 3747 - 1 - 0 - 0 - 0 - - - 3748 - 6500 - 0 - 0 - 0 - - - 3749 - 78000 - 0 - 0 - 0 - - - 3750 - 78000 - 0 - 0 - 0 - - - 3751 - 78000 - 0 - 0 - 0 - - - 3752 - 78000 - 0 - 0 - 0 - - - 3753 - 78000 - 0 - 0 - 0 - - - 3754 - 78000 - 0 - 0 - 0 - - - 3755 - 78000 - 0 - 0 - 0 - - - 3756 - 78000 - 0 - 0 - 0 - - - 3757 - 5000 - 0 - 0 - 0 - - - 3758 - 5000 - 0 - 0 - 0 - - - 3759 - 325 - 0 - 0 - 0 - - - 3760 - 325 - 0 - 0 - 0 - - - 3761 - 325 - 0 - 0 - 0 - - - 3762 - 325 - 0 - 0 - 0 - - - 3763 - 325 - 0 - 0 - 0 - - - 3764 - 325 - 0 - 0 - 0 - - - 3765 - 325 - 0 - 0 - 0 - - - 3766 - 325 - 0 - 0 - 0 - - - 3767 - 325 - 0 - 0 - 0 - - - 3768 - 325 - 0 - 0 - 0 - - - 3769 - 325 - 0 - 0 - 0 - - - 3770 - 325 - 0 - 0 - 0 - - - 3771 - 325 - 0 - 0 - 0 - - - 3772 - 325 - 0 - 0 - 0 - - - 3773 - 325 - 0 - 0 - 0 - - - 3774 - 325 - 0 - 0 - 0 - - - 3775 - 325 - 0 - 0 - 0 - - - 3776 - 325 - 0 - 0 - 0 - - - 3777 - 325 - 0 - 0 - 0 - - - 3778 - 325 - 0 - 0 - 0 - - - 3779 - 325 - 0 - 0 - 0 - - - 3780 - 325 - 0 - 0 - 0 - - - 3781 - 325 - 0 - 0 - 0 - - - 3782 - 325 - 0 - 0 - 0 - - - 3783 - 325 - 0 - 0 - 0 - - - 3784 - 325 - 0 - 0 - 0 - - - 3785 - 325 - 0 - 0 - 0 - - - 3786 - 325 - 0 - 0 - 0 - - - 3787 - 325 - 0 - 0 - 0 - - - 3788 - 325 - 0 - 0 - 0 - - - 3789 - 325 - 0 - 0 - 0 - - - 3790 - 325 - 0 - 0 - 0 - - - 3791 - 650 - 0 - 0 - 0 - - - 3792 - 650 - 0 - 0 - 0 - - - 3793 - 650 - 0 - 0 - 0 - - - 3794 - 650 - 0 - 0 - 0 - - - 3795 - 650 - 0 - 0 - 0 - - - 3796 - 650 - 0 - 0 - 0 - - - 3797 - 650 - 0 - 0 - 0 - - - 3798 - 650 - 0 - 0 - 0 - - - 3799 - 650 - 0 - 0 - 0 - - - 3800 - 650 - 0 - 0 - 0 - - - 3801 - 325 - 0 - 0 - 0 - - - 3802 - 325 - 0 - 0 - 0 - - - 3803 - 26 - 0 - 0 - 0 - - - 3804 - 26 - 0 - 0 - 0 - - - 3805 - 5 - 0 - 0 - 0 - - - 3806 - 5 - 0 - 0 - 0 - - - 3807 - 1 - 0 - 0 - 0 - - - 3808 - 1 - 0 - 0 - 0 - - - 3809 - 1 - 0 - 0 - 0 - - - 3810 - 1 - 0 - 0 - 0 - - - 3811 - 1 - 0 - 0 - 0 - - - 3812 - 1 - 0 - 0 - 0 - - - 3813 - 1 - 0 - 0 - 0 - - - 3814 - 1 - 0 - 0 - 0 - - - 3815 - 1 - 0 - 0 - 0 - - - 3816 - 1 - 0 - 0 - 0 - - - 3817 - 1 - 0 - 0 - 0 - - - 3818 - 1 - 0 - 0 - 0 - - - 3819 - 1 - 0 - 0 - 0 - - - 3820 - 1 - 0 - 0 - 0 - - - 3821 - 1 - 0 - 0 - 0 - - - 3822 - 1 - 0 - 0 - 0 - - - 3823 - 1 - 0 - 0 - 0 - - - 3824 - 1 - 0 - 0 - 0 - - - 3825 - 1 - 0 - 0 - 0 - - - 3826 - 1 - 0 - 0 - 0 - - - 3827 - 200 - 0 - 0 - 0 - - - 3828 - 200 - 0 - 0 - 0 - - - 3829 - 200 - 0 - 0 - 0 - - - 3830 - 200 - 0 - 0 - 0 - - - 3831 - 200 - 0 - 0 - 0 - - - 3832 - 200 - 0 - 0 - 0 - - - 3833 - 200 - 0 - 0 - 0 - - - 3834 - 200 - 0 - 0 - 0 - - - 3835 - 200 - 0 - 0 - 0 - - - 3836 - 200 - 0 - 0 - 0 - - - 3837 - 200 - 0 - 0 - 0 - - - 3838 - 200 - 0 - 0 - 0 - - - 3839 - 200 - 0 - 0 - 0 - - - 3840 - 200 - 0 - 0 - 0 - - - 3841 - 200 - 0 - 0 - 0 - - - 3842 - 200 - 0 - 0 - 0 - - - 3843 - 200 - 0 - 0 - 0 - - - 3844 - 200 - 0 - 0 - 0 - - - 3845 - 1 - 0 - 0 - 0 - - - 3846 - 1 - 0 - 0 - 0 - - - 3847 - 1 - 0 - 0 - 0 - - - 3848 - 1 - 0 - 0 - 0 - - - 3849 - 1 - 0 - 0 - 0 - - - 3850 - 1 - 0 - 0 - 0 - - - 3851 - 1 - 0 - 0 - 0 - - - 3852 - 200 - 0 - 0 - 0 - - - 3853 - 1050 - 0 - 0 - 0 - - - 3854 - 1050 - 0 - 0 - 0 - - - 3855 - 1050 - 0 - 0 - 0 - - - 3856 - 1050 - 0 - 0 - 0 - - - 3857 - 1050 - 0 - 0 - 0 - - - 3858 - 1050 - 0 - 0 - 0 - - - 3859 - 1050 - 0 - 0 - 0 - - - 3860 - 1050 - 0 - 0 - 0 - - - 3861 - 1050 - 0 - 0 - 0 - - - 3862 - 1050 - 0 - 0 - 0 - - - 3863 - 1050 - 0 - 0 - 0 - - - 3864 - 1050 - 0 - 0 - 0 - - - 3865 - 1050 - 0 - 0 - 0 - - - 3866 - 1050 - 0 - 0 - 0 - - - 3867 - 1050 - 0 - 0 - 0 - - - 3868 - 1050 - 0 - 0 - 0 - - - 3869 - 1 - 0 - 0 - 0 - - - 3870 - 1 - 0 - 0 - 0 - - - 3871 - 1 - 0 - 0 - 0 - - - 3872 - 1 - 0 - 0 - 0 - - - 3873 - 1 - 0 - 0 - 0 - - - 3874 - 1 - 0 - 0 - 0 - - - 3875 - 1 - 0 - 0 - 0 - - - 3876 - 1 - 0 - 0 - 0 - - - 3877 - 1 - 0 - 0 - 0 - - - 3878 - 1 - 0 - 0 - 0 - - - 3879 - 1 - 0 - 0 - 0 - - - 3880 - 1 - 0 - 0 - 0 - - - 3881 - 1 - 0 - 0 - 0 - - - 3882 - 1 - 0 - 0 - 0 - - - 3883 - 1 - 0 - 0 - 0 - - - 3884 - 1 - 0 - 0 - 0 - - - 3885 - 1 - 0 - 0 - 0 - - - 3886 - 1 - 0 - 0 - 0 - - - 3887 - 1 - 0 - 0 - 0 - - - 3888 - 1 - 0 - 0 - 0 - - - 3889 - 1 - 0 - 0 - 0 - - - 3890 - 1 - 0 - 0 - 0 - - - 3891 - 1 - 0 - 0 - 0 - - - 3892 - 1 - 0 - 0 - 0 - - - 3893 - 800 - 0 - 0 - 0 - - - 3894 - 1 - 0 - 0 - 0 - - - 3895 - 1 - 0 - 0 - 0 - - - 3896 - 1 - 0 - 0 - 0 - - - 3897 - 1 - 0 - 0 - 0 - - - 3898 - 1 - 0 - 0 - 0 - - - 3899 - 15 - 0 - 0 - 0 - - - 3900 - 1 - 0 - 0 - 0 - - - 3901 - 1 - 0 - 0 - 0 - - - 3902 - 1 - 0 - 0 - 0 - - - 3903 - 1 - 0 - 0 - 0 - - - 3904 - 1 - 0 - 0 - 0 - - - 3905 - 1 - 0 - 0 - 0 - - - 3906 - 1 - 0 - 0 - 0 - - - 3907 - 1 - 0 - 0 - 0 - - - 3908 - 1 - 0 - 0 - 0 - - - 3909 - 1 - 0 - 0 - 0 - - - 3910 - 1 - 0 - 0 - 0 - - - 3911 - 1 - 0 - 0 - 0 - - - 3912 - 1 - 0 - 0 - 0 - - - 3913 - 1 - 0 - 0 - 0 - - - 3914 - 1 - 0 - 0 - 0 - - - 3915 - 1 - 0 - 0 - 0 - - - 3916 - 1 - 0 - 0 - 0 - - - 3917 - 1 - 0 - 0 - 0 - - - 3918 - 1 - 0 - 0 - 0 - - - 3919 - 1 - 0 - 0 - 0 - - - 3920 - 1 - 0 - 0 - 0 - - - 3921 - 1 - 0 - 0 - 0 - - - 3922 - 1 - 0 - 0 - 0 - - - 3923 - 1 - 0 - 0 - 0 - - - 3924 - 1 - 0 - 0 - 0 - - - 3925 - 1 - 0 - 0 - 0 - - - 3926 - 1 - 0 - 0 - 0 - - - 3927 - 1 - 0 - 0 - 0 - - - 3928 - 1 - 0 - 0 - 0 - - - 3929 - 1 - 0 - 0 - 0 - - - 3930 - 1 - 0 - 0 - 0 - - - 3931 - 1 - 0 - 0 - 0 - - - 3932 - 1 - 0 - 0 - 0 - - - 3933 - 1 - 0 - 0 - 0 - - - 3934 - 1 - 0 - 0 - 0 - - - 3935 - 1 - 0 - 0 - 0 - - - 3936 - 1 - 0 - 0 - 0 - - - 3937 - 1 - 0 - 0 - 0 - - - 3938 - 1 - 0 - 0 - 0 - - - 3939 - 1 - 0 - 0 - 0 - - - 3940 - 1 - 0 - 0 - 0 - - - 3941 - 1 - 0 - 0 - 0 - - - 3942 - 1 - 0 - 0 - 0 - - - 3943 - 1 - 0 - 0 - 0 - - - 3944 - 1 - 0 - 0 - 0 - - - 3945 - 1 - 0 - 0 - 0 - - - 3946 - 1 - 0 - 0 - 0 - - - 3947 - 1 - 0 - 0 - 0 - - - 3948 - 1 - 0 - 0 - 0 - - - 3949 - 1 - 0 - 0 - 0 - - - 3950 - 1 - 0 - 0 - 0 - - - 3951 - 1 - 0 - 0 - 0 - - - 3952 - 1 - 0 - 0 - 0 - - - 3953 - 1 - 0 - 0 - 0 - - - 3954 - 1 - 0 - 0 - 0 - - - 3955 - 1 - 0 - 0 - 0 - - - 3956 - 1 - 0 - 0 - 0 - - - 3957 - 1 - 0 - 0 - 0 - - - 3958 - 1 - 0 - 0 - 0 - - - 3959 - 1 - 0 - 0 - 0 - - - 3960 - 1 - 0 - 0 - 0 - - - 3961 - 1 - 0 - 0 - 0 - - - 3962 - 1 - 0 - 0 - 0 - - - 3963 - 1 - 0 - 0 - 0 - - - 3964 - 1 - 0 - 0 - 0 - - - 3965 - 1 - 0 - 0 - 0 - - - 3966 - 1 - 0 - 0 - 0 - - - 3967 - 1 - 0 - 0 - 0 - - - 3968 - 1 - 0 - 0 - 0 - - - 3969 - 1 - 0 - 0 - 0 - - - 3970 - 1 - 0 - 0 - 0 - - - 3971 - 1 - 0 - 0 - 0 - - - 3972 - 1 - 0 - 0 - 0 - - - 3973 - 1 - 0 - 0 - 0 - - - 3974 - 1 - 0 - 0 - 0 - - - 3975 - 1 - 0 - 0 - 0 - - - 3976 - 1 - 0 - 0 - 0 - - - 3977 - 1 - 0 - 0 - 0 - - - 3978 - 1 - 0 - 0 - 0 - - - 3979 - 1 - 0 - 0 - 0 - - - 3980 - 1 - 0 - 0 - 0 - - - 3981 - 1 - 0 - 0 - 0 - - - 3982 - 1 - 0 - 0 - 0 - - - 3983 - 1 - 0 - 0 - 0 - - - 3984 - 1 - 0 - 0 - 0 - - - 3985 - 1 - 0 - 0 - 0 - - - 3986 - 1 - 0 - 0 - 0 - - - 3987 - 1 - 0 - 0 - 0 - - - 3988 - 1 - 0 - 0 - 0 - - - 3989 - 1 - 0 - 0 - 0 - - - 3990 - 1 - 0 - 0 - 0 - - - 3991 - 1 - 0 - 0 - 0 - - - 3992 - 1 - 0 - 0 - 0 - - - 3993 - 1 - 0 - 0 - 0 - - - 3994 - 1 - 0 - 0 - 0 - - - 3995 - 1 - 0 - 0 - 0 - - - 3996 - 1 - 0 - 0 - 0 - - - 3997 - 1 - 0 - 0 - 0 - - - 3998 - 1 - 0 - 0 - 0 - - - 3999 - 1 - 0 - 0 - 0 - - - 4000 - 1 - 0 - 0 - 0 - - - 4001 - 5 - 0 - 0 - 0 - - - 4002 - 1 - 0 - 0 - 0 - - - 4003 - 1 - 0 - 0 - 0 - - - 4004 - 1 - 0 - 0 - 0 - - - 4005 - 1 - 0 - 0 - 0 - - - 4006 - 10 - 0 - 0 - 0 - - - 4007 - 800 - 0 - 0 - 0 - - - 4008 - 3 - 0 - 0 - 0 - - - 4009 - 3 - 0 - 0 - 0 - - - 4010 - 3 - 0 - 0 - 0 - - - 4011 - 1 - 0 - 0 - 0 - - - 4012 - 3 - 0 - 0 - 0 - - - 4013 - 1 - 0 - 0 - 0 - - - 4014 - 50 - 0 - 0 - 0 - - - 4015 - 1 - 0 - 0 - 0 - - - 4016 - 300 - 0 - 0 - 0 - - - 4017 - 1 - 0 - 0 - 0 - - - 4018 - 10 - 0 - 0 - 0 - - - 4019 - 1 - 0 - 0 - 0 - - - 4020 - 10 - 0 - 0 - 0 - - - 4021 - 550 - 0 - 0 - 0 - - - 4022 - 500 - 0 - 0 - 0 - - - 4023 - 1000 - 0 - 0 - 0 - - - 4024 - 3000 - 0 - 0 - 0 - - - 4025 - 6000 - 0 - 0 - 0 - - - 4026 - 1000 - 0 - 0 - 0 - - - 4027 - 1500 - 0 - 0 - 0 - - - 4028 - 10000 - 0 - 0 - 0 - - - 4029 - 500 - 0 - 0 - 0 - - - 4030 - 2000 - 0 - 0 - 0 - - - 4031 - 500 - 0 - 0 - 0 - - - 4032 - 1 - 0 - 0 - 0 - - - 4033 - 1 - 0 - 0 - 0 - - - 4034 - 1 - 0 - 0 - 0 - - - 4035 - 1 - 0 - 0 - 0 - - - 4036 - 1 - 0 - 0 - 0 - - - 4037 - 1 - 0 - 0 - 0 - - - 4038 - 1 - 0 - 0 - 0 - - - 4039 - 1 - 0 - 0 - 0 - - - 4040 - 1 - 0 - 0 - 0 - - - 4041 - 1 - 0 - 0 - 0 - - - 4042 - 1 - 0 - 0 - 0 - - - 4043 - 1 - 0 - 0 - 0 - - - 4044 - 1 - 0 - 0 - 0 - - - 4045 - 1 - 0 - 0 - 0 - - - 4046 - 1 - 0 - 0 - 0 - - - 4047 - 1 - 0 - 0 - 0 - - - 4048 - 1 - 0 - 0 - 0 - - - 4049 - 1 - 0 - 0 - 0 - - - 4050 - 1 - 0 - 0 - 0 - - - 4051 - 1 - 0 - 0 - 0 - - - 4052 - 1 - 0 - 0 - 0 - - - 4053 - 1 - 0 - 0 - 0 - - - 4054 - 1 - 0 - 0 - 0 - - - 4055 - 5 - 0 - 0 - 0 - - - 4056 - 1 - 0 - 0 - 0 - - - 4057 - 1 - 0 - 0 - 0 - - - 4058 - 1 - 0 - 0 - 0 - - - 4059 - 1 - 0 - 0 - 0 - - - 4060 - 1 - 0 - 0 - 0 - - - 4061 - 1 - 0 - 0 - 0 - - - 4062 - 1 - 0 - 0 - 0 - - - 4063 - 1 - 0 - 0 - 0 - - - 4064 - 1 - 0 - 0 - 0 - - - 4065 - 1 - 0 - 0 - 0 - - - 4066 - 1 - 0 - 0 - 0 - - - 4067 - 1 - 0 - 0 - 0 - - - 4068 - 500 - 0 - 0 - 0 - - - 4069 - 2000 - 0 - 0 - 0 - - - 4070 - 1000 - 0 - 0 - 0 - - - 4071 - 300 - 0 - 0 - 0 - - - 4072 - 850 - 0 - 0 - 0 - - - 4073 - 1 - 0 - 0 - 0 - - - 4074 - 1 - 0 - 0 - 0 - - - 4075 - 1 - 0 - 0 - 0 - - - 4076 - 1 - 0 - 0 - 0 - - - 4077 - 1 - 0 - 0 - 0 - - - 4078 - 1 - 0 - 0 - 0 - - - 4079 - 10 - 0 - 0 - 0 - - - 4080 - 1 - 0 - 0 - 0 - - - 4081 - 1 - 0 - 0 - 0 - - - 4082 - 1 - 0 - 0 - 0 - - - 4083 - 150 - 0 - 0 - 0 - - - 4084 - 175 - 0 - 0 - 0 - - - 4085 - 15 - 0 - 0 - 0 - - - 4086 - 5 - 0 - 0 - 0 - - - 4087 - 270000 - 0 - 0 - 0 - - - 4088 - 270000 - 0 - 0 - 0 - - - 4089 - 15000 - 0 - 0 - 0 - - - 4090 - 15000 - 0 - 0 - 0 - - - 4091 - 120000 - 0 - 0 - 0 - - - 4092 - 120000 - 0 - 0 - 0 - - - 4093 - 80000 - 0 - 0 - 0 - - - 4094 - 80000 - 0 - 0 - 0 - - - 4095 - 10000 - 0 - 0 - 0 - - - 4096 - 10000 - 0 - 0 - 0 - - - 4097 - 10000 - 0 - 0 - 0 - - - 4098 - 10000 - 0 - 0 - 0 - - - 4099 - 15000 - 0 - 0 - 0 - - - 4100 - 15000 - 0 - 0 - 0 - - - 4101 - 120000 - 0 - 0 - 0 - - - 4102 - 120000 - 0 - 0 - 0 - - - 4103 - 80000 - 0 - 0 - 0 - - - 4104 - 80000 - 0 - 0 - 0 - - - 4105 - 10000 - 0 - 0 - 0 - - - 4106 - 10000 - 0 - 0 - 0 - - - 4107 - 10000 - 0 - 0 - 0 - - - 4108 - 10000 - 0 - 0 - 0 - - - 4109 - 15000 - 0 - 0 - 0 - - - 4110 - 15000 - 0 - 0 - 0 - - - 4111 - 120000 - 0 - 0 - 0 - - - 4112 - 120000 - 0 - 0 - 0 - - - 4113 - 80000 - 0 - 0 - 0 - - - 4114 - 80000 - 0 - 0 - 0 - - - 4115 - 10000 - 0 - 0 - 0 - - - 4116 - 10000 - 0 - 0 - 0 - - - 4117 - 10000 - 0 - 0 - 0 - - - 4118 - 10000 - 0 - 0 - 0 - - - 4119 - 24 - 0 - 0 - 0 - - - 4120 - 24 - 0 - 0 - 0 - - - 4121 - 84 - 0 - 0 - 0 - - - 4122 - 84 - 0 - 0 - 0 - - - 4123 - 300 - 0 - 0 - 0 - - - 4124 - 300 - 0 - 0 - 0 - - - 4125 - 576 - 0 - 0 - 0 - - - 4126 - 576 - 0 - 0 - 0 - - - 4127 - 780 - 0 - 0 - 0 - - - 4128 - 780 - 0 - 0 - 0 - - - 4129 - 1920 - 0 - 0 - 0 - - - 4130 - 1920 - 0 - 0 - 0 - - - 4131 - 12500 - 0 - 0 - 0 - - - 4132 - 12500 - 0 - 0 - 0 - - - 4133 - 1 - 0 - 0 - 0 - - - 4134 - 1 - 0 - 0 - 0 - - - 4135 - 1 - 0 - 0 - 0 - - - 4136 - 1 - 0 - 0 - 0 - - - 4137 - 1 - 0 - 0 - 0 - - - 4138 - 1 - 0 - 0 - 0 - - - 4139 - 1 - 0 - 0 - 0 - - - 4140 - 1 - 0 - 0 - 0 - - - 4141 - 1 - 0 - 0 - 0 - - - 4142 - 1 - 0 - 0 - 0 - - - 4143 - 1 - 0 - 0 - 0 - - - 4144 - 1 - 0 - 0 - 0 - - - 4145 - 1 - 0 - 0 - 0 - - - 4146 - 1 - 0 - 0 - 0 - - - 4147 - 1 - 0 - 0 - 0 - - - 4148 - 1 - 0 - 0 - 0 - - - 4149 - 1 - 0 - 0 - 0 - - - 4150 - 1 - 0 - 0 - 0 - - - 4151 - 120001 - 0 - 0 - 0 - - - 4152 - 120001 - 0 - 0 - 0 - - - 4153 - 50000 - 0 - 0 - 0 - - - 4154 - 50000 - 0 - 0 - 0 - - - 4155 - 1 - 0 - 0 - 0 - - - 4156 - 5000 - 0 - 0 - 0 - - - 4157 - 5000 - 0 - 0 - 0 - - - 4158 - 31000 - 0 - 0 - 0 - - - 4159 - 31000 - 0 - 0 - 0 - - - 4160 - 90 - 0 - 0 - 0 - - - 4161 - 10 - 0 - 0 - 0 - - - 4162 - 500 - 0 - 0 - 0 - - - 4163 - 500 - 0 - 0 - 0 - - - 4164 - 200 - 0 - 0 - 0 - - - 4165 - 200 - 0 - 0 - 0 - - - 4166 - 200 - 0 - 0 - 0 - - - 4167 - 200 - 0 - 0 - 0 - - - 4168 - 200 - 0 - 0 - 0 - - - 4169 - 200 - 0 - 0 - 0 - - - 4170 - 21000 - 0 - 0 - 0 - - - 4171 - 21000 - 0 - 0 - 0 - - - 4172 - 1 - 0 - 0 - 0 - - - 4173 - 1 - 0 - 0 - 0 - - - 4174 - 1 - 0 - 0 - 0 - - - 4175 - 1 - 0 - 0 - 0 - - - 4176 - 1 - 0 - 0 - 0 - - - 4177 - 1 - 0 - 0 - 0 - - - 4178 - 6 - 0 - 0 - 0 - - - 4179 - 1 - 0 - 0 - 0 - - - 4180 - 6 - 0 - 0 - 0 - - - 4181 - 1 - 0 - 0 - 0 - - - 4182 - 6 - 0 - 0 - 0 - - - 4183 - 1 - 0 - 0 - 0 - - - 4184 - 1 - 0 - 0 - 0 - - - 4185 - 1 - 0 - 0 - 0 - - - 4186 - 1 - 0 - 0 - 0 - - - 4187 - 1 - 0 - 0 - 0 - - - 4188 - 1 - 0 - 0 - 0 - - - 4189 - 1 - 0 - 0 - 0 - - - 4190 - 1 - 0 - 0 - 0 - - - 4191 - 1 - 0 - 0 - 0 - - - 4192 - 1 - 0 - 0 - 0 - - - 4193 - 1 - 0 - 0 - 0 - - - 4194 - 1 - 0 - 0 - 0 - - - 4195 - 1 - 0 - 0 - 0 - - - 4196 - 1 - 0 - 0 - 0 - - - 4197 - 1 - 0 - 0 - 0 - - - 4198 - 1 - 0 - 0 - 0 - - - 4199 - 1 - 0 - 0 - 0 - - - 4200 - 1 - 0 - 0 - 0 - - - 4201 - 1 - 0 - 0 - 0 - - - 4202 - 1 - 0 - 0 - 0 - - - 4203 - 1 - 0 - 0 - 0 - - - 4204 - 1 - 0 - 0 - 0 - - - 4205 - 1 - 0 - 0 - 0 - - - 4206 - 1 - 0 - 0 - 0 - - - 4207 - 1 - 0 - 0 - 0 - - - 4208 - 1 - 0 - 0 - 0 - - - 4209 - 2 - 0 - 0 - 0 - - - 4210 - 1 - 0 - 0 - 0 - - - 4211 - 1 - 0 - 0 - 0 - - - 4212 - 900000 - 0 - 0 - 0 - - - 4213 - 900000 - 0 - 0 - 0 - - - 4214 - 900000 - 0 - 0 - 0 - - - 4215 - 800000 - 0 - 0 - 0 - - - 4216 - 700000 - 0 - 0 - 0 - - - 4217 - 600000 - 0 - 0 - 0 - - - 4218 - 500000 - 0 - 0 - 0 - - - 4219 - 400000 - 0 - 0 - 0 - - - 4220 - 300000 - 0 - 0 - 0 - - - 4221 - 200000 - 0 - 0 - 0 - - - 4222 - 100000 - 0 - 0 - 0 - - - 4223 - 50000 - 0 - 0 - 0 - - - 4224 - 800000 - 0 - 0 - 0 - - - 4225 - 750000 - 0 - 0 - 0 - - - 4226 - 670000 - 0 - 0 - 0 - - - 4227 - 590000 - 0 - 0 - 0 - - - 4228 - 510000 - 0 - 0 - 0 - - - 4229 - 430000 - 0 - 0 - 0 - - - 4230 - 350000 - 0 - 0 - 0 - - - 4231 - 270000 - 0 - 0 - 0 - - - 4232 - 190000 - 0 - 0 - 0 - - - 4233 - 100000 - 0 - 0 - 0 - - - 4234 - 25000 - 0 - 0 - 0 - - - 4235 - 800000 - 0 - 0 - 0 - - - 4236 - 160 - 0 - 0 - 0 - - - 4237 - 4 - 0 - 0 - 0 - - - 4238 - 1 - 0 - 0 - 0 - - - 4239 - 4 - 0 - 0 - 0 - - - 4240 - 4 - 0 - 0 - 0 - - - 4241 - 3 - 0 - 0 - 0 - - - 4242 - 10 - 0 - 0 - 0 - - - 4243 - 10 - 0 - 0 - 0 - - - 4244 - 2 - 0 - 0 - 0 - - - 4245 - 10 - 0 - 0 - 0 - - - 4246 - 10 - 0 - 0 - 0 - - - 4247 - 1 - 0 - 0 - 0 - - - 4248 - 1 - 0 - 0 - 0 - - - 4249 - 1 - 0 - 0 - 0 - - - 4250 - 35 - 0 - 0 - 0 - - - 4251 - 1 - 0 - 0 - 0 - - - 4252 - 1 - 0 - 0 - 0 - - - 4253 - 1 - 0 - 0 - 0 - - - 4254 - 1 - 0 - 0 - 0 - - - 4255 - 1 - 0 - 0 - 0 - - - 4256 - 1 - 0 - 0 - 0 - - - 4257 - 1 - 0 - 0 - 0 - - - 4258 - 1 - 0 - 0 - 0 - - - 4259 - 1 - 0 - 0 - 0 - - - 4260 - 1 - 0 - 0 - 0 - - - 4261 - 1 - 0 - 0 - 0 - - - 4262 - 1 - 0 - 0 - 0 - - - 4263 - 1 - 0 - 0 - 0 - - - 4264 - 1 - 0 - 0 - 0 - - - 4265 - 1 - 0 - 0 - 0 - - - 4266 - 1 - 0 - 0 - 0 - - - 4267 - 1 - 0 - 0 - 0 - - - 4268 - 1 - 0 - 0 - 0 - - - 4269 - 1 - 0 - 0 - 0 - - - 4270 - 1 - 0 - 0 - 0 - - - 4271 - 1 - 0 - 0 - 0 - - - 4272 - 1 - 0 - 0 - 0 - - - 4273 - 1 - 0 - 0 - 0 - - - 4274 - 1 - 0 - 0 - 0 - - - 4275 - 1 - 0 - 0 - 0 - - - 4276 - 1 - 0 - 0 - 0 - - - 4277 - 1 - 0 - 0 - 0 - - - 4278 - 1 - 0 - 0 - 0 - - - 4279 - 1 - 0 - 0 - 0 - - - 4280 - 1 - 0 - 0 - 0 - - - 4281 - 1 - 0 - 0 - 0 - - - 4282 - 1 - 0 - 0 - 0 - - - 4283 - 1 - 0 - 0 - 0 - - - 4284 - 1 - 0 - 0 - 0 - - - 4285 - 1 - 0 - 0 - 0 - - - 4286 - 1 - 0 - 0 - 0 - - - 4287 - 1 - 0 - 0 - 0 - - - 4288 - 1 - 0 - 0 - 0 - - - 4289 - 1 - 0 - 0 - 0 - - - 4290 - 1 - 0 - 0 - 0 - - - 4291 - 4 - 0 - 0 - 0 - - - 4292 - 4 - 0 - 0 - 0 - - - 4293 - 4 - 0 - 0 - 0 - - - 4294 - 4 - 0 - 0 - 0 - - - 4295 - 1 - 0 - 0 - 0 - - - 4296 - 1 - 0 - 0 - 0 - - - 4297 - 1 - 0 - 0 - 0 - - - 4298 - 75 - 0 - 0 - 0 - - - 4299 - 75 - 0 - 0 - 0 - - - 4300 - 75 - 0 - 0 - 0 - - - 4301 - 75 - 0 - 0 - 0 - - - 4302 - 75 - 0 - 0 - 0 - - - 4303 - 75 - 0 - 0 - 0 - - - 4304 - 75 - 0 - 0 - 0 - - - 4305 - 75 - 0 - 0 - 0 - - - 4306 - 75 - 0 - 0 - 0 - - - 4307 - 75 - 0 - 0 - 0 - - - 4308 - 75 - 0 - 0 - 0 - - - 4309 - 75 - 0 - 0 - 0 - - - 4310 - 75 - 0 - 0 - 0 - - - 4311 - 75 - 0 - 0 - 0 - - - 4312 - 1 - 0 - 0 - 0 - - - 4313 - 2 - 0 - 0 - 0 - - - 4314 - 1 - 0 - 0 - 0 - - - 4315 - 50 - 0 - 0 - 0 - - - 4316 - 50 - 0 - 0 - 0 - - - 4317 - 50 - 0 - 0 - 0 - - - 4318 - 50 - 0 - 0 - 0 - - - 4319 - 50 - 0 - 0 - 0 - - - 4320 - 50 - 0 - 0 - 0 - - - 4321 - 50 - 0 - 0 - 0 - - - 4322 - 50 - 0 - 0 - 0 - - - 4323 - 50 - 0 - 0 - 0 - - - 4324 - 50 - 0 - 0 - 0 - - - 4325 - 50 - 0 - 0 - 0 - - - 4326 - 50 - 0 - 0 - 0 - - - 4327 - 50 - 0 - 0 - 0 - - - 4328 - 50 - 0 - 0 - 0 - - - 4329 - 50 - 0 - 0 - 0 - - - 4330 - 50 - 0 - 0 - 0 - - - 4331 - 50 - 0 - 0 - 0 - - - 4332 - 50 - 0 - 0 - 0 - - - 4333 - 50 - 0 - 0 - 0 - - - 4334 - 50 - 0 - 0 - 0 - - - 4335 - 50 - 0 - 0 - 0 - - - 4336 - 50 - 0 - 0 - 0 - - - 4337 - 50 - 0 - 0 - 0 - - - 4338 - 50 - 0 - 0 - 0 - - - 4339 - 50 - 0 - 0 - 0 - - - 4340 - 50 - 0 - 0 - 0 - - - 4341 - 50 - 0 - 0 - 0 - - - 4342 - 50 - 0 - 0 - 0 - - - 4343 - 50 - 0 - 0 - 0 - - - 4344 - 50 - 0 - 0 - 0 - - - 4345 - 50 - 0 - 0 - 0 - - - 4346 - 50 - 0 - 0 - 0 - - - 4347 - 50 - 0 - 0 - 0 - - - 4348 - 50 - 0 - 0 - 0 - - - 4349 - 50 - 0 - 0 - 0 - - - 4350 - 50 - 0 - 0 - 0 - - - 4351 - 50 - 0 - 0 - 0 - - - 4352 - 50 - 0 - 0 - 0 - - - 4353 - 50 - 0 - 0 - 0 - - - 4354 - 50 - 0 - 0 - 0 - - - 4355 - 50 - 0 - 0 - 0 - - - 4356 - 50 - 0 - 0 - 0 - - - 4357 - 50 - 0 - 0 - 0 - - - 4358 - 50 - 0 - 0 - 0 - - - 4359 - 50 - 0 - 0 - 0 - - - 4360 - 50 - 0 - 0 - 0 - - - 4361 - 50 - 0 - 0 - 0 - - - 4362 - 50 - 0 - 0 - 0 - - - 4363 - 50 - 0 - 0 - 0 - - - 4364 - 50 - 0 - 0 - 0 - - - 4365 - 50 - 0 - 0 - 0 - - - 4366 - 50 - 0 - 0 - 0 - - - 4367 - 50 - 0 - 0 - 0 - - - 4368 - 50 - 0 - 0 - 0 - - - 4369 - 50 - 0 - 0 - 0 - - - 4370 - 50 - 0 - 0 - 0 - - - 4371 - 50 - 0 - 0 - 0 - - - 4372 - 50 - 0 - 0 - 0 - - - 4373 - 50 - 0 - 0 - 0 - - - 4374 - 50 - 0 - 0 - 0 - - - 4375 - 50 - 0 - 0 - 0 - - - 4376 - 50 - 0 - 0 - 0 - - - 4377 - 50 - 0 - 0 - 0 - - - 4378 - 50 - 0 - 0 - 0 - - - 4379 - 50 - 0 - 0 - 0 - - - 4380 - 50 - 0 - 0 - 0 - - - 4381 - 50 - 0 - 0 - 0 - - - 4382 - 50 - 0 - 0 - 0 - - - 4383 - 50 - 0 - 0 - 0 - - - 4384 - 50 - 0 - 0 - 0 - - - 4385 - 50 - 0 - 0 - 0 - - - 4386 - 50 - 0 - 0 - 0 - - - 4387 - 50 - 0 - 0 - 0 - - - 4388 - 50 - 0 - 0 - 0 - - - 4389 - 50 - 0 - 0 - 0 - - - 4390 - 50 - 0 - 0 - 0 - - - 4391 - 50 - 0 - 0 - 0 - - - 4392 - 50 - 0 - 0 - 0 - - - 4393 - 50 - 0 - 0 - 0 - - - 4394 - 50 - 0 - 0 - 0 - - - 4395 - 50 - 0 - 0 - 0 - - - 4396 - 50 - 0 - 0 - 0 - - - 4397 - 50 - 0 - 0 - 0 - - - 4398 - 50 - 0 - 0 - 0 - - - 4399 - 50 - 0 - 0 - 0 - - - 4400 - 50 - 0 - 0 - 0 - - - 4401 - 50 - 0 - 0 - 0 - - - 4402 - 50 - 0 - 0 - 0 - - - 4403 - 50 - 0 - 0 - 0 - - - 4404 - 50 - 0 - 0 - 0 - - - 4405 - 50 - 0 - 0 - 0 - - - 4406 - 50 - 0 - 0 - 0 - - - 4407 - 50 - 0 - 0 - 0 - - - 4408 - 50 - 0 - 0 - 0 - - - 4409 - 50 - 0 - 0 - 0 - - - 4410 - 50 - 0 - 0 - 0 - - - 4411 - 50 - 0 - 0 - 0 - - - 4412 - 50 - 0 - 0 - 0 - - - 4413 - 50 - 0 - 0 - 0 - - - 4414 - 50 - 0 - 0 - 0 - - - 4415 - 56 - 0 - 0 - 0 - - - 4416 - 100 - 0 - 0 - 0 - - - 4417 - 50 - 0 - 0 - 0 - - - 4418 - 50 - 0 - 0 - 0 - - - 4419 - 50 - 0 - 0 - 0 - - - 4420 - 50 - 0 - 0 - 0 - - - 4421 - 50 - 0 - 0 - 0 - - - 4422 - 50 - 0 - 0 - 0 - - - 4423 - 50 - 0 - 0 - 0 - - - 4424 - 50 - 0 - 0 - 0 - - - 4425 - 1 - 0 - 0 - 0 - - - 4426 - 1 - 0 - 0 - 0 - - - 4427 - 5 - 0 - 0 - 0 - - - 4428 - 50 - 0 - 0 - 0 - - - 4429 - 1 - 0 - 0 - 0 - - - 4430 - 1 - 0 - 0 - 0 - - - 4431 - 1 - 0 - 0 - 0 - - - 4432 - 1 - 0 - 0 - 0 - - - 4433 - 1 - 0 - 0 - 0 - - - 4434 - 1 - 0 - 0 - 0 - - - 4435 - 1 - 0 - 0 - 0 - - - 4436 - 10 - 0 - 0 - 0 - - - 4437 - 10 - 0 - 0 - 0 - - - 4438 - 10 - 0 - 0 - 0 - - - 4439 - 10 - 0 - 0 - 0 - - - 4440 - 15 - 0 - 0 - 0 - - - 4441 - 15 - 0 - 0 - 0 - - - 4442 - 1 - 0 - 0 - 0 - - - 4443 - 1 - 0 - 0 - 0 - - - 4444 - 56 - 0 - 0 - 0 - - - 4445 - 1 - 0 - 0 - 0 - - - 4446 - 1 - 0 - 0 - 0 - - - 4447 - 1 - 0 - 0 - 0 - - - 4448 - 1 - 0 - 0 - 0 - - - 4449 - 1 - 0 - 0 - 0 - - - 4450 - 1 - 0 - 0 - 0 - - - 4451 - 1 - 0 - 0 - 0 - - - 4452 - 1 - 0 - 0 - 0 - - - 4453 - 1 - 0 - 0 - 0 - - - 4454 - 1 - 0 - 0 - 0 - - - 4455 - 1 - 0 - 0 - 0 - - - 4456 - 10 - 0 - 0 - 0 - - - 4457 - 10 - 0 - 0 - 0 - - - 4458 - 10 - 0 - 0 - 0 - - - 4459 - 10 - 0 - 0 - 0 - - - 4460 - 10 - 0 - 0 - 0 - - - 4461 - 10 - 0 - 0 - 0 - - - 4462 - 10 - 0 - 0 - 0 - - - 4463 - 10 - 0 - 0 - 0 - - - 4464 - 10 - 0 - 0 - 0 - - - 4465 - 10 - 0 - 0 - 0 - - - 4466 - 10 - 0 - 0 - 0 - - - 4467 - 10 - 0 - 0 - 0 - - - 4468 - 10 - 0 - 0 - 0 - - - 4469 - 10 - 0 - 0 - 0 - - - 4470 - 15 - 0 - 0 - 0 - - - 4471 - 15 - 0 - 0 - 0 - - - 4472 - 15 - 0 - 0 - 0 - - - 4473 - 15 - 0 - 0 - 0 - - - 4474 - 15 - 0 - 0 - 0 - - - 4475 - 15 - 0 - 0 - 0 - - - 4476 - 15 - 0 - 0 - 0 - - - 4477 - 15 - 0 - 0 - 0 - - - 4478 - 30 - 0 - 0 - 0 - - - 4479 - 30 - 0 - 0 - 0 - - - 4480 - 30 - 0 - 0 - 0 - - - 4481 - 30 - 0 - 0 - 0 - - - 4482 - 30 - 0 - 0 - 0 - - - 4483 - 30 - 0 - 0 - 0 - - - 4484 - 1 - 0 - 0 - 0 - - - 4485 - 1 - 0 - 0 - 0 - - - 4486 - 1 - 0 - 0 - 0 - - - 4487 - 1 - 0 - 0 - 0 - - - 4488 - 1 - 0 - 0 - 0 - - - 4489 - 1 - 0 - 0 - 0 - - - 4490 - 1 - 0 - 0 - 0 - - - 4491 - 1 - 0 - 0 - 0 - - - 4492 - 1 - 0 - 0 - 0 - - - 4493 - 1 - 0 - 0 - 0 - - - 4494 - 1 - 0 - 0 - 0 - - - 4495 - 1 - 0 - 0 - 0 - - - 4496 - 1 - 0 - 0 - 0 - - - 4497 - 1 - 0 - 0 - 0 - - - 4498 - 1 - 0 - 0 - 0 - - - 4499 - 1 - 0 - 0 - 0 - - - 4500 - 1 - 0 - 0 - 0 - - - 4501 - 1 - 0 - 0 - 0 - - - 4502 - 1 - 0 - 0 - 0 - - - 4503 - 1300 - 0 - 0 - 0 - - - 4504 - 5200 - 0 - 0 - 0 - - - 4505 - 2600 - 0 - 0 - 0 - - - 4506 - 780 - 0 - 0 - 0 - - - 4507 - 2210 - 0 - 0 - 0 - - - 4508 - 3200 - 0 - 0 - 0 - - - 4509 - 12800 - 0 - 0 - 0 - - - 4510 - 6400 - 0 - 0 - 0 - - - 4511 - 1920 - 0 - 0 - 0 - - - 4512 - 5440 - 0 - 0 - 0 - - - 4513 - 1 - 0 - 0 - 0 - - - 4514 - 100 - 0 - 0 - 0 - - - 4515 - 1 - 0 - 0 - 0 - - - 4516 - 100 - 0 - 0 - 0 - - - 4517 - 100 - 0 - 0 - 0 - - - 4518 - 100 - 0 - 0 - 0 - - - 4519 - 1 - 0 - 0 - 0 - - - 4520 - 1 - 0 - 0 - 0 - - - 4521 - 1 - 0 - 0 - 0 - - - 4522 - 28 - 0 - 0 - 0 - - - 4523 - 28 - 0 - 0 - 0 - - - 4524 - 28 - 0 - 0 - 0 - - - 4525 - 25 - 0 - 0 - 0 - - - 4526 - 25 - 0 - 0 - 0 - - - 4527 - 10 - 0 - 0 - 0 - - - 4528 - 10 - 0 - 0 - 0 - - - 4529 - 15 - 0 - 0 - 0 - - - 4530 - 15 - 0 - 0 - 0 - - - 4531 - 15 - 0 - 0 - 0 - - - 4532 - 15 - 0 - 0 - 0 - - - 4533 - 15 - 0 - 0 - 0 - - - 4534 - 15 - 0 - 0 - 0 - - - 4535 - 120 - 0 - 0 - 0 - - - 4536 - 120 - 0 - 0 - 0 - - - 4537 - 125 - 0 - 0 - 0 - - - 4538 - 125 - 0 - 0 - 0 - - - 4539 - 125 - 0 - 0 - 0 - - - 4540 - 90 - 0 - 0 - 0 - - - 4541 - 90 - 0 - 0 - 0 - - - 4542 - 70 - 0 - 0 - 0 - - - 4543 - 70 - 0 - 0 - 0 - - - 4544 - 300 - 0 - 0 - 0 - - - 4545 - 300 - 0 - 0 - 0 - - - 4546 - 400 - 0 - 0 - 0 - - - 4547 - 400 - 0 - 0 - 0 - - - 4548 - 420 - 0 - 0 - 0 - - - 4549 - 420 - 0 - 0 - 0 - - - 4550 - 420 - 0 - 0 - 0 - - - 4551 - 650 - 0 - 0 - 0 - - - 4552 - 650 - 0 - 0 - 0 - - - 4553 - 1 - 0 - 0 - 0 - - - 4554 - 1 - 0 - 0 - 0 - - - 4555 - 1 - 0 - 0 - 0 - - - 4556 - 1 - 0 - 0 - 0 - - - 4557 - 1 - 0 - 0 - 0 - - - 4558 - 1 - 0 - 0 - 0 - - - 4559 - 1 - 0 - 0 - 0 - - - 4560 - 1 - 0 - 0 - 0 - - - 4561 - 1 - 0 - 0 - 0 - - - 4562 - 1 - 0 - 0 - 0 - - - 4563 - 1 - 0 - 0 - 0 - - - 4564 - 1 - 0 - 0 - 0 - - - 4565 - 1 - 0 - 0 - 0 - - - 4566 - 1 - 0 - 0 - 0 - - - 4567 - 1 - 0 - 0 - 0 - - - 4568 - 1 - 0 - 0 - 0 - - - 4569 - 1 - 0 - 0 - 0 - - - 4570 - 1 - 0 - 0 - 0 - - - 4571 - 1 - 0 - 0 - 0 - - - 4572 - 1 - 0 - 0 - 0 - - - 4573 - 1 - 0 - 0 - 0 - - - 4574 - 1 - 0 - 0 - 0 - - - 4575 - 1 - 0 - 0 - 0 - - - 4576 - 1 - 0 - 0 - 0 - - - 4577 - 1 - 0 - 0 - 0 - - - 4578 - 1 - 0 - 0 - 0 - - - 4579 - 1 - 0 - 0 - 0 - - - 4580 - 650 - 0 - 0 - 0 - - - 4581 - 650 - 0 - 0 - 0 - - - 4582 - 750 - 0 - 0 - 0 - - - 4583 - 750 - 0 - 0 - 0 - - - 4584 - 750 - 0 - 0 - 0 - - - 4585 - 270000 - 0 - 0 - 0 - - - 4586 - 270000 - 0 - 0 - 0 - - - 4587 - 100000 - 0 - 0 - 0 - - - 4588 - 100000 - 0 - 0 - 0 - - - 4589 - 1 - 0 - 0 - 0 - - - 4590 - 1 - 0 - 0 - 0 - - - 4591 - 1 - 0 - 0 - 0 - - - 4592 - 1 - 0 - 0 - 0 - - - 4593 - 1 - 0 - 0 - 0 - - - 4594 - 1 - 0 - 0 - 0 - - - 4595 - 1 - 0 - 0 - 0 - - - 4596 - 1 - 0 - 0 - 0 - - - 4597 - 1 - 0 - 0 - 0 - - - 4598 - 1 - 0 - 0 - 0 - - - 4599 - 1 - 0 - 0 - 0 - - - 4600 - 600 - 0 - 0 - 0 - - - 4601 - 1 - 0 - 0 - 0 - - - 4602 - 1 - 0 - 0 - 0 - - - 4603 - 1 - 0 - 0 - 0 - - - 4604 - 1 - 0 - 0 - 0 - - - 4605 - 1 - 0 - 0 - 0 - - - 4606 - 1 - 0 - 0 - 0 - - - 4607 - 1 - 0 - 0 - 0 - - - 4608 - 3 - 0 - 0 - 0 - - - 4609 - 3 - 0 - 0 - 0 - - - 4610 - 1 - 0 - 0 - 0 - - - 4611 - 1 - 0 - 0 - 0 - - - 4612 - 600 - 0 - 0 - 0 - - - 4613 - 75 - 0 - 0 - 0 - - - 4614 - 1 - 0 - 0 - 0 - - - 4615 - 1 - 0 - 0 - 0 - - - 4616 - 2 - 0 - 0 - 0 - - - 4617 - 1 - 0 - 0 - 0 - - - 4618 - 2 - 0 - 0 - 0 - - - 4619 - 1 - 0 - 0 - 0 - - - 4620 - 1 - 0 - 0 - 0 - - - 4621 - 1 - 0 - 0 - 0 - - - 4622 - 1 - 0 - 0 - 0 - - - 4623 - 1 - 0 - 0 - 0 - - - 4624 - 1 - 0 - 0 - 0 - - - 4625 - 1 - 0 - 0 - 0 - - - 4626 - 1 - 0 - 0 - 0 - - - 4627 - 650 - 0 - 0 - 0 - - - 4628 - 650 - 0 - 0 - 0 - - - 4629 - 1 - 0 - 0 - 0 - - - 4630 - 1 - 0 - 0 - 0 - - - 4631 - 1 - 0 - 0 - 0 - - - 4632 - 1 - 0 - 0 - 0 - - - 4633 - 1 - 0 - 0 - 0 - - - 4634 - 1 - 0 - 0 - 0 - - - 4635 - 1 - 0 - 0 - 0 - - - 4636 - 1 - 0 - 0 - 0 - - - 4637 - 1 - 0 - 0 - 0 - - - 4638 - 1 - 0 - 0 - 0 - - - 4639 - 1 - 0 - 0 - 0 - - - 4640 - 1 - 0 - 0 - 0 - - - 4641 - 1 - 0 - 0 - 0 - - - 4642 - 1 - 0 - 0 - 0 - - - 4643 - 1 - 0 - 0 - 0 - - - 4644 - 1 - 0 - 0 - 0 - - - 4645 - 1 - 0 - 0 - 0 - - - 4646 - 1 - 0 - 0 - 0 - - - 4647 - 1 - 0 - 0 - 0 - - - 4648 - 1 - 0 - 0 - 0 - - - 4649 - 1 - 0 - 0 - 0 - - - 4650 - 1 - 0 - 0 - 0 - - - 4651 - 1 - 0 - 0 - 0 - - - 4652 - 1 - 0 - 0 - 0 - - - 4653 - 250 - 0 - 0 - 0 - - - 4654 - 250 - 0 - 0 - 0 - - - 4655 - 250 - 0 - 0 - 0 - - - 4656 - 250 - 0 - 0 - 0 - - - 4657 - 350 - 0 - 0 - 0 - - - 4658 - 250 - 0 - 0 - 0 - - - 4659 - 250 - 0 - 0 - 0 - - - 4660 - 250 - 0 - 0 - 0 - - - 4661 - 250 - 0 - 0 - 0 - - - 4662 - 250 - 0 - 0 - 0 - - - 4663 - 250 - 0 - 0 - 0 - - - 4664 - 250 - 0 - 0 - 0 - - - 4665 - 250 - 0 - 0 - 0 - - - 4666 - 250 - 0 - 0 - 0 - - - 4667 - 250 - 0 - 0 - 0 - - - 4668 - 10 - 0 - 0 - 0 - - - 4669 - 1 - 0 - 0 - 0 - - - 4670 - 250 - 0 - 0 - 0 - - - 4671 - 250 - 0 - 0 - 0 - - - 4672 - 250 - 0 - 0 - 0 - - - 4673 - 250 - 0 - 0 - 0 - - - 4674 - 250 - 0 - 0 - 0 - - - 4675 - 100000 - 0 - 0 - 0 - - - 4676 - 100000 - 0 - 0 - 0 - - - 4677 - 1 - 0 - 0 - 0 - - - 4678 - 1 - 0 - 0 - 0 - - - 4679 - 1 - 0 - 0 - 0 - - - 4680 - 1 - 0 - 0 - 0 - - - 4681 - 1 - 0 - 0 - 0 - - - 4682 - 1 - 0 - 0 - 0 - - - 4683 - 1 - 0 - 0 - 0 - - - 4684 - 30 - 0 - 0 - 0 - - - 4685 - 1 - 0 - 0 - 0 - - - 4686 - 1 - 0 - 0 - 0 - - - 4687 - 30 - 0 - 0 - 0 - - - 4688 - 1 - 0 - 0 - 0 - - - 4689 - 20 - 0 - 0 - 0 - - - 4690 - 1 - 0 - 0 - 0 - - - 4691 - 1 - 0 - 0 - 0 - - - 4692 - 1 - 0 - 0 - 0 - - - 4693 - 6 - 0 - 0 - 0 - - - 4694 - 20 - 0 - 0 - 0 - - - 4695 - 20 - 0 - 0 - 0 - - - 4696 - 20 - 0 - 0 - 0 - - - 4697 - 20 - 0 - 0 - 0 - - - 4698 - 20 - 0 - 0 - 0 - - - 4699 - 20 - 0 - 0 - 0 - - - 4700 - 600 - 0 - 0 - 0 - - - 4701 - 600 - 0 - 0 - 0 - - - 4702 - 600 - 0 - 0 - 0 - - - 4703 - 1 - 0 - 0 - 0 - - - 4704 - 1 - 0 - 0 - 0 - - - 4705 - 1 - 0 - 0 - 0 - - - 4706 - 1 - 0 - 0 - 0 - - - 4707 - 5 - 0 - 0 - 0 - - - 4708 - 13000 - 0 - 0 - 0 - - - 4709 - 13000 - 0 - 0 - 0 - - - 4710 - 85000 - 0 - 0 - 0 - - - 4711 - 85000 - 0 - 0 - 0 - - - 4712 - 50000 - 0 - 0 - 0 - - - 4713 - 50000 - 0 - 0 - 0 - - - 4714 - 47000 - 0 - 0 - 0 - - - 4715 - 47000 - 0 - 0 - 0 - - - 4716 - 103000 - 0 - 0 - 0 - - - 4717 - 103000 - 0 - 0 - 0 - - - 4718 - 208000 - 0 - 0 - 0 - - - 4719 - 208000 - 0 - 0 - 0 - - - 4720 - 280000 - 0 - 0 - 0 - - - 4721 - 280000 - 0 - 0 - 0 - - - 4722 - 275000 - 0 - 0 - 0 - - - 4723 - 275000 - 0 - 0 - 0 - - - 4724 - 103000 - 0 - 0 - 0 - - - 4725 - 103000 - 0 - 0 - 0 - - - 4726 - 100000 - 0 - 0 - 0 - - - 4727 - 100000 - 0 - 0 - 0 - - - 4728 - 280000 - 0 - 0 - 0 - - - 4729 - 280000 - 0 - 0 - 0 - - - 4730 - 275000 - 0 - 0 - 0 - - - 4731 - 275000 - 0 - 0 - 0 - - - 4732 - 13000 - 0 - 0 - 0 - - - 4733 - 13000 - 0 - 0 - 0 - - - 4734 - 160000 - 0 - 0 - 0 - - - 4735 - 160000 - 0 - 0 - 0 - - - 4736 - 50000 - 0 - 0 - 0 - - - 4737 - 50000 - 0 - 0 - 0 - - - 4738 - 47000 - 0 - 0 - 0 - - - 4739 - 47000 - 0 - 0 - 0 - - - 4740 - 420 - 0 - 0 - 0 - - - 4741 - 1 - 0 - 0 - 0 - - - 4742 - 1 - 0 - 0 - 0 - - - 4743 - 1 - 0 - 0 - 0 - - - 4744 - 1 - 0 - 0 - 0 - - - 4745 - 103000 - 0 - 0 - 0 - - - 4746 - 103000 - 0 - 0 - 0 - - - 4747 - 160000 - 0 - 0 - 0 - - - 4748 - 160000 - 0 - 0 - 0 - - - 4749 - 280000 - 0 - 0 - 0 - - - 4750 - 280000 - 0 - 0 - 0 - - - 4751 - 275000 - 0 - 0 - 0 - - - 4752 - 275000 - 0 - 0 - 0 - - - 4753 - 103000 - 0 - 0 - 0 - - - 4754 - 103000 - 0 - 0 - 0 - - - 4755 - 160000 - 0 - 0 - 0 - - - 4756 - 160000 - 0 - 0 - 0 - - - 4757 - 280000 - 0 - 0 - 0 - - - 4758 - 280000 - 0 - 0 - 0 - - - 4759 - 275000 - 0 - 0 - 0 - - - 4760 - 275000 - 0 - 0 - 0 - - - 4761 - 1 - 0 - 0 - 0 - - - 4762 - 1 - 0 - 0 - 0 - - - 4763 - 1 - 0 - 0 - 0 - - - 4764 - 1 - 0 - 0 - 0 - - - 4765 - 1 - 0 - 0 - 0 - - - 4766 - 1 - 0 - 0 - 0 - - - 4767 - 1 - 0 - 0 - 0 - - - 4768 - 1 - 0 - 0 - 0 - - - 4769 - 1 - 0 - 0 - 0 - - - 4770 - 1 - 0 - 0 - 0 - - - 4771 - 1 - 0 - 0 - 0 - - - 4772 - 1 - 0 - 0 - 0 - - - 4773 - 5 - 0 - 0 - 0 - - - 4774 - 1 - 0 - 0 - 0 - - - 4775 - 1 - 0 - 0 - 0 - - - 4776 - 1 - 0 - 0 - 0 - - - 4777 - 1 - 0 - 0 - 0 - - - 4778 - 10 - 0 - 0 - 0 - - - 4779 - 1 - 0 - 0 - 0 - - - 4780 - 1 - 0 - 0 - 0 - - - 4781 - 1 - 0 - 0 - 0 - - - 4782 - 1 - 0 - 0 - 0 - - - 4783 - 20 - 0 - 0 - 0 - - - 4784 - 1 - 0 - 0 - 0 - - - 4785 - 1 - 0 - 0 - 0 - - - 4786 - 1 - 0 - 0 - 0 - - - 4787 - 1 - 0 - 0 - 0 - - - 4788 - 35 - 0 - 0 - 0 - - - 4789 - 1 - 0 - 0 - 0 - - - 4790 - 1 - 0 - 0 - 0 - - - 4791 - 1 - 0 - 0 - 0 - - - 4792 - 1 - 0 - 0 - 0 - - - 4793 - 50 - 0 - 0 - 0 - - - 4794 - 1 - 0 - 0 - 0 - - - 4795 - 1 - 0 - 0 - 0 - - - 4796 - 1 - 0 - 0 - 0 - - - 4797 - 1 - 0 - 0 - 0 - - - 4798 - 95 - 0 - 0 - 0 - - - 4799 - 1 - 0 - 0 - 0 - - - 4800 - 1 - 0 - 0 - 0 - - - 4801 - 1 - 0 - 0 - 0 - - - 4802 - 1 - 0 - 0 - 0 - - - 4803 - 450 - 0 - 0 - 0 - - - 4804 - 1 - 0 - 0 - 0 - - - 4805 - 1 - 0 - 0 - 0 - - - 4806 - 1 - 0 - 0 - 0 - - - 4807 - 1 - 0 - 0 - 0 - - - 4808 - 1 - 0 - 0 - 0 - - - 4809 - 1 - 0 - 0 - 0 - - - 4810 - 1 - 0 - 0 - 0 - - - 4811 - 1 - 0 - 0 - 0 - - - 4812 - 1 - 0 - 0 - 0 - - - 4813 - 1 - 0 - 0 - 0 - - - 4814 - 1 - 0 - 0 - 0 - - - 4815 - 1 - 0 - 0 - 0 - - - 4816 - 1 - 0 - 0 - 0 - - - 4817 - 1 - 0 - 0 - 0 - - - 4818 - 1 - 0 - 0 - 0 - - - 4819 - 7 - 0 - 0 - 0 - - - 4820 - 33 - 0 - 0 - 0 - - - 4821 - 12 - 0 - 0 - 0 - - - 4822 - 18 - 0 - 0 - 0 - - - 4823 - 45 - 0 - 0 - 0 - - - 4824 - 220 - 0 - 0 - 0 - - - 4825 - 90 - 0 - 0 - 0 - - - 4826 - 90 - 0 - 0 - 0 - - - 4827 - 180 - 0 - 0 - 0 - - - 4828 - 180 - 0 - 0 - 0 - - - 4829 - 1 - 0 - 0 - 0 - - - 4830 - 1 - 0 - 0 - 0 - - - 4831 - 1 - 0 - 0 - 0 - - - 4832 - 1 - 0 - 0 - 0 - - - 4833 - 1 - 0 - 0 - 0 - - - 4834 - 1 - 0 - 0 - 0 - - - 4835 - 1 - 0 - 0 - 0 - - - 4836 - 1 - 0 - 0 - 0 - - - 4837 - 1 - 0 - 0 - 0 - - - 4838 - 10 - 0 - 0 - 0 - - - 4839 - 1 - 0 - 0 - 0 - - - 4840 - 5 - 0 - 0 - 0 - - - 4841 - 5 - 0 - 0 - 0 - - - 4842 - 225 - 0 - 0 - 0 - - - 4843 - 225 - 0 - 0 - 0 - - - 4844 - 200 - 0 - 0 - 0 - - - 4845 - 200 - 0 - 0 - 0 - - - 4846 - 150 - 0 - 0 - 0 - - - 4847 - 150 - 0 - 0 - 0 - - - 4848 - 75 - 0 - 0 - 0 - - - 4849 - 75 - 0 - 0 - 0 - - - 4850 - 100 - 0 - 0 - 0 - - - 4851 - 100 - 0 - 0 - 0 - - - 4852 - 1 - 0 - 0 - 0 - - - 4853 - 1 - 0 - 0 - 0 - - - 4854 - 1 - 0 - 0 - 0 - - - 4855 - 1 - 0 - 0 - 0 - - - 4856 - 13000 - 0 - 0 - 0 - - - 4857 - 13000 - 0 - 0 - 0 - - - 4858 - 13000 - 0 - 0 - 0 - - - 4859 - 13000 - 0 - 0 - 0 - - - 4860 - 13000 - 0 - 0 - 0 - - - 4861 - 13000 - 0 - 0 - 0 - - - 4862 - 85000 - 0 - 0 - 0 - - - 4863 - 85000 - 0 - 0 - 0 - - - 4864 - 85000 - 0 - 0 - 0 - - - 4865 - 85000 - 0 - 0 - 0 - - - 4866 - 85000 - 0 - 0 - 0 - - - 4867 - 85000 - 0 - 0 - 0 - - - 4868 - 50000 - 0 - 0 - 0 - - - 4869 - 50000 - 0 - 0 - 0 - - - 4870 - 50000 - 0 - 0 - 0 - - - 4871 - 50000 - 0 - 0 - 0 - - - 4872 - 50000 - 0 - 0 - 0 - - - 4873 - 50000 - 0 - 0 - 0 - - - 4874 - 47000 - 0 - 0 - 0 - - - 4875 - 47000 - 0 - 0 - 0 - - - 4876 - 47000 - 0 - 0 - 0 - - - 4877 - 47000 - 0 - 0 - 0 - - - 4878 - 47000 - 0 - 0 - 0 - - - 4879 - 47000 - 0 - 0 - 0 - - - 4880 - 103000 - 0 - 0 - 0 - - - 4881 - 103000 - 0 - 0 - 0 - - - 4882 - 103000 - 0 - 0 - 0 - - - 4883 - 103000 - 0 - 0 - 0 - - - 4884 - 103000 - 0 - 0 - 0 - - - 4885 - 103000 - 0 - 0 - 0 - - - 4886 - 208000 - 0 - 0 - 0 - - - 4887 - 208000 - 0 - 0 - 0 - - - 4888 - 208000 - 0 - 0 - 0 - - - 4889 - 208000 - 0 - 0 - 0 - - - 4890 - 208000 - 0 - 0 - 0 - - - 4891 - 208000 - 0 - 0 - 0 - - - 4892 - 280000 - 0 - 0 - 0 - - - 4893 - 280000 - 0 - 0 - 0 - - - 4894 - 280000 - 0 - 0 - 0 - - - 4895 - 280000 - 0 - 0 - 0 - - - 4896 - 280000 - 0 - 0 - 0 - - - 4897 - 280000 - 0 - 0 - 0 - - - 4898 - 275000 - 0 - 0 - 0 - - - 4899 - 275000 - 0 - 0 - 0 - - - 4900 - 275000 - 0 - 0 - 0 - - - 4901 - 275000 - 0 - 0 - 0 - - - 4902 - 275000 - 0 - 0 - 0 - - - 4903 - 275000 - 0 - 0 - 0 - - - 4904 - 103000 - 0 - 0 - 0 - - - 4905 - 103000 - 0 - 0 - 0 - - - 4906 - 103000 - 0 - 0 - 0 - - - 4907 - 103000 - 0 - 0 - 0 - - - 4908 - 103000 - 0 - 0 - 0 - - - 4909 - 103000 - 0 - 0 - 0 - - - 4910 - 100000 - 0 - 0 - 0 - - - 4911 - 100000 - 0 - 0 - 0 - - - 4912 - 100000 - 0 - 0 - 0 - - - 4913 - 100000 - 0 - 0 - 0 - - - 4914 - 100000 - 0 - 0 - 0 - - - 4915 - 100000 - 0 - 0 - 0 - - - 4916 - 280000 - 0 - 0 - 0 - - - 4917 - 280000 - 0 - 0 - 0 - - - 4918 - 280000 - 0 - 0 - 0 - - - 4919 - 280000 - 0 - 0 - 0 - - - 4920 - 280000 - 0 - 0 - 0 - - - 4921 - 280000 - 0 - 0 - 0 - - - 4922 - 275000 - 0 - 0 - 0 - - - 4923 - 275000 - 0 - 0 - 0 - - - 4924 - 275000 - 0 - 0 - 0 - - - 4925 - 275000 - 0 - 0 - 0 - - - 4926 - 275000 - 0 - 0 - 0 - - - 4927 - 275000 - 0 - 0 - 0 - - - 4928 - 13000 - 0 - 0 - 0 - - - 4929 - 13000 - 0 - 0 - 0 - - - 4930 - 13000 - 0 - 0 - 0 - - - 4931 - 13000 - 0 - 0 - 0 - - - 4932 - 13000 - 0 - 0 - 0 - - - 4933 - 13000 - 0 - 0 - 0 - - - 4934 - 160000 - 0 - 0 - 0 - - - 4935 - 160000 - 0 - 0 - 0 - - - 4936 - 160000 - 0 - 0 - 0 - - - 4937 - 160000 - 0 - 0 - 0 - - - 4938 - 160000 - 0 - 0 - 0 - - - 4939 - 160000 - 0 - 0 - 0 - - - 4940 - 50000 - 0 - 0 - 0 - - - 4941 - 50000 - 0 - 0 - 0 - - - 4942 - 50000 - 0 - 0 - 0 - - - 4943 - 50000 - 0 - 0 - 0 - - - 4944 - 50000 - 0 - 0 - 0 - - - 4945 - 50000 - 0 - 0 - 0 - - - 4946 - 47000 - 0 - 0 - 0 - - - 4947 - 47000 - 0 - 0 - 0 - - - 4948 - 47000 - 0 - 0 - 0 - - - 4949 - 47000 - 0 - 0 - 0 - - - 4950 - 47000 - 0 - 0 - 0 - - - 4951 - 47000 - 0 - 0 - 0 - - - 4952 - 103000 - 0 - 0 - 0 - - - 4953 - 103000 - 0 - 0 - 0 - - - 4954 - 103000 - 0 - 0 - 0 - - - 4955 - 103000 - 0 - 0 - 0 - - - 4956 - 103000 - 0 - 0 - 0 - - - 4957 - 103000 - 0 - 0 - 0 - - - 4958 - 160000 - 0 - 0 - 0 - - - 4959 - 160000 - 0 - 0 - 0 - - - 4960 - 160000 - 0 - 0 - 0 - - - 4961 - 160000 - 0 - 0 - 0 - - - 4962 - 160000 - 0 - 0 - 0 - - - 4963 - 160000 - 0 - 0 - 0 - - - 4964 - 280000 - 0 - 0 - 0 - - - 4965 - 280000 - 0 - 0 - 0 - - - 4966 - 280000 - 0 - 0 - 0 - - - 4967 - 280000 - 0 - 0 - 0 - - - 4968 - 280000 - 0 - 0 - 0 - - - 4969 - 280000 - 0 - 0 - 0 - - - 4970 - 275000 - 0 - 0 - 0 - - - 4971 - 275000 - 0 - 0 - 0 - - - 4972 - 275000 - 0 - 0 - 0 - - - 4973 - 275000 - 0 - 0 - 0 - - - 4974 - 275000 - 0 - 0 - 0 - - - 4975 - 275000 - 0 - 0 - 0 - - - 4976 - 103000 - 0 - 0 - 0 - - - 4977 - 103000 - 0 - 0 - 0 - - - 4978 - 103000 - 0 - 0 - 0 - - - 4979 - 103000 - 0 - 0 - 0 - - - 4980 - 103000 - 0 - 0 - 0 - - - 4981 - 103000 - 0 - 0 - 0 - - - 4982 - 160000 - 0 - 0 - 0 - - - 4983 - 160000 - 0 - 0 - 0 - - - 4984 - 160000 - 0 - 0 - 0 - - - 4985 - 160000 - 0 - 0 - 0 - - - 4986 - 160000 - 0 - 0 - 0 - - - 4987 - 160000 - 0 - 0 - 0 - - - 4988 - 280000 - 0 - 0 - 0 - - - 4989 - 280000 - 0 - 0 - 0 - - - 4990 - 280000 - 0 - 0 - 0 - - - 4991 - 280000 - 0 - 0 - 0 - - - 4992 - 280000 - 0 - 0 - 0 - - - 4993 - 280000 - 0 - 0 - 0 - - - 4994 - 275000 - 0 - 0 - 0 - - - 4995 - 275000 - 0 - 0 - 0 - - - 4996 - 275000 - 0 - 0 - 0 - - - 4997 - 275000 - 0 - 0 - 0 - - - 4998 - 275000 - 0 - 0 - 0 - - - 4999 - 275000 - 0 - 0 - 0 - - - 5000 - 1 - 0 - 0 - 0 - - - 5001 - 15 - 0 - 0 - 0 - - - 5002 - 1 - 0 - 0 - 0 - - - 5003 - 15 - 0 - 0 - 0 - - - 5004 - 5 - 0 - 0 - 0 - - - 5005 - 15 - 0 - 0 - 0 - - - 5006 - 1 - 0 - 0 - 0 - - - 5007 - 15 - 0 - 0 - 0 - - - 5008 - 1 - 0 - 0 - 0 - - - 5009 - 1 - 0 - 0 - 0 - - - 5010 - 1 - 0 - 0 - 0 - - - 5011 - 1 - 0 - 0 - 0 - - - 5012 - 1 - 0 - 0 - 0 - - - 5013 - 600 - 0 - 0 - 0 - - - 5014 - 900 - 0 - 0 - 0 - - - 5015 - 900 - 0 - 0 - 0 - - - 5016 - 600 - 0 - 0 - 0 - - - 5017 - 600 - 0 - 0 - 0 - - - 5018 - 600 - 0 - 0 - 0 - - - 5019 - 600 - 0 - 0 - 0 - - - 5020 - 1 - 0 - 0 - 0 - - - 5021 - 1 - 0 - 0 - 0 - - - 5022 - 1 - 0 - 0 - 0 - - - 5023 - 1 - 0 - 0 - 0 - - - 5024 - 650 - 0 - 0 - 0 - - - 5025 - 650 - 0 - 0 - 0 - - - 5026 - 812 - 0 - 0 - 0 - - - 5027 - 812 - 0 - 0 - 0 - - - 5028 - 845 - 0 - 0 - 0 - - - 5029 - 845 - 0 - 0 - 0 - - - 5030 - 585 - 0 - 0 - 0 - - - 5031 - 585 - 0 - 0 - 0 - - - 5032 - 780 - 0 - 0 - 0 - - - 5033 - 780 - 0 - 0 - 0 - - - 5034 - 812 - 0 - 0 - 0 - - - 5035 - 812 - 0 - 0 - 0 - - - 5036 - 715 - 0 - 0 - 0 - - - 5037 - 715 - 0 - 0 - 0 - - - 5038 - 910 - 0 - 0 - 0 - - - 5039 - 910 - 0 - 0 - 0 - - - 5040 - 975 - 0 - 0 - 0 - - - 5041 - 975 - 0 - 0 - 0 - - - 5042 - 364 - 0 - 0 - 0 - - - 5043 - 364 - 0 - 0 - 0 - - - 5044 - 468 - 0 - 0 - 0 - - - 5045 - 468 - 0 - 0 - 0 - - - 5046 - 507 - 0 - 0 - 0 - - - 5047 - 507 - 0 - 0 - 0 - - - 5048 - 455 - 0 - 0 - 0 - - - 5049 - 455 - 0 - 0 - 0 - - - 5050 - 715 - 0 - 0 - 0 - - - 5051 - 715 - 0 - 0 - 0 - - - 5052 - 812 - 0 - 0 - 0 - - - 5053 - 812 - 0 - 0 - 0 - - - 5054 - 1 - 0 - 0 - 0 - - - 5055 - 1 - 0 - 0 - 0 - - - 5056 - 1 - 0 - 0 - 0 - - - 5057 - 1 - 0 - 0 - 0 - - - 5058 - 1 - 0 - 0 - 0 - - - 5059 - 1 - 0 - 0 - 0 - - - 5060 - 1 - 0 - 0 - 0 - - - 5061 - 1 - 0 - 0 - 0 - - - 5062 - 1 - 0 - 0 - 0 - - - 5063 - 1 - 0 - 0 - 0 - - - 5064 - 1 - 0 - 0 - 0 - - - 5065 - 1 - 0 - 0 - 0 - - - 5066 - 1 - 0 - 0 - 0 - - - 5067 - 1 - 0 - 0 - 0 - - - 5068 - 1 - 0 - 0 - 0 - - - 5069 - 1 - 0 - 0 - 0 - - - 5070 - 1 - 0 - 0 - 0 - - - 5071 - 1 - 0 - 0 - 0 - - - 5072 - 1 - 0 - 0 - 0 - - - 5073 - 1 - 0 - 0 - 0 - - - 5074 - 1 - 0 - 0 - 0 - - - 5075 - 1 - 0 - 0 - 0 - - - 5076 - 1 - 0 - 0 - 0 - - - 5077 - 1 - 0 - 0 - 0 - - - 5078 - 1 - 0 - 0 - 0 - - - 5079 - 1 - 0 - 0 - 0 - - - 5080 - 1 - 0 - 0 - 0 - - - 5081 - 1 - 0 - 0 - 0 - - - 5082 - 1 - 0 - 0 - 0 - - - 5083 - 1 - 0 - 0 - 0 - - - 5084 - 1 - 0 - 0 - 0 - - - 5085 - 1 - 0 - 0 - 0 - - - 5086 - 1 - 0 - 0 - 0 - - - 5087 - 1 - 0 - 0 - 0 - - - 5088 - 1 - 0 - 0 - 0 - - - 5089 - 1 - 0 - 0 - 0 - - - 5090 - 1 - 0 - 0 - 0 - - - 5091 - 1 - 0 - 0 - 0 - - - 5092 - 1 - 0 - 0 - 0 - - - 5093 - 1 - 0 - 0 - 0 - - - 5094 - 1 - 0 - 0 - 0 - - - 5095 - 1 - 0 - 0 - 0 - - - 5096 - 84 - 0 - 0 - 0 - - - 5097 - 98 - 0 - 0 - 0 - - - 5098 - 11 - 0 - 0 - 0 - - - 5099 - 11 - 0 - 0 - 0 - - - 5100 - 12 - 0 - 0 - 0 - - - 5101 - 4 - 0 - 0 - 0 - - - 5102 - 9 - 0 - 0 - 0 - - - 5103 - 25 - 0 - 0 - 0 - - - 5104 - 60 - 0 - 0 - 0 - - - 5105 - 133 - 0 - 0 - 0 - - - 5106 - 166 - 0 - 0 - 0 - - - 5107 - 1 - 0 - 0 - 0 - - - 5108 - 1 - 0 - 0 - 0 - - - 5109 - 1 - 0 - 0 - 0 - - - 5110 - 1 - 0 - 0 - 0 - - - 5111 - 1 - 0 - 0 - 0 - - - 5112 - 1 - 0 - 0 - 0 - - - 5113 - 1 - 0 - 0 - 0 - - - 5114 - 1 - 0 - 0 - 0 - - - 5115 - 1 - 0 - 0 - 0 - - - 5116 - 1 - 0 - 0 - 0 - - - 5117 - 1 - 0 - 0 - 0 - - - 5118 - 1 - 0 - 0 - 0 - - - 5119 - 1 - 0 - 0 - 0 - - - 5120 - 1 - 0 - 0 - 0 - - - 5121 - 1 - 0 - 0 - 0 - - - 5122 - 1 - 0 - 0 - 0 - - - 5123 - 1 - 0 - 0 - 0 - - - 5124 - 1 - 0 - 0 - 0 - - - 5125 - 1 - 0 - 0 - 0 - - - 5126 - 1 - 0 - 0 - 0 - - - 5127 - 1 - 0 - 0 - 0 - - - 5128 - 1 - 0 - 0 - 0 - - - 5129 - 1 - 0 - 0 - 0 - - - 5130 - 1 - 0 - 0 - 0 - - - 5131 - 1 - 0 - 0 - 0 - - - 5132 - 1 - 0 - 0 - 0 - - - 5133 - 1 - 0 - 0 - 0 - - - 5134 - 1 - 0 - 0 - 0 - - - 5135 - 1 - 0 - 0 - 0 - - - 5136 - 1 - 0 - 0 - 0 - - - 5137 - 1 - 0 - 0 - 0 - - - 5138 - 1 - 0 - 0 - 0 - - - 5139 - 1 - 0 - 0 - 0 - - - 5140 - 1 - 0 - 0 - 0 - - - 5141 - 1 - 0 - 0 - 0 - - - 5142 - 1 - 0 - 0 - 0 - - - 5143 - 1 - 0 - 0 - 0 - - - 5144 - 1 - 0 - 0 - 0 - - - 5145 - 1 - 0 - 0 - 0 - - - 5146 - 1 - 0 - 0 - 0 - - - 5147 - 1 - 0 - 0 - 0 - - - 5148 - 1 - 0 - 0 - 0 - - - 5149 - 1 - 0 - 0 - 0 - - - 5150 - 1 - 0 - 0 - 0 - - - 5151 - 1 - 0 - 0 - 0 - - - 5152 - 1 - 0 - 0 - 0 - - - 5153 - 1 - 0 - 0 - 0 - - - 5154 - 1 - 0 - 0 - 0 - - - 5155 - 1 - 0 - 0 - 0 - - - 5156 - 1 - 0 - 0 - 0 - - - 5157 - 1 - 0 - 0 - 0 - - - 5158 - 1 - 0 - 0 - 0 - - - 5159 - 1 - 0 - 0 - 0 - - - 5160 - 1 - 0 - 0 - 0 - - - 5161 - 1 - 0 - 0 - 0 - - - 5162 - 1 - 0 - 0 - 0 - - - 5163 - 1 - 0 - 0 - 0 - - - 5164 - 1 - 0 - 0 - 0 - - - 5165 - 1 - 0 - 0 - 0 - - - 5166 - 1 - 0 - 0 - 0 - - - 5167 - 1 - 0 - 0 - 0 - - - 5168 - 1 - 0 - 0 - 0 - - - 5169 - 1 - 0 - 0 - 0 - - - 5170 - 1 - 0 - 0 - 0 - - - 5171 - 1 - 0 - 0 - 0 - - - 5172 - 1 - 0 - 0 - 0 - - - 5173 - 1 - 0 - 0 - 0 - - - 5174 - 1 - 0 - 0 - 0 - - - 5175 - 1 - 0 - 0 - 0 - - - 5176 - 1 - 0 - 0 - 0 - - - 5177 - 1 - 0 - 0 - 0 - - - 5178 - 1 - 0 - 0 - 0 - - - 5179 - 1 - 0 - 0 - 0 - - - 5180 - 1 - 0 - 0 - 0 - - - 5181 - 1 - 0 - 0 - 0 - - - 5182 - 1 - 0 - 0 - 0 - - - 5183 - 1 - 0 - 0 - 0 - - - 5184 - 1 - 0 - 0 - 0 - - - 5185 - 1 - 0 - 0 - 0 - - - 5186 - 1 - 0 - 0 - 0 - - - 5187 - 1 - 0 - 0 - 0 - - - 5188 - 1 - 0 - 0 - 0 - - - 5189 - 1 - 0 - 0 - 0 - - - 5190 - 1 - 0 - 0 - 0 - - - 5191 - 1 - 0 - 0 - 0 - - - 5192 - 1 - 0 - 0 - 0 - - - 5193 - 1 - 0 - 0 - 0 - - - 5194 - 1 - 0 - 0 - 0 - - - 5195 - 1 - 0 - 0 - 0 - - - 5196 - 1 - 0 - 0 - 0 - - - 5197 - 1 - 0 - 0 - 0 - - - 5198 - 1 - 0 - 0 - 0 - - - 5199 - 1 - 0 - 0 - 0 - - - 5200 - 1 - 0 - 0 - 0 - - - 5201 - 1 - 0 - 0 - 0 - - - 5202 - 1 - 0 - 0 - 0 - - - 5203 - 1 - 0 - 0 - 0 - - - 5204 - 1 - 0 - 0 - 0 - - - 5205 - 1 - 0 - 0 - 0 - - - 5206 - 1 - 0 - 0 - 0 - - - 5207 - 1 - 0 - 0 - 0 - - - 5208 - 1 - 0 - 0 - 0 - - - 5209 - 1 - 0 - 0 - 0 - - - 5210 - 1 - 0 - 0 - 0 - - - 5211 - 1 - 0 - 0 - 0 - - - 5212 - 1 - 0 - 0 - 0 - - - 5213 - 1 - 0 - 0 - 0 - - - 5214 - 1 - 0 - 0 - 0 - - - 5215 - 1 - 0 - 0 - 0 - - - 5216 - 1 - 0 - 0 - 0 - - - 5217 - 1 - 0 - 0 - 0 - - - 5218 - 1 - 0 - 0 - 0 - - - 5219 - 1 - 0 - 0 - 0 - - - 5220 - 1 - 0 - 0 - 0 - - - 5221 - 1 - 0 - 0 - 0 - - - 5222 - 1 - 0 - 0 - 0 - - - 5223 - 1 - 0 - 0 - 0 - - - 5224 - 1 - 0 - 0 - 0 - - - 5225 - 1 - 0 - 0 - 0 - - - 5226 - 1 - 0 - 0 - 0 - - - 5227 - 1 - 0 - 0 - 0 - - - 5228 - 1 - 0 - 0 - 0 - - - 5229 - 1 - 0 - 0 - 0 - - - 5230 - 1 - 0 - 0 - 0 - - - 5231 - 1 - 0 - 0 - 0 - - - 5232 - 1 - 0 - 0 - 0 - - - 5233 - 1 - 0 - 0 - 0 - - - 5234 - 1 - 0 - 0 - 0 - - - 5235 - 1 - 0 - 0 - 0 - - - 5236 - 1 - 0 - 0 - 0 - - - 5237 - 1 - 0 - 0 - 0 - - - 5238 - 1 - 0 - 0 - 0 - - - 5239 - 1 - 0 - 0 - 0 - - - 5240 - 1 - 0 - 0 - 0 - - - 5241 - 1 - 0 - 0 - 0 - - - 5242 - 1 - 0 - 0 - 0 - - - 5243 - 1 - 0 - 0 - 0 - - - 5244 - 1 - 0 - 0 - 0 - - - 5245 - 1 - 0 - 0 - 0 - - - 5246 - 1 - 0 - 0 - 0 - - - 5247 - 1 - 0 - 0 - 0 - - - 5248 - 1 - 0 - 0 - 0 - - - 5249 - 1 - 0 - 0 - 0 - - - 5250 - 1 - 0 - 0 - 0 - - - 5251 - 1 - 0 - 0 - 0 - - - 5252 - 1 - 0 - 0 - 0 - - - 5253 - 1 - 0 - 0 - 0 - - - 5254 - 1 - 0 - 0 - 0 - - - 5255 - 1 - 0 - 0 - 0 - - - 5256 - 1 - 0 - 0 - 0 - - - 5257 - 1 - 0 - 0 - 0 - - - 5258 - 1 - 0 - 0 - 0 - - - 5259 - 1 - 0 - 0 - 0 - - - 5260 - 1 - 0 - 0 - 0 - - - 5261 - 1 - 0 - 0 - 0 - - - 5262 - 1 - 0 - 0 - 0 - - - 5263 - 1 - 0 - 0 - 0 - - - 5264 - 1 - 0 - 0 - 0 - - - 5265 - 1 - 0 - 0 - 0 - - - 5266 - 1 - 0 - 0 - 0 - - - 5267 - 1 - 0 - 0 - 0 - - - 5268 - 1 - 0 - 0 - 0 - - - 5269 - 1 - 0 - 0 - 0 - - - 5270 - 1 - 0 - 0 - 0 - - - 5271 - 1 - 0 - 0 - 0 - - - 5272 - 1 - 0 - 0 - 0 - - - 5273 - 1 - 0 - 0 - 0 - - - 5274 - 1 - 0 - 0 - 0 - - - 5275 - 1 - 0 - 0 - 0 - - - 5276 - 1 - 0 - 0 - 0 - - - 5277 - 1 - 0 - 0 - 0 - - - 5278 - 1 - 0 - 0 - 0 - - - 5279 - 1 - 0 - 0 - 0 - - - 5280 - 99 - 0 - 0 - 0 - - - 5281 - 177 - 0 - 0 - 0 - - - 5282 - 86 - 0 - 0 - 0 - - - 5283 - 13 - 0 - 0 - 0 - - - 5284 - 20 - 0 - 0 - 0 - - - 5285 - 31 - 0 - 0 - 0 - - - 5286 - 39 - 0 - 0 - 0 - - - 5287 - 74 - 0 - 0 - 0 - - - 5288 - 115 - 0 - 0 - 0 - - - 5289 - 254 - 0 - 0 - 0 - - - 5290 - 340 - 0 - 0 - 0 - - - 5291 - 4 - 0 - 0 - 0 - - - 5292 - 5 - 0 - 0 - 0 - - - 5293 - 7 - 0 - 0 - 0 - - - 5294 - 12 - 0 - 0 - 0 - - - 5295 - 19 - 0 - 0 - 0 - - - 5296 - 34 - 0 - 0 - 0 - - - 5297 - 64 - 0 - 0 - 0 - - - 5298 - 64 - 0 - 0 - 0 - - - 5299 - 64 - 0 - 0 - 0 - - - 5300 - 64 - 0 - 0 - 0 - - - 5301 - 64 - 0 - 0 - 0 - - - 5302 - 64 - 0 - 0 - 0 - - - 5303 - 64 - 0 - 0 - 0 - - - 5304 - 64 - 0 - 0 - 0 - - - 5305 - 38 - 0 - 0 - 0 - - - 5306 - 86 - 0 - 0 - 0 - - - 5307 - 52 - 0 - 0 - 0 - - - 5308 - 60 - 0 - 0 - 0 - - - 5309 - 210 - 0 - 0 - 0 - - - 5310 - 8 - 0 - 0 - 0 - - - 5311 - 14 - 0 - 0 - 0 - - - 5312 - 6 - 0 - 0 - 0 - - - 5313 - 16 - 0 - 0 - 0 - - - 5314 - 48 - 0 - 0 - 0 - - - 5315 - 143 - 0 - 0 - 0 - - - 5316 - 422 - 0 - 0 - 0 - - - 5317 - 1 - 0 - 0 - 0 - - - 5318 - 8 - 0 - 0 - 0 - - - 5319 - 10 - 0 - 0 - 0 - - - 5320 - 8 - 0 - 0 - 0 - - - 5321 - 56 - 0 - 0 - 0 - - - 5322 - 4 - 0 - 0 - 0 - - - 5323 - 18 - 0 - 0 - 0 - - - 5324 - 25 - 0 - 0 - 0 - - - 5325 - 12 - 0 - 0 - 0 - - - 5326 - 12 - 0 - 0 - 0 - - - 5327 - 1 - 0 - 0 - 0 - - - 5328 - 1 - 0 - 0 - 0 - - - 5329 - 5 - 0 - 0 - 0 - - - 5330 - 5 - 0 - 0 - 0 - - - 5331 - 8 - 0 - 0 - 0 - - - 5332 - 8 - 0 - 0 - 0 - - - 5333 - 1 - 0 - 0 - 0 - - - 5334 - 1 - 0 - 0 - 0 - - - 5335 - 1 - 0 - 0 - 0 - - - 5336 - 1 - 0 - 0 - 0 - - - 5337 - 1 - 0 - 0 - 0 - - - 5338 - 1 - 0 - 0 - 0 - - - 5339 - 1 - 0 - 0 - 0 - - - 5340 - 1 - 0 - 0 - 0 - - - 5341 - 6 - 0 - 0 - 0 - - - 5342 - 6 - 0 - 0 - 0 - - - 5343 - 6 - 0 - 0 - 0 - - - 5344 - 6 - 0 - 0 - 0 - - - 5345 - 25 - 0 - 0 - 0 - - - 5346 - 25 - 0 - 0 - 0 - - - 5347 - 1 - 0 - 0 - 0 - - - 5348 - 1 - 0 - 0 - 0 - - - 5349 - 1 - 0 - 0 - 0 - - - 5350 - 1 - 0 - 0 - 0 - - - 5351 - 1 - 0 - 0 - 0 - - - 5352 - 1 - 0 - 0 - 0 - - - 5353 - 1 - 0 - 0 - 0 - - - 5354 - 1 - 0 - 0 - 0 - - - 5355 - 1 - 0 - 0 - 0 - - - 5356 - 1 - 0 - 0 - 0 - - - 5357 - 1 - 0 - 0 - 0 - - - 5358 - 1 - 0 - 0 - 0 - - - 5359 - 1 - 0 - 0 - 0 - - - 5360 - 1 - 0 - 0 - 0 - - - 5361 - 1 - 0 - 0 - 0 - - - 5362 - 1 - 0 - 0 - 0 - - - 5363 - 1 - 0 - 0 - 0 - - - 5364 - 1 - 0 - 0 - 0 - - - 5365 - 1 - 0 - 0 - 0 - - - 5366 - 1 - 0 - 0 - 0 - - - 5367 - 1 - 0 - 0 - 0 - - - 5368 - 1 - 0 - 0 - 0 - - - 5369 - 1 - 0 - 0 - 0 - - - 5370 - 1 - 0 - 0 - 0 - - - 5371 - 1 - 0 - 0 - 0 - - - 5372 - 1 - 0 - 0 - 0 - - - 5373 - 1 - 0 - 0 - 0 - - - 5374 - 1 - 0 - 0 - 0 - - - 5375 - 1 - 0 - 0 - 0 - - - 5376 - 1 - 0 - 0 - 0 - - - 5377 - 1 - 0 - 0 - 0 - - - 5378 - 1 - 0 - 0 - 0 - - - 5379 - 1 - 0 - 0 - 0 - - - 5380 - 1 - 0 - 0 - 0 - - - 5381 - 1 - 0 - 0 - 0 - - - 5382 - 1 - 0 - 0 - 0 - - - 5383 - 1 - 0 - 0 - 0 - - - 5384 - 1 - 0 - 0 - 0 - - - 5385 - 1 - 0 - 0 - 0 - - - 5386 - 1 - 0 - 0 - 0 - - - 5387 - 1 - 0 - 0 - 0 - - - 5388 - 1 - 0 - 0 - 0 - - - 5389 - 1 - 0 - 0 - 0 - - - 5390 - 1 - 0 - 0 - 0 - - - 5391 - 1 - 0 - 0 - 0 - - - 5392 - 1 - 0 - 0 - 0 - - - 5393 - 1 - 0 - 0 - 0 - - - 5394 - 1 - 0 - 0 - 0 - - - 5395 - 1 - 0 - 0 - 0 - - - 5396 - 1 - 0 - 0 - 0 - - - 5397 - 1 - 0 - 0 - 0 - - - 5398 - 1 - 0 - 0 - 0 - - - 5399 - 1 - 0 - 0 - 0 - - - 5400 - 1 - 0 - 0 - 0 - - - 5401 - 1 - 0 - 0 - 0 - - - 5402 - 1 - 0 - 0 - 0 - - - 5403 - 1 - 0 - 0 - 0 - - - 5404 - 1 - 0 - 0 - 0 - - - 5405 - 1 - 0 - 0 - 0 - - - 5406 - 1 - 0 - 0 - 0 - - - 5407 - 1 - 0 - 0 - 0 - - - 5408 - 1 - 0 - 0 - 0 - - - 5409 - 1 - 0 - 0 - 0 - - - 5410 - 1 - 0 - 0 - 0 - - - 5411 - 1 - 0 - 0 - 0 - - - 5412 - 1 - 0 - 0 - 0 - - - 5413 - 1 - 0 - 0 - 0 - - - 5414 - 1 - 0 - 0 - 0 - - - 5415 - 1 - 0 - 0 - 0 - - - 5416 - 1 - 0 - 0 - 0 - - - 5417 - 1 - 0 - 0 - 0 - - - 5418 - 1 - 0 - 0 - 0 - - - 5419 - 1 - 0 - 0 - 0 - - - 5420 - 1 - 0 - 0 - 0 - - - 5421 - 1 - 0 - 0 - 0 - - - 5422 - 1 - 0 - 0 - 0 - - - 5423 - 1 - 0 - 0 - 0 - - - 5424 - 1 - 0 - 0 - 0 - - - 5425 - 1 - 0 - 0 - 0 - - - 5426 - 1 - 0 - 0 - 0 - - - 5427 - 1 - 0 - 0 - 0 - - - 5428 - 1 - 0 - 0 - 0 - - - 5429 - 1 - 0 - 0 - 0 - - - 5430 - 1 - 0 - 0 - 0 - - - 5431 - 1 - 0 - 0 - 0 - - - 5432 - 1 - 0 - 0 - 0 - - - 5433 - 1 - 0 - 0 - 0 - - - 5434 - 1 - 0 - 0 - 0 - - - 5435 - 1 - 0 - 0 - 0 - - - 5436 - 1 - 0 - 0 - 0 - - - 5437 - 1 - 0 - 0 - 0 - - - 5438 - 1 - 0 - 0 - 0 - - - 5439 - 1 - 0 - 0 - 0 - - - 5440 - 1 - 0 - 0 - 0 - - - 5441 - 1 - 0 - 0 - 0 - - - 5442 - 1 - 0 - 0 - 0 - - - 5443 - 1 - 0 - 0 - 0 - - - 5444 - 1 - 0 - 0 - 0 - - - 5445 - 1 - 0 - 0 - 0 - - - 5446 - 1 - 0 - 0 - 0 - - - 5447 - 1 - 0 - 0 - 0 - - - 5448 - 1 - 0 - 0 - 0 - - - 5449 - 1 - 0 - 0 - 0 - - - 5450 - 1 - 0 - 0 - 0 - - - 5451 - 1 - 0 - 0 - 0 - - - 5452 - 1 - 0 - 0 - 0 - - - 5453 - 1 - 0 - 0 - 0 - - - 5454 - 1 - 0 - 0 - 0 - - - 5455 - 1 - 0 - 0 - 0 - - - 5456 - 1 - 0 - 0 - 0 - - - 5457 - 1 - 0 - 0 - 0 - - - 5458 - 1 - 0 - 0 - 0 - - - 5459 - 1 - 0 - 0 - 0 - - - 5460 - 1 - 0 - 0 - 0 - - - 5461 - 1 - 0 - 0 - 0 - - - 5462 - 1 - 0 - 0 - 0 - - - 5463 - 1 - 0 - 0 - 0 - - - 5464 - 1 - 0 - 0 - 0 - - - 5465 - 1 - 0 - 0 - 0 - - - 5466 - 1 - 0 - 0 - 0 - - - 5467 - 1 - 0 - 0 - 0 - - - 5468 - 1 - 0 - 0 - 0 - - - 5469 - 1 - 0 - 0 - 0 - - - 5470 - 1 - 0 - 0 - 0 - - - 5471 - 1 - 0 - 0 - 0 - - - 5472 - 1 - 0 - 0 - 0 - - - 5473 - 1 - 0 - 0 - 0 - - - 5474 - 1 - 0 - 0 - 0 - - - 5475 - 1 - 0 - 0 - 0 - - - 5476 - 1 - 0 - 0 - 0 - - - 5477 - 1 - 0 - 0 - 0 - - - 5478 - 1 - 0 - 0 - 0 - - - 5479 - 1 - 0 - 0 - 0 - - - 5480 - 1 - 0 - 0 - 0 - - - 5481 - 1 - 0 - 0 - 0 - - - 5482 - 1 - 0 - 0 - 0 - - - 5483 - 1 - 0 - 0 - 0 - - - 5484 - 1 - 0 - 0 - 0 - - - 5485 - 1 - 0 - 0 - 0 - - - 5486 - 1 - 0 - 0 - 0 - - - 5487 - 1 - 0 - 0 - 0 - - - 5488 - 1 - 0 - 0 - 0 - - - 5489 - 1 - 0 - 0 - 0 - - - 5490 - 1 - 0 - 0 - 0 - - - 5491 - 1 - 0 - 0 - 0 - - - 5492 - 1 - 0 - 0 - 0 - - - 5493 - 1 - 0 - 0 - 0 - - - 5494 - 1 - 0 - 0 - 0 - - - 5495 - 1 - 0 - 0 - 0 - - - 5496 - 1 - 0 - 0 - 0 - - - 5497 - 1 - 0 - 0 - 0 - - - 5498 - 1 - 0 - 0 - 0 - - - 5499 - 1 - 0 - 0 - 0 - - - 5500 - 1 - 0 - 0 - 0 - - - 5501 - 1 - 0 - 0 - 0 - - - 5502 - 1 - 0 - 0 - 0 - - - 5503 - 1 - 0 - 0 - 0 - - - 5504 - 17 - 0 - 0 - 0 - - - 5505 - 17 - 0 - 0 - 0 - - - 5506 - 1 - 0 - 0 - 0 - - - 5507 - 1 - 0 - 0 - 0 - - - 5508 - 1 - 0 - 0 - 0 - - - 5509 - 150 - 0 - 0 - 0 - - - 5510 - 300 - 0 - 0 - 0 - - - 5511 - 300 - 0 - 0 - 0 - - - 5512 - 600 - 0 - 0 - 0 - - - 5513 - 600 - 0 - 0 - 0 - - - 5514 - 1200 - 0 - 0 - 0 - - - 5515 - 1200 - 0 - 0 - 0 - - - 5516 - 500 - 0 - 0 - 0 - - - 5517 - 500 - 0 - 0 - 0 - - - 5518 - 1 - 0 - 0 - 0 - - - 5519 - 1 - 0 - 0 - 0 - - - 5520 - 10 - 0 - 0 - 0 - - - 5521 - 1425 - 0 - 0 - 0 - - - 5522 - 1425 - 0 - 0 - 0 - - - 5523 - 100 - 0 - 0 - 0 - - - 5524 - 100 - 0 - 0 - 0 - - - 5525 - 100 - 0 - 0 - 0 - - - 5526 - 100 - 0 - 0 - 0 - - - 5527 - 100 - 0 - 0 - 0 - - - 5528 - 100 - 0 - 0 - 0 - - - 5529 - 100 - 0 - 0 - 0 - - - 5530 - 100 - 0 - 0 - 0 - - - 5531 - 100 - 0 - 0 - 0 - - - 5532 - 100 - 0 - 0 - 0 - - - 5533 - 100 - 0 - 0 - 0 - - - 5534 - 100 - 0 - 0 - 0 - - - 5535 - 100 - 0 - 0 - 0 - - - 5536 - 100 - 0 - 0 - 0 - - - 5537 - 100 - 0 - 0 - 0 - - - 5538 - 100 - 0 - 0 - 0 - - - 5539 - 100 - 0 - 0 - 0 - - - 5540 - 100 - 0 - 0 - 0 - - - 5541 - 100 - 0 - 0 - 0 - - - 5542 - 100 - 0 - 0 - 0 - - - 5543 - 100 - 0 - 0 - 0 - - - 5544 - 100 - 0 - 0 - 0 - - - 5545 - 100 - 0 - 0 - 0 - - - 5546 - 100 - 0 - 0 - 0 - - - 5547 - 100 - 0 - 0 - 0 - - - 5548 - 100 - 0 - 0 - 0 - - - 5549 - 100 - 0 - 0 - 0 - - - 5550 - 100 - 0 - 0 - 0 - - - 5551 - 100 - 0 - 0 - 0 - - - 5552 - 100 - 0 - 0 - 0 - - - 5553 - 625 - 0 - 0 - 0 - - - 5554 - 375 - 0 - 0 - 0 - - - 5555 - 500 - 0 - 0 - 0 - - - 5556 - 250 - 0 - 0 - 0 - - - 5557 - 250 - 0 - 0 - 0 - - - 5558 - 50 - 0 - 0 - 0 - - - 5559 - 1 - 0 - 0 - 0 - - - 5560 - 10 - 0 - 0 - 0 - - - 5561 - 1 - 0 - 0 - 0 - - - 5562 - 1 - 0 - 0 - 0 - - - 5563 - 1 - 0 - 0 - 0 - - - 5564 - 1 - 0 - 0 - 0 - - - 5565 - 1 - 0 - 0 - 0 - - - 5566 - 1 - 0 - 0 - 0 - - - 5567 - 1 - 0 - 0 - 0 - - - 5568 - 1 - 0 - 0 - 0 - - - 5569 - 1 - 0 - 0 - 0 - - - 5570 - 1 - 0 - 0 - 0 - - - 5571 - 1 - 0 - 0 - 0 - - - 5572 - 1 - 0 - 0 - 0 - - - 5573 - 1 - 0 - 0 - 0 - - - 5574 - 6000 - 0 - 0 - 0 - - - 5575 - 10000 - 0 - 0 - 0 - - - 5576 - 8000 - 0 - 0 - 0 - - - 5577 - 1 - 0 - 0 - 0 - - - 5578 - 1 - 0 - 0 - 0 - - - 5579 - 1 - 0 - 0 - 0 - - - 5580 - 1 - 0 - 0 - 0 - - - 5581 - 1 - 0 - 0 - 0 - - - 5582 - 1 - 0 - 0 - 0 - - - 5583 - 1 - 0 - 0 - 0 - - - 5584 - 1 - 0 - 0 - 0 - - - 5585 - 1 - 0 - 0 - 0 - - - 5586 - 1 - 0 - 0 - 0 - - - 5587 - 1 - 0 - 0 - 0 - - - 5588 - 1 - 0 - 0 - 0 - - - 5589 - 1 - 0 - 0 - 0 - - - 5590 - 1 - 0 - 0 - 0 - - - 5591 - 1 - 0 - 0 - 0 - - - 5592 - 10 - 0 - 0 - 0 - - - 5593 - 20 - 0 - 0 - 0 - - - 5594 - 20 - 0 - 0 - 0 - - - 5595 - 20 - 0 - 0 - 0 - - - 5596 - 20 - 0 - 0 - 0 - - - 5597 - 20 - 0 - 0 - 0 - - - 5598 - 20 - 0 - 0 - 0 - - - 5599 - 20 - 0 - 0 - 0 - - - 5600 - 20 - 0 - 0 - 0 - - - 5601 - 1 - 0 - 0 - 0 - - - 5602 - 20 - 0 - 0 - 0 - - - 5603 - 1 - 0 - 0 - 0 - - - 5604 - 3 - 0 - 0 - 0 - - - 5605 - 6 - 0 - 0 - 0 - - - 5606 - 1 - 0 - 0 - 0 - - - 5607 - 1 - 0 - 0 - 0 - - - 5608 - 1 - 0 - 0 - 0 - - - 5609 - 1 - 0 - 0 - 0 - - - 5610 - 1 - 0 - 0 - 0 - - - 5611 - 6000 - 0 - 0 - 0 - - - 5612 - 10000 - 0 - 0 - 0 - - - 5613 - 8000 - 0 - 0 - 0 - - - 5614 - 1 - 0 - 0 - 0 - - - 5615 - 1 - 0 - 0 - 0 - - - 5616 - 1 - 0 - 0 - 0 - - - 5617 - 3 - 0 - 0 - 0 - - - 5618 - 12 - 0 - 0 - 0 - - - 5619 - 32 - 0 - 0 - 0 - - - 5620 - 80 - 0 - 0 - 0 - - - 5621 - 400 - 0 - 0 - 0 - - - 5622 - 1 - 0 - 0 - 0 - - - 5623 - 3 - 0 - 0 - 0 - - - 5624 - 12 - 0 - 0 - 0 - - - 5625 - 32 - 0 - 0 - 0 - - - 5626 - 80 - 0 - 0 - 0 - - - 5627 - 400 - 0 - 0 - 0 - - - 5628 - 1 - 0 - 0 - 0 - - - 5629 - 2 - 0 - 0 - 0 - - - 5630 - 10 - 0 - 0 - 0 - - - 5631 - 18 - 0 - 0 - 0 - - - 5632 - 25 - 0 - 0 - 0 - - - 5633 - 65 - 0 - 0 - 0 - - - 5634 - 350 - 0 - 0 - 0 - - - 5635 - 1 - 0 - 0 - 0 - - - 5636 - 2 - 0 - 0 - 0 - - - 5637 - 10 - 0 - 0 - 0 - - - 5638 - 18 - 0 - 0 - 0 - - - 5639 - 25 - 0 - 0 - 0 - - - 5640 - 65 - 0 - 0 - 0 - - - 5641 - 350 - 0 - 0 - 0 - - - 5642 - 4 - 0 - 0 - 0 - - - 5643 - 6 - 0 - 0 - 0 - - - 5644 - 24 - 0 - 0 - 0 - - - 5645 - 64 - 0 - 0 - 0 - - - 5646 - 160 - 0 - 0 - 0 - - - 5647 - 400 - 0 - 0 - 0 - - - 5648 - 4 - 0 - 0 - 0 - - - 5649 - 6 - 0 - 0 - 0 - - - 5650 - 24 - 0 - 0 - 0 - - - 5651 - 64 - 0 - 0 - 0 - - - 5652 - 160 - 0 - 0 - 0 - - - 5653 - 400 - 0 - 0 - 0 - - - 5654 - 1 - 0 - 0 - 0 - - - 5655 - 3 - 0 - 0 - 0 - - - 5656 - 10 - 0 - 0 - 0 - - - 5657 - 27 - 0 - 0 - 0 - - - 5658 - 18 - 0 - 0 - 0 - - - 5659 - 66 - 0 - 0 - 0 - - - 5660 - 166 - 0 - 0 - 0 - - - 5661 - 1 - 0 - 0 - 0 - - - 5662 - 3 - 0 - 0 - 0 - - - 5663 - 10 - 0 - 0 - 0 - - - 5664 - 27 - 0 - 0 - 0 - - - 5665 - 18 - 0 - 0 - 0 - - - 5666 - 66 - 0 - 0 - 0 - - - 5667 - 166 - 0 - 0 - 0 - - - 5668 - 35 - 0 - 0 - 0 - - - 5669 - 35 - 0 - 0 - 0 - - - 5670 - 10 - 0 - 0 - 0 - - - 5671 - 10 - 0 - 0 - 0 - - - 5672 - 125 - 0 - 0 - 0 - - - 5673 - 125 - 0 - 0 - 0 - - - 5674 - 325 - 0 - 0 - 0 - - - 5675 - 325 - 0 - 0 - 0 - - - 5676 - 800 - 0 - 0 - 0 - - - 5677 - 800 - 0 - 0 - 0 - - - 5678 - 8000 - 0 - 0 - 0 - - - 5679 - 8000 - 0 - 0 - 0 - - - 5680 - 24000 - 0 - 0 - 0 - - - 5681 - 24000 - 0 - 0 - 0 - - - 5682 - 240 - 0 - 0 - 0 - - - 5683 - 240 - 0 - 0 - 0 - - - 5684 - 565 - 0 - 0 - 0 - - - 5685 - 565 - 0 - 0 - 0 - - - 5686 - 35 - 0 - 0 - 0 - - - 5687 - 35 - 0 - 0 - 0 - - - 5688 - 10 - 0 - 0 - 0 - - - 5689 - 10 - 0 - 0 - 0 - - - 5690 - 125 - 0 - 0 - 0 - - - 5691 - 125 - 0 - 0 - 0 - - - 5692 - 325 - 0 - 0 - 0 - - - 5693 - 325 - 0 - 0 - 0 - - - 5694 - 800 - 0 - 0 - 0 - - - 5695 - 800 - 0 - 0 - 0 - - - 5696 - 8000 - 0 - 0 - 0 - - - 5697 - 8000 - 0 - 0 - 0 - - - 5698 - 24000 - 0 - 0 - 0 - - - 5699 - 24000 - 0 - 0 - 0 - - - 5700 - 240 - 0 - 0 - 0 - - - 5701 - 240 - 0 - 0 - 0 - - - 5702 - 565 - 0 - 0 - 0 - - - 5703 - 565 - 0 - 0 - 0 - - - 5704 - 26 - 0 - 0 - 0 - - - 5705 - 26 - 0 - 0 - 0 - - - 5706 - 91 - 0 - 0 - 0 - - - 5707 - 91 - 0 - 0 - 0 - - - 5708 - 325 - 0 - 0 - 0 - - - 5709 - 325 - 0 - 0 - 0 - - - 5710 - 845 - 0 - 0 - 0 - - - 5711 - 845 - 0 - 0 - 0 - - - 5712 - 2080 - 0 - 0 - 0 - - - 5713 - 2080 - 0 - 0 - 0 - - - 5714 - 20800 - 0 - 0 - 0 - - - 5715 - 20800 - 0 - 0 - 0 - - - 5716 - 62400 - 0 - 0 - 0 - - - 5717 - 62400 - 0 - 0 - 0 - - - 5718 - 26 - 0 - 0 - 0 - - - 5719 - 26 - 0 - 0 - 0 - - - 5720 - 91 - 0 - 0 - 0 - - - 5721 - 91 - 0 - 0 - 0 - - - 5722 - 325 - 0 - 0 - 0 - - - 5723 - 325 - 0 - 0 - 0 - - - 5724 - 845 - 0 - 0 - 0 - - - 5725 - 845 - 0 - 0 - 0 - - - 5726 - 2080 - 0 - 0 - 0 - - - 5727 - 2080 - 0 - 0 - 0 - - - 5728 - 20800 - 0 - 0 - 0 - - - 5729 - 20800 - 0 - 0 - 0 - - - 5730 - 62400 - 0 - 0 - 0 - - - 5731 - 62400 - 0 - 0 - 0 - - - 5732 - 1 - 0 - 0 - 0 - - - 5733 - 1 - 0 - 0 - 0 - - - 5734 - 750 - 0 - 0 - 0 - - - 5735 - 750 - 0 - 0 - 0 - - - 5736 - 750 - 0 - 0 - 0 - - - 5737 - 750 - 0 - 0 - 0 - - - 5738 - 1 - 0 - 0 - 0 - - - 5739 - 2 - 0 - 0 - 0 - - - 5740 - 2 - 0 - 0 - 0 - - - 5741 - 2 - 0 - 0 - 0 - - - 5742 - 2 - 0 - 0 - 0 - - - 5743 - 2 - 0 - 0 - 0 - - - 5744 - 2 - 0 - 0 - 0 - - - 5745 - 2 - 0 - 0 - 0 - - - 5746 - 2 - 0 - 0 - 0 - - - 5747 - 2 - 0 - 0 - 0 - - - 5748 - 2 - 0 - 0 - 0 - - - 5749 - 5 - 0 - 0 - 0 - - - 5750 - 5 - 0 - 0 - 0 - - - 5751 - 2 - 0 - 0 - 0 - - - 5752 - 2 - 0 - 0 - 0 - - - 5753 - 2 - 0 - 0 - 0 - - - 5754 - 2 - 0 - 0 - 0 - - - 5755 - 2 - 0 - 0 - 0 - - - 5756 - 2 - 0 - 0 - 0 - - - 5757 - 2 - 0 - 0 - 0 - - - 5758 - 2 - 0 - 0 - 0 - - - 5759 - 2 - 0 - 0 - 0 - - - 5760 - 2 - 0 - 0 - 0 - - - 5761 - 2 - 0 - 0 - 0 - - - 5762 - 2 - 0 - 0 - 0 - - - 5763 - 2 - 0 - 0 - 0 - - - 5764 - 2 - 0 - 0 - 0 - - - 5765 - 2 - 0 - 0 - 0 - - - 5766 - 2 - 0 - 0 - 0 - - - 5767 - 1 - 0 - 0 - 0 - - - 5768 - 1 - 0 - 0 - 0 - - - 5769 - 1 - 0 - 0 - 0 - - - 5770 - 1 - 0 - 0 - 0 - - - 5771 - 1 - 0 - 0 - 0 - - - 5772 - 1 - 0 - 0 - 0 - - - 5773 - 1 - 0 - 0 - 0 - - - 5774 - 1 - 0 - 0 - 0 - - - 5775 - 1 - 0 - 0 - 0 - - - 5776 - 1 - 0 - 0 - 0 - - - 5777 - 1 - 0 - 0 - 0 - - - 5778 - 1 - 0 - 0 - 0 - - - 5779 - 1 - 0 - 0 - 0 - - - 5780 - 1 - 0 - 0 - 0 - - - 5781 - 1 - 0 - 0 - 0 - - - 5782 - 1 - 0 - 0 - 0 - - - 5783 - 1 - 0 - 0 - 0 - - - 5784 - 1 - 0 - 0 - 0 - - - 5785 - 1 - 0 - 0 - 0 - - - 5786 - 1 - 0 - 0 - 0 - - - 5787 - 1 - 0 - 0 - 0 - - - 5788 - 1 - 0 - 0 - 0 - - - 5789 - 1 - 0 - 0 - 0 - - - 5790 - 1 - 0 - 0 - 0 - - - 5791 - 1 - 0 - 0 - 0 - - - 5792 - 1 - 0 - 0 - 0 - - - 5793 - 1 - 0 - 0 - 0 - - - 5794 - 1 - 0 - 0 - 0 - - - 5795 - 1 - 0 - 0 - 0 - - - 5796 - 1 - 0 - 0 - 0 - - - 5797 - 1 - 0 - 0 - 0 - - - 5798 - 1 - 0 - 0 - 0 - - - 5799 - 1 - 0 - 0 - 0 - - - 5800 - 1 - 0 - 0 - 0 - - - 5801 - 1 - 0 - 0 - 0 - - - 5802 - 1 - 0 - 0 - 0 - - - 5803 - 1 - 0 - 0 - 0 - - - 5804 - 1 - 0 - 0 - 0 - - - 5805 - 1 - 0 - 0 - 0 - - - 5806 - 1 - 0 - 0 - 0 - - - 5807 - 1 - 0 - 0 - 0 - - - 5808 - 1 - 0 - 0 - 0 - - - 5809 - 1 - 0 - 0 - 0 - - - 5810 - 1 - 0 - 0 - 0 - - - 5811 - 1 - 0 - 0 - 0 - - - 5812 - 1 - 0 - 0 - 0 - - - 5813 - 1 - 0 - 0 - 0 - - - 5814 - 1 - 0 - 0 - 0 - - - 5815 - 1 - 0 - 0 - 0 - - - 5816 - 1 - 0 - 0 - 0 - - - 5817 - 1 - 0 - 0 - 0 - - - 5818 - 1 - 0 - 0 - 0 - - - 5819 - 1 - 0 - 0 - 0 - - - 5820 - 1 - 0 - 0 - 0 - - - 5821 - 1 - 0 - 0 - 0 - - - 5822 - 1 - 0 - 0 - 0 - - - 5823 - 1 - 0 - 0 - 0 - - - 5824 - 1 - 0 - 0 - 0 - - - 5825 - 1 - 0 - 0 - 0 - - - 5826 - 1 - 0 - 0 - 0 - - - 5827 - 1 - 0 - 0 - 0 - - - 5828 - 1 - 0 - 0 - 0 - - - 5829 - 1 - 0 - 0 - 0 - - - 5830 - 1 - 0 - 0 - 0 - - - 5831 - 1 - 0 - 0 - 0 - - - 5832 - 1 - 0 - 0 - 0 - - - 5833 - 1 - 0 - 0 - 0 - - - 5834 - 1 - 0 - 0 - 0 - - - 5835 - 1 - 0 - 0 - 0 - - - 5836 - 1 - 0 - 0 - 0 - - - 5837 - 1 - 0 - 0 - 0 - - - 5838 - 1 - 0 - 0 - 0 - - - 5839 - 1 - 0 - 0 - 0 - - - 5840 - 1 - 0 - 0 - 0 - - - 5841 - 1 - 0 - 0 - 0 - - - 5842 - 1 - 0 - 0 - 0 - - - 5843 - 1 - 0 - 0 - 0 - - - 5844 - 1 - 0 - 0 - 0 - - - 5845 - 1 - 0 - 0 - 0 - - - 5846 - 1 - 0 - 0 - 0 - - - 5847 - 1 - 0 - 0 - 0 - - - 5848 - 1 - 0 - 0 - 0 - - - 5849 - 1 - 0 - 0 - 0 - - - 5850 - 1 - 0 - 0 - 0 - - - 5851 - 1 - 0 - 0 - 0 - - - 5852 - 1 - 0 - 0 - 0 - - - 5853 - 1 - 0 - 0 - 0 - - - 5854 - 1 - 0 - 0 - 0 - - - 5855 - 1 - 0 - 0 - 0 - - - 5856 - 1 - 0 - 0 - 0 - - - 5857 - 1 - 0 - 0 - 0 - - - 5858 - 1 - 0 - 0 - 0 - - - 5859 - 1 - 0 - 0 - 0 - - - 5860 - 1 - 0 - 0 - 0 - - - 5861 - 1 - 0 - 0 - 0 - - - 5862 - 1 - 0 - 0 - 0 - - - 5863 - 1 - 0 - 0 - 0 - - - 5864 - 1 - 0 - 0 - 0 - - - 5865 - 1 - 0 - 0 - 0 - - - 5866 - 1 - 0 - 0 - 0 - - - 5867 - 1 - 0 - 0 - 0 - - - 5868 - 1 - 0 - 0 - 0 - - - 5869 - 1 - 0 - 0 - 0 - - - 5870 - 1 - 0 - 0 - 0 - - - 5871 - 1 - 0 - 0 - 0 - - - 5872 - 1 - 0 - 0 - 0 - - - 5873 - 1 - 0 - 0 - 0 - - - 5874 - 1 - 0 - 0 - 0 - - - 5875 - 1 - 0 - 0 - 0 - - - 5876 - 1 - 0 - 0 - 0 - - - 5877 - 1 - 0 - 0 - 0 - - - 5878 - 1 - 0 - 0 - 0 - - - 5879 - 1 - 0 - 0 - 0 - - - 5880 - 1 - 0 - 0 - 0 - - - 5881 - 1 - 0 - 0 - 0 - - - 5882 - 1 - 0 - 0 - 0 - - - 5883 - 1 - 0 - 0 - 0 - - - 5884 - 1 - 0 - 0 - 0 - - - 5885 - 1 - 0 - 0 - 0 - - - 5886 - 1 - 0 - 0 - 0 - - - 5887 - 1 - 0 - 0 - 0 - - - 5888 - 1 - 0 - 0 - 0 - - - 5889 - 1 - 0 - 0 - 0 - - - 5890 - 1 - 0 - 0 - 0 - - - 5891 - 1 - 0 - 0 - 0 - - - 5892 - 1 - 0 - 0 - 0 - - - 5893 - 1 - 0 - 0 - 0 - - - 5894 - 1 - 0 - 0 - 0 - - - 5895 - 1 - 0 - 0 - 0 - - - 5896 - 1 - 0 - 0 - 0 - - - 5897 - 1 - 0 - 0 - 0 - - - 5898 - 1 - 0 - 0 - 0 - - - 5899 - 1 - 0 - 0 - 0 - - - 5900 - 1 - 0 - 0 - 0 - - - 5901 - 1 - 0 - 0 - 0 - - - 5902 - 1 - 0 - 0 - 0 - - - 5903 - 1 - 0 - 0 - 0 - - - 5904 - 1 - 0 - 0 - 0 - - - 5905 - 1 - 0 - 0 - 0 - - - 5906 - 1 - 0 - 0 - 0 - - - 5907 - 1 - 0 - 0 - 0 - - - 5908 - 1 - 0 - 0 - 0 - - - 5909 - 1 - 0 - 0 - 0 - - - 5910 - 1 - 0 - 0 - 0 - - - 5911 - 1 - 0 - 0 - 0 - - - 5912 - 1 - 0 - 0 - 0 - - - 5913 - 1 - 0 - 0 - 0 - - - 5914 - 1 - 0 - 0 - 0 - - - 5915 - 1 - 0 - 0 - 0 - - - 5916 - 1 - 0 - 0 - 0 - - - 5917 - 1 - 0 - 0 - 0 - - - 5918 - 1 - 0 - 0 - 0 - - - 5919 - 1 - 0 - 0 - 0 - - - 5920 - 1 - 0 - 0 - 0 - - - 5921 - 1 - 0 - 0 - 0 - - - 5922 - 1 - 0 - 0 - 0 - - - 5923 - 1 - 0 - 0 - 0 - - - 5924 - 1 - 0 - 0 - 0 - - - 5925 - 1 - 0 - 0 - 0 - - - 5926 - 1 - 0 - 0 - 0 - - - 5927 - 1 - 0 - 0 - 0 - - - 5928 - 1 - 0 - 0 - 0 - - - 5929 - 1 - 0 - 0 - 0 - - - 5930 - 1 - 0 - 0 - 0 - - - 5931 - 6 - 0 - 0 - 0 - - - 5932 - 6 - 0 - 0 - 0 - - - 5933 - 2 - 0 - 0 - 0 - - - 5934 - 2 - 0 - 0 - 0 - - - 5935 - 1 - 0 - 0 - 0 - - - 5936 - 1 - 0 - 0 - 0 - - - 5937 - 288 - 0 - 0 - 0 - - - 5938 - 288 - 0 - 0 - 0 - - - 5939 - 14 - 0 - 0 - 0 - - - 5940 - 432 - 0 - 0 - 0 - - - 5941 - 432 - 0 - 0 - 0 - - - 5942 - 14 - 0 - 0 - 0 - - - 5943 - 360 - 0 - 0 - 0 - - - 5944 - 360 - 0 - 0 - 0 - - - 5945 - 288 - 0 - 0 - 0 - - - 5946 - 288 - 0 - 0 - 0 - - - 5947 - 216 - 0 - 0 - 0 - - - 5948 - 216 - 0 - 0 - 0 - - - 5949 - 144 - 0 - 0 - 0 - - - 5950 - 144 - 0 - 0 - 0 - - - 5951 - 14 - 0 - 0 - 0 - - - 5952 - 360 - 0 - 0 - 0 - - - 5953 - 360 - 0 - 0 - 0 - - - 5954 - 288 - 0 - 0 - 0 - - - 5955 - 288 - 0 - 0 - 0 - - - 5956 - 216 - 0 - 0 - 0 - - - 5957 - 216 - 0 - 0 - 0 - - - 5958 - 144 - 0 - 0 - 0 - - - 5959 - 144 - 0 - 0 - 0 - - - 5960 - 1 - 0 - 0 - 0 - - - 5961 - 1 - 0 - 0 - 0 - - - 5962 - 1 - 0 - 0 - 0 - - - 5963 - 1 - 0 - 0 - 0 - - - 5964 - 1 - 0 - 0 - 0 - - - 5965 - 1 - 0 - 0 - 0 - - - 5966 - 1 - 0 - 0 - 0 - - - 5967 - 1 - 0 - 0 - 0 - - - 5968 - 1 - 0 - 0 - 0 - - - 5969 - 1 - 0 - 0 - 0 - - - 5970 - 19 - 0 - 0 - 0 - - - 5971 - 19 - 0 - 0 - 0 - - - 5972 - 64 - 0 - 0 - 0 - - - 5973 - 64 - 0 - 0 - 0 - - - 5974 - 87 - 0 - 0 - 0 - - - 5975 - 87 - 0 - 0 - 0 - - - 5976 - 87 - 0 - 0 - 0 - - - 5977 - 87 - 0 - 0 - 0 - - - 5978 - 1 - 0 - 0 - 0 - - - 5979 - 1 - 0 - 0 - 0 - - - 5980 - 54 - 0 - 0 - 0 - - - 5981 - 54 - 0 - 0 - 0 - - - 5982 - 48 - 0 - 0 - 0 - - - 5983 - 48 - 0 - 0 - 0 - - - 5984 - 16 - 0 - 0 - 0 - - - 5985 - 16 - 0 - 0 - 0 - - - 5986 - 9 - 0 - 0 - 0 - - - 5987 - 9 - 0 - 0 - 0 - - - 5988 - 9 - 0 - 0 - 0 - - - 5989 - 9 - 0 - 0 - 0 - - - 5990 - 1 - 0 - 0 - 0 - - - 5991 - 1 - 0 - 0 - 0 - - - 5992 - 13 - 0 - 0 - 0 - - - 5993 - 13 - 0 - 0 - 0 - - - 5994 - 4 - 0 - 0 - 0 - - - 5995 - 4 - 0 - 0 - 0 - - - 5996 - 5 - 0 - 0 - 0 - - - 5997 - 5 - 0 - 0 - 0 - - - 5998 - 7 - 0 - 0 - 0 - - - 5999 - 7 - 0 - 0 - 0 - - - 6000 - 10 - 0 - 0 - 0 - - - 6001 - 10 - 0 - 0 - 0 - - - 6002 - 15 - 0 - 0 - 0 - - - 6003 - 15 - 0 - 0 - 0 - - - 6004 - 38 - 0 - 0 - 0 - - - 6005 - 38 - 0 - 0 - 0 - - - 6006 - 4 - 0 - 0 - 0 - - - 6007 - 4 - 0 - 0 - 0 - - - 6008 - 4 - 0 - 0 - 0 - - - 6009 - 4 - 0 - 0 - 0 - - - 6010 - 1 - 0 - 0 - 0 - - - 6011 - 1 - 0 - 0 - 0 - - - 6012 - 4 - 0 - 0 - 0 - - - 6013 - 4 - 0 - 0 - 0 - - - 6014 - 2 - 0 - 0 - 0 - - - 6015 - 2 - 0 - 0 - 0 - - - 6016 - 1 - 0 - 0 - 0 - - - 6017 - 1 - 0 - 0 - 0 - - - 6018 - 65 - 0 - 0 - 0 - - - 6019 - 65 - 0 - 0 - 0 - - - 6020 - 1 - 0 - 0 - 0 - - - 6021 - 1 - 0 - 0 - 0 - - - 6022 - 1 - 0 - 0 - 0 - - - 6023 - 1 - 0 - 0 - 0 - - - 6024 - 1 - 0 - 0 - 0 - - - 6025 - 1 - 0 - 0 - 0 - - - 6026 - 1 - 0 - 0 - 0 - - - 6027 - 1 - 0 - 0 - 0 - - - 6028 - 1 - 0 - 0 - 0 - - - 6029 - 1 - 0 - 0 - 0 - - - 6030 - 1 - 0 - 0 - 0 - - - 6031 - 1 - 0 - 0 - 0 - - - 6032 - 20 - 0 - 0 - 0 - - - 6033 - 20 - 0 - 0 - 0 - - - 6034 - 85 - 0 - 0 - 0 - - - 6035 - 85 - 0 - 0 - 0 - - - 6036 - 40 - 0 - 0 - 0 - - - 6037 - 40 - 0 - 0 - 0 - - - 6038 - 1 - 0 - 0 - 0 - - - 6039 - 1 - 0 - 0 - 0 - - - 6040 - 1 - 0 - 0 - 0 - - - 6041 - 1 - 0 - 0 - 0 - - - 6042 - 1 - 0 - 0 - 0 - - - 6043 - 1 - 0 - 0 - 0 - - - 6044 - 1 - 0 - 0 - 0 - - - 6045 - 1 - 0 - 0 - 0 - - - 6046 - 1 - 0 - 0 - 0 - - - 6047 - 1 - 0 - 0 - 0 - - - 6048 - 1 - 0 - 0 - 0 - - - 6049 - 1 - 0 - 0 - 0 - - - 6050 - 1 - 0 - 0 - 0 - - - 6051 - 1 - 0 - 0 - 0 - - - 6052 - 1 - 0 - 0 - 0 - - - 6053 - 1 - 0 - 0 - 0 - - - 6054 - 1 - 0 - 0 - 0 - - - 6055 - 1 - 0 - 0 - 0 - - - 6056 - 1 - 0 - 0 - 0 - - - 6057 - 1 - 0 - 0 - 0 - - - 6058 - 1 - 0 - 0 - 0 - - - 6059 - 1 - 0 - 0 - 0 - - - 6060 - 1 - 0 - 0 - 0 - - - 6061 - 3 - 0 - 0 - 0 - - - 6062 - 3 - 0 - 0 - 0 - - - 6063 - 1 - 0 - 0 - 0 - - - 6064 - 20 - 0 - 0 - 0 - - - 6065 - 150 - 0 - 0 - 0 - - - 6066 - 20 - 0 - 0 - 0 - - - 6067 - 100 - 0 - 0 - 0 - - - 6068 - 20 - 0 - 0 - 0 - - - 6069 - 30 - 0 - 0 - 0 - - - 6070 - 50 - 0 - 0 - 0 - - - 6071 - 1 - 0 - 0 - 0 - - - 6072 - 10 - 0 - 0 - 0 - - - 6073 - 2 - 0 - 0 - 0 - - - 6074 - 1 - 0 - 0 - 0 - - - 6075 - 2 - 0 - 0 - 0 - - - 6076 - 1 - 0 - 0 - 0 - - - 6077 - 2 - 0 - 0 - 0 - - - 6078 - 1 - 0 - 0 - 0 - - - 6079 - 2 - 0 - 0 - 0 - - - 6080 - 1 - 0 - 0 - 0 - - - 6081 - 10 - 0 - 0 - 0 - - - 6082 - 150 - 0 - 0 - 0 - - - 6083 - 1 - 0 - 0 - 0 - - - 6084 - 1 - 0 - 0 - 0 - - - 6085 - 20 - 0 - 0 - 0 - - - 6086 - 20 - 0 - 0 - 0 - - - 6087 - 20 - 0 - 0 - 0 - - - 6088 - 20 - 0 - 0 - 0 - - - 6089 - 1 - 0 - 0 - 0 - - - 6090 - 1 - 0 - 0 - 0 - - - 6091 - 1 - 0 - 0 - 0 - - - 6092 - 1 - 0 - 0 - 0 - - - 6093 - 1 - 0 - 0 - 0 - - - 6094 - 1 - 0 - 0 - 0 - - - 6095 - 1 - 0 - 0 - 0 - - - 6096 - 1 - 0 - 0 - 0 - - - 6097 - 10 - 0 - 0 - 0 - - - 6098 - 50 - 0 - 0 - 0 - - - 6099 - 50 - 0 - 0 - 0 - - - 6100 - 45 - 0 - 0 - 0 - - - 6101 - 35 - 0 - 0 - 0 - - - 6102 - 15 - 0 - 0 - 0 - - - 6103 - 1 - 0 - 0 - 0 - - - 6104 - 1 - 0 - 0 - 0 - - - 6105 - 1 - 0 - 0 - 0 - - - 6106 - 300 - 0 - 0 - 0 - - - 6107 - 300 - 0 - 0 - 0 - - - 6108 - 300 - 0 - 0 - 0 - - - 6109 - 300 - 0 - 0 - 0 - - - 6110 - 300 - 0 - 0 - 0 - - - 6111 - 250 - 0 - 0 - 0 - - - 6112 - 2 - 0 - 0 - 0 - - - 6113 - 4 - 0 - 0 - 0 - - - 6114 - 1 - 0 - 0 - 0 - - - 6115 - 1 - 0 - 0 - 0 - - - 6116 - 1 - 0 - 0 - 0 - - - 6117 - 1 - 0 - 0 - 0 - - - 6118 - 2 - 0 - 0 - 0 - - - 6119 - 1 - 0 - 0 - 0 - - - 6120 - 1 - 0 - 0 - 0 - - - 6121 - 1 - 0 - 0 - 0 - - - 6122 - 1 - 0 - 0 - 0 - - - 6123 - 1 - 0 - 0 - 0 - - - 6124 - 1 - 0 - 0 - 0 - - - 6125 - 1000 - 0 - 0 - 0 - - - 6126 - 1000 - 0 - 0 - 0 - - - 6127 - 1000 - 0 - 0 - 0 - - - 6128 - 35200 - 0 - 0 - 0 - - - 6129 - 65000 - 0 - 0 - 0 - - - 6130 - 64000 - 0 - 0 - 0 - - - 6131 - 60000 - 0 - 0 - 0 - - - 6132 - 60000 - 0 - 0 - 0 - - - 6133 - 7800 - 0 - 0 - 0 - - - 6134 - 7800 - 0 - 0 - 0 - - - 6135 - 3900 - 0 - 0 - 0 - - - 6136 - 3900 - 0 - 0 - 0 - - - 6137 - 10000 - 0 - 0 - 0 - - - 6138 - 10000 - 0 - 0 - 0 - - - 6139 - 45000 - 0 - 0 - 0 - - - 6140 - 45000 - 0 - 0 - 0 - - - 6141 - 40000 - 0 - 0 - 0 - - - 6142 - 40000 - 0 - 0 - 0 - - - 6143 - 650 - 0 - 0 - 0 - - - 6144 - 650 - 0 - 0 - 0 - - - 6145 - 650 - 0 - 0 - 0 - - - 6146 - 650 - 0 - 0 - 0 - - - 6147 - 650 - 0 - 0 - 0 - - - 6148 - 650 - 0 - 0 - 0 - - - 6149 - 650 - 0 - 0 - 0 - - - 6150 - 650 - 0 - 0 - 0 - - - 6151 - 650 - 0 - 0 - 0 - - - 6152 - 650 - 0 - 0 - 0 - - - 6153 - 650 - 0 - 0 - 0 - - - 6154 - 650 - 0 - 0 - 0 - - - 6155 - 60 - 0 - 0 - 0 - - - 6156 - 60 - 0 - 0 - 0 - - - 6157 - 60 - 0 - 0 - 0 - - - 6158 - 60 - 0 - 0 - 0 - - - 6159 - 60 - 0 - 0 - 0 - - - 6160 - 60 - 0 - 0 - 0 - - - 6161 - 60 - 0 - 0 - 0 - - - 6162 - 60 - 0 - 0 - 0 - - - 6163 - 60 - 0 - 0 - 0 - - - 6164 - 60 - 0 - 0 - 0 - - - 6165 - 60 - 0 - 0 - 0 - - - 6166 - 60 - 0 - 0 - 0 - - - 6167 - 60 - 0 - 0 - 0 - - - 6168 - 60 - 0 - 0 - 0 - - - 6169 - 60 - 0 - 0 - 0 - - - 6170 - 60 - 0 - 0 - 0 - - - 6171 - 60 - 0 - 0 - 0 - - - 6172 - 60 - 0 - 0 - 0 - - - 6173 - 60 - 0 - 0 - 0 - - - 6174 - 60 - 0 - 0 - 0 - - - 6175 - 35200 - 0 - 0 - 0 - - - 6176 - 65000 - 0 - 0 - 0 - - - 6177 - 64000 - 0 - 0 - 0 - - - 6178 - 5 - 0 - 0 - 0 - - - 6179 - 5 - 0 - 0 - 0 - - - 6180 - 1 - 0 - 0 - 0 - - - 6181 - 1 - 0 - 0 - 0 - - - 6182 - 1 - 0 - 0 - 0 - - - 6183 - 1 - 0 - 0 - 0 - - - 6184 - 1 - 0 - 0 - 0 - - - 6185 - 1 - 0 - 0 - 0 - - - 6186 - 1 - 0 - 0 - 0 - - - 6187 - 1 - 0 - 0 - 0 - - - 6188 - 1 - 0 - 0 - 0 - - - 6189 - 1 - 0 - 0 - 0 - - - 6190 - 1 - 0 - 0 - 0 - - - 6191 - 1 - 0 - 0 - 0 - - - 6192 - 1 - 0 - 0 - 0 - - - 6193 - 1 - 0 - 0 - 0 - - - 6194 - 1 - 0 - 0 - 0 - - - 6195 - 1 - 0 - 0 - 0 - - - 6196 - 1 - 0 - 0 - 0 - - - 6197 - 1 - 0 - 0 - 0 - - - 6198 - 1 - 0 - 0 - 0 - - - 6199 - 1 - 0 - 0 - 0 - - - 6200 - 1 - 0 - 0 - 0 - - - 6201 - 1 - 0 - 0 - 0 - - - 6202 - 1 - 0 - 0 - 0 - - - 6203 - 1 - 0 - 0 - 0 - - - 6204 - 1 - 0 - 0 - 0 - - - 6205 - 1 - 0 - 0 - 0 - - - 6206 - 1 - 0 - 0 - 0 - - - 6207 - 1 - 0 - 0 - 0 - - - 6208 - 1 - 0 - 0 - 0 - - - 6209 - 5 - 0 - 0 - 0 - - - 6210 - 1 - 0 - 0 - 0 - - - 6211 - 100 - 0 - 0 - 0 - - - 6212 - 100 - 0 - 0 - 0 - - - 6213 - 200 - 0 - 0 - 0 - - - 6214 - 200 - 0 - 0 - 0 - - - 6215 - 3000 - 0 - 0 - 0 - - - 6216 - 3000 - 0 - 0 - 0 - - - 6217 - 2750 - 0 - 0 - 0 - - - 6218 - 2750 - 0 - 0 - 0 - - - 6219 - 3000 - 0 - 0 - 0 - - - 6220 - 3000 - 0 - 0 - 0 - - - 6221 - 3000 - 0 - 0 - 0 - - - 6222 - 3000 - 0 - 0 - 0 - - - 6223 - 3000 - 0 - 0 - 0 - - - 6224 - 3000 - 0 - 0 - 0 - - - 6225 - 3000 - 0 - 0 - 0 - - - 6226 - 3000 - 0 - 0 - 0 - - - 6227 - 3000 - 0 - 0 - 0 - - - 6228 - 3000 - 0 - 0 - 0 - - - 6229 - 3000 - 0 - 0 - 0 - - - 6230 - 3000 - 0 - 0 - 0 - - - 6231 - 3000 - 0 - 0 - 0 - - - 6232 - 3000 - 0 - 0 - 0 - - - 6233 - 3000 - 0 - 0 - 0 - - - 6234 - 3000 - 0 - 0 - 0 - - - 6235 - 3000 - 0 - 0 - 0 - - - 6236 - 3000 - 0 - 0 - 0 - - - 6237 - 3000 - 0 - 0 - 0 - - - 6238 - 3000 - 0 - 0 - 0 - - - 6239 - 3000 - 0 - 0 - 0 - - - 6240 - 3000 - 0 - 0 - 0 - - - 6241 - 3000 - 0 - 0 - 0 - - - 6242 - 3000 - 0 - 0 - 0 - - - 6243 - 3000 - 0 - 0 - 0 - - - 6244 - 3000 - 0 - 0 - 0 - - - 6245 - 3000 - 0 - 0 - 0 - - - 6246 - 3000 - 0 - 0 - 0 - - - 6247 - 3000 - 0 - 0 - 0 - - - 6248 - 3000 - 0 - 0 - 0 - - - 6249 - 3000 - 0 - 0 - 0 - - - 6250 - 3000 - 0 - 0 - 0 - - - 6251 - 3000 - 0 - 0 - 0 - - - 6252 - 3000 - 0 - 0 - 0 - - - 6253 - 3000 - 0 - 0 - 0 - - - 6254 - 3000 - 0 - 0 - 0 - - - 6255 - 3000 - 0 - 0 - 0 - - - 6256 - 3000 - 0 - 0 - 0 - - - 6257 - 3000 - 0 - 0 - 0 - - - 6258 - 3000 - 0 - 0 - 0 - - - 6259 - 3000 - 0 - 0 - 0 - - - 6260 - 3000 - 0 - 0 - 0 - - - 6261 - 3000 - 0 - 0 - 0 - - - 6262 - 3000 - 0 - 0 - 0 - - - 6263 - 3000 - 0 - 0 - 0 - - - 6264 - 3000 - 0 - 0 - 0 - - - 6265 - 3000 - 0 - 0 - 0 - - - 6266 - 3000 - 0 - 0 - 0 - - - 6267 - 3000 - 0 - 0 - 0 - - - 6268 - 3000 - 0 - 0 - 0 - - - 6269 - 3000 - 0 - 0 - 0 - - - 6270 - 3000 - 0 - 0 - 0 - - - 6271 - 3000 - 0 - 0 - 0 - - - 6272 - 3000 - 0 - 0 - 0 - - - 6273 - 3000 - 0 - 0 - 0 - - - 6274 - 3000 - 0 - 0 - 0 - - - 6275 - 3000 - 0 - 0 - 0 - - - 6276 - 3000 - 0 - 0 - 0 - - - 6277 - 3000 - 0 - 0 - 0 - - - 6278 - 3000 - 0 - 0 - 0 - - - 6279 - 3000 - 0 - 0 - 0 - - - 6280 - 3000 - 0 - 0 - 0 - - - 6281 - 10 - 0 - 0 - 0 - - - 6282 - 10 - 0 - 0 - 0 - - - 6283 - 15 - 0 - 0 - 0 - - - 6284 - 15 - 0 - 0 - 0 - - - 6285 - 20 - 0 - 0 - 0 - - - 6286 - 20 - 0 - 0 - 0 - - - 6287 - 35 - 0 - 0 - 0 - - - 6288 - 35 - 0 - 0 - 0 - - - 6289 - 50 - 0 - 0 - 0 - - - 6290 - 50 - 0 - 0 - 0 - - - 6291 - 15 - 0 - 0 - 0 - - - 6292 - 15 - 0 - 0 - 0 - - - 6293 - 25 - 0 - 0 - 0 - - - 6294 - 25 - 0 - 0 - 0 - - - 6295 - 20 - 0 - 0 - 0 - - - 6296 - 20 - 0 - 0 - 0 - - - 6297 - 50 - 0 - 0 - 0 - - - 6298 - 50 - 0 - 0 - 0 - - - 6299 - 40 - 0 - 0 - 0 - - - 6300 - 40 - 0 - 0 - 0 - - - 6301 - 1 - 0 - 0 - 0 - - - 6302 - 1 - 0 - 0 - 0 - - - 6303 - 1 - 0 - 0 - 0 - - - 6304 - 1 - 0 - 0 - 0 - - - 6305 - 10 - 0 - 0 - 0 - - - 6306 - 1 - 0 - 0 - 0 - - - 6307 - 1 - 0 - 0 - 0 - - - 6308 - 1 - 0 - 0 - 0 - - - 6309 - 1 - 0 - 0 - 0 - - - 6310 - 1 - 0 - 0 - 0 - - - 6311 - 200 - 0 - 0 - 0 - - - 6312 - 200 - 0 - 0 - 0 - - - 6313 - 500 - 0 - 0 - 0 - - - 6314 - 500 - 0 - 0 - 0 - - - 6315 - 1000 - 0 - 0 - 0 - - - 6316 - 1000 - 0 - 0 - 0 - - - 6317 - 2000 - 0 - 0 - 0 - - - 6318 - 2000 - 0 - 0 - 0 - - - 6319 - 5 - 0 - 0 - 0 - - - 6320 - 5 - 0 - 0 - 0 - - - 6321 - 1 - 0 - 0 - 0 - - - 6322 - 1250 - 0 - 0 - 0 - - - 6323 - 1250 - 0 - 0 - 0 - - - 6324 - 1175 - 0 - 0 - 0 - - - 6325 - 1175 - 0 - 0 - 0 - - - 6326 - 300 - 0 - 0 - 0 - - - 6327 - 300 - 0 - 0 - 0 - - - 6328 - 250 - 0 - 0 - 0 - - - 6329 - 250 - 0 - 0 - 0 - - - 6330 - 434 - 0 - 0 - 0 - - - 6331 - 434 - 0 - 0 - 0 - - - 6332 - 50 - 0 - 0 - 0 - - - 6333 - 30 - 0 - 0 - 0 - - - 6334 - 30 - 0 - 0 - 0 - - - 6335 - 500 - 0 - 0 - 0 - - - 6336 - 500 - 0 - 0 - 0 - - - 6337 - 500 - 0 - 0 - 0 - - - 6338 - 500 - 0 - 0 - 0 - - - 6339 - 500 - 0 - 0 - 0 - - - 6340 - 500 - 0 - 0 - 0 - - - 6341 - 300 - 0 - 0 - 0 - - - 6342 - 300 - 0 - 0 - 0 - - - 6343 - 250 - 0 - 0 - 0 - - - 6344 - 250 - 0 - 0 - 0 - - - 6345 - 200 - 0 - 0 - 0 - - - 6346 - 200 - 0 - 0 - 0 - - - 6347 - 150 - 0 - 0 - 0 - - - 6348 - 150 - 0 - 0 - 0 - - - 6349 - 100 - 0 - 0 - 0 - - - 6350 - 100 - 0 - 0 - 0 - - - 6351 - 300 - 0 - 0 - 0 - - - 6352 - 300 - 0 - 0 - 0 - - - 6353 - 250 - 0 - 0 - 0 - - - 6354 - 250 - 0 - 0 - 0 - - - 6355 - 200 - 0 - 0 - 0 - - - 6356 - 200 - 0 - 0 - 0 - - - 6357 - 100 - 0 - 0 - 0 - - - 6358 - 100 - 0 - 0 - 0 - - - 6359 - 150 - 0 - 0 - 0 - - - 6360 - 150 - 0 - 0 - 0 - - - 6361 - 300 - 0 - 0 - 0 - - - 6362 - 300 - 0 - 0 - 0 - - - 6363 - 250 - 0 - 0 - 0 - - - 6364 - 250 - 0 - 0 - 0 - - - 6365 - 200 - 0 - 0 - 0 - - - 6366 - 200 - 0 - 0 - 0 - - - 6367 - 100 - 0 - 0 - 0 - - - 6368 - 100 - 0 - 0 - 0 - - - 6369 - 150 - 0 - 0 - 0 - - - 6370 - 150 - 0 - 0 - 0 - - - 6371 - 300 - 0 - 0 - 0 - - - 6372 - 300 - 0 - 0 - 0 - - - 6373 - 250 - 0 - 0 - 0 - - - 6374 - 250 - 0 - 0 - 0 - - - 6375 - 200 - 0 - 0 - 0 - - - 6376 - 200 - 0 - 0 - 0 - - - 6377 - 100 - 0 - 0 - 0 - - - 6378 - 100 - 0 - 0 - 0 - - - 6379 - 150 - 0 - 0 - 0 - - - 6380 - 150 - 0 - 0 - 0 - - - 6381 - 1 - 0 - 0 - 0 - - - 6382 - 20 - 0 - 0 - 0 - - - 6383 - 20 - 0 - 0 - 0 - - - 6384 - 15 - 0 - 0 - 0 - - - 6385 - 15 - 0 - 0 - 0 - - - 6386 - 25 - 0 - 0 - 0 - - - 6387 - 25 - 0 - 0 - 0 - - - 6388 - 35 - 0 - 0 - 0 - - - 6389 - 35 - 0 - 0 - 0 - - - 6390 - 25 - 0 - 0 - 0 - - - 6391 - 25 - 0 - 0 - 0 - - - 6392 - 35 - 0 - 0 - 0 - - - 6393 - 35 - 0 - 0 - 0 - - - 6394 - 20 - 0 - 0 - 0 - - - 6395 - 20 - 0 - 0 - 0 - - - 6396 - 40 - 0 - 0 - 0 - - - 6397 - 40 - 0 - 0 - 0 - - - 6398 - 20 - 0 - 0 - 0 - - - 6399 - 20 - 0 - 0 - 0 - - - 6400 - 35 - 0 - 0 - 0 - - - 6401 - 35 - 0 - 0 - 0 - - - 6402 - 20 - 0 - 0 - 0 - - - 6403 - 20 - 0 - 0 - 0 - - - 6404 - 40 - 0 - 0 - 0 - - - 6405 - 40 - 0 - 0 - 0 - - - 6406 - 20 - 0 - 0 - 0 - - - 6407 - 20 - 0 - 0 - 0 - - - 6408 - 400 - 0 - 0 - 0 - - - 6409 - 400 - 0 - 0 - 0 - - - 6410 - 400 - 0 - 0 - 0 - - - 6411 - 400 - 0 - 0 - 0 - - - 6412 - 800 - 0 - 0 - 0 - - - 6413 - 800 - 0 - 0 - 0 - - - 6414 - 800 - 0 - 0 - 0 - - - 6415 - 800 - 0 - 0 - 0 - - - 6416 - 1200 - 0 - 0 - 0 - - - 6417 - 1200 - 0 - 0 - 0 - - - 6418 - 1600 - 0 - 0 - 0 - - - 6419 - 1600 - 0 - 0 - 0 - - - 6420 - 1600 - 0 - 0 - 0 - - - 6421 - 1600 - 0 - 0 - 0 - - - 6422 - 1 - 0 - 0 - 0 - - - 6423 - 1 - 0 - 0 - 0 - - - 6424 - 1 - 0 - 0 - 0 - - - 6425 - 1 - 0 - 0 - 0 - - - 6426 - 1 - 0 - 0 - 0 - - - 6427 - 1 - 0 - 0 - 0 - - - 6428 - 1 - 0 - 0 - 0 - - - 6429 - 1 - 0 - 0 - 0 - - - 6430 - 1 - 0 - 0 - 0 - - - 6431 - 1 - 0 - 0 - 0 - - - 6432 - 1 - 0 - 0 - 0 - - - 6433 - 1 - 0 - 0 - 0 - - - 6434 - 1 - 0 - 0 - 0 - - - 6435 - 1 - 0 - 0 - 0 - - - 6436 - 1 - 0 - 0 - 0 - - - 6437 - 1 - 0 - 0 - 0 - - - 6438 - 1 - 0 - 0 - 0 - - - 6439 - 1 - 0 - 0 - 0 - - - 6440 - 1 - 0 - 0 - 0 - - - 6441 - 1 - 0 - 0 - 0 - - - 6442 - 1 - 0 - 0 - 0 - - - 6443 - 1 - 0 - 0 - 0 - - - 6444 - 1 - 0 - 0 - 0 - - - 6445 - 1 - 0 - 0 - 0 - - - 6446 - 1 - 0 - 0 - 0 - - - 6447 - 1 - 0 - 0 - 0 - - - 6448 - 3 - 0 - 0 - 0 - - - 6449 - 1 - 0 - 0 - 0 - - - 6450 - 1 - 0 - 0 - 0 - - - 6451 - 1 - 0 - 0 - 0 - - - 6452 - 1 - 0 - 0 - 0 - - - 6453 - 1 - 0 - 0 - 0 - - - 6454 - 1 - 0 - 0 - 0 - - - 6455 - 1 - 0 - 0 - 0 - - - 6456 - 1 - 0 - 0 - 0 - - - 6457 - 1 - 0 - 0 - 0 - - - 6458 - 1 - 0 - 0 - 0 - - - 6459 - 1 - 0 - 0 - 0 - - - 6460 - 1 - 0 - 0 - 0 - - - 6461 - 1 - 0 - 0 - 0 - - - 6462 - 1 - 0 - 0 - 0 - - - 6463 - 1 - 0 - 0 - 0 - - - 6464 - 1 - 0 - 0 - 0 - - - 6465 - 1 - 0 - 0 - 0 - - - 6466 - 1 - 0 - 0 - 0 - - - 6467 - 1 - 0 - 0 - 0 - - - 6468 - 1 - 0 - 0 - 0 - - - 6469 - 1 - 0 - 0 - 0 - - - 6470 - 120 - 0 - 0 - 0 - - - 6471 - 120 - 0 - 0 - 0 - - - 6472 - 90 - 0 - 0 - 0 - - - 6473 - 90 - 0 - 0 - 0 - - - 6474 - 60 - 0 - 0 - 0 - - - 6475 - 60 - 0 - 0 - 0 - - - 6476 - 30 - 0 - 0 - 0 - - - 6477 - 30 - 0 - 0 - 0 - - - 6478 - 1 - 0 - 0 - 0 - - - 6479 - 1 - 0 - 0 - 0 - - - 6480 - 1 - 0 - 0 - 0 - - - 6481 - 1 - 0 - 0 - 0 - - - 6482 - 1 - 0 - 0 - 0 - - - 6483 - 1 - 0 - 0 - 0 - - - 6484 - 1 - 0 - 0 - 0 - - - 6485 - 1 - 0 - 0 - 0 - - - 6486 - 1 - 0 - 0 - 0 - - - 6487 - 1 - 0 - 0 - 0 - - - 6488 - 1 - 0 - 0 - 0 - - - 6489 - 1 - 0 - 0 - 0 - - - 6490 - 1 - 0 - 0 - 0 - - - 6491 - 1 - 0 - 0 - 0 - - - 6492 - 1 - 0 - 0 - 0 - - - 6493 - 1 - 0 - 0 - 0 - - - 6494 - 1 - 0 - 0 - 0 - - - 6495 - 1 - 0 - 0 - 0 - - - 6496 - 1 - 0 - 0 - 0 - - - 6497 - 1 - 0 - 0 - 0 - - - 6498 - 1 - 0 - 0 - 0 - - - 6499 - 1 - 0 - 0 - 0 - - - 6500 - 1 - 0 - 0 - 0 - - - 6501 - 1 - 0 - 0 - 0 - - - 6502 - 1 - 0 - 0 - 0 - - - 6503 - 1 - 0 - 0 - 0 - - - 6504 - 1 - 0 - 0 - 0 - - - 6505 - 1 - 0 - 0 - 0 - - - 6506 - 1 - 0 - 0 - 0 - - - 6507 - 1 - 0 - 0 - 0 - - - 6508 - 1 - 0 - 0 - 0 - - - 6509 - 1 - 0 - 0 - 0 - - - 6510 - 1 - 0 - 0 - 0 - - - 6511 - 1 - 0 - 0 - 0 - - - 6512 - 1 - 0 - 0 - 0 - - - 6513 - 1 - 0 - 0 - 0 - - - 6514 - 1 - 0 - 0 - 0 - - - 6515 - 1 - 0 - 0 - 0 - - - 6516 - 1 - 0 - 0 - 0 - - - 6517 - 1 - 0 - 0 - 0 - - - 6518 - 1 - 0 - 0 - 0 - - - 6519 - 1 - 0 - 0 - 0 - - - 6520 - 1 - 0 - 0 - 0 - - - 6521 - 1 - 0 - 0 - 0 - - - 6522 - 250 - 0 - 0 - 0 - - - 6523 - 40000 - 0 - 0 - 0 - - - 6524 - 45000 - 0 - 0 - 0 - - - 6525 - 25000 - 0 - 0 - 0 - - - 6526 - 35000 - 0 - 0 - 0 - - - 6527 - 30000 - 0 - 0 - 0 - - - 6528 - 50000 - 0 - 0 - 0 - - - 6529 - 1 - 0 - 0 - 0 - - - 6530 - 1 - 0 - 0 - 0 - - - 6531 - 1 - 0 - 0 - 0 - - - 6532 - 1 - 0 - 0 - 0 - - - 6533 - 1 - 0 - 0 - 0 - - - 6534 - 1 - 0 - 0 - 0 - - - 6535 - 40000 - 0 - 0 - 0 - - - 6536 - 45000 - 0 - 0 - 0 - - - 6537 - 25000 - 0 - 0 - 0 - - - 6538 - 35000 - 0 - 0 - 0 - - - 6539 - 30000 - 0 - 0 - 0 - - - 6540 - 50000 - 0 - 0 - 0 - - - 6541 - 1 - 0 - 0 - 0 - - - 6542 - 1 - 0 - 0 - 0 - - - 6543 - 1 - 0 - 0 - 0 - - - 6544 - 1 - 0 - 0 - 0 - - - 6545 - 1 - 0 - 0 - 0 - - - 6546 - 1 - 0 - 0 - 0 - - - 6547 - 1 - 0 - 0 - 0 - - - 6548 - 1 - 0 - 0 - 0 - - - 6549 - 1 - 0 - 0 - 0 - - - 6550 - 1 - 0 - 0 - 0 - - - 6551 - 1 - 0 - 0 - 0 - - - 6552 - 1 - 0 - 0 - 0 - - - 6553 - 1 - 0 - 0 - 0 - - - 6554 - 1 - 0 - 0 - 0 - - - 6555 - 1 - 0 - 0 - 0 - - - 6556 - 1 - 0 - 0 - 0 - - - 6557 - 1 - 0 - 0 - 0 - - - 6558 - 1 - 0 - 0 - 0 - - - 6559 - 1 - 0 - 0 - 0 - - - 6560 - 1 - 0 - 0 - 0 - - - 6561 - 2 - 0 - 0 - 0 - - - 6562 - 17000 - 0 - 0 - 0 - - - 6563 - 45000 - 0 - 0 - 0 - - - 6564 - 1 - 0 - 0 - 0 - - - 6565 - 1 - 0 - 0 - 0 - - - 6566 - 1 - 0 - 0 - 0 - - - 6567 - 1 - 0 - 0 - 0 - - - 6568 - 60000 - 0 - 0 - 0 - - - 6569 - 60000 - 0 - 0 - 0 - - - 6570 - 60000 - 0 - 0 - 0 - - - 6571 - 300000 - 0 - 0 - 0 - - - 6572 - 300000 - 0 - 0 - 0 - - - 6573 - 300000 - 0 - 0 - 0 - - - 6574 - 300000 - 0 - 0 - 0 - - - 6575 - 201000 - 0 - 0 - 0 - - - 6576 - 201000 - 0 - 0 - 0 - - - 6577 - 201000 - 0 - 0 - 0 - - - 6578 - 201000 - 0 - 0 - 0 - - - 6579 - 201000 - 0 - 0 - 0 - - - 6580 - 201000 - 0 - 0 - 0 - - - 6581 - 201000 - 0 - 0 - 0 - - - 6582 - 201000 - 0 - 0 - 0 - - - 6583 - 202000 - 0 - 0 - 0 - - - 6584 - 202000 - 0 - 0 - 0 - - - 6585 - 202000 - 0 - 0 - 0 - - - 6586 - 202000 - 0 - 0 - 0 - - - 6587 - 360 - 0 - 0 - 0 - - - 6588 - 360 - 0 - 0 - 0 - - - 6589 - 1248 - 0 - 0 - 0 - - - 6590 - 1248 - 0 - 0 - 0 - - - 6591 - 240 - 0 - 0 - 0 - - - 6592 - 240 - 0 - 0 - 0 - - - 6593 - 240 - 0 - 0 - 0 - - - 6594 - 240 - 0 - 0 - 0 - - - 6595 - 240 - 0 - 0 - 0 - - - 6596 - 240 - 0 - 0 - 0 - - - 6597 - 240 - 0 - 0 - 0 - - - 6598 - 240 - 0 - 0 - 0 - - - 6599 - 1920 - 0 - 0 - 0 - - - 6600 - 1920 - 0 - 0 - 0 - - - 6601 - 432 - 0 - 0 - 0 - - - 6602 - 432 - 0 - 0 - 0 - - - 6603 - 200 - 0 - 0 - 0 - - - 6604 - 200 - 0 - 0 - 0 - - - 6605 - 624 - 0 - 0 - 0 - - - 6606 - 624 - 0 - 0 - 0 - - - 6607 - 960 - 0 - 0 - 0 - - - 6608 - 960 - 0 - 0 - 0 - - - 6609 - 1920 - 0 - 0 - 0 - - - 6610 - 1920 - 0 - 0 - 0 - - - 6611 - 768 - 0 - 0 - 0 - - - 6612 - 768 - 0 - 0 - 0 - - - 6613 - 980 - 0 - 0 - 0 - - - 6614 - 980 - 0 - 0 - 0 - - - 6615 - 1440 - 0 - 0 - 0 - - - 6616 - 1440 - 0 - 0 - 0 - - - 6617 - 3840 - 0 - 0 - 0 - - - 6618 - 3840 - 0 - 0 - 0 - - - 6619 - 576 - 0 - 0 - 0 - - - 6620 - 576 - 0 - 0 - 0 - - - 6621 - 576 - 0 - 0 - 0 - - - 6622 - 576 - 0 - 0 - 0 - - - 6623 - 1056 - 0 - 0 - 0 - - - 6624 - 1056 - 0 - 0 - 0 - - - 6625 - 1920 - 0 - 0 - 0 - - - 6626 - 1920 - 0 - 0 - 0 - - - 6627 - 1920 - 0 - 0 - 0 - - - 6628 - 1920 - 0 - 0 - 0 - - - 6629 - 6 - 0 - 0 - 0 - - - 6630 - 6 - 0 - 0 - 0 - - - 6631 - 1152 - 0 - 0 - 0 - - - 6632 - 1152 - 0 - 0 - 0 - - - 6633 - 1632 - 0 - 0 - 0 - - - 6634 - 1632 - 0 - 0 - 0 - - - 6635 - 1 - 0 - 0 - 0 - - - 6636 - 1 - 0 - 0 - 0 - - - 6637 - 1 - 0 - 0 - 0 - - - 6638 - 1 - 0 - 0 - 0 - - - 6639 - 1 - 0 - 0 - 0 - - - 6640 - 1 - 0 - 0 - 0 - - - 6641 - 1 - 0 - 0 - 0 - - - 6642 - 1 - 0 - 0 - 0 - - - 6643 - 1 - 0 - 0 - 0 - - - 6644 - 1 - 0 - 0 - 0 - - - 6645 - 1 - 0 - 0 - 0 - - - 6646 - 1 - 0 - 0 - 0 - - - 6647 - 1 - 0 - 0 - 0 - - - 6648 - 1 - 0 - 0 - 0 - - - 6649 - 1 - 0 - 0 - 0 - - - 6650 - 1 - 0 - 0 - 0 - - - 6651 - 1 - 0 - 0 - 0 - - - 6652 - 1 - 0 - 0 - 0 - - - 6653 - 1 - 0 - 0 - 0 - - - 6654 - 1 - 0 - 0 - 0 - - - 6655 - 1 - 0 - 0 - 0 - - - 6656 - 1 - 0 - 0 - 0 - - - 6657 - 1 - 0 - 0 - 0 - - - 6658 - 1 - 0 - 0 - 0 - - - 6659 - 1 - 0 - 0 - 0 - - - 6660 - 1 - 0 - 0 - 0 - - - 6661 - 1 - 0 - 0 - 0 - - - 6662 - 1 - 0 - 0 - 0 - - - 6663 - 1 - 0 - 0 - 0 - - - 6664 - 60 - 0 - 0 - 0 - - - 6665 - 1 - 0 - 0 - 0 - - - 6666 - 1 - 0 - 0 - 0 - - - 6667 - 1 - 0 - 0 - 0 - - - 6668 - 1 - 0 - 0 - 0 - - - 6669 - 1 - 0 - 0 - 0 - - - 6670 - 1 - 0 - 0 - 0 - - - 6671 - 1 - 0 - 0 - 0 - - - 6672 - 1 - 0 - 0 - 0 - - - 6673 - 1 - 0 - 0 - 0 - - - 6674 - 10 - 0 - 0 - 0 - - - 6675 - 1 - 0 - 0 - 0 - - - 6676 - 1 - 0 - 0 - 0 - - - 6677 - 5 - 0 - 0 - 0 - - - 6678 - 5 - 0 - 0 - 0 - - - 6679 - 5 - 0 - 0 - 0 - - - 6680 - 5 - 0 - 0 - 0 - - - 6681 - 2 - 0 - 0 - 0 - - - 6682 - 2 - 0 - 0 - 0 - - - 6683 - 2 - 0 - 0 - 0 - - - 6684 - 2 - 0 - 0 - 0 - - - 6685 - 200 - 0 - 0 - 0 - - - 6686 - 200 - 0 - 0 - 0 - - - 6687 - 175 - 0 - 0 - 0 - - - 6688 - 175 - 0 - 0 - 0 - - - 6689 - 150 - 0 - 0 - 0 - - - 6690 - 150 - 0 - 0 - 0 - - - 6691 - 125 - 0 - 0 - 0 - - - 6692 - 125 - 0 - 0 - 0 - - - 6693 - 1 - 0 - 0 - 0 - - - 6694 - 1 - 0 - 0 - 0 - - - 6695 - 1 - 0 - 0 - 0 - - - 6696 - 1 - 0 - 0 - 0 - - - 6697 - 4 - 0 - 0 - 0 - - - 6698 - 4 - 0 - 0 - 0 - - - 6699 - 1 - 0 - 0 - 0 - - - 6700 - 1 - 0 - 0 - 0 - - - 6701 - 4 - 0 - 0 - 0 - - - 6702 - 4 - 0 - 0 - 0 - - - 6703 - 8 - 0 - 0 - 0 - - - 6704 - 8 - 0 - 0 - 0 - - - 6705 - 350 - 0 - 0 - 0 - - - 6706 - 350 - 0 - 0 - 0 - - - 6707 - 1 - 0 - 0 - 0 - - - 6708 - 1 - 0 - 0 - 0 - - - 6709 - 1 - 0 - 0 - 0 - - - 6710 - 166 - 0 - 0 - 0 - - - 6711 - 1 - 0 - 0 - 0 - - - 6712 - 1 - 0 - 0 - 0 - - - 6713 - 5 - 0 - 0 - 0 - - - 6714 - 5 - 0 - 0 - 0 - - - 6715 - 1 - 0 - 0 - 0 - - - 6716 - 1 - 0 - 0 - 0 - - - 6717 - 20000 - 0 - 0 - 0 - - - 6718 - 1 - 0 - 0 - 0 - - - 6719 - 1 - 0 - 0 - 0 - - - 6720 - 200 - 0 - 0 - 0 - - - 6721 - 1 - 0 - 0 - 0 - - - 6722 - 1 - 0 - 0 - 0 - - - 6723 - 1 - 0 - 0 - 0 - - - 6724 - 8000 - 0 - 0 - 0 - - - 6725 - 8000 - 0 - 0 - 0 - - - 6726 - 17000 - 0 - 0 - 0 - - - 6727 - 45000 - 0 - 0 - 0 - - - 6728 - 1 - 0 - 0 - 0 - - - 6729 - 1 - 0 - 0 - 0 - - - 6730 - 1 - 0 - 0 - 0 - - - 6731 - 10000 - 0 - 0 - 0 - - - 6732 - 10000 - 0 - 0 - 0 - - - 6733 - 10000 - 0 - 0 - 0 - - - 6734 - 10000 - 0 - 0 - 0 - - - 6735 - 10000 - 0 - 0 - 0 - - - 6736 - 10000 - 0 - 0 - 0 - - - 6737 - 10000 - 0 - 0 - 0 - - - 6738 - 10000 - 0 - 0 - 0 - - - 6739 - 55000 - 0 - 0 - 0 - - - 6740 - 55000 - 0 - 0 - 0 - - - 6741 - 1800 - 0 - 0 - 0 - - - 6742 - 1800 - 0 - 0 - 0 - - - 6743 - 54999 - 0 - 0 - 0 - - - 6744 - 54999 - 0 - 0 - 0 - - - 6745 - 50 - 0 - 0 - 0 - - - 6746 - 500 - 0 - 0 - 0 - - - 6747 - 1 - 0 - 0 - 0 - - - 6748 - 1 - 0 - 0 - 0 - - - 6749 - 1 - 0 - 0 - 0 - - - 6750 - 40 - 0 - 0 - 0 - - - 6751 - 1 - 0 - 0 - 0 - - - 6752 - 30 - 0 - 0 - 0 - - - 6753 - 1 - 0 - 0 - 0 - - - 6754 - 1 - 0 - 0 - 0 - - - 6755 - 1 - 0 - 0 - 0 - - - 6756 - 1 - 0 - 0 - 0 - - - 6757 - 1 - 0 - 0 - 0 - - - 6758 - 1 - 0 - 0 - 0 - - - 6759 - 1 - 0 - 0 - 0 - - - 6760 - 625 - 0 - 0 - 0 - - - 6761 - 625 - 0 - 0 - 0 - - - 6762 - 625 - 0 - 0 - 0 - - - 6763 - 625 - 0 - 0 - 0 - - - 6764 - 625 - 0 - 0 - 0 - - - 6765 - 625 - 0 - 0 - 0 - - - 6766 - 1 - 0 - 0 - 0 - - - 6767 - 1 - 0 - 0 - 0 - - - 6768 - 1 - 0 - 0 - 0 - - - 6769 - 1 - 0 - 0 - 0 - - - 6770 - 1 - 0 - 0 - 0 - - - 6771 - 1 - 0 - 0 - 0 - - - 6772 - 1 - 0 - 0 - 0 - - - 6773 - 1 - 0 - 0 - 0 - - - 6774 - 1 - 0 - 0 - 0 - - - 6775 - 1 - 0 - 0 - 0 - - - 6776 - 1 - 0 - 0 - 0 - - - 6777 - 1 - 0 - 0 - 0 - - - 6778 - 1 - 0 - 0 - 0 - - - 6779 - 1 - 0 - 0 - 0 - - - 6780 - 1 - 0 - 0 - 0 - - - 6781 - 1 - 0 - 0 - 0 - - - 6782 - 1 - 0 - 0 - 0 - - - 6783 - 1 - 0 - 0 - 0 - - - 6784 - 1 - 0 - 0 - 0 - - - 6785 - 1 - 0 - 0 - 0 - - - 6786 - 1 - 0 - 0 - 0 - - - 6787 - 1 - 0 - 0 - 0 - - - 6788 - 1 - 0 - 0 - 0 - - - 6789 - 1 - 0 - 0 - 0 - - - 6790 - 1 - 0 - 0 - 0 - - - 6791 - 1 - 0 - 0 - 0 - - - 6792 - 1 - 0 - 0 - 0 - - - 6793 - 1 - 0 - 0 - 0 - - - 6794 - 30 - 0 - 0 - 0 - - - 6795 - 30 - 0 - 0 - 0 - - - 6796 - 1 - 0 - 0 - 0 - - - 6797 - 1 - 0 - 0 - 0 - - - 6798 - 1 - 0 - 0 - 0 - - - 6799 - 1 - 0 - 0 - 0 - - - 6800 - 1 - 0 - 0 - 0 - - - 6801 - 1 - 0 - 0 - 0 - - - 6802 - 1 - 0 - 0 - 0 - - - 6803 - 1 - 0 - 0 - 0 - - - 6804 - 1 - 0 - 0 - 0 - - - 6805 - 1 - 0 - 0 - 0 - - - 6806 - 1 - 0 - 0 - 0 - - - 6807 - 1 - 0 - 0 - 0 - - - 6808 - 1 - 0 - 0 - 0 - - - 6809 - 66000 - 0 - 0 - 0 - - - 6810 - 1 - 0 - 0 - 0 - - - 6811 - 1 - 0 - 0 - 0 - - - 6812 - 1 - 0 - 0 - 0 - - - 6813 - 66000 - 0 - 0 - 0 - - - 6814 - 10 - 0 - 0 - 0 - - - 6815 - 10 - 0 - 0 - 0 - - - 6816 - 1 - 0 - 0 - 0 - - - 6817 - 1 - 0 - 0 - 0 - - - 6818 - 1 - 0 - 0 - 0 - - - 6819 - 1 - 0 - 0 - 0 - - - 6820 - 1 - 0 - 0 - 0 - - - 6821 - 1 - 0 - 0 - 0 - - - 6822 - 1 - 0 - 0 - 0 - - - 6823 - 1 - 0 - 0 - 0 - - - 6824 - 1 - 0 - 0 - 0 - - - 6825 - 1 - 0 - 0 - 0 - - - 6826 - 1 - 0 - 0 - 0 - - - 6827 - 1 - 0 - 0 - 0 - - - 6828 - 1 - 0 - 0 - 0 - - - 6829 - 1 - 0 - 0 - 0 - - - 6830 - 1 - 0 - 0 - 0 - - - 6831 - 1 - 0 - 0 - 0 - - - 6832 - 1 - 0 - 0 - 0 - - - 6833 - 1 - 0 - 0 - 0 - - - 6834 - 1 - 0 - 0 - 0 - - - 6835 - 1 - 0 - 0 - 0 - - - 6836 - 1 - 0 - 0 - 0 - - - 6837 - 1 - 0 - 0 - 0 - - - 6838 - 1 - 0 - 0 - 0 - - - 6839 - 1 - 0 - 0 - 0 - - - 6840 - 1 - 0 - 0 - 0 - - - 6841 - 1 - 0 - 0 - 0 - - - 6842 - 1 - 0 - 0 - 0 - - - 6843 - 1 - 0 - 0 - 0 - - - 6844 - 1 - 0 - 0 - 0 - - - 6845 - 1 - 0 - 0 - 0 - - - 6846 - 1 - 0 - 0 - 0 - - - 6847 - 1 - 0 - 0 - 0 - - - 6848 - 1 - 0 - 0 - 0 - - - 6849 - 1 - 0 - 0 - 0 - - - 6850 - 1 - 0 - 0 - 0 - - - 6851 - 1 - 0 - 0 - 0 - - - 6852 - 1 - 0 - 0 - 0 - - - 6853 - 1 - 0 - 0 - 0 - - - 6854 - 1 - 0 - 0 - 0 - - - 6855 - 1 - 0 - 0 - 0 - - - 6856 - 1 - 0 - 0 - 0 - - - 6857 - 1 - 0 - 0 - 0 - - - 6858 - 1 - 0 - 0 - 0 - - - 6859 - 1 - 0 - 0 - 0 - - - 6860 - 1 - 0 - 0 - 0 - - - 6861 - 1 - 0 - 0 - 0 - - - 6862 - 1 - 0 - 0 - 0 - - - 6863 - 1 - 0 - 0 - 0 - - - 6864 - 60000 - 0 - 0 - 0 - - - 6865 - 1 - 0 - 0 - 0 - - - 6866 - 1 - 0 - 0 - 0 - - - 6867 - 1 - 0 - 0 - 0 - - - 6868 - 1 - 0 - 0 - 0 - - - 6869 - 1 - 0 - 0 - 0 - - - 6870 - 1 - 0 - 0 - 0 - - - 6871 - 1 - 0 - 0 - 0 - - - 6872 - 1 - 0 - 0 - 0 - - - 6873 - 1 - 0 - 0 - 0 - - - 6874 - 1 - 0 - 0 - 0 - - - 6875 - 1 - 0 - 0 - 0 - - - 6876 - 1 - 0 - 0 - 0 - - - 6877 - 1 - 0 - 0 - 0 - - - 6878 - 1 - 0 - 0 - 0 - - - 6879 - 1 - 0 - 0 - 0 - - - 6880 - 1 - 0 - 0 - 0 - - - 6881 - 1 - 0 - 0 - 0 - - - 6882 - 1 - 0 - 0 - 0 - - - 6883 - 1 - 0 - 0 - 0 - - - 6884 - 1 - 0 - 0 - 0 - - - 6885 - 1 - 0 - 0 - 0 - - - 6886 - 1 - 0 - 0 - 0 - - - 6887 - 1 - 0 - 0 - 0 - - - 6888 - 1 - 0 - 0 - 0 - - - 6889 - 500 - 0 - 0 - 0 - - - 6890 - 500 - 0 - 0 - 0 - - - 6891 - 1 - 0 - 0 - 0 - - - 6892 - 1 - 0 - 0 - 0 - - - 6893 - 1 - 0 - 0 - 0 - - - 6894 - 1 - 0 - 0 - 0 - - - 6895 - 1 - 0 - 0 - 0 - - - 6896 - 1 - 0 - 0 - 0 - - - 6897 - 1 - 0 - 0 - 0 - - - 6898 - 1 - 0 - 0 - 0 - - - 6899 - 1 - 0 - 0 - 0 - - - 6900 - 1 - 0 - 0 - 0 - - - 6901 - 1 - 0 - 0 - 0 - - - 6902 - 1 - 0 - 0 - 0 - - - 6903 - 1 - 0 - 0 - 0 - - - 6904 - 1 - 0 - 0 - 0 - - - 6905 - 1 - 0 - 0 - 0 - - - 6906 - 1 - 0 - 0 - 0 - - - 6907 - 1 - 0 - 0 - 0 - - - 6908 - 1200 - 0 - 0 - 0 - - - 6909 - 1200 - 0 - 0 - 0 - - - 6910 - 1500 - 0 - 0 - 0 - - - 6911 - 1500 - 0 - 0 - 0 - - - 6912 - 2000 - 0 - 0 - 0 - - - 6913 - 2000 - 0 - 0 - 0 - - - 6914 - 2500 - 0 - 0 - 0 - - - 6915 - 2500 - 0 - 0 - 0 - - - 6916 - 140000 - 0 - 0 - 0 - - - 6917 - 140000 - 0 - 0 - 0 - - - 6918 - 17000 - 0 - 0 - 0 - - - 6919 - 17000 - 0 - 0 - 0 - - - 6920 - 12000 - 0 - 0 - 0 - - - 6921 - 12000 - 0 - 0 - 0 - - - 6922 - 12000 - 0 - 0 - 0 - - - 6923 - 12000 - 0 - 0 - 0 - - - 6924 - 90000 - 0 - 0 - 0 - - - 6925 - 90000 - 0 - 0 - 0 - - - 6926 - 1 - 0 - 0 - 0 - - - 6927 - 1 - 0 - 0 - 0 - - - 6928 - 1 - 0 - 0 - 0 - - - 6929 - 1 - 0 - 0 - 0 - - - 6930 - 1 - 0 - 0 - 0 - - - 6931 - 1 - 0 - 0 - 0 - - - 6932 - 1 - 0 - 0 - 0 - - - 6933 - 1 - 0 - 0 - 0 - - - 6934 - 1 - 0 - 0 - 0 - - - 6935 - 1 - 0 - 0 - 0 - - - 6936 - 1 - 0 - 0 - 0 - - - 6937 - 1 - 0 - 0 - 0 - - - 6938 - 1 - 0 - 0 - 0 - - - 6939 - 1 - 0 - 0 - 0 - - - 6940 - 1 - 0 - 0 - 0 - - - 6941 - 1 - 0 - 0 - 0 - - - 6942 - 1 - 0 - 0 - 0 - - - 6943 - 1 - 0 - 0 - 0 - - - 6944 - 1 - 0 - 0 - 0 - - - 6945 - 1 - 0 - 0 - 0 - - - 6946 - 1 - 0 - 0 - 0 - - - 6947 - 1 - 0 - 0 - 0 - - - 6948 - 1 - 0 - 0 - 0 - - - 6949 - 1 - 0 - 0 - 0 - - - 6950 - 1 - 0 - 0 - 0 - - - 6951 - 1 - 0 - 0 - 0 - - - 6952 - 1 - 0 - 0 - 0 - - - 6953 - 1 - 0 - 0 - 0 - - - 6954 - 1 - 0 - 0 - 0 - - - 6955 - 1 - 0 - 0 - 0 - - - 6956 - 1 - 0 - 0 - 0 - - - 6957 - 1 - 0 - 0 - 0 - - - 6958 - 1 - 0 - 0 - 0 - - - 6959 - 1 - 0 - 0 - 0 - - - 6960 - 1 - 0 - 0 - 0 - - - 6961 - 25 - 0 - 0 - 0 - - - 6962 - 25 - 0 - 0 - 0 - - - 6963 - 25 - 0 - 0 - 0 - - - 6964 - 6 - 0 - 0 - 0 - - - 6965 - 25 - 0 - 0 - 0 - - - 6966 - 1 - 0 - 0 - 0 - - - 6967 - 6 - 0 - 0 - 0 - - - 6968 - 25 - 0 - 0 - 0 - - - 6969 - 6 - 0 - 0 - 0 - - - 6970 - 1 - 0 - 0 - 0 - - - 6971 - 1 - 0 - 0 - 0 - - - 6972 - 1 - 0 - 0 - 0 - - - 6973 - 1 - 0 - 0 - 0 - - - 6974 - 1 - 0 - 0 - 0 - - - 6975 - 1 - 0 - 0 - 0 - - - 6976 - 1 - 0 - 0 - 0 - - - 6977 - 1 - 0 - 0 - 0 - - - 6978 - 1 - 0 - 0 - 0 - - - 6979 - 1 - 0 - 0 - 0 - - - 6980 - 1 - 0 - 0 - 0 - - - 6981 - 1 - 0 - 0 - 0 - - - 6982 - 1 - 0 - 0 - 0 - - - 6983 - 1 - 0 - 0 - 0 - - - 6984 - 1 - 0 - 0 - 0 - - - 6985 - 1 - 0 - 0 - 0 - - - 6986 - 1 - 0 - 0 - 0 - - - 6987 - 1 - 0 - 0 - 0 - - - 6988 - 1 - 0 - 0 - 0 - - - 6989 - 1 - 0 - 0 - 0 - - - 6990 - 1 - 0 - 0 - 0 - - - 6991 - 1 - 0 - 0 - 0 - - - 6992 - 1 - 0 - 0 - 0 - - - 6993 - 1 - 0 - 0 - 0 - - - 6994 - 1 - 0 - 0 - 0 - - - 6995 - 1 - 0 - 0 - 0 - - - 6996 - 1 - 0 - 0 - 0 - - - 6997 - 1 - 0 - 0 - 0 - - - 6998 - 1 - 0 - 0 - 0 - - - 6999 - 1 - 0 - 0 - 0 - - - 7000 - 1 - 0 - 0 - 0 - - - 7001 - 1 - 0 - 0 - 0 - - - 7002 - 1 - 0 - 0 - 0 - - - 7003 - 1 - 0 - 0 - 0 - - - 7004 - 1 - 0 - 0 - 0 - - - 7005 - 1 - 0 - 0 - 0 - - - 7006 - 1 - 0 - 0 - 0 - - - 7007 - 1 - 0 - 0 - 0 - - - 7008 - 1 - 0 - 0 - 0 - - - 7009 - 1 - 0 - 0 - 0 - - - 7010 - 1 - 0 - 0 - 0 - - - 7011 - 1 - 0 - 0 - 0 - - - 7012 - 1 - 0 - 0 - 0 - - - 7013 - 1 - 0 - 0 - 0 - - - 7014 - 1 - 0 - 0 - 0 - - - 7015 - 1 - 0 - 0 - 0 - - - 7016 - 1 - 0 - 0 - 0 - - - 7017 - 1 - 0 - 0 - 0 - - - 7018 - 1 - 0 - 0 - 0 - - - 7019 - 1 - 0 - 0 - 0 - - - 7020 - 1 - 0 - 0 - 0 - - - 7021 - 1 - 0 - 0 - 0 - - - 7022 - 1 - 0 - 0 - 0 - - - 7023 - 1 - 0 - 0 - 0 - - - 7024 - 1 - 0 - 0 - 0 - - - 7025 - 1 - 0 - 0 - 0 - - - 7026 - 1 - 0 - 0 - 0 - - - 7027 - 1 - 0 - 0 - 0 - - - 7028 - 1 - 0 - 0 - 0 - - - 7029 - 1 - 0 - 0 - 0 - - - 7030 - 1 - 0 - 0 - 0 - - - 7031 - 1 - 0 - 0 - 0 - - - 7032 - 1 - 0 - 0 - 0 - - - 7033 - 1 - 0 - 0 - 0 - - - 7034 - 1 - 0 - 0 - 0 - - - 7035 - 1 - 0 - 0 - 0 - - - 7036 - 1 - 0 - 0 - 0 - - - 7037 - 1 - 0 - 0 - 0 - - - 7038 - 1 - 0 - 0 - 0 - - - 7039 - 1 - 0 - 0 - 0 - - - 7040 - 1 - 0 - 0 - 0 - - - 7041 - 1 - 0 - 0 - 0 - - - 7042 - 1 - 0 - 0 - 0 - - - 7043 - 1 - 0 - 0 - 0 - - - 7044 - 1 - 0 - 0 - 0 - - - 7045 - 1 - 0 - 0 - 0 - - - 7046 - 1 - 0 - 0 - 0 - - - 7047 - 1 - 0 - 0 - 0 - - - 7048 - 1 - 0 - 0 - 0 - - - 7049 - 1 - 0 - 0 - 0 - - - 7050 - 1 - 0 - 0 - 0 - - - 7051 - 130 - 0 - 0 - 0 - - - 7052 - 130 - 0 - 0 - 0 - - - 7053 - 130 - 0 - 0 - 0 - - - 7054 - 13 - 0 - 0 - 0 - - - 7055 - 13 - 0 - 0 - 0 - - - 7056 - 12 - 0 - 0 - 0 - - - 7057 - 12 - 0 - 0 - 0 - - - 7058 - 45 - 0 - 0 - 0 - - - 7059 - 45 - 0 - 0 - 0 - - - 7060 - 113 - 0 - 0 - 0 - - - 7061 - 113 - 0 - 0 - 0 - - - 7062 - 13 - 0 - 0 - 0 - - - 7063 - 13 - 0 - 0 - 0 - - - 7064 - 12 - 0 - 0 - 0 - - - 7065 - 12 - 0 - 0 - 0 - - - 7066 - 45 - 0 - 0 - 0 - - - 7067 - 45 - 0 - 0 - 0 - - - 7068 - 113 - 0 - 0 - 0 - - - 7069 - 113 - 0 - 0 - 0 - - - 7070 - 8 - 0 - 0 - 0 - - - 7071 - 8 - 0 - 0 - 0 - - - 7072 - 9 - 0 - 0 - 0 - - - 7073 - 9 - 0 - 0 - 0 - - - 7074 - 7 - 0 - 0 - 0 - - - 7075 - 7 - 0 - 0 - 0 - - - 7076 - 8 - 0 - 0 - 0 - - - 7077 - 8 - 0 - 0 - 0 - - - 7078 - 8 - 0 - 0 - 0 - - - 7079 - 8 - 0 - 0 - 0 - - - 7080 - 42 - 0 - 0 - 0 - - - 7081 - 42 - 0 - 0 - 0 - - - 7082 - 42 - 0 - 0 - 0 - - - 7083 - 42 - 0 - 0 - 0 - - - 7084 - 7 - 0 - 0 - 0 - - - 7085 - 7 - 0 - 0 - 0 - - - 7086 - 104 - 0 - 0 - 0 - - - 7087 - 104 - 0 - 0 - 0 - - - 7088 - 13 - 0 - 0 - 0 - - - 7089 - 13 - 0 - 0 - 0 - - - 7090 - 5 - 0 - 0 - 0 - - - 7091 - 5 - 0 - 0 - 0 - - - 7092 - 5 - 0 - 0 - 0 - - - 7093 - 5 - 0 - 0 - 0 - - - 7094 - 5 - 0 - 0 - 0 - - - 7095 - 5 - 0 - 0 - 0 - - - 7096 - 1 - 0 - 0 - 0 - - - 7097 - 1 - 0 - 0 - 0 - - - 7098 - 1 - 0 - 0 - 0 - - - 7099 - 1 - 0 - 0 - 0 - - - 7100 - 1 - 0 - 0 - 0 - - - 7101 - 1 - 0 - 0 - 0 - - - 7102 - 1 - 0 - 0 - 0 - - - 7103 - 1 - 0 - 0 - 0 - - - 7104 - 1 - 0 - 0 - 0 - - - 7105 - 1 - 0 - 0 - 0 - - - 7106 - 1 - 0 - 0 - 0 - - - 7107 - 1 - 0 - 0 - 0 - - - 7108 - 52 - 0 - 0 - 0 - - - 7109 - 52 - 0 - 0 - 0 - - - 7110 - 300 - 0 - 0 - 0 - - - 7111 - 300 - 0 - 0 - 0 - - - 7112 - 100 - 0 - 0 - 0 - - - 7113 - 100 - 0 - 0 - 0 - - - 7114 - 350 - 0 - 0 - 0 - - - 7115 - 350 - 0 - 0 - 0 - - - 7116 - 350 - 0 - 0 - 0 - - - 7117 - 350 - 0 - 0 - 0 - - - 7118 - 1 - 0 - 0 - 0 - - - 7119 - 1 - 0 - 0 - 0 - - - 7120 - 1 - 0 - 0 - 0 - - - 7121 - 1 - 0 - 0 - 0 - - - 7122 - 300 - 0 - 0 - 0 - - - 7123 - 300 - 0 - 0 - 0 - - - 7124 - 100 - 0 - 0 - 0 - - - 7125 - 100 - 0 - 0 - 0 - - - 7126 - 350 - 0 - 0 - 0 - - - 7127 - 350 - 0 - 0 - 0 - - - 7128 - 300 - 0 - 0 - 0 - - - 7129 - 300 - 0 - 0 - 0 - - - 7130 - 100 - 0 - 0 - 0 - - - 7131 - 100 - 0 - 0 - 0 - - - 7132 - 350 - 0 - 0 - 0 - - - 7133 - 350 - 0 - 0 - 0 - - - 7134 - 300 - 0 - 0 - 0 - - - 7135 - 300 - 0 - 0 - 0 - - - 7136 - 100 - 0 - 0 - 0 - - - 7137 - 100 - 0 - 0 - 0 - - - 7138 - 350 - 0 - 0 - 0 - - - 7139 - 350 - 0 - 0 - 0 - - - 7140 - 2560 - 0 - 0 - 0 - - - 7141 - 1040 - 0 - 0 - 0 - - - 7142 - 25600 - 0 - 0 - 0 - - - 7143 - 1 - 0 - 0 - 0 - - - 7144 - 1 - 0 - 0 - 0 - - - 7145 - 1 - 0 - 0 - 0 - - - 7146 - 1 - 0 - 0 - 0 - - - 7147 - 1 - 0 - 0 - 0 - - - 7148 - 1 - 0 - 0 - 0 - - - 7149 - 1 - 0 - 0 - 0 - - - 7150 - 1 - 0 - 0 - 0 - - - 7151 - 1 - 0 - 0 - 0 - - - 7152 - 1 - 0 - 0 - 0 - - - 7153 - 1 - 0 - 0 - 0 - - - 7154 - 1 - 0 - 0 - 0 - - - 7155 - 18 - 0 - 0 - 0 - - - 7156 - 1 - 0 - 0 - 0 - - - 7157 - 30 - 0 - 0 - 0 - - - 7158 - 220000 - 0 - 0 - 0 - - - 7159 - 200 - 0 - 0 - 0 - - - 7160 - 1 - 0 - 0 - 0 - - - 7161 - 200 - 0 - 0 - 0 - - - 7162 - 5 - 0 - 0 - 0 - - - 7163 - 5 - 0 - 0 - 0 - - - 7164 - 22 - 0 - 0 - 0 - - - 7165 - 22 - 0 - 0 - 0 - - - 7166 - 26 - 0 - 0 - 0 - - - 7167 - 26 - 0 - 0 - 0 - - - 7168 - 27 - 0 - 0 - 0 - - - 7169 - 27 - 0 - 0 - 0 - - - 7170 - 54 - 0 - 0 - 0 - - - 7171 - 54 - 0 - 0 - 0 - - - 7172 - 8 - 0 - 0 - 0 - - - 7173 - 8 - 0 - 0 - 0 - - - 7174 - 11 - 0 - 0 - 0 - - - 7175 - 11 - 0 - 0 - 0 - - - 7176 - 12 - 0 - 0 - 0 - - - 7177 - 12 - 0 - 0 - 0 - - - 7178 - 90 - 0 - 0 - 0 - - - 7179 - 90 - 0 - 0 - 0 - - - 7180 - 12 - 0 - 0 - 0 - - - 7181 - 12 - 0 - 0 - 0 - - - 7182 - 24 - 0 - 0 - 0 - - - 7183 - 24 - 0 - 0 - 0 - - - 7184 - 49 - 0 - 0 - 0 - - - 7185 - 49 - 0 - 0 - 0 - - - 7186 - 50 - 0 - 0 - 0 - - - 7187 - 50 - 0 - 0 - 0 - - - 7188 - 100 - 0 - 0 - 0 - - - 7189 - 100 - 0 - 0 - 0 - - - 7190 - 50 - 0 - 0 - 0 - - - 7191 - 50 - 0 - 0 - 0 - - - 7192 - 54 - 0 - 0 - 0 - - - 7193 - 54 - 0 - 0 - 0 - - - 7194 - 154 - 0 - 0 - 0 - - - 7195 - 154 - 0 - 0 - 0 - - - 7196 - 155 - 0 - 0 - 0 - - - 7197 - 155 - 0 - 0 - 0 - - - 7198 - 310 - 0 - 0 - 0 - - - 7199 - 310 - 0 - 0 - 0 - - - 7200 - 155 - 0 - 0 - 0 - - - 7201 - 155 - 0 - 0 - 0 - - - 7202 - 5 - 0 - 0 - 0 - - - 7203 - 5 - 0 - 0 - 0 - - - 7204 - 90 - 0 - 0 - 0 - - - 7205 - 90 - 0 - 0 - 0 - - - 7206 - 91 - 0 - 0 - 0 - - - 7207 - 91 - 0 - 0 - 0 - - - 7208 - 182 - 0 - 0 - 0 - - - 7209 - 182 - 0 - 0 - 0 - - - 7210 - 91 - 0 - 0 - 0 - - - 7211 - 91 - 0 - 0 - 0 - - - 7212 - 21 - 0 - 0 - 0 - - - 7213 - 21 - 0 - 0 - 0 - - - 7214 - 69 - 0 - 0 - 0 - - - 7215 - 69 - 0 - 0 - 0 - - - 7216 - 70 - 0 - 0 - 0 - - - 7217 - 70 - 0 - 0 - 0 - - - 7218 - 140 - 0 - 0 - 0 - - - 7219 - 140 - 0 - 0 - 0 - - - 7220 - 70 - 0 - 0 - 0 - - - 7221 - 70 - 0 - 0 - 0 - - - 7222 - 1 - 0 - 0 - 0 - - - 7223 - 20 - 0 - 0 - 0 - - - 7224 - 84 - 0 - 0 - 0 - - - 7225 - 64 - 0 - 0 - 0 - - - 7226 - 1 - 0 - 0 - 0 - - - 7227 - 1 - 0 - 0 - 0 - - - 7228 - 85 - 0 - 0 - 0 - - - 7229 - 85 - 0 - 0 - 0 - - - 7230 - 149 - 0 - 0 - 0 - - - 7231 - 1 - 0 - 0 - 0 - - - 7232 - 20 - 0 - 0 - 0 - - - 7233 - 84 - 0 - 0 - 0 - - - 7234 - 64 - 0 - 0 - 0 - - - 7235 - 149 - 0 - 0 - 0 - - - 7236 - 1 - 0 - 0 - 0 - - - 7237 - 50 - 0 - 0 - 0 - - - 7238 - 1 - 0 - 0 - 0 - - - 7239 - 1 - 0 - 0 - 0 - - - 7240 - 50 - 0 - 0 - 0 - - - 7241 - 1 - 0 - 0 - 0 - - - 7242 - 50 - 0 - 0 - 0 - - - 7243 - 1 - 0 - 0 - 0 - - - 7244 - 50 - 0 - 0 - 0 - - - 7245 - 1 - 0 - 0 - 0 - - - 7246 - 50 - 0 - 0 - 0 - - - 7247 - 1 - 0 - 0 - 0 - - - 7248 - 1 - 0 - 0 - 0 - - - 7249 - 1 - 0 - 0 - 0 - - - 7250 - 1 - 0 - 0 - 0 - - - 7251 - 1 - 0 - 0 - 0 - - - 7252 - 1 - 0 - 0 - 0 - - - 7253 - 1 - 0 - 0 - 0 - - - 7254 - 1 - 0 - 0 - 0 - - - 7255 - 1 - 0 - 0 - 0 - - - 7256 - 1 - 0 - 0 - 0 - - - 7257 - 50 - 0 - 0 - 0 - - - 7258 - 1 - 0 - 0 - 0 - - - 7259 - 50 - 0 - 0 - 0 - - - 7260 - 1 - 0 - 0 - 0 - - - 7261 - 50 - 0 - 0 - 0 - - - 7262 - 1 - 0 - 0 - 0 - - - 7263 - 50 - 0 - 0 - 0 - - - 7264 - 1 - 0 - 0 - 0 - - - 7265 - 50 - 0 - 0 - 0 - - - 7266 - 1 - 0 - 0 - 0 - - - 7267 - 50 - 0 - 0 - 0 - - - 7268 - 1 - 0 - 0 - 0 - - - 7269 - 1 - 0 - 0 - 0 - - - 7270 - 1 - 0 - 0 - 0 - - - 7271 - 1 - 0 - 0 - 0 - - - 7272 - 1 - 0 - 0 - 0 - - - 7273 - 1 - 0 - 0 - 0 - - - 7274 - 1 - 0 - 0 - 0 - - - 7275 - 1 - 0 - 0 - 0 - - - 7276 - 1 - 0 - 0 - 0 - - - 7277 - 1 - 0 - 0 - 0 - - - 7278 - 1 - 0 - 0 - 0 - - - 7279 - 1 - 0 - 0 - 0 - - - 7280 - 1 - 0 - 0 - 0 - - - 7281 - 1 - 0 - 0 - 0 - - - 7282 - 1 - 0 - 0 - 0 - - - 7283 - 1 - 0 - 0 - 0 - - - 7284 - 1 - 0 - 0 - 0 - - - 7285 - 1 - 0 - 0 - 0 - - - 7286 - 1 - 0 - 0 - 0 - - - 7287 - 50 - 0 - 0 - 0 - - - 7288 - 1 - 0 - 0 - 0 - - - 7289 - 50 - 0 - 0 - 0 - - - 7290 - 1 - 0 - 0 - 0 - - - 7291 - 50 - 0 - 0 - 0 - - - 7292 - 1 - 0 - 0 - 0 - - - 7293 - 50 - 0 - 0 - 0 - - - 7294 - 1 - 0 - 0 - 0 - - - 7295 - 50 - 0 - 0 - 0 - - - 7296 - 1 - 0 - 0 - 0 - - - 7297 - 1 - 0 - 0 - 0 - - - 7298 - 1 - 0 - 0 - 0 - - - 7299 - 1 - 0 - 0 - 0 - - - 7300 - 1 - 0 - 0 - 0 - - - 7301 - 1 - 0 - 0 - 0 - - - 7302 - 1 - 0 - 0 - 0 - - - 7303 - 1 - 0 - 0 - 0 - - - 7304 - 1 - 0 - 0 - 0 - - - 7305 - 1 - 0 - 0 - 0 - - - 7306 - 50 - 0 - 0 - 0 - - - 7307 - 1 - 0 - 0 - 0 - - - 7308 - 50 - 0 - 0 - 0 - - - 7309 - 1 - 0 - 0 - 0 - - - 7310 - 50 - 0 - 0 - 0 - - - 7311 - 1 - 0 - 0 - 0 - - - 7312 - 50 - 0 - 0 - 0 - - - 7313 - 1 - 0 - 0 - 0 - - - 7314 - 50 - 0 - 0 - 0 - - - 7315 - 1 - 0 - 0 - 0 - - - 7316 - 50 - 0 - 0 - 0 - - - 7317 - 1 - 0 - 0 - 0 - - - 7318 - 50 - 0 - 0 - 0 - - - 7319 - 225 - 0 - 0 - 0 - - - 7320 - 225 - 0 - 0 - 0 - - - 7321 - 225 - 0 - 0 - 0 - - - 7322 - 225 - 0 - 0 - 0 - - - 7323 - 225 - 0 - 0 - 0 - - - 7324 - 225 - 0 - 0 - 0 - - - 7325 - 225 - 0 - 0 - 0 - - - 7326 - 225 - 0 - 0 - 0 - - - 7327 - 225 - 0 - 0 - 0 - - - 7328 - 225 - 0 - 0 - 0 - - - 7329 - 15 - 0 - 0 - 0 - - - 7330 - 15 - 0 - 0 - 0 - - - 7331 - 15 - 0 - 0 - 0 - - - 7332 - 1632 - 0 - 0 - 0 - - - 7333 - 1632 - 0 - 0 - 0 - - - 7334 - 5440 - 0 - 0 - 0 - - - 7335 - 5440 - 0 - 0 - 0 - - - 7336 - 54400 - 0 - 0 - 0 - - - 7337 - 54400 - 0 - 0 - 0 - - - 7338 - 16325 - 0 - 0 - 0 - - - 7339 - 16325 - 0 - 0 - 0 - - - 7340 - 5440 - 0 - 0 - 0 - - - 7341 - 5440 - 0 - 0 - 0 - - - 7342 - 54400 - 0 - 0 - 0 - - - 7343 - 54400 - 0 - 0 - 0 - - - 7344 - 1632 - 0 - 0 - 0 - - - 7345 - 1632 - 0 - 0 - 0 - - - 7346 - 5440 - 0 - 0 - 0 - - - 7347 - 5440 - 0 - 0 - 0 - - - 7348 - 54400 - 0 - 0 - 0 - - - 7349 - 54400 - 0 - 0 - 0 - - - 7350 - 1632 - 0 - 0 - 0 - - - 7351 - 1632 - 0 - 0 - 0 - - - 7352 - 5440 - 0 - 0 - 0 - - - 7353 - 5440 - 0 - 0 - 0 - - - 7354 - 54400 - 0 - 0 - 0 - - - 7355 - 54400 - 0 - 0 - 0 - - - 7356 - 1632 - 0 - 0 - 0 - - - 7357 - 1632 - 0 - 0 - 0 - - - 7358 - 5440 - 0 - 0 - 0 - - - 7359 - 5440 - 0 - 0 - 0 - - - 7360 - 54400 - 0 - 0 - 0 - - - 7361 - 54400 - 0 - 0 - 0 - - - 7362 - 850 - 0 - 0 - 0 - - - 7363 - 850 - 0 - 0 - 0 - - - 7364 - 850 - 0 - 0 - 0 - - - 7365 - 850 - 0 - 0 - 0 - - - 7366 - 750 - 0 - 0 - 0 - - - 7367 - 750 - 0 - 0 - 0 - - - 7368 - 750 - 0 - 0 - 0 - - - 7369 - 750 - 0 - 0 - 0 - - - 7370 - 7800 - 0 - 0 - 0 - - - 7371 - 7800 - 0 - 0 - 0 - - - 7372 - 7800 - 0 - 0 - 0 - - - 7373 - 7800 - 0 - 0 - 0 - - - 7374 - 9360 - 0 - 0 - 0 - - - 7375 - 9360 - 0 - 0 - 0 - - - 7376 - 9360 - 0 - 0 - 0 - - - 7377 - 9360 - 0 - 0 - 0 - - - 7378 - 3900 - 0 - 0 - 0 - - - 7379 - 3900 - 0 - 0 - 0 - - - 7380 - 3900 - 0 - 0 - 0 - - - 7381 - 3900 - 0 - 0 - 0 - - - 7382 - 4320 - 0 - 0 - 0 - - - 7383 - 4320 - 0 - 0 - 0 - - - 7384 - 4320 - 0 - 0 - 0 - - - 7385 - 4320 - 0 - 0 - 0 - - - 7386 - 2 - 0 - 0 - 0 - - - 7387 - 2 - 0 - 0 - 0 - - - 7388 - 2 - 0 - 0 - 0 - - - 7389 - 2 - 0 - 0 - 0 - - - 7390 - 15 - 0 - 0 - 0 - - - 7391 - 15 - 0 - 0 - 0 - - - 7392 - 15 - 0 - 0 - 0 - - - 7393 - 15 - 0 - 0 - 0 - - - 7394 - 2 - 0 - 0 - 0 - - - 7395 - 2 - 0 - 0 - 0 - - - 7396 - 2 - 0 - 0 - 0 - - - 7397 - 2 - 0 - 0 - 0 - - - 7398 - 80000 - 0 - 0 - 0 - - - 7399 - 120000 - 0 - 0 - 0 - - - 7400 - 15000 - 0 - 0 - 0 - - - 7401 - 80000 - 0 - 0 - 0 - - - 7402 - 120000 - 0 - 0 - 0 - - - 7403 - 15000 - 0 - 0 - 0 - - - 7404 - 4 - 0 - 0 - 0 - - - 7405 - 4 - 0 - 0 - 0 - - - 7406 - 4 - 0 - 0 - 0 - - - 7407 - 220000 - 0 - 0 - 0 - - - 7408 - 1 - 0 - 0 - 0 - - - 7409 - 1 - 0 - 0 - 0 - - - 7410 - 1 - 0 - 0 - 0 - - - 7411 - 1 - 0 - 0 - 0 - - - 7412 - 1 - 0 - 0 - 0 - - - 7413 - 1 - 0 - 0 - 0 - - - 7414 - 1 - 0 - 0 - 0 - - - 7415 - 1 - 0 - 0 - 0 - - - 7416 - 600 - 0 - 0 - 0 - - - 7417 - 600 - 0 - 0 - 0 - - - 7418 - 400 - 0 - 0 - 0 - - - 7419 - 400 - 0 - 0 - 0 - - - 7420 - 1 - 0 - 0 - 0 - - - 7421 - 300 - 0 - 0 - 0 - - - 7422 - 1 - 0 - 0 - 0 - - - 7423 - 1 - 0 - 0 - 0 - - - 7424 - 1 - 0 - 0 - 0 - - - 7425 - 1 - 0 - 0 - 0 - - - 7426 - 1 - 0 - 0 - 0 - - - 7427 - 1 - 0 - 0 - 0 - - - 7428 - 1 - 0 - 0 - 0 - - - 7429 - 1 - 0 - 0 - 0 - - - 7430 - 1 - 0 - 0 - 0 - - - 7431 - 1 - 0 - 0 - 0 - - - 7432 - 10 - 0 - 0 - 0 - - - 7433 - 35 - 0 - 0 - 0 - - - 7434 - 35 - 0 - 0 - 0 - - - 7435 - 50 - 0 - 0 - 0 - - - 7436 - 50 - 0 - 0 - 0 - - - 7437 - 325 - 0 - 0 - 0 - - - 7438 - 325 - 0 - 0 - 0 - - - 7439 - 1920 - 0 - 0 - 0 - - - 7440 - 1920 - 0 - 0 - 0 - - - 7441 - 1660 - 0 - 0 - 0 - - - 7442 - 1660 - 0 - 0 - 0 - - - 7443 - 3200 - 0 - 0 - 0 - - - 7444 - 3200 - 0 - 0 - 0 - - - 7445 - 14400 - 0 - 0 - 0 - - - 7446 - 14400 - 0 - 0 - 0 - - - 7447 - 8000 - 0 - 0 - 0 - - - 7448 - 8000 - 0 - 0 - 0 - - - 7449 - 41500 - 0 - 0 - 0 - - - 7450 - 41500 - 0 - 0 - 0 - - - 7451 - 25600 - 0 - 0 - 0 - - - 7452 - 25600 - 0 - 0 - 0 - - - 7453 - 50 - 0 - 0 - 0 - - - 7454 - 100 - 0 - 0 - 0 - - - 7455 - 250 - 0 - 0 - 0 - - - 7456 - 500 - 0 - 0 - 0 - - - 7457 - 1000 - 0 - 0 - 0 - - - 7458 - 1500 - 0 - 0 - 0 - - - 7459 - 2500 - 0 - 0 - 0 - - - 7460 - 5000 - 0 - 0 - 0 - - - 7461 - 100000 - 0 - 0 - 0 - - - 7462 - 100000 - 0 - 0 - 0 - - - 7463 - 2 - 0 - 0 - 0 - - - 7464 - 1 - 0 - 0 - 0 - - - 7465 - 1 - 0 - 0 - 0 - - - 7466 - 1 - 0 - 0 - 0 - - - 7467 - 1 - 0 - 0 - 0 - - - 7468 - 10 - 0 - 0 - 0 - - - 7469 - 1 - 0 - 0 - 0 - - - 7470 - 1 - 0 - 0 - 0 - - - 7471 - 1 - 0 - 0 - 0 - - - 7472 - 1 - 0 - 0 - 0 - - - 7473 - 1 - 0 - 0 - 0 - - - 7474 - 1 - 0 - 0 - 0 - - - 7475 - 1 - 0 - 0 - 0 - - - 7476 - 1 - 0 - 0 - 0 - - - 7477 - 1 - 0 - 0 - 0 - - - 7478 - 1 - 0 - 0 - 0 - - - 7479 - 20 - 0 - 0 - 0 - - - 7480 - 1 - 0 - 0 - 0 - - - 7481 - 1 - 0 - 0 - 0 - - - 7482 - 1 - 0 - 0 - 0 - - - 7483 - 1 - 0 - 0 - 0 - - - 7484 - 1 - 0 - 0 - 0 - - - 7485 - 1 - 0 - 0 - 0 - - - 7486 - 1 - 0 - 0 - 0 - - - 7487 - 1 - 0 - 0 - 0 - - - 7488 - 1 - 0 - 0 - 0 - - - 7489 - 1 - 0 - 0 - 0 - - - 7490 - 1 - 0 - 0 - 0 - - - 7491 - 1 - 0 - 0 - 0 - - - 7492 - 1 - 0 - 0 - 0 - - - 7493 - 1 - 0 - 0 - 0 - - - 7494 - 1 - 0 - 0 - 0 - - - 7495 - 1 - 0 - 0 - 0 - - - 7496 - 1 - 0 - 0 - 0 - - - 7497 - 30 - 0 - 0 - 0 - - - 7498 - 1 - 0 - 0 - 0 - - - 7499 - 1 - 0 - 0 - 0 - - - 7500 - 1 - 0 - 0 - 0 - - - 7501 - 1 - 0 - 0 - 0 - - - 7502 - 1 - 0 - 0 - 0 - - - 7503 - 1 - 0 - 0 - 0 - - - 7504 - 1 - 0 - 0 - 0 - - - 7505 - 1 - 0 - 0 - 0 - - - 7506 - 1 - 0 - 0 - 0 - - - 7507 - 1 - 0 - 0 - 0 - - - 7508 - 2 - 0 - 0 - 0 - - - 7509 - 1 - 0 - 0 - 0 - - - 7510 - 1 - 0 - 0 - 0 - - - 7511 - 10 - 0 - 0 - 0 - - - 7512 - 6 - 0 - 0 - 0 - - - 7513 - 3 - 0 - 0 - 0 - - - 7514 - 2 - 0 - 0 - 0 - - - 7515 - 1 - 0 - 0 - 0 - - - 7516 - 1 - 0 - 0 - 0 - - - 7517 - 1 - 0 - 0 - 0 - - - 7518 - 1 - 0 - 0 - 0 - - - 7519 - 1 - 0 - 0 - 0 - - - 7520 - 1 - 0 - 0 - 0 - - - 7521 - 50 - 0 - 0 - 0 - - - 7522 - 50 - 0 - 0 - 0 - - - 7523 - 40 - 0 - 0 - 0 - - - 7524 - 30 - 0 - 0 - 0 - - - 7525 - 20 - 0 - 0 - 0 - - - 7526 - 10 - 0 - 0 - 0 - - - 7527 - 1 - 0 - 0 - 0 - - - 7528 - 1 - 0 - 0 - 0 - - - 7529 - 100 - 0 - 0 - 0 - - - 7530 - 100 - 0 - 0 - 0 - - - 7531 - 1 - 0 - 0 - 0 - - - 7532 - 1 - 0 - 0 - 0 - - - 7533 - 1 - 0 - 0 - 0 - - - 7534 - 1 - 0 - 0 - 0 - - - 7535 - 1 - 0 - 0 - 0 - - - 7536 - 1 - 0 - 0 - 0 - - - 7537 - 1 - 0 - 0 - 0 - - - 7538 - 1 - 0 - 0 - 0 - - - 7539 - 1 - 0 - 0 - 0 - - - 7540 - 1 - 0 - 0 - 0 - - - 7541 - 1 - 0 - 0 - 0 - - - 7542 - 50 - 0 - 0 - 0 - - - 7543 - 20 - 0 - 0 - 0 - - - 7544 - 4 - 0 - 0 - 0 - - - 7545 - 6 - 0 - 0 - 0 - - - 7546 - 10 - 0 - 0 - 0 - - - 7547 - 5 - 0 - 0 - 0 - - - 7548 - 1 - 0 - 0 - 0 - - - 7549 - 1 - 0 - 0 - 0 - - - 7550 - 1 - 0 - 0 - 0 - - - 7551 - 1 - 0 - 0 - 0 - - - 7552 - 1 - 0 - 0 - 0 - - - 7553 - 1 - 0 - 0 - 0 - - - 7554 - 1 - 0 - 0 - 0 - - - 7555 - 1 - 0 - 0 - 0 - - - 7556 - 1 - 0 - 0 - 0 - - - 7557 - 1 - 0 - 0 - 0 - - - 7558 - 1 - 0 - 0 - 0 - - - 7559 - 1 - 0 - 0 - 0 - - - 7560 - 1 - 0 - 0 - 0 - - - 7561 - 1 - 0 - 0 - 0 - - - 7562 - 1 - 0 - 0 - 0 - - - 7563 - 1 - 0 - 0 - 0 - - - 7564 - 1 - 0 - 0 - 0 - - - 7565 - 1 - 0 - 0 - 0 - - - 7566 - 100 - 0 - 0 - 0 - - - 7567 - 100 - 0 - 0 - 0 - - - 7568 - 150 - 0 - 0 - 0 - - - 7569 - 150 - 0 - 0 - 0 - - - 7570 - 1 - 0 - 0 - 0 - - - 7571 - 1 - 0 - 0 - 0 - - - 7572 - 5 - 0 - 0 - 0 - - - 7573 - 5 - 0 - 0 - 0 - - - 7574 - 5 - 0 - 0 - 0 - - - 7575 - 5 - 0 - 0 - 0 - - - 7576 - 1 - 0 - 0 - 0 - - - 7577 - 1 - 0 - 0 - 0 - - - 7578 - 1 - 0 - 0 - 0 - - - 7579 - 1 - 0 - 0 - 0 - - - 7580 - 1 - 0 - 0 - 0 - - - 7581 - 1 - 0 - 0 - 0 - - - 7582 - 1 - 0 - 0 - 0 - - - 7583 - 1 - 0 - 0 - 0 - - - 7584 - 1 - 0 - 0 - 0 - - - 7585 - 1 - 0 - 0 - 0 - - - 7586 - 1 - 0 - 0 - 0 - - - 7587 - 1 - 0 - 0 - 0 - - - 7588 - 1 - 0 - 0 - 0 - - - 7589 - 1 - 0 - 0 - 0 - - - 7590 - 1 - 0 - 0 - 0 - - - 7591 - 1 - 0 - 0 - 0 - - - 7592 - 1 - 0 - 0 - 0 - - - 7593 - 1 - 0 - 0 - 0 - - - 7594 - 1 - 0 - 0 - 0 - - - 7595 - 1 - 0 - 0 - 0 - - - 7596 - 1 - 0 - 0 - 0 - - - 7597 - 1 - 0 - 0 - 0 - - - 7598 - 1 - 0 - 0 - 0 - - - 7599 - 1 - 0 - 0 - 0 - - - 7600 - 1 - 0 - 0 - 0 - - - 7601 - 1 - 0 - 0 - 0 - - - 7602 - 1 - 0 - 0 - 0 - - - 7603 - 1 - 0 - 0 - 0 - - - 7604 - 1 - 0 - 0 - 0 - - - 7605 - 1 - 0 - 0 - 0 - - - 7606 - 1 - 0 - 0 - 0 - - - 7607 - 1 - 0 - 0 - 0 - - - 7608 - 1 - 0 - 0 - 0 - - - 7609 - 1 - 0 - 0 - 0 - - - 7610 - 1 - 0 - 0 - 0 - - - 7611 - 1 - 0 - 0 - 0 - - - 7612 - 1 - 0 - 0 - 0 - - - 7613 - 1 - 0 - 0 - 0 - - - 7614 - 1 - 0 - 0 - 0 - - - 7615 - 1 - 0 - 0 - 0 - - - 7616 - 1 - 0 - 0 - 0 - - - 7617 - 1 - 0 - 0 - 0 - - - 7618 - 1 - 0 - 0 - 0 - - - 7619 - 1 - 0 - 0 - 0 - - - 7620 - 1 - 0 - 0 - 0 - - - 7621 - 1 - 0 - 0 - 0 - - - 7622 - 1 - 0 - 0 - 0 - - - 7623 - 1 - 0 - 0 - 0 - - - 7624 - 1 - 0 - 0 - 0 - - - 7625 - 1 - 0 - 0 - 0 - - - 7626 - 1 - 0 - 0 - 0 - - - 7627 - 1 - 0 - 0 - 0 - - - 7628 - 1 - 0 - 0 - 0 - - - 7629 - 1 - 0 - 0 - 0 - - - 7630 - 1 - 0 - 0 - 0 - - - 7631 - 1 - 0 - 0 - 0 - - - 7632 - 1 - 0 - 0 - 0 - - - 7633 - 1 - 0 - 0 - 0 - - - 7634 - 1 - 0 - 0 - 0 - - - 7635 - 1 - 0 - 0 - 0 - - - 7636 - 1 - 0 - 0 - 0 - - - 7637 - 1 - 0 - 0 - 0 - - - 7638 - 1 - 0 - 0 - 0 - - - 7639 - 1000 - 0 - 0 - 0 - - - 7640 - 1000 - 0 - 0 - 0 - - - 7641 - 1000 - 0 - 0 - 0 - - - 7642 - 1000 - 0 - 0 - 0 - - - 7643 - 1000 - 0 - 0 - 0 - - - 7644 - 1000 - 0 - 0 - 0 - - - 7645 - 1000 - 0 - 0 - 0 - - - 7646 - 1000 - 0 - 0 - 0 - - - 7647 - 1000 - 0 - 0 - 0 - - - 7648 - 1000 - 0 - 0 - 0 - - - 7649 - 100 - 0 - 0 - 0 - - - 7650 - 200 - 0 - 0 - 0 - - - 7651 - 200 - 0 - 0 - 0 - - - 7652 - 110 - 0 - 0 - 0 - - - 7653 - 110 - 0 - 0 - 0 - - - 7654 - 110 - 0 - 0 - 0 - - - 7655 - 110 - 0 - 0 - 0 - - - 7656 - 110 - 0 - 0 - 0 - - - 7657 - 110 - 0 - 0 - 0 - - - 7658 - 110 - 0 - 0 - 0 - - - 7659 - 110 - 0 - 0 - 0 - - - 7660 - 200 - 0 - 0 - 0 - - - 7661 - 200 - 0 - 0 - 0 - - - 7662 - 200 - 0 - 0 - 0 - - - 7663 - 200 - 0 - 0 - 0 - - - 7664 - 200 - 0 - 0 - 0 - - - 7665 - 200 - 0 - 0 - 0 - - - 7666 - 200 - 0 - 0 - 0 - - - 7667 - 200 - 0 - 0 - 0 - - - 7668 - 3000 - 0 - 0 - 0 - - - 7669 - 3000 - 0 - 0 - 0 - - - 7670 - 1 - 0 - 0 - 0 - - - 7671 - 1 - 0 - 0 - 0 - - - 7672 - 1 - 0 - 0 - 0 - - - 7673 - 1 - 0 - 0 - 0 - - - 7674 - 1 - 0 - 0 - 0 - - - 7675 - 1 - 0 - 0 - 0 - - - 7676 - 1 - 0 - 0 - 0 - - - 7677 - 1 - 0 - 0 - 0 - - - 7678 - 1 - 0 - 0 - 0 - - - 7679 - 1 - 0 - 0 - 0 - - - 7680 - 1 - 0 - 0 - 0 - - - 7681 - 1 - 0 - 0 - 0 - - - 7682 - 1 - 0 - 0 - 0 - - - 7683 - 1 - 0 - 0 - 0 - - - 7684 - 1 - 0 - 0 - 0 - - - 7685 - 1 - 0 - 0 - 0 - - - 7686 - 1 - 0 - 0 - 0 - - - 7687 - 1 - 0 - 0 - 0 - - - 7688 - 1 - 0 - 0 - 0 - - - 7689 - 1 - 0 - 0 - 0 - - - 7690 - 1 - 0 - 0 - 0 - - - 7691 - 1 - 0 - 0 - 0 - - - 7692 - 1 - 0 - 0 - 0 - - - 7693 - 1 - 0 - 0 - 0 - - - 7694 - 1 - 0 - 0 - 0 - - - 7695 - 1 - 0 - 0 - 0 - - - 7696 - 1 - 0 - 0 - 0 - - - 7697 - 1 - 0 - 0 - 0 - - - 7698 - 1 - 0 - 0 - 0 - - - 7699 - 1 - 0 - 0 - 0 - - - 7700 - 1 - 0 - 0 - 0 - - - 7701 - 1 - 0 - 0 - 0 - - - 7702 - 1 - 0 - 0 - 0 - - - 7703 - 1 - 0 - 0 - 0 - - - 7704 - 1 - 0 - 0 - 0 - - - 7705 - 1 - 0 - 0 - 0 - - - 7706 - 1 - 0 - 0 - 0 - - - 7707 - 1 - 0 - 0 - 0 - - - 7708 - 1 - 0 - 0 - 0 - - - 7709 - 1 - 0 - 0 - 0 - - - 7710 - 1 - 0 - 0 - 0 - - - 7711 - 1 - 0 - 0 - 0 - - - 7712 - 1 - 0 - 0 - 0 - - - 7713 - 1 - 0 - 0 - 0 - - - 7714 - 1 - 0 - 0 - 0 - - - 7715 - 1 - 0 - 0 - 0 - - - 7716 - 1 - 0 - 0 - 0 - - - 7717 - 1 - 0 - 0 - 0 - - - 7718 - 1 - 0 - 0 - 0 - - - 7719 - 1 - 0 - 0 - 0 - - - 7720 - 1 - 0 - 0 - 0 - - - 7721 - 1 - 0 - 0 - 0 - - - 7722 - 1 - 0 - 0 - 0 - - - 7723 - 1 - 0 - 0 - 0 - - - 7724 - 1 - 0 - 0 - 0 - - - 7725 - 1 - 0 - 0 - 0 - - - 7726 - 1 - 0 - 0 - 0 - - - 7727 - 1 - 0 - 0 - 0 - - - 7728 - 2 - 0 - 0 - 0 - - - 7729 - 2 - 0 - 0 - 0 - - - 7730 - 10 - 0 - 0 - 0 - - - 7731 - 10 - 0 - 0 - 0 - - - 7732 - 2 - 0 - 0 - 0 - - - 7733 - 10 - 0 - 0 - 0 - - - 7734 - 10 - 0 - 0 - 0 - - - 7735 - 2 - 0 - 0 - 0 - - - 7736 - 10 - 0 - 0 - 0 - - - 7737 - 10 - 0 - 0 - 0 - - - 7738 - 1 - 0 - 0 - 0 - - - 7739 - 1 - 0 - 0 - 0 - - - 7740 - 2 - 0 - 0 - 0 - - - 7741 - 2 - 0 - 0 - 0 - - - 7742 - 2 - 0 - 0 - 0 - - - 7743 - 2 - 0 - 0 - 0 - - - 7744 - 2 - 0 - 0 - 0 - - - 7745 - 2 - 0 - 0 - 0 - - - 7746 - 2 - 0 - 0 - 0 - - - 7747 - 2 - 0 - 0 - 0 - - - 7748 - 2 - 0 - 0 - 0 - - - 7749 - 2 - 0 - 0 - 0 - - - 7750 - 5 - 0 - 0 - 0 - - - 7751 - 5 - 0 - 0 - 0 - - - 7752 - 2 - 0 - 0 - 0 - - - 7753 - 2 - 0 - 0 - 0 - - - 7754 - 2 - 0 - 0 - 0 - - - 7755 - 2 - 0 - 0 - 0 - - - 7756 - 1 - 0 - 0 - 0 - - - 7757 - 1 - 0 - 0 - 0 - - - 7758 - 1 - 0 - 0 - 0 - - - 7759 - 10 - 0 - 0 - 0 - - - 7760 - 10 - 0 - 0 - 0 - - - 7761 - 10 - 0 - 0 - 0 - - - 7762 - 10 - 0 - 0 - 0 - - - 7763 - 10 - 0 - 0 - 0 - - - 7764 - 10 - 0 - 0 - 0 - - - 7765 - 10 - 0 - 0 - 0 - - - 7766 - 10 - 0 - 0 - 0 - - - 7767 - 10 - 0 - 0 - 0 - - - 7768 - 10 - 0 - 0 - 0 - - - 7769 - 10 - 0 - 0 - 0 - - - 7770 - 10 - 0 - 0 - 0 - - - 7771 - 10 - 0 - 0 - 0 - - - 7772 - 10 - 0 - 0 - 0 - - - 7773 - 1 - 0 - 0 - 0 - - - 7774 - 1 - 0 - 0 - 0 - - - 7775 - 1 - 0 - 0 - 0 - - - 7776 - 1 - 0 - 0 - 0 - - - 7777 - 1 - 0 - 0 - 0 - - - 7778 - 1 - 0 - 0 - 0 - - - 7779 - 5000 - 0 - 0 - 0 - - - 7780 - 5000 - 0 - 0 - 0 - - - 7781 - 5000 - 0 - 0 - 0 - - - 7782 - 5000 - 0 - 0 - 0 - - - 7783 - 5000 - 0 - 0 - 0 - - - 7784 - 5000 - 0 - 0 - 0 - - - 7785 - 5000 - 0 - 0 - 0 - - - 7786 - 5000 - 0 - 0 - 0 - - - 7787 - 5000 - 0 - 0 - 0 - - - 7788 - 5000 - 0 - 0 - 0 - - - 7789 - 5000 - 0 - 0 - 0 - - - 7790 - 5000 - 0 - 0 - 0 - - - 7791 - 5000 - 0 - 0 - 0 - - - 7792 - 5000 - 0 - 0 - 0 - - - 7793 - 5000 - 0 - 0 - 0 - - - 7794 - 5000 - 0 - 0 - 0 - - - 7795 - 5000 - 0 - 0 - 0 - - - 7796 - 5000 - 0 - 0 - 0 - - - 7797 - 5000 - 0 - 0 - 0 - - - 7798 - 5000 - 0 - 0 - 0 - - - 7799 - 5000 - 0 - 0 - 0 - - - 7800 - 1 - 0 - 0 - 0 - - - 7801 - 35 - 0 - 0 - 0 - - - 7802 - 35 - 0 - 0 - 0 - - - 7803 - 1 - 0 - 0 - 0 - - - 7804 - 625 - 0 - 0 - 0 - - - 7805 - 625 - 0 - 0 - 0 - - - 7806 - 26 - 0 - 0 - 0 - - - 7807 - 182 - 0 - 0 - 0 - - - 7808 - 18 - 0 - 0 - 0 - - - 7809 - 26 - 0 - 0 - 0 - - - 7810 - 1 - 0 - 0 - 0 - - - 7811 - 1 - 0 - 0 - 0 - - - 7812 - 1 - 0 - 0 - 0 - - - 7813 - 1 - 0 - 0 - 0 - - - 7814 - 1 - 0 - 0 - 0 - - - 7815 - 1 - 0 - 0 - 0 - - - 7816 - 1 - 0 - 0 - 0 - - - 7817 - 1 - 0 - 0 - 0 - - - 7818 - 1 - 0 - 0 - 0 - - - 7819 - 1 - 0 - 0 - 0 - - - 7820 - 1 - 0 - 0 - 0 - - - 7821 - 1 - 0 - 0 - 0 - - - 7822 - 1 - 0 - 0 - 0 - - - 7823 - 1 - 0 - 0 - 0 - - - 7824 - 1 - 0 - 0 - 0 - - - 7825 - 1 - 0 - 0 - 0 - - - 7826 - 1 - 0 - 0 - 0 - - - 7827 - 1 - 0 - 0 - 0 - - - 7828 - 1 - 0 - 0 - 0 - - - 7829 - 1 - 0 - 0 - 0 - - - 7830 - 1 - 0 - 0 - 0 - - - 7831 - 1 - 0 - 0 - 0 - - - 7832 - 1 - 0 - 0 - 0 - - - 7833 - 1 - 0 - 0 - 0 - - - 7834 - 1 - 0 - 0 - 0 - - - 7835 - 1 - 0 - 0 - 0 - - - 7836 - 1 - 0 - 0 - 0 - - - 7837 - 1 - 0 - 0 - 0 - - - 7838 - 1 - 0 - 0 - 0 - - - 7839 - 1 - 0 - 0 - 0 - - - 7840 - 1 - 0 - 0 - 0 - - - 7841 - 1 - 0 - 0 - 0 - - - 7842 - 1 - 0 - 0 - 0 - - - 7843 - 1 - 0 - 0 - 0 - - - 7844 - 1 - 0 - 0 - 0 - - - 7845 - 1 - 0 - 0 - 0 - - - 7846 - 1 - 0 - 0 - 0 - - - 7847 - 1 - 0 - 0 - 0 - - - 7848 - 1 - 0 - 0 - 0 - - - 7849 - 1 - 0 - 0 - 0 - - - 7850 - 1 - 0 - 0 - 0 - - - 7851 - 1 - 0 - 0 - 0 - - - 7852 - 1 - 0 - 0 - 0 - - - 7853 - 1 - 0 - 0 - 0 - - - 7854 - 1 - 0 - 0 - 0 - - - 7855 - 1 - 0 - 0 - 0 - - - 7856 - 1 - 0 - 0 - 0 - - - 7857 - 1 - 0 - 0 - 0 - - - 7858 - 1 - 0 - 0 - 0 - - - 7859 - 1 - 0 - 0 - 0 - - - 7860 - 1 - 0 - 0 - 0 - - - 7861 - 1 - 0 - 0 - 0 - - - 7862 - 1 - 0 - 0 - 0 - - - 7863 - 1 - 0 - 0 - 0 - - - 7864 - 1 - 0 - 0 - 0 - - - 7865 - 1 - 0 - 0 - 0 - - - 7866 - 1 - 0 - 0 - 0 - - - 7867 - 1 - 0 - 0 - 0 - - - 7868 - 1 - 0 - 0 - 0 - - - 7869 - 1 - 0 - 0 - 0 - - - 7870 - 1 - 0 - 0 - 0 - - - 7871 - 1 - 0 - 0 - 0 - - - 7872 - 1 - 0 - 0 - 0 - - - 7873 - 1 - 0 - 0 - 0 - - - 7874 - 1 - 0 - 0 - 0 - - - 7875 - 1 - 0 - 0 - 0 - - - 7876 - 1 - 0 - 0 - 0 - - - 7877 - 1 - 0 - 0 - 0 - - - 7878 - 1 - 0 - 0 - 0 - - - 7879 - 1 - 0 - 0 - 0 - - - 7880 - 1 - 0 - 0 - 0 - - - 7881 - 1 - 0 - 0 - 0 - - - 7882 - 1 - 0 - 0 - 0 - - - 7883 - 1 - 0 - 0 - 0 - - - 7884 - 1 - 0 - 0 - 0 - - - 7885 - 1 - 0 - 0 - 0 - - - 7886 - 1 - 0 - 0 - 0 - - - 7887 - 1 - 0 - 0 - 0 - - - 7888 - 1 - 0 - 0 - 0 - - - 7889 - 1 - 0 - 0 - 0 - - - 7890 - 1 - 0 - 0 - 0 - - - 7891 - 1 - 0 - 0 - 0 - - - 7892 - 1 - 0 - 0 - 0 - - - 7893 - 1 - 0 - 0 - 0 - - - 7894 - 1 - 0 - 0 - 0 - - - 7895 - 1 - 0 - 0 - 0 - - - 7896 - 1 - 0 - 0 - 0 - - - 7897 - 1 - 0 - 0 - 0 - - - 7898 - 1 - 0 - 0 - 0 - - - 7899 - 1 - 0 - 0 - 0 - - - 7900 - 1 - 0 - 0 - 0 - - - 7901 - 1 - 0 - 0 - 0 - - - 7902 - 1 - 0 - 0 - 0 - - - 7903 - 1 - 0 - 0 - 0 - - - 7904 - 1 - 0 - 0 - 0 - - - 7905 - 1 - 0 - 0 - 0 - - - 7906 - 1 - 0 - 0 - 0 - - - 7907 - 1 - 0 - 0 - 0 - - - 7908 - 1 - 0 - 0 - 0 - - - 7909 - 1 - 0 - 0 - 0 - - - 7910 - 1 - 0 - 0 - 0 - - - 7911 - 1 - 0 - 0 - 0 - - - 7912 - 1 - 0 - 0 - 0 - - - 7913 - 1 - 0 - 0 - 0 - - - 7914 - 1 - 0 - 0 - 0 - - - 7915 - 1 - 0 - 0 - 0 - - - 7916 - 1 - 0 - 0 - 0 - - - 7917 - 1 - 0 - 0 - 0 - - - 7918 - 1 - 0 - 0 - 0 - - - 7919 - 500 - 0 - 0 - 0 - - - 7920 - 500 - 0 - 0 - 0 - - - 7921 - 1 - 0 - 0 - 0 - - - 7922 - 1 - 0 - 0 - 0 - - - 7923 - 1 - 0 - 0 - 0 - - - 7924 - 1 - 0 - 0 - 0 - - - 7925 - 1 - 0 - 0 - 0 - - - 7926 - 1 - 0 - 0 - 0 - - - 7927 - 1 - 0 - 0 - 0 - - - 7928 - 1 - 0 - 0 - 0 - - - 7929 - 1 - 0 - 0 - 0 - - - 7930 - 1 - 0 - 0 - 0 - - - 7931 - 1 - 0 - 0 - 0 - - - 7932 - 1 - 0 - 0 - 0 - - - 7933 - 1 - 0 - 0 - 0 - - - 7934 - 390 - 0 - 0 - 0 - - - 7935 - 390 - 0 - 0 - 0 - - - 7936 - 4 - 0 - 0 - 0 - - - 7937 - 4 - 0 - 0 - 0 - - - 7938 - 4 - 0 - 0 - 0 - - - 7939 - 1000 - 0 - 0 - 0 - - - 7940 - 1000 - 0 - 0 - 0 - - - 7941 - 30 - 0 - 0 - 0 - - - 7942 - 10 - 0 - 0 - 0 - - - 7943 - 10 - 0 - 0 - 0 - - - 7944 - 230 - 0 - 0 - 0 - - - 7945 - 230 - 0 - 0 - 0 - - - 7946 - 230 - 0 - 0 - 0 - - - 7947 - 230 - 0 - 0 - 0 - - - 7948 - 3 - 0 - 0 - 0 - - - 7949 - 3 - 0 - 0 - 0 - - - 7950 - 30 - 0 - 0 - 0 - - - 7951 - 1 - 0 - 0 - 0 - - - 7952 - 1 - 0 - 0 - 0 - - - 7953 - 1 - 0 - 0 - 0 - - - 7954 - 1 - 0 - 0 - 0 - - - 7955 - 1 - 0 - 0 - 0 - - - 7956 - 50 - 0 - 0 - 0 - - - 7957 - 1 - 0 - 0 - 0 - - - 7958 - 10 - 0 - 0 - 0 - - - 7959 - 10 - 0 - 0 - 0 - - - 7960 - 10 - 0 - 0 - 0 - - - 7961 - 10 - 0 - 0 - 0 - - - 7962 - 10 - 0 - 0 - 0 - - - 7963 - 10 - 0 - 0 - 0 - - - 7964 - 10 - 0 - 0 - 0 - - - 7965 - 10 - 0 - 0 - 0 - - - 7966 - 10 - 0 - 0 - 0 - - - 7967 - 5 - 0 - 0 - 0 - - - 7968 - 10 - 0 - 0 - 0 - - - 7969 - 5 - 0 - 0 - 0 - - - 7970 - 5 - 0 - 0 - 0 - - - 7971 - 5 - 0 - 0 - 0 - - - 7972 - 5 - 0 - 0 - 0 - - - 7973 - 5 - 0 - 0 - 0 - - - 7974 - 10 - 0 - 0 - 0 - - - 7975 - 1000 - 0 - 0 - 0 - - - 7976 - 2000 - 0 - 0 - 0 - - - 7977 - 4000 - 0 - 0 - 0 - - - 7978 - 6000 - 0 - 0 - 0 - - - 7979 - 12000 - 0 - 0 - 0 - - - 7980 - 50000 - 0 - 0 - 0 - - - 7981 - 50000 - 0 - 0 - 0 - - - 7982 - 1000 - 0 - 0 - 0 - - - 7983 - 2000 - 0 - 0 - 0 - - - 7984 - 4000 - 0 - 0 - 0 - - - 7985 - 6000 - 0 - 0 - 0 - - - 7986 - 12000 - 0 - 0 - 0 - - - 7987 - 50000 - 0 - 0 - 0 - - - 7988 - 50000 - 0 - 0 - 0 - - - 7989 - 1000 - 0 - 0 - 0 - - - 7990 - 1000 - 0 - 0 - 0 - - - 7991 - 2500 - 0 - 0 - 0 - - - 7992 - 2500 - 0 - 0 - 0 - - - 7993 - 5000 - 0 - 0 - 0 - - - 7994 - 5000 - 0 - 0 - 0 - - - 7995 - 1000 - 0 - 0 - 0 - - - 7996 - 1000 - 0 - 0 - 0 - - - 7997 - 1000 - 0 - 0 - 0 - - - 7998 - 1000 - 0 - 0 - 0 - - - 7999 - 2000 - 0 - 0 - 0 - - - 8000 - 2000 - 0 - 0 - 0 - - - 8001 - 2000 - 0 - 0 - 0 - - - 8002 - 2000 - 0 - 0 - 0 - - - 8003 - 2000 - 0 - 0 - 0 - - - 8004 - 1000 - 0 - 0 - 0 - - - 8005 - 1000 - 0 - 0 - 0 - - - 8006 - 1000 - 0 - 0 - 0 - - - 8007 - 1 - 0 - 0 - 0 - - - 8008 - 1 - 0 - 0 - 0 - - - 8009 - 1 - 0 - 0 - 0 - - - 8010 - 1 - 0 - 0 - 0 - - - 8011 - 1 - 0 - 0 - 0 - - - 8012 - 1 - 0 - 0 - 0 - - - 8013 - 1 - 0 - 0 - 0 - - - 8014 - 1 - 0 - 0 - 0 - - - 8015 - 1 - 0 - 0 - 0 - - - 8016 - 1 - 0 - 0 - 0 - - - 8017 - 1 - 0 - 0 - 0 - - - 8018 - 1 - 0 - 0 - 0 - - - 8019 - 1 - 0 - 0 - 0 - - - 8020 - 1 - 0 - 0 - 0 - - - 8021 - 1 - 0 - 0 - 0 - - - 8022 - 1 - 0 - 0 - 0 - - - 8023 - 1 - 0 - 0 - 0 - - - 8024 - 1 - 0 - 0 - 0 - - - 8025 - 1 - 0 - 0 - 0 - - - 8026 - 1 - 0 - 0 - 0 - - - 8027 - 1 - 0 - 0 - 0 - - - 8028 - 1 - 0 - 0 - 0 - - - 8029 - 1 - 0 - 0 - 0 - - - 8030 - 1 - 0 - 0 - 0 - - - 8031 - 1 - 0 - 0 - 0 - - - 8032 - 1 - 0 - 0 - 0 - - - 8033 - 1 - 0 - 0 - 0 - - - 8034 - 1 - 0 - 0 - 0 - - - 8035 - 1 - 0 - 0 - 0 - - - 8036 - 1 - 0 - 0 - 0 - - - 8037 - 1 - 0 - 0 - 0 - - - 8038 - 1 - 0 - 0 - 0 - - - 8039 - 1 - 0 - 0 - 0 - - - 8040 - 1 - 0 - 0 - 0 - - - 8041 - 1 - 0 - 0 - 0 - - - 8042 - 1 - 0 - 0 - 0 - - - 8043 - 1 - 0 - 0 - 0 - - - 8044 - 1 - 0 - 0 - 0 - - - 8045 - 1 - 0 - 0 - 0 - - - 8046 - 1 - 0 - 0 - 0 - - - 8047 - 1 - 0 - 0 - 0 - - - 8048 - 1 - 0 - 0 - 0 - - - 8049 - 1 - 0 - 0 - 0 - - - 8050 - 1 - 0 - 0 - 0 - - - 8051 - 1 - 0 - 0 - 0 - - - 8052 - 1 - 0 - 0 - 0 - - - 8053 - 1 - 0 - 0 - 0 - - - 8054 - 1 - 0 - 0 - 0 - - - 8055 - 1 - 0 - 0 - 0 - - - 8056 - 1 - 0 - 0 - 0 - - - 8057 - 1 - 0 - 0 - 0 - - - 8058 - 1 - 0 - 0 - 0 - - - 8059 - 1 - 0 - 0 - 0 - - - 8060 - 1 - 0 - 0 - 0 - - - 8061 - 1 - 0 - 0 - 0 - - - 8062 - 1 - 0 - 0 - 0 - - - 8063 - 1 - 0 - 0 - 0 - - - 8064 - 1 - 0 - 0 - 0 - - - 8065 - 1 - 0 - 0 - 0 - - - 8066 - 1 - 0 - 0 - 0 - - - 8067 - 1 - 0 - 0 - 0 - - - 8068 - 1 - 0 - 0 - 0 - - - 8069 - 1 - 0 - 0 - 0 - - - 8070 - 1 - 0 - 0 - 0 - - - 8071 - 1 - 0 - 0 - 0 - - - 8072 - 1 - 0 - 0 - 0 - - - 8073 - 1 - 0 - 0 - 0 - - - 8074 - 1 - 0 - 0 - 0 - - - 8075 - 1 - 0 - 0 - 0 - - - 8076 - 1 - 0 - 0 - 0 - - - 8077 - 1 - 0 - 0 - 0 - - - 8078 - 1 - 0 - 0 - 0 - - - 8079 - 1 - 0 - 0 - 0 - - - 8080 - 1 - 0 - 0 - 0 - - - 8081 - 1 - 0 - 0 - 0 - - - 8082 - 1 - 0 - 0 - 0 - - - 8083 - 1 - 0 - 0 - 0 - - - 8084 - 1 - 0 - 0 - 0 - - - 8085 - 1 - 0 - 0 - 0 - - - 8086 - 1 - 0 - 0 - 0 - - - 8087 - 1 - 0 - 0 - 0 - - - 8088 - 1 - 0 - 0 - 0 - - - 8089 - 1 - 0 - 0 - 0 - - - 8090 - 1 - 0 - 0 - 0 - - - 8091 - 1 - 0 - 0 - 0 - - - 8092 - 1 - 0 - 0 - 0 - - - 8093 - 1 - 0 - 0 - 0 - - - 8094 - 1 - 0 - 0 - 0 - - - 8095 - 1 - 0 - 0 - 0 - - - 8096 - 1 - 0 - 0 - 0 - - - 8097 - 1 - 0 - 0 - 0 - - - 8098 - 1 - 0 - 0 - 0 - - - 8099 - 1 - 0 - 0 - 0 - - - 8100 - 1 - 0 - 0 - 0 - - - 8101 - 1 - 0 - 0 - 0 - - - 8102 - 1 - 0 - 0 - 0 - - - 8103 - 1 - 0 - 0 - 0 - - - 8104 - 1 - 0 - 0 - 0 - - - 8105 - 1 - 0 - 0 - 0 - - - 8106 - 1 - 0 - 0 - 0 - - - 8107 - 1 - 0 - 0 - 0 - - - 8108 - 1 - 0 - 0 - 0 - - - 8109 - 1 - 0 - 0 - 0 - - - 8110 - 1 - 0 - 0 - 0 - - - 8111 - 1 - 0 - 0 - 0 - - - 8112 - 1 - 0 - 0 - 0 - - - 8113 - 1 - 0 - 0 - 0 - - - 8114 - 1 - 0 - 0 - 0 - - - 8115 - 1 - 0 - 0 - 0 - - - 8116 - 1 - 0 - 0 - 0 - - - 8117 - 1 - 0 - 0 - 0 - - - 8118 - 1 - 0 - 0 - 0 - - - 8119 - 1 - 0 - 0 - 0 - - - 8120 - 1 - 0 - 0 - 0 - - - 8121 - 1 - 0 - 0 - 0 - - - 8122 - 1 - 0 - 0 - 0 - - - 8123 - 1 - 0 - 0 - 0 - - - 8124 - 1 - 0 - 0 - 0 - - - 8125 - 1 - 0 - 0 - 0 - - - 8126 - 1 - 0 - 0 - 0 - - - 8127 - 1 - 0 - 0 - 0 - - - 8128 - 1 - 0 - 0 - 0 - - - 8129 - 1 - 0 - 0 - 0 - - - 8130 - 1 - 0 - 0 - 0 - - - 8131 - 1 - 0 - 0 - 0 - - - 8132 - 1 - 0 - 0 - 0 - - - 8133 - 1 - 0 - 0 - 0 - - - 8134 - 1 - 0 - 0 - 0 - - - 8135 - 1 - 0 - 0 - 0 - - - 8136 - 1 - 0 - 0 - 0 - - - 8137 - 1 - 0 - 0 - 0 - - - 8138 - 1 - 0 - 0 - 0 - - - 8139 - 1 - 0 - 0 - 0 - - - 8140 - 1 - 0 - 0 - 0 - - - 8141 - 1 - 0 - 0 - 0 - - - 8142 - 1 - 0 - 0 - 0 - - - 8143 - 1 - 0 - 0 - 0 - - - 8144 - 1 - 0 - 0 - 0 - - - 8145 - 1 - 0 - 0 - 0 - - - 8146 - 1 - 0 - 0 - 0 - - - 8147 - 1 - 0 - 0 - 0 - - - 8148 - 1 - 0 - 0 - 0 - - - 8149 - 1 - 0 - 0 - 0 - - - 8150 - 1 - 0 - 0 - 0 - - - 8151 - 1 - 0 - 0 - 0 - - - 8152 - 1 - 0 - 0 - 0 - - - 8153 - 1 - 0 - 0 - 0 - - - 8154 - 1 - 0 - 0 - 0 - - - 8155 - 1 - 0 - 0 - 0 - - - 8156 - 1 - 0 - 0 - 0 - - - 8157 - 1 - 0 - 0 - 0 - - - 8158 - 1 - 0 - 0 - 0 - - - 8159 - 1 - 0 - 0 - 0 - - - 8160 - 1 - 0 - 0 - 0 - - - 8161 - 1 - 0 - 0 - 0 - - - 8162 - 1 - 0 - 0 - 0 - - - 8163 - 1 - 0 - 0 - 0 - - - 8164 - 1 - 0 - 0 - 0 - - - 8165 - 1 - 0 - 0 - 0 - - - 8166 - 1 - 0 - 0 - 0 - - - 8167 - 1 - 0 - 0 - 0 - - - 8168 - 1 - 0 - 0 - 0 - - - 8169 - 1 - 0 - 0 - 0 - - - 8170 - 1 - 0 - 0 - 0 - - - 8171 - 1 - 0 - 0 - 0 - - - 8172 - 1 - 0 - 0 - 0 - - - 8173 - 1 - 0 - 0 - 0 - - - 8174 - 1 - 0 - 0 - 0 - - - 8175 - 1 - 0 - 0 - 0 - - - 8176 - 1 - 0 - 0 - 0 - - - 8177 - 1 - 0 - 0 - 0 - - - 8178 - 1 - 0 - 0 - 0 - - - 8179 - 1 - 0 - 0 - 0 - - - 8180 - 1 - 0 - 0 - 0 - - - 8181 - 1 - 0 - 0 - 0 - - - 8182 - 1 - 0 - 0 - 0 - - - 8183 - 1 - 0 - 0 - 0 - - - 8184 - 1 - 0 - 0 - 0 - - - 8185 - 1 - 0 - 0 - 0 - - - 8186 - 1 - 0 - 0 - 0 - - - 8187 - 1 - 0 - 0 - 0 - - - 8188 - 1 - 0 - 0 - 0 - - - 8189 - 1 - 0 - 0 - 0 - - - 8190 - 1 - 0 - 0 - 0 - - - 8191 - 1 - 0 - 0 - 0 - - - 8192 - 1 - 0 - 0 - 0 - - - 8193 - 1 - 0 - 0 - 0 - - - 8194 - 1 - 0 - 0 - 0 - - - 8195 - 1 - 0 - 0 - 0 - - - 8196 - 1 - 0 - 0 - 0 - - - 8197 - 1 - 0 - 0 - 0 - - - 8198 - 1 - 0 - 0 - 0 - - - 8199 - 1 - 0 - 0 - 0 - - - 8200 - 1 - 0 - 0 - 0 - - - 8201 - 1 - 0 - 0 - 0 - - - 8202 - 1 - 0 - 0 - 0 - - - 8203 - 1 - 0 - 0 - 0 - - - 8204 - 1 - 0 - 0 - 0 - - - 8205 - 1 - 0 - 0 - 0 - - - 8206 - 1 - 0 - 0 - 0 - - - 8207 - 1 - 0 - 0 - 0 - - - 8208 - 1 - 0 - 0 - 0 - - - 8209 - 1 - 0 - 0 - 0 - - - 8210 - 1 - 0 - 0 - 0 - - - 8211 - 1 - 0 - 0 - 0 - - - 8212 - 1 - 0 - 0 - 0 - - - 8213 - 1 - 0 - 0 - 0 - - - 8214 - 1 - 0 - 0 - 0 - - - 8215 - 1 - 0 - 0 - 0 - - - 8216 - 1 - 0 - 0 - 0 - - - 8217 - 1 - 0 - 0 - 0 - - - 8218 - 1 - 0 - 0 - 0 - - - 8219 - 1 - 0 - 0 - 0 - - - 8220 - 1 - 0 - 0 - 0 - - - 8221 - 1 - 0 - 0 - 0 - - - 8222 - 1 - 0 - 0 - 0 - - - 8223 - 1 - 0 - 0 - 0 - - - 8224 - 1 - 0 - 0 - 0 - - - 8225 - 1 - 0 - 0 - 0 - - - 8226 - 1 - 0 - 0 - 0 - - - 8227 - 1 - 0 - 0 - 0 - - - 8228 - 1 - 0 - 0 - 0 - - - 8229 - 1 - 0 - 0 - 0 - - - 8230 - 1 - 0 - 0 - 0 - - - 8231 - 1 - 0 - 0 - 0 - - - 8232 - 1 - 0 - 0 - 0 - - - 8233 - 1 - 0 - 0 - 0 - - - 8234 - 1 - 0 - 0 - 0 - - - 8235 - 1 - 0 - 0 - 0 - - - 8236 - 1 - 0 - 0 - 0 - - - 8237 - 1 - 0 - 0 - 0 - - - 8238 - 1 - 0 - 0 - 0 - - - 8239 - 1 - 0 - 0 - 0 - - - 8240 - 1 - 0 - 0 - 0 - - - 8241 - 1 - 0 - 0 - 0 - - - 8242 - 1 - 0 - 0 - 0 - - - 8243 - 1 - 0 - 0 - 0 - - - 8244 - 1 - 0 - 0 - 0 - - - 8245 - 1 - 0 - 0 - 0 - - - 8246 - 1 - 0 - 0 - 0 - - - 8247 - 1 - 0 - 0 - 0 - - - 8248 - 1 - 0 - 0 - 0 - - - 8249 - 1 - 0 - 0 - 0 - - - 8250 - 1 - 0 - 0 - 0 - - - 8251 - 1 - 0 - 0 - 0 - - - 8252 - 1 - 0 - 0 - 0 - - - 8253 - 1 - 0 - 0 - 0 - - - 8254 - 1 - 0 - 0 - 0 - - - 8255 - 1 - 0 - 0 - 0 - - - 8256 - 1 - 0 - 0 - 0 - - - 8257 - 1 - 0 - 0 - 0 - - - 8258 - 1 - 0 - 0 - 0 - - - 8259 - 1 - 0 - 0 - 0 - - - 8260 - 1 - 0 - 0 - 0 - - - 8261 - 1 - 0 - 0 - 0 - - - 8262 - 1 - 0 - 0 - 0 - - - 8263 - 1 - 0 - 0 - 0 - - - 8264 - 1 - 0 - 0 - 0 - - - 8265 - 1 - 0 - 0 - 0 - - - 8266 - 1 - 0 - 0 - 0 - - - 8267 - 1 - 0 - 0 - 0 - - - 8268 - 1 - 0 - 0 - 0 - - - 8269 - 1 - 0 - 0 - 0 - - - 8270 - 1 - 0 - 0 - 0 - - - 8271 - 1 - 0 - 0 - 0 - - - 8272 - 1 - 0 - 0 - 0 - - - 8273 - 1 - 0 - 0 - 0 - - - 8274 - 1 - 0 - 0 - 0 - - - 8275 - 1 - 0 - 0 - 0 - - - 8276 - 1 - 0 - 0 - 0 - - - 8277 - 1 - 0 - 0 - 0 - - - 8278 - 1 - 0 - 0 - 0 - - - 8279 - 1 - 0 - 0 - 0 - - - 8280 - 1 - 0 - 0 - 0 - - - 8281 - 1 - 0 - 0 - 0 - - - 8282 - 1 - 0 - 0 - 0 - - - 8283 - 1 - 0 - 0 - 0 - - - 8284 - 1 - 0 - 0 - 0 - - - 8285 - 1 - 0 - 0 - 0 - - - 8286 - 1 - 0 - 0 - 0 - - - 8287 - 1 - 0 - 0 - 0 - - - 8288 - 1 - 0 - 0 - 0 - - - 8289 - 1 - 0 - 0 - 0 - - - 8290 - 1 - 0 - 0 - 0 - - - 8291 - 1 - 0 - 0 - 0 - - - 8292 - 1 - 0 - 0 - 0 - - - 8293 - 1 - 0 - 0 - 0 - - - 8294 - 1 - 0 - 0 - 0 - - - 8295 - 1 - 0 - 0 - 0 - - - 8296 - 1 - 0 - 0 - 0 - - - 8297 - 1 - 0 - 0 - 0 - - - 8298 - 1 - 0 - 0 - 0 - - - 8299 - 1 - 0 - 0 - 0 - - - 8300 - 1 - 0 - 0 - 0 - - - 8301 - 1 - 0 - 0 - 0 - - - 8302 - 1 - 0 - 0 - 0 - - - 8303 - 1 - 0 - 0 - 0 - - - 8304 - 1 - 0 - 0 - 0 - - - 8305 - 1 - 0 - 0 - 0 - - - 8306 - 1 - 0 - 0 - 0 - - - 8307 - 1 - 0 - 0 - 0 - - - 8308 - 1 - 0 - 0 - 0 - - - 8309 - 1 - 0 - 0 - 0 - - - 8310 - 1 - 0 - 0 - 0 - - - 8311 - 1 - 0 - 0 - 0 - - - 8312 - 1 - 0 - 0 - 0 - - - 8313 - 1 - 0 - 0 - 0 - - - 8314 - 1 - 0 - 0 - 0 - - - 8315 - 1 - 0 - 0 - 0 - - - 8316 - 1 - 0 - 0 - 0 - - - 8317 - 1 - 0 - 0 - 0 - - - 8318 - 1 - 0 - 0 - 0 - - - 8319 - 1 - 0 - 0 - 0 - - - 8320 - 1 - 0 - 0 - 0 - - - 8321 - 1 - 0 - 0 - 0 - - - 8322 - 1 - 0 - 0 - 0 - - - 8323 - 1 - 0 - 0 - 0 - - - 8324 - 1 - 0 - 0 - 0 - - - 8325 - 1 - 0 - 0 - 0 - - - 8326 - 1 - 0 - 0 - 0 - - - 8327 - 1 - 0 - 0 - 0 - - - 8328 - 1 - 0 - 0 - 0 - - - 8329 - 1 - 0 - 0 - 0 - - - 8330 - 1 - 0 - 0 - 0 - - - 8331 - 1 - 0 - 0 - 0 - - - 8332 - 1 - 0 - 0 - 0 - - - 8333 - 1 - 0 - 0 - 0 - - - 8334 - 1 - 0 - 0 - 0 - - - 8335 - 1 - 0 - 0 - 0 - - - 8336 - 1 - 0 - 0 - 0 - - - 8337 - 1 - 0 - 0 - 0 - - - 8338 - 1 - 0 - 0 - 0 - - - 8339 - 1 - 0 - 0 - 0 - - - 8340 - 1 - 0 - 0 - 0 - - - 8341 - 1 - 0 - 0 - 0 - - - 8342 - 1 - 0 - 0 - 0 - - - 8343 - 1 - 0 - 0 - 0 - - - 8344 - 1 - 0 - 0 - 0 - - - 8345 - 1 - 0 - 0 - 0 - - - 8346 - 1 - 0 - 0 - 0 - - - 8347 - 1 - 0 - 0 - 0 - - - 8348 - 1 - 0 - 0 - 0 - - - 8349 - 1 - 0 - 0 - 0 - - - 8350 - 1 - 0 - 0 - 0 - - - 8351 - 1 - 0 - 0 - 0 - - - 8352 - 1 - 0 - 0 - 0 - - - 8353 - 1 - 0 - 0 - 0 - - - 8354 - 1 - 0 - 0 - 0 - - - 8355 - 1 - 0 - 0 - 0 - - - 8356 - 1 - 0 - 0 - 0 - - - 8357 - 1 - 0 - 0 - 0 - - - 8358 - 1 - 0 - 0 - 0 - - - 8359 - 1 - 0 - 0 - 0 - - - 8360 - 1 - 0 - 0 - 0 - - - 8361 - 1 - 0 - 0 - 0 - - - 8362 - 1 - 0 - 0 - 0 - - - 8363 - 1 - 0 - 0 - 0 - - - 8364 - 1 - 0 - 0 - 0 - - - 8365 - 1 - 0 - 0 - 0 - - - 8366 - 1 - 0 - 0 - 0 - - - 8367 - 1 - 0 - 0 - 0 - - - 8368 - 1 - 0 - 0 - 0 - - - 8369 - 1 - 0 - 0 - 0 - - - 8370 - 1 - 0 - 0 - 0 - - - 8371 - 1 - 0 - 0 - 0 - - - 8372 - 1 - 0 - 0 - 0 - - - 8373 - 1 - 0 - 0 - 0 - - - 8374 - 1 - 0 - 0 - 0 - - - 8375 - 1 - 0 - 0 - 0 - - - 8376 - 1 - 0 - 0 - 0 - - - 8377 - 1 - 0 - 0 - 0 - - - 8378 - 1 - 0 - 0 - 0 - - - 8379 - 1 - 0 - 0 - 0 - - - 8380 - 1 - 0 - 0 - 0 - - - 8381 - 1 - 0 - 0 - 0 - - - 8382 - 1 - 0 - 0 - 0 - - - 8383 - 1 - 0 - 0 - 0 - - - 8384 - 1 - 0 - 0 - 0 - - - 8385 - 1 - 0 - 0 - 0 - - - 8386 - 1 - 0 - 0 - 0 - - - 8387 - 1 - 0 - 0 - 0 - - - 8388 - 1 - 0 - 0 - 0 - - - 8389 - 1 - 0 - 0 - 0 - - - 8390 - 1 - 0 - 0 - 0 - - - 8391 - 1 - 0 - 0 - 0 - - - 8392 - 1 - 0 - 0 - 0 - - - 8393 - 1 - 0 - 0 - 0 - - - 8394 - 1 - 0 - 0 - 0 - - - 8395 - 1000 - 0 - 0 - 0 - - - 8396 - 5000 - 0 - 0 - 0 - - - 8397 - 5000 - 0 - 0 - 0 - - - 8398 - 10000 - 0 - 0 - 0 - - - 8399 - 25000 - 0 - 0 - 0 - - - 8400 - 25000 - 0 - 0 - 0 - - - 8401 - 15000 - 0 - 0 - 0 - - - 8402 - 15000 - 0 - 0 - 0 - - - 8403 - 25000 - 0 - 0 - 0 - - - 8404 - 25000 - 0 - 0 - 0 - - - 8405 - 50000 - 0 - 0 - 0 - - - 8406 - 10000 - 0 - 0 - 0 - - - 8407 - 50000 - 0 - 0 - 0 - - - 8408 - 100000 - 0 - 0 - 0 - - - 8409 - 150000 - 0 - 0 - 0 - - - 8410 - 150000 - 0 - 0 - 0 - - - 8411 - 7500 - 0 - 0 - 0 - - - 8412 - 7500 - 0 - 0 - 0 - - - 8413 - 7500 - 0 - 0 - 0 - - - 8414 - 250000 - 0 - 0 - 0 - - - 8415 - 1000 - 0 - 0 - 0 - - - 8416 - 75000 - 0 - 0 - 0 - - - 8417 - 1000 - 0 - 0 - 0 - - - 8418 - 1000 - 0 - 0 - 0 - - - 8419 - 2000 - 0 - 0 - 0 - - - 8420 - 2000 - 0 - 0 - 0 - - - 8421 - 5000 - 0 - 0 - 0 - - - 8422 - 5000 - 0 - 0 - 0 - - - 8423 - 10000 - 0 - 0 - 0 - - - 8424 - 10000 - 0 - 0 - 0 - - - 8425 - 15000 - 0 - 0 - 0 - - - 8426 - 15000 - 0 - 0 - 0 - - - 8427 - 20000 - 0 - 0 - 0 - - - 8428 - 20000 - 0 - 0 - 0 - - - 8429 - 50000 - 0 - 0 - 0 - - - 8430 - 50000 - 0 - 0 - 0 - - - 8431 - 1000 - 0 - 0 - 0 - - - 8432 - 1000 - 0 - 0 - 0 - - - 8433 - 5000 - 0 - 0 - 0 - - - 8434 - 5000 - 0 - 0 - 0 - - - 8435 - 10000 - 0 - 0 - 0 - - - 8436 - 10000 - 0 - 0 - 0 - - - 8437 - 5000 - 0 - 0 - 0 - - - 8438 - 5000 - 0 - 0 - 0 - - - 8439 - 10000 - 0 - 0 - 0 - - - 8440 - 10000 - 0 - 0 - 0 - - - 8441 - 15000 - 0 - 0 - 0 - - - 8442 - 15000 - 0 - 0 - 0 - - - 8443 - 20000 - 0 - 0 - 0 - - - 8444 - 20000 - 0 - 0 - 0 - - - 8445 - 25000 - 0 - 0 - 0 - - - 8446 - 25000 - 0 - 0 - 0 - - - 8447 - 50000 - 0 - 0 - 0 - - - 8448 - 50000 - 0 - 0 - 0 - - - 8449 - 100000 - 0 - 0 - 0 - - - 8450 - 100000 - 0 - 0 - 0 - - - 8451 - 5000 - 0 - 0 - 0 - - - 8452 - 5000 - 0 - 0 - 0 - - - 8453 - 10000 - 0 - 0 - 0 - - - 8454 - 10000 - 0 - 0 - 0 - - - 8455 - 15000 - 0 - 0 - 0 - - - 8456 - 15000 - 0 - 0 - 0 - - - 8457 - 5000 - 0 - 0 - 0 - - - 8458 - 5000 - 0 - 0 - 0 - - - 8459 - 10000 - 0 - 0 - 0 - - - 8460 - 10000 - 0 - 0 - 0 - - - 8461 - 15000 - 0 - 0 - 0 - - - 8462 - 15000 - 0 - 0 - 0 - - - 8463 - 1 - 0 - 0 - 0 - - - 8464 - 35200 - 0 - 0 - 0 - - - 8465 - 1 - 0 - 0 - 0 - - - 8466 - 35200 - 0 - 0 - 0 - - - 8467 - 1 - 0 - 0 - 0 - - - 8468 - 35200 - 0 - 0 - 0 - - - 8469 - 1 - 0 - 0 - 0 - - - 8470 - 35200 - 0 - 0 - 0 - - - 8471 - 1 - 0 - 0 - 0 - - - 8472 - 35200 - 0 - 0 - 0 - - - 8473 - 1 - 0 - 0 - 0 - - - 8474 - 35200 - 0 - 0 - 0 - - - 8475 - 1 - 0 - 0 - 0 - - - 8476 - 35200 - 0 - 0 - 0 - - - 8477 - 1 - 0 - 0 - 0 - - - 8478 - 35200 - 0 - 0 - 0 - - - 8479 - 1 - 0 - 0 - 0 - - - 8480 - 35200 - 0 - 0 - 0 - - - 8481 - 1 - 0 - 0 - 0 - - - 8482 - 35200 - 0 - 0 - 0 - - - 8483 - 1 - 0 - 0 - 0 - - - 8484 - 35200 - 0 - 0 - 0 - - - 8485 - 1 - 0 - 0 - 0 - - - 8486 - 35200 - 0 - 0 - 0 - - - 8487 - 1 - 0 - 0 - 0 - - - 8488 - 35200 - 0 - 0 - 0 - - - 8489 - 1 - 0 - 0 - 0 - - - 8490 - 35200 - 0 - 0 - 0 - - - 8491 - 1 - 0 - 0 - 0 - - - 8492 - 35200 - 0 - 0 - 0 - - - 8493 - 1 - 0 - 0 - 0 - - - 8494 - 35200 - 0 - 0 - 0 - - - 8495 - 1 - 0 - 0 - 0 - - - 8496 - 10 - 0 - 0 - 0 - - - 8497 - 10 - 0 - 0 - 0 - - - 8498 - 10 - 0 - 0 - 0 - - - 8499 - 10 - 0 - 0 - 0 - - - 8500 - 10 - 0 - 0 - 0 - - - 8501 - 10 - 0 - 0 - 0 - - - 8502 - 10 - 0 - 0 - 0 - - - 8503 - 10 - 0 - 0 - 0 - - - 8504 - 10 - 0 - 0 - 0 - - - 8505 - 10 - 0 - 0 - 0 - - - 8506 - 10 - 0 - 0 - 0 - - - 8507 - 10 - 0 - 0 - 0 - - - 8508 - 10 - 0 - 0 - 0 - - - 8509 - 10 - 0 - 0 - 0 - - - 8510 - 10 - 0 - 0 - 0 - - - 8511 - 10 - 0 - 0 - 0 - - - 8512 - 10 - 0 - 0 - 0 - - - 8513 - 10 - 0 - 0 - 0 - - - 8514 - 10 - 0 - 0 - 0 - - - 8515 - 10 - 0 - 0 - 0 - - - 8516 - 10 - 0 - 0 - 0 - - - 8517 - 10 - 0 - 0 - 0 - - - 8518 - 10 - 0 - 0 - 0 - - - 8519 - 10 - 0 - 0 - 0 - - - 8520 - 10 - 0 - 0 - 0 - - - 8521 - 10 - 0 - 0 - 0 - - - 8522 - 10 - 0 - 0 - 0 - - - 8523 - 10 - 0 - 0 - 0 - - - 8524 - 10 - 0 - 0 - 0 - - - 8525 - 10 - 0 - 0 - 0 - - - 8526 - 10 - 0 - 0 - 0 - - - 8527 - 10 - 0 - 0 - 0 - - - 8528 - 10 - 0 - 0 - 0 - - - 8529 - 10 - 0 - 0 - 0 - - - 8530 - 10 - 0 - 0 - 0 - - - 8531 - 10 - 0 - 0 - 0 - - - 8532 - 10 - 0 - 0 - 0 - - - 8533 - 10 - 0 - 0 - 0 - - - 8534 - 10 - 0 - 0 - 0 - - - 8535 - 10 - 0 - 0 - 0 - - - 8536 - 10 - 0 - 0 - 0 - - - 8537 - 10 - 0 - 0 - 0 - - - 8538 - 10 - 0 - 0 - 0 - - - 8539 - 10 - 0 - 0 - 0 - - - 8540 - 10 - 0 - 0 - 0 - - - 8541 - 10 - 0 - 0 - 0 - - - 8542 - 10 - 0 - 0 - 0 - - - 8543 - 10 - 0 - 0 - 0 - - - 8544 - 10 - 0 - 0 - 0 - - - 8545 - 10 - 0 - 0 - 0 - - - 8546 - 10 - 0 - 0 - 0 - - - 8547 - 10 - 0 - 0 - 0 - - - 8548 - 10 - 0 - 0 - 0 - - - 8549 - 10 - 0 - 0 - 0 - - - 8550 - 10 - 0 - 0 - 0 - - - 8551 - 10 - 0 - 0 - 0 - - - 8552 - 10 - 0 - 0 - 0 - - - 8553 - 10 - 0 - 0 - 0 - - - 8554 - 10 - 0 - 0 - 0 - - - 8555 - 10 - 0 - 0 - 0 - - - 8556 - 10 - 0 - 0 - 0 - - - 8557 - 10 - 0 - 0 - 0 - - - 8558 - 10 - 0 - 0 - 0 - - - 8559 - 10 - 0 - 0 - 0 - - - 8560 - 10 - 0 - 0 - 0 - - - 8561 - 10 - 0 - 0 - 0 - - - 8562 - 10 - 0 - 0 - 0 - - - 8563 - 10 - 0 - 0 - 0 - - - 8564 - 10 - 0 - 0 - 0 - - - 8565 - 10 - 0 - 0 - 0 - - - 8566 - 10 - 0 - 0 - 0 - - - 8567 - 10 - 0 - 0 - 0 - - - 8568 - 10 - 0 - 0 - 0 - - - 8569 - 10 - 0 - 0 - 0 - - - 8570 - 10 - 0 - 0 - 0 - - - 8571 - 10 - 0 - 0 - 0 - - - 8572 - 10 - 0 - 0 - 0 - - - 8573 - 10 - 0 - 0 - 0 - - - 8574 - 10 - 0 - 0 - 0 - - - 8575 - 10 - 0 - 0 - 0 - - - 8576 - 10 - 0 - 0 - 0 - - - 8577 - 10 - 0 - 0 - 0 - - - 8578 - 10 - 0 - 0 - 0 - - - 8579 - 10 - 0 - 0 - 0 - - - 8580 - 10 - 0 - 0 - 0 - - - 8581 - 10 - 0 - 0 - 0 - - - 8582 - 10 - 0 - 0 - 0 - - - 8583 - 10 - 0 - 0 - 0 - - - 8584 - 10 - 0 - 0 - 0 - - - 8585 - 10 - 0 - 0 - 0 - - - 8586 - 10 - 0 - 0 - 0 - - - 8587 - 10 - 0 - 0 - 0 - - - 8588 - 10 - 0 - 0 - 0 - - - 8589 - 10 - 0 - 0 - 0 - - - 8590 - 10 - 0 - 0 - 0 - - - 8591 - 10 - 0 - 0 - 0 - - - 8592 - 10 - 0 - 0 - 0 - - - 8593 - 10 - 0 - 0 - 0 - - - 8594 - 10 - 0 - 0 - 0 - - - 8595 - 10 - 0 - 0 - 0 - - - 8596 - 10 - 0 - 0 - 0 - - - 8597 - 10 - 0 - 0 - 0 - - - 8598 - 10 - 0 - 0 - 0 - - - 8599 - 10 - 0 - 0 - 0 - - - 8600 - 10 - 0 - 0 - 0 - - - 8601 - 10 - 0 - 0 - 0 - - - 8602 - 10 - 0 - 0 - 0 - - - 8603 - 10 - 0 - 0 - 0 - - - 8604 - 10 - 0 - 0 - 0 - - - 8605 - 10 - 0 - 0 - 0 - - - 8606 - 10 - 0 - 0 - 0 - - - 8607 - 10 - 0 - 0 - 0 - - - 8608 - 10 - 0 - 0 - 0 - - - 8609 - 10 - 0 - 0 - 0 - - - 8610 - 10 - 0 - 0 - 0 - - - 8611 - 10 - 0 - 0 - 0 - - - 8612 - 10 - 0 - 0 - 0 - - - 8613 - 10 - 0 - 0 - 0 - - - 8614 - 10 - 0 - 0 - 0 - - - 8615 - 10 - 0 - 0 - 0 - - - 8616 - 10 - 0 - 0 - 0 - - - 8617 - 10 - 0 - 0 - 0 - - - 8618 - 10 - 0 - 0 - 0 - - - 8619 - 10 - 0 - 0 - 0 - - - 8620 - 10 - 0 - 0 - 0 - - - 8621 - 10 - 0 - 0 - 0 - - - 8622 - 10 - 0 - 0 - 0 - - - 8623 - 10 - 0 - 0 - 0 - - - 8624 - 10 - 0 - 0 - 0 - - - 8625 - 10 - 0 - 0 - 0 - - - 8626 - 10 - 0 - 0 - 0 - - - 8627 - 10 - 0 - 0 - 0 - - - 8628 - 10 - 0 - 0 - 0 - - - 8629 - 10 - 0 - 0 - 0 - - - 8630 - 10 - 0 - 0 - 0 - - - 8631 - 10 - 0 - 0 - 0 - - - 8632 - 10 - 0 - 0 - 0 - - - 8633 - 10 - 0 - 0 - 0 - - - 8634 - 10 - 0 - 0 - 0 - - - 8635 - 10 - 0 - 0 - 0 - - - 8636 - 10 - 0 - 0 - 0 - - - 8637 - 10 - 0 - 0 - 0 - - - 8638 - 10 - 0 - 0 - 0 - - - 8639 - 10 - 0 - 0 - 0 - - - 8640 - 10 - 0 - 0 - 0 - - - 8641 - 10 - 0 - 0 - 0 - - - 8642 - 10 - 0 - 0 - 0 - - - 8643 - 10 - 0 - 0 - 0 - - - 8644 - 10 - 0 - 0 - 0 - - - 8645 - 10 - 0 - 0 - 0 - - - 8646 - 10 - 0 - 0 - 0 - - - 8647 - 10 - 0 - 0 - 0 - - - 8648 - 10 - 0 - 0 - 0 - - - 8649 - 10 - 0 - 0 - 0 - - - 8650 - 1 - 0 - 0 - 0 - - - 8651 - 1 - 0 - 0 - 0 - - - 8652 - 1 - 0 - 0 - 0 - - - 8653 - 1 - 0 - 0 - 0 - - - 8654 - 1 - 0 - 0 - 0 - - - 8655 - 1 - 0 - 0 - 0 - - - 8656 - 1 - 0 - 0 - 0 - - - 8657 - 1 - 0 - 0 - 0 - - - 8658 - 1 - 0 - 0 - 0 - - - 8659 - 1 - 0 - 0 - 0 - - - 8660 - 1 - 0 - 0 - 0 - - - 8661 - 1 - 0 - 0 - 0 - - - 8662 - 1 - 0 - 0 - 0 - - - 8663 - 1 - 0 - 0 - 0 - - - 8664 - 1 - 0 - 0 - 0 - - - 8665 - 1 - 0 - 0 - 0 - - - 8666 - 1 - 0 - 0 - 0 - - - 8667 - 1 - 0 - 0 - 0 - - - 8668 - 1 - 0 - 0 - 0 - - - 8669 - 1 - 0 - 0 - 0 - - - 8670 - 1 - 0 - 0 - 0 - - - 8671 - 1 - 0 - 0 - 0 - - - 8672 - 1 - 0 - 0 - 0 - - - 8673 - 1 - 0 - 0 - 0 - - - 8674 - 1 - 0 - 0 - 0 - - - 8675 - 1 - 0 - 0 - 0 - - - 8676 - 1 - 0 - 0 - 0 - - - 8677 - 1 - 0 - 0 - 0 - - - 8678 - 1 - 0 - 0 - 0 - - - 8679 - 1 - 0 - 0 - 0 - - - 8680 - 1 - 0 - 0 - 0 - - - 8681 - 1 - 0 - 0 - 0 - - - 8682 - 600 - 0 - 0 - 0 - - - 8683 - 1 - 0 - 0 - 0 - - - 8684 - 600 - 0 - 0 - 0 - - - 8685 - 1 - 0 - 0 - 0 - - - 8686 - 600 - 0 - 0 - 0 - - - 8687 - 1 - 0 - 0 - 0 - - - 8688 - 600 - 0 - 0 - 0 - - - 8689 - 1 - 0 - 0 - 0 - - - 8690 - 600 - 0 - 0 - 0 - - - 8691 - 1 - 0 - 0 - 0 - - - 8692 - 600 - 0 - 0 - 0 - - - 8693 - 1 - 0 - 0 - 0 - - - 8694 - 600 - 0 - 0 - 0 - - - 8695 - 1 - 0 - 0 - 0 - - - 8696 - 600 - 0 - 0 - 0 - - - 8697 - 1 - 0 - 0 - 0 - - - 8698 - 600 - 0 - 0 - 0 - - - 8699 - 1 - 0 - 0 - 0 - - - 8700 - 600 - 0 - 0 - 0 - - - 8701 - 1 - 0 - 0 - 0 - - - 8702 - 600 - 0 - 0 - 0 - - - 8703 - 1 - 0 - 0 - 0 - - - 8704 - 600 - 0 - 0 - 0 - - - 8705 - 1 - 0 - 0 - 0 - - - 8706 - 600 - 0 - 0 - 0 - - - 8707 - 1 - 0 - 0 - 0 - - - 8708 - 600 - 0 - 0 - 0 - - - 8709 - 1 - 0 - 0 - 0 - - - 8710 - 600 - 0 - 0 - 0 - - - 8711 - 1 - 0 - 0 - 0 - - - 8712 - 600 - 0 - 0 - 0 - - - 8713 - 1 - 0 - 0 - 0 - - - 8714 - 54400 - 0 - 0 - 0 - - - 8715 - 1 - 0 - 0 - 0 - - - 8716 - 54400 - 0 - 0 - 0 - - - 8717 - 1 - 0 - 0 - 0 - - - 8718 - 54400 - 0 - 0 - 0 - - - 8719 - 1 - 0 - 0 - 0 - - - 8720 - 54400 - 0 - 0 - 0 - - - 8721 - 1 - 0 - 0 - 0 - - - 8722 - 54400 - 0 - 0 - 0 - - - 8723 - 1 - 0 - 0 - 0 - - - 8724 - 54400 - 0 - 0 - 0 - - - 8725 - 1 - 0 - 0 - 0 - - - 8726 - 54400 - 0 - 0 - 0 - - - 8727 - 1 - 0 - 0 - 0 - - - 8728 - 54400 - 0 - 0 - 0 - - - 8729 - 1 - 0 - 0 - 0 - - - 8730 - 54400 - 0 - 0 - 0 - - - 8731 - 1 - 0 - 0 - 0 - - - 8732 - 54400 - 0 - 0 - 0 - - - 8733 - 1 - 0 - 0 - 0 - - - 8734 - 54400 - 0 - 0 - 0 - - - 8735 - 1 - 0 - 0 - 0 - - - 8736 - 54400 - 0 - 0 - 0 - - - 8737 - 1 - 0 - 0 - 0 - - - 8738 - 54400 - 0 - 0 - 0 - - - 8739 - 1 - 0 - 0 - 0 - - - 8740 - 54400 - 0 - 0 - 0 - - - 8741 - 1 - 0 - 0 - 0 - - - 8742 - 54400 - 0 - 0 - 0 - - - 8743 - 1 - 0 - 0 - 0 - - - 8744 - 54400 - 0 - 0 - 0 - - - 8745 - 1 - 0 - 0 - 0 - - - 8746 - 900 - 0 - 0 - 0 - - - 8747 - 1 - 0 - 0 - 0 - - - 8748 - 900 - 0 - 0 - 0 - - - 8749 - 1 - 0 - 0 - 0 - - - 8750 - 900 - 0 - 0 - 0 - - - 8751 - 1 - 0 - 0 - 0 - - - 8752 - 900 - 0 - 0 - 0 - - - 8753 - 1 - 0 - 0 - 0 - - - 8754 - 900 - 0 - 0 - 0 - - - 8755 - 1 - 0 - 0 - 0 - - - 8756 - 900 - 0 - 0 - 0 - - - 8757 - 1 - 0 - 0 - 0 - - - 8758 - 900 - 0 - 0 - 0 - - - 8759 - 1 - 0 - 0 - 0 - - - 8760 - 900 - 0 - 0 - 0 - - - 8761 - 1 - 0 - 0 - 0 - - - 8762 - 900 - 0 - 0 - 0 - - - 8763 - 1 - 0 - 0 - 0 - - - 8764 - 900 - 0 - 0 - 0 - - - 8765 - 1 - 0 - 0 - 0 - - - 8766 - 900 - 0 - 0 - 0 - - - 8767 - 1 - 0 - 0 - 0 - - - 8768 - 900 - 0 - 0 - 0 - - - 8769 - 1 - 0 - 0 - 0 - - - 8770 - 900 - 0 - 0 - 0 - - - 8771 - 1 - 0 - 0 - 0 - - - 8772 - 900 - 0 - 0 - 0 - - - 8773 - 1 - 0 - 0 - 0 - - - 8774 - 900 - 0 - 0 - 0 - - - 8775 - 1 - 0 - 0 - 0 - - - 8776 - 900 - 0 - 0 - 0 - - - 8777 - 1 - 0 - 0 - 0 - - - 8778 - 250 - 0 - 0 - 0 - - - 8779 - 250 - 0 - 0 - 0 - - - 8780 - 500 - 0 - 0 - 0 - - - 8781 - 500 - 0 - 0 - 0 - - - 8782 - 1500 - 0 - 0 - 0 - - - 8783 - 1500 - 0 - 0 - 0 - - - 8784 - 130000 - 0 - 0 - 0 - - - 8785 - 130000 - 0 - 0 - 0 - - - 8786 - 325000 - 0 - 0 - 0 - - - 8787 - 325000 - 0 - 0 - 0 - - - 8788 - 975000 - 0 - 0 - 0 - - - 8789 - 975000 - 0 - 0 - 0 - - - 8790 - 650 - 0 - 0 - 0 - - - 8791 - 650 - 0 - 0 - 0 - - - 8792 - 500 - 0 - 0 - 0 - - - 8793 - 500 - 0 - 0 - 0 - - - 8794 - 13 - 0 - 0 - 0 - - - 8795 - 13 - 0 - 0 - 0 - - - 8796 - 20 - 0 - 0 - 0 - - - 8797 - 1 - 0 - 0 - 0 - - - 8798 - 1 - 0 - 0 - 0 - - - 8799 - 1 - 0 - 0 - 0 - - - 8800 - 1 - 0 - 0 - 0 - - - 8801 - 1 - 0 - 0 - 0 - - - 8802 - 1 - 0 - 0 - 0 - - - 8803 - 1 - 0 - 0 - 0 - - - 8804 - 1 - 0 - 0 - 0 - - - 8805 - 1 - 0 - 0 - 0 - - - 8806 - 1 - 0 - 0 - 0 - - - 8807 - 1 - 0 - 0 - 0 - - - 8808 - 1 - 0 - 0 - 0 - - - 8809 - 1 - 0 - 0 - 0 - - - 8810 - 1 - 0 - 0 - 0 - - - 8811 - 1 - 0 - 0 - 0 - - - 8812 - 1 - 0 - 0 - 0 - - - 8813 - 1 - 0 - 0 - 0 - - - 8814 - 1 - 0 - 0 - 0 - - - 8815 - 1 - 0 - 0 - 0 - - - 8816 - 1 - 0 - 0 - 0 - - - 8817 - 1 - 0 - 0 - 0 - - - 8818 - 1 - 0 - 0 - 0 - - - 8819 - 1 - 0 - 0 - 0 - - - 8820 - 1 - 0 - 0 - 0 - - - 8821 - 1 - 0 - 0 - 0 - - - 8822 - 1 - 0 - 0 - 0 - - - 8823 - 1 - 0 - 0 - 0 - - - 8824 - 1 - 0 - 0 - 0 - - - 8825 - 1 - 0 - 0 - 0 - - - 8826 - 1 - 0 - 0 - 0 - - - 8827 - 1 - 0 - 0 - 0 - - - 8828 - 1 - 0 - 0 - 0 - - - 8829 - 1 - 0 - 0 - 0 - - - 8830 - 1 - 0 - 0 - 0 - - - 8831 - 1 - 0 - 0 - 0 - - - 8832 - 1 - 0 - 0 - 0 - - - 8833 - 1 - 0 - 0 - 0 - - - 8834 - 1 - 0 - 0 - 0 - - - 8835 - 1 - 0 - 0 - 0 - - - 8836 - 50 - 0 - 0 - 0 - - - 8837 - 1 - 0 - 0 - 0 - - - 8838 - 1 - 0 - 0 - 0 - - - 8839 - 48000 - 0 - 0 - 0 - - - 8840 - 45000 - 0 - 0 - 0 - - - 8841 - 12000 - 0 - 0 - 0 - - - 8842 - 6052 - 0 - 0 - 0 - - - 8843 - 1 - 0 - 0 - 0 - - - 8844 - 1 - 0 - 0 - 0 - - - 8845 - 1 - 0 - 0 - 0 - - - 8846 - 1 - 0 - 0 - 0 - - - 8847 - 1 - 0 - 0 - 0 - - - 8848 - 1 - 0 - 0 - 0 - - - 8849 - 1 - 0 - 0 - 0 - - - 8850 - 1 - 0 - 0 - 0 - - - 8851 - 1 - 0 - 0 - 0 - - - 8852 - 1 - 0 - 0 - 0 - - - 8853 - 1 - 0 - 0 - 0 - - - 8854 - 1 - 0 - 0 - 0 - - - 8855 - 1 - 0 - 0 - 0 - - - 8856 - 1 - 0 - 0 - 0 - - - 8857 - 1 - 0 - 0 - 0 - - - 8858 - 1 - 0 - 0 - 0 - - - 8859 - 1 - 0 - 0 - 0 - - - 8860 - 1 - 0 - 0 - 0 - - - 8861 - 1 - 0 - 0 - 0 - - - 8862 - 1 - 0 - 0 - 0 - - - 8863 - 1 - 0 - 0 - 0 - - - 8864 - 1 - 0 - 0 - 0 - - - 8865 - 2 - 0 - 0 - 0 - - - 8866 - 1 - 0 - 0 - 0 - - - 8867 - 1 - 0 - 0 - 0 - - - 8868 - 1 - 0 - 0 - 0 - - - 8869 - 1 - 0 - 0 - 0 - - - 8870 - 1 - 0 - 0 - 0 - - - 8871 - 1 - 0 - 0 - 0 - - - 8872 - 2000 - 0 - 0 - 0 - - - 8873 - 2000 - 0 - 0 - 0 - - - 8874 - 2000 - 0 - 0 - 0 - - - 8875 - 2000 - 0 - 0 - 0 - - - 8876 - 2000 - 0 - 0 - 0 - - - 8877 - 2000 - 0 - 0 - 0 - - - 8878 - 2000 - 0 - 0 - 0 - - - 8879 - 2000 - 0 - 0 - 0 - - - 8880 - 2000 - 0 - 0 - 0 - - - 8881 - 2000 - 0 - 0 - 0 - - - 8882 - 4 - 0 - 0 - 0 - - - 8883 - 1 - 0 - 0 - 0 - - - 8884 - 1 - 0 - 0 - 0 - - - 8885 - 1 - 0 - 0 - 0 - - - 8886 - 1 - 0 - 0 - 0 - - - 8887 - 1 - 0 - 0 - 0 - - - 8888 - 1 - 0 - 0 - 0 - - - 8889 - 1 - 0 - 0 - 0 - - - 8890 - 1 - 0 - 0 - 0 - - - 8891 - 1 - 0 - 0 - 0 - - - 8892 - 1 - 0 - 0 - 0 - - - 8893 - 1 - 0 - 0 - 0 - - - 8894 - 1 - 0 - 0 - 0 - - - 8895 - 1 - 0 - 0 - 0 - - - 8896 - 1 - 0 - 0 - 0 - - - 8897 - 1 - 0 - 0 - 0 - - - 8898 - 1 - 0 - 0 - 0 - - - 8899 - 1 - 0 - 0 - 0 - - - 8900 - 1 - 0 - 0 - 0 - - - 8901 - 3000 - 0 - 0 - 0 - - - 8902 - 3000 - 0 - 0 - 0 - - - 8903 - 3000 - 0 - 0 - 0 - - - 8904 - 3000 - 0 - 0 - 0 - - - 8905 - 3000 - 0 - 0 - 0 - - - 8906 - 3000 - 0 - 0 - 0 - - - 8907 - 3000 - 0 - 0 - 0 - - - 8908 - 3000 - 0 - 0 - 0 - - - 8909 - 3000 - 0 - 0 - 0 - - - 8910 - 3000 - 0 - 0 - 0 - - - 8911 - 3000 - 0 - 0 - 0 - - - 8912 - 3000 - 0 - 0 - 0 - - - 8913 - 3000 - 0 - 0 - 0 - - - 8914 - 3000 - 0 - 0 - 0 - - - 8915 - 3000 - 0 - 0 - 0 - - - 8916 - 3000 - 0 - 0 - 0 - - - 8917 - 3000 - 0 - 0 - 0 - - - 8918 - 3000 - 0 - 0 - 0 - - - 8919 - 3000 - 0 - 0 - 0 - - - 8920 - 3000 - 0 - 0 - 0 - - - 8921 - 3000 - 0 - 0 - 0 - - - 8922 - 3000 - 0 - 0 - 0 - - - 8923 - 900 - 0 - 0 - 0 - - - 8924 - 100 - 0 - 0 - 0 - - - 8925 - 100 - 0 - 0 - 0 - - - 8926 - 100 - 0 - 0 - 0 - - - 8927 - 100 - 0 - 0 - 0 - - - 8928 - 100 - 0 - 0 - 0 - - - 8929 - 100 - 0 - 0 - 0 - - - 8930 - 1 - 0 - 0 - 0 - - - 8931 - 1 - 0 - 0 - 0 - - - 8932 - 1 - 0 - 0 - 0 - - - 8933 - 1 - 0 - 0 - 0 - - - 8934 - 1 - 0 - 0 - 0 - - - 8935 - 1 - 0 - 0 - 0 - - - 8936 - 1 - 0 - 0 - 0 - - - 8937 - 1 - 0 - 0 - 0 - - - 8938 - 1 - 0 - 0 - 0 - - - 8939 - 1 - 0 - 0 - 0 - - - 8940 - 2 - 0 - 0 - 0 - - - 8941 - 2 - 0 - 0 - 0 - - - 8942 - 1 - 0 - 0 - 0 - - - 8943 - 1 - 0 - 0 - 0 - - - 8944 - 1 - 0 - 0 - 0 - - - 8945 - 1 - 0 - 0 - 0 - - - 8946 - 1 - 0 - 0 - 0 - - - 8947 - 1 - 0 - 0 - 0 - - - 8948 - 1 - 0 - 0 - 0 - - - 8949 - 1 - 0 - 0 - 0 - - - 8950 - 1 - 0 - 0 - 0 - - - 8951 - 1 - 0 - 0 - 0 - - - 8952 - 100 - 0 - 0 - 0 - - - 8953 - 100 - 0 - 0 - 0 - - - 8954 - 100 - 0 - 0 - 0 - - - 8955 - 100 - 0 - 0 - 0 - - - 8956 - 100 - 0 - 0 - 0 - - - 8957 - 100 - 0 - 0 - 0 - - - 8958 - 100 - 0 - 0 - 0 - - - 8959 - 50 - 0 - 0 - 0 - - - 8960 - 50 - 0 - 0 - 0 - - - 8961 - 50 - 0 - 0 - 0 - - - 8962 - 50 - 0 - 0 - 0 - - - 8963 - 50 - 0 - 0 - 0 - - - 8964 - 50 - 0 - 0 - 0 - - - 8965 - 50 - 0 - 0 - 0 - - - 8966 - 200 - 0 - 0 - 0 - - - 8967 - 200 - 0 - 0 - 0 - - - 8968 - 300 - 0 - 0 - 0 - - - 8969 - 400 - 0 - 0 - 0 - - - 8970 - 500 - 0 - 0 - 0 - - - 8971 - 600 - 0 - 0 - 0 - - - 8972 - 1 - 0 - 0 - 0 - - - 8973 - 1 - 0 - 0 - 0 - - - 8974 - 1 - 0 - 0 - 0 - - - 8975 - 1 - 0 - 0 - 0 - - - 8976 - 1 - 0 - 0 - 0 - - - 8977 - 1 - 0 - 0 - 0 - - - 8978 - 1 - 0 - 0 - 0 - - - 8979 - 1 - 0 - 0 - 0 - - - 8980 - 1 - 0 - 0 - 0 - - - 8981 - 1 - 0 - 0 - 0 - - - 8982 - 1 - 0 - 0 - 0 - - - 8983 - 1 - 0 - 0 - 0 - - - 8984 - 1 - 0 - 0 - 0 - - - 8985 - 1 - 0 - 0 - 0 - - - 8986 - 1 - 0 - 0 - 0 - - - 8987 - 1 - 0 - 0 - 0 - - - 8988 - 5 - 0 - 0 - 0 - - - 8989 - 1 - 0 - 0 - 0 - - - 8990 - 1 - 0 - 0 - 0 - - - 8991 - 100 - 0 - 0 - 0 - - - 8992 - 100 - 0 - 0 - 0 - - - 8993 - 100 - 0 - 0 - 0 - - - 8994 - 100 - 0 - 0 - 0 - - - 8995 - 100 - 0 - 0 - 0 - - - 8996 - 100 - 0 - 0 - 0 - - - 8997 - 100 - 0 - 0 - 0 - - - 8998 - 100 - 0 - 0 - 0 - - - 8999 - 100 - 0 - 0 - 0 - - - 9000 - 100 - 0 - 0 - 0 - - - 9001 - 100 - 0 - 0 - 0 - - - 9002 - 100 - 0 - 0 - 0 - - - 9003 - 2 - 0 - 0 - 0 - - - 9004 - 2 - 0 - 0 - 0 - - - 9005 - 1 - 0 - 0 - 0 - - - 9006 - 1 - 0 - 0 - 0 - - - 9007 - 1 - 0 - 0 - 0 - - - 9008 - 1 - 0 - 0 - 0 - - - 9009 - 1 - 0 - 0 - 0 - - - 9010 - 1 - 0 - 0 - 0 - - - 9011 - 1 - 0 - 0 - 0 - - - 9012 - 1 - 0 - 0 - 0 - - - 9013 - 1 - 0 - 0 - 0 - - - 9014 - 2 - 0 - 0 - 0 - - - 9015 - 2 - 0 - 0 - 0 - - - 9016 - 150 - 0 - 0 - 0 - - - 9017 - 75 - 0 - 0 - 0 - - - 9018 - 150 - 0 - 0 - 0 - - - 9019 - 75 - 0 - 0 - 0 - - - 9020 - 1 - 0 - 0 - 0 - - - 9021 - 280 - 0 - 0 - 0 - - - 9022 - 230 - 0 - 0 - 0 - - - 9023 - 180 - 0 - 0 - 0 - - - 9024 - 130 - 0 - 0 - 0 - - - 9025 - 1 - 0 - 0 - 0 - - - 9026 - 10 - 0 - 0 - 0 - - - 9027 - 10 - 0 - 0 - 0 - - - 9028 - 30 - 0 - 0 - 0 - - - 9029 - 30 - 0 - 0 - 0 - - - 9030 - 20 - 0 - 0 - 0 - - - 9031 - 20 - 0 - 0 - 0 - - - 9032 - 10 - 0 - 0 - 0 - - - 9033 - 10 - 0 - 0 - 0 - - - 9034 - 30 - 0 - 0 - 0 - - - 9035 - 30 - 0 - 0 - 0 - - - 9036 - 10 - 0 - 0 - 0 - - - 9037 - 10 - 0 - 0 - 0 - - - 9038 - 20 - 0 - 0 - 0 - - - 9039 - 20 - 0 - 0 - 0 - - - 9040 - 30 - 0 - 0 - 0 - - - 9041 - 30 - 0 - 0 - 0 - - - 9042 - 20 - 0 - 0 - 0 - - - 9043 - 20 - 0 - 0 - 0 - - - 9044 - 100 - 0 - 0 - 0 - - - 9045 - 100 - 0 - 0 - 0 - - - 9046 - 100 - 0 - 0 - 0 - - - 9047 - 100 - 0 - 0 - 0 - - - 9048 - 100 - 0 - 0 - 0 - - - 9049 - 100 - 0 - 0 - 0 - - - 9050 - 100 - 0 - 0 - 0 - - - 9051 - 100 - 0 - 0 - 0 - - - 9052 - 1 - 0 - 0 - 0 - - - 9053 - 1 - 0 - 0 - 0 - - - 9054 - 40 - 0 - 0 - 0 - - - 9055 - 40 - 0 - 0 - 0 - - - 9056 - 40 - 0 - 0 - 0 - - - 9057 - 40 - 0 - 0 - 0 - - - 9058 - 40 - 0 - 0 - 0 - - - 9059 - 40 - 0 - 0 - 0 - - - 9060 - 1 - 0 - 0 - 0 - - - 9061 - 1 - 0 - 0 - 0 - - - 9062 - 1 - 0 - 0 - 0 - - - 9063 - 1 - 0 - 0 - 0 - - - 9064 - 20 - 0 - 0 - 0 - - - 9065 - 20 - 0 - 0 - 0 - - - 9066 - 10 - 0 - 0 - 0 - - - 9067 - 4 - 0 - 0 - 0 - - - 9068 - 1000 - 0 - 0 - 0 - - - 9069 - 1000 - 0 - 0 - 0 - - - 9070 - 1000 - 0 - 0 - 0 - - - 9071 - 1000 - 0 - 0 - 0 - - - 9072 - 900 - 0 - 0 - 0 - - - 9073 - 900 - 0 - 0 - 0 - - - 9074 - 200 - 0 - 0 - 0 - - - 9075 - 220 - 0 - 0 - 0 - - - 9076 - 3 - 0 - 0 - 0 - - - 9077 - 8 - 0 - 0 - 0 - - - 9078 - 1 - 0 - 0 - 0 - - - 9079 - 20 - 0 - 0 - 0 - - - 9080 - 60 - 0 - 0 - 0 - - - 9081 - 60 - 0 - 0 - 0 - - - 9082 - 20 - 0 - 0 - 0 - - - 9083 - 1 - 0 - 0 - 0 - - - 9084 - 1 - 0 - 0 - 0 - - - 9085 - 1 - 0 - 0 - 0 - - - 9086 - 1 - 0 - 0 - 0 - - - 9087 - 1 - 0 - 0 - 0 - - - 9088 - 1 - 0 - 0 - 0 - - - 9089 - 1 - 0 - 0 - 0 - - - 9090 - 1 - 0 - 0 - 0 - - - 9091 - 15 - 0 - 0 - 0 - - - 9092 - 15 - 0 - 0 - 0 - - - 9093 - 15 - 0 - 0 - 0 - - - 9094 - 1 - 0 - 0 - 0 - - - 9095 - 1 - 0 - 0 - 0 - - - 9096 - 100 - 0 - 0 - 0 - - - 9097 - 100 - 0 - 0 - 0 - - - 9098 - 100 - 0 - 0 - 0 - - - 9099 - 100 - 0 - 0 - 0 - - - 9100 - 100 - 0 - 0 - 0 - - - 9101 - 2 - 0 - 0 - 0 - - - 9102 - 30 - 0 - 0 - 0 - - - 9103 - 30 - 0 - 0 - 0 - - - 9104 - 100 - 0 - 0 - 0 - - - 9105 - 1 - 0 - 0 - 0 - - - 9106 - 100 - 0 - 0 - 0 - - - 9107 - 1 - 0 - 0 - 0 - - - 9108 - 1 - 0 - 0 - 0 - - - 9109 - 1 - 0 - 0 - 0 - - - 9110 - 1 - 0 - 0 - 0 - - - 9111 - 1 - 0 - 0 - 0 - - - 9112 - 1 - 0 - 0 - 0 - - - 9113 - 1 - 0 - 0 - 0 - - - 9114 - 1 - 0 - 0 - 0 - - - 9115 - 1 - 0 - 0 - 0 - - - 9116 - 1 - 0 - 0 - 0 - - - 9117 - 1 - 0 - 0 - 0 - - - 9118 - 1 - 0 - 0 - 0 - - - 9119 - 1 - 0 - 0 - 0 - - - 9120 - 1 - 0 - 0 - 0 - - - 9121 - 1 - 0 - 0 - 0 - - - 9122 - 1 - 0 - 0 - 0 - - - 9123 - 1 - 0 - 0 - 0 - - - 9124 - 1 - 0 - 0 - 0 - - - 9125 - 1 - 0 - 0 - 0 - - - 9126 - 1 - 0 - 0 - 0 - - - 9127 - 1 - 0 - 0 - 0 - - - 9128 - 1 - 0 - 0 - 0 - - - 9129 - 1 - 0 - 0 - 0 - - - 9130 - 1 - 0 - 0 - 0 - - - 9131 - 1 - 0 - 0 - 0 - - - 9132 - 1 - 0 - 0 - 0 - - - 9133 - 1 - 0 - 0 - 0 - - - 9134 - 1 - 0 - 0 - 0 - - - 9135 - 1 - 0 - 0 - 0 - - - 9136 - 1 - 0 - 0 - 0 - - - 9137 - 1 - 0 - 0 - 0 - - - 9138 - 1 - 0 - 0 - 0 - - - 9139 - 2 - 0 - 0 - 0 - - - 9140 - 2 - 0 - 0 - 0 - - - 9141 - 8 - 0 - 0 - 0 - - - 9142 - 20 - 0 - 0 - 0 - - - 9143 - 58 - 0 - 0 - 0 - - - 9144 - 300 - 0 - 0 - 0 - - - 9145 - 5 - 0 - 0 - 0 - - - 9146 - 1 - 0 - 0 - 0 - - - 9147 - 1 - 0 - 0 - 0 - - - 9148 - 1 - 0 - 0 - 0 - - - 9149 - 1 - 0 - 0 - 0 - - - 9150 - 1 - 0 - 0 - 0 - - - 9151 - 1 - 0 - 0 - 0 - - - 9152 - 1 - 0 - 0 - 0 - - - 9153 - 1 - 0 - 0 - 0 - - - 9154 - 1 - 0 - 0 - 0 - - - 9155 - 1 - 0 - 0 - 0 - - - 9156 - 1 - 0 - 0 - 0 - - - 9157 - 1 - 0 - 0 - 0 - - - 9158 - 1 - 0 - 0 - 0 - - - 9159 - 1 - 0 - 0 - 0 - - - 9160 - 1 - 0 - 0 - 0 - - - 9161 - 1 - 0 - 0 - 0 - - - 9162 - 1 - 0 - 0 - 0 - - - 9163 - 1 - 0 - 0 - 0 - - - 9164 - 1 - 0 - 0 - 0 - - - 9165 - 1 - 0 - 0 - 0 - - - 9166 - 1 - 0 - 0 - 0 - - - 9167 - 1 - 0 - 0 - 0 - - - 9168 - 1 - 0 - 0 - 0 - - - 9169 - 1 - 0 - 0 - 0 - - - 9170 - 1 - 0 - 0 - 0 - - - 9171 - 1 - 0 - 0 - 0 - - - 9172 - 1 - 0 - 0 - 0 - - - 9173 - 1 - 0 - 0 - 0 - - - 9174 - 73 - 0 - 0 - 0 - - - 9175 - 73 - 0 - 0 - 0 - - - 9176 - 93 - 0 - 0 - 0 - - - 9177 - 157 - 0 - 0 - 0 - - - 9178 - 157 - 0 - 0 - 0 - - - 9179 - 360 - 0 - 0 - 0 - - - 9180 - 360 - 0 - 0 - 0 - - - 9181 - 783 - 0 - 0 - 0 - - - 9182 - 783 - 0 - 0 - 0 - - - 9183 - 1767 - 0 - 0 - 0 - - - 9184 - 1767 - 0 - 0 - 0 - - - 9185 - 16200 - 0 - 0 - 0 - - - 9186 - 16200 - 0 - 0 - 0 - - - 9187 - 10 - 0 - 0 - 0 - - - 9188 - 13 - 0 - 0 - 0 - - - 9189 - 17 - 0 - 0 - 0 - - - 9190 - 33 - 0 - 0 - 0 - - - 9191 - 67 - 0 - 0 - 0 - - - 9192 - 133 - 0 - 0 - 0 - - - 9193 - 667 - 0 - 0 - 0 - - - 9194 - 1000 - 0 - 0 - 0 - - - 9195 - 1 - 0 - 0 - 0 - - - 9196 - 1 - 0 - 0 - 0 - - - 9197 - 1 - 0 - 0 - 0 - - - 9198 - 1 - 0 - 0 - 0 - - - 9199 - 1 - 0 - 0 - 0 - - - 9200 - 1 - 0 - 0 - 0 - - - 9201 - 1 - 0 - 0 - 0 - - - 9202 - 1 - 0 - 0 - 0 - - - 9203 - 1 - 0 - 0 - 0 - - - 9204 - 1 - 0 - 0 - 0 - - - 9205 - 1 - 0 - 0 - 0 - - - 9206 - 1 - 0 - 0 - 0 - - - 9207 - 1 - 0 - 0 - 0 - - - 9208 - 1 - 0 - 0 - 0 - - - 9209 - 1 - 0 - 0 - 0 - - - 9210 - 1 - 0 - 0 - 0 - - - 9211 - 1 - 0 - 0 - 0 - - - 9212 - 1 - 0 - 0 - 0 - - - 9213 - 1 - 0 - 0 - 0 - - - 9214 - 1 - 0 - 0 - 0 - - - 9215 - 1 - 0 - 0 - 0 - - - 9216 - 1 - 0 - 0 - 0 - - - 9217 - 1 - 0 - 0 - 0 - - - 9218 - 1 - 0 - 0 - 0 - - - 9219 - 1 - 0 - 0 - 0 - - - 9220 - 1 - 0 - 0 - 0 - - - 9221 - 1 - 0 - 0 - 0 - - - 9222 - 1 - 0 - 0 - 0 - - - 9223 - 1 - 0 - 0 - 0 - - - 9224 - 1 - 0 - 0 - 0 - - - 9225 - 1 - 0 - 0 - 0 - - - 9226 - 1 - 0 - 0 - 0 - - - 9227 - 1 - 0 - 0 - 0 - - - 9228 - 1 - 0 - 0 - 0 - - - 9229 - 1 - 0 - 0 - 0 - - - 9230 - 1 - 0 - 0 - 0 - - - 9231 - 1 - 0 - 0 - 0 - - - 9232 - 1 - 0 - 0 - 0 - - - 9233 - 1 - 0 - 0 - 0 - - - 9234 - 1 - 0 - 0 - 0 - - - 9235 - 1 - 0 - 0 - 0 - - - 9236 - 7 - 0 - 0 - 0 - - - 9237 - 13 - 0 - 0 - 0 - - - 9238 - 15 - 0 - 0 - 0 - - - 9239 - 24 - 0 - 0 - 0 - - - 9240 - 40 - 0 - 0 - 0 - - - 9241 - 59 - 0 - 0 - 0 - - - 9242 - 138 - 0 - 0 - 0 - - - 9243 - 211 - 0 - 0 - 0 - - - 9244 - 1063 - 0 - 0 - 0 - - - 9245 - 15000 - 0 - 0 - 0 - - - 9246 - 1 - 0 - 0 - 0 - - - 9247 - 1 - 0 - 0 - 0 - - - 9248 - 1 - 0 - 0 - 0 - - - 9249 - 1 - 0 - 0 - 0 - - - 9250 - 1 - 0 - 0 - 0 - - - 9251 - 1 - 0 - 0 - 0 - - - 9252 - 1 - 0 - 0 - 0 - - - 9253 - 1 - 0 - 0 - 0 - - - 9254 - 1 - 0 - 0 - 0 - - - 9255 - 1 - 0 - 0 - 0 - - - 9256 - 1 - 0 - 0 - 0 - - - 9257 - 1 - 0 - 0 - 0 - - - 9258 - 1 - 0 - 0 - 0 - - - 9259 - 1 - 0 - 0 - 0 - - - 9260 - 1 - 0 - 0 - 0 - - - 9261 - 1 - 0 - 0 - 0 - - - 9262 - 1 - 0 - 0 - 0 - - - 9263 - 1 - 0 - 0 - 0 - - - 9264 - 1 - 0 - 0 - 0 - - - 9265 - 1 - 0 - 0 - 0 - - - 9266 - 1 - 0 - 0 - 0 - - - 9267 - 1 - 0 - 0 - 0 - - - 9268 - 1 - 0 - 0 - 0 - - - 9269 - 1 - 0 - 0 - 0 - - - 9270 - 1 - 0 - 0 - 0 - - - 9271 - 1 - 0 - 0 - 0 - - - 9272 - 1 - 0 - 0 - 0 - - - 9273 - 1 - 0 - 0 - 0 - - - 9274 - 1 - 0 - 0 - 0 - - - 9275 - 1 - 0 - 0 - 0 - - - 9276 - 1 - 0 - 0 - 0 - - - 9277 - 1 - 0 - 0 - 0 - - - 9278 - 1 - 0 - 0 - 0 - - - 9279 - 1 - 0 - 0 - 0 - - - 9280 - 1 - 0 - 0 - 0 - - - 9281 - 1 - 0 - 0 - 0 - - - 9282 - 1 - 0 - 0 - 0 - - - 9283 - 1 - 0 - 0 - 0 - - - 9284 - 1 - 0 - 0 - 0 - - - 9285 - 1 - 0 - 0 - 0 - - - 9286 - 2 - 0 - 0 - 0 - - - 9287 - 2 - 0 - 0 - 0 - - - 9288 - 8 - 0 - 0 - 0 - - - 9289 - 20 - 0 - 0 - 0 - - - 9290 - 58 - 0 - 0 - 0 - - - 9291 - 300 - 0 - 0 - 0 - - - 9292 - 5 - 0 - 0 - 0 - - - 9293 - 2 - 0 - 0 - 0 - - - 9294 - 2 - 0 - 0 - 0 - - - 9295 - 8 - 0 - 0 - 0 - - - 9296 - 20 - 0 - 0 - 0 - - - 9297 - 58 - 0 - 0 - 0 - - - 9298 - 300 - 0 - 0 - 0 - - - 9299 - 5 - 0 - 0 - 0 - - - 9300 - 2 - 0 - 0 - 0 - - - 9301 - 2 - 0 - 0 - 0 - - - 9302 - 8 - 0 - 0 - 0 - - - 9303 - 20 - 0 - 0 - 0 - - - 9304 - 58 - 0 - 0 - 0 - - - 9305 - 300 - 0 - 0 - 0 - - - 9306 - 5 - 0 - 0 - 0 - - - 9307 - 1 - 0 - 0 - 0 - - - 9308 - 1 - 0 - 0 - 0 - - - 9309 - 1 - 0 - 0 - 0 - - - 9310 - 1 - 0 - 0 - 0 - - - 9311 - 1 - 0 - 0 - 0 - - - 9312 - 1 - 0 - 0 - 0 - - - 9313 - 1 - 0 - 0 - 0 - - - 9314 - 1 - 0 - 0 - 0 - - - 9315 - 1 - 0 - 0 - 0 - - - 9316 - 1 - 0 - 0 - 0 - - - 9317 - 1 - 0 - 0 - 0 - - - 9318 - 1 - 0 - 0 - 0 - - - 9319 - 1 - 0 - 0 - 0 - - - 9320 - 1 - 0 - 0 - 0 - - - 9321 - 1 - 0 - 0 - 0 - - - 9322 - 1 - 0 - 0 - 0 - - - 9323 - 1 - 0 - 0 - 0 - - - 9324 - 1 - 0 - 0 - 0 - - - 9325 - 1 - 0 - 0 - 0 - - - 9326 - 1 - 0 - 0 - 0 - - - 9327 - 1 - 0 - 0 - 0 - - - 9328 - 1 - 0 - 0 - 0 - - - 9329 - 1 - 0 - 0 - 0 - - - 9330 - 1 - 0 - 0 - 0 - - - 9331 - 1 - 0 - 0 - 0 - - - 9332 - 1 - 0 - 0 - 0 - - - 9333 - 1 - 0 - 0 - 0 - - - 9334 - 1 - 0 - 0 - 0 - - - 9335 - 12 - 0 - 0 - 0 - - - 9336 - 22 - 0 - 0 - 0 - - - 9337 - 37 - 0 - 0 - 0 - - - 9338 - 53 - 0 - 0 - 0 - - - 9339 - 125 - 0 - 0 - 0 - - - 9340 - 192 - 0 - 0 - 0 - - - 9341 - 967 - 0 - 0 - 0 - - - 9342 - 13633 - 0 - 0 - 0 - - - 9343 - 1 - 0 - 0 - 0 - - - 9344 - 1 - 0 - 0 - 0 - - - 9345 - 1 - 0 - 0 - 0 - - - 9346 - 1 - 0 - 0 - 0 - - - 9347 - 1 - 0 - 0 - 0 - - - 9348 - 1 - 0 - 0 - 0 - - - 9349 - 1 - 0 - 0 - 0 - - - 9350 - 1 - 0 - 0 - 0 - - - 9351 - 1 - 0 - 0 - 0 - - - 9352 - 1 - 0 - 0 - 0 - - - 9353 - 1 - 0 - 0 - 0 - - - 9354 - 1 - 0 - 0 - 0 - - - 9355 - 1 - 0 - 0 - 0 - - - 9356 - 1 - 0 - 0 - 0 - - - 9357 - 1 - 0 - 0 - 0 - - - 9358 - 1 - 0 - 0 - 0 - - - 9359 - 1 - 0 - 0 - 0 - - - 9360 - 1 - 0 - 0 - 0 - - - 9361 - 1 - 0 - 0 - 0 - - - 9362 - 1 - 0 - 0 - 0 - - - 9363 - 1 - 0 - 0 - 0 - - - 9364 - 1 - 0 - 0 - 0 - - - 9365 - 1 - 0 - 0 - 0 - - - 9366 - 1 - 0 - 0 - 0 - - - 9367 - 1 - 0 - 0 - 0 - - - 9368 - 1 - 0 - 0 - 0 - - - 9369 - 1 - 0 - 0 - 0 - - - 9370 - 1 - 0 - 0 - 0 - - - 9371 - 1 - 0 - 0 - 0 - - - 9372 - 1 - 0 - 0 - 0 - - - 9373 - 1 - 0 - 0 - 0 - - - 9374 - 1 - 0 - 0 - 0 - - - 9375 - 1 - 0 - 0 - 0 - - - 9376 - 1 - 0 - 0 - 0 - - - 9377 - 1 - 0 - 0 - 0 - - - 9378 - 1 - 0 - 0 - 0 - - - 9379 - 1 - 0 - 0 - 0 - - - 9380 - 1 - 0 - 0 - 0 - - - 9381 - 1 - 0 - 0 - 0 - - - 9382 - 1 - 0 - 0 - 0 - - - 9383 - 1 - 0 - 0 - 0 - - - 9384 - 1 - 0 - 0 - 0 - - - 9385 - 1 - 0 - 0 - 0 - - - 9386 - 1 - 0 - 0 - 0 - - - 9387 - 1 - 0 - 0 - 0 - - - 9388 - 1 - 0 - 0 - 0 - - - 9389 - 1 - 0 - 0 - 0 - - - 9390 - 1 - 0 - 0 - 0 - - - 9391 - 1 - 0 - 0 - 0 - - - 9392 - 1 - 0 - 0 - 0 - - - 9393 - 1 - 0 - 0 - 0 - - - 9394 - 1 - 0 - 0 - 0 - - - 9395 - 1 - 0 - 0 - 0 - - - 9396 - 1 - 0 - 0 - 0 - - - 9397 - 1 - 0 - 0 - 0 - - - 9398 - 1 - 0 - 0 - 0 - - - 9399 - 1 - 0 - 0 - 0 - - - 9400 - 1 - 0 - 0 - 0 - - - 9401 - 1 - 0 - 0 - 0 - - - 9402 - 1 - 0 - 0 - 0 - - - 9403 - 1 - 0 - 0 - 0 - - - 9404 - 1 - 0 - 0 - 0 - - - 9405 - 1 - 0 - 0 - 0 - - - 9406 - 1 - 0 - 0 - 0 - - - 9407 - 1 - 0 - 0 - 0 - - - 9408 - 1 - 0 - 0 - 0 - - - 9409 - 1 - 0 - 0 - 0 - - - 9410 - 1 - 0 - 0 - 0 - - - 9411 - 1 - 0 - 0 - 0 - - - 9412 - 1 - 0 - 0 - 0 - - - 9413 - 1 - 0 - 0 - 0 - - - 9414 - 1 - 0 - 0 - 0 - - - 9415 - 1 - 0 - 0 - 0 - - - 9416 - 30 - 0 - 0 - 0 - - - 9417 - 30 - 0 - 0 - 0 - - - 9418 - 48 - 0 - 0 - 0 - - - 9419 - 1 - 0 - 0 - 0 - - - 9420 - 20 - 0 - 0 - 0 - - - 9421 - 20 - 0 - 0 - 0 - - - 9422 - 33 - 0 - 0 - 0 - - - 9423 - 70 - 0 - 0 - 0 - - - 9424 - 70 - 0 - 0 - 0 - - - 9425 - 250 - 0 - 0 - 0 - - - 9426 - 250 - 0 - 0 - 0 - - - 9427 - 650 - 0 - 0 - 0 - - - 9428 - 650 - 0 - 0 - 0 - - - 9429 - 1600 - 0 - 0 - 0 - - - 9430 - 1600 - 0 - 0 - 0 - - - 9431 - 16000 - 0 - 0 - 0 - - - 9432 - 16000 - 0 - 0 - 0 - - - 9433 - 5000 - 0 - 0 - 0 - - - 9434 - 25 - 0 - 0 - 0 - - - 9435 - 25 - 0 - 0 - 0 - - - 9436 - 2 - 0 - 0 - 0 - - - 9437 - 2 - 0 - 0 - 0 - - - 9438 - 1 - 0 - 0 - 0 - - - 9439 - 1 - 0 - 0 - 0 - - - 9440 - 20 - 0 - 0 - 0 - - - 9441 - 20 - 0 - 0 - 0 - - - 9442 - 27 - 0 - 0 - 0 - - - 9443 - 27 - 0 - 0 - 0 - - - 9444 - 53 - 0 - 0 - 0 - - - 9445 - 53 - 0 - 0 - 0 - - - 9446 - 77 - 0 - 0 - 0 - - - 9447 - 77 - 0 - 0 - 0 - - - 9448 - 100 - 0 - 0 - 0 - - - 9449 - 100 - 0 - 0 - 0 - - - 9450 - 133 - 0 - 0 - 0 - - - 9451 - 133 - 0 - 0 - 0 - - - 9452 - 167 - 0 - 0 - 0 - - - 9453 - 167 - 0 - 0 - 0 - - - 9454 - 40 - 0 - 0 - 0 - - - 9455 - 40 - 0 - 0 - 0 - - - 9456 - 60 - 0 - 0 - 0 - - - 9457 - 123 - 0 - 0 - 0 - - - 9458 - 123 - 0 - 0 - 0 - - - 9459 - 327 - 0 - 0 - 0 - - - 9460 - 327 - 0 - 0 - 0 - - - 9461 - 750 - 0 - 0 - 0 - - - 9462 - 750 - 0 - 0 - 0 - - - 9463 - 1733 - 0 - 0 - 0 - - - 9464 - 1733 - 0 - 0 - 0 - - - 9465 - 16167 - 0 - 0 - 0 - - - 9466 - 16167 - 0 - 0 - 0 - - - 9467 - 18 - 0 - 0 - 0 - - - 9468 - 1 - 0 - 0 - 0 - - - 9469 - 250 - 0 - 0 - 0 - - - 9470 - 1000 - 0 - 0 - 0 - - - 9471 - 1000 - 0 - 0 - 0 - - - 9472 - 1000 - 0 - 0 - 0 - - - 9473 - 1000 - 0 - 0 - 0 - - - 9474 - 250 - 0 - 0 - 0 - - - 9475 - 250 - 0 - 0 - 0 - - - 9476 - 250 - 0 - 0 - 0 - - - 9477 - 1 - 0 - 0 - 0 - - - 9478 - 2 - 0 - 0 - 0 - - - 9479 - 2 - 0 - 0 - 0 - - - 9480 - 2 - 0 - 0 - 0 - - - 9481 - 2 - 0 - 0 - 0 - - - 9482 - 2 - 0 - 0 - 0 - - - 9483 - 2 - 0 - 0 - 0 - - - 9484 - 2 - 0 - 0 - 0 - - - 9485 - 2 - 0 - 0 - 0 - - - 9486 - 2 - 0 - 0 - 0 - - - 9487 - 1 - 0 - 0 - 0 - - - 9488 - 1 - 0 - 0 - 0 - - - 9489 - 1 - 0 - 0 - 0 - - - 9490 - 1 - 0 - 0 - 0 - - - 9491 - 1 - 0 - 0 - 0 - - - 9492 - 1 - 0 - 0 - 0 - - - 9493 - 1 - 0 - 0 - 0 - - - 9494 - 1 - 0 - 0 - 0 - - - 9495 - 1 - 0 - 0 - 0 - - - 9496 - 1 - 0 - 0 - 0 - - - 9497 - 1 - 0 - 0 - 0 - - - 9498 - 1 - 0 - 0 - 0 - - - 9499 - 1 - 0 - 0 - 0 - - - 9500 - 1 - 0 - 0 - 0 - - - 9501 - 1 - 0 - 0 - 0 - - - 9502 - 1 - 0 - 0 - 0 - - - 9503 - 1 - 0 - 0 - 0 - - - 9504 - 1 - 0 - 0 - 0 - - - 9505 - 1 - 0 - 0 - 0 - - - 9506 - 1 - 0 - 0 - 0 - - - 9507 - 1 - 0 - 0 - 0 - - - 9508 - 1 - 0 - 0 - 0 - - - 9509 - 1 - 0 - 0 - 0 - - - 9510 - 1 - 0 - 0 - 0 - - - 9511 - 1 - 0 - 0 - 0 - - - 9512 - 1 - 0 - 0 - 0 - - - 9513 - 1 - 0 - 0 - 0 - - - 9514 - 1 - 0 - 0 - 0 - - - 9515 - 1 - 0 - 0 - 0 - - - 9516 - 1 - 0 - 0 - 0 - - - 9517 - 1 - 0 - 0 - 0 - - - 9518 - 1 - 0 - 0 - 0 - - - 9519 - 1 - 0 - 0 - 0 - - - 9520 - 1 - 0 - 0 - 0 - - - 9521 - 1 - 0 - 0 - 0 - - - 9522 - 1 - 0 - 0 - 0 - - - 9523 - 1 - 0 - 0 - 0 - - - 9524 - 1 - 0 - 0 - 0 - - - 9525 - 1 - 0 - 0 - 0 - - - 9526 - 1 - 0 - 0 - 0 - - - 9527 - 1 - 0 - 0 - 0 - - - 9528 - 1 - 0 - 0 - 0 - - - 9529 - 1 - 0 - 0 - 0 - - - 9530 - 1 - 0 - 0 - 0 - - - 9531 - 1 - 0 - 0 - 0 - - - 9532 - 1 - 0 - 0 - 0 - - - 9533 - 1 - 0 - 0 - 0 - - - 9534 - 1 - 0 - 0 - 0 - - - 9535 - 1 - 0 - 0 - 0 - - - 9536 - 1 - 0 - 0 - 0 - - - 9537 - 1 - 0 - 0 - 0 - - - 9538 - 1 - 0 - 0 - 0 - - - 9539 - 1 - 0 - 0 - 0 - - - 9540 - 1 - 0 - 0 - 0 - - - 9541 - 1 - 0 - 0 - 0 - - - 9542 - 1 - 0 - 0 - 0 - - - 9543 - 1 - 0 - 0 - 0 - - - 9544 - 1 - 0 - 0 - 0 - - - 9545 - 1 - 0 - 0 - 0 - - - 9546 - 1 - 0 - 0 - 0 - - - 9547 - 1 - 0 - 0 - 0 - - - 9548 - 1 - 0 - 0 - 0 - - - 9549 - 1 - 0 - 0 - 0 - - - 9550 - 1 - 0 - 0 - 0 - - - 9551 - 1 - 0 - 0 - 0 - - - 9552 - 1 - 0 - 0 - 0 - - - 9553 - 1 - 0 - 0 - 0 - - - 9554 - 1 - 0 - 0 - 0 - - - 9555 - 1 - 0 - 0 - 0 - - - 9556 - 1 - 0 - 0 - 0 - - - 9557 - 1 - 0 - 0 - 0 - - - 9558 - 2 - 0 - 0 - 0 - - - 9559 - 2 - 0 - 0 - 0 - - - 9560 - 2 - 0 - 0 - 0 - - - 9561 - 2 - 0 - 0 - 0 - - - 9562 - 2 - 0 - 0 - 0 - - - 9563 - 2 - 0 - 0 - 0 - - - 9564 - 2 - 0 - 0 - 0 - - - 9565 - 2 - 0 - 0 - 0 - - - 9566 - 2 - 0 - 0 - 0 - - - 9567 - 2 - 0 - 0 - 0 - - - 9568 - 2 - 0 - 0 - 0 - - - 9569 - 2 - 0 - 0 - 0 - - - 9570 - 2 - 0 - 0 - 0 - - - 9571 - 2 - 0 - 0 - 0 - - - 9572 - 2 - 0 - 0 - 0 - - - 9573 - 2 - 0 - 0 - 0 - - - 9574 - 2 - 0 - 0 - 0 - - - 9575 - 2 - 0 - 0 - 0 - - - 9576 - 2 - 0 - 0 - 0 - - - 9577 - 2 - 0 - 0 - 0 - - - 9578 - 2 - 0 - 0 - 0 - - - 9579 - 2 - 0 - 0 - 0 - - - 9580 - 2 - 0 - 0 - 0 - - - 9581 - 2 - 0 - 0 - 0 - - - 9582 - 2 - 0 - 0 - 0 - - - 9583 - 2 - 0 - 0 - 0 - - - 9584 - 2 - 0 - 0 - 0 - - - 9585 - 10 - 0 - 0 - 0 - - - 9586 - 10 - 0 - 0 - 0 - - - 9587 - 10 - 0 - 0 - 0 - - - 9588 - 2 - 0 - 0 - 0 - - - 9589 - 1 - 0 - 0 - 0 - - - 9590 - 1 - 0 - 0 - 0 - - - 9591 - 1 - 0 - 0 - 0 - - - 9592 - 1 - 0 - 0 - 0 - - - 9593 - 1 - 0 - 0 - 0 - - - 9594 - 1 - 0 - 0 - 0 - - - 9595 - 1 - 0 - 0 - 0 - - - 9596 - 10 - 0 - 0 - 0 - - - 9597 - 1 - 0 - 0 - 0 - - - 9598 - 1 - 0 - 0 - 0 - - - 9599 - 1 - 0 - 0 - 0 - - - 9600 - 1 - 0 - 0 - 0 - - - 9601 - 1 - 0 - 0 - 0 - - - 9602 - 1 - 0 - 0 - 0 - - - 9603 - 1 - 0 - 0 - 0 - - - 9604 - 1 - 0 - 0 - 0 - - - 9605 - 1 - 0 - 0 - 0 - - - 9606 - 1 - 0 - 0 - 0 - - - 9607 - 1 - 0 - 0 - 0 - - - 9608 - 1 - 0 - 0 - 0 - - - 9609 - 1 - 0 - 0 - 0 - - - 9610 - 1 - 0 - 0 - 0 - - - 9611 - 1 - 0 - 0 - 0 - - - 9612 - 1 - 0 - 0 - 0 - - - 9613 - 1 - 0 - 0 - 0 - - - 9614 - 1 - 0 - 0 - 0 - - - 9615 - 1 - 0 - 0 - 0 - - - 9616 - 1 - 0 - 0 - 0 - - - 9617 - 1 - 0 - 0 - 0 - - - 9618 - 1 - 0 - 0 - 0 - - - 9619 - 1 - 0 - 0 - 0 - - - 9620 - 1 - 0 - 0 - 0 - - - 9621 - 1 - 0 - 0 - 0 - - - 9622 - 1 - 0 - 0 - 0 - - - 9623 - 1 - 0 - 0 - 0 - - - 9624 - 1 - 0 - 0 - 0 - - - 9625 - 10 - 0 - 0 - 0 - - - 9626 - 10000 - 0 - 0 - 0 - - - 9627 - 2 - 0 - 0 - 0 - - - 9628 - 1 - 0 - 0 - 0 - - - 9629 - 1265 - 0 - 0 - 0 - - - 9630 - 1265 - 0 - 0 - 0 - - - 9631 - 2 - 0 - 0 - 0 - - - 9632 - 100 - 0 - 0 - 0 - - - 9633 - 1 - 0 - 0 - 0 - - - 9634 - 650 - 0 - 0 - 0 - - - 9635 - 650 - 0 - 0 - 0 - - - 9636 - 650 - 0 - 0 - 0 - - - 9637 - 650 - 0 - 0 - 0 - - - 9638 - 650 - 0 - 0 - 0 - - - 9639 - 650 - 0 - 0 - 0 - - - 9640 - 6 - 0 - 0 - 0 - - - 9641 - 6 - 0 - 0 - 0 - - - 9642 - 6 - 0 - 0 - 0 - - - 9643 - 6 - 0 - 0 - 0 - - - 9644 - 6 - 0 - 0 - 0 - - - 9645 - 6 - 0 - 0 - 0 - - - 9646 - 1 - 0 - 0 - 0 - - - 9647 - 1 - 0 - 0 - 0 - - - 9648 - 1 - 0 - 0 - 0 - - - 9649 - 1 - 0 - 0 - 0 - - - 9650 - 1 - 0 - 0 - 0 - - - 9651 - 1 - 0 - 0 - 0 - - - 9652 - 1 - 0 - 0 - 0 - - - 9653 - 1 - 0 - 0 - 0 - - - 9654 - 1 - 0 - 0 - 0 - - - 9655 - 1 - 0 - 0 - 0 - - - 9656 - 1 - 0 - 0 - 0 - - - 9657 - 1 - 0 - 0 - 0 - - - 9658 - 1 - 0 - 0 - 0 - - - 9659 - 1 - 0 - 0 - 0 - - - 9660 - 1 - 0 - 0 - 0 - - - 9661 - 1 - 0 - 0 - 0 - - - 9662 - 1 - 0 - 0 - 0 - - - 9663 - 1 - 0 - 0 - 0 - - - 9664 - 1 - 0 - 0 - 0 - - - 9665 - 1 - 0 - 0 - 0 - - - 9666 - 25000 - 0 - 0 - 0 - - - 9667 - 25000 - 0 - 0 - 0 - - - 9668 - 20000 - 0 - 0 - 0 - - - 9669 - 20000 - 0 - 0 - 0 - - - 9670 - 25000 - 0 - 0 - 0 - - - 9671 - 25000 - 0 - 0 - 0 - - - 9672 - 8000 - 0 - 0 - 0 - - - 9673 - 8000 - 0 - 0 - 0 - - - 9674 - 12000 - 0 - 0 - 0 - - - 9675 - 12000 - 0 - 0 - 0 - - - 9676 - 10000 - 0 - 0 - 0 - - - 9677 - 10000 - 0 - 0 - 0 - - - 9678 - 10000 - 0 - 0 - 0 - - - 9679 - 10000 - 0 - 0 - 0 - - - 9680 - 1 - 0 - 0 - 0 - - - 9681 - 1 - 0 - 0 - 0 - - - 9682 - 1 - 0 - 0 - 0 - - - 9683 - 1 - 0 - 0 - 0 - - - 9684 - 1 - 0 - 0 - 0 - - - 9685 - 1 - 0 - 0 - 0 - - - 9686 - 1 - 0 - 0 - 0 - - - 9687 - 1 - 0 - 0 - 0 - - - 9688 - 1 - 0 - 0 - 0 - - - 9689 - 1 - 0 - 0 - 0 - - - 9690 - 1 - 0 - 0 - 0 - - - 9691 - 1 - 0 - 0 - 0 - - - 9692 - 1 - 0 - 0 - 0 - - - 9693 - 1 - 0 - 0 - 0 - - - 9694 - 1 - 0 - 0 - 0 - - - 9695 - 1 - 0 - 0 - 0 - - - 9696 - 1 - 0 - 0 - 0 - - - 9697 - 1 - 0 - 0 - 0 - - - 9698 - 1 - 0 - 0 - 0 - - - 9699 - 1 - 0 - 0 - 0 - - - 9700 - 1 - 0 - 0 - 0 - - - 9701 - 1 - 0 - 0 - 0 - - - 9702 - 1 - 0 - 0 - 0 - - - 9703 - 1 - 0 - 0 - 0 - - - 9704 - 1 - 0 - 0 - 0 - - - 9705 - 1 - 0 - 0 - 0 - - - 9706 - 1 - 0 - 0 - 0 - - - 9707 - 1 - 0 - 0 - 0 - - - 9708 - 1 - 0 - 0 - 0 - - - 9709 - 1 - 0 - 0 - 0 - - - 9710 - 1 - 0 - 0 - 0 - - - 9711 - 1 - 0 - 0 - 0 - - - 9712 - 1 - 0 - 0 - 0 - - - 9713 - 1 - 0 - 0 - 0 - - - 9714 - 1 - 0 - 0 - 0 - - - 9715 - 10 - 0 - 0 - 0 - - - 9716 - 1 - 0 - 0 - 0 - - - 9717 - 1 - 0 - 0 - 0 - - - 9718 - 1 - 0 - 0 - 0 - - - 9719 - 1 - 0 - 0 - 0 - - - 9720 - 1 - 0 - 0 - 0 - - - 9721 - 1 - 0 - 0 - 0 - - - 9722 - 1 - 0 - 0 - 0 - - - 9723 - 1 - 0 - 0 - 0 - - - 9724 - 1 - 0 - 0 - 0 - - - 9725 - 1 - 0 - 0 - 0 - - - 9726 - 1 - 0 - 0 - 0 - - - 9727 - 1 - 0 - 0 - 0 - - - 9728 - 1 - 0 - 0 - 0 - - - 9729 - 20 - 0 - 0 - 0 - - - 9730 - 20 - 0 - 0 - 0 - - - 9731 - 3000 - 0 - 0 - 0 - - - 9732 - 3000 - 0 - 0 - 0 - - - 9733 - 3000 - 0 - 0 - 0 - - - 9734 - 3000 - 0 - 0 - 0 - - - 9735 - 12 - 0 - 0 - 0 - - - 9736 - 12 - 0 - 0 - 0 - - - 9737 - 12 - 0 - 0 - 0 - - - 9738 - 12 - 0 - 0 - 0 - - - 9739 - 215 - 0 - 0 - 0 - - - 9740 - 215 - 0 - 0 - 0 - - - 9741 - 160 - 0 - 0 - 0 - - - 9742 - 160 - 0 - 0 - 0 - - - 9743 - 105 - 0 - 0 - 0 - - - 9744 - 105 - 0 - 0 - 0 - - - 9745 - 52 - 0 - 0 - 0 - - - 9746 - 52 - 0 - 0 - 0 - - - 9747 - 99000 - 0 - 0 - 0 - - - 9748 - 99000 - 0 - 0 - 0 - - - 9749 - 1 - 0 - 0 - 0 - - - 9750 - 99000 - 0 - 0 - 0 - - - 9751 - 99000 - 0 - 0 - 0 - - - 9752 - 1 - 0 - 0 - 0 - - - 9753 - 99000 - 0 - 0 - 0 - - - 9754 - 99000 - 0 - 0 - 0 - - - 9755 - 1 - 0 - 0 - 0 - - - 9756 - 99000 - 0 - 0 - 0 - - - 9757 - 99000 - 0 - 0 - 0 - - - 9758 - 1 - 0 - 0 - 0 - - - 9759 - 99000 - 0 - 0 - 0 - - - 9760 - 99000 - 0 - 0 - 0 - - - 9761 - 1 - 0 - 0 - 0 - - - 9762 - 99000 - 0 - 0 - 0 - - - 9763 - 99000 - 0 - 0 - 0 - - - 9764 - 1 - 0 - 0 - 0 - - - 9765 - 99000 - 0 - 0 - 0 - - - 9766 - 99000 - 0 - 0 - 0 - - - 9767 - 1 - 0 - 0 - 0 - - - 9768 - 99000 - 0 - 0 - 0 - - - 9769 - 99000 - 0 - 0 - 0 - - - 9770 - 1 - 0 - 0 - 0 - - - 9771 - 99000 - 0 - 0 - 0 - - - 9772 - 99000 - 0 - 0 - 0 - - - 9773 - 1 - 0 - 0 - 0 - - - 9774 - 99000 - 0 - 0 - 0 - - - 9775 - 99000 - 0 - 0 - 0 - - - 9776 - 1 - 0 - 0 - 0 - - - 9777 - 99000 - 0 - 0 - 0 - - - 9778 - 99000 - 0 - 0 - 0 - - - 9779 - 1 - 0 - 0 - 0 - - - 9780 - 99000 - 0 - 0 - 0 - - - 9781 - 99000 - 0 - 0 - 0 - - - 9782 - 1 - 0 - 0 - 0 - - - 9783 - 99000 - 0 - 0 - 0 - - - 9784 - 99000 - 0 - 0 - 0 - - - 9785 - 1 - 0 - 0 - 0 - - - 9786 - 99000 - 0 - 0 - 0 - - - 9787 - 99000 - 0 - 0 - 0 - - - 9788 - 1 - 0 - 0 - 0 - - - 9789 - 99000 - 0 - 0 - 0 - - - 9790 - 99000 - 0 - 0 - 0 - - - 9791 - 1 - 0 - 0 - 0 - - - 9792 - 99000 - 0 - 0 - 0 - - - 9793 - 99000 - 0 - 0 - 0 - - - 9794 - 1 - 0 - 0 - 0 - - - 9795 - 99000 - 0 - 0 - 0 - - - 9796 - 99000 - 0 - 0 - 0 - - - 9797 - 1 - 0 - 0 - 0 - - - 9798 - 99000 - 0 - 0 - 0 - - - 9799 - 99000 - 0 - 0 - 0 - - - 9800 - 1 - 0 - 0 - 0 - - - 9801 - 99000 - 0 - 0 - 0 - - - 9802 - 99000 - 0 - 0 - 0 - - - 9803 - 1 - 0 - 0 - 0 - - - 9804 - 99000 - 0 - 0 - 0 - - - 9805 - 99000 - 0 - 0 - 0 - - - 9806 - 1 - 0 - 0 - 0 - - - 9807 - 99000 - 0 - 0 - 0 - - - 9808 - 99000 - 0 - 0 - 0 - - - 9809 - 1 - 0 - 0 - 0 - - - 9810 - 99000 - 0 - 0 - 0 - - - 9811 - 99000 - 0 - 0 - 0 - - - 9812 - 1 - 0 - 0 - 0 - - - 9813 - 99000 - 0 - 0 - 0 - - - 9814 - 1 - 0 - 0 - 0 - - - 9815 - 1 - 0 - 0 - 0 - - - 9816 - 1 - 0 - 0 - 0 - - - 9817 - 1 - 0 - 0 - 0 - - - 9818 - 1 - 0 - 0 - 0 - - - 9819 - 1 - 0 - 0 - 0 - - - 9820 - 1 - 0 - 0 - 0 - - - 9821 - 1 - 0 - 0 - 0 - - - 9822 - 1 - 0 - 0 - 0 - - - 9823 - 1 - 0 - 0 - 0 - - - 9824 - 1 - 0 - 0 - 0 - - - 9825 - 1 - 0 - 0 - 0 - - - 9826 - 1 - 0 - 0 - 0 - - - 9827 - 1 - 0 - 0 - 0 - - - 9828 - 1 - 0 - 0 - 0 - - - 9829 - 1 - 0 - 0 - 0 - - - 9830 - 1 - 0 - 0 - 0 - - - 9831 - 1 - 0 - 0 - 0 - - - 9832 - 1 - 0 - 0 - 0 - - - 9833 - 1 - 0 - 0 - 0 - - - 9834 - 1 - 0 - 0 - 0 - - - 9835 - 1 - 0 - 0 - 0 - - - 9836 - 1 - 0 - 0 - 0 - - - 9837 - 1 - 0 - 0 - 0 - - - 9838 - 1 - 0 - 0 - 0 - - - 9839 - 1 - 0 - 0 - 0 - - - 9840 - 1 - 0 - 0 - 0 - - - 9841 - 1 - 0 - 0 - 0 - - - 9842 - 50000 - 0 - 0 - 0 - - - 9843 - 10 - 0 - 0 - 0 - - - 9844 - 10 - 0 - 0 - 0 - - - 9845 - 10 - 0 - 0 - 0 - - - 9846 - 10 - 0 - 0 - 0 - - - 9847 - 10 - 0 - 0 - 0 - - - 9848 - 10 - 0 - 0 - 0 - - - 9849 - 10 - 0 - 0 - 0 - - - 9850 - 10 - 0 - 0 - 0 - - - 9851 - 10 - 0 - 0 - 0 - - - 9852 - 10 - 0 - 0 - 0 - - - 9853 - 10 - 0 - 0 - 0 - - - 9854 - 10 - 0 - 0 - 0 - - - 9855 - 10 - 0 - 0 - 0 - - - 9856 - 10 - 0 - 0 - 0 - - - 9857 - 10 - 0 - 0 - 0 - - - 9858 - 10 - 0 - 0 - 0 - - - 9859 - 10 - 0 - 0 - 0 - - - 9860 - 10 - 0 - 0 - 0 - - - 9861 - 10 - 0 - 0 - 0 - - - 9862 - 10 - 0 - 0 - 0 - - - 9863 - 10 - 0 - 0 - 0 - - - 9864 - 10 - 0 - 0 - 0 - - - 9865 - 10 - 0 - 0 - 0 - - - 9866 - 10 - 0 - 0 - 0 - - - 9867 - 10 - 0 - 0 - 0 - - - 9868 - 10 - 0 - 0 - 0 - - - 9869 - 10 - 0 - 0 - 0 - - - 9870 - 10 - 0 - 0 - 0 - - - 9871 - 10 - 0 - 0 - 0 - - - 9872 - 10 - 0 - 0 - 0 - - - 9873 - 10 - 0 - 0 - 0 - - - 9874 - 10 - 0 - 0 - 0 - - - 9875 - 10 - 0 - 0 - 0 - - - 9876 - 10 - 0 - 0 - 0 - - - 9877 - 10 - 0 - 0 - 0 - - - 9878 - 10 - 0 - 0 - 0 - - - 9879 - 10 - 0 - 0 - 0 - - - 9880 - 10 - 0 - 0 - 0 - - - 9881 - 10 - 0 - 0 - 0 - - - 9882 - 10 - 0 - 0 - 0 - - - 9883 - 10 - 0 - 0 - 0 - - - 9884 - 10 - 0 - 0 - 0 - - - 9885 - 10 - 0 - 0 - 0 - - - 9886 - 10 - 0 - 0 - 0 - - - 9887 - 10 - 0 - 0 - 0 - - - 9888 - 10 - 0 - 0 - 0 - - - 9889 - 10 - 0 - 0 - 0 - - - 9890 - 10 - 0 - 0 - 0 - - - 9891 - 10 - 0 - 0 - 0 - - - 9892 - 10 - 0 - 0 - 0 - - - 9893 - 1 - 0 - 0 - 0 - - - 9894 - 1 - 0 - 0 - 0 - - - 9895 - 1 - 0 - 0 - 0 - - - 9896 - 1 - 0 - 0 - 0 - - - 9897 - 1 - 0 - 0 - 0 - - - 9898 - 1 - 0 - 0 - 0 - - - 9899 - 1 - 0 - 0 - 0 - - - 9900 - 1 - 0 - 0 - 0 - - - 9901 - 5 - 0 - 0 - 0 - - - 9902 - 5 - 0 - 0 - 0 - - - 9903 - 20 - 0 - 0 - 0 - - - 9904 - 1 - 0 - 0 - 0 - - - 9905 - 1 - 0 - 0 - 0 - - - 9906 - 1 - 0 - 0 - 0 - - - 9907 - 1 - 0 - 0 - 0 - - - 9908 - 1 - 0 - 0 - 0 - - - 9909 - 1 - 0 - 0 - 0 - - - 9910 - 1 - 0 - 0 - 0 - - - 9911 - 1 - 0 - 0 - 0 - - - 9912 - 1 - 0 - 0 - 0 - - - 9913 - 1 - 0 - 0 - 0 - - - 9914 - 1 - 0 - 0 - 0 - - - 9915 - 1 - 0 - 0 - 0 - - - 9916 - 1 - 0 - 0 - 0 - - - 9917 - 1 - 0 - 0 - 0 - - - 9918 - 1 - 0 - 0 - 0 - - - 9919 - 1 - 0 - 0 - 0 - - - 9920 - 1 - 0 - 0 - 0 - - - 9921 - 1 - 0 - 0 - 0 - - - 9922 - 1 - 0 - 0 - 0 - - - 9923 - 1 - 0 - 0 - 0 - - - 9924 - 1 - 0 - 0 - 0 - - - 9925 - 1 - 0 - 0 - 0 - - - 9926 - 3 - 0 - 0 - 0 - - - 9927 - 3 - 0 - 0 - 0 - - - 9928 - 3 - 0 - 0 - 0 - - - 9929 - 3 - 0 - 0 - 0 - - - 9930 - 3 - 0 - 0 - 0 - - - 9931 - 3 - 0 - 0 - 0 - - - 9932 - 1 - 0 - 0 - 0 - - - 9933 - 1 - 0 - 0 - 0 - - - 9934 - 1 - 0 - 0 - 0 - - - 9935 - 1 - 0 - 0 - 0 - - - 9936 - 1 - 0 - 0 - 0 - - - 9937 - 1 - 0 - 0 - 0 - - - 9938 - 1 - 0 - 0 - 0 - - - 9939 - 1 - 0 - 0 - 0 - - - 9940 - 1 - 0 - 0 - 0 - - - 9941 - 1 - 0 - 0 - 0 - - - 9942 - 1 - 0 - 0 - 0 - - - 9943 - 1 - 0 - 0 - 0 - - - 9944 - 1 - 0 - 0 - 0 - - - 9945 - 1 - 0 - 0 - 0 - - - 9946 - 1 - 0 - 0 - 0 - - - 9947 - 1 - 0 - 0 - 0 - - - 9948 - 99000 - 0 - 0 - 0 - - - 9949 - 99000 - 0 - 0 - 0 - - - 9950 - 1 - 0 - 0 - 0 - - - 9951 - 1 - 0 - 0 - 0 - - - 9952 - 1 - 0 - 0 - 0 - - - 9953 - 1 - 0 - 0 - 0 - - - 9954 - 1 - 0 - 0 - 0 - - - 9955 - 1 - 0 - 0 - 0 - - - 9956 - 1 - 0 - 0 - 0 - - - 9957 - 1 - 0 - 0 - 0 - - - 9958 - 1 - 0 - 0 - 0 - - - 9959 - 1 - 0 - 0 - 0 - - - 9960 - 1 - 0 - 0 - 0 - - - 9961 - 1 - 0 - 0 - 0 - - - 9962 - 1 - 0 - 0 - 0 - - - 9963 - 1 - 0 - 0 - 0 - - - 9964 - 1 - 0 - 0 - 0 - - - 9965 - 1 - 0 - 0 - 0 - - - 9966 - 1 - 0 - 0 - 0 - - - 9967 - 1 - 0 - 0 - 0 - - - 9968 - 1 - 0 - 0 - 0 - - - 9969 - 1 - 0 - 0 - 0 - - - 9970 - 1 - 0 - 0 - 0 - - - 9971 - 1 - 0 - 0 - 0 - - - 9972 - 1 - 0 - 0 - 0 - - - 9973 - 1 - 0 - 0 - 0 - - - 9974 - 1 - 0 - 0 - 0 - - - 9975 - 1 - 0 - 0 - 0 - - - 9976 - 1 - 0 - 0 - 0 - - - 9977 - 1 - 0 - 0 - 0 - - - 9978 - 17 - 0 - 0 - 0 - - - 9979 - 17 - 0 - 0 - 0 - - - 9980 - 17 - 0 - 0 - 0 - - - 9981 - 17 - 0 - 0 - 0 - - - 9982 - 1 - 0 - 0 - 0 - - - 9983 - 1 - 0 - 0 - 0 - - - 9984 - 81 - 0 - 0 - 0 - - - 9985 - 81 - 0 - 0 - 0 - - - 9986 - 25 - 0 - 0 - 0 - - - 9987 - 25 - 0 - 0 - 0 - - - 9988 - 25 - 0 - 0 - 0 - - - 9989 - 25 - 0 - 0 - 0 - - - 9990 - 1 - 0 - 0 - 0 - - - 9991 - 1 - 0 - 0 - 0 - - - 9992 - 89 - 0 - 0 - 0 - - - 9993 - 89 - 0 - 0 - 0 - - - 9994 - 3 - 0 - 0 - 0 - - - 9995 - 3 - 0 - 0 - 0 - - - 9996 - 8 - 0 - 0 - 0 - - - 9997 - 8 - 0 - 0 - 0 - - - 9998 - 15 - 0 - 0 - 0 - - - 9999 - 15 - 0 - 0 - 0 - - - 10000 - 12 - 0 - 0 - 0 - - - 10001 - 12 - 0 - 0 - 0 - - - 10002 - 9 - 0 - 0 - 0 - - - 10003 - 9 - 0 - 0 - 0 - - - 10004 - 6 - 0 - 0 - 0 - - - 10005 - 6 - 0 - 0 - 0 - - - 10006 - 6 - 0 - 0 - 0 - - - 10007 - 6 - 0 - 0 - 0 - - - 10008 - 38 - 0 - 0 - 0 - - - 10009 - 38 - 0 - 0 - 0 - - - 10010 - 24 - 0 - 0 - 0 - - - 10011 - 24 - 0 - 0 - 0 - - - 10012 - 1 - 0 - 0 - 0 - - - 10013 - 1 - 0 - 0 - 0 - - - 10014 - 70 - 0 - 0 - 0 - - - 10015 - 70 - 0 - 0 - 0 - - - 10016 - 50 - 0 - 0 - 0 - - - 10017 - 50 - 0 - 0 - 0 - - - 10018 - 30 - 0 - 0 - 0 - - - 10019 - 30 - 0 - 0 - 0 - - - 10020 - 18 - 0 - 0 - 0 - - - 10021 - 18 - 0 - 0 - 0 - - - 10022 - 1 - 0 - 0 - 0 - - - 10023 - 1 - 0 - 0 - 0 - - - 10024 - 1 - 0 - 0 - 0 - - - 10025 - 720 - 0 - 0 - 0 - - - 10026 - 720 - 0 - 0 - 0 - - - 10027 - 600 - 0 - 0 - 0 - - - 10028 - 600 - 0 - 0 - 0 - - - 10029 - 1 - 0 - 0 - 0 - - - 10030 - 1 - 0 - 0 - 0 - - - 10031 - 18 - 0 - 0 - 0 - - - 10032 - 18 - 0 - 0 - 0 - - - 10033 - 140 - 0 - 0 - 0 - - - 10034 - 160 - 0 - 0 - 0 - - - 10035 - 200 - 0 - 0 - 0 - - - 10036 - 200 - 0 - 0 - 0 - - - 10037 - 200 - 0 - 0 - 0 - - - 10038 - 200 - 0 - 0 - 0 - - - 10039 - 1000 - 0 - 0 - 0 - - - 10040 - 1000 - 0 - 0 - 0 - - - 10041 - 100 - 0 - 0 - 0 - - - 10042 - 100 - 0 - 0 - 0 - - - 10043 - 100 - 0 - 0 - 0 - - - 10044 - 100 - 0 - 0 - 0 - - - 10045 - 500 - 0 - 0 - 0 - - - 10046 - 500 - 0 - 0 - 0 - - - 10047 - 150 - 0 - 0 - 0 - - - 10048 - 150 - 0 - 0 - 0 - - - 10049 - 150 - 0 - 0 - 0 - - - 10050 - 150 - 0 - 0 - 0 - - - 10051 - 750 - 0 - 0 - 0 - - - 10052 - 750 - 0 - 0 - 0 - - - 10053 - 20 - 0 - 0 - 0 - - - 10054 - 20 - 0 - 0 - 0 - - - 10055 - 20 - 0 - 0 - 0 - - - 10056 - 20 - 0 - 0 - 0 - - - 10057 - 20 - 0 - 0 - 0 - - - 10058 - 20 - 0 - 0 - 0 - - - 10059 - 20 - 0 - 0 - 0 - - - 10060 - 20 - 0 - 0 - 0 - - - 10061 - 20 - 0 - 0 - 0 - - - 10062 - 20 - 0 - 0 - 0 - - - 10063 - 20 - 0 - 0 - 0 - - - 10064 - 20 - 0 - 0 - 0 - - - 10065 - 20 - 0 - 0 - 0 - - - 10066 - 20 - 0 - 0 - 0 - - - 10067 - 20 - 0 - 0 - 0 - - - 10068 - 20 - 0 - 0 - 0 - - - 10069 - 400 - 0 - 0 - 0 - - - 10070 - 400 - 0 - 0 - 0 - - - 10071 - 800 - 0 - 0 - 0 - - - 10072 - 800 - 0 - 0 - 0 - - - 10073 - 400 - 0 - 0 - 0 - - - 10074 - 800 - 0 - 0 - 0 - - - 10075 - 600 - 0 - 0 - 0 - - - 10076 - 600 - 0 - 0 - 0 - - - 10077 - 160 - 0 - 0 - 0 - - - 10078 - 160 - 0 - 0 - 0 - - - 10079 - 2500 - 0 - 0 - 0 - - - 10080 - 2500 - 0 - 0 - 0 - - - 10081 - 3000 - 0 - 0 - 0 - - - 10082 - 3000 - 0 - 0 - 0 - - - 10083 - 3600 - 0 - 0 - 0 - - - 10084 - 3600 - 0 - 0 - 0 - - - 10085 - 4320 - 0 - 0 - 0 - - - 10086 - 4320 - 0 - 0 - 0 - - - 10087 - 20 - 0 - 0 - 0 - - - 10088 - 10 - 0 - 0 - 0 - - - 10089 - 16 - 0 - 0 - 0 - - - 10090 - 13 - 0 - 0 - 0 - - - 10091 - 15 - 0 - 0 - 0 - - - 10092 - 40 - 0 - 0 - 0 - - - 10093 - 72 - 0 - 0 - 0 - - - 10094 - 72 - 0 - 0 - 0 - - - 10095 - 96 - 0 - 0 - 0 - - - 10096 - 96 - 0 - 0 - 0 - - - 10097 - 108 - 0 - 0 - 0 - - - 10098 - 108 - 0 - 0 - 0 - - - 10099 - 144 - 0 - 0 - 0 - - - 10100 - 144 - 0 - 0 - 0 - - - 10101 - 144 - 0 - 0 - 0 - - - 10102 - 144 - 0 - 0 - 0 - - - 10103 - 192 - 0 - 0 - 0 - - - 10104 - 192 - 0 - 0 - 0 - - - 10105 - 90 - 0 - 0 - 0 - - - 10106 - 90 - 0 - 0 - 0 - - - 10107 - 150 - 0 - 0 - 0 - - - 10108 - 150 - 0 - 0 - 0 - - - 10109 - 160 - 0 - 0 - 0 - - - 10110 - 160 - 0 - 0 - 0 - - - 10111 - 160 - 0 - 0 - 0 - - - 10112 - 160 - 0 - 0 - 0 - - - 10113 - 43 - 0 - 0 - 0 - - - 10114 - 43 - 0 - 0 - 0 - - - 10115 - 210 - 0 - 0 - 0 - - - 10116 - 210 - 0 - 0 - 0 - - - 10117 - 12 - 0 - 0 - 0 - - - 10118 - 12 - 0 - 0 - 0 - - - 10119 - 16 - 0 - 0 - 0 - - - 10120 - 16 - 0 - 0 - 0 - - - 10121 - 14 - 0 - 0 - 0 - - - 10122 - 14 - 0 - 0 - 0 - - - 10123 - 20 - 0 - 0 - 0 - - - 10124 - 20 - 0 - 0 - 0 - - - 10125 - 150 - 0 - 0 - 0 - - - 10126 - 150 - 0 - 0 - 0 - - - 10127 - 270 - 0 - 0 - 0 - - - 10128 - 270 - 0 - 0 - 0 - - - 10129 - 36 - 0 - 0 - 0 - - - 10130 - 36 - 0 - 0 - 0 - - - 10131 - 1 - 0 - 0 - 0 - - - 10132 - 30 - 0 - 0 - 0 - - - 10133 - 30 - 0 - 0 - 0 - - - 10134 - 30 - 0 - 0 - 0 - - - 10135 - 30 - 0 - 0 - 0 - - - 10136 - 120 - 0 - 0 - 0 - - - 10137 - 120 - 0 - 0 - 0 - - - 10138 - 120 - 0 - 0 - 0 - - - 10139 - 120 - 0 - 0 - 0 - - - 10140 - 1 - 0 - 0 - 0 - - - 10141 - 1 - 0 - 0 - 0 - - - 10142 - 3 - 0 - 0 - 0 - - - 10143 - 1 - 0 - 0 - 0 - - - 10144 - 1 - 0 - 0 - 0 - - - 10145 - 1 - 0 - 0 - 0 - - - 10146 - 100 - 0 - 0 - 0 - - - 10147 - 150 - 0 - 0 - 0 - - - 10148 - 200 - 0 - 0 - 0 - - - 10149 - 50 - 0 - 0 - 0 - - - 10150 - 4 - 0 - 0 - 0 - - - 10151 - 4 - 0 - 0 - 0 - - - 10152 - 1 - 0 - 0 - 0 - - - 10153 - 1 - 0 - 0 - 0 - - - 10154 - 1 - 0 - 0 - 0 - - - 10155 - 1 - 0 - 0 - 0 - - - 10156 - 1300 - 0 - 0 - 0 - - - 10157 - 1300 - 0 - 0 - 0 - - - 10158 - 18 - 0 - 0 - 0 - - - 10159 - 31 - 0 - 0 - 0 - - - 10160 - 100 - 0 - 0 - 0 - - - 10161 - 150 - 0 - 0 - 0 - - - 10162 - 200 - 0 - 0 - 0 - - - 10163 - 50 - 0 - 0 - 0 - - - 10164 - 1 - 0 - 0 - 0 - - - 10165 - 1 - 0 - 0 - 0 - - - 10166 - 1 - 0 - 0 - 0 - - - 10167 - 1 - 0 - 0 - 0 - - - 10168 - 1 - 0 - 0 - 0 - - - 10169 - 1 - 0 - 0 - 0 - - - 10170 - 1 - 0 - 0 - 0 - - - 10171 - 1 - 0 - 0 - 0 - - - 10172 - 1 - 0 - 0 - 0 - - - 10173 - 1 - 0 - 0 - 0 - - - 10174 - 1 - 0 - 0 - 0 - - - 10175 - 1 - 0 - 0 - 0 - - - 10176 - 1 - 0 - 0 - 0 - - - 10177 - 1 - 0 - 0 - 0 - - - 10178 - 1 - 0 - 0 - 0 - - - 10179 - 1 - 0 - 0 - 0 - - - 10180 - 1 - 0 - 0 - 0 - - - 10181 - 50 - 0 - 0 - 0 - - - 10182 - 1 - 0 - 0 - 0 - - - 10183 - 50 - 0 - 0 - 0 - - - 10184 - 1 - 0 - 0 - 0 - - - 10185 - 50 - 0 - 0 - 0 - - - 10186 - 1 - 0 - 0 - 0 - - - 10187 - 50 - 0 - 0 - 0 - - - 10188 - 1 - 0 - 0 - 0 - - - 10189 - 50 - 0 - 0 - 0 - - - 10190 - 1 - 0 - 0 - 0 - - - 10191 - 50 - 0 - 0 - 0 - - - 10192 - 1 - 0 - 0 - 0 - - - 10193 - 50 - 0 - 0 - 0 - - - 10194 - 1 - 0 - 0 - 0 - - - 10195 - 50 - 0 - 0 - 0 - - - 10196 - 1 - 0 - 0 - 0 - - - 10197 - 50 - 0 - 0 - 0 - - - 10198 - 1 - 0 - 0 - 0 - - - 10199 - 50 - 0 - 0 - 0 - - - 10200 - 1 - 0 - 0 - 0 - - - 10201 - 50 - 0 - 0 - 0 - - - 10202 - 1 - 0 - 0 - 0 - - - 10203 - 50 - 0 - 0 - 0 - - - 10204 - 1 - 0 - 0 - 0 - - - 10205 - 50 - 0 - 0 - 0 - - - 10206 - 1 - 0 - 0 - 0 - - - 10207 - 50 - 0 - 0 - 0 - - - 10208 - 1 - 0 - 0 - 0 - - - 10209 - 50 - 0 - 0 - 0 - - - 10210 - 1 - 0 - 0 - 0 - - - 10211 - 50 - 0 - 0 - 0 - - - 10212 - 1 - 0 - 0 - 0 - - - 10213 - 50 - 0 - 0 - 0 - - - 10214 - 1 - 0 - 0 - 0 - - - 10215 - 50 - 0 - 0 - 0 - - - 10216 - 1 - 0 - 0 - 0 - - - 10217 - 50 - 0 - 0 - 0 - - - 10218 - 1 - 0 - 0 - 0 - - - 10219 - 50 - 0 - 0 - 0 - - - 10220 - 1 - 0 - 0 - 0 - - - 10221 - 50 - 0 - 0 - 0 - - - 10222 - 1 - 0 - 0 - 0 - - - 10223 - 50 - 0 - 0 - 0 - - - 10224 - 1 - 0 - 0 - 0 - - - 10225 - 50 - 0 - 0 - 0 - - - 10226 - 1 - 0 - 0 - 0 - - - 10227 - 50 - 0 - 0 - 0 - - - 10228 - 1 - 0 - 0 - 0 - - - 10229 - 50 - 0 - 0 - 0 - - - 10230 - 1 - 0 - 0 - 0 - - - 10231 - 50 - 0 - 0 - 0 - - - 10232 - 1 - 0 - 0 - 0 - - - 10233 - 50 - 0 - 0 - 0 - - - 10234 - 1 - 0 - 0 - 0 - - - 10235 - 50 - 0 - 0 - 0 - - - 10236 - 1 - 0 - 0 - 0 - - - 10237 - 50 - 0 - 0 - 0 - - - 10238 - 1 - 0 - 0 - 0 - - - 10239 - 50 - 0 - 0 - 0 - - - 10240 - 1 - 0 - 0 - 0 - - - 10241 - 50 - 0 - 0 - 0 - - - 10242 - 1 - 0 - 0 - 0 - - - 10243 - 50 - 0 - 0 - 0 - - - 10244 - 1 - 0 - 0 - 0 - - - 10245 - 50 - 0 - 0 - 0 - - - 10246 - 1 - 0 - 0 - 0 - - - 10247 - 50 - 0 - 0 - 0 - - - 10248 - 1 - 0 - 0 - 0 - - - 10249 - 50 - 0 - 0 - 0 - - - 10250 - 1 - 0 - 0 - 0 - - - 10251 - 50 - 0 - 0 - 0 - - - 10252 - 1 - 0 - 0 - 0 - - - 10253 - 50 - 0 - 0 - 0 - - - 10254 - 1 - 0 - 0 - 0 - - - 10255 - 50 - 0 - 0 - 0 - - - 10256 - 1 - 0 - 0 - 0 - - - 10257 - 50 - 0 - 0 - 0 - - - 10258 - 1 - 0 - 0 - 0 - - - 10259 - 50 - 0 - 0 - 0 - - - 10260 - 1 - 0 - 0 - 0 - - - 10261 - 50 - 0 - 0 - 0 - - - 10262 - 1 - 0 - 0 - 0 - - - 10263 - 50 - 0 - 0 - 0 - - - 10264 - 1 - 0 - 0 - 0 - - - 10265 - 50 - 0 - 0 - 0 - - - 10266 - 1 - 0 - 0 - 0 - - - 10267 - 50 - 0 - 0 - 0 - - - 10268 - 1 - 0 - 0 - 0 - - - 10269 - 50 - 0 - 0 - 0 - - - 10270 - 1 - 0 - 0 - 0 - - - 10271 - 50 - 0 - 0 - 0 - - - 10272 - 1 - 0 - 0 - 0 - - - 10273 - 50 - 0 - 0 - 0 - - - 10274 - 1 - 0 - 0 - 0 - - - 10275 - 50 - 0 - 0 - 0 - - - 10276 - 1 - 0 - 0 - 0 - - - 10277 - 50 - 0 - 0 - 0 - - - 10278 - 1 - 0 - 0 - 0 - - - 10279 - 50 - 0 - 0 - 0 - - - 10280 - 300 - 0 - 0 - 0 - - - 10281 - 300 - 0 - 0 - 0 - - - 10282 - 1800 - 0 - 0 - 0 - - - 10283 - 1800 - 0 - 0 - 0 - - - 10284 - 2500 - 0 - 0 - 0 - - - 10285 - 2500 - 0 - 0 - 0 - - - 10286 - 35200 - 0 - 0 - 0 - - - 10287 - 35200 - 0 - 0 - 0 - - - 10288 - 35200 - 0 - 0 - 0 - - - 10289 - 35200 - 0 - 0 - 0 - - - 10290 - 35200 - 0 - 0 - 0 - - - 10291 - 35200 - 0 - 0 - 0 - - - 10292 - 35200 - 0 - 0 - 0 - - - 10293 - 35200 - 0 - 0 - 0 - - - 10294 - 35200 - 0 - 0 - 0 - - - 10295 - 35200 - 0 - 0 - 0 - - - 10296 - 3520 - 0 - 0 - 0 - - - 10297 - 3520 - 0 - 0 - 0 - - - 10298 - 3520 - 0 - 0 - 0 - - - 10299 - 3520 - 0 - 0 - 0 - - - 10300 - 3520 - 0 - 0 - 0 - - - 10301 - 3520 - 0 - 0 - 0 - - - 10302 - 3520 - 0 - 0 - 0 - - - 10303 - 3520 - 0 - 0 - 0 - - - 10304 - 3520 - 0 - 0 - 0 - - - 10305 - 3520 - 0 - 0 - 0 - - - 10306 - 1056 - 0 - 0 - 0 - - - 10307 - 1056 - 0 - 0 - 0 - - - 10308 - 1056 - 0 - 0 - 0 - - - 10309 - 1056 - 0 - 0 - 0 - - - 10310 - 1056 - 0 - 0 - 0 - - - 10311 - 1056 - 0 - 0 - 0 - - - 10312 - 1056 - 0 - 0 - 0 - - - 10313 - 1056 - 0 - 0 - 0 - - - 10314 - 1056 - 0 - 0 - 0 - - - 10315 - 1056 - 0 - 0 - 0 - - - 10316 - 3 - 0 - 0 - 0 - - - 10317 - 3 - 0 - 0 - 0 - - - 10318 - 3 - 0 - 0 - 0 - - - 10319 - 3 - 0 - 0 - 0 - - - 10320 - 3 - 0 - 0 - 0 - - - 10321 - 3 - 0 - 0 - 0 - - - 10322 - 3 - 0 - 0 - 0 - - - 10323 - 3 - 0 - 0 - 0 - - - 10324 - 3 - 0 - 0 - 0 - - - 10325 - 3 - 0 - 0 - 0 - - - 10326 - 15 - 0 - 0 - 0 - - - 10327 - 15 - 0 - 0 - 0 - - - 10328 - 1 - 0 - 0 - 0 - - - 10329 - 1 - 0 - 0 - 0 - - - 10330 - 45000 - 0 - 0 - 0 - - - 10331 - 45000 - 0 - 0 - 0 - - - 10332 - 40000 - 0 - 0 - 0 - - - 10333 - 40000 - 0 - 0 - 0 - - - 10334 - 10000 - 0 - 0 - 0 - - - 10335 - 10000 - 0 - 0 - 0 - - - 10336 - 15000 - 0 - 0 - 0 - - - 10337 - 15000 - 0 - 0 - 0 - - - 10338 - 45000 - 0 - 0 - 0 - - - 10339 - 45000 - 0 - 0 - 0 - - - 10340 - 40000 - 0 - 0 - 0 - - - 10341 - 40000 - 0 - 0 - 0 - - - 10342 - 10000 - 0 - 0 - 0 - - - 10343 - 10000 - 0 - 0 - 0 - - - 10344 - 20000 - 0 - 0 - 0 - - - 10345 - 20000 - 0 - 0 - 0 - - - 10346 - 200000 - 0 - 0 - 0 - - - 10347 - 200000 - 0 - 0 - 0 - - - 10348 - 200000 - 0 - 0 - 0 - - - 10349 - 200000 - 0 - 0 - 0 - - - 10350 - 90000 - 0 - 0 - 0 - - - 10351 - 90000 - 0 - 0 - 0 - - - 10352 - 180000 - 0 - 0 - 0 - - - 10353 - 180000 - 0 - 0 - 0 - - - 10354 - 17625 - 0 - 0 - 0 - - - 10355 - 17625 - 0 - 0 - 0 - - - 10356 - 17625 - 0 - 0 - 0 - - - 10357 - 17625 - 0 - 0 - 0 - - - 10358 - 17625 - 0 - 0 - 0 - - - 10359 - 17625 - 0 - 0 - 0 - - - 10360 - 17625 - 0 - 0 - 0 - - - 10361 - 17625 - 0 - 0 - 0 - - - 10362 - 17625 - 0 - 0 - 0 - - - 10363 - 17625 - 0 - 0 - 0 - - - 10364 - 2025 - 0 - 0 - 0 - - - 10365 - 2025 - 0 - 0 - 0 - - - 10366 - 900 - 0 - 0 - 0 - - - 10367 - 900 - 0 - 0 - 0 - - - 10368 - 4000 - 0 - 0 - 0 - - - 10369 - 4000 - 0 - 0 - 0 - - - 10370 - 13000 - 0 - 0 - 0 - - - 10371 - 13000 - 0 - 0 - 0 - - - 10372 - 6000 - 0 - 0 - 0 - - - 10373 - 6000 - 0 - 0 - 0 - - - 10374 - 2000 - 0 - 0 - 0 - - - 10375 - 2000 - 0 - 0 - 0 - - - 10376 - 6000 - 0 - 0 - 0 - - - 10377 - 6000 - 0 - 0 - 0 - - - 10378 - 13000 - 0 - 0 - 0 - - - 10379 - 13000 - 0 - 0 - 0 - - - 10380 - 6000 - 0 - 0 - 0 - - - 10381 - 6000 - 0 - 0 - 0 - - - 10382 - 2000 - 0 - 0 - 0 - - - 10383 - 2000 - 0 - 0 - 0 - - - 10384 - 4000 - 0 - 0 - 0 - - - 10385 - 4000 - 0 - 0 - 0 - - - 10386 - 13000 - 0 - 0 - 0 - - - 10387 - 13000 - 0 - 0 - 0 - - - 10388 - 6000 - 0 - 0 - 0 - - - 10389 - 6000 - 0 - 0 - 0 - - - 10390 - 2000 - 0 - 0 - 0 - - - 10391 - 2000 - 0 - 0 - 0 - - - 10392 - 2000 - 0 - 0 - 0 - - - 10393 - 2000 - 0 - 0 - 0 - - - 10394 - 2000 - 0 - 0 - 0 - - - 10395 - 2000 - 0 - 0 - 0 - - - 10396 - 2000 - 0 - 0 - 0 - - - 10397 - 2000 - 0 - 0 - 0 - - - 10398 - 2000 - 0 - 0 - 0 - - - 10399 - 2000 - 0 - 0 - 0 - - - 10400 - 2000 - 0 - 0 - 0 - - - 10401 - 2000 - 0 - 0 - 0 - - - 10402 - 2000 - 0 - 0 - 0 - - - 10403 - 2000 - 0 - 0 - 0 - - - 10404 - 2000 - 0 - 0 - 0 - - - 10405 - 2000 - 0 - 0 - 0 - - - 10406 - 2000 - 0 - 0 - 0 - - - 10407 - 2000 - 0 - 0 - 0 - - - 10408 - 2000 - 0 - 0 - 0 - - - 10409 - 2000 - 0 - 0 - 0 - - - 10410 - 2000 - 0 - 0 - 0 - - - 10411 - 2000 - 0 - 0 - 0 - - - 10412 - 2000 - 0 - 0 - 0 - - - 10413 - 2000 - 0 - 0 - 0 - - - 10414 - 2000 - 0 - 0 - 0 - - - 10415 - 2000 - 0 - 0 - 0 - - - 10416 - 2000 - 0 - 0 - 0 - - - 10417 - 2000 - 0 - 0 - 0 - - - 10418 - 2000 - 0 - 0 - 0 - - - 10419 - 2000 - 0 - 0 - 0 - - - 10420 - 2000 - 0 - 0 - 0 - - - 10421 - 2000 - 0 - 0 - 0 - - - 10422 - 2000 - 0 - 0 - 0 - - - 10423 - 2000 - 0 - 0 - 0 - - - 10424 - 2000 - 0 - 0 - 0 - - - 10425 - 2000 - 0 - 0 - 0 - - - 10426 - 2000 - 0 - 0 - 0 - - - 10427 - 2000 - 0 - 0 - 0 - - - 10428 - 2000 - 0 - 0 - 0 - - - 10429 - 2000 - 0 - 0 - 0 - - - 10430 - 2000 - 0 - 0 - 0 - - - 10431 - 2000 - 0 - 0 - 0 - - - 10432 - 2000 - 0 - 0 - 0 - - - 10433 - 2000 - 0 - 0 - 0 - - - 10434 - 2000 - 0 - 0 - 0 - - - 10435 - 2000 - 0 - 0 - 0 - - - 10436 - 2000 - 0 - 0 - 0 - - - 10437 - 2000 - 0 - 0 - 0 - - - 10438 - 2000 - 0 - 0 - 0 - - - 10439 - 2000 - 0 - 0 - 0 - - - 10440 - 5000 - 0 - 0 - 0 - - - 10441 - 5000 - 0 - 0 - 0 - - - 10442 - 5000 - 0 - 0 - 0 - - - 10443 - 5000 - 0 - 0 - 0 - - - 10444 - 5000 - 0 - 0 - 0 - - - 10445 - 5000 - 0 - 0 - 0 - - - 10446 - 2000 - 0 - 0 - 0 - - - 10447 - 2000 - 0 - 0 - 0 - - - 10448 - 2000 - 0 - 0 - 0 - - - 10449 - 2000 - 0 - 0 - 0 - - - 10450 - 2000 - 0 - 0 - 0 - - - 10451 - 2000 - 0 - 0 - 0 - - - 10452 - 5000 - 0 - 0 - 0 - - - 10453 - 5000 - 0 - 0 - 0 - - - 10454 - 5000 - 0 - 0 - 0 - - - 10455 - 5000 - 0 - 0 - 0 - - - 10456 - 5000 - 0 - 0 - 0 - - - 10457 - 5000 - 0 - 0 - 0 - - - 10458 - 7000 - 0 - 0 - 0 - - - 10459 - 7000 - 0 - 0 - 0 - - - 10460 - 7000 - 0 - 0 - 0 - - - 10461 - 7000 - 0 - 0 - 0 - - - 10462 - 7000 - 0 - 0 - 0 - - - 10463 - 7000 - 0 - 0 - 0 - - - 10464 - 7000 - 0 - 0 - 0 - - - 10465 - 7000 - 0 - 0 - 0 - - - 10466 - 7000 - 0 - 0 - 0 - - - 10467 - 7000 - 0 - 0 - 0 - - - 10468 - 7000 - 0 - 0 - 0 - - - 10469 - 7000 - 0 - 0 - 0 - - - 10470 - 2500 - 0 - 0 - 0 - - - 10471 - 2500 - 0 - 0 - 0 - - - 10472 - 2500 - 0 - 0 - 0 - - - 10473 - 2500 - 0 - 0 - 0 - - - 10474 - 2500 - 0 - 0 - 0 - - - 10475 - 2500 - 0 - 0 - 0 - - - 10476 - 15 - 0 - 0 - 0 - - - 10477 - 1 - 0 - 0 - 0 - - - 10478 - 1 - 0 - 0 - 0 - - - 10479 - 1 - 0 - 0 - 0 - - - 10480 - 1 - 0 - 0 - 0 - - - 10481 - 1 - 0 - 0 - 0 - - - 10482 - 1 - 0 - 0 - 0 - - - 10483 - 1 - 0 - 0 - 0 - - - 10484 - 1 - 0 - 0 - 0 - - - 10485 - 1 - 0 - 0 - 0 - - - 10486 - 1 - 0 - 0 - 0 - - - 10487 - 100 - 0 - 0 - 0 - - - 10488 - 10 - 0 - 0 - 0 - - - 10489 - 15 - 0 - 0 - 0 - - - 10490 - 1 - 0 - 0 - 0 - - - 10491 - 20 - 0 - 0 - 0 - - - 10492 - 1 - 0 - 0 - 0 - - - 10493 - 2 - 0 - 0 - 0 - - - 10494 - 10 - 0 - 0 - 0 - - - 10495 - 10 - 0 - 0 - 0 - - - 10496 - 1 - 0 - 0 - 0 - - - 10497 - 1 - 0 - 0 - 0 - - - 10498 - 769 - 0 - 0 - 0 - - - 10499 - 1000 - 0 - 0 - 0 - - - 10500 - 20 - 0 - 0 - 0 - - - 10501 - 1 - 0 - 0 - 0 - - - 10502 - 1 - 0 - 0 - 0 - - - 10503 - 1 - 0 - 0 - 0 - - - 10504 - 1 - 0 - 0 - 0 - - - 10505 - 1 - 0 - 0 - 0 - - - 10506 - 1 - 0 - 0 - 0 - - - 10507 - 1 - 0 - 0 - 0 - - - 10508 - 1 - 0 - 0 - 0 - - - 10509 - 1 - 0 - 0 - 0 - - - 10510 - 1 - 0 - 0 - 0 - - - 10511 - 1 - 0 - 0 - 0 - - - 10512 - 1 - 0 - 0 - 0 - - - 10513 - 1 - 0 - 0 - 0 - - - 10514 - 1 - 0 - 0 - 0 - - - 10515 - 1 - 0 - 0 - 0 - - - 10516 - 1 - 0 - 0 - 0 - - - 10517 - 1 - 0 - 0 - 0 - - - 10518 - 1 - 0 - 0 - 0 - - - 10519 - 1 - 0 - 0 - 0 - - - 10520 - 1 - 0 - 0 - 0 - - - 10521 - 1 - 0 - 0 - 0 - - - 10522 - 1 - 0 - 0 - 0 - - - 10523 - 1 - 0 - 0 - 0 - - - 10524 - 1 - 0 - 0 - 0 - - - 10525 - 1 - 0 - 0 - 0 - - - 10526 - 1 - 0 - 0 - 0 - - - 10527 - 1 - 0 - 0 - 0 - - - 10528 - 1 - 0 - 0 - 0 - - - 10529 - 1 - 0 - 0 - 0 - - - 10530 - 1 - 0 - 0 - 0 - - - 10531 - 1 - 0 - 0 - 0 - - - 10532 - 1 - 0 - 0 - 0 - - - 10533 - 1 - 0 - 0 - 0 - - - 10534 - 1 - 0 - 0 - 0 - - - 10535 - 1 - 0 - 0 - 0 - - - 10536 - 1 - 0 - 0 - 0 - - - 10537 - 1 - 0 - 0 - 0 - - - 10538 - 1 - 0 - 0 - 0 - - - 10539 - 1 - 0 - 0 - 0 - - - 10540 - 1 - 0 - 0 - 0 - - - 10541 - 1 - 0 - 0 - 0 - - - 10542 - 1 - 0 - 0 - 0 - - - 10543 - 1 - 0 - 0 - 0 - - - 10544 - 1 - 0 - 0 - 0 - - - 10545 - 1 - 0 - 0 - 0 - - - 10546 - 1 - 0 - 0 - 0 - - - 10547 - 5000 - 0 - 0 - 0 - - - 10548 - 5000 - 0 - 0 - 0 - - - 10549 - 5000 - 0 - 0 - 0 - - - 10550 - 5000 - 0 - 0 - 0 - - - 10551 - 20000 - 0 - 0 - 0 - - - 10552 - 5000 - 0 - 0 - 0 - - - 10553 - 200 - 0 - 0 - 0 - - - 10554 - 200 - 0 - 0 - 0 - - - 10555 - 8000 - 0 - 0 - 0 - - - 10556 - 1 - 0 - 0 - 0 - - - 10557 - 1 - 0 - 0 - 0 - - - 10558 - 1 - 0 - 0 - 0 - - - 10559 - 1 - 0 - 0 - 0 - - - 10560 - 1 - 0 - 0 - 0 - - - 10561 - 1 - 0 - 0 - 0 - - - 10562 - 1 - 0 - 0 - 0 - - - 10563 - 1 - 0 - 0 - 0 - - - 10564 - 80000 - 0 - 0 - 0 - - - 10565 - 80000 - 0 - 0 - 0 - - - 10566 - 60000 - 0 - 0 - 0 - - - 10567 - 1 - 0 - 0 - 0 - - - 10568 - 1 - 0 - 0 - 0 - - - 10569 - 1 - 0 - 0 - 0 - - - 10570 - 1 - 0 - 0 - 0 - - - 10571 - 1 - 0 - 0 - 0 - - - 10572 - 1 - 0 - 0 - 0 - - - 10573 - 1 - 0 - 0 - 0 - - - 10574 - 1 - 0 - 0 - 0 - - - 10575 - 1 - 0 - 0 - 0 - - - 10576 - 1 - 0 - 0 - 0 - - - 10577 - 1 - 0 - 0 - 0 - - - 10578 - 1 - 0 - 0 - 0 - - - 10579 - 1 - 0 - 0 - 0 - - - 10580 - 1 - 0 - 0 - 0 - - - 10581 - 10000 - 0 - 0 - 0 - - - 10582 - 10000 - 0 - 0 - 0 - - - 10583 - 10000 - 0 - 0 - 0 - - - 10584 - 10000 - 0 - 0 - 0 - - - 10585 - 1 - 0 - 0 - 0 - - - 10586 - 1 - 0 - 0 - 0 - - - 10587 - 50 - 0 - 0 - 0 - - - 10588 - 300 - 0 - 0 - 0 - - - 10589 - 46000 - 0 - 0 - 0 - - - 10590 - 46000 - 0 - 0 - 0 - - - 10591 - 1 - 0 - 0 - 0 - - - 10592 - 1 - 0 - 0 - 0 - - - 10593 - 1 - 0 - 0 - 0 - - - 10594 - 1 - 0 - 0 - 0 - - - 10595 - 1 - 0 - 0 - 0 - - - 10596 - 1 - 0 - 0 - 0 - - - 10597 - 1 - 0 - 0 - 0 - - - 10598 - 1 - 0 - 0 - 0 - - - 10599 - 1 - 0 - 0 - 0 - - - 10600 - 1 - 0 - 0 - 0 - - - 10601 - 15000 - 0 - 0 - 0 - - - 10602 - 15000 - 0 - 0 - 0 - - - 10603 - 15000 - 0 - 0 - 0 - - - 10604 - 10000 - 0 - 0 - 0 - - - 10605 - 140000 - 0 - 0 - 0 - - - 10606 - 10000 - 0 - 0 - 0 - - - 10607 - 300 - 0 - 0 - 0 - - - 10608 - 1000 - 0 - 0 - 0 - - - 10609 - 100 - 0 - 0 - 0 - - - 10610 - 2000 - 0 - 0 - 0 - - - 10611 - 48000 - 0 - 0 - 0 - - - 10612 - 375 - 0 - 0 - 0 - - - 10613 - 35200 - 0 - 0 - 0 - - - 10614 - 60000 - 0 - 0 - 0 - - - 10615 - 500 - 0 - 0 - 0 - - - 10616 - 500 - 0 - 0 - 0 - - - 10617 - 500 - 0 - 0 - 0 - - - 10618 - 3840 - 0 - 0 - 0 - - - 10619 - 10000 - 0 - 0 - 0 - - - 10620 - 12000 - 0 - 0 - 0 - - - 10621 - 150 - 0 - 0 - 0 - - - 10622 - 200 - 0 - 0 - 0 - - - 10623 - 100 - 0 - 0 - 0 - - - 10624 - 150 - 0 - 0 - 0 - - - 10625 - 20 - 0 - 0 - 0 - - - 10626 - 20 - 0 - 0 - 0 - - - 10627 - 20 - 0 - 0 - 0 - - - 10628 - 20 - 0 - 0 - 0 - - - 10629 - 1 - 0 - 0 - 0 - - - 10630 - 1 - 0 - 0 - 0 - - - 10631 - 1 - 0 - 0 - 0 - - - 10632 - 1 - 0 - 0 - 0 - - - 10633 - 1 - 0 - 0 - 0 - - - 10634 - 40 - 0 - 0 - 0 - - - 10635 - 450 - 0 - 0 - 0 - - - 10636 - 60000 - 0 - 0 - 0 - - - 10637 - 60000 - 0 - 0 - 0 - - - 10638 - 50 - 0 - 0 - 0 - - - 10639 - 99000 - 0 - 0 - 0 - - - 10640 - 99000 - 0 - 0 - 0 - - - 10641 - 99000 - 0 - 0 - 0 - - - 10642 - 99000 - 0 - 0 - 0 - - - 10643 - 99000 - 0 - 0 - 0 - - - 10644 - 99000 - 0 - 0 - 0 - - - 10645 - 99000 - 0 - 0 - 0 - - - 10646 - 99000 - 0 - 0 - 0 - - - 10647 - 99000 - 0 - 0 - 0 - - - 10648 - 99000 - 0 - 0 - 0 - - - 10649 - 99000 - 0 - 0 - 0 - - - 10650 - 99000 - 0 - 0 - 0 - - - 10651 - 99000 - 0 - 0 - 0 - - - 10652 - 99000 - 0 - 0 - 0 - - - 10653 - 99000 - 0 - 0 - 0 - - - 10654 - 99000 - 0 - 0 - 0 - - - 10655 - 99000 - 0 - 0 - 0 - - - 10656 - 99000 - 0 - 0 - 0 - - - 10657 - 99000 - 0 - 0 - 0 - - - 10658 - 99000 - 0 - 0 - 0 - - - 10659 - 99000 - 0 - 0 - 0 - - - 10660 - 99000 - 0 - 0 - 0 - - - 10661 - 99000 - 0 - 0 - 0 - - - 10662 - 99000 - 0 - 0 - 0 - - - 10663 - 400 - 0 - 0 - 0 - - - 10664 - 800 - 0 - 0 - 0 - - - 10665 - 1632 - 0 - 0 - 0 - - - 10666 - 5440 - 0 - 0 - 0 - - - 10667 - 54400 - 0 - 0 - 0 - - - 10668 - 16325 - 0 - 0 - 0 - - - 10669 - 5440 - 0 - 0 - 0 - - - 10670 - 54400 - 0 - 0 - 0 - - - 10671 - 1632 - 0 - 0 - 0 - - - 10672 - 5440 - 0 - 0 - 0 - - - 10673 - 54400 - 0 - 0 - 0 - - - 10674 - 1632 - 0 - 0 - 0 - - - 10675 - 5440 - 0 - 0 - 0 - - - 10676 - 54400 - 0 - 0 - 0 - - - 10677 - 1632 - 0 - 0 - 0 - - - 10678 - 5440 - 0 - 0 - 0 - - - 10679 - 54400 - 0 - 0 - 0 - - - 10680 - 850 - 0 - 0 - 0 - - - 10681 - 850 - 0 - 0 - 0 - - - 10682 - 7800 - 0 - 0 - 0 - - - 10683 - 7800 - 0 - 0 - 0 - - - 10684 - 9360 - 0 - 0 - 0 - - - 10685 - 9360 - 0 - 0 - 0 - - - 10686 - 15 - 0 - 0 - 0 - - - 10687 - 15 - 0 - 0 - 0 - - - 10688 - 120000 - 0 - 0 - 0 - - - 10689 - 200 - 0 - 0 - 0 - - - 10690 - 3840 - 0 - 0 - 0 - - - 10691 - 3840 - 0 - 0 - 0 - - - 10692 - 40 - 0 - 0 - 0 - - - 10693 - 80 - 0 - 0 - 0 - - - 10694 - 80 - 0 - 0 - 0 - - - 10695 - 80 - 0 - 0 - 0 - - - 10696 - 200 - 0 - 0 - 0 - - - 10697 - 12800 - 0 - 0 - 0 - - - 10698 - 12800 - 0 - 0 - 0 - - - 10699 - 1056 - 0 - 0 - 0 - - - 10700 - 1056 - 0 - 0 - 0 - - - 10701 - 1056 - 0 - 0 - 0 - - - 10702 - 1056 - 0 - 0 - 0 - - - 10703 - 1056 - 0 - 0 - 0 - - - 10704 - 35200 - 0 - 0 - 0 - - - 10705 - 35200 - 0 - 0 - 0 - - - 10706 - 35200 - 0 - 0 - 0 - - - 10707 - 35200 - 0 - 0 - 0 - - - 10708 - 35200 - 0 - 0 - 0 - - - 10709 - 3520 - 0 - 0 - 0 - - - 10710 - 3520 - 0 - 0 - 0 - - - 10711 - 3520 - 0 - 0 - 0 - - - 10712 - 3520 - 0 - 0 - 0 - - - 10713 - 3520 - 0 - 0 - 0 - - - 10714 - 3 - 0 - 0 - 0 - - - 10715 - 3 - 0 - 0 - 0 - - - 10716 - 3 - 0 - 0 - 0 - - - 10717 - 3 - 0 - 0 - 0 - - - 10718 - 3 - 0 - 0 - 0 - - - 10719 - 17625 - 0 - 0 - 0 - - - 10720 - 100 - 0 - 0 - 0 - - - 10721 - 1 - 0 - 0 - 0 - - - 10722 - 1 - 0 - 0 - 0 - - - 10723 - 1 - 0 - 0 - 0 - - - 10724 - 1 - 0 - 0 - 0 - - - 10725 - 1 - 0 - 0 - 0 - - - 10726 - 1 - 0 - 0 - 0 - - - 10727 - 1 - 0 - 0 - 0 - - - 10728 - 1 - 0 - 0 - 0 - - - 10729 - 1 - 0 - 0 - 0 - - - 10730 - 1 - 0 - 0 - 0 - - - 10731 - 1 - 0 - 0 - 0 - - - 10732 - 1 - 0 - 0 - 0 - - - 10733 - 10 - 0 - 0 - 0 - - - 10734 - 1 - 0 - 0 - 0 - - - 10735 - 1 - 0 - 0 - 0 - - - 10736 - 2025 - 0 - 0 - 0 - - - 10737 - 1 - 0 - 0 - 0 - - - 10738 - 900 - 0 - 0 - 0 - - - 10739 - 1 - 0 - 0 - 0 - - - 10740 - 2000 - 0 - 0 - 0 - - - 10741 - 1 - 0 - 0 - 0 - - - 10742 - 2000 - 0 - 0 - 0 - - - 10743 - 1 - 0 - 0 - 0 - - - 10744 - 2000 - 0 - 0 - 0 - - - 10745 - 1 - 0 - 0 - 0 - - - 10746 - 2000 - 0 - 0 - 0 - - - 10747 - 1 - 0 - 0 - 0 - - - 10748 - 2000 - 0 - 0 - 0 - - - 10749 - 1 - 0 - 0 - 0 - - - 10750 - 2000 - 0 - 0 - 0 - - - 10751 - 1 - 0 - 0 - 0 - - - 10752 - 2000 - 0 - 0 - 0 - - - 10753 - 1 - 0 - 0 - 0 - - - 10754 - 2000 - 0 - 0 - 0 - - - 10755 - 1 - 0 - 0 - 0 - - - 10756 - 2000 - 0 - 0 - 0 - - - 10757 - 1 - 0 - 0 - 0 - - - 10758 - 225 - 0 - 0 - 0 - - - 10759 - 1 - 0 - 0 - 0 - - - 10760 - 225 - 0 - 0 - 0 - - - 10761 - 1 - 0 - 0 - 0 - - - 10762 - 225 - 0 - 0 - 0 - - - 10763 - 1 - 0 - 0 - 0 - - - 10764 - 225 - 0 - 0 - 0 - - - 10765 - 1 - 0 - 0 - 0 - - - 10766 - 225 - 0 - 0 - 0 - - - 10767 - 1 - 0 - 0 - 0 - - - 10768 - 40 - 0 - 0 - 0 - - - 10769 - 1 - 0 - 0 - 0 - - - 10770 - 40 - 0 - 0 - 0 - - - 10771 - 1 - 0 - 0 - 0 - - - 10772 - 40 - 0 - 0 - 0 - - - 10773 - 1 - 0 - 0 - 0 - - - 10774 - 180 - 0 - 0 - 0 - - - 10775 - 1 - 0 - 0 - 0 - - - 10776 - 65000 - 0 - 0 - 0 - - - 10777 - 1 - 0 - 0 - 0 - - - 10778 - 65000 - 0 - 0 - 0 - - - 10779 - 1 - 0 - 0 - 0 - - - 10780 - 65000 - 0 - 0 - 0 - - - 10781 - 1 - 0 - 0 - 0 - - - 10782 - 65000 - 0 - 0 - 0 - - - 10783 - 1 - 0 - 0 - 0 - - - 10784 - 7000 - 0 - 0 - 0 - - - 10785 - 1 - 0 - 0 - 0 - - - 10786 - 7000 - 0 - 0 - 0 - - - 10787 - 1 - 0 - 0 - 0 - - - 10788 - 7000 - 0 - 0 - 0 - - - 10789 - 1 - 0 - 0 - 0 - - - 10790 - 13000 - 0 - 0 - 0 - - - 10791 - 1 - 0 - 0 - 0 - - - 10792 - 13000 - 0 - 0 - 0 - - - 10793 - 1 - 0 - 0 - 0 - - - 10794 - 13000 - 0 - 0 - 0 - - - 10795 - 1 - 0 - 0 - 0 - - - 10796 - 450 - 0 - 0 - 0 - - - 10797 - 1 - 0 - 0 - 0 - - - 10798 - 65000 - 0 - 0 - 0 - - - 10799 - 1 - 0 - 0 - 0 - - - 10800 - 65000 - 0 - 0 - 0 - - - 10801 - 1 - 0 - 0 - 0 - - - 10802 - 200 - 0 - 0 - 0 - - - 10803 - 1 - 0 - 0 - 0 - - - 10804 - 200 - 0 - 0 - 0 - - - 10805 - 1 - 0 - 0 - 0 - - - 10806 - 200 - 0 - 0 - 0 - - - 10807 - 1 - 0 - 0 - 0 - - - 10808 - 200 - 0 - 0 - 0 - - - 10809 - 200 - 0 - 0 - 0 - - - 10810 - 35 - 0 - 0 - 0 - - - 10811 - 35 - 0 - 0 - 0 - - - 10812 - 90 - 0 - 0 - 0 - - - 10813 - 90 - 0 - 0 - 0 - - - 10814 - 120 - 0 - 0 - 0 - - - 10815 - 120 - 0 - 0 - 0 - - - 10816 - 2 - 0 - 0 - 0 - - - 10817 - 2 - 0 - 0 - 0 - - - 10818 - 50 - 0 - 0 - 0 - - - 10819 - 50 - 0 - 0 - 0 - - - 10820 - 100 - 0 - 0 - 0 - - - 10821 - 100 - 0 - 0 - 0 - - - 10822 - 500 - 0 - 0 - 0 - - - 10823 - 500 - 0 - 0 - 0 - - - 10824 - 500 - 0 - 0 - 0 - - - 10825 - 500 - 0 - 0 - 0 - - - 10826 - 500 - 0 - 0 - 0 - - - 10827 - 500 - 0 - 0 - 0 - - - 10828 - 50000 - 0 - 0 - 0 - - - 10829 - 1 - 0 - 0 - 0 - - - 10830 - 1 - 0 - 0 - 0 - - - 10831 - 1 - 0 - 0 - 0 - - - 10832 - 1 - 0 - 0 - 0 - - - 10833 - 1 - 0 - 0 - 0 - - - 10834 - 1 - 0 - 0 - 0 - - - 10835 - 1 - 0 - 0 - 0 - - - 10836 - 100 - 0 - 0 - 0 - - - 10837 - 100 - 0 - 0 - 0 - - - 10838 - 100 - 0 - 0 - 0 - - - 10839 - 100 - 0 - 0 - 0 - - - 10840 - 1000 - 0 - 0 - 0 - - - 10841 - 1 - 0 - 0 - 0 - - - 10842 - 1 - 0 - 0 - 0 - - - 10843 - 50000 - 0 - 0 - 0 - - - 10844 - 1 - 0 - 0 - 0 - - - 10845 - 1 - 0 - 0 - 0 - - - 10846 - 1 - 0 - 0 - 0 - - - 10847 - 1 - 0 - 0 - 0 - - - 10848 - 1 - 0 - 0 - 0 - - - 10849 - 1 - 0 - 0 - 0 - - - 10850 - 1 - 0 - 0 - 0 - - - 10851 - 1 - 0 - 0 - 0 - - - 10852 - 1 - 0 - 0 - 0 - - - 10853 - 1 - 0 - 0 - 0 - - - 10854 - 1 - 0 - 0 - 0 - - - 10855 - 1 - 0 - 0 - 0 - - - 10856 - 5 - 0 - 0 - 0 - - - 10857 - 1 - 0 - 0 - 0 - - - 10858 - 1 - 0 - 0 - 0 - - - 10859 - 1 - 0 - 0 - 0 - - - 10860 - 1 - 0 - 0 - 0 - - - 10861 - 1 - 0 - 0 - 0 - - - 10862 - 100 - 0 - 0 - 0 - - - 10863 - 100 - 0 - 0 - 0 - - - 10864 - 100 - 0 - 0 - 0 - - - 10865 - 100 - 0 - 0 - 0 - - - 10866 - 1 - 0 - 0 - 0 - - - 10867 - 1 - 0 - 0 - 0 - - - 10868 - 1 - 0 - 0 - 0 - - - 10869 - 1 - 0 - 0 - 0 - - - 10870 - 1 - 0 - 0 - 0 - - - 10871 - 1 - 0 - 0 - 0 - - - 10872 - 1 - 0 - 0 - 0 - - - 10873 - 1 - 0 - 0 - 0 - - - 10874 - 1 - 0 - 0 - 0 - - - 10875 - 1 - 0 - 0 - 0 - - - 10876 - 1 - 0 - 0 - 0 - - - 10877 - 100 - 0 - 0 - 0 - - - 10878 - 100 - 0 - 0 - 0 - - - 10879 - 100 - 0 - 0 - 0 - - - 10880 - 100 - 0 - 0 - 0 - - - 10881 - 100 - 0 - 0 - 0 - - - 10882 - 100 - 0 - 0 - 0 - - - 10883 - 100 - 0 - 0 - 0 - - - 10884 - 52 - 0 - 0 - 0 - - - 10885 - 1 - 0 - 0 - 0 - - - 10886 - 1 - 0 - 0 - 0 - - - 10887 - 230000 - 0 - 0 - 0 - - - 10888 - 1 - 0 - 0 - 0 - - - 10889 - 1 - 0 - 0 - 0 - - - 10890 - 1 - 0 - 0 - 0 - - - 10891 - 1 - 0 - 0 - 0 - - - 10892 - 1 - 0 - 0 - 0 - - - 10893 - 1 - 0 - 0 - 0 - - - 10894 - 1 - 0 - 0 - 0 - - - 10895 - 1 - 0 - 0 - 0 - - - 10896 - 1 - 0 - 0 - 0 - - - 10897 - 1 - 0 - 0 - 0 - - - 10898 - 1 - 0 - 0 - 0 - - - 10899 - 1 - 0 - 0 - 0 - - - 10900 - 1 - 0 - 0 - 0 - - - 10901 - 1 - 0 - 0 - 0 - - - 10902 - 1 - 0 - 0 - 0 - - - 10903 - 1 - 0 - 0 - 0 - - - 10904 - 1 - 0 - 0 - 0 - - - 10905 - 1 - 0 - 0 - 0 - - - 10906 - 1 - 0 - 0 - 0 - - - 10907 - 1 - 0 - 0 - 0 - - - 10908 - 1 - 0 - 0 - 0 - - - 10909 - 300 - 0 - 0 - 0 - - - 10910 - 300 - 0 - 0 - 0 - - - 10911 - 240 - 0 - 0 - 0 - - - 10912 - 240 - 0 - 0 - 0 - - - 10913 - 180 - 0 - 0 - 0 - - - 10914 - 180 - 0 - 0 - 0 - - - 10915 - 120 - 0 - 0 - 0 - - - 10916 - 120 - 0 - 0 - 0 - - - 10917 - 300 - 0 - 0 - 0 - - - 10918 - 300 - 0 - 0 - 0 - - - 10919 - 240 - 0 - 0 - 0 - - - 10920 - 240 - 0 - 0 - 0 - - - 10921 - 180 - 0 - 0 - 0 - - - 10922 - 180 - 0 - 0 - 0 - - - 10923 - 120 - 0 - 0 - 0 - - - 10924 - 120 - 0 - 0 - 0 - - - 10925 - 300 - 0 - 0 - 0 - - - 10926 - 300 - 0 - 0 - 0 - - - 10927 - 240 - 0 - 0 - 0 - - - 10928 - 240 - 0 - 0 - 0 - - - 10929 - 180 - 0 - 0 - 0 - - - 10930 - 180 - 0 - 0 - 0 - - - 10931 - 120 - 0 - 0 - 0 - - - 10932 - 120 - 0 - 0 - 0 - - - 10933 - 50 - 0 - 0 - 0 - - - 10934 - 1 - 0 - 0 - 0 - - - 10935 - 1 - 0 - 0 - 0 - - - 10936 - 1 - 0 - 0 - 0 - - - 10937 - 300 - 0 - 0 - 0 - - - 10938 - 300 - 0 - 0 - 0 - - - 10939 - 50 - 0 - 0 - 0 - - - 10940 - 50 - 0 - 0 - 0 - - - 10941 - 50 - 0 - 0 - 0 - - - 10942 - 1 - 0 - 0 - 0 - - - 10943 - 1 - 0 - 0 - 0 - - - 10944 - 1 - 0 - 0 - 0 - - - 10945 - 50 - 0 - 0 - 0 - - - 10946 - 1 - 0 - 0 - 0 - - - 10947 - 1 - 0 - 0 - 0 - - - 10948 - 1 - 0 - 0 - 0 - - - 10949 - 1 - 0 - 0 - 0 - - - 10950 - 1 - 0 - 0 - 0 - - - 10951 - 1 - 0 - 0 - 0 - - - 10952 - 150 - 0 - 0 - 0 - - - 10953 - 150 - 0 - 0 - 0 - - - 10954 - 1000 - 0 - 0 - 0 - - - 10955 - 1000 - 0 - 0 - 0 - - - 10956 - 900 - 0 - 0 - 0 - - - 10957 - 900 - 0 - 0 - 0 - - - 10958 - 200 - 0 - 0 - 0 - - - 10959 - 200 - 0 - 0 - 0 - - - 10960 - 10 - 0 - 0 - 0 - - - 10961 - 10 - 0 - 0 - 0 - - - 10962 - 10 - 0 - 0 - 0 - - - 10963 - 10 - 0 - 0 - 0 - - - 10964 - 10 - 0 - 0 - 0 - - - 10965 - 10 - 0 - 0 - 0 - - - 10966 - 50 - 0 - 0 - 0 - - - 10967 - 50 - 0 - 0 - 0 - - - 10968 - 50 - 0 - 0 - 0 - - - 10969 - 50 - 0 - 0 - 0 - - - 10970 - 50 - 0 - 0 - 0 - - - 10971 - 50 - 0 - 0 - 0 - - - 10972 - 20 - 0 - 0 - 0 - - - 10973 - 350 - 0 - 0 - 0 - - - 10974 - 350 - 0 - 0 - 0 - - - 10975 - 1 - 0 - 0 - 0 - - - 10976 - 5 - 0 - 0 - 0 - - - 10977 - 7 - 0 - 0 - 0 - - - 10978 - 2 - 0 - 0 - 0 - - - 10979 - 2 - 0 - 0 - 0 - - - 10980 - 250 - 0 - 0 - 0 - - - 10981 - 20 - 0 - 0 - 0 - - - 10982 - 20 - 0 - 0 - 0 - - - 10983 - 1 - 0 - 0 - 0 - - - 10984 - 1 - 0 - 0 - 0 - - - 10985 - 1 - 0 - 0 - 0 - - - 10986 - 1 - 0 - 0 - 0 - - - 10987 - 1 - 0 - 0 - 0 - - - 10988 - 1 - 0 - 0 - 0 - - - 10989 - 1 - 0 - 0 - 0 - - - 10990 - 1 - 0 - 0 - 0 - - - 10991 - 1 - 0 - 0 - 0 - - - 10992 - 1 - 0 - 0 - 0 - - - 10993 - 1 - 0 - 0 - 0 - - - 10994 - 1 - 0 - 0 - 0 - - - 10995 - 1000 - 0 - 0 - 0 - - - 10996 - 1000 - 0 - 0 - 0 - - - 10997 - 1 - 0 - 0 - 0 - - - 10998 - 1 - 0 - 0 - 0 - - - 10999 - 1 - 0 - 0 - 0 - - - 11000 - 1 - 0 - 0 - 0 - - - 11001 - 10 - 0 - 0 - 0 - - - 11002 - 10 - 0 - 0 - 0 - - - 11003 - 5 - 0 - 0 - 0 - - - 11004 - 1 - 0 - 0 - 0 - - - 11005 - 1 - 0 - 0 - 0 - - - 11006 - 10 - 0 - 0 - 0 - - - 11007 - 10 - 0 - 0 - 0 - - - 11008 - 1 - 0 - 0 - 0 - - - 11009 - 1 - 0 - 0 - 0 - - - 11010 - 1 - 0 - 0 - 0 - - - 11011 - 1 - 0 - 0 - 0 - - - 11012 - 1 - 0 - 0 - 0 - - - 11013 - 1 - 0 - 0 - 0 - - - 11014 - 1 - 0 - 0 - 0 - - - 11015 - 1 - 0 - 0 - 0 - - - 11016 - 1 - 0 - 0 - 0 - - - 11017 - 1 - 0 - 0 - 0 - - - 11018 - 1 - 0 - 0 - 0 - - - 11019 - 1 - 0 - 0 - 0 - - - 11020 - 1 - 0 - 0 - 0 - - - 11021 - 1 - 0 - 0 - 0 - - - 11022 - 1 - 0 - 0 - 0 - - - 11023 - 1 - 0 - 0 - 0 - - - 11024 - 1 - 0 - 0 - 0 - - - 11025 - 1 - 0 - 0 - 0 - - - 11026 - 1 - 0 - 0 - 0 - - - 11027 - 1 - 0 - 0 - 0 - - - 11028 - 1 - 0 - 0 - 0 - - - 11029 - 1 - 0 - 0 - 0 - - - 11030 - 1 - 0 - 0 - 0 - - - 11031 - 1 - 0 - 0 - 0 - - - 11032 - 1 - 0 - 0 - 0 - - - 11033 - 1 - 0 - 0 - 0 - - - 11034 - 1 - 0 - 0 - 0 - - - 11035 - 1 - 0 - 0 - 0 - - - 11036 - 1 - 0 - 0 - 0 - - - 11037 - 26000 - 0 - 0 - 0 - - - 11038 - 26000 - 0 - 0 - 0 - - - 11039 - 1 - 0 - 0 - 0 - - - 11040 - 1 - 0 - 0 - 0 - - - 11041 - 1 - 0 - 0 - 0 - - - 11042 - 1 - 0 - 0 - 0 - - - 11043 - 1 - 0 - 0 - 0 - - - 11044 - 1 - 0 - 0 - 0 - - - 11045 - 1 - 0 - 0 - 0 - - - 11046 - 1 - 0 - 0 - 0 - - - 11047 - 1 - 0 - 0 - 0 - - - 11048 - 1 - 0 - 0 - 0 - - - 11049 - 1 - 0 - 0 - 0 - - - 11050 - 1 - 0 - 0 - 0 - - - 11051 - 1 - 0 - 0 - 0 - - - 11052 - 1 - 0 - 0 - 0 - - - 11053 - 1 - 0 - 0 - 0 - - - 11054 - 1 - 0 - 0 - 0 - - - 11055 - 1 - 0 - 0 - 0 - - - 11056 - 1 - 0 - 0 - 0 - - - 11057 - 1 - 0 - 0 - 0 - - - 11058 - 1 - 0 - 0 - 0 - - - 11059 - 1 - 0 - 0 - 0 - - - 11060 - 20 - 0 - 0 - 0 - - - 11061 - 1000 - 0 - 0 - 0 - - - 11062 - 1 - 0 - 0 - 0 - - - 11063 - 1 - 0 - 0 - 0 - - - 11064 - 1000 - 0 - 0 - 0 - - - 11065 - 5 - 0 - 0 - 0 - - - 11066 - 5 - 0 - 0 - 0 - - - 11067 - 1 - 0 - 0 - 0 - - - 11068 - 1 - 0 - 0 - 0 - - - 11069 - 550 - 0 - 0 - 0 - - - 11070 - 550 - 0 - 0 - 0 - - - 11071 - 1 - 0 - 0 - 0 - - - 11072 - 1150 - 0 - 0 - 0 - - - 11073 - 1150 - 0 - 0 - 0 - - - 11074 - 1265 - 0 - 0 - 0 - - - 11075 - 1265 - 0 - 0 - 0 - - - 11076 - 1525 - 0 - 0 - 0 - - - 11077 - 1525 - 0 - 0 - 0 - - - 11078 - 1 - 0 - 0 - 0 - - - 11079 - 1675 - 0 - 0 - 0 - - - 11080 - 1675 - 0 - 0 - 0 - - - 11081 - 1675 - 0 - 0 - 0 - - - 11082 - 1675 - 0 - 0 - 0 - - - 11083 - 1675 - 0 - 0 - 0 - - - 11084 - 1675 - 0 - 0 - 0 - - - 11085 - 2325 - 0 - 0 - 0 - - - 11086 - 2325 - 0 - 0 - 0 - - - 11087 - 1 - 0 - 0 - 0 - - - 11088 - 2560 - 0 - 0 - 0 - - - 11089 - 2560 - 0 - 0 - 0 - - - 11090 - 4050 - 0 - 0 - 0 - - - 11091 - 4050 - 0 - 0 - 0 - - - 11092 - 3825 - 0 - 0 - 0 - - - 11093 - 3825 - 0 - 0 - 0 - - - 11094 - 1 - 0 - 0 - 0 - - - 11095 - 4200 - 0 - 0 - 0 - - - 11096 - 4200 - 0 - 0 - 0 - - - 11097 - 4200 - 0 - 0 - 0 - - - 11098 - 4200 - 0 - 0 - 0 - - - 11099 - 4200 - 0 - 0 - 0 - - - 11100 - 4200 - 0 - 0 - 0 - - - 11101 - 4200 - 0 - 0 - 0 - - - 11102 - 4200 - 0 - 0 - 0 - - - 11103 - 4200 - 0 - 0 - 0 - - - 11104 - 4200 - 0 - 0 - 0 - - - 11105 - 20200 - 0 - 0 - 0 - - - 11106 - 20200 - 0 - 0 - 0 - - - 11107 - 20200 - 0 - 0 - 0 - - - 11108 - 20200 - 0 - 0 - 0 - - - 11109 - 20200 - 0 - 0 - 0 - - - 11110 - 20200 - 0 - 0 - 0 - - - 11111 - 20200 - 0 - 0 - 0 - - - 11112 - 20200 - 0 - 0 - 0 - - - 11113 - 20200 - 0 - 0 - 0 - - - 11114 - 20200 - 0 - 0 - 0 - - - 11115 - 19125 - 0 - 0 - 0 - - - 11116 - 19125 - 0 - 0 - 0 - - - 11117 - 1 - 0 - 0 - 0 - - - 11118 - 21040 - 0 - 0 - 0 - - - 11119 - 21040 - 0 - 0 - 0 - - - 11120 - 21040 - 0 - 0 - 0 - - - 11121 - 21040 - 0 - 0 - 0 - - - 11122 - 21040 - 0 - 0 - 0 - - - 11123 - 21040 - 0 - 0 - 0 - - - 11124 - 21040 - 0 - 0 - 0 - - - 11125 - 21040 - 0 - 0 - 0 - - - 11126 - 21040 - 0 - 0 - 0 - - - 11127 - 21040 - 0 - 0 - 0 - - - 11128 - 202000 - 0 - 0 - 0 - - - 11129 - 202000 - 0 - 0 - 0 - - - 11130 - 201000 - 0 - 0 - 0 - - - 11131 - 201000 - 0 - 0 - 0 - - - 11132 - 1 - 0 - 0 - 0 - - - 11133 - 202000 - 0 - 0 - 0 - - - 11134 - 202000 - 0 - 0 - 0 - - - 11135 - 1 - 0 - 0 - 0 - - - 11136 - 1 - 0 - 0 - 0 - - - 11137 - 1 - 0 - 0 - 0 - - - 11138 - 1 - 0 - 0 - 0 - - - 11139 - 1 - 0 - 0 - 0 - - - 11140 - 1 - 0 - 0 - 0 - - - 11141 - 1 - 0 - 0 - 0 - - - 11142 - 1 - 0 - 0 - 0 - - - 11143 - 1 - 0 - 0 - 0 - - - 11144 - 1 - 0 - 0 - 0 - - - 11145 - 1 - 0 - 0 - 0 - - - 11146 - 1 - 0 - 0 - 0 - - - 11147 - 1 - 0 - 0 - 0 - - - 11148 - 1 - 0 - 0 - 0 - - - 11149 - 1 - 0 - 0 - 0 - - - 11150 - 1 - 0 - 0 - 0 - - - 11151 - 1 - 0 - 0 - 0 - - - 11152 - 1 - 0 - 0 - 0 - - - 11153 - 1 - 0 - 0 - 0 - - - 11154 - 1 - 0 - 0 - 0 - - - 11155 - 20 - 0 - 0 - 0 - - - 11156 - 1 - 0 - 0 - 0 - - - 11157 - 1 - 0 - 0 - 0 - - - 11158 - 1 - 0 - 0 - 0 - - - 11159 - 1 - 0 - 0 - 0 - - - 11160 - 1 - 0 - 0 - 0 - - - 11161 - 1 - 0 - 0 - 0 - - - 11162 - 1 - 0 - 0 - 0 - - - 11163 - 1 - 0 - 0 - 0 - - - 11164 - 1 - 0 - 0 - 0 - - - 11165 - 4 - 0 - 0 - 0 - - - 11166 - 4 - 0 - 0 - 0 - - - 11167 - 4 - 0 - 0 - 0 - - - 11168 - 4 - 0 - 0 - 0 - - - 11169 - 1 - 0 - 0 - 0 - - - 11170 - 1 - 0 - 0 - 0 - - - 11171 - 1 - 0 - 0 - 0 - - - 11172 - 1 - 0 - 0 - 0 - - - 11173 - 1 - 0 - 0 - 0 - - - 11174 - 1 - 0 - 0 - 0 - - - 11175 - 1 - 0 - 0 - 0 - - - 11176 - 1 - 0 - 0 - 0 - - - 11177 - 1 - 0 - 0 - 0 - - - 11178 - 1 - 0 - 0 - 0 - - - 11179 - 1 - 0 - 0 - 0 - - - 11180 - 1 - 0 - 0 - 0 - - - 11181 - 1 - 0 - 0 - 0 - - - 11182 - 1 - 0 - 0 - 0 - - - 11183 - 1 - 0 - 0 - 0 - - - 11184 - 1 - 0 - 0 - 0 - - - 11185 - 1 - 0 - 0 - 0 - - - 11186 - 1 - 0 - 0 - 0 - - - 11187 - 1 - 0 - 0 - 0 - - - 11188 - 1 - 0 - 0 - 0 - - - 11189 - 1 - 0 - 0 - 0 - - - 11190 - 2400 - 0 - 0 - 0 - - - 11191 - 2400 - 0 - 0 - 0 - - - 11192 - 2400 - 0 - 0 - 0 - - - 11193 - 2400 - 0 - 0 - 0 - - - 11194 - 2400 - 0 - 0 - 0 - - - 11195 - 1 - 0 - 0 - 0 - - - 11196 - 1 - 0 - 0 - 0 - - - 11197 - 1 - 0 - 0 - 0 - - - 11198 - 1 - 0 - 0 - 0 - - - 11199 - 1 - 0 - 0 - 0 - - - 11200 - 60000 - 0 - 0 - 0 - - - 11201 - 60000 - 0 - 0 - 0 - - - 11202 - 1 - 0 - 0 - 0 - - - 11203 - 1 - 0 - 0 - 0 - - - 11204 - 1 - 0 - 0 - 0 - - - 11205 - 2 - 0 - 0 - 0 - - - 11206 - 1 - 0 - 0 - 0 - - - 11207 - 1 - 0 - 0 - 0 - - - 11208 - 1 - 0 - 0 - 0 - - - 11209 - 1 - 0 - 0 - 0 - - - 11210 - 1 - 0 - 0 - 0 - - - 11211 - 1 - 0 - 0 - 0 - - - 11212 - 800 - 0 - 0 - 0 - - - 11213 - 1 - 0 - 0 - 0 - - - 11214 - 1 - 0 - 0 - 0 - - - 11215 - 1 - 0 - 0 - 0 - - - 11216 - 1 - 0 - 0 - 0 - - - 11217 - 3 - 0 - 0 - 0 - - - 11218 - 1 - 0 - 0 - 0 - - - 11219 - 1 - 0 - 0 - 0 - - - 11220 - 1 - 0 - 0 - 0 - - - 11221 - 1 - 0 - 0 - 0 - - - 11222 - 10 - 0 - 0 - 0 - - - 11223 - 1 - 0 - 0 - 0 - - - 11224 - 1 - 0 - 0 - 0 - - - 11225 - 1 - 0 - 0 - 0 - - - 11226 - 1 - 0 - 0 - 0 - - - 11227 - 800 - 0 - 0 - 0 - - - 11228 - 800 - 0 - 0 - 0 - - - 11229 - 800 - 0 - 0 - 0 - - - 11230 - 500 - 0 - 0 - 0 - - - 11231 - 500 - 0 - 0 - 0 - - - 11232 - 250 - 0 - 0 - 0 - - - 11233 - 500 - 0 - 0 - 0 - - - 11234 - 500 - 0 - 0 - 0 - - - 11235 - 120002 - 0 - 0 - 0 - - - 11236 - 120002 - 0 - 0 - 0 - - - 11237 - 500 - 0 - 0 - 0 - - - 11238 - 50 - 0 - 0 - 0 - - - 11239 - 50 - 0 - 0 - 0 - - - 11240 - 50 - 0 - 0 - 0 - - - 11241 - 50 - 0 - 0 - 0 - - - 11242 - 50 - 0 - 0 - 0 - - - 11243 - 50 - 0 - 0 - 0 - - - 11244 - 50 - 0 - 0 - 0 - - - 11245 - 50 - 0 - 0 - 0 - - - 11246 - 50 - 0 - 0 - 0 - - - 11247 - 50 - 0 - 0 - 0 - - - 11248 - 50 - 0 - 0 - 0 - - - 11249 - 50 - 0 - 0 - 0 - - - 11250 - 50 - 0 - 0 - 0 - - - 11251 - 50 - 0 - 0 - 0 - - - 11252 - 50 - 0 - 0 - 0 - - - 11253 - 50 - 0 - 0 - 0 - - - 11254 - 50 - 0 - 0 - 0 - - - 11255 - 50 - 0 - 0 - 0 - - - 11256 - 50 - 0 - 0 - 0 - - - 11257 - 50 - 0 - 0 - 0 - - - 11258 - 600 - 0 - 0 - 0 - - - 11259 - 1000 - 0 - 0 - 0 - - - 11260 - 1 - 0 - 0 - 0 - - - 11261 - 1 - 0 - 0 - 0 - - - 11262 - 50 - 0 - 0 - 0 - - - 11263 - 50 - 0 - 0 - 0 - - - 11264 - 10 - 0 - 0 - 0 - - - 11265 - 10 - 0 - 0 - 0 - - - 11266 - 5 - 0 - 0 - 0 - - - 11267 - 1 - 0 - 0 - 0 - - - 11268 - 1000 - 0 - 0 - 0 - - - 11269 - 1 - 0 - 0 - 0 - - - 11270 - 1 - 0 - 0 - 0 - - - 11271 - 50 - 0 - 0 - 0 - - - 11272 - 50 - 0 - 0 - 0 - - - 11273 - 3 - 0 - 0 - 0 - - - 11274 - 75 - 0 - 0 - 0 - - - 11275 - 48 - 0 - 0 - 0 - - - 11276 - 1 - 0 - 0 - 0 - - - 11277 - 200 - 0 - 0 - 0 - - - 11278 - 80 - 0 - 0 - 0 - - - 11279 - 1 - 0 - 0 - 0 - - - 11280 - 200 - 0 - 0 - 0 - - - 11281 - 200 - 0 - 0 - 0 - - - 11282 - 80 - 0 - 0 - 0 - - - 11283 - 2000000 - 0 - 0 - 0 - - - 11284 - 2000000 - 0 - 0 - 0 - - - 11285 - 2000000 - 0 - 0 - 0 - - - 11286 - 750000 - 0 - 0 - 0 - - - 11287 - 750000 - 0 - 0 - 0 - - - 11288 - 40 - 0 - 0 - 0 - - - 11289 - 40 - 0 - 0 - 0 - - - 11290 - 40 - 0 - 0 - 0 - - - 11291 - 40 - 0 - 0 - 0 - - - 11292 - 40 - 0 - 0 - 0 - - - 11293 - 40 - 0 - 0 - 0 - - - 11294 - 40 - 0 - 0 - 0 - - - 11295 - 40 - 0 - 0 - 0 - - - 11296 - 40 - 0 - 0 - 0 - - - 11297 - 40 - 0 - 0 - 0 - - - 11298 - 40 - 0 - 0 - 0 - - - 11299 - 40 - 0 - 0 - 0 - - - 11300 - 40 - 0 - 0 - 0 - - - 11301 - 40 - 0 - 0 - 0 - - - 11302 - 40 - 0 - 0 - 0 - - - 11303 - 40 - 0 - 0 - 0 - - - 11304 - 40 - 0 - 0 - 0 - - - 11305 - 40 - 0 - 0 - 0 - - - 11306 - 1 - 0 - 0 - 0 - - - 11307 - 1 - 0 - 0 - 0 - - - 11308 - 1 - 0 - 0 - 0 - - - 11309 - 1 - 0 - 0 - 0 - - - 11310 - 1 - 0 - 0 - 0 - - - 11311 - 1 - 0 - 0 - 0 - - - 11312 - 1 - 0 - 0 - 0 - - - 11313 - 1 - 0 - 0 - 0 - - - 11314 - 1 - 0 - 0 - 0 - - - 11315 - 1 - 0 - 0 - 0 - - - 11316 - 1 - 0 - 0 - 0 - - - 11317 - 1 - 0 - 0 - 0 - - - 11318 - 1 - 0 - 0 - 0 - - - 11319 - 1 - 0 - 0 - 0 - - - 11320 - 3 - 0 - 0 - 0 - - - 11321 - 3 - 0 - 0 - 0 - - - 11322 - 3 - 0 - 0 - 0 - - - 11323 - 1 - 0 - 0 - 0 - - - 11324 - 20 - 0 - 0 - 0 - - - 11325 - 20 - 0 - 0 - 0 - - - 11326 - 50 - 0 - 0 - 0 - - - 11327 - 50 - 0 - 0 - 0 - - - 11328 - 25 - 0 - 0 - 0 - - - 11329 - 25 - 0 - 0 - 0 - - - 11330 - 50 - 0 - 0 - 0 - - - 11331 - 50 - 0 - 0 - 0 - - - 11332 - 75 - 0 - 0 - 0 - - - 11333 - 75 - 0 - 0 - 0 - - - 11334 - 1 - 0 - 0 - 0 - - - 11335 - 150000 - 0 - 0 - 0 - - - 11336 - 150000 - 0 - 0 - 0 - - - 11337 - 1000 - 0 - 0 - 0 - - - 11338 - 3500 - 0 - 0 - 0 - - - 11339 - 1 - 0 - 0 - 0 - - - 11340 - 1 - 0 - 0 - 0 - - - 11341 - 1 - 0 - 0 - 0 - - - 11342 - 1 - 0 - 0 - 0 - - - 11343 - 1 - 0 - 0 - 0 - - - 11344 - 1 - 0 - 0 - 0 - - - 11345 - 1 - 0 - 0 - 0 - - - 11346 - 1 - 0 - 0 - 0 - - - 11347 - 1 - 0 - 0 - 0 - - - 11348 - 1 - 0 - 0 - 0 - - - 11349 - 1 - 0 - 0 - 0 - - - 11350 - 1 - 0 - 0 - 0 - - - 11351 - 1 - 0 - 0 - 0 - - - 11352 - 1 - 0 - 0 - 0 - - - 11353 - 1 - 0 - 0 - 0 - - - 11354 - 1 - 0 - 0 - 0 - - - 11355 - 1 - 0 - 0 - 0 - - - 11356 - 1 - 0 - 0 - 0 - - - 11357 - 1 - 0 - 0 - 0 - - - 11358 - 1 - 0 - 0 - 0 - - - 11359 - 1 - 0 - 0 - 0 - - - 11360 - 1 - 0 - 0 - 0 - - - 11361 - 1 - 0 - 0 - 0 - - - 11362 - 1 - 0 - 0 - 0 - - - 11363 - 1 - 0 - 0 - 0 - - - 11364 - 1 - 0 - 0 - 0 - - - 11365 - 1 - 0 - 0 - 0 - - - 11366 - 1 - 0 - 0 - 0 - - - 11367 - 26 - 0 - 0 - 0 - - - 11368 - 26 - 0 - 0 - 0 - - - 11369 - 91 - 0 - 0 - 0 - - - 11370 - 91 - 0 - 0 - 0 - - - 11371 - 325 - 0 - 0 - 0 - - - 11372 - 325 - 0 - 0 - 0 - - - 11373 - 845 - 0 - 0 - 0 - - - 11374 - 845 - 0 - 0 - 0 - - - 11375 - 2080 - 0 - 0 - 0 - - - 11376 - 2080 - 0 - 0 - 0 - - - 11377 - 20800 - 0 - 0 - 0 - - - 11378 - 20800 - 0 - 0 - 0 - - - 11379 - 26 - 0 - 0 - 0 - - - 11380 - 26 - 0 - 0 - 0 - - - 11381 - 26 - 0 - 0 - 0 - - - 11382 - 26 - 0 - 0 - 0 - - - 11383 - 26 - 0 - 0 - 0 - - - 11384 - 26 - 0 - 0 - 0 - - - 11385 - 26 - 0 - 0 - 0 - - - 11386 - 91 - 0 - 0 - 0 - - - 11387 - 91 - 0 - 0 - 0 - - - 11388 - 91 - 0 - 0 - 0 - - - 11389 - 91 - 0 - 0 - 0 - - - 11390 - 91 - 0 - 0 - 0 - - - 11391 - 91 - 0 - 0 - 0 - - - 11392 - 91 - 0 - 0 - 0 - - - 11393 - 325 - 0 - 0 - 0 - - - 11394 - 325 - 0 - 0 - 0 - - - 11395 - 325 - 0 - 0 - 0 - - - 11396 - 325 - 0 - 0 - 0 - - - 11397 - 325 - 0 - 0 - 0 - - - 11398 - 325 - 0 - 0 - 0 - - - 11399 - 325 - 0 - 0 - 0 - - - 11400 - 845 - 0 - 0 - 0 - - - 11401 - 845 - 0 - 0 - 0 - - - 11402 - 845 - 0 - 0 - 0 - - - 11403 - 845 - 0 - 0 - 0 - - - 11404 - 845 - 0 - 0 - 0 - - - 11405 - 845 - 0 - 0 - 0 - - - 11406 - 845 - 0 - 0 - 0 - - - 11407 - 2080 - 0 - 0 - 0 - - - 11408 - 2080 - 0 - 0 - 0 - - - 11409 - 2080 - 0 - 0 - 0 - - - 11410 - 2080 - 0 - 0 - 0 - - - 11411 - 2080 - 0 - 0 - 0 - - - 11412 - 2080 - 0 - 0 - 0 - - - 11413 - 2080 - 0 - 0 - 0 - - - 11414 - 20800 - 0 - 0 - 0 - - - 11415 - 20800 - 0 - 0 - 0 - - - 11416 - 20800 - 0 - 0 - 0 - - - 11417 - 20800 - 0 - 0 - 0 - - - 11418 - 20800 - 0 - 0 - 0 - - - 11419 - 20800 - 0 - 0 - 0 - - - 11420 - 20800 - 0 - 0 - 0 - - - 11421 - 1 - 0 - 0 - 0 - - - 11422 - 1 - 0 - 0 - 0 - - - 11423 - 1 - 0 - 0 - 0 - - - 11424 - 1 - 0 - 0 - 0 - - - 11425 - 1 - 0 - 0 - 0 - - - 11426 - 1 - 0 - 0 - 0 - - - 11427 - 3 - 0 - 0 - 0 - - - 11428 - 3 - 0 - 0 - 0 - - - 11429 - 9 - 0 - 0 - 0 - - - 11430 - 9 - 0 - 0 - 0 - - - 11431 - 6 - 0 - 0 - 0 - - - 11432 - 6 - 0 - 0 - 0 - - - 11433 - 216 - 0 - 0 - 0 - - - 11434 - 216 - 0 - 0 - 0 - - - 11435 - 144 - 0 - 0 - 0 - - - 11436 - 144 - 0 - 0 - 0 - - - 11437 - 150 - 0 - 0 - 0 - - - 11438 - 150 - 0 - 0 - 0 - - - 11439 - 75 - 0 - 0 - 0 - - - 11440 - 75 - 0 - 0 - 0 - - - 11441 - 11 - 0 - 0 - 0 - - - 11442 - 11 - 0 - 0 - 0 - - - 11443 - 13 - 0 - 0 - 0 - - - 11444 - 13 - 0 - 0 - 0 - - - 11445 - 105 - 0 - 0 - 0 - - - 11446 - 105 - 0 - 0 - 0 - - - 11447 - 52 - 0 - 0 - 0 - - - 11448 - 52 - 0 - 0 - 0 - - - 11449 - 66 - 0 - 0 - 0 - - - 11450 - 66 - 0 - 0 - 0 - - - 11451 - 44 - 0 - 0 - 0 - - - 11452 - 44 - 0 - 0 - 0 - - - 11453 - 72 - 0 - 0 - 0 - - - 11454 - 72 - 0 - 0 - 0 - - - 11455 - 36 - 0 - 0 - 0 - - - 11456 - 36 - 0 - 0 - 0 - - - 11457 - 90 - 0 - 0 - 0 - - - 11458 - 90 - 0 - 0 - 0 - - - 11459 - 60 - 0 - 0 - 0 - - - 11460 - 60 - 0 - 0 - 0 - - - 11461 - 100 - 0 - 0 - 0 - - - 11462 - 100 - 0 - 0 - 0 - - - 11463 - 50 - 0 - 0 - 0 - - - 11464 - 50 - 0 - 0 - 0 - - - 11465 - 114 - 0 - 0 - 0 - - - 11466 - 114 - 0 - 0 - 0 - - - 11467 - 76 - 0 - 0 - 0 - - - 11468 - 76 - 0 - 0 - 0 - - - 11469 - 135 - 0 - 0 - 0 - - - 11470 - 135 - 0 - 0 - 0 - - - 11471 - 90 - 0 - 0 - 0 - - - 11472 - 90 - 0 - 0 - 0 - - - 11473 - 216 - 0 - 0 - 0 - - - 11474 - 216 - 0 - 0 - 0 - - - 11475 - 144 - 0 - 0 - 0 - - - 11476 - 144 - 0 - 0 - 0 - - - 11477 - 150 - 0 - 0 - 0 - - - 11478 - 150 - 0 - 0 - 0 - - - 11479 - 100 - 0 - 0 - 0 - - - 11480 - 100 - 0 - 0 - 0 - - - 11481 - 160 - 0 - 0 - 0 - - - 11482 - 160 - 0 - 0 - 0 - - - 11483 - 90 - 0 - 0 - 0 - - - 11484 - 90 - 0 - 0 - 0 - - - 11485 - 165 - 0 - 0 - 0 - - - 11486 - 165 - 0 - 0 - 0 - - - 11487 - 110 - 0 - 0 - 0 - - - 11488 - 110 - 0 - 0 - 0 - - - 11489 - 180 - 0 - 0 - 0 - - - 11490 - 180 - 0 - 0 - 0 - - - 11491 - 130 - 0 - 0 - 0 - - - 11492 - 130 - 0 - 0 - 0 - - - 11493 - 180 - 0 - 0 - 0 - - - 11494 - 180 - 0 - 0 - 0 - - - 11495 - 120 - 0 - 0 - 0 - - - 11496 - 120 - 0 - 0 - 0 - - - 11497 - 198 - 0 - 0 - 0 - - - 11498 - 198 - 0 - 0 - 0 - - - 11499 - 132 - 0 - 0 - 0 - - - 11500 - 132 - 0 - 0 - 0 - - - 11501 - 216 - 0 - 0 - 0 - - - 11502 - 216 - 0 - 0 - 0 - - - 11503 - 144 - 0 - 0 - 0 - - - 11504 - 144 - 0 - 0 - 0 - - - 11505 - 198 - 0 - 0 - 0 - - - 11506 - 198 - 0 - 0 - 0 - - - 11507 - 132 - 0 - 0 - 0 - - - 11508 - 132 - 0 - 0 - 0 - - - 11509 - 216 - 0 - 0 - 0 - - - 11510 - 216 - 0 - 0 - 0 - - - 11511 - 144 - 0 - 0 - 0 - - - 11512 - 144 - 0 - 0 - 0 - - - 11513 - 200 - 0 - 0 - 0 - - - 11514 - 200 - 0 - 0 - 0 - - - 11515 - 150 - 0 - 0 - 0 - - - 11516 - 150 - 0 - 0 - 0 - - - 11517 - 9 - 0 - 0 - 0 - - - 11518 - 9 - 0 - 0 - 0 - - - 11519 - 6 - 0 - 0 - 0 - - - 11520 - 6 - 0 - 0 - 0 - - - 11521 - 150 - 0 - 0 - 0 - - - 11522 - 150 - 0 - 0 - 0 - - - 11523 - 125 - 0 - 0 - 0 - - - 11524 - 125 - 0 - 0 - 0 - - - 11525 - 25 - 0 - 0 - 0 - - - 11526 - 3 - 0 - 0 - 0 - - - 11527 - 3 - 0 - 0 - 0 - - - 11528 - 3 - 0 - 0 - 0 - - - 11529 - 3 - 0 - 0 - 0 - - - 11530 - 3 - 0 - 0 - 0 - - - 11531 - 3 - 0 - 0 - 0 - - - 11532 - 3 - 0 - 0 - 0 - - - 11533 - 3 - 0 - 0 - 0 - - - 11534 - 3 - 0 - 0 - 0 - - - 11535 - 3 - 0 - 0 - 0 - - - 11536 - 3 - 0 - 0 - 0 - - - 11537 - 3 - 0 - 0 - 0 - - - 11538 - 3 - 0 - 0 - 0 - - - 11539 - 3 - 0 - 0 - 0 - - - 11540 - 3 - 0 - 0 - 0 - - - 11541 - 3 - 0 - 0 - 0 - - - 11542 - 3 - 0 - 0 - 0 - - - 11543 - 3 - 0 - 0 - 0 - - - 11544 - 3 - 0 - 0 - 0 - - - 11545 - 3 - 0 - 0 - 0 - - - 11546 - 3 - 0 - 0 - 0 - - - 11547 - 3 - 0 - 0 - 0 - - - 11548 - 3 - 0 - 0 - 0 - - - 11549 - 3 - 0 - 0 - 0 - - - 11550 - 3 - 0 - 0 - 0 - - - 11551 - 3 - 0 - 0 - 0 - - - 11552 - 3 - 0 - 0 - 0 - - - 11553 - 3 - 0 - 0 - 0 - - - 11554 - 3 - 0 - 0 - 0 - - - 11555 - 3 - 0 - 0 - 0 - - - 11556 - 3 - 0 - 0 - 0 - - - 11557 - 3 - 0 - 0 - 0 - - - 11558 - 3 - 0 - 0 - 0 - - - 11559 - 3 - 0 - 0 - 0 - - - 11560 - 3 - 0 - 0 - 0 - - - 11561 - 3 - 0 - 0 - 0 - - - 11562 - 3 - 0 - 0 - 0 - - - 11563 - 3 - 0 - 0 - 0 - - - 11564 - 3 - 0 - 0 - 0 - - - 11565 - 3 - 0 - 0 - 0 - - - 11566 - 3 - 0 - 0 - 0 - - - 11567 - 3 - 0 - 0 - 0 - - - 11568 - 3 - 0 - 0 - 0 - - - 11569 - 3 - 0 - 0 - 0 - - - 11570 - 3 - 0 - 0 - 0 - - - 11571 - 3 - 0 - 0 - 0 - - - 11572 - 3 - 0 - 0 - 0 - - - 11573 - 3 - 0 - 0 - 0 - - - 11574 - 3 - 0 - 0 - 0 - - - 11575 - 3 - 0 - 0 - 0 - - - 11576 - 3 - 0 - 0 - 0 - - - 11577 - 3 - 0 - 0 - 0 - - - 11578 - 3 - 0 - 0 - 0 - - - 11579 - 3 - 0 - 0 - 0 - - - 11580 - 3 - 0 - 0 - 0 - - - 11581 - 3 - 0 - 0 - 0 - - - 11582 - 3 - 0 - 0 - 0 - - - 11583 - 3 - 0 - 0 - 0 - - - 11584 - 3 - 0 - 0 - 0 - - - 11585 - 3 - 0 - 0 - 0 - - - 11586 - 3 - 0 - 0 - 0 - - - 11587 - 3 - 0 - 0 - 0 - - - 11588 - 3 - 0 - 0 - 0 - - - 11589 - 3 - 0 - 0 - 0 - - - 11590 - 3 - 0 - 0 - 0 - - - 11591 - 3 - 0 - 0 - 0 - - - 11592 - 3 - 0 - 0 - 0 - - - 11593 - 3 - 0 - 0 - 0 - - - 11594 - 3 - 0 - 0 - 0 - - - 11595 - 3 - 0 - 0 - 0 - - - 11596 - 3 - 0 - 0 - 0 - - - 11597 - 3 - 0 - 0 - 0 - - - 11598 - 3 - 0 - 0 - 0 - - - 11599 - 3 - 0 - 0 - 0 - - - 11600 - 3 - 0 - 0 - 0 - - - 11601 - 3 - 0 - 0 - 0 - - - 11602 - 3 - 0 - 0 - 0 - - - 11603 - 3 - 0 - 0 - 0 - - - 11604 - 3 - 0 - 0 - 0 - - - 11605 - 3 - 0 - 0 - 0 - - - 11606 - 3 - 0 - 0 - 0 - - - 11607 - 3 - 0 - 0 - 0 - - - 11608 - 3 - 0 - 0 - 0 - - - 11609 - 3 - 0 - 0 - 0 - - - 11610 - 3 - 0 - 0 - 0 - - - 11611 - 3 - 0 - 0 - 0 - - - 11612 - 3 - 0 - 0 - 0 - - - 11613 - 3 - 0 - 0 - 0 - - - 11614 - 3 - 0 - 0 - 0 - - - 11615 - 3 - 0 - 0 - 0 - - - 11616 - 3 - 0 - 0 - 0 - - - 11617 - 3 - 0 - 0 - 0 - - - 11618 - 3 - 0 - 0 - 0 - - - 11619 - 3 - 0 - 0 - 0 - - - 11620 - 3 - 0 - 0 - 0 - - - 11621 - 3 - 0 - 0 - 0 - - - 11622 - 3 - 0 - 0 - 0 - - - 11623 - 3 - 0 - 0 - 0 - - - 11624 - 3 - 0 - 0 - 0 - - - 11625 - 3 - 0 - 0 - 0 - - - 11626 - 3 - 0 - 0 - 0 - - - 11627 - 3 - 0 - 0 - 0 - - - 11628 - 3 - 0 - 0 - 0 - - - 11629 - 3 - 0 - 0 - 0 - - - 11630 - 3 - 0 - 0 - 0 - - - 11631 - 3 - 0 - 0 - 0 - - - 11632 - 3 - 0 - 0 - 0 - - - 11633 - 3 - 0 - 0 - 0 - - - 11634 - 3 - 0 - 0 - 0 - - - 11635 - 3 - 0 - 0 - 0 - - - 11636 - 3 - 0 - 0 - 0 - - - 11637 - 3 - 0 - 0 - 0 - - - 11638 - 3 - 0 - 0 - 0 - - - 11639 - 3 - 0 - 0 - 0 - - - 11640 - 1 - 0 - 0 - 0 - - - 11641 - 3 - 0 - 0 - 0 - - - 11642 - 3 - 0 - 0 - 0 - - - 11643 - 3 - 0 - 0 - 0 - - - 11644 - 3 - 0 - 0 - 0 - - - 11645 - 3 - 0 - 0 - 0 - - - 11646 - 3 - 0 - 0 - 0 - - - 11647 - 3 - 0 - 0 - 0 - - - 11648 - 3 - 0 - 0 - 0 - - - 11649 - 3 - 0 - 0 - 0 - - - 11650 - 3 - 0 - 0 - 0 - - - 11651 - 3 - 0 - 0 - 0 - - - 11652 - 3 - 0 - 0 - 0 - - - 11653 - 3 - 0 - 0 - 0 - - - 11654 - 3 - 0 - 0 - 0 - - - 11655 - 3 - 0 - 0 - 0 - - - 11656 - 3 - 0 - 0 - 0 - - - 11657 - 3 - 0 - 0 - 0 - - - 11658 - 1 - 0 - 0 - 0 - - - 11659 - 1 - 0 - 0 - 0 - - - 11660 - 1 - 0 - 0 - 0 - - - 11661 - 1 - 0 - 0 - 0 - - - 11662 - 1 - 0 - 0 - 0 - - - 11663 - 9851 - 0 - 0 - 0 - - - 11664 - 9851 - 0 - 0 - 0 - - - 11665 - 9851 - 0 - 0 - 0 - - - 11666 - 100 - 0 - 0 - 0 - - - 11667 - 100 - 0 - 0 - 0 - - - 11668 - 100 - 0 - 0 - 0 - - - 11669 - 100 - 0 - 0 - 0 - - - 11670 - 100 - 0 - 0 - 0 - - - 11671 - 100 - 0 - 0 - 0 - - - 11672 - 100 - 0 - 0 - 0 - - - 11673 - 100 - 0 - 0 - 0 - - - 11674 - 9851 - 0 - 0 - 0 - - - 11675 - 9851 - 0 - 0 - 0 - - - 11676 - 9851 - 0 - 0 - 0 - - - 11677 - 6 - 0 - 0 - 0 - - - 11678 - 1 - 0 - 0 - 0 - - - 11679 - 1 - 0 - 0 - 0 - - - 11680 - 1 - 0 - 0 - 0 - - - 11681 - 1 - 0 - 0 - 0 - - - 11682 - 1 - 0 - 0 - 0 - - - 11683 - 1 - 0 - 0 - 0 - - - 11684 - 1 - 0 - 0 - 0 - - - 11685 - 1 - 0 - 0 - 0 - - - 11686 - 500000 - 0 - 0 - 0 - - - 11687 - 500000 - 0 - 0 - 0 - - - 11688 - 500000 - 0 - 0 - 0 - - - 11689 - 500000 - 0 - 0 - 0 - - - 11690 - 750000 - 0 - 0 - 0 - - - 11691 - 750000 - 0 - 0 - 0 - - - 11692 - 500000 - 0 - 0 - 0 - - - 11693 - 500000 - 0 - 0 - 0 - - - 11694 - 1250000 - 0 - 0 - 0 - - - 11695 - 1250000 - 0 - 0 - 0 - - - 11696 - 1250000 - 0 - 0 - 0 - - - 11697 - 1250000 - 0 - 0 - 0 - - - 11698 - 1250000 - 0 - 0 - 0 - - - 11699 - 1250000 - 0 - 0 - 0 - - - 11700 - 1250000 - 0 - 0 - 0 - - - 11701 - 1250000 - 0 - 0 - 0 - - - 11702 - 500000 - 0 - 0 - 0 - - - 11703 - 500000 - 0 - 0 - 0 - - - 11704 - 500000 - 0 - 0 - 0 - - - 11705 - 500000 - 0 - 0 - 0 - - - 11706 - 500000 - 0 - 0 - 0 - - - 11707 - 500000 - 0 - 0 - 0 - - - 11708 - 500000 - 0 - 0 - 0 - - - 11709 - 500000 - 0 - 0 - 0 - - - 11710 - 250000 - 0 - 0 - 0 - - - 11711 - 250000 - 0 - 0 - 0 - - - 11712 - 250000 - 0 - 0 - 0 - - - 11713 - 250000 - 0 - 0 - 0 - - - 11714 - 250000 - 0 - 0 - 0 - - - 11715 - 250000 - 0 - 0 - 0 - - - 11716 - 100000 - 0 - 0 - 0 - - - 11717 - 100000 - 0 - 0 - 0 - - - 11718 - 20000 - 0 - 0 - 0 - - - 11719 - 20000 - 0 - 0 - 0 - - - 11720 - 60000 - 0 - 0 - 0 - - - 11721 - 60000 - 0 - 0 - 0 - - - 11722 - 50000 - 0 - 0 - 0 - - - 11723 - 50000 - 0 - 0 - 0 - - - 11724 - 265000 - 0 - 0 - 0 - - - 11725 - 265000 - 0 - 0 - 0 - - - 11726 - 272500 - 0 - 0 - 0 - - - 11727 - 272500 - 0 - 0 - 0 - - - 11728 - 25000 - 0 - 0 - 0 - - - 11729 - 25000 - 0 - 0 - 0 - - - 11730 - 130000 - 0 - 0 - 0 - - - 11731 - 130000 - 0 - 0 - 0 - - - 11732 - 20000 - 0 - 0 - 0 - - - 11733 - 20000 - 0 - 0 - 0 - - - 11734 - 1 - 0 - 0 - 0 - - - 11735 - 1 - 0 - 0 - 0 - - - 11736 - 17000 - 0 - 0 - 0 - - - 11737 - 17000 - 0 - 0 - 0 - - - 11738 - 45000 - 0 - 0 - 0 - - - 11739 - 45000 - 0 - 0 - 0 - - - 11740 - 1 - 0 - 0 - 0 - - - 11741 - 1 - 0 - 0 - 0 - - - 11742 - 1 - 0 - 0 - 0 - - - 11743 - 1 - 0 - 0 - 0 - - - 11744 - 1 - 0 - 0 - 0 - - - 11745 - 1 - 0 - 0 - 0 - - - 11746 - 1 - 0 - 0 - 0 - - - 11747 - 1 - 0 - 0 - 0 - - - 11748 - 1 - 0 - 0 - 0 - - - 11749 - 10 - 0 - 0 - 0 - - - 11750 - 1 - 0 - 0 - 0 - - - 11751 - 1 - 0 - 0 - 0 - - - 11752 - 1 - 0 - 0 - 0 - - - 11753 - 1 - 0 - 0 - 0 - - - 11754 - 1 - 0 - 0 - 0 - - - 11755 - 1 - 0 - 0 - 0 - - - 11756 - 1 - 0 - 0 - 0 - - - 11757 - 1 - 0 - 0 - 0 - - - 11758 - 1 - 0 - 0 - 0 - - - 11759 - 1 - 0 - 0 - 0 - - - 11760 - 1 - 0 - 0 - 0 - - - 11761 - 1 - 0 - 0 - 0 - - - 11762 - 1 - 0 - 0 - 0 - - - 11763 - 1 - 0 - 0 - 0 - - - 11764 - 1 - 0 - 0 - 0 - - - 11765 - 1 - 0 - 0 - 0 - - - 11766 - 1 - 0 - 0 - 0 - - - 11767 - 1 - 0 - 0 - 0 - - - 11768 - 1 - 0 - 0 - 0 - - - 11769 - 1 - 0 - 0 - 0 - - - 11770 - 1 - 0 - 0 - 0 - - - 11771 - 1 - 0 - 0 - 0 - - - 11772 - 1 - 0 - 0 - 0 - - - 11773 - 1 - 0 - 0 - 0 - - - 11774 - 1 - 0 - 0 - 0 - - - 11775 - 1 - 0 - 0 - 0 - - - 11776 - 1 - 0 - 0 - 0 - - - 11777 - 1 - 0 - 0 - 0 - - - 11778 - 1 - 0 - 0 - 0 - - - 11779 - 1 - 0 - 0 - 0 - - - 11780 - 1 - 0 - 0 - 0 - - - 11781 - 5 - 0 - 0 - 0 - - - 11782 - 1 - 0 - 0 - 0 - - - 11783 - 1 - 0 - 0 - 0 - - - 11784 - 1 - 0 - 0 - 0 - - - 11785 - 1 - 0 - 0 - 0 - - - 11786 - 1 - 0 - 0 - 0 - - - 11787 - 1 - 0 - 0 - 0 - - - 11788 - 1 - 0 - 0 - 0 - - - 11789 - 1 - 0 - 0 - 0 - - - 11790 - 1 - 0 - 0 - 0 - - - 11791 - 40 - 0 - 0 - 0 - - - 11792 - 1 - 0 - 0 - 0 - - - 11793 - 1 - 0 - 0 - 0 - - - 11794 - 20 - 0 - 0 - 0 - - - 11795 - 1 - 0 - 0 - 0 - - - 11796 - 1 - 0 - 0 - 0 - - - 11797 - 1 - 0 - 0 - 0 - - - 11798 - 1 - 0 - 0 - 0 - - - 11799 - 1 - 0 - 0 - 0 - - - 11800 - 1 - 0 - 0 - 0 - - - 11801 - 1 - 0 - 0 - 0 - - - 11802 - 1 - 0 - 0 - 0 - - - 11803 - 1 - 0 - 0 - 0 - - - 11804 - 1 - 0 - 0 - 0 - - - 11805 - 1 - 0 - 0 - 0 - - - 11806 - 1 - 0 - 0 - 0 - - - 11807 - 10 - 0 - 0 - 0 - - - 11808 - 20 - 0 - 0 - 0 - - - 11809 - 14 - 0 - 0 - 0 - - - 11810 - 13 - 0 - 0 - 0 - - - 11811 - 13 - 0 - 0 - 0 - - - 11812 - 13 - 0 - 0 - 0 - - - 11813 - 1 - 0 - 0 - 0 - - - 11814 - 20 - 0 - 0 - 0 - - - 11815 - 20 - 0 - 0 - 0 - - - 11816 - 20 - 0 - 0 - 0 - - - 11817 - 20 - 0 - 0 - 0 - - - 11818 - 20 - 0 - 0 - 0 - - - 11819 - 20 - 0 - 0 - 0 - - - 11820 - 20 - 0 - 0 - 0 - - - 11821 - 20 - 0 - 0 - 0 - - - 11822 - 20 - 0 - 0 - 0 - - - 11823 - 20 - 0 - 0 - 0 - - - 11824 - 20 - 0 - 0 - 0 - - - 11825 - 20 - 0 - 0 - 0 - - - 11826 - 20 - 0 - 0 - 0 - - - 11827 - 20 - 0 - 0 - 0 - - - 11828 - 20 - 0 - 0 - 0 - - - 11829 - 20 - 0 - 0 - 0 - - - 11830 - 20 - 0 - 0 - 0 - - - 11831 - 20 - 0 - 0 - 0 - - - 11832 - 20 - 0 - 0 - 0 - - - 11833 - 20 - 0 - 0 - 0 - - - 11834 - 20 - 0 - 0 - 0 - - - 11835 - 20 - 0 - 0 - 0 - - - 11836 - 20 - 0 - 0 - 0 - - - 11837 - 20 - 0 - 0 - 0 - - - 11838 - 20 - 0 - 0 - 0 - - - 11839 - 20 - 0 - 0 - 0 - - - 11840 - 20 - 0 - 0 - 0 - - - 11841 - 20 - 0 - 0 - 0 - - - 11842 - 20 - 0 - 0 - 0 - - - 11843 - 20 - 0 - 0 - 0 - - - 11844 - 20 - 0 - 0 - 0 - - - 11845 - 20 - 0 - 0 - 0 - - - 11846 - 20 - 0 - 0 - 0 - - - 11847 - 20 - 0 - 0 - 0 - - - 11848 - 20 - 0 - 0 - 0 - - - 11849 - 20 - 0 - 0 - 0 - - - 11850 - 20 - 0 - 0 - 0 - - - 11851 - 20 - 0 - 0 - 0 - - - 11852 - 20 - 0 - 0 - 0 - - - 11853 - 20 - 0 - 0 - 0 - - - 11854 - 20 - 0 - 0 - 0 - - - 11855 - 20 - 0 - 0 - 0 - - - 11856 - 20 - 0 - 0 - 0 - - - 11857 - 20 - 0 - 0 - 0 - - - 11858 - 20 - 0 - 0 - 0 - - - 11859 - 20 - 0 - 0 - 0 - - - 11860 - 20 - 0 - 0 - 0 - - - 11861 - 20 - 0 - 0 - 0 - - - 11862 - 20 - 0 - 0 - 0 - - - 11863 - 20 - 0 - 0 - 0 - - - 11864 - 20 - 0 - 0 - 0 - - - 11865 - 20 - 0 - 0 - 0 - - - 11866 - 20 - 0 - 0 - 0 - - - 11867 - 20 - 0 - 0 - 0 - - - 11868 - 20 - 0 - 0 - 0 - - - 11869 - 20 - 0 - 0 - 0 - - - 11870 - 20 - 0 - 0 - 0 - - - 11871 - 20 - 0 - 0 - 0 - - - 11872 - 20 - 0 - 0 - 0 - - - 11873 - 20 - 0 - 0 - 0 - - - 11874 - 20 - 0 - 0 - 0 - - - 11875 - 20 - 0 - 0 - 0 - - - 11876 - 20 - 0 - 0 - 0 - - - 11877 - 20 - 0 - 0 - 0 - - - 11878 - 20 - 0 - 0 - 0 - - - 11879 - 20 - 0 - 0 - 0 - - - 11880 - 20 - 0 - 0 - 0 - - - 11881 - 20 - 0 - 0 - 0 - - - 11882 - 20 - 0 - 0 - 0 - - - 11883 - 20 - 0 - 0 - 0 - - - 11884 - 20 - 0 - 0 - 0 - - - 11885 - 20 - 0 - 0 - 0 - - - 11886 - 20 - 0 - 0 - 0 - - - 11887 - 20 - 0 - 0 - 0 - - - 11888 - 20 - 0 - 0 - 0 - - - 11889 - 20 - 0 - 0 - 0 - - - 11890 - 20 - 0 - 0 - 0 - - - 11891 - 20 - 0 - 0 - 0 - - - 11892 - 20 - 0 - 0 - 0 - - - 11893 - 20 - 0 - 0 - 0 - - - 11894 - 20 - 0 - 0 - 0 - - - 11895 - 20 - 0 - 0 - 0 - - - 11896 - 20 - 0 - 0 - 0 - - - 11897 - 20 - 0 - 0 - 0 - - - 11898 - 20 - 0 - 0 - 0 - - - 11899 - 20 - 0 - 0 - 0 - - - 11900 - 20 - 0 - 0 - 0 - - - 11901 - 20 - 0 - 0 - 0 - - - 11902 - 20 - 0 - 0 - 0 - - - 11903 - 20 - 0 - 0 - 0 - - - 11904 - 20 - 0 - 0 - 0 - - - 11905 - 20 - 0 - 0 - 0 - - - 11906 - 20 - 0 - 0 - 0 - - - 11907 - 20 - 0 - 0 - 0 - - - 11908 - 20 - 0 - 0 - 0 - - - 11909 - 20 - 0 - 0 - 0 - - - 11910 - 20 - 0 - 0 - 0 - - - 11911 - 20 - 0 - 0 - 0 - - - 11912 - 20 - 0 - 0 - 0 - - - 11913 - 20 - 0 - 0 - 0 - - - 11914 - 20 - 0 - 0 - 0 - - - 11915 - 20 - 0 - 0 - 0 - - - 11916 - 20 - 0 - 0 - 0 - - - 11917 - 20 - 0 - 0 - 0 - - - 11918 - 20 - 0 - 0 - 0 - - - 11919 - 20 - 0 - 0 - 0 - - - 11920 - 20 - 0 - 0 - 0 - - - 11921 - 20 - 0 - 0 - 0 - - - 11922 - 20 - 0 - 0 - 0 - - - 11923 - 20 - 0 - 0 - 0 - - - 11924 - 20 - 0 - 0 - 0 - - - 11925 - 20 - 0 - 0 - 0 - - - 11926 - 20 - 0 - 0 - 0 - - - 11927 - 20 - 0 - 0 - 0 - - - 11928 - 20 - 0 - 0 - 0 - - - 11929 - 20 - 0 - 0 - 0 - - - 11930 - 20 - 0 - 0 - 0 - - - 11931 - 20 - 0 - 0 - 0 - - - 11932 - 20 - 0 - 0 - 0 - - - 11933 - 20 - 0 - 0 - 0 - - - 11934 - 20 - 0 - 0 - 0 - - - 11935 - 20 - 0 - 0 - 0 - - - 11936 - 20 - 0 - 0 - 0 - - - 11937 - 20 - 0 - 0 - 0 - - - 11938 - 20 - 0 - 0 - 0 - - - 11939 - 20 - 0 - 0 - 0 - - - 11940 - 20 - 0 - 0 - 0 - - - 11941 - 20 - 0 - 0 - 0 - - - 11942 - 20 - 0 - 0 - 0 - - - 11943 - 20 - 0 - 0 - 0 - - - 11944 - 20 - 0 - 0 - 0 - - - 11945 - 20 - 0 - 0 - 0 - - - 11946 - 20 - 0 - 0 - 0 - - - 11947 - 20 - 0 - 0 - 0 - - - 11948 - 1 - 0 - 0 - 0 - - - 11949 - 1 - 0 - 0 - 0 - - - 11950 - 1 - 0 - 0 - 0 - - - 11951 - 1 - 0 - 0 - 0 - - - 11952 - 1 - 0 - 0 - 0 - - - 11953 - 1 - 0 - 0 - 0 - - - 11954 - 1 - 0 - 0 - 0 - - - 11955 - 1 - 0 - 0 - 0 - - - 11956 - 1 - 0 - 0 - 0 - - - 11957 - 1 - 0 - 0 - 0 - - - 11958 - 1 - 0 - 0 - 0 - - - 11959 - 1 - 0 - 0 - 0 - - - 11960 - 20 - 0 - 0 - 0 - - - 11961 - 20 - 0 - 0 - 0 - - - 11962 - 20 - 0 - 0 - 0 - - - 11963 - 20 - 0 - 0 - 0 - - - 11964 - 1 - 0 - 0 - 0 - - - 11965 - 1 - 0 - 0 - 0 - - - 11966 - 1 - 0 - 0 - 0 - - - 11967 - 20 - 0 - 0 - 0 - - - 11968 - 20 - 0 - 0 - 0 - - - 11969 - 100 - 0 - 0 - 0 - - - 11970 - 1 - 0 - 0 - 0 - - - 11971 - 1 - 0 - 0 - 0 - - - 11972 - 1 - 0 - 0 - 0 - - - 11973 - 1 - 0 - 0 - 0 - - - 11974 - 1 - 0 - 0 - 0 - - - 11975 - 1 - 0 - 0 - 0 - - - 11976 - 1 - 0 - 0 - 0 - - - 11977 - 1 - 0 - 0 - 0 - - - 11978 - 1 - 0 - 0 - 0 - - - 11979 - 1 - 0 - 0 - 0 - - - 11980 - 1 - 0 - 0 - 0 - - - 11981 - 1 - 0 - 0 - 0 - - - 11982 - 1 - 0 - 0 - 0 - - - 11983 - 1 - 0 - 0 - 0 - - - 11984 - 1 - 0 - 0 - 0 - - - 11985 - 1 - 0 - 0 - 0 - - - 11986 - 1 - 0 - 0 - 0 - - - 11987 - 1 - 0 - 0 - 0 - - - 11988 - 1 - 0 - 0 - 0 - - - 11989 - 1 - 0 - 0 - 0 - - - 11990 - 1 - 0 - 0 - 0 - - - 11991 - 1 - 0 - 0 - 0 - - - 11992 - 1 - 0 - 0 - 0 - - - 11993 - 1 - 0 - 0 - 0 - - - 11994 - 1 - 0 - 0 - 0 - - - 11995 - 1 - 0 - 0 - 0 - - - 11996 - 1 - 0 - 0 - 0 - - - 11997 - 1 - 0 - 0 - 0 - - - 11998 - 1 - 0 - 0 - 0 - - - 11999 - 1 - 0 - 0 - 0 - - - 12000 - 1 - 0 - 0 - 0 - - - 12001 - 1 - 0 - 0 - 0 - - - 12002 - 1 - 0 - 0 - 0 - - - 12003 - 1 - 0 - 0 - 0 - - - 12004 - 1 - 0 - 0 - 0 - - - 12005 - 1 - 0 - 0 - 0 - - - 12006 - 1 - 0 - 0 - 0 - - - 12007 - 724 - 0 - 0 - 0 - - - 12008 - 724 - 0 - 0 - 0 - - - 12009 - 599 - 0 - 0 - 0 - - - 12010 - 599 - 0 - 0 - 0 - - - 12011 - 4624 - 0 - 0 - 0 - - - 12012 - 4624 - 0 - 0 - 0 - - - 12013 - 3524 - 0 - 0 - 0 - - - 12014 - 3524 - 0 - 0 - 0 - - - 12015 - 3324 - 0 - 0 - 0 - - - 12016 - 3324 - 0 - 0 - 0 - - - 12017 - 449 - 0 - 0 - 0 - - - 12018 - 449 - 0 - 0 - 0 - - - 12019 - 649 - 0 - 0 - 0 - - - 12020 - 649 - 0 - 0 - 0 - - - 12021 - 2224 - 0 - 0 - 0 - - - 12022 - 2224 - 0 - 0 - 0 - - - 12023 - 4024 - 0 - 0 - 0 - - - 12024 - 4024 - 0 - 0 - 0 - - - 12025 - 3624 - 0 - 0 - 0 - - - 12026 - 3624 - 0 - 0 - 0 - - - 12027 - 4199 - 0 - 0 - 0 - - - 12028 - 4199 - 0 - 0 - 0 - - - 12029 - 3174 - 0 - 0 - 0 - - - 12030 - 3174 - 0 - 0 - 0 - - - 12031 - 449 - 0 - 0 - 0 - - - 12032 - 449 - 0 - 0 - 0 - - - 12033 - 3674 - 0 - 0 - 0 - - - 12034 - 3674 - 0 - 0 - 0 - - - 12035 - 3074 - 0 - 0 - 0 - - - 12036 - 3074 - 0 - 0 - 0 - - - 12037 - 3399 - 0 - 0 - 0 - - - 12038 - 3399 - 0 - 0 - 0 - - - 12039 - 3924 - 0 - 0 - 0 - - - 12040 - 3924 - 0 - 0 - 0 - - - 12041 - 2624 - 0 - 0 - 0 - - - 12042 - 2624 - 0 - 0 - 0 - - - 12043 - 624 - 0 - 0 - 0 - - - 12044 - 624 - 0 - 0 - 0 - - - 12045 - 3624 - 0 - 0 - 0 - - - 12046 - 3624 - 0 - 0 - 0 - - - 12047 - 599 - 0 - 0 - 0 - - - 12048 - 599 - 0 - 0 - 0 - - - 12049 - 1549 - 0 - 0 - 0 - - - 12050 - 1549 - 0 - 0 - 0 - - - 12051 - 3024 - 0 - 0 - 0 - - - 12052 - 3024 - 0 - 0 - 0 - - - 12053 - 2449 - 0 - 0 - 0 - - - 12054 - 2449 - 0 - 0 - 0 - - - 12055 - 1849 - 0 - 0 - 0 - - - 12056 - 1849 - 0 - 0 - 0 - - - 12057 - 774 - 0 - 0 - 0 - - - 12058 - 774 - 0 - 0 - 0 - - - 12059 - 624 - 0 - 0 - 0 - - - 12060 - 624 - 0 - 0 - 0 - - - 12061 - 3349 - 0 - 0 - 0 - - - 12062 - 3349 - 0 - 0 - 0 - - - 12063 - 1699 - 0 - 0 - 0 - - - 12064 - 1699 - 0 - 0 - 0 - - - 12065 - 2524 - 0 - 0 - 0 - - - 12066 - 2524 - 0 - 0 - 0 - - - 12067 - 2299 - 0 - 0 - 0 - - - 12068 - 2299 - 0 - 0 - 0 - - - 12069 - 4574 - 0 - 0 - 0 - - - 12070 - 4574 - 0 - 0 - 0 - - - 12071 - 2374 - 0 - 0 - 0 - - - 12072 - 2374 - 0 - 0 - 0 - - - 12073 - 2974 - 0 - 0 - 0 - - - 12074 - 2974 - 0 - 0 - 0 - - - 12075 - 3549 - 0 - 0 - 0 - - - 12076 - 3549 - 0 - 0 - 0 - - - 12077 - 3949 - 0 - 0 - 0 - - - 12078 - 3949 - 0 - 0 - 0 - - - 12079 - 4224 - 0 - 0 - 0 - - - 12080 - 4224 - 0 - 0 - 0 - - - 12081 - 4024 - 0 - 0 - 0 - - - 12082 - 4024 - 0 - 0 - 0 - - - 12083 - 449 - 0 - 0 - 0 - - - 12084 - 449 - 0 - 0 - 0 - - - 12085 - 3949 - 0 - 0 - 0 - - - 12086 - 3949 - 0 - 0 - 0 - - - 12087 - 699 - 0 - 0 - 0 - - - 12088 - 699 - 0 - 0 - 0 - - - 12089 - 5499 - 0 - 0 - 0 - - - 12090 - 5499 - 0 - 0 - 0 - - - 12091 - 1599 - 0 - 0 - 0 - - - 12092 - 1599 - 0 - 0 - 0 - - - 12093 - 5699 - 0 - 0 - 0 - - - 12094 - 5699 - 0 - 0 - 0 - - - 12095 - 2624 - 0 - 0 - 0 - - - 12096 - 2624 - 0 - 0 - 0 - - - 12097 - 2624 - 0 - 0 - 0 - - - 12098 - 2624 - 0 - 0 - 0 - - - 12099 - 2624 - 0 - 0 - 0 - - - 12100 - 2624 - 0 - 0 - 0 - - - 12101 - 2624 - 0 - 0 - 0 - - - 12102 - 2624 - 0 - 0 - 0 - - - 12103 - 2624 - 0 - 0 - 0 - - - 12104 - 2624 - 0 - 0 - 0 - - - 12105 - 2624 - 0 - 0 - 0 - - - 12106 - 2624 - 0 - 0 - 0 - - - 12107 - 2624 - 0 - 0 - 0 - - - 12108 - 2624 - 0 - 0 - 0 - - - 12109 - 100 - 0 - 0 - 0 - - - 12110 - 100 - 0 - 0 - 0 - - - 12111 - 1000 - 0 - 0 - 0 - - - 12112 - 1000 - 0 - 0 - 0 - - - 12113 - 1000 - 0 - 0 - 0 - - - 12114 - 1000 - 0 - 0 - 0 - - - 12115 - 1000 - 0 - 0 - 0 - - - 12116 - 1000 - 0 - 0 - 0 - - - 12117 - 1000 - 0 - 0 - 0 - - - 12118 - 1000 - 0 - 0 - 0 - - - 12119 - 1000 - 0 - 0 - 0 - - - 12120 - 1000 - 0 - 0 - 0 - - - 12121 - 1000 - 0 - 0 - 0 - - - 12122 - 1000 - 0 - 0 - 0 - - - 12123 - 699 - 0 - 0 - 0 - - - 12124 - 699 - 0 - 0 - 0 - - - 12125 - 10 - 0 - 0 - 0 - - - 12126 - 10 - 0 - 0 - 0 - - - 12127 - 10 - 0 - 0 - 0 - - - 12128 - 10 - 0 - 0 - 0 - - - 12129 - 1 - 0 - 0 - 0 - - - 12130 - 10 - 0 - 0 - 0 - - - 12131 - 10 - 0 - 0 - 0 - - - 12132 - 200 - 0 - 0 - 0 - - - 12133 - 200 - 0 - 0 - 0 - - - 12134 - 30 - 0 - 0 - 0 - - - 12135 - 30 - 0 - 0 - 0 - - - 12136 - 20 - 0 - 0 - 0 - - - 12137 - 20 - 0 - 0 - 0 - - - 12138 - 10 - 0 - 0 - 0 - - - 12139 - 10 - 0 - 0 - 0 - - - 12140 - 190 - 0 - 0 - 0 - - - 12141 - 190 - 0 - 0 - 0 - - - 12142 - 152 - 0 - 0 - 0 - - - 12143 - 152 - 0 - 0 - 0 - - - 12144 - 114 - 0 - 0 - 0 - - - 12145 - 114 - 0 - 0 - 0 - - - 12146 - 76 - 0 - 0 - 0 - - - 12147 - 76 - 0 - 0 - 0 - - - 12148 - 20 - 0 - 0 - 0 - - - 12149 - 1 - 0 - 0 - 0 - - - 12150 - 1 - 0 - 0 - 0 - - - 12151 - 1 - 0 - 0 - 0 - - - 12152 - 1 - 0 - 0 - 0 - - - 12153 - 50 - 0 - 0 - 0 - - - 12154 - 50 - 0 - 0 - 0 - - - 12155 - 1 - 0 - 0 - 0 - - - 12156 - 5 - 0 - 0 - 0 - - - 12157 - 5 - 0 - 0 - 0 - - - 12158 - 100 - 0 - 0 - 0 - - - 12159 - 100 - 0 - 0 - 0 - - - 12160 - 100 - 0 - 0 - 0 - - - 12161 - 1 - 0 - 0 - 0 - - - 12162 - 1 - 0 - 0 - 0 - - - 12163 - 100 - 0 - 0 - 0 - - - 12164 - 1 - 0 - 0 - 0 - - - 12165 - 1 - 0 - 0 - 0 - - - 12166 - 1 - 0 - 0 - 0 - - - 12167 - 1 - 0 - 0 - 0 - - - 12168 - 1 - 0 - 0 - 0 - - - 12169 - 99000 - 0 - 0 - 0 - - - 12170 - 99000 - 0 - 0 - 0 - - - 12171 - 1 - 0 - 0 - 0 - - - 12172 - 24 - 0 - 0 - 0 - - - 12173 - 24 - 0 - 0 - 0 - - - 12174 - 1 - 0 - 0 - 0 - - - 12175 - 1 - 0 - 0 - 0 - - - 12176 - 18 - 0 - 0 - 0 - - - 12177 - 1 - 0 - 0 - 0 - - - 12178 - 1 - 0 - 0 - 0 - - - 12179 - 1 - 0 - 0 - 0 - - - 12180 - 1 - 0 - 0 - 0 - - - 12181 - 23 - 0 - 0 - 0 - - - 12182 - 23 - 0 - 0 - 0 - - - 12183 - 25 - 0 - 0 - 0 - - - 12184 - 1 - 0 - 0 - 0 - - - 12185 - 1 - 0 - 0 - 0 - - - 12186 - 1 - 0 - 0 - 0 - - - 12187 - 1 - 0 - 0 - 0 - - - 12188 - 1 - 0 - 0 - 0 - - - 12189 - 1 - 0 - 0 - 0 - - - 12190 - 1 - 0 - 0 - 0 - - - 12191 - 1 - 0 - 0 - 0 - - - 12192 - 1 - 0 - 0 - 0 - - - 12193 - 1 - 0 - 0 - 0 - - - 12194 - 1 - 0 - 0 - 0 - - - 12195 - 1 - 0 - 0 - 0 - - - 12196 - 1 - 0 - 0 - 0 - - - 12197 - 1 - 0 - 0 - 0 - - - 12198 - 1 - 0 - 0 - 0 - - - 12199 - 1 - 0 - 0 - 0 - - - 12200 - 1 - 0 - 0 - 0 - - - 12201 - 1 - 0 - 0 - 0 - - - 12202 - 1 - 0 - 0 - 0 - - - 12203 - 1 - 0 - 0 - 0 - - - 12204 - 100 - 0 - 0 - 0 - - - 12205 - 100 - 0 - 0 - 0 - - - 12206 - 1 - 0 - 0 - 0 - - - 12207 - 200 - 0 - 0 - 0 - - - 12208 - 200 - 0 - 0 - 0 - - - 12209 - 1 - 0 - 0 - 0 - - - 12210 - 400 - 0 - 0 - 0 - - - 12211 - 400 - 0 - 0 - 0 - - - 12212 - 1 - 0 - 0 - 0 - - - 12213 - 400 - 0 - 0 - 0 - - - 12214 - 400 - 0 - 0 - 0 - - - 12215 - 1 - 0 - 0 - 0 - - - 12216 - 400 - 0 - 0 - 0 - - - 12217 - 400 - 0 - 0 - 0 - - - 12218 - 1 - 0 - 0 - 0 - - - 12219 - 400 - 0 - 0 - 0 - - - 12220 - 400 - 0 - 0 - 0 - - - 12221 - 1 - 0 - 0 - 0 - - - 12222 - 400 - 0 - 0 - 0 - - - 12223 - 400 - 0 - 0 - 0 - - - 12224 - 1 - 0 - 0 - 0 - - - 12225 - 1 - 0 - 0 - 0 - - - 12226 - 1 - 0 - 0 - 0 - - - 12227 - 1 - 0 - 0 - 0 - - - 12228 - 1 - 0 - 0 - 0 - - - 12229 - 1 - 0 - 0 - 0 - - - 12230 - 1 - 0 - 0 - 0 - - - 12231 - 1 - 0 - 0 - 0 - - - 12232 - 1 - 0 - 0 - 0 - - - 12233 - 1 - 0 - 0 - 0 - - - 12234 - 1 - 0 - 0 - 0 - - - 12235 - 1 - 0 - 0 - 0 - - - 12236 - 1 - 0 - 0 - 0 - - - 12237 - 1 - 0 - 0 - 0 - - - 12238 - 1 - 0 - 0 - 0 - - - 12239 - 1 - 0 - 0 - 0 - - - 12240 - 1 - 0 - 0 - 0 - - - 12241 - 1 - 0 - 0 - 0 - - - 12242 - 1 - 0 - 0 - 0 - - - 12243 - 1 - 0 - 0 - 0 - - - 12244 - 1 - 0 - 0 - 0 - - - 12245 - 1 - 0 - 0 - 0 - - - 12246 - 1 - 0 - 0 - 0 - - - 12247 - 1 - 0 - 0 - 0 - - - 12248 - 1 - 0 - 0 - 0 - - - 12249 - 1 - 0 - 0 - 0 - - - 12250 - 1 - 0 - 0 - 0 - - - 12251 - 1 - 0 - 0 - 0 - - - 12252 - 1 - 0 - 0 - 0 - - - 12253 - 1 - 0 - 0 - 0 - - - 12254 - 1 - 0 - 0 - 0 - - - 12255 - 1 - 0 - 0 - 0 - - - 12256 - 1 - 0 - 0 - 0 - - - 12257 - 1 - 0 - 0 - 0 - - - 12258 - 1 - 0 - 0 - 0 - - - 12259 - 1 - 0 - 0 - 0 - - - 12260 - 1 - 0 - 0 - 0 - - - 12261 - 1 - 0 - 0 - 0 - - - 12262 - 1 - 0 - 0 - 0 - - - 12263 - 1 - 0 - 0 - 0 - - - 12264 - 1 - 0 - 0 - 0 - - - 12265 - 1 - 0 - 0 - 0 - - - 12266 - 1 - 0 - 0 - 0 - - - 12267 - 1 - 0 - 0 - 0 - - - 12268 - 1 - 0 - 0 - 0 - - - 12269 - 1 - 0 - 0 - 0 - - - 12270 - 1 - 0 - 0 - 0 - - - 12271 - 1 - 0 - 0 - 0 - - - 12272 - 1 - 0 - 0 - 0 - - - 12273 - 1 - 0 - 0 - 0 - - - 12274 - 1 - 0 - 0 - 0 - - - 12275 - 1 - 0 - 0 - 0 - - - 12276 - 1 - 0 - 0 - 0 - - - 12277 - 1 - 0 - 0 - 0 - - - 12278 - 1 - 0 - 0 - 0 - - - 12279 - 1 - 0 - 0 - 0 - - - 12280 - 1 - 0 - 0 - 0 - - - 12281 - 1 - 0 - 0 - 0 - - - 12282 - 1 - 0 - 0 - 0 - - - 12283 - 1 - 0 - 0 - 0 - - - 12284 - 1 - 0 - 0 - 0 - - - 12285 - 1 - 0 - 0 - 0 - - - 12286 - 1 - 0 - 0 - 0 - - - 12287 - 1 - 0 - 0 - 0 - - - 12288 - 1 - 0 - 0 - 0 - - - 12289 - 1 - 0 - 0 - 0 - - - 12290 - 1 - 0 - 0 - 0 - - - 12291 - 1 - 0 - 0 - 0 - - - 12292 - 1 - 0 - 0 - 0 - - - 12293 - 1 - 0 - 0 - 0 - - - 12294 - 1 - 0 - 0 - 0 - - - 12295 - 1 - 0 - 0 - 0 - - - 12296 - 1 - 0 - 0 - 0 - - - 12297 - 1 - 0 - 0 - 0 - - - 12298 - 1 - 0 - 0 - 0 - - - 12299 - 1 - 0 - 0 - 0 - - - 12300 - 1 - 0 - 0 - 0 - - - 12301 - 1 - 0 - 0 - 0 - - - 12302 - 1 - 0 - 0 - 0 - - - 12303 - 1 - 0 - 0 - 0 - - - 12304 - 1 - 0 - 0 - 0 - - - 12305 - 1 - 0 - 0 - 0 - - - 12306 - 1 - 0 - 0 - 0 - - - 12307 - 1 - 0 - 0 - 0 - - - 12308 - 1 - 0 - 0 - 0 - - - 12309 - 1 - 0 - 0 - 0 - - - 12310 - 1 - 0 - 0 - 0 - - - 12311 - 1 - 0 - 0 - 0 - - - 12312 - 1 - 0 - 0 - 0 - - - 12313 - 1 - 0 - 0 - 0 - - - 12314 - 1 - 0 - 0 - 0 - - - 12315 - 1 - 0 - 0 - 0 - - - 12316 - 1 - 0 - 0 - 0 - - - 12317 - 1 - 0 - 0 - 0 - - - 12318 - 1 - 0 - 0 - 0 - - - 12319 - 1 - 0 - 0 - 0 - - - 12320 - 1 - 0 - 0 - 0 - - - 12321 - 1 - 0 - 0 - 0 - - - 12322 - 1 - 0 - 0 - 0 - - - 12323 - 1 - 0 - 0 - 0 - - - 12324 - 1 - 0 - 0 - 0 - - - 12325 - 1 - 0 - 0 - 0 - - - 12326 - 1 - 0 - 0 - 0 - - - 12327 - 1 - 0 - 0 - 0 - - - 12328 - 1 - 0 - 0 - 0 - - - 12329 - 1 - 0 - 0 - 0 - - - 12330 - 1 - 0 - 0 - 0 - - - 12331 - 1 - 0 - 0 - 0 - - - 12332 - 1 - 0 - 0 - 0 - - - 12333 - 1 - 0 - 0 - 0 - - - 12334 - 1 - 0 - 0 - 0 - - - 12335 - 1 - 0 - 0 - 0 - - - 12336 - 1 - 0 - 0 - 0 - - - 12337 - 1 - 0 - 0 - 0 - - - 12338 - 1 - 0 - 0 - 0 - - - 12339 - 1 - 0 - 0 - 0 - - - 12340 - 1 - 0 - 0 - 0 - - - 12341 - 1 - 0 - 0 - 0 - - - 12342 - 1 - 0 - 0 - 0 - - - 12343 - 1 - 0 - 0 - 0 - - - 12344 - 1 - 0 - 0 - 0 - - - 12345 - 1 - 0 - 0 - 0 - - - 12346 - 1 - 0 - 0 - 0 - - - 12347 - 1 - 0 - 0 - 0 - - - 12348 - 1 - 0 - 0 - 0 - - - 12349 - 1 - 0 - 0 - 0 - - - 12350 - 1 - 0 - 0 - 0 - - - 12351 - 1 - 0 - 0 - 0 - - - 12352 - 1 - 0 - 0 - 0 - - - 12353 - 1 - 0 - 0 - 0 - - - 12354 - 1 - 0 - 0 - 0 - - - 12355 - 1 - 0 - 0 - 0 - - - 12356 - 1 - 0 - 0 - 0 - - - 12357 - 1 - 0 - 0 - 0 - - - 12358 - 1 - 0 - 0 - 0 - - - 12359 - 1 - 0 - 0 - 0 - - - 12360 - 1 - 0 - 0 - 0 - - - 12361 - 1 - 0 - 0 - 0 - - - 12362 - 1 - 0 - 0 - 0 - - - 12363 - 1 - 0 - 0 - 0 - - - 12364 - 1 - 0 - 0 - 0 - - - 12365 - 1 - 0 - 0 - 0 - - - 12366 - 1 - 0 - 0 - 0 - - - 12367 - 1 - 0 - 0 - 0 - - - 12368 - 1 - 0 - 0 - 0 - - - 12369 - 1 - 0 - 0 - 0 - - - 12370 - 1 - 0 - 0 - 0 - - - 12371 - 1 - 0 - 0 - 0 - - - 12372 - 1 - 0 - 0 - 0 - - - 12373 - 1 - 0 - 0 - 0 - - - 12374 - 1 - 0 - 0 - 0 - - - 12375 - 1 - 0 - 0 - 0 - - - 12376 - 1 - 0 - 0 - 0 - - - 12377 - 1 - 0 - 0 - 0 - - - 12378 - 1 - 0 - 0 - 0 - - - 12379 - 1 - 0 - 0 - 0 - - - 12380 - 1 - 0 - 0 - 0 - - - 12381 - 1 - 0 - 0 - 0 - - - 12382 - 1 - 0 - 0 - 0 - - - 12383 - 1 - 0 - 0 - 0 - - - 12384 - 1 - 0 - 0 - 0 - - - 12385 - 1 - 0 - 0 - 0 - - - 12386 - 1 - 0 - 0 - 0 - - - 12387 - 1 - 0 - 0 - 0 - - - 12388 - 1 - 0 - 0 - 0 - - - 12389 - 1 - 0 - 0 - 0 - - - 12390 - 1 - 0 - 0 - 0 - - - 12391 - 1 - 0 - 0 - 0 - - - 12392 - 1 - 0 - 0 - 0 - - - 12393 - 1 - 0 - 0 - 0 - - - 12394 - 1 - 0 - 0 - 0 - - - 12395 - 1 - 0 - 0 - 0 - - - 12396 - 1 - 0 - 0 - 0 - - - 12397 - 1 - 0 - 0 - 0 - - - 12398 - 1 - 0 - 0 - 0 - - - 12399 - 1 - 0 - 0 - 0 - - - 12400 - 1 - 0 - 0 - 0 - - - 12401 - 1 - 0 - 0 - 0 - - - 12402 - 1 - 0 - 0 - 0 - - - 12403 - 1 - 0 - 0 - 0 - - - 12404 - 1 - 0 - 0 - 0 - - - 12405 - 1 - 0 - 0 - 0 - - - 12406 - 1 - 0 - 0 - 0 - - - 12407 - 1 - 0 - 0 - 0 - - - 12408 - 1 - 0 - 0 - 0 - - - 12409 - 1 - 0 - 0 - 0 - - - 12410 - 1 - 0 - 0 - 0 - - - 12411 - 1 - 0 - 0 - 0 - - - 12412 - 1 - 0 - 0 - 0 - - - 12413 - 1 - 0 - 0 - 0 - - - 12414 - 1 - 0 - 0 - 0 - - - 12415 - 1 - 0 - 0 - 0 - - - 12416 - 1 - 0 - 0 - 0 - - - 12417 - 1 - 0 - 0 - 0 - - - 12418 - 1 - 0 - 0 - 0 - - - 12419 - 1 - 0 - 0 - 0 - - - 12420 - 1 - 0 - 0 - 0 - - - 12421 - 1 - 0 - 0 - 0 - - - 12422 - 237 - 0 - 0 - 0 - - - 12423 - 367 - 0 - 0 - 0 - - - 12424 - 314 - 0 - 0 - 0 - - - 12425 - 59 - 0 - 0 - 0 - - - 12426 - 262 - 0 - 0 - 0 - - - 12427 - 339 - 0 - 0 - 0 - - - 12428 - 62 - 0 - 0 - 0 - - - 12429 - 222 - 0 - 0 - 0 - - - 12430 - 229 - 0 - 0 - 0 - - - 12431 - 69 - 0 - 0 - 0 - - - 12432 - 184 - 0 - 0 - 0 - - - 12433 - 252 - 0 - 0 - 0 - - - 12434 - 392 - 0 - 0 - 0 - - - 12435 - 569 - 0 - 0 - 0 - - - 12436 - 332 - 0 - 0 - 0 - - - 12437 - 549 - 0 - 0 - 0 - - - 12438 - 317 - 0 - 0 - 0 - - - 12439 - 44 - 0 - 0 - 0 - - - 12440 - 159 - 0 - 0 - 0 - - - 12441 - 72 - 0 - 0 - 0 - - - 12442 - 362 - 0 - 0 - 0 - - - 12443 - 227 - 0 - 0 - 0 - - - 12444 - 334 - 0 - 0 - 0 - - - 12445 - 62 - 0 - 0 - 0 - - - 12446 - 169 - 0 - 0 - 0 - - - 12447 - 244 - 0 - 0 - 0 - - - 12448 - 302 - 0 - 0 - 0 - - - 12449 - 457 - 0 - 0 - 0 - - - 12450 - 462 - 0 - 0 - 0 - - - 12451 - 77 - 0 - 0 - 0 - - - 12452 - 69 - 0 - 0 - 0 - - - 12453 - 419 - 0 - 0 - 0 - - - 12454 - 307 - 0 - 0 - 0 - - - 12455 - 402 - 0 - 0 - 0 - - - 12456 - 44 - 0 - 0 - 0 - - - 12457 - 352 - 0 - 0 - 0 - - - 12458 - 262 - 0 - 0 - 0 - - - 12459 - 64 - 0 - 0 - 0 - - - 12460 - 154 - 0 - 0 - 0 - - - 12461 - 297 - 0 - 0 - 0 - - - 12462 - 354 - 0 - 0 - 0 - - - 12463 - 394 - 0 - 0 - 0 - - - 12464 - 422 - 0 - 0 - 0 - - - 12465 - 402 - 0 - 0 - 0 - - - 12466 - 44 - 0 - 0 - 0 - - - 12467 - 362 - 0 - 0 - 0 - - - 12468 - 394 - 0 - 0 - 0 - - - 12469 - 1 - 0 - 0 - 0 - - - 12470 - 1 - 0 - 0 - 0 - - - 12471 - 1 - 0 - 0 - 0 - - - 12472 - 1 - 0 - 0 - 0 - - - 12473 - 1 - 0 - 0 - 0 - - - 12474 - 1 - 0 - 0 - 0 - - - 12475 - 1 - 0 - 0 - 0 - - - 12476 - 1 - 0 - 0 - 0 - - - 12477 - 1 - 0 - 0 - 0 - - - 12478 - 1 - 0 - 0 - 0 - - - 12479 - 1 - 0 - 0 - 0 - - - 12480 - 1 - 0 - 0 - 0 - - - 12481 - 1 - 0 - 0 - 0 - - - 12482 - 1 - 0 - 0 - 0 - - - 12483 - 1 - 0 - 0 - 0 - - - 12484 - 1 - 0 - 0 - 0 - - - 12485 - 1 - 0 - 0 - 0 - - - 12486 - 1 - 0 - 0 - 0 - - - 12487 - 1 - 0 - 0 - 0 - - - 12488 - 1 - 0 - 0 - 0 - - - 12489 - 1 - 0 - 0 - 0 - - - 12490 - 1 - 0 - 0 - 0 - - - 12491 - 1 - 0 - 0 - 0 - - - 12492 - 1 - 0 - 0 - 0 - - - 12493 - 1 - 0 - 0 - 0 - - - 12494 - 1 - 0 - 0 - 0 - - - 12495 - 1 - 0 - 0 - 0 - - - 12496 - 1 - 0 - 0 - 0 - - - 12497 - 1 - 0 - 0 - 0 - - - 12498 - 1 - 0 - 0 - 0 - - - 12499 - 1 - 0 - 0 - 0 - - - 12500 - 1 - 0 - 0 - 0 - - - 12501 - 1 - 0 - 0 - 0 - - - 12502 - 1 - 0 - 0 - 0 - - - 12503 - 1 - 0 - 0 - 0 - - - 12504 - 1 - 0 - 0 - 0 - - - 12505 - 1 - 0 - 0 - 0 - - - 12506 - 1 - 0 - 0 - 0 - - - 12507 - 1 - 0 - 0 - 0 - - - 12508 - 1 - 0 - 0 - 0 - - - 12509 - 1 - 0 - 0 - 0 - - - 12510 - 1 - 0 - 0 - 0 - - - 12511 - 1 - 0 - 0 - 0 - - - 12512 - 1 - 0 - 0 - 0 - - - 12513 - 1 - 0 - 0 - 0 - - - 12514 - 1 - 0 - 0 - 0 - - - 12515 - 1 - 0 - 0 - 0 - - - 12516 - 1 - 0 - 0 - 0 - - - 12517 - 1 - 0 - 0 - 0 - - - 12518 - 1 - 0 - 0 - 0 - - - 12519 - 1 - 0 - 0 - 0 - - - 12520 - 1 - 0 - 0 - 0 - - - 12521 - 1 - 0 - 0 - 0 - - - 12522 - 1 - 0 - 0 - 0 - - - 12523 - 1 - 0 - 0 - 0 - - - 12524 - 99000 - 0 - 0 - 0 - - - 12525 - 1 - 0 - 0 - 0 - - - 12526 - 1 - 0 - 0 - 0 - - - 12527 - 1 - 0 - 0 - 0 - - - 12528 - 1 - 0 - 0 - 0 - - - 12529 - 1 - 0 - 0 - 0 - - - 12530 - 1 - 0 - 0 - 0 - - - 12531 - 3149 - 0 - 0 - 0 - - - 12532 - 3149 - 0 - 0 - 0 - - - 12533 - 59 - 0 - 0 - 0 - - - 12535 - 500 - 0 - 0 - 0 - - - 12536 - 500 - 0 - 0 - 0 - - - 12537 - 1 - 0 - 0 - 0 - - - 12538 - 1 - 0 - 0 - 0 - - - 12539 - 200 - 0 - 0 - 0 - - - 12540 - 1 - 0 - 0 - 0 - - - 12541 - 1 - 0 - 0 - 0 - - - 12542 - 1 - 0 - 0 - 0 - - - 12543 - 1 - 0 - 0 - 0 - - - 12544 - 1 - 0 - 0 - 0 - - - 12545 - 1 - 0 - 0 - 0 - - - 12546 - 1 - 0 - 0 - 0 - - - 12547 - 1 - 0 - 0 - 0 - - - 12548 - 1 - 0 - 0 - 0 - - - 12549 - 1 - 0 - 0 - 0 - - - 12550 - 1 - 0 - 0 - 0 - - - 12551 - 1 - 0 - 0 - 0 - - - 12552 - 1 - 0 - 0 - 0 - - - 12553 - 1 - 0 - 0 - 0 - - - 12554 - 1 - 0 - 0 - 0 - - - 12555 - 1 - 0 - 0 - 0 - - - 12556 - 1 - 0 - 0 - 0 - - - 12557 - 1 - 0 - 0 - 0 - - - 12558 - 10 - 0 - 0 - 0 - - - 12559 - 60 - 0 - 0 - 0 - - - 12560 - 60 - 0 - 0 - 0 - - - 12561 - 60 - 0 - 0 - 0 - - - 12562 - 60 - 0 - 0 - 0 - - - 12563 - 60 - 0 - 0 - 0 - - - 12564 - 60 - 0 - 0 - 0 - - - 12565 - 60 - 0 - 0 - 0 - - - 12566 - 60 - 0 - 0 - 0 - - - 12567 - 60 - 0 - 0 - 0 - - - 12568 - 100 - 0 - 0 - 0 - - - 12569 - 100 - 0 - 0 - 0 - - - 12570 - 180 - 0 - 0 - 0 - - - 12571 - 180 - 0 - 0 - 0 - - - 12572 - 4 - 0 - 0 - 0 - - - 12573 - 4 - 0 - 0 - 0 - - - 12574 - 4 - 0 - 0 - 0 - - - 12575 - 4 - 0 - 0 - 0 - - - 12576 - 4 - 0 - 0 - 0 - - - 12577 - 4 - 0 - 0 - 0 - - - 12578 - 4 - 0 - 0 - 0 - - - 12579 - 4 - 0 - 0 - 0 - - - 12580 - 4 - 0 - 0 - 0 - - - 12581 - 126 - 0 - 0 - 0 - - - 12582 - 126 - 0 - 0 - 0 - - - 12583 - 252 - 0 - 0 - 0 - - - 12584 - 252 - 0 - 0 - 0 - - - 12585 - 1 - 0 - 0 - 0 - - - 12586 - 1 - 0 - 0 - 0 - - - 12587 - 1 - 0 - 0 - 0 - - - 12588 - 2 - 0 - 0 - 0 - - - 12589 - 2 - 0 - 0 - 0 - - - 12590 - 2 - 0 - 0 - 0 - - - 12591 - 2 - 0 - 0 - 0 - - - 12592 - 60 - 0 - 0 - 0 - - - 12593 - 1 - 0 - 0 - 0 - - - 12594 - 1 - 0 - 0 - 0 - - - 12595 - 1 - 0 - 0 - 0 - - - 12596 - 1 - 0 - 0 - 0 - - - 12597 - 1 - 0 - 0 - 0 - - - 12598 - 1 - 0 - 0 - 0 - - - 12599 - 1 - 0 - 0 - 0 - - - 12600 - 1 - 0 - 0 - 0 - - - 12601 - 1 - 0 - 0 - 0 - - - 12602 - 1 - 0 - 0 - 0 - - - 12603 - 1 - 0 - 0 - 0 - - - 12604 - 1 - 0 - 0 - 0 - - - 12605 - 1 - 0 - 0 - 0 - - - 12606 - 1 - 0 - 0 - 0 - - - 12607 - 1 - 0 - 0 - 0 - - - 12608 - 200 - 0 - 0 - 0 - - - 12609 - 200 - 0 - 0 - 0 - - - 12610 - 200 - 0 - 0 - 0 - - - 12611 - 200 - 0 - 0 - 0 - - - 12612 - 200 - 0 - 0 - 0 - - - 12613 - 200 - 0 - 0 - 0 - - - 12614 - 200 - 0 - 0 - 0 - - - 12615 - 200 - 0 - 0 - 0 - - - 12616 - 200 - 0 - 0 - 0 - - - 12617 - 200 - 0 - 0 - 0 - - - 12618 - 200 - 0 - 0 - 0 - - - 12619 - 200 - 0 - 0 - 0 - - - 12620 - 200 - 0 - 0 - 0 - - - 12621 - 200 - 0 - 0 - 0 - - - 12622 - 200 - 0 - 0 - 0 - - - 12623 - 200 - 0 - 0 - 0 - - - 12624 - 20 - 0 - 0 - 0 - - - 12625 - 1 - 0 - 0 - 0 - - - 12626 - 2 - 0 - 0 - 0 - - - 12627 - 1 - 0 - 0 - 0 - - - 12628 - 1 - 0 - 0 - 0 - - - 12629 - 1 - 0 - 0 - 0 - - - 12630 - 1 - 0 - 0 - 0 - - - 12631 - 1 - 0 - 0 - 0 - - - 12632 - 1 - 0 - 0 - 0 - - - 12633 - 60 - 0 - 0 - 0 - - - 12634 - 1 - 0 - 0 - 0 - - - 12635 - 1 - 0 - 0 - 0 - - - 12636 - 1 - 0 - 0 - 0 - - - 12637 - 1 - 0 - 0 - 0 - - - 12638 - 1 - 0 - 0 - 0 - - - 12639 - 1 - 0 - 0 - 0 - - - 12640 - 1 - 0 - 0 - 0 - - - 12641 - 1 - 0 - 0 - 0 - - - 12642 - 1 - 0 - 0 - 0 - - - 12643 - 1 - 0 - 0 - 0 - - - 12644 - 1 - 0 - 0 - 0 - - - 12645 - 32 - 0 - 0 - 0 - - - 12646 - 1 - 0 - 0 - 0 - - - 12647 - 1 - 0 - 0 - 0 - - - 12648 - 1 - 0 - 0 - 0 - - - 12649 - 1 - 0 - 0 - 0 - - - 12650 - 1 - 0 - 0 - 0 - - - 12651 - 1 - 0 - 0 - 0 - - - 12652 - 1 - 0 - 0 - 0 - - - 12653 - 1 - 0 - 0 - 0 - - - 12654 - 1 - 0 - 0 - 0 - - - 12655 - 1 - 0 - 0 - 0 - - - 12656 - 1 - 0 - 0 - 0 - - - 12657 - 1 - 0 - 0 - 0 - - - 12658 - 3520 - 0 - 0 - 0 - - - 12659 - 3520 - 0 - 0 - 0 - - - 12660 - 300 - 0 - 0 - 0 - - - 12661 - 300 - 0 - 0 - 0 - - - 12662 - 10000 - 0 - 0 - 0 - - - 12663 - 10000 - 0 - 0 - 0 - - - 12664 - 35200 - 0 - 0 - 0 - - - 12665 - 35200 - 0 - 0 - 0 - - - 12666 - 100000 - 0 - 0 - 0 - - - 12667 - 100000 - 0 - 0 - 0 - - - 12668 - 100 - 0 - 0 - 0 - - - 12669 - 100 - 0 - 0 - 0 - - - 12670 - 20000 - 0 - 0 - 0 - - - 12671 - 20000 - 0 - 0 - 0 - - - 12672 - 78000 - 0 - 0 - 0 - - - 12673 - 78000 - 0 - 0 - 0 - - - 12674 - 78000 - 0 - 0 - 0 - - - 12675 - 78000 - 0 - 0 - 0 - - - 12676 - 78000 - 0 - 0 - 0 - - - 12677 - 78000 - 0 - 0 - 0 - - - 12678 - 78000 - 0 - 0 - 0 - - - 12679 - 78000 - 0 - 0 - 0 - - - 12680 - 50000 - 0 - 0 - 0 - - - 12681 - 50000 - 0 - 0 - 0 - - - 12682 - 1 - 0 - 0 - 0 - - - 12683 - 1 - 0 - 0 - 0 - - - 12684 - 1 - 0 - 0 - 0 - - - 12685 - 1 - 0 - 0 - 0 - - - 12686 - 1 - 0 - 0 - 0 - - - 12687 - 1 - 0 - 0 - 0 - - - 12688 - 1 - 0 - 0 - 0 - - - 12689 - 1 - 0 - 0 - 0 - - - 12690 - 1 - 0 - 0 - 0 - - - 12691 - 1 - 0 - 0 - 0 - - - 12692 - 1 - 0 - 0 - 0 - - - 12693 - 1 - 0 - 0 - 0 - - - 12694 - 1 - 0 - 0 - 0 - - - 12695 - 1 - 0 - 0 - 0 - - - 12696 - 1 - 0 - 0 - 0 - - - 12697 - 1 - 0 - 0 - 0 - - - 12698 - 1 - 0 - 0 - 0 - - - 12699 - 1 - 0 - 0 - 0 - - - 12700 - 1 - 0 - 0 - 0 - - - 12701 - 1 - 0 - 0 - 0 - - - 12702 - 1 - 0 - 0 - 0 - - - 12703 - 1 - 0 - 0 - 0 - - - 12704 - 1 - 0 - 0 - 0 - - - 12705 - 1 - 0 - 0 - 0 - - - 12706 - 1 - 0 - 0 - 0 - - - 12707 - 1 - 0 - 0 - 0 - - - 12708 - 1 - 0 - 0 - 0 - - - 12709 - 1 - 0 - 0 - 0 - - - 12710 - 1 - 0 - 0 - 0 - - - 12711 - 1 - 0 - 0 - 0 - - - 12712 - 1 - 0 - 0 - 0 - - - 12713 - 1 - 0 - 0 - 0 - - - 12714 - 1 - 0 - 0 - 0 - - - 12715 - 1 - 0 - 0 - 0 - - - 12716 - 1 - 0 - 0 - 0 - - - 12717 - 1 - 0 - 0 - 0 - - - 12718 - 1 - 0 - 0 - 0 - - - 12719 - 1 - 0 - 0 - 0 - - - 12720 - 1 - 0 - 0 - 0 - - - 12721 - 1 - 0 - 0 - 0 - - - 12722 - 1 - 0 - 0 - 0 - - - 12723 - 1 - 0 - 0 - 0 - - - 12724 - 1 - 0 - 0 - 0 - - - 12725 - 1 - 0 - 0 - 0 - - - 12726 - 1 - 0 - 0 - 0 - - - 12727 - 1 - 0 - 0 - 0 - - - 12728 - 1 - 0 - 0 - 0 - - - 12729 - 1 - 0 - 0 - 0 - - - 12730 - 1 - 0 - 0 - 0 - - - 12731 - 1 - 0 - 0 - 0 - - - 12732 - 1 - 0 - 0 - 0 - - - 12733 - 1 - 0 - 0 - 0 - - - 12734 - 1 - 0 - 0 - 0 - - - 12735 - 1 - 0 - 0 - 0 - - - 12736 - 1 - 0 - 0 - 0 - - - 12737 - 1 - 0 - 0 - 0 - - - 12738 - 1 - 0 - 0 - 0 - - - 12739 - 1 - 0 - 0 - 0 - - - 12740 - 1 - 0 - 0 - 0 - - - 12741 - 1 - 0 - 0 - 0 - - - 12742 - 1 - 0 - 0 - 0 - - - 12743 - 1 - 0 - 0 - 0 - - - 12744 - 1 - 0 - 0 - 0 - - - 12745 - 1 - 0 - 0 - 0 - - - 12746 - 1 - 0 - 0 - 0 - - - 12747 - 1 - 0 - 0 - 0 - - - 12748 - 1 - 0 - 0 - 0 - - - 12749 - 1 - 0 - 0 - 0 - - - 12750 - 1 - 0 - 0 - 0 - - - 12751 - 1 - 0 - 0 - 0 - - - 12752 - 1 - 0 - 0 - 0 - - - 12753 - 1 - 0 - 0 - 0 - - - 12754 - 1 - 0 - 0 - 0 - - - 12755 - 1 - 0 - 0 - 0 - - - 12756 - 1 - 0 - 0 - 0 - - - 12757 - 1 - 0 - 0 - 0 - - - 12758 - 1 - 0 - 0 - 0 - - - 12759 - 1 - 0 - 0 - 0 - - - 12760 - 1 - 0 - 0 - 0 - - - 12761 - 1 - 0 - 0 - 0 - - - 12762 - 1 - 0 - 0 - 0 - - - 12763 - 1 - 0 - 0 - 0 - - - 12764 - 1 - 0 - 0 - 0 - - - 12765 - 1 - 0 - 0 - 0 - - - 12766 - 1 - 0 - 0 - 0 - - - 12767 - 1 - 0 - 0 - 0 - - - 12768 - 1 - 0 - 0 - 0 - - - 12769 - 1 - 0 - 0 - 0 - - - 12770 - 1 - 0 - 0 - 0 - - - 12771 - 1 - 0 - 0 - 0 - - - 12772 - 1 - 0 - 0 - 0 - - - 12773 - 1 - 0 - 0 - 0 - - - 12774 - 1 - 0 - 0 - 0 - - - 12775 - 1 - 0 - 0 - 0 - - - 12776 - 4174 - 0 - 0 - 0 - - - 12777 - 4174 - 0 - 0 - 0 - - - 12778 - 449 - 0 - 0 - 0 - - - 12779 - 449 - 0 - 0 - 0 - - - 12780 - 2274 - 0 - 0 - 0 - - - 12781 - 2274 - 0 - 0 - 0 - - - 12782 - 3949 - 0 - 0 - 0 - - - 12783 - 3949 - 0 - 0 - 0 - - - 12784 - 4299 - 0 - 0 - 0 - - - 12785 - 4299 - 0 - 0 - 0 - - - 12786 - 5974 - 0 - 0 - 0 - - - 12787 - 5974 - 0 - 0 - 0 - - - 12788 - 5899 - 0 - 0 - 0 - - - 12789 - 5899 - 0 - 0 - 0 - - - 12790 - 4874 - 0 - 0 - 0 - - - 12791 - 4874 - 0 - 0 - 0 - - - 12792 - 5299 - 0 - 0 - 0 - - - 12793 - 5299 - 0 - 0 - 0 - - - 12794 - 4774 - 0 - 0 - 0 - - - 12795 - 4774 - 0 - 0 - 0 - - - 12796 - 3249 - 0 - 0 - 0 - - - 12797 - 3249 - 0 - 0 - 0 - - - 12798 - 2274 - 0 - 0 - 0 - - - 12799 - 2274 - 0 - 0 - 0 - - - 12800 - 2524 - 0 - 0 - 0 - - - 12801 - 2524 - 0 - 0 - 0 - - - 12802 - 5374 - 0 - 0 - 0 - - - 12803 - 5374 - 0 - 0 - 0 - - - 12804 - 5474 - 0 - 0 - 0 - - - 12805 - 5474 - 0 - 0 - 0 - - - 12806 - 5374 - 0 - 0 - 0 - - - 12807 - 5374 - 0 - 0 - 0 - - - 12808 - 2024 - 0 - 0 - 0 - - - 12809 - 2024 - 0 - 0 - 0 - - - 12810 - 4274 - 0 - 0 - 0 - - - 12811 - 4274 - 0 - 0 - 0 - - - 12812 - 4249 - 0 - 0 - 0 - - - 12813 - 4249 - 0 - 0 - 0 - - - 12814 - 2274 - 0 - 0 - 0 - - - 12815 - 2274 - 0 - 0 - 0 - - - 12816 - 3199 - 0 - 0 - 0 - - - 12817 - 3199 - 0 - 0 - 0 - - - 12818 - 2274 - 0 - 0 - 0 - - - 12819 - 2274 - 0 - 0 - 0 - - - 12820 - 2399 - 0 - 0 - 0 - - - 12821 - 2399 - 0 - 0 - 0 - - - 12822 - 5374 - 0 - 0 - 0 - - - 12823 - 5374 - 0 - 0 - 0 - - - 12824 - 537 - 0 - 0 - 0 - - - 12825 - 487 - 0 - 0 - 0 - - - 12826 - 529 - 0 - 0 - 0 - - - 12827 - 324 - 0 - 0 - 0 - - - 12828 - 537 - 0 - 0 - 0 - - - 12829 - 319 - 0 - 0 - 0 - - - 12830 - 239 - 0 - 0 - 0 - - - 12831 - 477 - 0 - 0 - 0 - - - 12832 - 417 - 0 - 0 - 0 - - - 12833 - 597 - 0 - 0 - 0 - - - 12834 - 252 - 0 - 0 - 0 - - - 12835 - 427 - 0 - 0 - 0 - - - 12836 - 424 - 0 - 0 - 0 - - - 12837 - 529 - 0 - 0 - 0 - - - 12838 - 44 - 0 - 0 - 0 - - - 12839 - 202 - 0 - 0 - 0 - - - 12840 - 429 - 0 - 0 - 0 - - - 12841 - 394 - 0 - 0 - 0 - - - 12842 - 1 - 0 - 0 - 0 - - - 12843 - 1 - 0 - 0 - 0 - - - 12844 - 100 - 0 - 0 - 0 - - - 12845 - 1 - 0 - 0 - 0 - - - 12846 - 1 - 0 - 0 - 0 - - - 12847 - 1 - 0 - 0 - 0 - - - 12848 - 1 - 0 - 0 - 0 - - - 12849 - 1 - 0 - 0 - 0 - - - 12850 - 2 - 0 - 0 - 0 - - - 12851 - 2 - 0 - 0 - 0 - - - 12852 - 1 - 0 - 0 - 0 - - - 12853 - 1 - 0 - 0 - 0 - - - 12854 - 1 - 0 - 0 - 0 - - - 12855 - 1 - 0 - 0 - 0 - - - 12856 - 1 - 0 - 0 - 0 - - - 12857 - 1 - 0 - 0 - 0 - - - 12858 - 1 - 0 - 0 - 0 - - - 12859 - 1 - 0 - 0 - 0 - - - 12860 - 1 - 0 - 0 - 0 - - - 12861 - 1 - 0 - 0 - 0 - - - 12862 - 1 - 0 - 0 - 0 - - - 12863 - 1 - 0 - 0 - 0 - - - 12864 - 1 - 0 - 0 - 0 - - - 12865 - 1 - 0 - 0 - 0 - - - 12866 - 25000 - 0 - 0 - 0 - - - 12867 - 25000 - 0 - 0 - 0 - - - 12868 - 25000 - 0 - 0 - 0 - - - 12869 - 25000 - 0 - 0 - 0 - - - 12870 - 25000 - 0 - 0 - 0 - - - 12871 - 25000 - 0 - 0 - 0 - - - 12872 - 25000 - 0 - 0 - 0 - - - 12873 - 50000 - 0 - 0 - 0 - - - 12874 - 50000 - 0 - 0 - 0 - - - 12875 - 50000 - 0 - 0 - 0 - - - 12876 - 50000 - 0 - 0 - 0 - - - 12877 - 50000 - 0 - 0 - 0 - - - 12878 - 50000 - 0 - 0 - 0 - - - 12879 - 50000 - 0 - 0 - 0 - - - 12880 - 40000 - 0 - 0 - 0 - - - 12881 - 40000 - 0 - 0 - 0 - - - 12882 - 40000 - 0 - 0 - 0 - - - 12883 - 40000 - 0 - 0 - 0 - - - 12884 - 40000 - 0 - 0 - 0 - - - 12885 - 40000 - 0 - 0 - 0 - - - 12886 - 40000 - 0 - 0 - 0 - - - 12887 - 2000 - 0 - 0 - 0 - - - 12888 - 2000 - 0 - 0 - 0 - - - 12889 - 2000 - 0 - 0 - 0 - - - 12890 - 2000 - 0 - 0 - 0 - - - 12891 - 2000 - 0 - 0 - 0 - - - 12892 - 2000 - 0 - 0 - 0 - - - 12893 - 2000 - 0 - 0 - 0 - - - 12894 - 6000 - 0 - 0 - 0 - - - 12895 - 6000 - 0 - 0 - 0 - - - 12896 - 6000 - 0 - 0 - 0 - - - 12897 - 6000 - 0 - 0 - 0 - - - 12898 - 6000 - 0 - 0 - 0 - - - 12899 - 6000 - 0 - 0 - 0 - - - 12900 - 6000 - 0 - 0 - 0 - - - 12901 - 5000 - 0 - 0 - 0 - - - 12902 - 5000 - 0 - 0 - 0 - - - 12903 - 5000 - 0 - 0 - 0 - - - 12904 - 5000 - 0 - 0 - 0 - - - 12905 - 5000 - 0 - 0 - 0 - - - 12906 - 5000 - 0 - 0 - 0 - - - 12907 - 5000 - 0 - 0 - 0 - - - 12908 - 4050 - 0 - 0 - 0 - - - 12909 - 4050 - 0 - 0 - 0 - - - 12910 - 4050 - 0 - 0 - 0 - - - 12911 - 4050 - 0 - 0 - 0 - - - 12912 - 4050 - 0 - 0 - 0 - - - 12913 - 4050 - 0 - 0 - 0 - - - 12914 - 4050 - 0 - 0 - 0 - - - 12915 - 6140 - 0 - 0 - 0 - - - 12916 - 6140 - 0 - 0 - 0 - - - 12917 - 6140 - 0 - 0 - 0 - - - 12918 - 6140 - 0 - 0 - 0 - - - 12919 - 6140 - 0 - 0 - 0 - - - 12920 - 6140 - 0 - 0 - 0 - - - 12921 - 6140 - 0 - 0 - 0 - - - 12922 - 40000 - 0 - 0 - 0 - - - 12923 - 40000 - 0 - 0 - 0 - - - 12924 - 40000 - 0 - 0 - 0 - - - 12925 - 40000 - 0 - 0 - 0 - - - 12926 - 40000 - 0 - 0 - 0 - - - 12927 - 40000 - 0 - 0 - 0 - - - 12928 - 40000 - 0 - 0 - 0 - - - 12929 - 64400 - 0 - 0 - 0 - - - 12930 - 64400 - 0 - 0 - 0 - - - 12931 - 64400 - 0 - 0 - 0 - - - 12932 - 64400 - 0 - 0 - 0 - - - 12933 - 64400 - 0 - 0 - 0 - - - 12934 - 64400 - 0 - 0 - 0 - - - 12935 - 64400 - 0 - 0 - 0 - - - 12936 - 2000 - 0 - 0 - 0 - - - 12937 - 2000 - 0 - 0 - 0 - - - 12938 - 2000 - 0 - 0 - 0 - - - 12939 - 2000 - 0 - 0 - 0 - - - 12940 - 2000 - 0 - 0 - 0 - - - 12941 - 2000 - 0 - 0 - 0 - - - 12942 - 2000 - 0 - 0 - 0 - - - 12943 - 3500 - 0 - 0 - 0 - - - 12944 - 3500 - 0 - 0 - 0 - - - 12945 - 3500 - 0 - 0 - 0 - - - 12946 - 3500 - 0 - 0 - 0 - - - 12947 - 3500 - 0 - 0 - 0 - - - 12948 - 3500 - 0 - 0 - 0 - - - 12949 - 3500 - 0 - 0 - 0 - - - 12950 - 3500 - 0 - 0 - 0 - - - 12951 - 3500 - 0 - 0 - 0 - - - 12952 - 3500 - 0 - 0 - 0 - - - 12953 - 3500 - 0 - 0 - 0 - - - 12954 - 3500 - 0 - 0 - 0 - - - 12955 - 3500 - 0 - 0 - 0 - - - 12956 - 3500 - 0 - 0 - 0 - - - 12957 - 5500 - 0 - 0 - 0 - - - 12958 - 5500 - 0 - 0 - 0 - - - 12959 - 5500 - 0 - 0 - 0 - - - 12960 - 5500 - 0 - 0 - 0 - - - 12961 - 5500 - 0 - 0 - 0 - - - 12962 - 5500 - 0 - 0 - 0 - - - 12963 - 5500 - 0 - 0 - 0 - - - 12964 - 1000 - 0 - 0 - 0 - - - 12965 - 1000 - 0 - 0 - 0 - - - 12966 - 1000 - 0 - 0 - 0 - - - 12967 - 1000 - 0 - 0 - 0 - - - 12968 - 1000 - 0 - 0 - 0 - - - 12969 - 1000 - 0 - 0 - 0 - - - 12970 - 1000 - 0 - 0 - 0 - - - 12971 - 3000 - 0 - 0 - 0 - - - 12972 - 3000 - 0 - 0 - 0 - - - 12973 - 3000 - 0 - 0 - 0 - - - 12974 - 3000 - 0 - 0 - 0 - - - 12975 - 3000 - 0 - 0 - 0 - - - 12976 - 3000 - 0 - 0 - 0 - - - 12977 - 3000 - 0 - 0 - 0 - - - 12978 - 2500 - 0 - 0 - 0 - - - 12979 - 2500 - 0 - 0 - 0 - - - 12980 - 2500 - 0 - 0 - 0 - - - 12981 - 2500 - 0 - 0 - 0 - - - 12982 - 2500 - 0 - 0 - 0 - - - 12983 - 2500 - 0 - 0 - 0 - - - 12984 - 2500 - 0 - 0 - 0 - - - 12985 - 100 - 0 - 0 - 0 - - - 12986 - 100 - 0 - 0 - 0 - - - 12987 - 100 - 0 - 0 - 0 - - - 12988 - 300 - 0 - 0 - 0 - - - 12989 - 300 - 0 - 0 - 0 - - - 12990 - 300 - 0 - 0 - 0 - - - 12991 - 1000 - 0 - 0 - 0 - - - 12992 - 1000 - 0 - 0 - 0 - - - 12993 - 1000 - 0 - 0 - 0 - - - 12994 - 2000 - 0 - 0 - 0 - - - 12995 - 2000 - 0 - 0 - 0 - - - 12996 - 2000 - 0 - 0 - 0 - - - 12997 - 3000 - 0 - 0 - 0 - - - 12998 - 3000 - 0 - 0 - 0 - - - 12999 - 3000 - 0 - 0 - 0 - - - 13000 - 5000 - 0 - 0 - 0 - - - 13001 - 5000 - 0 - 0 - 0 - - - 13002 - 5000 - 0 - 0 - 0 - - - 13003 - 7500 - 0 - 0 - 0 - - - 13004 - 7500 - 0 - 0 - 0 - - - 13005 - 7500 - 0 - 0 - 0 - - - 13006 - 50000 - 0 - 0 - 0 - - - 13007 - 50000 - 0 - 0 - 0 - - - 13008 - 50000 - 0 - 0 - 0 - - - 13009 - 1 - 0 - 0 - 0 - - - 13010 - 1 - 0 - 0 - 0 - - - 13011 - 100 - 0 - 0 - 0 - - - 13012 - 1 - 0 - 0 - 0 - - - 13013 - 100 - 0 - 0 - 0 - - - 13014 - 1 - 0 - 0 - 0 - - - 13015 - 100 - 0 - 0 - 0 - - - 13016 - 1 - 0 - 0 - 0 - - - 13017 - 100 - 0 - 0 - 0 - - - 13018 - 1 - 0 - 0 - 0 - - - 13019 - 100 - 0 - 0 - 0 - - - 13020 - 1 - 0 - 0 - 0 - - - 13021 - 100 - 0 - 0 - 0 - - - 13022 - 1 - 0 - 0 - 0 - - - 13023 - 100 - 0 - 0 - 0 - - - 13024 - 1 - 0 - 0 - 0 - - - 13025 - 100 - 0 - 0 - 0 - - - 13026 - 1 - 0 - 0 - 0 - - - 13027 - 100 - 0 - 0 - 0 - - - 13028 - 1 - 0 - 0 - 0 - - - 13029 - 100 - 0 - 0 - 0 - - - 13030 - 1 - 0 - 0 - 0 - - - 13031 - 100 - 0 - 0 - 0 - - - 13032 - 1 - 0 - 0 - 0 - - - 13033 - 100 - 0 - 0 - 0 - - - 13034 - 1 - 0 - 0 - 0 - - - 13035 - 100 - 0 - 0 - 0 - - - 13036 - 1 - 0 - 0 - 0 - - - 13037 - 50 - 0 - 0 - 0 - - - 13038 - 1 - 0 - 0 - 0 - - - 13039 - 50 - 0 - 0 - 0 - - - 13040 - 1 - 0 - 0 - 0 - - - 13041 - 1 - 0 - 0 - 0 - - - 13042 - 1 - 0 - 0 - 0 - - - 13043 - 50 - 0 - 0 - 0 - - - 13044 - 1 - 0 - 0 - 0 - - - 13045 - 50 - 0 - 0 - 0 - - - 13046 - 1 - 0 - 0 - 0 - - - 13047 - 50 - 0 - 0 - 0 - - - 13048 - 1 - 0 - 0 - 0 - - - 13049 - 1 - 0 - 0 - 0 - - - 13050 - 1 - 0 - 0 - 0 - - - 13051 - 1 - 0 - 0 - 0 - - - 13052 - 50 - 0 - 0 - 0 - - - 13053 - 1 - 0 - 0 - 0 - - - 13054 - 50 - 0 - 0 - 0 - - - 13055 - 1 - 0 - 0 - 0 - - - 13056 - 1 - 0 - 0 - 0 - - - 13057 - 50 - 0 - 0 - 0 - - - 13058 - 1 - 0 - 0 - 0 - - - 13059 - 50 - 0 - 0 - 0 - - - 13060 - 1 - 0 - 0 - 0 - - - 13061 - 1 - 0 - 0 - 0 - - - 13062 - 50 - 0 - 0 - 0 - - - 13063 - 1 - 0 - 0 - 0 - - - 13064 - 50 - 0 - 0 - 0 - - - 13065 - 1 - 0 - 0 - 0 - - - 13066 - 50 - 0 - 0 - 0 - - - 13067 - 1 - 0 - 0 - 0 - - - 13068 - 1 - 0 - 0 - 0 - - - 13069 - 1 - 0 - 0 - 0 - - - 13070 - 1 - 0 - 0 - 0 - - - 13071 - 1 - 0 - 0 - 0 - - - 13072 - 1 - 0 - 0 - 0 - - - 13073 - 1 - 0 - 0 - 0 - - - 13074 - 1 - 0 - 0 - 0 - - - 13075 - 1 - 0 - 0 - 0 - - - 13076 - 1 - 0 - 0 - 0 - - - 13077 - 50 - 0 - 0 - 0 - - - 13078 - 1 - 0 - 0 - 0 - - - 13079 - 1 - 0 - 0 - 0 - - - 13080 - 1 - 0 - 0 - 0 - - - 13081 - 800 - 0 - 0 - 0 - - - 13082 - 800 - 0 - 0 - 0 - - - 13083 - 14 - 0 - 0 - 0 - - - 13084 - 14 - 0 - 0 - 0 - - - 13085 - 14 - 0 - 0 - 0 - - - 13086 - 14 - 0 - 0 - 0 - - - 13087 - 1 - 0 - 0 - 0 - - - 13088 - 1 - 0 - 0 - 0 - - - 13089 - 1 - 0 - 0 - 0 - - - 13090 - 1 - 0 - 0 - 0 - - - 13091 - 1 - 0 - 0 - 0 - - - 13092 - 1 - 0 - 0 - 0 - - - 13093 - 1 - 0 - 0 - 0 - - - 13094 - 1 - 0 - 0 - 0 - - - 13095 - 432 - 0 - 0 - 0 - - - 13096 - 432 - 0 - 0 - 0 - - - 13097 - 1440 - 0 - 0 - 0 - - - 13098 - 1440 - 0 - 0 - 0 - - - 13099 - 14400 - 0 - 0 - 0 - - - 13100 - 14400 - 0 - 0 - 0 - - - 13101 - 200 - 0 - 0 - 0 - - - 13102 - 200 - 0 - 0 - 0 - - - 13103 - 250 - 0 - 0 - 0 - - - 13104 - 250 - 0 - 0 - 0 - - - 13105 - 1000 - 0 - 0 - 0 - - - 13106 - 1000 - 0 - 0 - 0 - - - 13107 - 800 - 0 - 0 - 0 - - - 13108 - 800 - 0 - 0 - 0 - - - 13109 - 800 - 0 - 0 - 0 - - - 13110 - 800 - 0 - 0 - 0 - - - 13111 - 800 - 0 - 0 - 0 - - - 13112 - 800 - 0 - 0 - 0 - - - 13113 - 800 - 0 - 0 - 0 - - - 13114 - 800 - 0 - 0 - 0 - - - 13115 - 800 - 0 - 0 - 0 - - - 13116 - 800 - 0 - 0 - 0 - - - 13117 - 1000 - 0 - 0 - 0 - - - 13118 - 1000 - 0 - 0 - 0 - - - 13119 - 1000 - 0 - 0 - 0 - - - 13120 - 1000 - 0 - 0 - 0 - - - 13121 - 1000 - 0 - 0 - 0 - - - 13122 - 1000 - 0 - 0 - 0 - - - 13123 - 1000 - 0 - 0 - 0 - - - 13124 - 1000 - 0 - 0 - 0 - - - 13125 - 1000 - 0 - 0 - 0 - - - 13126 - 1000 - 0 - 0 - 0 - - - 13127 - 1000 - 0 - 0 - 0 - - - 13128 - 1000 - 0 - 0 - 0 - - - 13129 - 1000 - 0 - 0 - 0 - - - 13130 - 1000 - 0 - 0 - 0 - - - 13131 - 1000 - 0 - 0 - 0 - - - 13132 - 1000 - 0 - 0 - 0 - - - 13133 - 1000 - 0 - 0 - 0 - - - 13134 - 1000 - 0 - 0 - 0 - - - 13135 - 1000 - 0 - 0 - 0 - - - 13136 - 1000 - 0 - 0 - 0 - - - 13137 - 1000 - 0 - 0 - 0 - - - 13138 - 1000 - 0 - 0 - 0 - - - 13139 - 1000 - 0 - 0 - 0 - - - 13140 - 1000 - 0 - 0 - 0 - - - 13141 - 1000 - 0 - 0 - 0 - - - 13142 - 1000 - 0 - 0 - 0 - - - 13143 - 1000 - 0 - 0 - 0 - - - 13144 - 1000 - 0 - 0 - 0 - - - 13145 - 1000 - 0 - 0 - 0 - - - 13146 - 1000 - 0 - 0 - 0 - - - 13147 - 1 - 0 - 0 - 0 - - - 13148 - 3 - 0 - 0 - 0 - - - 13149 - 3 - 0 - 0 - 0 - - - 13150 - 1 - 0 - 0 - 0 - - - 13151 - 3 - 0 - 0 - 0 - - - 13152 - 3 - 0 - 0 - 0 - - - 13153 - 3 - 0 - 0 - 0 - - - 13154 - 3 - 0 - 0 - 0 - - - 13155 - 3 - 0 - 0 - 0 - - - 13156 - 3 - 0 - 0 - 0 - - - 13157 - 3 - 0 - 0 - 0 - - - 13158 - 3 - 0 - 0 - 0 - - - 13159 - 3 - 0 - 0 - 0 - - - 13160 - 1 - 0 - 0 - 0 - - - 13161 - 1 - 0 - 0 - 0 - - - 13162 - 1 - 0 - 0 - 0 - - - 13163 - 432 - 0 - 0 - 0 - - - 13164 - 1440 - 0 - 0 - 0 - - - 13165 - 14400 - 0 - 0 - 0 - - - 13166 - 200 - 0 - 0 - 0 - - - 13167 - 250 - 0 - 0 - 0 - - - 13168 - 1000 - 0 - 0 - 0 - - - 13169 - 800 - 0 - 0 - 0 - - - 13170 - 800 - 0 - 0 - 0 - - - 13171 - 800 - 0 - 0 - 0 - - - 13172 - 800 - 0 - 0 - 0 - - - 13173 - 800 - 0 - 0 - 0 - - - 13174 - 1 - 0 - 0 - 0 - - - 13175 - 1 - 0 - 0 - 0 - - - 13176 - 1 - 0 - 0 - 0 - - - 13177 - 1 - 0 - 0 - 0 - - - 13178 - 1 - 0 - 0 - 0 - - - 13179 - 1 - 0 - 0 - 0 - - - 13180 - 1 - 0 - 0 - 0 - - - 13181 - 1 - 0 - 0 - 0 - - - 13182 - 1 - 0 - 0 - 0 - - - 13183 - 1 - 0 - 0 - 0 - - - 13184 - 1 - 0 - 0 - 0 - - - 13185 - 1 - 0 - 0 - 0 - - - 13186 - 1 - 0 - 0 - 0 - - - 13187 - 1 - 0 - 0 - 0 - - - 13188 - 1 - 0 - 0 - 0 - - - 13189 - 1 - 0 - 0 - 0 - - - 13190 - 1 - 0 - 0 - 0 - - - 13191 - 1 - 0 - 0 - 0 - - - 13192 - 1 - 0 - 0 - 0 - - - 13193 - 1 - 0 - 0 - 0 - - - 13194 - 1 - 0 - 0 - 0 - - - 13195 - 1 - 0 - 0 - 0 - - - 13196 - 1 - 0 - 0 - 0 - - - 13197 - 1 - 0 - 0 - 0 - - - 13198 - 1 - 0 - 0 - 0 - - - 13199 - 1 - 0 - 0 - 0 - - - 13200 - 1 - 0 - 0 - 0 - - - 13201 - 1 - 0 - 0 - 0 - - - 13202 - 1 - 0 - 0 - 0 - - - 13203 - 1 - 0 - 0 - 0 - - - 13204 - 1 - 0 - 0 - 0 - - - 13205 - 1 - 0 - 0 - 0 - - - 13206 - 1 - 0 - 0 - 0 - - - 13207 - 1 - 0 - 0 - 0 - - - 13208 - 1 - 0 - 0 - 0 - - - 13209 - 1 - 0 - 0 - 0 - - - 13210 - 1 - 0 - 0 - 0 - - - 13211 - 1 - 0 - 0 - 0 - - - 13212 - 1 - 0 - 0 - 0 - - - 13213 - 1 - 0 - 0 - 0 - - - 13214 - 1 - 0 - 0 - 0 - - - 13215 - 1 - 0 - 0 - 0 - - - 13216 - 1 - 0 - 0 - 0 - - - 13217 - 1 - 0 - 0 - 0 - - - 13218 - 1 - 0 - 0 - 0 - - - 13219 - 1 - 0 - 0 - 0 - - - 13220 - 1 - 0 - 0 - 0 - - - 13221 - 1 - 0 - 0 - 0 - - - 13222 - 1 - 0 - 0 - 0 - - - 13223 - 1 - 0 - 0 - 0 - - - 13224 - 1 - 0 - 0 - 0 - - - 13225 - 1 - 0 - 0 - 0 - - - 13226 - 1 - 0 - 0 - 0 - - - 13227 - 1 - 0 - 0 - 0 - - - 13228 - 1 - 0 - 0 - 0 - - - 13229 - 1 - 0 - 0 - 0 - - - 13230 - 1 - 0 - 0 - 0 - - - 13231 - 1 - 0 - 0 - 0 - - - 13232 - 1 - 0 - 0 - 0 - - - 13233 - 1 - 0 - 0 - 0 - - - 13234 - 1 - 0 - 0 - 0 - - - 13235 - 1 - 0 - 0 - 0 - - - 13236 - 3 - 0 - 0 - 0 - - - 13237 - 1 - 0 - 0 - 0 - - - 13238 - 1 - 0 - 0 - 0 - - - 13239 - 1 - 0 - 0 - 0 - - - 13240 - 1 - 0 - 0 - 0 - - - 13241 - 1 - 0 - 0 - 0 - - - 13242 - 1 - 0 - 0 - 0 - - - 13243 - 1 - 0 - 0 - 0 - - - 13244 - 1 - 0 - 0 - 0 - - - 13245 - 1 - 0 - 0 - 0 - - - 13246 - 1 - 0 - 0 - 0 - - - 13247 - 1 - 0 - 0 - 0 - - - 13248 - 1 - 0 - 0 - 0 - - - 13249 - 1 - 0 - 0 - 0 - - - 13250 - 1 - 0 - 0 - 0 - - - 13251 - 1 - 0 - 0 - 0 - - - 13252 - 1 - 0 - 0 - 0 - - - 13253 - 1 - 0 - 0 - 0 - - - 13254 - 1 - 0 - 0 - 0 - - - 13255 - 1 - 0 - 0 - 0 - - - 13256 - 1 - 0 - 0 - 0 - - - 13257 - 1 - 0 - 0 - 0 - - - 13258 - 1 - 0 - 0 - 0 - - - 13259 - 1 - 0 - 0 - 0 - - - 13260 - 1 - 0 - 0 - 0 - - - 13261 - 1 - 0 - 0 - 0 - - - 13262 - 1 - 0 - 0 - 0 - - - 13263 - 650 - 0 - 0 - 0 - - - 13264 - 1 - 0 - 0 - 0 - - - 13265 - 1 - 0 - 0 - 0 - - - 13266 - 1 - 0 - 0 - 0 - - - 13267 - 1 - 0 - 0 - 0 - - - 13268 - 3 - 0 - 0 - 0 - - - 13269 - 3 - 0 - 0 - 0 - - - 13270 - 3 - 0 - 0 - 0 - - - 13271 - 3 - 0 - 0 - 0 - - - 13272 - 3 - 0 - 0 - 0 - - - 13273 - 3 - 0 - 0 - 0 - - - 13274 - 3 - 0 - 0 - 0 - - - 13275 - 3 - 0 - 0 - 0 - - - 13276 - 1 - 0 - 0 - 0 - - - 13277 - 400 - 0 - 0 - 0 - - - 13278 - 50 - 0 - 0 - 0 - - - 13279 - 50 - 0 - 0 - 0 - - - 13280 - 50 - 0 - 0 - 0 - - - 13281 - 150 - 0 - 0 - 0 - - - 13282 - 150 - 0 - 0 - 0 - - - 13283 - 150 - 0 - 0 - 0 - - - 13284 - 150 - 0 - 0 - 0 - - - 13285 - 150 - 0 - 0 - 0 - - - 13286 - 150 - 0 - 0 - 0 - - - 13287 - 150 - 0 - 0 - 0 - - - 13288 - 150 - 0 - 0 - 0 - - - 13289 - 3 - 0 - 0 - 0 - - - 13290 - 68000 - 0 - 0 - 0 - - - 13291 - 68000 - 0 - 0 - 0 - - - 13292 - 1 - 0 - 0 - 0 - - - 13293 - 1 - 0 - 0 - 0 - - - 13294 - 1 - 0 - 0 - 0 - - - 13295 - 1 - 0 - 0 - 0 - - - 13296 - 1 - 0 - 0 - 0 - - - 13297 - 1 - 0 - 0 - 0 - - - 13298 - 1 - 0 - 0 - 0 - - - 13299 - 1 - 0 - 0 - 0 - - - 13300 - 1 - 0 - 0 - 0 - - - 13301 - 1 - 0 - 0 - 0 - - - 13302 - 1 - 0 - 0 - 0 - - - 13303 - 1 - 0 - 0 - 0 - - - 13304 - 1 - 0 - 0 - 0 - - - 13305 - 1 - 0 - 0 - 0 - - - 13306 - 1 - 0 - 0 - 0 - - - 13307 - 1 - 0 - 0 - 0 - - - 13308 - 1 - 0 - 0 - 0 - - - 13309 - 1 - 0 - 0 - 0 - - - 13310 - 1 - 0 - 0 - 0 - - - 13311 - 1 - 0 - 0 - 0 - - - 13312 - 1 - 0 - 0 - 0 - - - 13313 - 1 - 0 - 0 - 0 - - - 13314 - 1 - 0 - 0 - 0 - - - 13315 - 1 - 0 - 0 - 0 - - - 13316 - 1 - 0 - 0 - 0 - - - 13317 - 1 - 0 - 0 - 0 - - - 13318 - 1 - 0 - 0 - 0 - - - 13319 - 1 - 0 - 0 - 0 - - - 13320 - 1 - 0 - 0 - 0 - - - 13321 - 1 - 0 - 0 - 0 - - - 13322 - 1 - 0 - 0 - 0 - - - 13323 - 1 - 0 - 0 - 0 - - - 13324 - 1 - 0 - 0 - 0 - - - 13325 - 1 - 0 - 0 - 0 - - - 13326 - 1 - 0 - 0 - 0 - - - 13327 - 1 - 0 - 0 - 0 - - - 13328 - 1 - 0 - 0 - 0 - - - 13329 - 1 - 0 - 0 - 0 - - - 13330 - 1 - 0 - 0 - 0 - - - 13331 - 1 - 0 - 0 - 0 - - - 13332 - 1 - 0 - 0 - 0 - - - 13333 - 1 - 0 - 0 - 0 - - - 13334 - 1 - 0 - 0 - 0 - - - 13335 - 1 - 0 - 0 - 0 - - - 13336 - 1 - 0 - 0 - 0 - - - 13337 - 50 - 0 - 0 - 0 - - - 13338 - 50 - 0 - 0 - 0 - - - 13339 - 100 - 0 - 0 - 0 - - - 13340 - 100 - 0 - 0 - 0 - - - 13341 - 100 - 0 - 0 - 0 - - - 13342 - 100 - 0 - 0 - 0 - - - 13343 - 100 - 0 - 0 - 0 - - - 13344 - 100 - 0 - 0 - 0 - - - 13345 - 100 - 0 - 0 - 0 - - - 13346 - 100 - 0 - 0 - 0 - - - 13347 - 100 - 0 - 0 - 0 - - - 13348 - 100 - 0 - 0 - 0 - - - 13349 - 100 - 0 - 0 - 0 - - - 13350 - 100 - 0 - 0 - 0 - - - 13351 - 100 - 0 - 0 - 0 - - - 13352 - 100 - 0 - 0 - 0 - - - 13353 - 100 - 0 - 0 - 0 - - - 13354 - 100 - 0 - 0 - 0 - - - 13355 - 100 - 0 - 0 - 0 - - - 13356 - 100 - 0 - 0 - 0 - - - 13357 - 100 - 0 - 0 - 0 - - - 13358 - 300 - 0 - 0 - 0 - - - 13359 - 300 - 0 - 0 - 0 - - - 13360 - 300 - 0 - 0 - 0 - - - 13361 - 300 - 0 - 0 - 0 - - - 13362 - 300 - 0 - 0 - 0 - - - 13363 - 300 - 0 - 0 - 0 - - - 13364 - 350 - 0 - 0 - 0 - - - 13365 - 350 - 0 - 0 - 0 - - - 13366 - 350 - 0 - 0 - 0 - - - 13367 - 350 - 0 - 0 - 0 - - - 13368 - 350 - 0 - 0 - 0 - - - 13369 - 350 - 0 - 0 - 0 - - - 13370 - 100 - 0 - 0 - 0 - - - 13371 - 100 - 0 - 0 - 0 - - - 13372 - 100 - 0 - 0 - 0 - - - 13373 - 100 - 0 - 0 - 0 - - - 13374 - 100 - 0 - 0 - 0 - - - 13375 - 100 - 0 - 0 - 0 - - - 13376 - 100 - 0 - 0 - 0 - - - 13377 - 100 - 0 - 0 - 0 - - - 13378 - 100 - 0 - 0 - 0 - - - 13379 - 1 - 0 - 0 - 0 - - - 13380 - 1 - 0 - 0 - 0 - - - 13381 - 1 - 0 - 0 - 0 - - - 13382 - 1 - 0 - 0 - 0 - - - 13383 - 1 - 0 - 0 - 0 - - - 13384 - 1 - 0 - 0 - 0 - - - 13385 - 1 - 0 - 0 - 0 - - - 13386 - 1 - 0 - 0 - 0 - - - 13387 - 1 - 0 - 0 - 0 - - - 13388 - 1 - 0 - 0 - 0 - - - 13389 - 1 - 0 - 0 - 0 - - - 13390 - 1 - 0 - 0 - 0 - - - 13391 - 1 - 0 - 0 - 0 - - - 13392 - 1 - 0 - 0 - 0 - - - 13393 - 1 - 0 - 0 - 0 - - - 13394 - 1 - 0 - 0 - 0 - - - 13395 - 1 - 0 - 0 - 0 - - - 13396 - 1 - 0 - 0 - 0 - - - 13397 - 1 - 0 - 0 - 0 - - - 13398 - 1 - 0 - 0 - 0 - - - 13399 - 1 - 0 - 0 - 0 - - - 13400 - 1 - 0 - 0 - 0 - - - 13401 - 1 - 0 - 0 - 0 - - - 13402 - 1 - 0 - 0 - 0 - - - 13403 - 300 - 0 - 0 - 0 - - - 13404 - 1 - 0 - 0 - 0 - - - 13405 - 1 - 0 - 0 - 0 - - - 13406 - 1 - 0 - 0 - 0 - - - 13407 - 1 - 0 - 0 - 0 - - - 13408 - 1 - 0 - 0 - 0 - - - 13409 - 1 - 0 - 0 - 0 - - - 13410 - 1 - 0 - 0 - 0 - - - 13411 - 1 - 0 - 0 - 0 - - - 13412 - 1 - 0 - 0 - 0 - - - 13413 - 1 - 0 - 0 - 0 - - - 13414 - 1 - 0 - 0 - 0 - - - 13415 - 1 - 0 - 0 - 0 - - - 13416 - 1 - 0 - 0 - 0 - - - 13417 - 1 - 0 - 0 - 0 - - - 13418 - 1 - 0 - 0 - 0 - - - 13419 - 1 - 0 - 0 - 0 - - - 13420 - 1 - 0 - 0 - 0 - - - 13421 - 1 - 0 - 0 - 0 - - - 13422 - 1 - 0 - 0 - 0 - - - 13423 - 1 - 0 - 0 - 0 - - - 13424 - 1 - 0 - 0 - 0 - - - 13425 - 1 - 0 - 0 - 0 - - - 13426 - 1 - 0 - 0 - 0 - - - 13427 - 1 - 0 - 0 - 0 - - - 13428 - 1 - 0 - 0 - 0 - - - 13429 - 1 - 0 - 0 - 0 - - - 13430 - 1 - 0 - 0 - 0 - - - 13431 - 20 - 0 - 0 - 0 - - - 13432 - 20 - 0 - 0 - 0 - - - 13433 - 5 - 0 - 0 - 0 - - - 13434 - 5 - 0 - 0 - 0 - - - 13435 - 5 - 0 - 0 - 0 - - - 13436 - 5 - 0 - 0 - 0 - - - 13437 - 1 - 0 - 0 - 0 - - - 13438 - 1 - 0 - 0 - 0 - - - 13439 - 1 - 0 - 0 - 0 - - - 13440 - 1 - 0 - 0 - 0 - - - 13441 - 1 - 0 - 0 - 0 - - - 13442 - 1 - 0 - 0 - 0 - - - 13443 - 1 - 0 - 0 - 0 - - - 13444 - 1 - 0 - 0 - 0 - - - 13445 - 1 - 0 - 0 - 0 - - - 13446 - 1 - 0 - 0 - 0 - - - 13447 - 1 - 0 - 0 - 0 - - - 13448 - 1 - 0 - 0 - 0 - - - 13449 - 1 - 0 - 0 - 0 - - - 13450 - 1 - 0 - 0 - 0 - - - 13451 - 1 - 0 - 0 - 0 - - - 13452 - 1 - 0 - 0 - 0 - - - 13453 - 1 - 0 - 0 - 0 - - - 13454 - 1 - 0 - 0 - 0 - - - 13455 - 1 - 0 - 0 - 0 - - - 13456 - 1 - 0 - 0 - 0 - - - 13457 - 1 - 0 - 0 - 0 - - - 13458 - 1 - 0 - 0 - 0 - - - 13459 - 1 - 0 - 0 - 0 - - - 13460 - 1 - 0 - 0 - 0 - - - 13461 - 1 - 0 - 0 - 0 - - - 13462 - 1 - 0 - 0 - 0 - - - 13463 - 1 - 0 - 0 - 0 - - - 13464 - 1 - 0 - 0 - 0 - - - 13465 - 1 - 0 - 0 - 0 - - - 13466 - 1 - 0 - 0 - 0 - - - 13467 - 1 - 0 - 0 - 0 - - - 13468 - 1 - 0 - 0 - 0 - - - 13469 - 1 - 0 - 0 - 0 - - - 13470 - 1 - 0 - 0 - 0 - - - 13471 - 1 - 0 - 0 - 0 - - - 13472 - 1 - 0 - 0 - 0 - - - 13473 - 1 - 0 - 0 - 0 - - - 13474 - 1 - 0 - 0 - 0 - - - 13475 - 1 - 0 - 0 - 0 - - - 13476 - 1 - 0 - 0 - 0 - - - 13477 - 1 - 0 - 0 - 0 - - - 13478 - 1 - 0 - 0 - 0 - - - 13479 - 1 - 0 - 0 - 0 - - - 13480 - 1 - 0 - 0 - 0 - - - 13481 - 1 - 0 - 0 - 0 - - - 13482 - 1 - 0 - 0 - 0 - - - 13483 - 1 - 0 - 0 - 0 - - - 13484 - 1 - 0 - 0 - 0 - - - 13485 - 1 - 0 - 0 - 0 - - - 13486 - 1 - 0 - 0 - 0 - - - 13487 - 1 - 0 - 0 - 0 - - - 13488 - 1 - 0 - 0 - 0 - - - 13489 - 1 - 0 - 0 - 0 - - - 13490 - 1 - 0 - 0 - 0 - - - 13491 - 1 - 0 - 0 - 0 - - - 13492 - 1 - 0 - 0 - 0 - - - 13493 - 1 - 0 - 0 - 0 - - - 13494 - 1 - 0 - 0 - 0 - - - 13495 - 1 - 0 - 0 - 0 - - - 13496 - 1 - 0 - 0 - 0 - - - 13497 - 1 - 0 - 0 - 0 - - - 13498 - 1 - 0 - 0 - 0 - - - 13499 - 1 - 0 - 0 - 0 - - - 13500 - 1 - 0 - 0 - 0 - - - 13501 - 1 - 0 - 0 - 0 - - - 13502 - 1 - 0 - 0 - 0 - - - 13503 - 1 - 0 - 0 - 0 - - - 13504 - 1 - 0 - 0 - 0 - - - 13505 - 1 - 0 - 0 - 0 - - - 13506 - 1 - 0 - 0 - 0 - - - 13507 - 1 - 0 - 0 - 0 - - - 13508 - 1 - 0 - 0 - 0 - - - 13509 - 1 - 0 - 0 - 0 - - - 13510 - 1 - 0 - 0 - 0 - - - 13511 - 1 - 0 - 0 - 0 - - - 13512 - 1 - 0 - 0 - 0 - - - 13513 - 1 - 0 - 0 - 0 - - - 13514 - 1 - 0 - 0 - 0 - - - 13515 - 1 - 0 - 0 - 0 - - - 13516 - 1 - 0 - 0 - 0 - - - 13517 - 1 - 0 - 0 - 0 - - - 13518 - 1 - 0 - 0 - 0 - - - 13519 - 1 - 0 - 0 - 0 - - - 13520 - 1 - 0 - 0 - 0 - - - 13521 - 1 - 0 - 0 - 0 - - - 13522 - 1 - 0 - 0 - 0 - - - 13523 - 1 - 0 - 0 - 0 - - - 13524 - 1 - 0 - 0 - 0 - - - 13525 - 1 - 0 - 0 - 0 - - - 13526 - 1 - 0 - 0 - 0 - - - 13527 - 1 - 0 - 0 - 0 - - - 13528 - 1 - 0 - 0 - 0 - - - 13529 - 1 - 0 - 0 - 0 - - - 13530 - 1 - 0 - 0 - 0 - - - 13531 - 1 - 0 - 0 - 0 - - - 13532 - 1 - 0 - 0 - 0 - - - 13533 - 1 - 0 - 0 - 0 - - - 13534 - 1 - 0 - 0 - 0 - - - 13535 - 1 - 0 - 0 - 0 - - - 13536 - 1 - 0 - 0 - 0 - - - 13537 - 1 - 0 - 0 - 0 - - - 13538 - 1 - 0 - 0 - 0 - - - 13539 - 1 - 0 - 0 - 0 - - - 13540 - 1 - 0 - 0 - 0 - - - 13541 - 1 - 0 - 0 - 0 - - - 13542 - 1 - 0 - 0 - 0 - - - 13543 - 1 - 0 - 0 - 0 - - - 13544 - 1 - 0 - 0 - 0 - - - 13545 - 1 - 0 - 0 - 0 - - - 13546 - 1 - 0 - 0 - 0 - - - 13547 - 1 - 0 - 0 - 0 - - - 13548 - 1 - 0 - 0 - 0 - - - 13549 - 1 - 0 - 0 - 0 - - - 13550 - 1 - 0 - 0 - 0 - - - 13551 - 1 - 0 - 0 - 0 - - - 13552 - 1 - 0 - 0 - 0 - - - 13553 - 1 - 0 - 0 - 0 - - - 13554 - 1 - 0 - 0 - 0 - - - 13555 - 1 - 0 - 0 - 0 - - - 13556 - 1 - 0 - 0 - 0 - - - 13557 - 1 - 0 - 0 - 0 - - - 13558 - 1 - 0 - 0 - 0 - - - 13559 - 1 - 0 - 0 - 0 - - - 13560 - 350 - 0 - 0 - 0 - - - 13561 - 350 - 0 - 0 - 0 - - - 13562 - 350 - 0 - 0 - 0 - - - 13563 - 38 - 0 - 0 - 0 - - - 13564 - 38 - 0 - 0 - 0 - - - 13565 - 1 - 0 - 0 - 0 - - - 13566 - 1 - 0 - 0 - 0 - - - 13567 - 1 - 0 - 0 - 0 - - - 13568 - 1 - 0 - 0 - 0 - - - 13569 - 1 - 0 - 0 - 0 - - - 13570 - 1 - 0 - 0 - 0 - - - 13571 - 1 - 0 - 0 - 0 - - - 13572 - 1 - 0 - 0 - 0 - - - 13573 - 1 - 0 - 0 - 0 - - - 13574 - 1 - 0 - 0 - 0 - - - 13575 - 1 - 0 - 0 - 0 - - - 13576 - 1 - 0 - 0 - 0 - - - 13577 - 1 - 0 - 0 - 0 - - - 13578 - 1 - 0 - 0 - 0 - - - 13579 - 1 - 0 - 0 - 0 - - - 13580 - 1 - 0 - 0 - 0 - - - 13581 - 1 - 0 - 0 - 0 - - - 13582 - 1 - 0 - 0 - 0 - - - 13583 - 1 - 0 - 0 - 0 - - - 13584 - 1 - 0 - 0 - 0 - - - 13585 - 1 - 0 - 0 - 0 - - - 13586 - 1 - 0 - 0 - 0 - - - 13587 - 4 - 0 - 0 - 0 - - - 13588 - 1 - 0 - 0 - 0 - - - 13589 - 2 - 0 - 0 - 0 - - - 13590 - 1 - 0 - 0 - 0 - - - 13591 - 1 - 0 - 0 - 0 - - - 13592 - 1 - 0 - 0 - 0 - - - 13593 - 1 - 0 - 0 - 0 - - - 13594 - 1 - 0 - 0 - 0 - - - 13595 - 1 - 0 - 0 - 0 - - - 13596 - 1 - 0 - 0 - 0 - - - 13597 - 1 - 0 - 0 - 0 - - - 13598 - 100 - 0 - 0 - 0 - - - 13599 - 100 - 0 - 0 - 0 - - - 13600 - 100 - 0 - 0 - 0 - - - 13601 - 100 - 0 - 0 - 0 - - - 13602 - 100 - 0 - 0 - 0 - - - 13603 - 100 - 0 - 0 - 0 - - - 13604 - 100 - 0 - 0 - 0 - - - 13605 - 100 - 0 - 0 - 0 - - - 13606 - 100 - 0 - 0 - 0 - - - 13607 - 100 - 0 - 0 - 0 - - - 13608 - 100 - 0 - 0 - 0 - - - 13609 - 100 - 0 - 0 - 0 - - - 13610 - 100 - 0 - 0 - 0 - - - 13611 - 100 - 0 - 0 - 0 - - - 13612 - 20 - 0 - 0 - 0 - - - 13613 - 20 - 0 - 0 - 0 - - - 13614 - 75 - 0 - 0 - 0 - - - 13615 - 75 - 0 - 0 - 0 - - - 13616 - 75 - 0 - 0 - 0 - - - 13617 - 75 - 0 - 0 - 0 - - - 13618 - 75 - 0 - 0 - 0 - - - 13619 - 75 - 0 - 0 - 0 - - - 13620 - 75 - 0 - 0 - 0 - - - 13621 - 75 - 0 - 0 - 0 - - - 13622 - 75 - 0 - 0 - 0 - - - 13623 - 75 - 0 - 0 - 0 - - - 13624 - 75 - 0 - 0 - 0 - - - 13625 - 75 - 0 - 0 - 0 - - - 13626 - 75 - 0 - 0 - 0 - - - 13627 - 75 - 0 - 0 - 0 - - - 13628 - 75 - 0 - 0 - 0 - - - 13629 - 15 - 0 - 0 - 0 - - - 13630 - 1500 - 0 - 0 - 0 - - - 13631 - 1500 - 0 - 0 - 0 - - - 13632 - 1500 - 0 - 0 - 0 - - - 13633 - 1500 - 0 - 0 - 0 - - - 13634 - 1500 - 0 - 0 - 0 - - - 13635 - 1500 - 0 - 0 - 0 - - - 13636 - 1500 - 0 - 0 - 0 - - - 13637 - 1500 - 0 - 0 - 0 - - - 13638 - 1500 - 0 - 0 - 0 - - - 13639 - 1500 - 0 - 0 - 0 - - - 13640 - 1500 - 0 - 0 - 0 - - - 13641 - 1500 - 0 - 0 - 0 - - - 13642 - 1500 - 0 - 0 - 0 - - - 13643 - 20 - 0 - 0 - 0 - - - 13644 - 20 - 0 - 0 - 0 - - - 13645 - 20 - 0 - 0 - 0 - - - 13646 - 20 - 0 - 0 - 0 - - - 13647 - 20 - 0 - 0 - 0 - - - 13648 - 20 - 0 - 0 - 0 - - - 13649 - 4 - 0 - 0 - 0 - - - 13650 - 1 - 0 - 0 - 0 - - - 13651 - 1 - 0 - 0 - 0 - - - 13652 - 1 - 0 - 0 - 0 - - - 13653 - 1 - 0 - 0 - 0 - - - 13654 - 1 - 0 - 0 - 0 - - - 13655 - 100 - 0 - 0 - 0 - - - 13656 - 75 - 0 - 0 - 0 - - - 13657 - 75 - 0 - 0 - 0 - - - 13658 - 75 - 0 - 0 - 0 - - - 13659 - 100 - 0 - 0 - 0 - - - 13660 - 200 - 0 - 0 - 0 - - - 13661 - 300 - 0 - 0 - 0 - - - 13662 - 1 - 0 - 0 - 0 - - - 13663 - 10 - 0 - 0 - 0 - - - 13664 - 10 - 0 - 0 - 0 - - - 13665 - 10 - 0 - 0 - 0 - - - 13666 - 10 - 0 - 0 - 0 - - - 13667 - 10 - 0 - 0 - 0 - - - 13668 - 10 - 0 - 0 - 0 - - - 13669 - 10 - 0 - 0 - 0 - - - 13670 - 10 - 0 - 0 - 0 - - - 13671 - 10 - 0 - 0 - 0 - - - 13672 - 10 - 0 - 0 - 0 - - - 13673 - 10 - 0 - 0 - 0 - - - 13674 - 10 - 0 - 0 - 0 - - - 13675 - 10 - 0 - 0 - 0 - - - 13676 - 10 - 0 - 0 - 0 - - - 13677 - 10 - 0 - 0 - 0 - - - 13678 - 10 - 0 - 0 - 0 - - - 13679 - 10 - 0 - 0 - 0 - - - 13680 - 10 - 0 - 0 - 0 - - - 13681 - 10 - 0 - 0 - 0 - - - 13682 - 10 - 0 - 0 - 0 - - - 13683 - 10 - 0 - 0 - 0 - - - 13684 - 10 - 0 - 0 - 0 - - - 13685 - 10 - 0 - 0 - 0 - - - 13686 - 10 - 0 - 0 - 0 - - - 13687 - 10 - 0 - 0 - 0 - - - 13688 - 10 - 0 - 0 - 0 - - - 13689 - 10 - 0 - 0 - 0 - - - 13690 - 10 - 0 - 0 - 0 - - - 13691 - 10 - 0 - 0 - 0 - - - 13692 - 10 - 0 - 0 - 0 - - - 13693 - 10 - 0 - 0 - 0 - - - 13694 - 10 - 0 - 0 - 0 - - - 13695 - 10 - 0 - 0 - 0 - - - 13696 - 10 - 0 - 0 - 0 - - - 13697 - 10 - 0 - 0 - 0 - - - 13698 - 10 - 0 - 0 - 0 - - - 13699 - 10 - 0 - 0 - 0 - - - 13700 - 10 - 0 - 0 - 0 - - - 13701 - 10 - 0 - 0 - 0 - - - 13702 - 10 - 0 - 0 - 0 - - - 13703 - 10 - 0 - 0 - 0 - - - 13704 - 10 - 0 - 0 - 0 - - - 13705 - 10 - 0 - 0 - 0 - - - 13706 - 10 - 0 - 0 - 0 - - - 13707 - 1 - 0 - 0 - 0 - - - 13708 - 1 - 0 - 0 - 0 - - - 13709 - 1 - 0 - 0 - 0 - - - 13710 - 1 - 0 - 0 - 0 - - - 13711 - 1 - 0 - 0 - 0 - - - 13712 - 1 - 0 - 0 - 0 - - - 13713 - 1 - 0 - 0 - 0 - - - 13714 - 1 - 0 - 0 - 0 - - - 13715 - 1 - 0 - 0 - 0 - - - 13716 - 1 - 0 - 0 - 0 - - - 13717 - 1 - 0 - 0 - 0 - - - 13718 - 1 - 0 - 0 - 0 - - - 13719 - 50 - 0 - 0 - 0 - - - 13720 - 14 - 0 - 0 - 0 - - - 13721 - 4 - 0 - 0 - 0 - - - 13722 - 7 - 0 - 0 - 0 - - - 13723 - 100 - 0 - 0 - 0 - - - 13724 - 100 - 0 - 0 - 0 - - - 13725 - 100 - 0 - 0 - 0 - - - 13726 - 100 - 0 - 0 - 0 - - - 13727 - 50 - 0 - 0 - 0 - - - 13728 - 1 - 0 - 0 - 0 - - - 13729 - 1 - 0 - 0 - 0 - - - 13730 - 1 - 0 - 0 - 0 - - - 13731 - 1 - 0 - 0 - 0 - - - 13732 - 1 - 0 - 0 - 0 - - - 13733 - 1 - 0 - 0 - 0 - - - 13734 - 70000 - 0 - 0 - 0 - - - 13735 - 70000 - 0 - 0 - 0 - - - 13736 - 1400000 - 0 - 0 - 0 - - - 13737 - 1400000 - 0 - 0 - 0 - - - 13738 - 2000000 - 0 - 0 - 0 - - - 13739 - 2000000 - 0 - 0 - 0 - - - 13740 - 2000000 - 0 - 0 - 0 - - - 13741 - 2000000 - 0 - 0 - 0 - - - 13742 - 2000000 - 0 - 0 - 0 - - - 13743 - 2000000 - 0 - 0 - 0 - - - 13744 - 2000000 - 0 - 0 - 0 - - - 13745 - 2000000 - 0 - 0 - 0 - - - 13746 - 750000 - 0 - 0 - 0 - - - 13747 - 750000 - 0 - 0 - 0 - - - 13748 - 750000 - 0 - 0 - 0 - - - 13749 - 750000 - 0 - 0 - 0 - - - 13750 - 750000 - 0 - 0 - 0 - - - 13751 - 750000 - 0 - 0 - 0 - - - 13752 - 750000 - 0 - 0 - 0 - - - 13753 - 750000 - 0 - 0 - 0 - - - 13754 - 750000 - 0 - 0 - 0 - - - 13755 - 750000 - 0 - 0 - 0 - - - 13756 - 40 - 0 - 0 - 0 - - - 13757 - 1 - 0 - 0 - 0 - - - 13758 - 1 - 0 - 0 - 0 - - - 13759 - 1 - 0 - 0 - 0 - - - 13760 - 1 - 0 - 0 - 0 - - - 13761 - 1 - 0 - 0 - 0 - - - 13762 - 1 - 0 - 0 - 0 - - - 13763 - 1 - 0 - 0 - 0 - - - 13764 - 1 - 0 - 0 - 0 - - - 13765 - 1 - 0 - 0 - 0 - - - 13766 - 1 - 0 - 0 - 0 - - - 13767 - 1 - 0 - 0 - 0 - - - 13768 - 1 - 0 - 0 - 0 - - - 13769 - 1 - 0 - 0 - 0 - - - 13770 - 1 - 0 - 0 - 0 - - - 13771 - 1 - 0 - 0 - 0 - - - 13772 - 1 - 0 - 0 - 0 - - - 13773 - 1 - 0 - 0 - 0 - - - 13774 - 1 - 0 - 0 - 0 - - - 13775 - 1 - 0 - 0 - 0 - - - 13776 - 1 - 0 - 0 - 0 - - - 13777 - 1 - 0 - 0 - 0 - - - 13778 - 1 - 0 - 0 - 0 - - - 13779 - 1 - 0 - 0 - 0 - - - 13780 - 1 - 0 - 0 - 0 - - - 13781 - 1 - 0 - 0 - 0 - - - 13782 - 1 - 0 - 0 - 0 - - - 13783 - 1 - 0 - 0 - 0 - - - 13784 - 1 - 0 - 0 - 0 - - - 13785 - 1 - 0 - 0 - 0 - - - 13786 - 1 - 0 - 0 - 0 - - - 13787 - 1 - 0 - 0 - 0 - - - 13788 - 1 - 0 - 0 - 0 - - - 13789 - 1 - 0 - 0 - 0 - - - 13790 - 1 - 0 - 0 - 0 - - - 13791 - 1 - 0 - 0 - 0 - - - 13792 - 1 - 0 - 0 - 0 - - - 13793 - 1 - 0 - 0 - 0 - - - 13794 - 1 - 0 - 0 - 0 - - - 13795 - 1 - 0 - 0 - 0 - - - 13796 - 1 - 0 - 0 - 0 - - - 13797 - 1 - 0 - 0 - 0 - - - 13798 - 1 - 0 - 0 - 0 - - - 13799 - 1 - 0 - 0 - 0 - - - 13800 - 1 - 0 - 0 - 0 - - - 13801 - 1 - 0 - 0 - 0 - - - 13802 - 1 - 0 - 0 - 0 - - - 13803 - 1 - 0 - 0 - 0 - - - 13804 - 1 - 0 - 0 - 0 - - - 13805 - 1 - 0 - 0 - 0 - - - 13806 - 1 - 0 - 0 - 0 - - - 13807 - 1 - 0 - 0 - 0 - - - 13808 - 1 - 0 - 0 - 0 - - - 13809 - 1 - 0 - 0 - 0 - - - 13810 - 1 - 0 - 0 - 0 - - - 13811 - 1 - 0 - 0 - 0 - - - 13812 - 1 - 0 - 0 - 0 - - - 13813 - 1 - 0 - 0 - 0 - - - 13814 - 1 - 0 - 0 - 0 - - - 13815 - 1 - 0 - 0 - 0 - - - 13816 - 1 - 0 - 0 - 0 - - - 13817 - 1 - 0 - 0 - 0 - - - 13818 - 1 - 0 - 0 - 0 - - - 13819 - 1 - 0 - 0 - 0 - - - 13820 - 1 - 0 - 0 - 0 - - - 13821 - 1 - 0 - 0 - 0 - - - 13822 - 1 - 0 - 0 - 0 - - - 13823 - 1 - 0 - 0 - 0 - - - 13824 - 1 - 0 - 0 - 0 - - - 13825 - 1 - 0 - 0 - 0 - - - 13826 - 1 - 0 - 0 - 0 - - - 13827 - 1 - 0 - 0 - 0 - - - 13828 - 1 - 0 - 0 - 0 - - - 13829 - 1 - 0 - 0 - 0 - - - 13830 - 1 - 0 - 0 - 0 - - - 13831 - 1 - 0 - 0 - 0 - - - 13832 - 1 - 0 - 0 - 0 - - - 13833 - 1 - 0 - 0 - 0 - - - 13834 - 1 - 0 - 0 - 0 - - - 13835 - 1 - 0 - 0 - 0 - - - 13836 - 1 - 0 - 0 - 0 - - - 13837 - 1 - 0 - 0 - 0 - - - 13838 - 1 - 0 - 0 - 0 - - - 13839 - 1 - 0 - 0 - 0 - - - 13840 - 1 - 0 - 0 - 0 - - - 13841 - 1 - 0 - 0 - 0 - - - 13842 - 1 - 0 - 0 - 0 - - - 13843 - 1 - 0 - 0 - 0 - - - 13844 - 1 - 0 - 0 - 0 - - - 13845 - 50000 - 0 - 0 - 0 - - - 13846 - 50000 - 0 - 0 - 0 - - - 13847 - 50000 - 0 - 0 - 0 - - - 13848 - 50000 - 0 - 0 - 0 - - - 13849 - 50000 - 0 - 0 - 0 - - - 13850 - 50000 - 0 - 0 - 0 - - - 13851 - 50000 - 0 - 0 - 0 - - - 13852 - 50000 - 0 - 0 - 0 - - - 13853 - 50000 - 0 - 0 - 0 - - - 13854 - 50000 - 0 - 0 - 0 - - - 13855 - 50000 - 0 - 0 - 0 - - - 13856 - 50000 - 0 - 0 - 0 - - - 13857 - 50000 - 0 - 0 - 0 - - - 13858 - 500000 - 0 - 0 - 0 - - - 13859 - 500000 - 0 - 0 - 0 - - - 13860 - 300000 - 0 - 0 - 0 - - - 13861 - 500000 - 0 - 0 - 0 - - - 13862 - 500000 - 0 - 0 - 0 - - - 13863 - 300000 - 0 - 0 - 0 - - - 13864 - 250000 - 0 - 0 - 0 - - - 13865 - 250000 - 0 - 0 - 0 - - - 13866 - 150000 - 0 - 0 - 0 - - - 13867 - 300000 - 0 - 0 - 0 - - - 13868 - 300000 - 0 - 0 - 0 - - - 13869 - 180000 - 0 - 0 - 0 - - - 13870 - 500000 - 0 - 0 - 0 - - - 13871 - 500000 - 0 - 0 - 0 - - - 13872 - 300000 - 0 - 0 - 0 - - - 13873 - 500000 - 0 - 0 - 0 - - - 13874 - 500000 - 0 - 0 - 0 - - - 13875 - 300000 - 0 - 0 - 0 - - - 13876 - 250000 - 0 - 0 - 0 - - - 13877 - 250000 - 0 - 0 - 0 - - - 13878 - 150000 - 0 - 0 - 0 - - - 13879 - 4000 - 0 - 0 - 0 - - - 13880 - 10000 - 0 - 0 - 0 - - - 13881 - 10000 - 0 - 0 - 0 - - - 13882 - 10000 - 0 - 0 - 0 - - - 13883 - 4000 - 0 - 0 - 0 - - - 13884 - 500000 - 0 - 0 - 0 - - - 13885 - 500000 - 0 - 0 - 0 - - - 13886 - 300000 - 0 - 0 - 0 - - - 13887 - 500000 - 0 - 0 - 0 - - - 13888 - 500000 - 0 - 0 - 0 - - - 13889 - 300000 - 0 - 0 - 0 - - - 13890 - 500000 - 0 - 0 - 0 - - - 13891 - 500000 - 0 - 0 - 0 - - - 13892 - 300000 - 0 - 0 - 0 - - - 13893 - 500000 - 0 - 0 - 0 - - - 13894 - 500000 - 0 - 0 - 0 - - - 13895 - 300000 - 0 - 0 - 0 - - - 13896 - 250000 - 0 - 0 - 0 - - - 13897 - 250000 - 0 - 0 - 0 - - - 13898 - 150000 - 0 - 0 - 0 - - - 13899 - 300000 - 0 - 0 - 0 - - - 13900 - 300000 - 0 - 0 - 0 - - - 13901 - 180000 - 0 - 0 - 0 - - - 13902 - 300000 - 0 - 0 - 0 - - - 13903 - 300000 - 0 - 0 - 0 - - - 13904 - 180000 - 0 - 0 - 0 - - - 13905 - 300000 - 0 - 0 - 0 - - - 13906 - 300000 - 0 - 0 - 0 - - - 13907 - 180000 - 0 - 0 - 0 - - - 13908 - 125000 - 0 - 0 - 0 - - - 13909 - 125000 - 0 - 0 - 0 - - - 13910 - 75000 - 0 - 0 - 0 - - - 13911 - 125000 - 0 - 0 - 0 - - - 13912 - 125000 - 0 - 0 - 0 - - - 13913 - 75000 - 0 - 0 - 0 - - - 13914 - 125000 - 0 - 0 - 0 - - - 13915 - 125000 - 0 - 0 - 0 - - - 13916 - 75000 - 0 - 0 - 0 - - - 13917 - 125000 - 0 - 0 - 0 - - - 13918 - 125000 - 0 - 0 - 0 - - - 13919 - 75000 - 0 - 0 - 0 - - - 13920 - 50000 - 0 - 0 - 0 - - - 13921 - 50000 - 0 - 0 - 0 - - - 13922 - 30000 - 0 - 0 - 0 - - - 13923 - 125000 - 0 - 0 - 0 - - - 13924 - 125000 - 0 - 0 - 0 - - - 13925 - 75000 - 0 - 0 - 0 - - - 13926 - 125000 - 0 - 0 - 0 - - - 13927 - 125000 - 0 - 0 - 0 - - - 13928 - 75000 - 0 - 0 - 0 - - - 13929 - 125000 - 0 - 0 - 0 - - - 13930 - 125000 - 0 - 0 - 0 - - - 13931 - 75000 - 0 - 0 - 0 - - - 13932 - 125000 - 0 - 0 - 0 - - - 13933 - 125000 - 0 - 0 - 0 - - - 13934 - 75000 - 0 - 0 - 0 - - - 13935 - 125000 - 0 - 0 - 0 - - - 13936 - 125000 - 0 - 0 - 0 - - - 13937 - 75000 - 0 - 0 - 0 - - - 13938 - 50000 - 0 - 0 - 0 - - - 13939 - 50000 - 0 - 0 - 0 - - - 13940 - 30000 - 0 - 0 - 0 - - - 13941 - 125000 - 0 - 0 - 0 - - - 13942 - 125000 - 0 - 0 - 0 - - - 13943 - 70000 - 0 - 0 - 0 - - - 13944 - 125000 - 0 - 0 - 0 - - - 13945 - 125000 - 0 - 0 - 0 - - - 13946 - 75000 - 0 - 0 - 0 - - - 13947 - 125000 - 0 - 0 - 0 - - - 13948 - 125000 - 0 - 0 - 0 - - - 13949 - 75000 - 0 - 0 - 0 - - - 13950 - 50000 - 0 - 0 - 0 - - - 13951 - 50000 - 0 - 0 - 0 - - - 13952 - 30000 - 0 - 0 - 0 - - - 13953 - 1000 - 0 - 0 - 0 - - - 13954 - 10000 - 0 - 0 - 0 - - - 13955 - 10000 - 0 - 0 - 0 - - - 13956 - 10000 - 0 - 0 - 0 - - - 13957 - 1000 - 0 - 0 - 0 - - - 13958 - 120000 - 0 - 0 - 0 - - - 13959 - 120000 - 0 - 0 - 0 - - - 13960 - 72000 - 0 - 0 - 0 - - - 13961 - 60000 - 0 - 0 - 0 - - - 13962 - 60000 - 0 - 0 - 0 - - - 13963 - 36000 - 0 - 0 - 0 - - - 13964 - 120000 - 0 - 0 - 0 - - - 13965 - 120000 - 0 - 0 - 0 - - - 13966 - 72000 - 0 - 0 - 0 - - - 13967 - 120000 - 0 - 0 - 0 - - - 13968 - 120000 - 0 - 0 - 0 - - - 13969 - 72000 - 0 - 0 - 0 - - - 13970 - 120000 - 0 - 0 - 0 - - - 13971 - 120000 - 0 - 0 - 0 - - - 13972 - 72000 - 0 - 0 - 0 - - - 13973 - 120000 - 0 - 0 - 0 - - - 13974 - 120000 - 0 - 0 - 0 - - - 13975 - 72000 - 0 - 0 - 0 - - - 13976 - 30000 - 0 - 0 - 0 - - - 13977 - 30000 - 0 - 0 - 0 - - - 13978 - 18000 - 0 - 0 - 0 - - - 13979 - 100000 - 0 - 0 - 0 - - - 13980 - 100000 - 0 - 0 - 0 - - - 13981 - 60000 - 0 - 0 - 0 - - - 13982 - 100000 - 0 - 0 - 0 - - - 13983 - 100000 - 0 - 0 - 0 - - - 13984 - 60000 - 0 - 0 - 0 - - - 13985 - 50000 - 0 - 0 - 0 - - - 13986 - 50000 - 0 - 0 - 0 - - - 13987 - 30000 - 0 - 0 - 0 - - - 13988 - 40000 - 0 - 0 - 0 - - - 13989 - 40000 - 0 - 0 - 0 - - - 13990 - 24000 - 0 - 0 - 0 - - - 13991 - 3 - 0 - 0 - 0 - - - 13992 - 3 - 0 - 0 - 0 - - - 13993 - 3 - 0 - 0 - 0 - - - 13994 - 3 - 0 - 0 - 0 - - - 13995 - 3 - 0 - 0 - 0 - - - 13996 - 3 - 0 - 0 - 0 - - - 13997 - 3 - 0 - 0 - 0 - - - 13998 - 3 - 0 - 0 - 0 - - - 13999 - 3 - 0 - 0 - 0 - - - 14000 - 3 - 0 - 0 - 0 - - - 14001 - 3 - 0 - 0 - 0 - - - 14002 - 3 - 0 - 0 - 0 - - - 14003 - 3 - 0 - 0 - 0 - - - 14004 - 3 - 0 - 0 - 0 - - - 14005 - 3 - 0 - 0 - 0 - - - 14006 - 3 - 0 - 0 - 0 - - - 14007 - 3 - 0 - 0 - 0 - - - 14008 - 3 - 0 - 0 - 0 - - - 14009 - 3 - 0 - 0 - 0 - - - 14010 - 3 - 0 - 0 - 0 - - - 14011 - 3 - 0 - 0 - 0 - - - 14012 - 3 - 0 - 0 - 0 - - - 14013 - 3 - 0 - 0 - 0 - - - 14014 - 3 - 0 - 0 - 0 - - - 14015 - 3 - 0 - 0 - 0 - - - 14016 - 3 - 0 - 0 - 0 - - - 14017 - 3 - 0 - 0 - 0 - - - 14018 - 3 - 0 - 0 - 0 - - - 14019 - 3 - 0 - 0 - 0 - - - 14020 - 3 - 0 - 0 - 0 - - - 14021 - 3 - 0 - 0 - 0 - - - 14022 - 3 - 0 - 0 - 0 - - - 14023 - 3 - 0 - 0 - 0 - - - 14024 - 3 - 0 - 0 - 0 - - - 14025 - 3 - 0 - 0 - 0 - - - 14026 - 3 - 0 - 0 - 0 - - - 14027 - 3 - 0 - 0 - 0 - - - 14028 - 3 - 0 - 0 - 0 - - - 14029 - 3 - 0 - 0 - 0 - - - 14030 - 3 - 0 - 0 - 0 - - - 14031 - 3 - 0 - 0 - 0 - - - 14032 - 3 - 0 - 0 - 0 - - - 14033 - 3 - 0 - 0 - 0 - - - 14034 - 3 - 0 - 0 - 0 - - - 14035 - 3 - 0 - 0 - 0 - - - 14036 - 3 - 0 - 0 - 0 - - - 14037 - 3 - 0 - 0 - 0 - - - 14038 - 3 - 0 - 0 - 0 - - - 14039 - 3 - 0 - 0 - 0 - - - 14040 - 3 - 0 - 0 - 0 - - - 14041 - 3 - 0 - 0 - 0 - - - 14042 - 3 - 0 - 0 - 0 - - - 14043 - 3 - 0 - 0 - 0 - - - 14044 - 3 - 0 - 0 - 0 - - - 14045 - 3 - 0 - 0 - 0 - - - 14046 - 3 - 0 - 0 - 0 - - - 14047 - 3 - 0 - 0 - 0 - - - 14048 - 3 - 0 - 0 - 0 - - - 14049 - 3 - 0 - 0 - 0 - - - 14050 - 3 - 0 - 0 - 0 - - - 14051 - 3 - 0 - 0 - 0 - - - 14052 - 3 - 0 - 0 - 0 - - - 14053 - 3 - 0 - 0 - 0 - - - 14054 - 3 - 0 - 0 - 0 - - - 14055 - 3 - 0 - 0 - 0 - - - 14056 - 5 - 0 - 0 - 0 - - - 14057 - 25 - 0 - 0 - 0 - - - 14058 - 2 - 0 - 0 - 0 - - - 14059 - 4 - 0 - 0 - 0 - - - 14060 - 4 - 0 - 0 - 0 - - - 14061 - 2 - 0 - 0 - 0 - - - 14062 - 2 - 0 - 0 - 0 - - - 14063 - 1 - 0 - 0 - 0 - - - 14064 - 5 - 0 - 0 - 0 - - - 14065 - 1 - 0 - 0 - 0 - - - 14066 - 1 - 0 - 0 - 0 - - - 14067 - 1 - 0 - 0 - 0 - - - 14068 - 15 - 0 - 0 - 0 - - - 14069 - 2 - 0 - 0 - 0 - - - 14070 - 1 - 0 - 0 - 0 - - - 14071 - 1 - 0 - 0 - 0 - - - 14072 - 1 - 0 - 0 - 0 - - - 14073 - 1 - 0 - 0 - 0 - - - 14074 - 1 - 0 - 0 - 0 - - - 14075 - 1 - 0 - 0 - 0 - - - 14076 - 15 - 0 - 0 - 0 - - - 14077 - 15 - 0 - 0 - 0 - - - 14078 - 15 - 0 - 0 - 0 - - - 14079 - 15 - 0 - 0 - 0 - - - 14080 - 15 - 0 - 0 - 0 - - - 14081 - 15 - 0 - 0 - 0 - - - 14082 - 1 - 0 - 0 - 0 - - - 14083 - 1 - 0 - 0 - 0 - - - 14084 - 1 - 0 - 0 - 0 - - - 14085 - 1 - 0 - 0 - 0 - - - 14086 - 15 - 0 - 0 - 0 - - - 14087 - 15 - 0 - 0 - 0 - - - 14088 - 15 - 0 - 0 - 0 - - - 14089 - 1 - 0 - 0 - 0 - - - 14090 - 1 - 0 - 0 - 0 - - - 14091 - 1 - 0 - 0 - 0 - - - 14092 - 1 - 0 - 0 - 0 - - - 14093 - 1 - 0 - 0 - 0 - - - 14094 - 43333 - 0 - 0 - 0 - - - 14095 - 42667 - 0 - 0 - 0 - - - 14096 - 23467 - 0 - 0 - 0 - - - 14097 - 17066 - 0 - 0 - 0 - - - 14098 - 21333 - 0 - 0 - 0 - - - 14099 - 21333 - 0 - 0 - 0 - - - 14100 - 21333 - 0 - 0 - 0 - - - 14101 - 21333 - 0 - 0 - 0 - - - 14102 - 21333 - 0 - 0 - 0 - - - 14103 - 21333 - 0 - 0 - 0 - - - 14104 - 21333 - 0 - 0 - 0 - - - 14105 - 21333 - 0 - 0 - 0 - - - 14106 - 21333 - 0 - 0 - 0 - - - 14107 - 21333 - 0 - 0 - 0 - - - 14108 - 21333 - 0 - 0 - 0 - - - 14109 - 21333 - 0 - 0 - 0 - - - 14110 - 1 - 0 - 0 - 0 - - - 14111 - 21333 - 0 - 0 - 0 - - - 14112 - 21333 - 0 - 0 - 0 - - - 14113 - 21333 - 0 - 0 - 0 - - - 14114 - 43333 - 0 - 0 - 0 - - - 14115 - 42667 - 0 - 0 - 0 - - - 14116 - 23467 - 0 - 0 - 0 - - - 14117 - 17066 - 0 - 0 - 0 - - - 14118 - 43333 - 0 - 0 - 0 - - - 14119 - 42667 - 0 - 0 - 0 - - - 14120 - 23467 - 0 - 0 - 0 - - - 14121 - 17066 - 0 - 0 - 0 - - - 14122 - 30 - 0 - 0 - 0 - - - 14123 - 30 - 0 - 0 - 0 - - - 14124 - 60 - 0 - 0 - 0 - - - 14125 - 60 - 0 - 0 - 0 - - - 14126 - 90 - 0 - 0 - 0 - - - 14127 - 90 - 0 - 0 - 0 - - - 14128 - 120 - 0 - 0 - 0 - - - 14129 - 120 - 0 - 0 - 0 - - - 14130 - 150 - 0 - 0 - 0 - - - 14131 - 150 - 0 - 0 - 0 - - - 14132 - 30 - 0 - 0 - 0 - - - 14133 - 30 - 0 - 0 - 0 - - - 14134 - 60 - 0 - 0 - 0 - - - 14135 - 60 - 0 - 0 - 0 - - - 14136 - 90 - 0 - 0 - 0 - - - 14137 - 90 - 0 - 0 - 0 - - - 14138 - 120 - 0 - 0 - 0 - - - 14139 - 120 - 0 - 0 - 0 - - - 14140 - 150 - 0 - 0 - 0 - - - 14141 - 150 - 0 - 0 - 0 - - - 14142 - 30 - 0 - 0 - 0 - - - 14143 - 30 - 0 - 0 - 0 - - - 14144 - 60 - 0 - 0 - 0 - - - 14145 - 60 - 0 - 0 - 0 - - - 14146 - 90 - 0 - 0 - 0 - - - 14147 - 90 - 0 - 0 - 0 - - - 14148 - 120 - 0 - 0 - 0 - - - 14149 - 120 - 0 - 0 - 0 - - - 14150 - 150 - 0 - 0 - 0 - - - 14151 - 150 - 0 - 0 - 0 - - - 14152 - 30 - 0 - 0 - 0 - - - 14153 - 30 - 0 - 0 - 0 - - - 14154 - 60 - 0 - 0 - 0 - - - 14155 - 60 - 0 - 0 - 0 - - - 14156 - 90 - 0 - 0 - 0 - - - 14157 - 90 - 0 - 0 - 0 - - - 14158 - 120 - 0 - 0 - 0 - - - 14159 - 120 - 0 - 0 - 0 - - - 14160 - 150 - 0 - 0 - 0 - - - 14161 - 150 - 0 - 0 - 0 - - - 14162 - 30 - 0 - 0 - 0 - - - 14163 - 30 - 0 - 0 - 0 - - - 14164 - 60 - 0 - 0 - 0 - - - 14165 - 60 - 0 - 0 - 0 - - - 14166 - 90 - 0 - 0 - 0 - - - 14167 - 90 - 0 - 0 - 0 - - - 14168 - 120 - 0 - 0 - 0 - - - 14169 - 120 - 0 - 0 - 0 - - - 14170 - 150 - 0 - 0 - 0 - - - 14171 - 150 - 0 - 0 - 0 - - - 14172 - 30 - 0 - 0 - 0 - - - 14173 - 30 - 0 - 0 - 0 - - - 14174 - 60 - 0 - 0 - 0 - - - 14175 - 60 - 0 - 0 - 0 - - - 14176 - 90 - 0 - 0 - 0 - - - 14177 - 90 - 0 - 0 - 0 - - - 14178 - 120 - 0 - 0 - 0 - - - 14179 - 120 - 0 - 0 - 0 - - - 14180 - 150 - 0 - 0 - 0 - - - 14181 - 150 - 0 - 0 - 0 - - - 14182 - 15 - 0 - 0 - 0 - - - 14183 - 15 - 0 - 0 - 0 - - - 14184 - 30 - 0 - 0 - 0 - - - 14185 - 30 - 0 - 0 - 0 - - - 14186 - 45 - 0 - 0 - 0 - - - 14187 - 45 - 0 - 0 - 0 - - - 14188 - 60 - 0 - 0 - 0 - - - 14189 - 60 - 0 - 0 - 0 - - - 14190 - 75 - 0 - 0 - 0 - - - 14191 - 75 - 0 - 0 - 0 - - - 14192 - 30 - 0 - 0 - 0 - - - 14193 - 30 - 0 - 0 - 0 - - - 14194 - 60 - 0 - 0 - 0 - - - 14195 - 60 - 0 - 0 - 0 - - - 14196 - 90 - 0 - 0 - 0 - - - 14197 - 90 - 0 - 0 - 0 - - - 14198 - 120 - 0 - 0 - 0 - - - 14199 - 120 - 0 - 0 - 0 - - - 14200 - 150 - 0 - 0 - 0 - - - 14201 - 150 - 0 - 0 - 0 - - - 14202 - 2 - 0 - 0 - 0 - - - 14203 - 4 - 0 - 0 - 0 - - - 14204 - 6 - 0 - 0 - 0 - - - 14205 - 8 - 0 - 0 - 0 - - - 14206 - 10 - 0 - 0 - 0 - - - 14207 - 150 - 0 - 0 - 0 - - - 14208 - 150 - 0 - 0 - 0 - - - 14209 - 120 - 0 - 0 - 0 - - - 14210 - 120 - 0 - 0 - 0 - - - 14211 - 90 - 0 - 0 - 0 - - - 14212 - 90 - 0 - 0 - 0 - - - 14213 - 60 - 0 - 0 - 0 - - - 14214 - 60 - 0 - 0 - 0 - - - 14215 - 30 - 0 - 0 - 0 - - - 14216 - 30 - 0 - 0 - 0 - - - 14217 - 150 - 0 - 0 - 0 - - - 14218 - 150 - 0 - 0 - 0 - - - 14219 - 120 - 0 - 0 - 0 - - - 14220 - 120 - 0 - 0 - 0 - - - 14221 - 90 - 0 - 0 - 0 - - - 14222 - 90 - 0 - 0 - 0 - - - 14223 - 60 - 0 - 0 - 0 - - - 14224 - 60 - 0 - 0 - 0 - - - 14225 - 30 - 0 - 0 - 0 - - - 14226 - 30 - 0 - 0 - 0 - - - 14227 - 150 - 0 - 0 - 0 - - - 14228 - 150 - 0 - 0 - 0 - - - 14229 - 120 - 0 - 0 - 0 - - - 14230 - 120 - 0 - 0 - 0 - - - 14231 - 90 - 0 - 0 - 0 - - - 14232 - 90 - 0 - 0 - 0 - - - 14233 - 60 - 0 - 0 - 0 - - - 14234 - 60 - 0 - 0 - 0 - - - 14235 - 30 - 0 - 0 - 0 - - - 14236 - 30 - 0 - 0 - 0 - - - 14237 - 150 - 0 - 0 - 0 - - - 14238 - 150 - 0 - 0 - 0 - - - 14239 - 120 - 0 - 0 - 0 - - - 14240 - 120 - 0 - 0 - 0 - - - 14241 - 90 - 0 - 0 - 0 - - - 14242 - 90 - 0 - 0 - 0 - - - 14243 - 60 - 0 - 0 - 0 - - - 14244 - 60 - 0 - 0 - 0 - - - 14245 - 30 - 0 - 0 - 0 - - - 14246 - 30 - 0 - 0 - 0 - - - 14247 - 150 - 0 - 0 - 0 - - - 14248 - 150 - 0 - 0 - 0 - - - 14249 - 120 - 0 - 0 - 0 - - - 14250 - 120 - 0 - 0 - 0 - - - 14251 - 90 - 0 - 0 - 0 - - - 14252 - 90 - 0 - 0 - 0 - - - 14253 - 60 - 0 - 0 - 0 - - - 14254 - 60 - 0 - 0 - 0 - - - 14255 - 30 - 0 - 0 - 0 - - - 14256 - 30 - 0 - 0 - 0 - - - 14257 - 150 - 0 - 0 - 0 - - - 14258 - 150 - 0 - 0 - 0 - - - 14259 - 120 - 0 - 0 - 0 - - - 14260 - 120 - 0 - 0 - 0 - - - 14261 - 90 - 0 - 0 - 0 - - - 14262 - 90 - 0 - 0 - 0 - - - 14263 - 60 - 0 - 0 - 0 - - - 14264 - 60 - 0 - 0 - 0 - - - 14265 - 30 - 0 - 0 - 0 - - - 14266 - 30 - 0 - 0 - 0 - - - 14267 - 150 - 0 - 0 - 0 - - - 14268 - 150 - 0 - 0 - 0 - - - 14269 - 120 - 0 - 0 - 0 - - - 14270 - 120 - 0 - 0 - 0 - - - 14271 - 90 - 0 - 0 - 0 - - - 14272 - 90 - 0 - 0 - 0 - - - 14273 - 60 - 0 - 0 - 0 - - - 14274 - 60 - 0 - 0 - 0 - - - 14275 - 30 - 0 - 0 - 0 - - - 14276 - 30 - 0 - 0 - 0 - - - 14277 - 150 - 0 - 0 - 0 - - - 14278 - 150 - 0 - 0 - 0 - - - 14279 - 120 - 0 - 0 - 0 - - - 14280 - 120 - 0 - 0 - 0 - - - 14281 - 90 - 0 - 0 - 0 - - - 14282 - 90 - 0 - 0 - 0 - - - 14283 - 60 - 0 - 0 - 0 - - - 14284 - 60 - 0 - 0 - 0 - - - 14285 - 30 - 0 - 0 - 0 - - - 14286 - 30 - 0 - 0 - 0 - - - 14287 - 30 - 0 - 0 - 0 - - - 14288 - 30 - 0 - 0 - 0 - - - 14289 - 60 - 0 - 0 - 0 - - - 14290 - 60 - 0 - 0 - 0 - - - 14291 - 90 - 0 - 0 - 0 - - - 14292 - 90 - 0 - 0 - 0 - - - 14293 - 120 - 0 - 0 - 0 - - - 14294 - 120 - 0 - 0 - 0 - - - 14295 - 150 - 0 - 0 - 0 - - - 14296 - 150 - 0 - 0 - 0 - - - 14297 - 30 - 0 - 0 - 0 - - - 14298 - 30 - 0 - 0 - 0 - - - 14299 - 60 - 0 - 0 - 0 - - - 14300 - 60 - 0 - 0 - 0 - - - 14301 - 90 - 0 - 0 - 0 - - - 14302 - 90 - 0 - 0 - 0 - - - 14303 - 120 - 0 - 0 - 0 - - - 14304 - 120 - 0 - 0 - 0 - - - 14305 - 150 - 0 - 0 - 0 - - - 14306 - 150 - 0 - 0 - 0 - - - 14307 - 30 - 0 - 0 - 0 - - - 14308 - 30 - 0 - 0 - 0 - - - 14309 - 60 - 0 - 0 - 0 - - - 14310 - 60 - 0 - 0 - 0 - - - 14311 - 90 - 0 - 0 - 0 - - - 14312 - 90 - 0 - 0 - 0 - - - 14313 - 120 - 0 - 0 - 0 - - - 14314 - 120 - 0 - 0 - 0 - - - 14315 - 150 - 0 - 0 - 0 - - - 14316 - 150 - 0 - 0 - 0 - - - 14317 - 30 - 0 - 0 - 0 - - - 14318 - 30 - 0 - 0 - 0 - - - 14319 - 60 - 0 - 0 - 0 - - - 14320 - 60 - 0 - 0 - 0 - - - 14321 - 90 - 0 - 0 - 0 - - - 14322 - 90 - 0 - 0 - 0 - - - 14323 - 120 - 0 - 0 - 0 - - - 14324 - 120 - 0 - 0 - 0 - - - 14325 - 150 - 0 - 0 - 0 - - - 14326 - 150 - 0 - 0 - 0 - - - 14327 - 30 - 0 - 0 - 0 - - - 14328 - 30 - 0 - 0 - 0 - - - 14329 - 60 - 0 - 0 - 0 - - - 14330 - 60 - 0 - 0 - 0 - - - 14331 - 90 - 0 - 0 - 0 - - - 14332 - 90 - 0 - 0 - 0 - - - 14333 - 120 - 0 - 0 - 0 - - - 14334 - 120 - 0 - 0 - 0 - - - 14335 - 150 - 0 - 0 - 0 - - - 14336 - 150 - 0 - 0 - 0 - - - 14337 - 30 - 0 - 0 - 0 - - - 14338 - 30 - 0 - 0 - 0 - - - 14339 - 60 - 0 - 0 - 0 - - - 14340 - 60 - 0 - 0 - 0 - - - 14341 - 90 - 0 - 0 - 0 - - - 14342 - 90 - 0 - 0 - 0 - - - 14343 - 120 - 0 - 0 - 0 - - - 14344 - 120 - 0 - 0 - 0 - - - 14345 - 150 - 0 - 0 - 0 - - - 14346 - 150 - 0 - 0 - 0 - - - 14347 - 30 - 0 - 0 - 0 - - - 14348 - 30 - 0 - 0 - 0 - - - 14349 - 60 - 0 - 0 - 0 - - - 14350 - 60 - 0 - 0 - 0 - - - 14351 - 90 - 0 - 0 - 0 - - - 14352 - 90 - 0 - 0 - 0 - - - 14353 - 120 - 0 - 0 - 0 - - - 14354 - 120 - 0 - 0 - 0 - - - 14355 - 150 - 0 - 0 - 0 - - - 14356 - 150 - 0 - 0 - 0 - - - 14357 - 30 - 0 - 0 - 0 - - - 14358 - 30 - 0 - 0 - 0 - - - 14359 - 60 - 0 - 0 - 0 - - - 14360 - 60 - 0 - 0 - 0 - - - 14361 - 90 - 0 - 0 - 0 - - - 14362 - 90 - 0 - 0 - 0 - - - 14363 - 120 - 0 - 0 - 0 - - - 14364 - 120 - 0 - 0 - 0 - - - 14365 - 150 - 0 - 0 - 0 - - - 14366 - 150 - 0 - 0 - 0 - - - 14367 - 30 - 0 - 0 - 0 - - - 14368 - 30 - 0 - 0 - 0 - - - 14369 - 60 - 0 - 0 - 0 - - - 14370 - 60 - 0 - 0 - 0 - - - 14371 - 90 - 0 - 0 - 0 - - - 14372 - 90 - 0 - 0 - 0 - - - 14373 - 120 - 0 - 0 - 0 - - - 14374 - 120 - 0 - 0 - 0 - - - 14375 - 150 - 0 - 0 - 0 - - - 14376 - 150 - 0 - 0 - 0 - - - 14377 - 30 - 0 - 0 - 0 - - - 14378 - 30 - 0 - 0 - 0 - - - 14379 - 60 - 0 - 0 - 0 - - - 14380 - 60 - 0 - 0 - 0 - - - 14381 - 90 - 0 - 0 - 0 - - - 14382 - 90 - 0 - 0 - 0 - - - 14383 - 120 - 0 - 0 - 0 - - - 14384 - 120 - 0 - 0 - 0 - - - 14385 - 150 - 0 - 0 - 0 - - - 14386 - 150 - 0 - 0 - 0 - - - 14387 - 1 - 0 - 0 - 0 - - - 14389 - 1 - 0 - 0 - 0 - - - 14391 - 30 - 0 - 0 - 0 - - - 14392 - 30 - 0 - 0 - 0 - - - 14393 - 60 - 0 - 0 - 0 - - - 14394 - 60 - 0 - 0 - 0 - - - 14395 - 90 - 0 - 0 - 0 - - - 14396 - 90 - 0 - 0 - 0 - - - 14397 - 120 - 0 - 0 - 0 - - - 14398 - 120 - 0 - 0 - 0 - - - 14399 - 150 - 0 - 0 - 0 - - - 14400 - 150 - 0 - 0 - 0 - - - 14401 - 30 - 0 - 0 - 0 - - - 14402 - 30 - 0 - 0 - 0 - - - 14403 - 60 - 0 - 0 - 0 - - - 14404 - 60 - 0 - 0 - 0 - - - 14405 - 90 - 0 - 0 - 0 - - - 14406 - 90 - 0 - 0 - 0 - - - 14407 - 120 - 0 - 0 - 0 - - - 14408 - 120 - 0 - 0 - 0 - - - 14409 - 150 - 0 - 0 - 0 - - - 14410 - 150 - 0 - 0 - 0 - - - 14411 - 30 - 0 - 0 - 0 - - - 14412 - 30 - 0 - 0 - 0 - - - 14413 - 60 - 0 - 0 - 0 - - - 14414 - 60 - 0 - 0 - 0 - - - 14415 - 90 - 0 - 0 - 0 - - - 14416 - 90 - 0 - 0 - 0 - - - 14417 - 120 - 0 - 0 - 0 - - - 14418 - 120 - 0 - 0 - 0 - - - 14419 - 150 - 0 - 0 - 0 - - - 14420 - 150 - 0 - 0 - 0 - - - 14421 - 30 - 0 - 0 - 0 - - - 14422 - 30 - 0 - 0 - 0 - - - 14423 - 30 - 0 - 0 - 0 - - - 14424 - 60 - 0 - 0 - 0 - - - 14425 - 60 - 0 - 0 - 0 - - - 14426 - 90 - 0 - 0 - 0 - - - 14427 - 90 - 0 - 0 - 0 - - - 14428 - 120 - 0 - 0 - 0 - - - 14429 - 120 - 0 - 0 - 0 - - - 14430 - 150 - 0 - 0 - 0 - - - 14431 - 150 - 0 - 0 - 0 - - - 14432 - 1 - 0 - 0 - 0 - - - 14433 - 1 - 0 - 0 - 0 - - - 14434 - 1 - 0 - 0 - 0 - - - 14435 - 1 - 0 - 0 - 0 - - - 14436 - 1 - 0 - 0 - 0 - - - 14437 - 1 - 0 - 0 - 0 - - - 14438 - 1 - 0 - 0 - 0 - - - 14439 - 1 - 0 - 0 - 0 - - - 14440 - 1 - 0 - 0 - 0 - - - 14441 - 1 - 0 - 0 - 0 - - - 14442 - 1 - 0 - 0 - 0 - - - 14443 - 1 - 0 - 0 - 0 - - - 14444 - 1 - 0 - 0 - 0 - - - 14445 - 1 - 0 - 0 - 0 - - - 14446 - 1 - 0 - 0 - 0 - - - 14447 - 1 - 0 - 0 - 0 - - - 14448 - 1 - 0 - 0 - 0 - - - 14449 - 1 - 0 - 0 - 0 - - - 14450 - 1 - 0 - 0 - 0 - - - 14451 - 1 - 0 - 0 - 0 - - - 14452 - 1 - 0 - 0 - 0 - - - 14453 - 1 - 0 - 0 - 0 - - - 14454 - 1 - 0 - 0 - 0 - - - 14455 - 1 - 0 - 0 - 0 - - - 14456 - 1 - 0 - 0 - 0 - - - 14457 - 1 - 0 - 0 - 0 - - - 14458 - 5 - 0 - 0 - 0 - - - 14459 - 1 - 0 - 0 - 0 - - - 14460 - 10 - 0 - 0 - 0 - - - 14461 - 1 - 0 - 0 - 0 - - - 14462 - 1 - 0 - 0 - 0 - - - 14463 - 1 - 0 - 0 - 0 - - - 14464 - 1 - 0 - 0 - 0 - - - 14465 - 1 - 0 - 0 - 0 - - - 14466 - 1 - 0 - 0 - 0 - - - 14467 - 1 - 0 - 0 - 0 - - - 14468 - 1 - 0 - 0 - 0 - - - 14469 - 1 - 0 - 0 - 0 - - - 14470 - 1 - 0 - 0 - 0 - - - 14471 - 1 - 0 - 0 - 0 - - - 14472 - 30000 - 0 - 0 - 0 - - - 14473 - 30000 - 0 - 0 - 0 - - - 14474 - 30000 - 0 - 0 - 0 - - - 14475 - 30000 - 0 - 0 - 0 - - - 14476 - 30000 - 0 - 0 - 0 - - - 14477 - 30000 - 0 - 0 - 0 - - - 14478 - 1 - 0 - 0 - 0 - - - 14479 - 1760000 - 0 - 0 - 0 - - - 14480 - 1760000 - 0 - 0 - 0 - - - 14481 - 1 - 0 - 0 - 0 - - - 14484 - 135000 - 0 - 0 - 0 - - - 14485 - 135000 - 0 - 0 - 0 - - - 14486 - 1 - 0 - 0 - 0 - - - 14487 - 1 - 0 - 0 - 0 - - - 14488 - 1 - 0 - 0 - 0 - - - 14489 - 1 - 0 - 0 - 0 - - - 14490 - 65000 - 0 - 0 - 0 - - - 14491 - 65000 - 0 - 0 - 0 - - - 14492 - 64000 - 0 - 0 - 0 - - - 14493 - 64000 - 0 - 0 - 0 - - - 14494 - 35200 - 0 - 0 - 0 - - - 14495 - 35200 - 0 - 0 - 0 - - - 14496 - 1 - 0 - 0 - 0 - - - 14497 - 120000 - 0 - 0 - 0 - - - 14498 - 120000 - 0 - 0 - 0 - - - 14499 - 15000 - 0 - 0 - 0 - - - 14500 - 15000 - 0 - 0 - 0 - - - 14501 - 80000 - 0 - 0 - 0 - - - 14502 - 80000 - 0 - 0 - 0 - - - 14503 - 1 - 0 - 0 - 0 - - - 14504 - 1 - 0 - 0 - 0 - - - 14505 - 1 - 0 - 0 - 0 - - - 14506 - 1 - 0 - 0 - 0 - - - 14507 - 1 - 0 - 0 - 0 - - - 14508 - 1 - 0 - 0 - 0 - - - 14509 - 1 - 0 - 0 - 0 - - - 14510 - 1 - 0 - 0 - 0 - - - 14511 - 1 - 0 - 0 - 0 - - - 14512 - 1 - 0 - 0 - 0 - - - 14513 - 1 - 0 - 0 - 0 - - - 14514 - 1 - 0 - 0 - 0 - - - 14515 - 1 - 0 - 0 - 0 - - - 14516 - 1 - 0 - 0 - 0 - - - 14517 - 1 - 0 - 0 - 0 - - - 14518 - 1 - 0 - 0 - 0 - - - 14519 - 1 - 0 - 0 - 0 - - - 14520 - 1 - 0 - 0 - 0 - - - 14521 - 1 - 0 - 0 - 0 - - - 14522 - 1 - 0 - 0 - 0 - - - 14523 - 1 - 0 - 0 - 0 - - - 14524 - 1 - 0 - 0 - 0 - - - 14525 - 20 - 0 - 0 - 0 - - - 14526 - 20 - 0 - 0 - 0 - - - 14527 - 20 - 0 - 0 - 0 - - - 14528 - 20 - 0 - 0 - 0 - - - 14529 - 20 - 0 - 0 - 0 - - - 14530 - 20 - 0 - 0 - 0 - - - 14531 - 20 - 0 - 0 - 0 - - - 14532 - 20 - 0 - 0 - 0 - - - 14533 - 1 - 0 - 0 - 0 - - - 14534 - 1 - 0 - 0 - 0 - - - 14535 - 1 - 0 - 0 - 0 - - - 14536 - 1 - 0 - 0 - 0 - - - 14537 - 1 - 0 - 0 - 0 - - - 14538 - 1 - 0 - 0 - 0 - - - 14539 - 9 - 0 - 0 - 0 - - - 14540 - 4 - 0 - 0 - 0 - - - 14541 - 1 - 0 - 0 - 0 - - - 14542 - 9 - 0 - 0 - 0 - - - 14543 - 4 - 0 - 0 - 0 - - - 14544 - 1 - 0 - 0 - 0 - - - 14545 - 1 - 0 - 0 - 0 - - - 14546 - 1 - 0 - 0 - 0 - - - 14547 - 1 - 0 - 0 - 0 - - - 14548 - 1 - 0 - 0 - 0 - - - 14549 - 1 - 0 - 0 - 0 - - - 14550 - 1 - 0 - 0 - 0 - - - 14551 - 1 - 0 - 0 - 0 - - - 14552 - 1 - 0 - 0 - 0 - - - 14553 - 1 - 0 - 0 - 0 - - - 14554 - 1 - 0 - 0 - 0 - - - 14555 - 1 - 0 - 0 - 0 - - - 14556 - 1 - 0 - 0 - 0 - - - 14557 - 1 - 0 - 0 - 0 - - - 14558 - 1 - 0 - 0 - 0 - - - 14559 - 1 - 0 - 0 - 0 - - - 14560 - 1 - 0 - 0 - 0 - - - 14561 - 1 - 0 - 0 - 0 - - - 14562 - 1 - 0 - 0 - 0 - - - 14563 - 1 - 0 - 0 - 0 - - - 14564 - 1 - 0 - 0 - 0 - - - 14565 - 1 - 0 - 0 - 0 - - - 14566 - 1 - 0 - 0 - 0 - - - 14567 - 1 - 0 - 0 - 0 - - - 14568 - 1 - 0 - 0 - 0 - - - 14569 - 1 - 0 - 0 - 0 - - - 14570 - 1 - 0 - 0 - 0 - - - 14571 - 1 - 0 - 0 - 0 - - - 14572 - 1 - 0 - 0 - 0 - - - 14573 - 1 - 0 - 0 - 0 - - - 14574 - 1 - 0 - 0 - 0 - - - 14575 - 1 - 0 - 0 - 0 - - - 14576 - 1 - 0 - 0 - 0 - - - 14577 - 100 - 0 - 0 - 0 - - - 14578 - 200 - 0 - 0 - 0 - - - 14579 - 300 - 0 - 0 - 0 - - - 14580 - 1000 - 0 - 0 - 0 - - - 14581 - 5000 - 0 - 0 - 0 - - - 14582 - 10000 - 0 - 0 - 0 - - - 14583 - 1 - 0 - 0 - 0 - - - 14584 - 500000 - 0 - 0 - 0 - - - 14585 - 1 - 0 - 0 - 0 - - - 14586 - 1 - 0 - 0 - 0 - - - 14587 - 1 - 0 - 0 - 0 - - - 14588 - 1 - 0 - 0 - 0 - - - 14589 - 1250 - 0 - 0 - 0 - - - 14590 - 1000 - 0 - 0 - 0 - - - 14591 - 1000 - 0 - 0 - 0 - - - 14592 - 1 - 0 - 0 - 0 - - - 14593 - 1 - 0 - 0 - 0 - - - 14594 - 1 - 0 - 0 - 0 - - - 14595 - 1 - 0 - 0 - 0 - - - 14596 - 1 - 0 - 0 - 0 - - - 14597 - 1 - 0 - 0 - 0 - - - 14598 - 1 - 0 - 0 - 0 - - - 14599 - 1 - 0 - 0 - 0 - - - 14600 - 1 - 0 - 0 - 0 - - - 14601 - 1 - 0 - 0 - 0 - - - 14602 - 1 - 0 - 0 - 0 - - - 14603 - 1 - 0 - 0 - 0 - - - 14604 - 1 - 0 - 0 - 0 - - - 14605 - 1 - 0 - 0 - 0 - - - 14606 - 1 - 0 - 0 - 0 - - - 14607 - 1 - 0 - 0 - 0 - - - 14608 - 1 - 0 - 0 - 0 - - - 14609 - 1 - 0 - 0 - 0 - - - 14610 - 1 - 0 - 0 - 0 - - - 14611 - 1 - 0 - 0 - 0 - - - 14612 - 1 - 0 - 0 - 0 - - - 14613 - 1 - 0 - 0 - 0 - - - 14614 - 1 - 0 - 0 - 0 - - - 14615 - 1 - 0 - 0 - 0 - - - 14616 - 100 - 0 - 0 - 0 - - - 14617 - 100 - 0 - 0 - 0 - - - 14618 - 1 - 0 - 0 - 0 - - - 14619 - 1 - 0 - 0 - 0 - - - 14620 - 1 - 0 - 0 - 0 - - - 14621 - 1 - 0 - 0 - 0 - - - 14622 - 498 - 0 - 0 - 0 - - - 14623 - 4986 - 0 - 0 - 0 - - - 14624 - 4986 - 0 - 0 - 0 - - - 14625 - 1 - 0 - 0 - 0 - - - 14626 - 1 - 0 - 0 - 0 - - - 14627 - 1 - 0 - 0 - 0 - - - 14628 - 1 - 0 - 0 - 0 - - - 14629 - 1 - 0 - 0 - 0 - - - 14630 - 1 - 0 - 0 - 0 - - - 14631 - 1 - 0 - 0 - 0 - - - 14632 - 200 - 0 - 0 - 0 - - - 14633 - 1 - 0 - 0 - 0 - - - 14634 - 1 - 0 - 0 - 0 - - - 14635 - 1 - 0 - 0 - 0 - - - 14636 - 650 - 0 - 0 - 0 - - - 14637 - 650 - 0 - 0 - 0 - - - 14638 - 17625 - 0 - 0 - 0 - - - 14639 - 17625 - 0 - 0 - 0 - - - 14640 - 17625 - 0 - 0 - 0 - - - 14641 - 17625 - 0 - 0 - 0 - - - 14642 - 17625 - 0 - 0 - 0 - - - 14643 - 17625 - 0 - 0 - 0 - - - 14644 - 17625 - 0 - 0 - 0 - - - 14645 - 17625 - 0 - 0 - 0 - - - 14646 - 17625 - 0 - 0 - 0 - - - 14647 - 17625 - 0 - 0 - 0 - - diff --git a/Server/db_exports/global.sql b/Server/db_exports/global.sql index 0b5e21b74..255acee9d 100644 --- a/Server/db_exports/global.sql +++ b/Server/db_exports/global.sql @@ -1,3 +1,7 @@ +CREATE DATABASE global; + +USE global; + -- phpMyAdmin SQL Dump -- version 5.0.2 -- https://www.phpmyadmin.net/ diff --git a/Server/db_exports/grafana-dashboard.json b/Server/db_exports/grafana-dashboard.json new file mode 100644 index 000000000..5d4b68c72 --- /dev/null +++ b/Server/db_exports/grafana-dashboard.json @@ -0,0 +1,539 @@ +{ + "__inputs": [ + { + "name": "DS_SQLITE", + "label": "SQLite", + "description": "", + "type": "datasource", + "pluginId": "frser-sqlite-datasource", + "pluginName": "SQLite" + } + ], + "__elements": {}, + "__requires": [ + { + "type": "datasource", + "id": "frser-sqlite-datasource", + "name": "SQLite", + "version": "3.3.0" + }, + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "10.0.1" + }, + { + "type": "panel", + "id": "timeseries", + "name": "Time series", + "version": "" + } + ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": null, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "frser-sqlite-datasource", + "uid": "${DS_SQLITE}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "misc_pulses" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "purple", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "other" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "dark-red", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "packet_incoming" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "super-light-red", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 9, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 1, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "10.0.1", + "targets": [ + { + "datasource": { + "type": "frser-sqlite-datasource", + "uid": "${DS_SQLITE}" + }, + "queryText": "SELECT * FROM tick_lengths", + "queryType": "table", + "rawQueryText": "SELECT * FROM tick_lengths", + "refId": "A", + "timeColumns": [ + "ts" + ] + } + ], + "title": "Tick Time", + "type": "timeseries" + }, + { + "datasource": { + "type": "frser-sqlite-datasource", + "uid": "${DS_SQLITE}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 13, + "w": 13, + "x": 0, + "y": 9 + }, + "id": 2, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "10.0.1", + "targets": [ + { + "datasource": { + "type": "frser-sqlite-datasource", + "uid": "${DS_SQLITE}" + }, + "queryText": "select * from top_pulses;", + "queryType": "table", + "rawQueryText": "select * from top_pulses;", + "refId": "A", + "timeColumns": [ + "time", + "ts" + ] + } + ], + "title": "Tick Time By Pulse (Total)", + "transformations": [ + { + "id": "extractFields", + "options": { + "format": "json", + "jsonPaths": [ + { + "path": "pulses" + } + ], + "keepTime": false, + "replace": false, + "source": "pulse_info" + } + }, + { + "id": "extractFields", + "options": { + "source": "pulses" + } + } + ], + "type": "timeseries" + }, + { + "datasource": { + "type": "frser-sqlite-datasource", + "uid": "${DS_SQLITE}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 11, + "x": 13, + "y": 9 + }, + "id": 4, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "frser-sqlite-datasource", + "uid": "${DS_SQLITE}" + }, + "queryText": "SELECT * FROM high_volume_pulses;", + "queryType": "table", + "rawQueryText": "SELECT * FROM high_volume_pulses;", + "refId": "A", + "timeColumns": [ + "time", + "ts" + ] + } + ], + "title": "High Volume Pulses", + "transformations": [ + { + "id": "extractFields", + "options": { + "format": "json", + "source": "pulse_info" + } + }, + { + "id": "extractFields", + "options": { + "source": "pulses" + } + } + ], + "type": "timeseries" + }, + { + "datasource": { + "type": "frser-sqlite-datasource", + "uid": "${DS_SQLITE}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "stepBefore", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 11, + "x": 13, + "y": 18 + }, + "id": 3, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "frser-sqlite-datasource", + "uid": "${DS_SQLITE}" + }, + "queryText": "SELECT * FROM bot_counts;", + "queryType": "table", + "rawQueryText": "SELECT * FROM bot_counts;", + "refId": "A", + "timeColumns": [ + "time", + "ts" + ] + } + ], + "title": "Bot Population", + "type": "timeseries" + } + ], + "refresh": "1h", + "schemaVersion": 38, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-5m", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Main", + "uid": "ffccf845-00a2-46f2-b0c0-a8b88032c701", + "version": 10, + "weekStart": "" +} \ No newline at end of file diff --git a/Server/db_exports/testuser.sql b/Server/db_exports/testuser.sql new file mode 100644 index 000000000..d9d752183 --- /dev/null +++ b/Server/db_exports/testuser.sql @@ -0,0 +1,2 @@ +CREATE USER IF NOT EXISTS 2009scapetest@localhost IDENTIFIED BY '2009scapetest'; +GRANT ALL PRIVILEGES ON global.* TO 2009scapetest@localhost; \ No newline at end of file diff --git a/Server/detekt.yml b/Server/detekt.yml index 8895d520d..706aec911 100644 --- a/Server/detekt.yml +++ b/Server/detekt.yml @@ -285,7 +285,7 @@ exceptions: - 'Throwable' allowedExceptionNameRegex: '_|(ignore|expected).*' TooGenericExceptionThrown: - active: true + active: false exceptionNames: - 'Error' - 'Exception' @@ -437,7 +437,7 @@ potential-bugs: - '*.CanIgnoreReturnValue' ignoreFunctionCall: [] ImplicitDefaultLocale: - active: true + active: false ImplicitUnitReturnType: active: false allowExplicitReturnType: true @@ -609,7 +609,7 @@ style: ObjectLiteralToLambda: active: true OptionalAbstractKeyword: - active: true + active: false OptionalUnit: active: false OptionalWhenBraces: @@ -696,9 +696,9 @@ style: UseOrEmpty: active: true UseRequire: - active: true + active: false UseRequireNotNull: - active: true + active: false UselessCallOnNotNull: active: true UtilityClassWithPublicConstructor: diff --git a/Server/pom.xml b/Server/pom.xml index 8adac8e6e..f3cc5f0ac 100644 --- a/Server/pom.xml +++ b/Server/pom.xml @@ -7,8 +7,8 @@ core.Server true - 1.6.21 - 1.6 + 1.8.20 + 11 5.7.0 11 11 @@ -16,7 +16,7 @@ org.jetbrains.kotlin - kotlin-stdlib-jdk8 + kotlin-stdlib ${kotlin.version} compile @@ -195,7 +195,7 @@ maven-compiler-plugin 3.10.1 - 1024m + 2048m false -XDcompilePolicy=simple diff --git a/Server/src/main/content/data/BossKillCounter.java b/Server/src/main/content/data/BossKillCounter.java index 3759a5951..e6d865600 100644 --- a/Server/src/main/content/data/BossKillCounter.java +++ b/Server/src/main/content/data/BossKillCounter.java @@ -30,11 +30,6 @@ public enum BossKillCounter { 8355, 8356, 8357, 8358, 8359, 8360, 8361, 8362, 8363, 8364, 8365, 8366, }, "Tormented demon", -1), - CALLISTO(new int[] { 8610 }, "Callisto", 14658), - SCORPIA(new int[] { 8611 }, "Scorpia", 14661), - VENENATIS(new int[] { 8612 }, "Venenatis", 14657), - VETION(new int[] { 8613 }, "Vet'ion", 14659), - KRAKEN(new int[] { 8614 }, "Cave Kraken", 14651), ; diff --git a/Server/src/main/content/data/ChargedItem.kt b/Server/src/main/content/data/ChargedItem.kt new file mode 100644 index 000000000..06f3d4093 --- /dev/null +++ b/Server/src/main/content/data/ChargedItem.kt @@ -0,0 +1,80 @@ +package content.data + +import core.api.toIntArray +import core.cache.def.impl.ItemDefinition +import org.rs09.consts.Items + +/** + * Represents a distinct charged item, i.e. items with (#). + * Not to be confused with items with internal charge, like degradation. + * @author RiL + */ +enum class ChargedItem(val ids: IntArray) { + AMULET_OF_GLORY((Items.AMULET_OF_GLORY4_1712 downTo Items.AMULET_OF_GLORY_1704 step 2).toIntArray()), + RING_OF_DUELLING((Items.RING_OF_DUELLING8_2552..Items.RING_OF_DUELLING1_2566 step 2).toIntArray()), + GAMES_NECKLACE((Items.GAMES_NECKLACE8_3853..Items.GAMES_NECKLACE1_3867 step 2).toIntArray()), + BROODOO_SHIELDA((Items.BROODOO_SHIELD_10_6215..Items.BROODOO_SHIELD_6235 step 2).toIntArray()), + BROODOO_SHIELDB((Items.BROODOO_SHIELD_10_6237..Items.BROODOO_SHIELD_6257 step 2).toIntArray()), + BROODOO_SHIELDC((Items.BROODOO_SHIELD_10_6259..Items.BROODOO_SHIELD_6279 step 2).toIntArray()), + ROD_OF_IVANDIS((Items.ROD_OF_IVANDIS10_7639..Items.ROD_OF_IVANDIS1_7648).toIntArray()), + BLACK_MASK((Items.BLACK_MASK_10_8901..Items.BLACK_MASK_8921 step 2).toIntArray()), + AMULET_OF_GLORYT((Items.AMULET_OF_GLORYT4_10354..Items.AMULET_OF_GLORYT_10362 step 2).toIntArray()), + CASTLEWAR_BRACE((Items.CASTLEWAR_BRACE3_11079..Items.CASTLEWAR_BRACE1_11083 step 2).toIntArray()), + FORINTHRY_BRACE((Items.FORINTHRY_BRACE5_11095..Items.FORINTHRY_BRACE1_11103 step 2).toIntArray()), + SKILLS_NECKLACE((Items.SKILLS_NECKLACE4_11105..Items.SKILLS_NECKLACE_11113 step 2).toIntArray()), + COMBAT_BRACELET((Items.COMBAT_BRACELET4_11118..Items.COMBAT_BRACELET_11126 step 2).toIntArray()), + DIGSITE_PENDANT((Items.DIGSITE_PENDANT_5_11194 downTo Items.DIGSITE_PENDANT_1_11190).toIntArray()), + VOID_SEAL((Items.VOID_SEAL8_11666..Items.VOID_SEAL1_11673).toIntArray()), + AMULET_OF_FARMING((Items.AMULET_OF_FARMING8_12622 downTo Items.AMULET_OF_FARMING1_12608 step 2).toIntArray()), + IVANDIS_FLAIL((Items.IVANDIS_FLAIL_30_13117..Items.IVANDIS_FLAIL_1_13146).toIntArray()), + RING_OF_SLAYING((Items.RING_OF_SLAYING8_13281..Items.RING_OF_SLAYING1_13288).toIntArray()), + RING_OF_WEALTH((Items.RING_OF_WEALTH4_14646 downTo Items.RING_OF_WEALTH_14638 step 2).toIntArray()); + + /** + * Get the item id for a specific [charge]. + * If [charge] is outside the valid range, return the item id with the closest charge. + */ + fun forCharge(charge: Int): Int { + return ids[maxCharge() - if (charge < 1) 1 + maxCharge() - ids.size else if (charge > maxCharge()) maxCharge() else charge] + } + + /** + * Get the max charge of this ChargedItem. + */ + fun maxCharge(): Int = maxCharges[ordinal] + + companion object { + private val CHARGE_REGEX = Regex("""\(\D?(\d+)\)""") + private val idMap = HashMap() + private val maxCharges = IntArray(values().size) + + init { + values().forEach { chargedItem -> + maxCharges[chargedItem.ordinal] = getMaxCharge(chargedItem) + chargedItem.ids.forEach { idMap[it] = chargedItem } + } + } + + private fun getMaxCharge(chargedItem: ChargedItem): Int { + return CHARGE_REGEX.find(ItemDefinition.forId(chargedItem.ids.first()).name)!!.groups[1]!!.value.toInt() + } + + /** + * Check if the item id is a charged item. + */ + fun contains(id: Int): Boolean = idMap.containsKey(id) + + /** + * Get the ChargedItem enum for an item id, or null if it's not a charged item. + */ + fun forId(id: Int): ChargedItem? = idMap[id] + + /** + * Get the charge value of an item id, or null if it's not a charged item. + */ + fun getCharge(id: Int): Int? { + val chargedItem = forId(id) ?: return null + return chargedItem.maxCharge() - chargedItem.ids.indexOf(id) + } + } +} diff --git a/Server/src/main/content/data/EnchantedJewellery.kt b/Server/src/main/content/data/EnchantedJewellery.kt index e44cd32fa..c34a0ffb6 100644 --- a/Server/src/main/content/data/EnchantedJewellery.kt +++ b/Server/src/main/content/data/EnchantedJewellery.kt @@ -2,11 +2,13 @@ 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.entity.player.link.audio.Audio import core.game.node.item.Item import core.game.system.task.Pulse import core.game.world.map.Location @@ -14,11 +16,12 @@ import core.game.world.update.flag.context.Animation import core.game.world.update.flag.context.Graphics import org.rs09.consts.Items import core.game.world.GameWorld.Pulser +import org.rs09.consts.Sounds import java.util.* /** * Represents an enchanted jewellery. - * @author Vexia & downthecrop + * @author Vexia, downthecrop, Player Name */ enum class EnchantedJewellery( @@ -70,10 +73,10 @@ enum class EnchantedJewellery( ), AMULET_OF_GLORY( arrayOf( - "Edgeville", - "Karamja", - "Draynor Village", - "Al-Kharid", + "Edgeville.", + "Karamja.", + "Draynor Village.", + "Al-Kharid.", "Nowhere." ), arrayOf( @@ -103,7 +106,7 @@ enum class EnchantedJewellery( "Barbarian Outpost.", "Clan Wars.", "Wilderness Volcano.", - "Corporeal Beast" + "Corporeal Beast." ), arrayOf( Location.create(2899, 3563, 0), @@ -135,10 +138,10 @@ enum class EnchantedJewellery( ), COMBAT_BRACELET( arrayOf( - "Champions' Guild", - "Monastery", - "Ranging Guild", - "Warriors' Guild", + "Champions' Guild.", + "Monastery.", + "Ranging Guild.", + "Warriors' Guild.", "Nowhere." ), arrayOf( @@ -155,10 +158,10 @@ enum class EnchantedJewellery( ), SKILLS_NECKLACE( arrayOf( - "Fishing Guild", - "Mining Guild", - "Crafting Guild", - "Cooking Guild", + "Fishing Guild.", + "Mining Guild.", + "Crafting Guild.", + "Cooking Guild.", "Nowhere." ), arrayOf( @@ -175,7 +178,7 @@ enum class EnchantedJewellery( ), RING_OF_WEALTH( arrayOf( - "Grand Exchange", + "Grand Exchange.", "Nowhere." ), arrayOf( @@ -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,38 +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) - playAudio(player, AUDIO, true) - player.impactHandler.disabledTicks = 4 - } - 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) { @@ -261,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) + } } } @@ -274,14 +301,14 @@ 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 - setVarbit(player, 2502, 0, slayerManager.flags.taskFlags shr 4) + setVarp(player, 2502, slayerManager.flags.taskFlags shr 4) } private fun canTeleport(player: Player, item: Item): Boolean { @@ -325,7 +352,6 @@ enum class EnchantedJewellery( companion object { private val ANIMATION = Animation(714) - private val AUDIO = Audio(200) private val GRAPHICS = Graphics(308, 100, 50) val idMap = HashMap() @@ -335,4 +361,4 @@ enum class EnchantedJewellery( } } } -} \ No newline at end of file +} diff --git a/Server/src/main/content/data/GodBook.java b/Server/src/main/content/data/GodBook.java index 37e0adae8..92029b479 100644 --- a/Server/src/main/content/data/GodBook.java +++ b/Server/src/main/content/data/GodBook.java @@ -3,6 +3,8 @@ package content.data; import core.game.node.entity.player.Player; import core.game.node.item.Item; +import static core.api.ContentAPIKt.hasRequirement; + /** * A god book. * @author Vexia @@ -98,6 +100,8 @@ public enum GodBook { * @param page the page. */ public void insertPage(Player player, Item book, Item page) { + if (!hasRequirement(player, Quests.HORROR_FROM_THE_DEEP)) + return; if (hasPage(player, book, page)) { player.sendMessage("The book already has that page."); return; diff --git a/Server/src/main/content/data/Lamps.java b/Server/src/main/content/data/Lamps.java index e4631c78b..9dc150bf1 100644 --- a/Server/src/main/content/data/Lamps.java +++ b/Server/src/main/content/data/Lamps.java @@ -22,6 +22,8 @@ public enum Lamps { L_ACHIEVEMENT_2(new Item(11186), 1000, 30), L_ACHIEVEMENT_3(new Item(11187), 1500, 35), + MYSTERIOUS_LAMP(new Item(13227), 10000, 30), + FALLY_ACHIEVEMENT_1(new Item(14580), 1000, 30), FALLY_ACHIEVEMENT_2(new Item(14581), 5000, 40), FALLY_ACHIEVEMENT_3(new Item(14582), 10000, 50), 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/consumables/Consumables.java b/Server/src/main/content/data/consumables/Consumables.java index 40c52e87e..82c6650a1 100644 --- a/Server/src/main/content/data/consumables/Consumables.java +++ b/Server/src/main/content/data/consumables/Consumables.java @@ -2,15 +2,17 @@ package content.data.consumables; import content.data.consumables.effects.*; import core.game.consumable.*; -import org.rs09.consts.Items; import core.game.node.entity.player.link.diary.DiaryType; -import core.game.node.entity.state.EntityState; -import core.game.world.update.flag.context.Animation; import core.game.node.entity.skill.Skills; -import content.data.consumables.effects.KegOfBeerEffect; +import core.game.world.update.flag.context.Animation; +import org.rs09.consts.Items; +import java.util.ArrayList; import java.util.HashMap; +import static core.tools.TickUtilsKt.minutesToTicks; +import static core.tools.TickUtilsKt.secondsToTicks; + /** * Represents a repository of active consumables in the framework. */ @@ -28,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))), + 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 */ @@ -108,8 +110,8 @@ public enum Consumables { MINT_CAKE(new Food(new int[] {9475}, new EnergyEffect(50))), /** Vegetables */ - POTATO(new Food(new int[] {1942}, new HealingEffect(1))), - BAKED_POTATO(new Food(new int[] {6701}, new HealingEffect(2))), + POTATO(new Food(new int[] {1942}, new HealingEffect(1), "You eat the potato. Yuck!")), + 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))), @@ -126,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))), @@ -145,16 +147,16 @@ 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))), PINEAPPLE_RING(new Food(new int[] {2118}, new HealingEffect(2))), DWELLBERRIES(new Food(new int[] {2126}, new HealingEffect(2))), - JANGERBERRIES(new Food(new int[] {247}, new MultiEffect(new SkillEffect(Skills.ATTACK, 2, 0), new SkillEffect(Skills.STRENGTH, 1, 0), new PrayerEffect(1, 0), new SkillEffect(Skills.DEFENCE, -1, 0)))), + JANGERBERRIES(new Food(new int[] {247}, new MultiEffect(new SkillEffect(Skills.ATTACK, 2, 0), new SkillEffect(Skills.STRENGTH, 1, 0), new PrayerEffect(1, 0), new SkillEffect(Skills.DEFENCE, -1, 0)), "You eat the jangerberries.", "They taste very bitter.")), STRAWBERRY(new Food(new int[] {5504}, new MultiEffect(new HealingEffect(1), new PercentageHealthEffect(6)))), TOMATO(new Food(new int[] {1982}, new HealingEffect(2))), - WATERMELON(new FakeConsumable(5982, new String[] {"Try using a knife to slice it into pieces."})), + WATERMELON(new FakeConsumable(5982, new String[] {"You can't eat it whole; maybe you should cut it up."})), WATERMELON_SLICE(new Food(new int[] {5984}, new PercentageHealthEffect(5))), LEMON(new Food(new int[] {2102}, new HealingEffect(2))), LEMON_CHUNKS(new Food(new int[] {2104}, new HealingEffect(2))), @@ -164,11 +166,11 @@ public enum Consumables { LIME_SLICES(new Food(new int[] {2124}, new HealingEffect(2))), PEACH(new Food(new int[] {6883}, new HealingEffect(8))), WHITE_TREE_FRUIT(new Food(new int[] {6469}, new MultiEffect(new RandomEnergyEffect(5, 10), new HealingEffect(3)))), - STRANGE_FRUIT(new Food(new int[] {464}, new MultiEffect(new RemoveStateEffect(EntityState.POISONED.ordinal()), new EnergyEffect(30)))), + 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))), @@ -244,40 +246,48 @@ public enum Consumables { /** Cocktails */ FRUIT_BLAST(new Drink(new int[] {2084, 2026}, new HealingEffect(9))), - PREMADE_FR_BLAST(new Food(new int[] {2034, 2026}, new HealingEffect(9))), + PREMADE_FR_BLAST(new Drink(new int[] {2034, 2026}, new HealingEffect(9))), + CRAFTED_FR_BLAST(new Drink(new int[] {9514, 2026}, new HealingEffect(9))), PINEAPPLE_PUNCH(new Drink(new int[] {2048, 2026}, new HealingEffect(9), "You drink the cocktail. It tastes great.")), PREMADE_P_PUNCH(new Drink(new int[] {2036, 2026}, new HealingEffect(9), "You drink the cocktail. It tastes great.")), + CRAFTED_P_PUNCH(new Drink(new int[] {9512, 2026}, new HealingEffect(9))), WIZARD_BLIZZARD(new Drink(new int[] {2054, 2026}, new MultiEffect(new HealingEffect(5), new SkillEffect(Skills.STRENGTH, 6, 0), new SkillEffect(Skills.ATTACK, -4, 0)))), PREMADE_WIZ_BLZD(new Drink(new int[] {2040, 2026}, new MultiEffect(new HealingEffect(5), new SkillEffect(Skills.STRENGTH, 6, 0), new SkillEffect(Skills.ATTACK, -4, 0)))), + CRAFTED_WIZ_BLZD(new Drink(new int[]{9508, 2026}, new MultiEffect(new HealingEffect(5), new SkillEffect(Skills.STRENGTH, 6, 0),new SkillEffect(Skills.ATTACK, -4, 0 )))), SHORT_GREEN_GUY(new Drink(new int[] {2080, 2026}, new MultiEffect(new HealingEffect(5), new SkillEffect(Skills.STRENGTH, 4, 0), new SkillEffect(Skills.ATTACK, -3, 0)))), PREMADE_SGG(new Drink(new int[] {2038, 2026}, new MultiEffect(new HealingEffect(5), new SkillEffect(Skills.STRENGTH, 4, 0), new SkillEffect(Skills.ATTACK, -3, 0)))), + CRAFTED_SGG(new Drink(new int[]{9510, 2026}, new MultiEffect(new HealingEffect(5), new SkillEffect(Skills.STRENGTH, 4, 0), new SkillEffect(Skills.ATTACK, -3,0)))), DRUNK_DRAGON(new Drink(new int[] {2092, 2026}, new MultiEffect(new HealingEffect(5), new SkillEffect(Skills.STRENGTH, 7, 0), new SkillEffect(Skills.ATTACK, -4, 0)))), PREMADE_DR_DRAGON(new Drink(new int[] {2032, 2026}, new MultiEffect(new HealingEffect(5), new SkillEffect(Skills.STRENGTH, 7, 0), new SkillEffect(Skills.ATTACK, -4, 0)))), + CRAFTED_DR_DRAGON(new Drink(new int[] {9516, 2026}, new MultiEffect(new HealingEffect(5), new SkillEffect(Skills.STRENGTH, 7, 0), new SkillEffect(Skills.ATTACK, -4,0)))), CHOC_SATURDAY(new Drink(new int[] {2074, 2026}, new MultiEffect(new HealingEffect(5), new SkillEffect(Skills.STRENGTH, 7, 0), new SkillEffect(Skills.ATTACK, -4, 0)))), PREMADE_CHOC_SDY(new Drink(new int[] {2030, 2026}, new MultiEffect(new HealingEffect(5), new SkillEffect(Skills.STRENGTH, 7, 0), new SkillEffect(Skills.ATTACK, -4, 0)))), + CRAFTED_CHOC_SDY(new Drink(new int[] {9518, 2026}, new MultiEffect(new HealingEffect(5), new SkillEffect(Skills.STRENGTH, 7,0), new SkillEffect(Skills.ATTACK, -4,0)))), BLURBERRY_SPECIAL(new Drink(new int[] {2064, 2026}, new MultiEffect(new HealingEffect(6), new SkillEffect(Skills.STRENGTH, 6, 0), new SkillEffect(Skills.ATTACK, -4, 0)))), PREMADE_BLURB_SP(new Drink(new int[] {2028, 2026}, new MultiEffect(new HealingEffect(6), new SkillEffect(Skills.STRENGTH, 6, 0), new SkillEffect(Skills.ATTACK, -4, 0)))), + CRAFTED_BLURB_SP(new Drink(new int[] {9520, 2026}, new MultiEffect(new HealingEffect(6), new SkillEffect(Skills.STRENGTH,6,0), new SkillEffect(Skills.ATTACK, -4,0)))), /** Bottled Drinks */ KARAMJAN_RUM(new Drink(new int[] {431}, new MultiEffect(new HealingEffect(5), new SkillEffect(Skills.STRENGTH, 5, 0), new SkillEffect(Skills.ATTACK, -4, 0)))), 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)))), HALF_FULL_WINE_JUG(new Drink(new int[] {1989, 1935}, new HealingEffect(7))), + JUG_OF_BAD_WINE(new Drink(new int[] {1991,1935}, new SkillEffect(Skills.ATTACK, -3, 0))), /** Tea */ CUP_OF_TEA(new Drink(new int[] {712, 1980}, new MultiEffect(new HealingEffect(3), new SkillEffect(Skills.ATTACK, 3, 0)), "Aaah, nothing like a nice cuppa tea!")), CUP_OF_TEA_NETTLE(new Drink(new int[] {4242, 1980}, new EnergyEffect(10))), CUP_OF_TEA_MILKY_NETTLE(new Drink(new int[] {4243, 1980}, new EnergyEffect(10))), - NETTLE_TEA(new Drink(new int[] {4239, 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))), @@ -297,8 +307,9 @@ 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.")), /** Special Events */ PUMPKIN(new Food(new int[] {1959}, new HealingEffect(14))), @@ -308,43 +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))), - ANTIPOISON(new Potion(new int[] {2446, 175, 177, 179}, new MultiEffect(new SetAttributeEffect("poison:immunity", 143), new RemoveStateEffect(EntityState.POISONED.ordinal())))), - ANTIPOISON_(new Potion(new int[] {5943, 5945, 5947, 5949}, new MultiEffect(new SetAttributeEffect("poison:immunity", 863), new RemoveStateEffect(EntityState.POISONED.ordinal())))), - ANTIPOISON__(new Potion(new int[] {5952, 5954, 5956, 5958}, new MultiEffect(new SetAttributeEffect("poison:immunity", 2000), new RemoveStateEffect(EntityState.POISONED.ordinal())))), - SUPER_ANTIP(new Potion(new int[] {2448, 181, 183, 185}, new MultiEffect(new SetAttributeEffect("poison:immunity", 1000), new RemoveStateEffect(EntityState.POISONED.ordinal())))), - RELICYM(new Potion(new int[] {4842, 4844, 4846, 4848}, new MultiEffect(new SetAttributeEffect("disease:immunity", 300), new RemoveStateEffect("disease")))), + 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 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))), - GUTH_REST(new Potion(new int[] {4417, 4419, 4421, 4423}, new MultiEffect(new RemoveStateEffect(EntityState.POISONED.ordinal()), new EnergyEffect(5), new HealingEffect(5)))), + 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 RemoveStateEffect(EntityState.POISONED.ordinal()), new RemoveStateEffect("disease")))), + 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 RemoveStateEffect(EntityState.POISONED.ordinal()), new SetAttributeEffect("poison:immunity", 143), new HealingEffect(3)))), - RELIC_MIX(new BarbarianMix(new int[] {11437, 11439}, new MultiEffect(new RemoveStateEffect("disease"), new SetAttributeEffect("disease:immunity", 300), 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 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)))), - ENERGY_MIX(new BarbarianMix(new int[] {11453, 11455}, new MultiEffect(new EnergyEffect(10), new HealingEffect(6)))), + 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(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)))), @@ -353,6 +365,9 @@ 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)))), + ANTIFIRE_MIX(new BarbarianMix(new int[] {11505, 11507}, new MultiEffect(new AddTimerEffect("dragonfire:immunity", 600, true), new RandomHealthEffect(3, 7)))), /** Stealing creation potions */ SC_PRAYER(new Potion(new int[] {14207, 14209, 14211, 14213, 14215}, new PrayerEffect(7, 0.25))), @@ -364,24 +379,28 @@ public enum Consumables { SC_MAGIC(new Potion(new int[] {14267, 14269, 14271, 14273, 14275}, new SkillEffect(Skills.MAGIC, 3, 0.1))), SC_SUMMONING(new Potion(new int[] {14277, 14279, 14281, 14283, 14285}, new SummoningEffect(7, 0.25))); - public static HashMap consumables = new HashMap<>(); + public static HashMap consumables = new HashMap<>(); + + public static ArrayList potions = new ArrayList<>(); private final Consumable consumable; + public boolean isIgnoreMainClock = false; Consumables(Consumable consumable) { this.consumable = consumable; } + Consumables(Consumable consumable, boolean isIgnoreMainClock) {this.consumable = consumable; this.isIgnoreMainClock = isIgnoreMainClock;} public Consumable getConsumable() { return consumable; } - public static Consumable getConsumableById(final int itemId) { + public static Consumables getConsumableById(final int itemId) { return consumables.get(itemId); } - public static void add(final Consumable consumable) { - for (int id : consumable.getIds()) { + public static void add(final Consumables consumable) { + for (int id : consumable.consumable.getIds()) { consumables.putIfAbsent(id, consumable); } } @@ -391,7 +410,12 @@ public enum Consumables { */ static { for (Consumables consumable : Consumables.values()) { - add(consumable.consumable); + add(consumable); + if (consumable.consumable instanceof Potion) { + for (int pot : consumable.consumable.getIds()) { + potions.add(pot); + } + } } } } diff --git a/Server/src/main/content/data/consumables/effects/AddTimerEffect.kt b/Server/src/main/content/data/consumables/effects/AddTimerEffect.kt new file mode 100644 index 000000000..d21985101 --- /dev/null +++ b/Server/src/main/content/data/consumables/effects/AddTimerEffect.kt @@ -0,0 +1,15 @@ +package content.data.consumables.effects + +import core.api.registerTimer +import core.api.removeTimer +import core.api.spawnTimer +import core.game.consumable.ConsumableEffect +import core.game.node.entity.player.Player + +class AddTimerEffect (val identifier: String, vararg val args: Any) : ConsumableEffect() { + override fun activate (p: Player) { + removeTimer (p, identifier) + val timer = spawnTimer (identifier, *args) ?: return + registerTimer (p, timer) + } +} 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/PrayerEffect.java b/Server/src/main/content/data/consumables/effects/PrayerEffect.java deleted file mode 100644 index 65d5516aa..000000000 --- a/Server/src/main/content/data/consumables/effects/PrayerEffect.java +++ /dev/null @@ -1,19 +0,0 @@ -package content.data.consumables.effects; - -import core.game.consumable.ConsumableEffect; -import core.game.node.entity.player.Player; -import core.game.node.entity.skill.Skills; - -public class PrayerEffect extends ConsumableEffect { - double base, bonus; - public PrayerEffect(double base, double bonus){ - this.base = base; - this.bonus = bonus; - } - @Override - public void activate(Player p) { - int level = p.getSkills().getStaticLevel(Skills.PRAYER); - double amt = base + (level * bonus); - p.getSkills().incrementPrayerPoints(amt); - } -} diff --git a/Server/src/main/content/data/consumables/effects/PrayerEffect.kt b/Server/src/main/content/data/consumables/effects/PrayerEffect.kt new file mode 100644 index 000000000..9f636bc27 --- /dev/null +++ b/Server/src/main/content/data/consumables/effects/PrayerEffect.kt @@ -0,0 +1,20 @@ +package content.data.consumables.effects + +import core.api.* +import core.game.consumable.ConsumableEffect +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import org.rs09.consts.Items +import kotlin.math.floor + +class PrayerEffect(var base: Double, var bonus: Double) : ConsumableEffect() { + override fun activate(player: Player?) { + if(player == null) return + val level = getStatLevel(player, Skills.PRAYER) + var b = bonus + if(inInventory(player, Items.HOLY_WRENCH_6714)) + b += 0.02 // Leaving this in for futureproofing. Current update is for properly rounding down. + val amount = floor(base + (level * b)) + modPrayerPoints(player, amount) + } +} \ No newline at end of file diff --git a/Server/src/main/content/data/consumables/effects/RemoveStateEffect.java b/Server/src/main/content/data/consumables/effects/RemoveStateEffect.java deleted file mode 100644 index 7c2d5040a..000000000 --- a/Server/src/main/content/data/consumables/effects/RemoveStateEffect.java +++ /dev/null @@ -1,25 +0,0 @@ -package content.data.consumables.effects; - -import core.game.consumable.ConsumableEffect; -import core.game.node.entity.player.Player; -import core.game.node.entity.state.EntityState; - -public class RemoveStateEffect extends ConsumableEffect { - int state = -1; - String statekey; - @Deprecated - public RemoveStateEffect(int state){ - this.state = state; - } - public RemoveStateEffect(String key){ - statekey = key; - } - @Override - public void activate(Player p) { - if(state == -1){ - p.clearState(statekey); - return; - } - p.getStateManager().remove(EntityState.values()[state]); - } -} diff --git a/Server/src/main/content/data/consumables/effects/RemoveTimerEffect.kt b/Server/src/main/content/data/consumables/effects/RemoveTimerEffect.kt new file mode 100644 index 000000000..6d45c35e9 --- /dev/null +++ b/Server/src/main/content/data/consumables/effects/RemoveTimerEffect.kt @@ -0,0 +1,11 @@ +package content.data.consumables.effects + +import core.api.* +import core.game.consumable.ConsumableEffect +import core.game.node.entity.player.Player + +class RemoveTimerEffect (val identifier: String) : ConsumableEffect() { + override fun activate (p: Player) { + removeTimer (p, identifier) + } +} 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/tables/AllotmentSeedDropTable.java b/Server/src/main/content/data/tables/AllotmentSeedDropTable.java index 4523be955..01b6f3f17 100644 --- a/Server/src/main/content/data/tables/AllotmentSeedDropTable.java +++ b/Server/src/main/content/data/tables/AllotmentSeedDropTable.java @@ -3,13 +3,13 @@ package content.data.tables; import core.api.StartupListener; import core.game.node.item.Item; import core.game.node.item.WeightedChanceItem; +import core.tools.Log; import core.tools.RandomFunction; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import core.ServerConstants; -import core.tools.SystemLogger; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; @@ -18,7 +18,7 @@ import java.io.File; import java.util.ArrayList; import java.util.List; -import static core.tools.SystemLogger.logInfo; +import static core.api.ContentAPIKt.log; /** * Handles the allotment seed drop table. @@ -57,11 +57,11 @@ public final class AllotmentSeedDropTable implements StartupListener { @Override public void startup() { if(ServerConstants.ASDT_DATA_PATH != null && !new File(ServerConstants.ASDT_DATA_PATH).exists()){ - SystemLogger.logErr(this.getClass(), "Can't locate ASDT file at " + ServerConstants.ASDT_DATA_PATH); + log(this.getClass(), Log.ERR, "Can't locate ASDT file at " + ServerConstants.ASDT_DATA_PATH); return; } parse(ServerConstants.ASDT_DATA_PATH); - logInfo(this.getClass(), "Initialized Allotment Seed Drop Table from " + ServerConstants.ASDT_DATA_PATH); + log(this.getClass(), Log.FINE, "Initialized Allotment Seed Drop Table from " + ServerConstants.ASDT_DATA_PATH); } /** diff --git a/Server/src/main/content/data/tables/BirdNest.java b/Server/src/main/content/data/tables/BirdNest.java index 929ca3adc..0552daa2e 100644 --- a/Server/src/main/content/data/tables/BirdNest.java +++ b/Server/src/main/content/data/tables/BirdNest.java @@ -8,6 +8,9 @@ import core.game.node.item.GroundItemManager; import core.game.node.item.Item; import core.tools.RandomFunction; import core.tools.StringUtils; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.playAudio; /** * Represents a birds nest. @@ -93,7 +96,7 @@ public enum BirdNest { */ public static void drop(final Player player) { final BirdNest nest = getRandomNest(false); - player.getAudioManager().send(1997); + playAudio(player, Sounds.CUCKOO_1_1997); GroundItemManager.create(nest.getNest(), player); player.getPacketDispatch().sendMessage("A bird's nest falls out of the tree."); } diff --git a/Server/src/main/content/data/tables/CELEMinorTable.java b/Server/src/main/content/data/tables/CELEMinorTable.java index bb19629f7..13313bc59 100644 --- a/Server/src/main/content/data/tables/CELEMinorTable.java +++ b/Server/src/main/content/data/tables/CELEMinorTable.java @@ -4,7 +4,7 @@ import core.api.StartupListener; import core.ServerConstants; import core.game.node.item.Item; import core.game.node.item.WeightedChanceItem; -import core.tools.SystemLogger; +import core.tools.Log; import core.tools.RandomFunction; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -18,7 +18,7 @@ import java.io.File; import java.util.ArrayList; import java.util.List; -import static core.tools.SystemLogger.logInfo; +import static core.api.ContentAPIKt.log; /** * Handles the Chaos Elemental's minor drop table. It is supposed to roll this table alongside its standard major drops on the main table. @@ -57,11 +57,11 @@ public final class CELEMinorTable implements StartupListener { @Override public void startup() { if(ServerConstants.CELEDT_DATA_PATH != null && !new File(ServerConstants.CELEDT_DATA_PATH).exists()){ - SystemLogger.logErr(this.getClass(), "Can't locate CELEDT file at " + ServerConstants.CELEDT_DATA_PATH); + log(this.getClass(), Log.ERR, "Can't locate CELEDT file at " + ServerConstants.CELEDT_DATA_PATH); return; } parse(ServerConstants.CELEDT_DATA_PATH); - logInfo(this.getClass(), "Loaded up Chaos Elemental drop table from " + ServerConstants.CELEDT_DATA_PATH); + log(this.getClass(), Log.FINE, "Loaded up Chaos Elemental drop table from " + ServerConstants.CELEDT_DATA_PATH); } /** diff --git a/Server/src/main/content/data/tables/GemDropTable.java b/Server/src/main/content/data/tables/GemDropTable.java index 8ec051d64..e9b486d28 100644 --- a/Server/src/main/content/data/tables/GemDropTable.java +++ b/Server/src/main/content/data/tables/GemDropTable.java @@ -3,13 +3,13 @@ package content.data.tables; import core.api.StartupListener; import core.game.node.item.Item; import core.game.node.item.WeightedChanceItem; +import core.tools.Log; import core.tools.RandomFunction; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import core.ServerConstants; -import core.tools.SystemLogger; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; @@ -18,7 +18,7 @@ import java.io.File; import java.util.ArrayList; import java.util.List; -import static core.tools.SystemLogger.logInfo; +import static core.api.ContentAPIKt.log; /** * Handles the gem drop table. @@ -57,11 +57,11 @@ public final class GemDropTable implements StartupListener { @Override public void startup() { if(ServerConstants.GDT_DATA_PATH != null && !new File(ServerConstants.GDT_DATA_PATH).exists()){ - SystemLogger.logErr(this.getClass(), "Can't locate GDT file at " + ServerConstants.GDT_DATA_PATH); + log(this.getClass(), Log.ERR, "Can't locate GDT file at " + ServerConstants.GDT_DATA_PATH); return; } parse(ServerConstants.GDT_DATA_PATH); - logInfo(this.getClass(), "Initialized Gem Drop Table from " + ServerConstants.GDT_DATA_PATH); + log(this.getClass(), Log.FINE, "Initialized Gem Drop Table from " + ServerConstants.GDT_DATA_PATH); } /** diff --git a/Server/src/main/content/data/tables/HerbDropTable.java b/Server/src/main/content/data/tables/HerbDropTable.java index 063444231..efd03da2b 100644 --- a/Server/src/main/content/data/tables/HerbDropTable.java +++ b/Server/src/main/content/data/tables/HerbDropTable.java @@ -3,13 +3,13 @@ package content.data.tables; import core.api.StartupListener; import core.game.node.item.Item; import core.game.node.item.WeightedChanceItem; +import core.tools.Log; import core.tools.RandomFunction; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import core.ServerConstants; -import core.tools.SystemLogger; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; @@ -18,7 +18,7 @@ import java.io.File; import java.util.ArrayList; import java.util.List; -import static core.tools.SystemLogger.logInfo; +import static core.api.ContentAPIKt.log; /** * Handles the herb drop table. @@ -57,11 +57,11 @@ public final class HerbDropTable implements StartupListener { @Override public void startup() { if(ServerConstants.HDT_DATA_PATH != null && !new File(ServerConstants.HDT_DATA_PATH).exists()){ - SystemLogger.logErr(this.getClass(), "Can't locate HDT file at " + ServerConstants.HDT_DATA_PATH); + log(this.getClass(), Log.ERR, "Can't locate HDT file at " + ServerConstants.HDT_DATA_PATH); return; } parse(ServerConstants.HDT_DATA_PATH); - logInfo(this.getClass(), "Initialized Herb Drop Table from " + ServerConstants.HDT_DATA_PATH); + log(this.getClass(), Log.FINE, "Initialized Herb Drop Table from " + ServerConstants.HDT_DATA_PATH); } /** diff --git a/Server/src/main/content/data/tables/RareDropTable.kt b/Server/src/main/content/data/tables/RareDropTable.kt index 6df2cc19e..253fc529a 100644 --- a/Server/src/main/content/data/tables/RareDropTable.kt +++ b/Server/src/main/content/data/tables/RareDropTable.kt @@ -2,15 +2,15 @@ package content.data.tables import core.ServerConstants import core.api.StartupListener +import core.api.log import core.api.shouldRemoveNothings import core.api.utils.WeightBasedTable import core.api.utils.WeightedItem import core.game.node.entity.Entity import core.game.node.entity.player.Player import core.game.node.item.Item +import core.tools.Log import core.tools.RandomFunction -import core.tools.SystemLogger.logErr -import core.tools.SystemLogger.logInfo import org.rs09.consts.Items import org.w3c.dom.Element import org.w3c.dom.Node @@ -26,11 +26,11 @@ import javax.xml.parsers.ParserConfigurationException class RareDropTable : StartupListener { override fun startup() { if (ServerConstants.RDT_DATA_PATH != null && !File(ServerConstants.RDT_DATA_PATH).exists()) { - logErr(this.javaClass, "Can't locate RDT file at " + ServerConstants.RDT_DATA_PATH) + log(this::class.java, Log.ERR, "Can't locate RDT file at " + ServerConstants.RDT_DATA_PATH) return } parse(ServerConstants.RDT_DATA_PATH) - logInfo(this.javaClass, "Initialized Rare Drop Table from " + ServerConstants.RDT_DATA_PATH) + log(this::class.java, Log.FINE, "Initialized Rare Drop Table from " + ServerConstants.RDT_DATA_PATH) } companion object { diff --git a/Server/src/main/content/data/tables/RareSeedDropTable.java b/Server/src/main/content/data/tables/RareSeedDropTable.java index 39c83efe1..0b892270d 100644 --- a/Server/src/main/content/data/tables/RareSeedDropTable.java +++ b/Server/src/main/content/data/tables/RareSeedDropTable.java @@ -3,13 +3,13 @@ package content.data.tables; import core.api.StartupListener; import core.game.node.item.Item; import core.game.node.item.WeightedChanceItem; +import core.tools.Log; import core.tools.RandomFunction; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import core.ServerConstants; -import core.tools.SystemLogger; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; @@ -18,7 +18,7 @@ import java.io.File; import java.util.ArrayList; import java.util.List; -import static core.tools.SystemLogger.logInfo; +import static core.api.ContentAPIKt.log; /** * Handles the rare seed drop table. @@ -57,11 +57,11 @@ public final class RareSeedDropTable implements StartupListener { @Override public void startup() { if(ServerConstants.RSDT_DATA_PATH != null && !new File(ServerConstants.RSDT_DATA_PATH).exists()){ - SystemLogger.logErr(this.getClass(), "Can't locate RSDT file at " + ServerConstants.RSDT_DATA_PATH); + log(this.getClass(), Log.ERR, "Can't locate RSDT file at " + ServerConstants.RSDT_DATA_PATH); return; } parse(ServerConstants.RSDT_DATA_PATH); - logInfo(this.getClass(), "Initialized Rare Seed Drop Table from " + ServerConstants.RSDT_DATA_PATH); + log(this.getClass(), Log.FINE, "Initialized Rare Seed Drop Table from " + ServerConstants.RSDT_DATA_PATH); } /** diff --git a/Server/src/main/content/data/tables/UncommonSeedDropTable.java b/Server/src/main/content/data/tables/UncommonSeedDropTable.java index cf4537ece..1c76ca555 100644 --- a/Server/src/main/content/data/tables/UncommonSeedDropTable.java +++ b/Server/src/main/content/data/tables/UncommonSeedDropTable.java @@ -3,13 +3,13 @@ package content.data.tables; import core.api.StartupListener; import core.game.node.item.Item; import core.game.node.item.WeightedChanceItem; +import core.tools.Log; import core.tools.RandomFunction; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import core.ServerConstants; -import core.tools.SystemLogger; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; @@ -18,7 +18,7 @@ import java.io.File; import java.util.ArrayList; import java.util.List; -import static core.tools.SystemLogger.logInfo; +import static core.api.ContentAPIKt.log; /** * Handles the uncommon seed drop table. @@ -57,11 +57,11 @@ public final class UncommonSeedDropTable implements StartupListener { @Override public void startup() { if(ServerConstants.USDT_DATA_PATH != null && !new File(ServerConstants.USDT_DATA_PATH).exists()){ - SystemLogger.logErr(this.getClass(), "Can't locate USDT file at " + ServerConstants.USDT_DATA_PATH); + log(this.getClass(), Log.ERR, "Can't locate USDT file at " + ServerConstants.USDT_DATA_PATH); return; } parse(ServerConstants.USDT_DATA_PATH); - logInfo(this.getClass(), "Initialized Uncommon Seed Drop Table from " + ServerConstants.USDT_DATA_PATH); + log(this.getClass(), Log.FINE, "Initialized Uncommon Seed Drop Table from " + ServerConstants.USDT_DATA_PATH); } /** diff --git a/Server/src/main/content/global/activity/cchallange/ChampionChallengeListener.kt b/Server/src/main/content/global/activity/cchallange/ChampionChallengeListener.kt new file mode 100644 index 000000000..e1e2cd6c8 --- /dev/null +++ b/Server/src/main/content/global/activity/cchallange/ChampionChallengeListener.kt @@ -0,0 +1,229 @@ +package content.global.activity.cchallange + +import content.global.activity.cchallange.npc.EarthWarriorChampionNPC.Companion.spawnEarthWarriorChampion +import content.global.activity.cchallange.npc.GhoulChampionNPC.Companion.spawnGhoulChampion +import content.global.activity.cchallange.npc.GiantChampionNPC.Companion.spawnGiantChampion +import content.global.activity.cchallange.npc.GoblinChampionNPC.Companion.spawnGoblinChampion +import content.global.activity.cchallange.npc.HobgoblinChampionNPC.Companion.spawnHobgoblinChampion +import content.global.activity.cchallange.npc.ImpChampionNPC.Companion.spawnImpChampion +import content.global.activity.cchallange.npc.JogreChampionNPC.Companion.spawnJogreChampion +import content.global.activity.cchallange.npc.LesserDemonChampionNPC.Companion.spawnLesserDemonChampion +import content.global.activity.cchallange.npc.SkeletonChampionNPC.Companion.spawnSkeletonChampion +import content.global.activity.cchallange.npc.ZombieChampionNPC.Companion.spawnZombieChampion +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.node.item.Item +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 +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import org.rs09.consts.Scenery + +/** + * Represents the Champion challenge. + */ + +class ChampionChallengeListener : InteractionListener, MapArea { + + val EARTH_WARRIOR_SCROLL = Items.CHAMPION_SCROLL_6798 + val GHOUL_SCROLL = Items.CHAMPION_SCROLL_6799 + val GIANT_SCROLL = Items.CHAMPION_SCROLL_6800 + val GOBLIN_SCROLL = Items.CHAMPION_SCROLL_6801 + val HOBGOBLIN_SCROLL = Items.CHAMPION_SCROLL_6802 + val IMP_SCROLL = Items.CHAMPION_SCROLL_6803 + val JOGRE_SCROLL = Items.CHAMPION_SCROLL_6804 + val LESSER_DEMON_SCROLL = Items.CHAMPION_SCROLL_6805 + val SKELETON_SCROLL = Items.CHAMPION_SCROLL_6806 + val ZOMBIE_SCROLL = Items.CHAMPION_SCROLL_6807 + + val MESSAGE_SCROLL = 222 + + private val IMP_SCROLL_TEXT = arrayOf( + "How about picking on someone your own size? I'll", + "see you at the Champion's 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.", + "", + "Champion of Goblins" + ) + + private val SKELETON_SCROLL_TEXT = arrayOf( + "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!", + "", + "Champion of Zombies" + ) + + private val GIANT_SCROLL_TEXT = arrayOf( + "Get yourself to the Champions' Guild, if you", + "dare to face me puny human.", + "", + "Champion of Giants" + ) + + private val HOBGOBLIN_SCROLL_TEXT = arrayOf( + "You won't defeat me, though you're welcome to", + "try at the Champions' Guild.", + "", + "Champion of Hobgoblins" + ) + + private val GHOUL_SCROLL_TEXT = arrayOf( + "Come and duel me at the Champions' Guild, I'll", + "make sure nothing goes to waste.", + "", + "Champion of Ghouls" + ) + + 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.", + "", + "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!", + "", + "Champion of Jogres" + ) + + private val LESSER_DEMON_SCROLL_TEXT = arrayOf( + "Come to the Champion's Guild so I can banish", + "you mortal!", + "", + "Champion of Lesser Demons" + ) + + private val PORTCULLIS = Scenery.PORTCULLIS_10553 + 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 + private val TRAPDOOR_OPEN = Scenery.TRAPDOOR_10559 + private val LARXUS = NPCs.LARXUS_3050 + + 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 + } + + on(ChampionScrollsDropHandler.SCROLLS, IntType.ITEM, "read") { player, node -> + updateAndReadScroll(player, node.asItem()) + 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, -1, node.location) + return@on true + } + + on(CHAMPION_STATUE_CLOSED, IntType.SCENERY, "open") { _, node -> + replaceScenery(node.asScenery(), CHAMPION_STATUE_OPEN, 100, node.location) + 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.") + } else { + lock(player, 3) + submitWorldPulse(object : Pulse() { + var counter = 0 + override fun pulse(): Boolean { + when (counter++) { + 1 -> { + player.familiarManager.dismiss() + } + 2 -> DoorActionHandler.handleDoor(player, node.asScenery()) + 3 -> when{ + removeItem(player, IMP_SCROLL) -> spawnImpChampion(player) + removeItem(player, GOBLIN_SCROLL) -> spawnGoblinChampion(player) + removeItem(player, SKELETON_SCROLL) -> spawnSkeletonChampion(player) + removeItem(player, ZOMBIE_SCROLL) -> spawnZombieChampion(player) + removeItem(player, GIANT_SCROLL) -> spawnGiantChampion(player) + removeItem(player, HOBGOBLIN_SCROLL) -> spawnHobgoblinChampion(player) + removeItem(player, GHOUL_SCROLL) -> spawnGhoulChampion(player) + removeItem(player, EARTH_WARRIOR_SCROLL) -> { + spawnEarthWarriorChampion(player) + player.prayer.reset() + } + removeItem(player, JOGRE_SCROLL) -> spawnJogreChampion(player) + removeItem(player, LESSER_DEMON_SCROLL) -> spawnLesserDemonChampion(player) + } + } + return false + } + }) + } + return@on true + } + } + + private fun updateAndReadScroll(player: Player, item: Item) { + val id = item.id + openInterface(player, MESSAGE_SCROLL).also { + when (id) { + IMP_SCROLL -> setInterfaceText(player, IMP_SCROLL_TEXT.joinToString("
"), MESSAGE_SCROLL, 4) + GOBLIN_SCROLL -> setInterfaceText(player, GOBLIN_SCROLL_TEXT.joinToString("
"), MESSAGE_SCROLL, 4) + SKELETON_SCROLL -> setInterfaceText(player, SKELETON_SCROLL_TEXT.joinToString("
"), MESSAGE_SCROLL, 4) + ZOMBIE_SCROLL -> setInterfaceText(player, ZOMBIE_SCROLL_TEXT.joinToString("
"), MESSAGE_SCROLL, 4) + GIANT_SCROLL -> setInterfaceText(player, GIANT_SCROLL_TEXT.joinToString("
"), MESSAGE_SCROLL, 4) + HOBGOBLIN_SCROLL -> setInterfaceText(player, HOBGOBLIN_SCROLL_TEXT.joinToString("
"), MESSAGE_SCROLL, 4) + GHOUL_SCROLL -> setInterfaceText(player, GHOUL_SCROLL_TEXT.joinToString("
"), MESSAGE_SCROLL, 4) + EARTH_WARRIOR_SCROLL -> setInterfaceText(player, EARTH_WARRIOR_TEXT.joinToString("
"), MESSAGE_SCROLL, 4) + JOGRE_SCROLL -> setInterfaceText(player, JOGRE_SCROLL_TEXT.joinToString("
"), MESSAGE_SCROLL, 4) + LESSER_DEMON_SCROLL -> setInterfaceText(player, LESSER_DEMON_SCROLL_TEXT.joinToString("
"), MESSAGE_SCROLL, 4) + } + } + } + + override fun defineAreaBorders(): Array { + return arrayOf(getRegionBorders(ARENA_ZONE)) + } + + override fun getRestrictions(): Array { + return arrayOf( + ZoneRestriction.CANNON, + ZoneRestriction.FIRES, + 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 new file mode 100644 index 000000000..02b3b2b0e --- /dev/null +++ b/Server/src/main/content/global/activity/cchallange/ChampionScrollsDropHandler.kt @@ -0,0 +1,279 @@ +package content.global.activity.cchallange + +import core.api.sendMessage +import core.api.sendNews +import core.game.event.NPCKillEvent +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.game.node.item.GroundItemManager +import core.game.node.item.Item +import core.tools.* +import core.game.world.GameWorld +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +/** + * Handles the drop for champion challenge. + * @authors Phil, Skelsoft, Ceikry + */ + +class ChampionScrollsDropHandler : ChampionScrollsEventHookBase() { + companion object { + + private val IMPS = intArrayOf( + NPCs.IMP_708, NPCs.IMP_709, NPCs.IMP_1531, NPCs.IMP_6211 + ) + + private val GOBLINS = intArrayOf( + NPCs.GOBLIN_100, NPCs.GOBLIN_101, NPCs.GOBLIN_102, NPCs.GOBLIN_444, NPCs.GOBLIN_445, + + NPCs.GOBLIN_1769, NPCs.GOBLIN_1770, NPCs.GOBLIN_1771, NPCs.GOBLIN_1772, NPCs.GOBLIN_1773, + NPCs.GOBLIN_1774, NPCs.GOBLIN_1775, NPCs.GOBLIN_1776, NPCs.GOBLIN_2274, NPCs.GOBLIN_2275, + NPCs.GOBLIN_2276, NPCs.GOBLIN_2277, NPCs.GOBLIN_2278, NPCs.GOBLIN_2279, NPCs.GOBLIN_2280, + NPCs.GOBLIN_2281, NPCs.GOBLIN_2678, NPCs.GOBLIN_2679, NPCs.GOBLIN_2680, NPCs.GOBLIN_2681, + NPCs.GOBLIN_3264, NPCs.GOBLIN_3265, NPCs.GOBLIN_3266, NPCs.GOBLIN_3267, NPCs.GOBLIN_3726, + NPCs.GOBLIN_4261, NPCs.GOBLIN_4262, NPCs.GOBLIN_4263, NPCs.GOBLIN_4264, NPCs.GOBLIN_4265, + NPCs.GOBLIN_4266, NPCs.GOBLIN_4267, NPCs.GOBLIN_4268, NPCs.GOBLIN_4269, NPCs.GOBLIN_4270, + NPCs.GOBLIN_4271, NPCs.GOBLIN_4272, NPCs.GOBLIN_4273, NPCs.GOBLIN_4274, NPCs.GOBLIN_4275, + NPCs.GOBLIN_4276, NPCs.GOBLIN_4407, NPCs.GOBLIN_4408, NPCs.GOBLIN_4409, NPCs.GOBLIN_4410, + NPCs.GOBLIN_4411, NPCs.GOBLIN_4412, NPCs.GOBLIN_4479, NPCs.GOBLIN_4480, NPCs.GOBLIN_4481, + NPCs.GOBLIN_4482, NPCs.GOBLIN_4483, NPCs.GOBLIN_4484, NPCs.GOBLIN_4485, NPCs.GOBLIN_4486, + NPCs.GOBLIN_4487, NPCs.GOBLIN_4488, NPCs.GOBLIN_4489, NPCs.GOBLIN_4490, NPCs.GOBLIN_4491, + NPCs.GOBLIN_4492, NPCs.GOBLIN_4499, NPCs.GOBLIN_4633, NPCs.GOBLIN_4634, NPCs.GOBLIN_4635, + NPCs.GOBLIN_4636, NPCs.GOBLIN_4637, NPCs.GOBLIN_5855, NPCs.GOBLIN_5856, NPCs.GOBLIN_6125, + NPCs.GOBLIN_6126, NPCs.GOBLIN_6132, NPCs.GOBLIN_6133, NPCs.GOBLIN_6279, NPCs.GOBLIN_6280, + NPCs.GOBLIN_6281, NPCs.GOBLIN_6282, NPCs.GOBLIN_6283, NPCs.GOBLIN_6284, NPCs.GOBLIN_6402, + NPCs.GOBLIN_6403, NPCs.GOBLIN_6404, NPCs.GOBLIN_6405, NPCs.GOBLIN_6406, NPCs.GOBLIN_6407, + NPCs.GOBLIN_6408, NPCs.GOBLIN_6409, NPCs.GOBLIN_6410, NPCs.GOBLIN_6411, NPCs.GOBLIN_6412, + NPCs.GOBLIN_6413, NPCs.GOBLIN_6414, NPCs.GOBLIN_6415, NPCs.GOBLIN_6416, NPCs.GOBLIN_6417, + NPCs.GOBLIN_6418, NPCs.GOBLIN_6419, NPCs.GOBLIN_6420, NPCs.GOBLIN_6421, NPCs.GOBLIN_6422, + NPCs.GOBLIN_6423, NPCs.GOBLIN_6424, NPCs.GOBLIN_6425, NPCs.GOBLIN_6426, NPCs.GOBLIN_6427, + NPCs.GOBLIN_6428, NPCs.GOBLIN_6429, NPCs.GOBLIN_6430, NPCs.GOBLIN_6431, NPCs.GOBLIN_6432, + NPCs.GOBLIN_6433, NPCs.GOBLIN_6434, NPCs.GOBLIN_6435, NPCs.GOBLIN_6436, NPCs.GOBLIN_6437, + NPCs.GOBLIN_6438, NPCs.GOBLIN_6439, NPCs.GOBLIN_6440, NPCs.GOBLIN_6441, NPCs.GOBLIN_6442, + NPCs.GOBLIN_6443, NPCs.GOBLIN_6444, NPCs.GOBLIN_6445, NPCs.GOBLIN_6446, NPCs.GOBLIN_6447, + NPCs.GOBLIN_6448, NPCs.GOBLIN_6449, NPCs.GOBLIN_6450, NPCs.GOBLIN_6451, NPCs.GOBLIN_6452, + NPCs.GOBLIN_6453, NPCs.GOBLIN_6454, NPCs.GOBLIN_6455, NPCs.GOBLIN_6456, NPCs.GOBLIN_6457, + NPCs.GOBLIN_6458, NPCs.GOBLIN_6459, NPCs.GOBLIN_6460, NPCs.GOBLIN_6461, NPCs.GOBLIN_6462, + NPCs.GOBLIN_6463, NPCs.GOBLIN_6464, NPCs.GOBLIN_6465, NPCs.GOBLIN_6466, NPCs.GOBLIN_6467, + NPCs.GOBLIN_6490, NPCs.GOBLIN_6491, NPCs.GOBLIN_6492, NPCs.GOBLIN_6493, NPCs.GOBLIN_6494, + NPCs.GOBLIN_6495, NPCs.GOBLIN_7964, NPCs.GOBLIN_7965, + + NPCs.CAVE_GOBLIN_1822, NPCs.CAVE_GOBLIN_1823, NPCs.CAVE_GOBLIN_1824, NPCs.CAVE_GOBLIN_1825, + NPCs.CAVE_GOBLIN_5752, NPCs.CAVE_GOBLIN_5753, NPCs.CAVE_GOBLIN_5754, NPCs.CAVE_GOBLIN_5755, + NPCs.CAVE_GOBLIN_5756, NPCs.CAVE_GOBLIN_5757, NPCs.CAVE_GOBLIN_5758, NPCs.CAVE_GOBLIN_5759, + NPCs.CAVE_GOBLIN_5760, NPCs.CAVE_GOBLIN_5761, NPCs.CAVE_GOBLIN_5762, NPCs.CAVE_GOBLIN_5763, + NPCs.CAVE_GOBLIN_5764, NPCs.CAVE_GOBLIN_5765, NPCs.CAVE_GOBLIN_5766, NPCs.CAVE_GOBLIN_5767, + NPCs.CAVE_GOBLIN_5768, NPCs.CAVE_GOBLIN_5769, NPCs.CAVE_GOBLIN_5783, NPCs.CAVE_GOBLIN_5785, + NPCs.CAVE_GOBLIN_5873, NPCs.CAVE_GOBLIN_5874, NPCs.CAVE_GOBLIN_5875, NPCs.CAVE_GOBLIN_5876, + NPCs.CAVE_GOBLIN_5877, NPCs.CAVE_GOBLIN_5878, NPCs.CAVE_GOBLIN_8451, + + NPCs.CAVE_GOBLIN_GUARD_2073, NPCs.CAVE_GOBLIN_GUARD_2074, + + NPCs.GOBLIN_GUARD_489, NPCs.GOBLIN_GUARD_6496, NPCs.GOBLIN_GUARD_6497, + NPCs.SERGEANT_GRIMSPIKE_6265, + NPCs.SERGEANT_STEELWILL_6263, + NPCs.SERGEANT_STRONGSTACK_6261 + ) + + private val SKELETONS = intArrayOf( + NPCs.SKELETON_90, NPCs.SKELETON_91, NPCs.SKELETON_92, NPCs.SKELETON_93, + + NPCs.SKELETON_459, + + NPCs.SKELETON_1471, NPCs.SKELETON_2036, NPCs.SKELETON_2037, NPCs.SKELETON_2715, + NPCs.SKELETON_3151, NPCs.SKELETON_3152, NPCs.SKELETON_3291, NPCs.SKELETON_3581, + NPCs.SKELETON_4384, NPCs.SKELETON_4385, NPCs.SKELETON_4386, NPCs.SKELETON_5332, + NPCs.SKELETON_5333, NPCs.SKELETON_5334, NPCs.SKELETON_5335, NPCs.SKELETON_5336, + NPCs.SKELETON_5337, NPCs.SKELETON_5338, NPCs.SKELETON_5339, NPCs.SKELETON_5340, + NPCs.SKELETON_5341, NPCs.SKELETON_5365, NPCs.SKELETON_5366, NPCs.SKELETON_5367, + NPCs.SKELETON_5368, NPCs.SKELETON_5381, NPCs.SKELETON_5385, NPCs.SKELETON_5386, + NPCs.SKELETON_5387, NPCs.SKELETON_5388, NPCs.SKELETON_5389, NPCs.SKELETON_5390, + NPCs.SKELETON_5391, NPCs.SKELETON_5392, NPCs.SKELETON_5411, NPCs.SKELETON_5412, + NPCs.SKELETON_5422, NPCs.SKELETON_5423, NPCs.SKELETON_6091, NPCs.SKELETON_6092, + NPCs.SKELETON_6093, NPCs.SKELETON_6764, NPCs.SKELETON_6765, NPCs.SKELETON_6766, + NPCs.SKELETON_6767, NPCs.SKELETON_6768, NPCs.SKELETON_7774, NPCs.SKELETON_7775, + NPCs.SKELETON_7776, NPCs.SKELETON_7777, NPCs.SKELETON_7778, NPCs.SKELETON_7779, + NPCs.SKELETON_7788, NPCs.SKELETON_7815, NPCs.GIANT_SKELETON_1973, + + NPCs.GIANT_SKELETON_5359, NPCs.GIANT_SKELETON_5384, NPCs.SKELETON_HELLHOUND_1575, + + NPCs.SKELETON_MAGE_94, NPCs.SKELETON_MAGE_2717, NPCs.SKELETON_MAGE_3844, + + NPCs.SKELETON_MAGE_3850, NPCs.SKELETON_MAGE_3851, + + NPCs.SKELETON_HERO_6103, NPCs.SKELETON_BRUTE_6104, NPCs.SKELETON_WARLORD_6105, + + NPCs.SKELETON_HEAVY_6106, NPCs.SKELETON_THUG_6107, + NPCs.NAZASTAROOL_508, + + NPCs.UNDEAD_ONE_5353, NPCs.UNDEAD_ONE_5354, NPCs.UNDEAD_ONE_5355, + NPCs.UNDEAD_ONE_5356, NPCs.UNDEAD_ONE_5357, NPCs.UNDEAD_ONE_5358, + ) + + private val ZOMBIES = intArrayOf( + NPCs.ZOMBIE_73, NPCs.ZOMBIE_74, NPCs.ZOMBIE_75, NPCs.ZOMBIE_76, NPCs.ZOMBIE_419, + NPCs.ZOMBIE_420, NPCs.ZOMBIE_421, NPCs.ZOMBIE_422, NPCs.ZOMBIE_423, NPCs.ZOMBIE_424, + + NPCs.ZOMBIE_1826, NPCs.ZOMBIE_2714, NPCs.ZOMBIE_2863, NPCs.ZOMBIE_2866, NPCs.ZOMBIE_2869, + NPCs.ZOMBIE_2878, NPCs.ZOMBIE_3622, NPCs.ZOMBIE_3623, NPCs.ZOMBIE_4392, NPCs.ZOMBIE_4393, + NPCs.ZOMBIE_4394, NPCs.ZOMBIE_5293, NPCs.ZOMBIE_5294, NPCs.ZOMBIE_5295, NPCs.ZOMBIE_5296, + NPCs.ZOMBIE_5297, NPCs.ZOMBIE_5298, NPCs.ZOMBIE_5299, NPCs.ZOMBIE_5300, NPCs.ZOMBIE_5301, + NPCs.ZOMBIE_5302, NPCs.ZOMBIE_5303, NPCs.ZOMBIE_5304, NPCs.ZOMBIE_5305, NPCs.ZOMBIE_5306, + NPCs.ZOMBIE_5307, NPCs.ZOMBIE_5308, NPCs.ZOMBIE_5309, NPCs.ZOMBIE_5310, NPCs.ZOMBIE_5311, + NPCs.ZOMBIE_5312, NPCs.ZOMBIE_5313, NPCs.ZOMBIE_5314, NPCs.ZOMBIE_5315, NPCs.ZOMBIE_5316, + NPCs.ZOMBIE_5317, NPCs.ZOMBIE_5318, NPCs.ZOMBIE_5319, NPCs.ZOMBIE_5320, NPCs.ZOMBIE_5321, + NPCs.ZOMBIE_5322, NPCs.ZOMBIE_5323, NPCs.ZOMBIE_5324, NPCs.ZOMBIE_5325, NPCs.ZOMBIE_5326, + NPCs.ZOMBIE_5327, NPCs.ZOMBIE_5328, NPCs.ZOMBIE_5329, NPCs.ZOMBIE_5330, NPCs.ZOMBIE_5331, + NPCs.ZOMBIE_5375, NPCs.ZOMBIE_5376, NPCs.ZOMBIE_5377, NPCs.ZOMBIE_5378, NPCs.ZOMBIE_5379, + NPCs.ZOMBIE_5380, NPCs.ZOMBIE_5393, NPCs.ZOMBIE_5394, NPCs.ZOMBIE_5395, NPCs.ZOMBIE_5396, + NPCs.ZOMBIE_5397, NPCs.ZOMBIE_5398, NPCs.ZOMBIE_5399, NPCs.ZOMBIE_5400, NPCs.ZOMBIE_5401, + NPCs.ZOMBIE_5402, NPCs.ZOMBIE_5403, NPCs.ZOMBIE_5404, NPCs.ZOMBIE_5405, NPCs.ZOMBIE_5406, + NPCs.ZOMBIE_5407, NPCs.ZOMBIE_5408, NPCs.ZOMBIE_5409, NPCs.ZOMBIE_5410, NPCs.ZOMBIE_6099, + NPCs.ZOMBIE_6100, NPCs.ZOMBIE_6131, NPCs.ZOMBIE_7789, NPCs.ZOMBIE_7790, NPCs.ZOMBIE_7791, + NPCs.ZOMBIE_7792, NPCs.ZOMBIE_7817, NPCs.ZOMBIE_7818, NPCs.ZOMBIE_7819, NPCs.ZOMBIE_7820, + NPCs.ZOMBIE_8142, NPCs.ZOMBIE_8143, NPCs.ZOMBIE_8144, NPCs.ZOMBIE_8145, + + NPCs.ZOMBIE_PIRATE_2837, NPCs.ZOMBIE_PIRATE_2838, NPCs.ZOMBIE_PIRATE_2839, + NPCs.ZOMBIE_PIRATE_2840, NPCs.ZOMBIE_PIRATE_2841, NPCs.ZOMBIE_PIRATE_2842, + NPCs.ZOMBIE_PIRATE_5629, NPCs.ZOMBIE_PIRATE_5630, NPCs.ZOMBIE_PIRATE_5631, + NPCs.ZOMBIE_PIRATE_5632, NPCs.ZOMBIE_PIRATE_5633, NPCs.ZOMBIE_PIRATE_5634, + NPCs.ZOMBIE_PIRATE_5635, NPCs.ZOMBIE_PIRATE_5636, NPCs.ZOMBIE_PIRATE_5637, + NPCs.ZOMBIE_PIRATE_5638, NPCs.ZOMBIE_PIRATE_5639, NPCs.ZOMBIE_PIRATE_5640, + NPCs.ZOMBIE_PIRATE_5641, NPCs.ZOMBIE_PIRATE_5642, NPCs.ZOMBIE_PIRATE_5643, + NPCs.ZOMBIE_PIRATE_5644, NPCs.ZOMBIE_PIRATE_5645, NPCs.ZOMBIE_PIRATE_5646, + NPCs.ZOMBIE_PIRATE_5647, NPCs.ZOMBIE_PIRATE_5648, NPCs.ZOMBIE_PIRATE_5649, + NPCs.ZOMBIE_PIRATE_5650, NPCs.ZOMBIE_PIRATE_5651, NPCs.ZOMBIE_PIRATE_5652, + NPCs.ZOMBIE_PIRATE_5653, NPCs.ZOMBIE_PIRATE_5654, NPCs.ZOMBIE_PIRATE_5655, + NPCs.ZOMBIE_PIRATE_5656, NPCs.ZOMBIE_PIRATE_5657, NPCs.ZOMBIE_PIRATE_5658, + NPCs.ZOMBIE_PIRATE_5659, NPCs.ZOMBIE_PIRATE_5660, NPCs.ZOMBIE_PIRATE_5661, + NPCs.ZOMBIE_PIRATE_5662, NPCs.ZOMBIE_PIRATE_5663, NPCs.ZOMBIE_PIRATE_5664, + NPCs.ZOMBIE_PIRATE_5665, + + NPCs.ZOMBIE_SWAB_2843, NPCs.ZOMBIE_SWAB_2845, + NPCs.ZOMBIE_SWAB_2846, NPCs.ZOMBIE_SWAB_2847, + NPCs.ZOMBIE_SWAB_2848, + NPCs.NAZASTAROOL_507, + + NPCs.SLASH_BASH_2060, NPCs.SOREBONES_5627, NPCs.SOREBONES_5628, + + NPCs.UNDEAD_LUMBERJACK_1524, + + NPCs.UNDEAD_ONE_502, NPCs.UNDEAD_ONE_503, NPCs.UNDEAD_ONE_504, NPCs.UNDEAD_ONE_505, + + 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, + NPCs.UNDEAD_LUMBERJACK_1524, NPCs.UNDEAD_LUMBERJACK_1525, NPCs.UNDEAD_LUMBERJACK_5678, + NPCs.UNDEAD_LUMBERJACK_5679, NPCs.UNDEAD_LUMBERJACK_5680, NPCs.UNDEAD_LUMBERJACK_5681, + NPCs.UNDEAD_LUMBERJACK_5682, NPCs.UNDEAD_LUMBERJACK_5683, NPCs.UNDEAD_LUMBERJACK_5684, + NPCs.UNDEAD_LUMBERJACK_5685, NPCs.UNDEAD_LUMBERJACK_5686, NPCs.UNDEAD_LUMBERJACK_5687, + NPCs.UNDEAD_LUMBERJACK_5688, NPCs.UNDEAD_LUMBERJACK_5689, NPCs.UNDEAD_LUMBERJACK_5690, + NPCs.UNDEAD_LUMBERJACK_5691, NPCs.UNDEAD_LUMBERJACK_5692, NPCs.UNDEAD_LUMBERJACK_5693, + NPCs.UNDEAD_LUMBERJACK_5694, NPCs.UNDEAD_LUMBERJACK_5695, NPCs.UNDEAD_LUMBERJACK_5696, + NPCs.UNDEAD_LUMBERJACK_5697, NPCs.UNDEAD_LUMBERJACK_5698, NPCs.UNDEAD_LUMBERJACK_5699, + NPCs.UNDEAD_LUMBERJACK_5700, NPCs.UNDEAD_LUMBERJACK_5701, NPCs.UNDEAD_LUMBERJACK_5702, + NPCs.UNDEAD_LUMBERJACK_5703, NPCs.UNDEAD_LUMBERJACK_5704, NPCs.UNDEAD_LUMBERJACK_5705, + NPCs.UNDEAD_LUMBERJACK_5706, NPCs.UNDEAD_LUMBERJACK_5707, NPCs.UNDEAD_LUMBERJACK_5708, + NPCs.UNDEAD_LUMBERJACK_5709, NPCs.UNDEAD_LUMBERJACK_5710, NPCs.UNDEAD_LUMBERJACK_5711, + NPCs.UNDEAD_LUMBERJACK_5712, NPCs.UNDEAD_LUMBERJACK_5713, NPCs.UNDEAD_LUMBERJACK_5714, + NPCs.UNDEAD_LUMBERJACK_5715, NPCs.UNDEAD_LUMBERJACK_5716, NPCs.UNDEAD_LUMBERJACK_5717, + NPCs.UNDEAD_LUMBERJACK_5718, NPCs.UNDEAD_LUMBERJACK_5719, NPCs.UNDEAD_LUMBERJACK_5720, + NPCs.UNDEAD_LUMBERJACK_5721, NPCs.UNDEAD_LUMBERJACK_5722, NPCs.UNDEAD_LUMBERJACK_5723, + NPCs.UNDEAD_LUMBERJACK_5724, NPCs.UNDEAD_LUMBERJACK_5725, NPCs.UNDEAD_LUMBERJACK_5726, + NPCs.UNDEAD_LUMBERJACK_5727, NPCs.UNDEAD_LUMBERJACK_5728, NPCs.UNDEAD_LUMBERJACK_5729, + NPCs.UNDEAD_LUMBERJACK_5730, NPCs.UNDEAD_LUMBERJACK_5731, NPCs.UNDEAD_LUMBERJACK_5732, + NPCs.UNDEAD_LUMBERJACK_5733, NPCs.UNDEAD_LUMBERJACK_5734, NPCs.UNDEAD_LUMBERJACK_5735, + NPCs.UNDEAD_LUMBERJACK_5736, NPCs.UNDEAD_LUMBERJACK_5737, NPCs.UNDEAD_LUMBERJACK_5738, + NPCs.UNDEAD_LUMBERJACK_5739, NPCs.UNDEAD_LUMBERJACK_5740, NPCs.UNDEAD_LUMBERJACK_5741, + NPCs.UNDEAD_LUMBERJACK_5742, NPCs.UNDEAD_LUMBERJACK_5743, NPCs.UNDEAD_LUMBERJACK_5744, + NPCs.UNDEAD_LUMBERJACK_5745, NPCs.UNDEAD_LUMBERJACK_5746, NPCs.UNDEAD_LUMBERJACK_5747, + ) + + private val GIANTS = intArrayOf( + NPCs.HILL_GIANT_117, NPCs.HILL_GIANT_4689, NPCs.HILL_GIANT_4690, NPCs.HILL_GIANT_4691, + NPCs.HILL_GIANT_4692, NPCs.HILL_GIANT_4693, + + NPCs.FIRE_GIANT_110, NPCs.FIRE_GIANT_1582, NPCs.FIRE_GIANT_1583, NPCs.FIRE_GIANT_1584, + NPCs.FIRE_GIANT_1585, NPCs.FIRE_GIANT_1586, NPCs.FIRE_GIANT_7003, NPCs.FIRE_GIANT_7004, + + NPCs.MOSS_GIANT_112, NPCs.MOSS_GIANT_1587, NPCs.MOSS_GIANT_1588, NPCs.MOSS_GIANT_1681, + NPCs.MOSS_GIANT_4534, NPCs.MOSS_GIANT_4688, NPCs.MOSS_GIANT_4706, + + NPCs.ICE_GIANT_111, NPCs.ICE_GIANT_3072, NPCs.ICE_GIANT_4685, NPCs.ICE_GIANT_4686, + NPCs.ICE_GIANT_4687, + NPCs.BLACK_KNIGHT_TITAN_221, + + NPCs.CYCLOPS_116, NPCs.CYCLOPS_4291, NPCs.CYCLOPS_4292, NPCs.CYCLOPS_6078, + NPCs.CYCLOPS_6079, NPCs.CYCLOPS_6080, NPCs.CYCLOPS_6081, NPCs.CYCLOPS_6082, + NPCs.CYCLOPS_6083, NPCs.CYCLOPS_6269, NPCs.CYCLOPS_6270, + ) + + private val HOBGOBLINS = intArrayOf( + NPCs.HOBGOBLIN_122, NPCs.HOBGOBLIN_123, NPCs.HOBGOBLIN_2685, NPCs.HOBGOBLIN_2686, + NPCs.HOBGOBLIN_2687, NPCs.HOBGOBLIN_2688, NPCs.HOBGOBLIN_3583, NPCs.HOBGOBLIN_4898, + NPCs.HOBGOBLIN_6275, + ) + + private val GHOUL = NPCs.GHOUL_1218 + private val EARTH_WARRIOR = NPCs.EARTH_WARRIOR_124 + + private val JOGRES = intArrayOf(NPCs.JOGRE_113, NPCs.JOGRE_6268) + + private val LESSER_DEMONS = intArrayOf( + NPCs.LESSER_DEMON_82, NPCs.LESSER_DEMON_4694, NPCs.LESSER_DEMON_4695, + NPCs.LESSER_DEMON_4696, NPCs.LESSER_DEMON_4697, NPCs.LESSER_DEMON_6101, + ) + + private val ZAKLN = NPCs.ZAKLN_GRITCH_6206 + + val SCROLLS = intArrayOf( + Items.CHAMPION_SCROLL_6803, Items.CHAMPION_SCROLL_6801, Items.CHAMPION_SCROLL_6806, + Items.CHAMPION_SCROLL_6807, Items.CHAMPION_SCROLL_6800, Items.CHAMPION_SCROLL_6802, + Items.CHAMPION_SCROLL_6799, Items.CHAMPION_SCROLL_6798, Items.CHAMPION_SCROLL_6804, + Items.CHAMPION_SCROLL_6805 + ) + + val idMap = HashMap() + + init { + for (id in IMPS) + idMap[id] = 0 + for (id in GOBLINS) + idMap[id] = 1 + for (id in SKELETONS) + idMap[id] = 2 + for (id in ZOMBIES) + idMap[id] = 3 + for (id in GIANTS) + idMap[id] = 4 + for (id in HOBGOBLINS) + idMap[id] = 5 + for (id in JOGRES) + idMap[id] = 8 + for (id in LESSER_DEMONS) + idMap[id] = 9 + + idMap[GHOUL] = 6 + idMap[EARTH_WARRIOR] = 7 + } + } + + override fun onNpcKilled(player: Player, event: NPCKillEvent) { + if (player.isArtificial) return + if (player.questRepository.points >= 32) { + val scrollType = idMap[event.npc.id] ?: return + rollForScroll (player, scrollType, event.npc) + } + } + + private fun rollForScroll(player: Player, scroll: Int, npc: NPC) { + val scrollID = SCROLLS[scroll] + val roll = RandomFunction.roll(if (GameWorld.settings!!.isDevMode) 5 else 5000) + if (roll) { + GroundItemManager.create(Item(scrollID), npc.location, player) + sendMessage(player, colorize("%RA Champion's scroll falls to the ground as you slay your opponent.")) + } + } + +} diff --git a/Server/src/main/content/global/activity/cchallange/ChampionScrollsEventHookBase.kt b/Server/src/main/content/global/activity/cchallange/ChampionScrollsEventHookBase.kt new file mode 100644 index 000000000..12f8260c9 --- /dev/null +++ b/Server/src/main/content/global/activity/cchallange/ChampionScrollsEventHookBase.kt @@ -0,0 +1,39 @@ +package content.global.activity.cchallange + +import core.api.LoginListener +import core.game.event.Event +import core.game.event.EventHook +import core.game.event.NPCKillEvent +import core.game.node.entity.Entity +import core.game.node.entity.player.Player + +/** + * Manages the champion scrolls drop. + * @authors Phil, Skelsoft. + */ + +abstract class ChampionScrollsEventHookBase : LoginListener { + + protected companion object { + private fun forEligibleEntityDo(entity: Entity, event: T, handler: (Player, T) -> Unit) { + if (entity !is Player) return + if (entity.isArtificial) return + handler(entity, event) + } + } + + class EventHandler( + private val owner: ChampionScrollsEventHookBase, + private val handler: (Player, T) -> Unit) : EventHook { + override fun process(entity: Entity, event: T) { + forEligibleEntityDo(entity, event, handler) + } + } + + final override fun login(player: Player) { + player.hook(core.api.Event.NPCKilled, EventHandler(this, ::onNpcKilled)) + } + + protected open fun onNpcKilled(player: Player, event: NPCKillEvent) {} + +} \ No newline at end of file diff --git a/Server/src/main/content/global/activity/cchallange/LarxusDialogue.kt b/Server/src/main/content/global/activity/cchallange/LarxusDialogue.kt new file mode 100644 index 000000000..6ec919bd7 --- /dev/null +++ b/Server/src/main/content/global/activity/cchallange/LarxusDialogue.kt @@ -0,0 +1,72 @@ +package content.global.activity.cchallange + +import core.api.* +import core.game.dialogue.DialogueFile +import core.game.dialogue.FacialExpression +import core.game.node.entity.npc.NPC +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import core.tools.START_DIALOGUE +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +/** + * Represents the dialogue plugin used for the larxus npc. + * @author 'Vexia + * @version 1.1 + */ + +@Initializable +class LarxusDialogue(val ChallengeStart: Boolean = false) : DialogueFile() { + + override fun handle(componentID: Int, buttonID: Int) { + val scrolls = intArrayOf(Items.CHAMPION_SCROLL_6798, Items.CHAMPION_SCROLL_6799, Items.CHAMPION_SCROLL_6800, Items.CHAMPION_SCROLL_6801, Items.CHAMPION_SCROLL_6802, Items.CHAMPION_SCROLL_6803, Items.CHAMPION_SCROLL_6804, Items.CHAMPION_SCROLL_6805, Items.CHAMPION_SCROLL_6806, Items.CHAMPION_SCROLL_6807,) + npc = NPC(NPCs.LARXUS_3050) + if (ChallengeStart) { + when (stage) { + 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 } + 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 } + 2 -> when (buttonID) { + 1 -> playerl("Yes, let me at him!").also { stage = 3 } + 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 -> { + end() + setAttribute(player!!, "championsarena:start", true) + } + } + } else { + when (stage) { + START_DIALOGUE -> { + face(findNPC(NPCs.LARXUS_3050)!!, player!!, 1) + npcl(FacialExpression.NEUTRAL, "Is there something I can help you with?").also { stage = 1 } + } + 1 -> options("I was given a challenge, what now?", "What is this place?", "Nothing thanks.").also { stage = 2 } + 2 -> when (buttonID) { + 1 -> playerl("I was given a challenge, what now?").also { stage = 3 } + 2 -> playerl("What is this place?").also { stage = 4 } + 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 } + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/activity/cchallange/npc/EarthWarriorChampionNPC.kt b/Server/src/main/content/global/activity/cchallange/npc/EarthWarriorChampionNPC.kt new file mode 100644 index 000000000..ea868cc2e --- /dev/null +++ b/Server/src/main/content/global/activity/cchallange/npc/EarthWarriorChampionNPC.kt @@ -0,0 +1,114 @@ +package content.global.activity.cchallange.npc + +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.node.entity.skill.Skills +import core.game.system.task.Pulse +import core.game.world.GameWorld +import core.game.world.map.Location +import core.plugin.Initializable +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +/** + * Represents the Earth warrior champion NPC for Champions challenge. + */ + +@Initializable +class EarthWarriorChampionNPC(id: Int = 0, location: Location? = null) : AbstractNPC(id, location) { + var clearTime = 0 + override fun construct(id: Int, location: Location, vararg objects: Any): AbstractNPC { + return EarthWarriorChampionNPC(id, location) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.EARTH_WARRIOR_CHAMPION_3057) + } + + override fun handleTickActions() { + super.handleTickActions() + if (clearTime++ > 288) poofClear(this) + } + + companion object { + fun spawnEarthWarriorChampion(player: Player) { + val champion = EarthWarriorChampionNPC(NPCs.EARTH_WARRIOR_CHAMPION_3057) + champion.location = location(3170, 9758, 0) + champion.isWalks = true + champion.isAggressive = true + champion.isActive = false + + if (champion.asNpc() != null && champion.isActive) { + champion.properties.teleportLocation = champion.properties.spawnLocation + } + + champion.isActive = true + GameWorld.Pulser.submit(object : Pulse(0, champion) { + override fun pulse(): Boolean { + val prayerItems = intArrayOf( + Items.PRAYER_POTION1_143, Items.PRAYER_POTION1_144, + Items.PRAYER_POTION2_141, Items.PRAYER_POTION2_142, + Items.PRAYER_POTION3_139, Items.PRAYER_POTION3_140, + Items.PRAYER_POTION4_2434, Items.PRAYER_POTION4_2435, + + Items.SUPER_RESTORE1_3030, Items.SUPER_RESTORE1_3031, + Items.SUPER_RESTORE2_3028, Items.SUPER_RESTORE2_3029, + Items.SUPER_RESTORE3_3026, Items.SUPER_RESTORE3_3027, + Items.SUPER_RESTORE4_3024, Items.SUPER_RESTORE4_3025, + + Items.PRAYER_CAPE_9759, Items.PRAYER_CAPET_9760, + Items.PRAYER_HOOD_9761, Items.PRAYER_CAPE_10643, + + Items.PRAYER_POTION4_14209, Items.PRAYER_POTION4_14210, + Items.PRAYER_POTION3_14211, Items.PRAYER_POTION3_14212, + Items.PRAYER_POTION2_14213, Items.PRAYER_POTION2_14214, + Items.PRAYER_POTION1_14215, Items.PRAYER_POTION1_14216, + + Items.FALADOR_SHIELD_1_14577, Items.FALADOR_SHIELD_2_14578, + Items.FALADOR_SHIELD_3_14579, + + Items.PRAYER_MIX1_11467, Items.PRAYER_MIX1_11468, + Items.PRAYER_MIX2_11465, Items.PRAYER_MIX2_11466, + + Items.SUP_RESTORE_MIX1_11495, Items.SUP_RESTORE_MIX1_11496, + Items.SUP_RESTORE_MIX2_11493, Items.SUP_RESTORE_MIX2_11494, + ) + if (player.inventory.containsAtLeastOneItem(prayerItems) || player.equipment.containsAtLeastOneItem( + prayerItems + ) + ) { + sendNPCDialogue(player, NPCs.LARXUS_3050, "For this fight you're not allowed to use prayers!") + teleport(player, Location.create(3182, 9758, 0)) // Behind Doors. + } else { + champion.init() + registerHintIcon(player, champion) + champion.attack(player) + } + return true + } + }) + } + } + + override fun finalizeDeath(killer: Entity?) { + if (killer is Player) { + lock(killer, 2) + runTask(killer, 1) { + openInterface(killer, 63) + setInterfaceText(killer, "Well done, you defeated the Earth Warrior Champion!", 63, 2) + killer.packetDispatch.sendItemZoomOnInterface(Items.CHAMPION_SCROLL_6798, 260, 63, 3) + setInterfaceText(killer, "432 Slayer Xp", 63, 6) + setInterfaceText(killer, "432 Hitpoint Xp", 63, 7) + } + setVarbit(killer, 1452, 1, true) + rewardXP(killer, Skills.HITPOINTS, 432.0) + rewardXP(killer, Skills.SLAYER, 432.0) + removeAttribute("championsarena:start") + clearHintIcon(killer) + } + clear() + super.finalizeDeath(killer) + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/activity/cchallange/npc/GhoulChampionNPC.kt b/Server/src/main/content/global/activity/cchallange/npc/GhoulChampionNPC.kt new file mode 100644 index 000000000..a6421a842 --- /dev/null +++ b/Server/src/main/content/global/activity/cchallange/npc/GhoulChampionNPC.kt @@ -0,0 +1,105 @@ +package content.global.activity.cchallange.npc + +import core.api.* +import core.game.container.impl.EquipmentContainer +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.AbstractNPC +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +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.plugin.Initializable +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +/** + * Represents the Ghoul champion NPC for Champions challenge. + */ + +@Initializable +class GhoulChampionNPC(id: Int = 0, location: Location? = null) : AbstractNPC(id, location) { + var clearTime = 0 + override fun construct(id: Int, location: Location, vararg objects: Any): AbstractNPC { + return GhoulChampionNPC(id, location) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.GHOUL_CHAMPION_3059) + } + + override fun handleTickActions() { + super.handleTickActions() + if (clearTime++ > 288) poofClear(this) + } + + companion object { + fun spawnGhoulChampion(player: Player) { + val champion = GhoulChampionNPC(NPCs.GHOUL_CHAMPION_3059) + champion.location = location(3170, 9758, 0) + champion.isWalks = true + champion.isAggressive = true + champion.isActive = false + + if (champion.asNpc() != null && champion.isActive) { + champion.properties.teleportLocation = champion.properties.spawnLocation + } + champion.isActive = true + GameWorld.Pulser.submit(object : Pulse(0, champion) { + override fun pulse(): Boolean { + champion.init() + registerHintIcon(player, champion) + champion.attack(player) + return true + } + }) + } + } + + override fun checkImpact(state: BattleState) { + super.checkImpact(state) + val player = state.attacker + if (player is Player) { + val i: Item = player.equipment[EquipmentContainer.SLOT_WEAPON].id.asItem() + if (i.definition.getConfiguration(ItemConfigParser.TWO_HANDED, false) == true && state.style == CombatStyle.MELEE) { + state.neutralizeHits() + state.estimatedHit = state.maximumHit + } + if (state.style == CombatStyle.MAGIC) { + sendMessage(player, "You can use only weapons in this challenge.") + if (state.estimatedHit > -1) { + state.estimatedHit = 0 + return + } + if (state.secondaryHit > -1) { + state.secondaryHit = 0 + return + } + } + } + } + + override fun finalizeDeath(killer: Entity?) { + if (killer is Player) { + lock(killer, 2) + runTask(killer, 1) { + openInterface(killer, 63) + setInterfaceText(killer, "Well done, you defeated the Ghoul Champion!", 63, 2) + killer.packetDispatch.sendItemZoomOnInterface(Items.CHAMPION_SCROLL_6799, 260, 63, 3) + setInterfaceText(killer, "400 Slayer Xp", 63, 6) + setInterfaceText(killer, "400 Hitpoint Xp", 63, 7) + } + setVarbit(killer, 1453, 1, true) + rewardXP(killer, Skills.HITPOINTS, 400.0) + rewardXP(killer, Skills.SLAYER, 400.0) + removeAttribute("championsarena:start") + clearHintIcon(killer) + } + clear() + super.finalizeDeath(killer) + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/activity/cchallange/npc/GiantChampionNPC.kt b/Server/src/main/content/global/activity/cchallange/npc/GiantChampionNPC.kt new file mode 100644 index 000000000..3309be77b --- /dev/null +++ b/Server/src/main/content/global/activity/cchallange/npc/GiantChampionNPC.kt @@ -0,0 +1,103 @@ +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.npc.AbstractNPC +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.game.system.task.Pulse +import core.game.world.GameWorld +import core.game.world.map.Location +import core.plugin.Initializable +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +/** + * Represents the Giant champion NPC for Champions challenge. + */ + +@Initializable +class GiantChampionNPC(id: Int = 0, location: Location? = null) : AbstractNPC(id, location) { + var clearTime = 0 + override fun construct(id: Int, location: Location, vararg objects: Any): AbstractNPC { + return GiantChampionNPC(id, location) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.GIANT_CHAMPION_3058) + + } + + override fun handleTickActions() { + super.handleTickActions() + if (clearTime++ > 288) poofClear(this) + } + + companion object { + fun spawnGiantChampion(player: Player) { + val champion = GiantChampionNPC(NPCs.GIANT_CHAMPION_3058) + champion.location = location(3170, 9758, 0) + champion.isWalks = true + champion.isAggressive = true + champion.isActive = false + + if (champion.asNpc() != null && champion.isActive) { + champion.properties.teleportLocation = champion.properties.spawnLocation + } + + champion.isActive = true + GameWorld.Pulser.submit(object : Pulse(0, champion) { + override fun pulse(): Boolean { + champion.init() + registerHintIcon(player, champion) + champion.attack(player) + return true + } + }) + } + } + + override fun checkImpact(state: BattleState) { + super.checkImpact(state) + val player = state.attacker + if (player is Player) { + if (state.style == CombatStyle.MELEE) { + state.neutralizeHits() + state.estimatedHit = state.maximumHit + } + if (state.style == CombatStyle.RANGE || state.style == CombatStyle.MAGIC) { + sendMessage(player, "You can use only melee in this challenge.") + if (state.estimatedHit > -1) { + state.estimatedHit = 0 + return + } + if (state.secondaryHit > -1) { + state.secondaryHit = 0 + return + } + } + } + } + + override fun finalizeDeath(killer: Entity?) { + if (killer is Player) { + lock(killer, 2) + runTask(killer, 1) { + openInterface(killer, 63) + setInterfaceText(killer, "Well done, you defeated the Giant Champion!", 63, 2) + killer.packetDispatch.sendItemZoomOnInterface(Items.CHAMPION_SCROLL_6800, 260, 63, 3) + setInterfaceText(killer, "280 Slayer Xp", 63, 6) + setInterfaceText(killer, "280 Hitpoint Xp", 63, 7) + } + setVarbit(killer, 1454, 1, true) + rewardXP(killer, Skills.HITPOINTS, 280.0) + rewardXP(killer, Skills.SLAYER, 280.0) + removeAttribute("championsarena:start") + clearHintIcon(killer) + } + clear() + super.finalizeDeath(killer) + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/activity/cchallange/npc/GoblinChampionNPC.kt b/Server/src/main/content/global/activity/cchallange/npc/GoblinChampionNPC.kt new file mode 100644 index 000000000..861287813 --- /dev/null +++ b/Server/src/main/content/global/activity/cchallange/npc/GoblinChampionNPC.kt @@ -0,0 +1,101 @@ +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.npc.AbstractNPC +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.game.system.task.Pulse +import core.game.world.GameWorld +import core.game.world.map.Location +import core.plugin.Initializable +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +/** + * Represents the Goblin champion NPC for Champions challenge. + */ + +@Initializable +class GoblinChampionNPC(id: Int = 0, location: Location? = null) : AbstractNPC(id, location) { + var clearTime = 0 + override fun construct(id: Int, location: Location, vararg objects: Any): AbstractNPC { + return GoblinChampionNPC(id, location) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.GOBLIN_CHAMPION_3060) + } + + override fun handleTickActions() { + super.handleTickActions() + if (clearTime++ > 288) poofClear(this) + } + + companion object { + fun spawnGoblinChampion(player: Player) { + val champion = GoblinChampionNPC(NPCs.GOBLIN_CHAMPION_3060) + champion.location = location(3170, 9758, 0) + champion.isWalks = true + champion.isAggressive = true + champion.isActive = false + + if (champion.asNpc() != null && champion.isActive) { + champion.properties.teleportLocation = champion.properties.spawnLocation + } + champion.isActive = true + GameWorld.Pulser.submit(object : Pulse(0, champion) { + override fun pulse(): Boolean { + champion.init() + registerHintIcon(player, champion) + champion.attack(player) + return true + } + }) + } + } + + override fun checkImpact(state: BattleState) { + super.checkImpact(state) + val player = state.attacker + if (player is Player) { + if (state.style == CombatStyle.MAGIC) { + state.neutralizeHits() + state.estimatedHit = state.maximumHit + } + if (state.style == CombatStyle.MELEE || state.style == CombatStyle.RANGE) { + sendMessage(player, "You can use only spells in this challenge.") + if (state.estimatedHit > -1) { + state.estimatedHit = 0 + return + } + if (state.secondaryHit > -1) { + state.secondaryHit = 0 + return + } + } + } + } + + override fun finalizeDeath(killer: Entity?) { + if (killer is Player) { + lock(killer, 2) + runTask(killer, 1) { + openInterface(killer, 63) + setInterfaceText(killer, "Well done, you defeated the Goblin Champion!", 63, 2) + killer.packetDispatch.sendItemZoomOnInterface(Items.CHAMPION_SCROLL_6801, 260, 63, 3) + setInterfaceText(killer, "128 Slayer Xp", 63, 6) + setInterfaceText(killer, "128 Hitpoint Xp", 63, 7) + } + setVarbit(killer, 1455, 1, true) + rewardXP(killer, Skills.HITPOINTS, 128.0) + rewardXP(killer, Skills.SLAYER, 128.0) + removeAttribute("championsarena:start") + clearHintIcon(killer) + } + clear() + super.finalizeDeath(killer) + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/activity/cchallange/npc/HobgoblinChampionNPC.kt b/Server/src/main/content/global/activity/cchallange/npc/HobgoblinChampionNPC.kt new file mode 100644 index 000000000..01d0ffa89 --- /dev/null +++ b/Server/src/main/content/global/activity/cchallange/npc/HobgoblinChampionNPC.kt @@ -0,0 +1,103 @@ +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.npc.AbstractNPC +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.game.system.task.Pulse +import core.game.world.GameWorld +import core.game.world.map.Location +import core.plugin.Initializable +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +/** + * Represents the Hobgoblin champion NPC for Champions challenge. + */ + +@Initializable +class HobgoblinChampionNPC(id: Int = 0, location: Location? = null) : AbstractNPC(id, location) { + var clearTime = 0 + override fun construct(id: Int, location: Location, vararg objects: Any): AbstractNPC { + return HobgoblinChampionNPC(id, location) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.HOBGOBLIN_CHAMPION_3061) + } + + + override fun handleTickActions() { + super.handleTickActions() + if (clearTime++ > 288) poofClear(this) + } + + companion object { + fun spawnHobgoblinChampion(player: Player) { + val champion = HobgoblinChampionNPC(NPCs.HOBGOBLIN_CHAMPION_3061) + champion.location = location(3170, 9758, 0) + champion.isWalks = true + champion.isAggressive = true + champion.isActive = false + + if (champion.asNpc() != null && champion.isActive) { + champion.properties.teleportLocation = champion.properties.spawnLocation + } + champion.isActive = true + GameWorld.Pulser.submit(object : Pulse(0, champion) { + override fun pulse(): Boolean { + champion.init() + registerHintIcon(player, champion) + champion.attack(player) + return true + } + }) + } + } + + override fun checkImpact(state: BattleState) { + super.checkImpact(state) + val player = state.attacker + if (player is Player) { + if (state.style == CombatStyle.MAGIC || state.style == CombatStyle.RANGE) { + state.neutralizeHits() + state.estimatedHit = state.maximumHit + } + + if (state.style == CombatStyle.MELEE) { + sendMessage(player, "You cannot use melee in this challenge.") + if (state.estimatedHit > -1) { + state.estimatedHit = 0 + return + } + if (state.secondaryHit > -1) { + state.secondaryHit = 0 + return + } + } + } + } + + override fun finalizeDeath(killer: Entity?) { + if (killer is Player) { + lock(killer, 2) + runTask(killer, 1) { + openInterface(killer, 63) + setInterfaceText(killer, "Well done, you defeated the Hobgoblin Champion!", 63, 2) + killer.packetDispatch.sendItemZoomOnInterface(Items.CHAMPION_SCROLL_6802, 260, 63, 3) + setInterfaceText(killer, "232 Slayer Xp", 63, 6) + setInterfaceText(killer, "232 Hitpoint Xp", 63, 7) + } + setVarbit(killer, 1456, 1, true) + rewardXP(killer, Skills.HITPOINTS, 232.0) + rewardXP(killer, Skills.SLAYER, 232.0) + removeAttribute("championsarena:start") + clearHintIcon(killer) + } + clear() + super.finalizeDeath(killer) + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/activity/cchallange/npc/ImpChampionNPC.kt b/Server/src/main/content/global/activity/cchallange/npc/ImpChampionNPC.kt new file mode 100644 index 000000000..4a81fc066 --- /dev/null +++ b/Server/src/main/content/global/activity/cchallange/npc/ImpChampionNPC.kt @@ -0,0 +1,104 @@ +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 +import core.game.system.task.Pulse +import core.game.world.GameWorld +import core.game.world.map.Location +import core.plugin.Initializable +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +/** + * Represents the Imp champion NPC for Champions challenge. + */ + +@Initializable +class ImpChampionNPC(id: Int = 0, location: Location? = null) : AbstractNPC(id, location) { + var clearTime = 0 + override fun construct(id: Int, location: Location, vararg objects: Any): AbstractNPC { + return ImpChampionNPC(id, location) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.IMP_CHAMPION_3062) + } + + override fun handleTickActions() { + super.handleTickActions() + if (clearTime++ > 288) poofClear(this) + } + + companion object { + fun spawnImpChampion(player: Player) { + val champion = ImpChampionNPC(NPCs.IMP_CHAMPION_3062) + champion.location = location(3170, 9758, 0) + champion.isWalks = true + champion.isAggressive = true + champion.isActive = false + + if (champion.asNpc() != null && champion.isActive) { + champion.properties.teleportLocation = champion.properties.spawnLocation + } + + champion.isActive = true + GameWorld.Pulser.submit(object : Pulse(0, champion) { + override fun pulse(): Boolean { + champion.init() + registerHintIcon(player, champion) + champion.attack(player) + return true + } + }) + } + } + + override fun checkImpact(state: BattleState) { + 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) { + 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 + } + } + } + } + + override fun finalizeDeath(killer: Entity?) { + if (killer is Player) { + lock(killer, 2) + runTask(killer, 1) { + openInterface(killer, 63) + setInterfaceText(killer, "Well done, you defeated the Imp Champion!", 63, 2) + killer.packetDispatch.sendItemZoomOnInterface(Items.CHAMPION_SCROLL_6803, 260, 63, 3) + setInterfaceText(killer, "160 Slayer Xp", 63, 6) + setInterfaceText(killer, "160 Hitpoint Xp", 63, 7) + } + setVarbit(killer, 1457, 1, true) + rewardXP(killer, Skills.HITPOINTS, 160.0) + rewardXP(killer, Skills.SLAYER, 160.0) + removeAttribute("championsarena:start") + clearHintIcon(killer) + } + clear() + super.finalizeDeath(killer) + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/activity/cchallange/npc/JogreChampionNPC.kt b/Server/src/main/content/global/activity/cchallange/npc/JogreChampionNPC.kt new file mode 100644 index 000000000..ad6889e7b --- /dev/null +++ b/Server/src/main/content/global/activity/cchallange/npc/JogreChampionNPC.kt @@ -0,0 +1,102 @@ +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.Weapon +import core.game.node.entity.npc.AbstractNPC +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.game.system.task.Pulse +import core.game.world.GameWorld +import core.game.world.map.Location +import core.plugin.Initializable +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +/** + * Represents the Jogre champion NPC for Champions challenge. + */ + +@Initializable +class JogreChampionNPC(id: Int = 0, location: Location? = null) : AbstractNPC(id, location) { + var clearTime = 0 + override fun construct(id: Int, location: Location, vararg objects: Any): AbstractNPC { + return JogreChampionNPC(id, location) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.JOGRE_CHAMPION_3063) + } + + override fun handleTickActions() { + super.handleTickActions() + if (clearTime++ > 288) poofClear(this) + } + + companion object { + fun spawnJogreChampion(player: Player) { + val champion = JogreChampionNPC(NPCs.JOGRE_CHAMPION_3063) + champion.location = location(3170, 9758, 0) + champion.isWalks = true + champion.isAggressive = true + champion.isActive = false + + if (champion.asNpc() != null && champion.isActive) { + champion.properties.teleportLocation = champion.properties.spawnLocation + } + champion.isActive = true + GameWorld.Pulser.submit(object : Pulse(0, champion) { + override fun pulse(): Boolean { + champion.init() + registerHintIcon(player, champion) + champion.attack(player) + return true + } + }) + } + } + + override fun checkImpact(state: BattleState) { + super.checkImpact(state) + val player = state.attacker + if (player is Player) { + if (state.style == CombatStyle.MAGIC || state.style == CombatStyle.MELEE) { + state.neutralizeHits() + state.estimatedHit = state.maximumHit + } + if (state.weapon.type == Weapon.WeaponType.DEGRADING || state.style == CombatStyle.RANGE) { + sendMessage(player, "You cannot use ranged weapons.") + if (state.estimatedHit > -1) { + state.estimatedHit = 0 + return + } + if (state.secondaryHit > -1) { + state.secondaryHit = 0 + return + } + } + } + } + + override fun finalizeDeath(killer: Entity?) { + if (killer is Player) { + lock(killer, 2) + runTask(killer, 1) { + openInterface(killer, 63) + setInterfaceText(killer, "Well done, you defeated the Jogre Champion!", 63, 2) + killer.packetDispatch.sendItemZoomOnInterface(Items.CHAMPION_SCROLL_6804, 260, 63, 3) + setInterfaceText(killer, "480 Slayer Xp", 63, 6) + setInterfaceText(killer, "480 Hitpoint Xp", 63, 7) + } + setVarbit(killer, 1458, 1, true) + rewardXP(killer, Skills.HITPOINTS, 480.0) + rewardXP(killer, Skills.SLAYER, 480.0) + removeAttribute("championsarena:start") + clearHintIcon(killer) + } + clear() + super.finalizeDeath(killer) + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/activity/cchallange/npc/LesserDemonChampionNPC.kt b/Server/src/main/content/global/activity/cchallange/npc/LesserDemonChampionNPC.kt new file mode 100644 index 000000000..e62f73bf2 --- /dev/null +++ b/Server/src/main/content/global/activity/cchallange/npc/LesserDemonChampionNPC.kt @@ -0,0 +1,102 @@ +package content.global.activity.cchallange.npc + +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.npc.AbstractNPC +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.game.system.task.Pulse +import core.game.world.GameWorld +import core.game.world.map.Location +import core.plugin.Initializable +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +/** + * Represents the Lesser demon champion NPC for Champions challenge. + */ + +@Initializable +class LesserDemonChampionNPC(id: Int = 0, location: Location? = null) : AbstractNPC(id, location) { + var clearTime = 0 + override fun construct(id: Int, location: Location, vararg objects: Any): AbstractNPC { + return LesserDemonChampionNPC(id, location) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.LESSER_DEMON_CHAMPION_3064) + } + + override fun handleTickActions() { + super.handleTickActions() + if (clearTime++ > 288) poofClear(this) + } + + companion object { + fun spawnLesserDemonChampion(player: Player) { + val champion = LesserDemonChampionNPC(NPCs.LESSER_DEMON_CHAMPION_3064) + champion.location = location(3170, 9758, 0) + champion.isWalks = true + champion.isAggressive = true + champion.isActive = false + + if (champion.asNpc() != null && champion.isActive) { + champion.properties.teleportLocation = champion.properties.spawnLocation + } + champion.isActive = true + GameWorld.Pulser.submit(object : Pulse(0, champion) { + override fun pulse(): Boolean { + champion.init() + registerHintIcon(player, champion) + champion.attack(player) + return true + } + }) + } + } + + override fun checkImpact(state: BattleState) { + super.checkImpact(state) + val player = state.attacker + if (player is Player) { + if (!player.equipment[3].hasItemPlugin()) { + state.neutralizeHits() + state.estimatedHit = state.maximumHit + } else { + EquipHandler.unequip(player, EquipmentContainer.SLOT_WEAPON, id) + sendMessage(player, "You cannot use weapons in this challenge.") + if (state.estimatedHit > -1) { + state.estimatedHit = 0 + return + } + if (state.secondaryHit > -1) { + state.secondaryHit = 0 + return + } + } + } + } + + override fun finalizeDeath(killer: Entity?) { + if (killer is Player) { + lock(killer, 2) + runTask(killer, 1) { + openInterface(killer, 63) + setInterfaceText(killer, "Well done, you defeated the Lesser Demon Champion!", 63, 2) + killer.packetDispatch.sendItemZoomOnInterface(Items.CHAMPION_SCROLL_6805, 260, 63, 3) + setInterfaceText(killer, "592 Slayer Xp", 63, 6) + setInterfaceText(killer, "592 Hitpoint Xp", 63, 7) + } + setVarbit(killer, 1459, 1, true) + rewardXP(killer, Skills.HITPOINTS, 592.0) + rewardXP(killer, Skills.SLAYER, 592.0) + removeAttribute("championsarena:start") + clearHintIcon(killer) + } + clear() + super.finalizeDeath(killer) + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/activity/cchallange/npc/SkeletonChampionNPC.kt b/Server/src/main/content/global/activity/cchallange/npc/SkeletonChampionNPC.kt new file mode 100644 index 000000000..40a33a420 --- /dev/null +++ b/Server/src/main/content/global/activity/cchallange/npc/SkeletonChampionNPC.kt @@ -0,0 +1,103 @@ +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.Weapon +import core.game.node.entity.npc.AbstractNPC +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.game.system.task.Pulse +import core.game.world.GameWorld +import core.game.world.map.Location +import core.plugin.Initializable +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +/** + * Represents the Skeleton champion NPC for Champions challenge. + */ + +@Initializable +class SkeletonChampionNPC(id: Int = 0, location: Location? = null) : AbstractNPC(id, location) { + var clearTime = 0 + override fun construct(id: Int, location: Location, vararg objects: Any): AbstractNPC { + return SkeletonChampionNPC(id, location) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.SKELETON_CHAMPION_3065) + } + + override fun handleTickActions() { + super.handleTickActions() + if (clearTime++ > 288) poofClear(this) + } + + companion object { + fun spawnSkeletonChampion(player: Player) { + val champion = SkeletonChampionNPC(NPCs.SKELETON_CHAMPION_3065) + champion.location = location(3170, 9758, 0) + champion.isWalks = true + champion.isAggressive = true + champion.isActive = false + + if (champion.asNpc() != null && champion.isActive) { + champion.properties.teleportLocation = champion.properties.spawnLocation + } + champion.isActive = true + GameWorld.Pulser.submit(object : Pulse(0, champion) { + override fun pulse(): Boolean { + champion.init() + animate(champion, 259, true) + registerHintIcon(player, champion) + champion.attack(player) + return true + } + }) + } + } + + override fun checkImpact(state: BattleState) { + super.checkImpact(state) + val player = state.attacker + if (player is Player) { + if (state.style == CombatStyle.RANGE && state.weapon.type == Weapon.WeaponType.DEGRADING && state.weapon.type == Weapon.WeaponType.DOUBLE_SHOT) { + state.neutralizeHits() + state.estimatedHit = state.maximumHit + } + if (state.style == CombatStyle.MAGIC || state.style == CombatStyle.MELEE) { + sendMessage(player, "You can use only ranged weapons in this challenge.") + if (state.estimatedHit > -1) { + state.estimatedHit = 0 + return + } + if (state.secondaryHit > -1) { + state.secondaryHit = 0 + return + } + } + } + } + + override fun finalizeDeath(killer: Entity?) { + if (killer is Player) { + lock(killer, 2) + runTask(killer, 1) { + openInterface(killer, 63) + setInterfaceText(killer, "Well done, you defeated the Skeleton Champion!", 63, 2) + killer.packetDispatch.sendItemZoomOnInterface(Items.CHAMPION_SCROLL_6806, 260, 63, 3) + setInterfaceText(killer, "232 Slayer Xp", 63, 6) + setInterfaceText(killer, "232 Hitpoint Xp", 63, 7) + } + setVarbit(killer, 1460, 1, true) + rewardXP(killer, Skills.HITPOINTS, 232.0) + rewardXP(killer, Skills.SLAYER, 232.0) + removeAttribute("championsarena:start") + clearHintIcon(killer) + } + clear() + super.finalizeDeath(killer) + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/activity/cchallange/npc/ZombieChampionNPC.kt b/Server/src/main/content/global/activity/cchallange/npc/ZombieChampionNPC.kt new file mode 100644 index 000000000..0be9eeb3d --- /dev/null +++ b/Server/src/main/content/global/activity/cchallange/npc/ZombieChampionNPC.kt @@ -0,0 +1,102 @@ +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.npc.AbstractNPC +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.game.system.task.Pulse +import core.game.world.GameWorld +import core.game.world.map.Location +import core.plugin.Initializable +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +/** + * Represents the Zombie champion NPC for Champions challenge. + */ + +@Initializable +class ZombieChampionNPC(id: Int = 0, location: Location? = null) : AbstractNPC(id, location) { + var clearTime = 0 + override fun construct(id: Int, location: Location, vararg objects: Any): AbstractNPC { + return ZombieChampionNPC(id, location) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.ZOMBIES_CHAMPION_3066) + } + + override fun handleTickActions() { + super.handleTickActions() + if (clearTime++ > 288) poofClear(this) + } + + companion object { + fun spawnZombieChampion(player: Player) { + val champion = ZombieChampionNPC(NPCs.ZOMBIES_CHAMPION_3066) + champion.location = location(3170, 9758, 0) + champion.isWalks = true + champion.isAggressive = true + champion.isActive = false + + if (champion.asNpc() != null && champion.isActive) { + champion.properties.teleportLocation = champion.properties.spawnLocation + } + + champion.isActive = true + GameWorld.Pulser.submit(object : Pulse(0, champion) { + override fun pulse(): Boolean { + champion.init() + registerHintIcon(player, champion) + champion.attack(player) + return true + } + }) + } + } + + override fun checkImpact(state: BattleState) { + super.checkImpact(state) + val player = state.attacker + if (player is Player) { + if (state.style == CombatStyle.MELEE || state.style == CombatStyle.RANGE) { + state.estimatedHit = state.maximumHit + state.neutralizeHits() + } + if (state.style == CombatStyle.MAGIC) { + sendMessage(player, "You cannot use spells in this challenge.") + if (state.estimatedHit > -1) { + state.estimatedHit = 0 + return + } + if (state.secondaryHit > -1) { + state.secondaryHit = 0 + return + } + } + } + } + + override fun finalizeDeath(killer: Entity?) { + if (killer is Player) { + lock(killer, 2) + runTask(killer, 1) { + openInterface(killer, 63) + setInterfaceText(killer, "Well done, you defeated the Zombie Champion!", 63, 2) + killer.packetDispatch.sendItemZoomOnInterface(Items.CHAMPION_SCROLL_6807, 260, 63, 3) + setInterfaceText(killer, "240 Slayer Xp", 63, 6) + setInterfaceText(killer, "240 Hitpoint Xp", 63, 7) + } + setVarbit(killer, 1461, 1, true) + rewardXP(killer, Skills.HITPOINTS, 240.0) + rewardXP(killer, Skills.SLAYER, 240.0) + removeAttribute("championsarena:start") + clearHintIcon(killer) + } + clear() + super.finalizeDeath(killer) + } +} diff --git a/Server/src/main/content/global/activity/jobs/CancelJobDialogueFile.kt b/Server/src/main/content/global/activity/jobs/CancelJobDialogueFile.kt deleted file mode 100644 index 62238ad76..000000000 --- a/Server/src/main/content/global/activity/jobs/CancelJobDialogueFile.kt +++ /dev/null @@ -1,83 +0,0 @@ -package content.global.activity.jobs - -import core.api.* -import content.global.activity.jobs.impl.GatheringJobs -import content.global.activity.jobs.impl.SlayingJobs -import core.game.dialogue.FacialExpression -import core.game.node.entity.npc.NPC -import core.game.node.item.Item -import org.rs09.consts.Items -import core.game.dialogue.DialogueFile -import core.game.dialogue.Topic -import core.tools.END_DIALOGUE -import core.tools.START_DIALOGUE - -class CancelJobDialogueFile : DialogueFile() { - override fun handle(componentID: Int, buttonID: Int) { - when (stage) { - START_DIALOGUE -> showTopics( - Topic(core.game.dialogue.FacialExpression.HALF_ASKING, "How am I doing on my job?", 1), - Topic(core.game.dialogue.FacialExpression.HALF_GUILTY, "I'd like to cancel my job.", 10) - ) - - 1 -> { - val jobType = JobType.values()[getAttribute(player!!, "jobs:type", 0)] - val jobId = getAttribute(player!!, "jobs:id", 0) - val needed = getAttribute(player!!, "jobs:amount", 0) - - when (jobType) { - JobType.GATHERING -> { - sendItemDialogue( - player!!, - Item(GatheringJobs.values()[jobId].itemId), - "You still need to gather $needed more." - ) - } - - JobType.SLAYING -> { - sendDialogue( - player!!, - "You still need to kill $needed more ${NPC(SlayingJobs.values()[jobId].ids[0]).name.lowercase()}" - ) - } - } - - stage = END_DIALOGUE - } - - 10 -> npc("It will cost 500 coins.") - .also { stage++ } - - 11 -> showTopics( - Topic(core.game.dialogue.FacialExpression.HAPPY, "Yes, please.", 20), - Topic(core.game.dialogue.FacialExpression.NEUTRAL, "No, thanks.", 30) - ) - - 20 -> npc("Alright then, hand over the money.") - .also { stage++ } - - 21 -> if (inInventory(player!!, Items.COINS_995, 500)) { - player("Here you go.") - - removeItem(player!!, Item(Items.COINS_995, 500)) - removeAttribute(player!!, "jobs:id") - removeAttribute(player!!, "jobs:amount") - removeAttribute(player!!, "jobs:original_amount") - removeAttribute(player!!, "jobs:type") - - stage = END_DIALOGUE - } else { - player( - core.game.dialogue.FacialExpression.HALF_WORRIED, - "Oh, I don't seem to have the money..." - ).also { stage++ } - } - - 22 -> npc("Ah, that sucks! Get to work.") - .also { stage = END_DIALOGUE } - - 30 -> npc("Alright then, get to work!") - .also { stage = END_DIALOGUE } - } - } -} \ No newline at end of file diff --git a/Server/src/main/content/global/activity/jobs/CheckJobDialogueFile.kt b/Server/src/main/content/global/activity/jobs/CheckJobDialogueFile.kt new file mode 100644 index 000000000..fc3a2e58a --- /dev/null +++ b/Server/src/main/content/global/activity/jobs/CheckJobDialogueFile.kt @@ -0,0 +1,174 @@ +package content.global.activity.jobs + +import content.global.activity.jobs.impl.BoneBuryingJobs +import content.global.activity.jobs.impl.CombatJobs +import content.global.activity.jobs.impl.ProductionJobs +import core.api.addItem +import core.api.amountInInventory +import core.api.hasAnItem +import core.game.dialogue.DialogueFile +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.item.Item +import core.tools.END_DIALOGUE +import core.tools.START_DIALOGUE +import core.tools.StringUtils +import org.rs09.consts.Items + +/** + * The dialogue file to handle a player asking about work with the employer that assigned them + * their current job. + */ +class CheckJobDialogueFile : DialogueFile() { + companion object { + const val TURN_IN_ITEMS_1 = 1 + const val TURN_IN_ITEMS_2 = 2 + const val TURN_IN_ITEMS_3 = 3 + const val TURN_IN_ITEMS_4 = 4 + const val TURN_IN_ITEMS_5 = 5 + const val REMIND_JOB = 101 + const val GET_TASK_LIST = 201 + const val GET_NEW_JOB_1 = 301 + const val GET_NEW_JOB_2 = 302 + const val GET_NEW_JOB_3 = 303 + const val GET_NEW_JOB_4 = 304 + const val GET_NEW_JOB_5 = 305 + const val GET_NEW_JOB_NONE = 306 + const val GET_NEW_JOB_REACHED_LIMIT = 307 + } + + override fun handle(componentID: Int, buttonID: Int) { + val playerJobManager: JobManager = JobManager.getInstance(player!!) + val job = playerJobManager.job ?: return + val originalAmount = playerJobManager.jobOriginalAmount + + when (stage) { + START_DIALOGUE -> showTopics( + IfTopic( + FacialExpression.HAPPY, + "I have the items you wanted.", + TURN_IN_ITEMS_1, + (job.type == JobType.PRODUCTION && amountInInventory(player!!, (job as ProductionJobs).itemId) > 0) + ), Topic( + FacialExpression.HALF_ASKING, "Can you remind me what job you gave me?", REMIND_JOB + ), Topic( + FacialExpression.HALF_ASKING, "Can I have a task list to remind me of my job?", GET_TASK_LIST + ), Topic( + FacialExpression.HALF_GUILTY, "I'd like a new job please.", GET_NEW_JOB_1 + ) + ) + + TURN_IN_ITEMS_1 -> { + job as ProductionJobs + val itemName = Item(job.itemId).name.lowercase() + val itemNamePluralized = if (playerJobManager.jobAmount > 1) StringUtils.plusS(itemName) else itemName + npcl("Splendid! Now, I needed $originalAmount $itemNamePluralized.") + stage = TURN_IN_ITEMS_2 + } + + TURN_IN_ITEMS_2 -> { + if (playerJobManager.jobAmount == originalAmount) { + npcl("So far, you haven't brought me any, so I'm waiting for ${playerJobManager.jobAmount}.") + } else { + npcl("So far, you have brought me ${originalAmount - playerJobManager.jobAmount}, so I'm waiting for ${playerJobManager.jobAmount}.") + } + stage = TURN_IN_ITEMS_3 + } + + TURN_IN_ITEMS_3 -> { + if (amountInInventory(player!!, (job as ProductionJobs).itemId) >= playerJobManager.jobAmount) { + npcl("I can see that you have plenty, would you like to hand some over to complete your job?") + } else { + npcl("I can see that you have some, would you like to hand them over?") + } + stage = TURN_IN_ITEMS_4 + } + + TURN_IN_ITEMS_4 -> showTopics( + Topic("Yes, I'll give you what I have.", TURN_IN_ITEMS_5, skipPlayer = true), + Topic("No, I'll keep hold of it for now.", END_DIALOGUE, skipPlayer = true) + ) + + TURN_IN_ITEMS_5 -> { + playerJobManager.turnInItems() + + if (playerJobManager.jobAmount == 0) { + playerJobManager.rewardPlayer() + npcl("There you go, thanks for your help. You're quite a skilled worker!") + } else { + npcl("Thanks for your help. Come back when you've got more!") + } + stage = END_DIALOGUE + } + + REMIND_JOB -> { + when (job.type) { + JobType.PRODUCTION -> { + job as ProductionJobs + val itemName = Item(job.itemId).name.lowercase() + val itemNamePluralized = + if (playerJobManager.jobAmount > 1) StringUtils.plusS(itemName) else itemName + npcl("You still need to gather ${playerJobManager.jobAmount} more $itemNamePluralized.") + } + + JobType.BONE_BURYING -> { + job as BoneBuryingJobs + val boneName = Item(job.boneIds.first()).name.lowercase() + val boneNamePluralized = + if (playerJobManager.jobAmount > 1) StringUtils.plusS(boneName) else boneName + npcl("You still need to bury ${playerJobManager.jobAmount} more $boneNamePluralized in the ${job.buryArea.title}.") + } + + JobType.COMBAT -> { + job as CombatJobs + val monsterName = NPC(job.monsterIds.first()).name.lowercase() + val monsterNamePluralized = + if (playerJobManager.jobAmount > 1) StringUtils.plusS(monsterName) else monsterName + npcl("You still need to kill ${playerJobManager.jobAmount} more $monsterNamePluralized.") + } + } + stage = END_DIALOGUE + } + + GET_TASK_LIST -> { + if (hasAnItem(player!!, Items.TASK_LIST_13464).exists()) { + npcl("You already have one of those!") + } else { + when (addItem(player!!, Items.TASK_LIST_13464)) { + true -> npcl("Here you go then.") + false -> dialogue("You don't have enough space in your inventory.") + } + } + + stage = END_DIALOGUE + } + + GET_NEW_JOB_1 -> npcl("Let me see if I've got any work for you.").also { + if (playerJobManager.hasReachedJobLimit()) { + stage = GET_NEW_JOB_REACHED_LIMIT + } else { + playerJobManager.generate(npc!!) + stage = if (playerJobManager.hasJob()) GET_NEW_JOB_2 else GET_NEW_JOB_NONE + } + } + + GET_NEW_JOB_2 -> npcl(playerJobManager.getAssignmentMessage()).also { stage = GET_NEW_JOB_3 } + // Historically, the player would also have the option to ask for an easier task + GET_NEW_JOB_3 -> playerl("Okay, off I go!").also { stage = GET_NEW_JOB_4 } + GET_NEW_JOB_4 -> npcl("There, I've updated your task list to show your new job. Best of luck!").also { + stage = GET_NEW_JOB_5 + } + + GET_NEW_JOB_5 -> playerl("Thanks.").also { stage = END_DIALOGUE } + GET_NEW_JOB_NONE -> npcl("I'm sorry, I don't currently have any jobs that you're qualified for.").also { + stage = END_DIALOGUE + } + + GET_NEW_JOB_REACHED_LIMIT -> npcl("You've hit your limit for the day. Come back tomorrow!").also { + stage = END_DIALOGUE + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/activity/jobs/Job.kt b/Server/src/main/content/global/activity/jobs/Job.kt new file mode 100644 index 000000000..f4558328c --- /dev/null +++ b/Server/src/main/content/global/activity/jobs/Job.kt @@ -0,0 +1,23 @@ +package content.global.activity.jobs + +import content.global.activity.jobs.impl.Employers +import core.tools.RandomFunction + +/** + * The interface for a job that a player can be assigned. + * + * TODO: Once player data is stored in a database with support for migrations, implement a per-player field to store + * the player's current employer, so that multiple employers can assign the same job, but the player will only be + * able to turn the job into the employer that assigned it to them. + */ +interface Job { + val type: JobType + val lower: Int + val upper: Int + + val employer: Employers + + fun getAmount(): Int { + return RandomFunction.random(lower, upper + 1) + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/activity/jobs/JobManager.kt b/Server/src/main/content/global/activity/jobs/JobManager.kt index cfeac8b51..001aeae6f 100644 --- a/Server/src/main/content/global/activity/jobs/JobManager.kt +++ b/Server/src/main/content/global/activity/jobs/JobManager.kt @@ -1,114 +1,215 @@ package content.global.activity.jobs +import content.global.activity.jobs.impl.BoneBuryingJobs +import content.global.activity.jobs.impl.CombatJobs +import content.global.activity.jobs.impl.ProductionJobs +import core.ServerStore +import core.ServerStore.Companion.getInt import core.api.* -import content.global.activity.jobs.impl.GatheringJobs -import content.global.activity.jobs.impl.SlayingJobs +import core.game.event.BoneBuryEvent +import core.game.event.EventHook +import core.game.event.JobAssignmentEvent +import core.game.event.NPCKillEvent +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.tools.StringUtils import org.json.simple.JSONObject import org.rs09.consts.Items -import core.ServerStore -import core.ServerStore.Companion.getInt +import java.lang.Integer.min -object JobManager { - @JvmStatic - fun updateJobRemaining(player: Player, amount: Int = 0) { - val cur = getAttribute(player, "jobs:amount", 0) - val new = cur - amount - val jobType = JobType.values()[getAttribute(player, "jobs:type", 0)] - val jobId = getAttribute(player, "jobs:id", 0) +/** + * Manages the player's job data & progression. + * + * @source https://runescape.wiki/w/Jobs?oldid=831782 + */ +class JobManager(val player: Player? = null) : LoginListener, PersistPlayer { + var job: Job? = null + var jobAmount: Int = 0 + var jobOriginalAmount: Int = 0 - when (jobType) { - JobType.GATHERING -> { } + private fun hasLevelRequirement(job: ProductionJobs): Boolean { + if (player == null) return false + if (player.isArtificial) return false - JobType.SLAYING -> { - val job = SlayingJobs.values()[jobId] - - if (new % 5 == 0 && new > 0) { - sendMessage( - player, - "You have $new ${NPC(job.ids[0]).name.lowercase()} left to go." - ) - } else if (new == 0) { - sendMessage( - player, - "You have completed your job." - ) - } - } - } - - setAttribute(player, "jobs:amount", new) + return getStatLevel(player, job.skill) >= job.lvlReq } - @JvmStatic - fun rewardPlayer(player: Player, npc: NPC) { - val amt = getAttribute(player, "jobs:original_amount", 0) - val jobType = JobType.values()[getAttribute(player, "jobs:type", 0)] - val jobId = getAttribute(player, "jobs:id", 0) + fun hasJob(): Boolean = this.job != null - when (jobType) { - JobType.GATHERING -> { - val it = Item(GatheringJobs.values()[jobId].itemId) - var amount = amountInInventory(player, it.id) - val needed = getAttribute(player, "jobs:amount", 0) + // Historically the job limit would be 6 jobs per hour of play-time, using 3 per day for balance purposes + fun hasReachedJobLimit(): Boolean = player?.let { getStoreFile().getInt(it.username.lowercase()) >= 3 } ?: false - if (amount == 0) { - openDialogue(player, CancelJobDialogueFile(), npc) - return - } + fun generate(npc: NPC) { + if (player == null) return + if (player.isArtificial) return - if (amount < needed) { - sendItemDialogue( - player, - GatheringJobs.values()[jobId].itemId, - "You still need to gather ${needed - amount} more." - ) + val instance = getInstance(player) - removeItem(player, Item(it.id, amount)) - setAttribute(player, "jobs:amount", needed - amount) - - return - } - - if (amount > needed) { - amount = needed - } - - removeItem(player, Item(it.id, amount)) - } - - JobType.SLAYING -> { - val needed = getAttribute(player, "jobs:amount", 0) - - if (needed > 0) { - sendDialogue( - player, - "You still need to kill $needed more ${NPC(SlayingJobs.values()[jobId].ids[0]).name.lowercase()}." - ) - - return - } - - sendDialogue( - player, - "Excellent work, thank you! Here's your reward." + // Create a list of jobs that the player can do and are given by the NPC being interacted with + val potentialJobs: List = ( + ProductionJobs.values().filter { hasLevelRequirement(it) } + + BoneBuryingJobs.values() + + CombatJobs.values() ) - } - } + .filter { job -> job.employer.npcId == npc.id } - addItemOrDrop(player, Items.COINS_995, 250 * amt) + val job = potentialJobs.randomOrNull() ?: return - removeAttribute(player, "jobs:id") - removeAttribute(player, "jobs:amount") - removeAttribute(player, "jobs:original_amount") - removeAttribute(player, "jobs:type") + instance.job = job + this.jobAmount = job.getAmount() + this.jobOriginalAmount = instance.jobAmount + player.dispatch(JobAssignmentEvent(job.type, npc)) getStoreFile()[player.username.lowercase()] = getStoreFile().getInt(player.username.lowercase()) + 1 } - fun getStoreFile(): JSONObject { - return ServerStore.getArchive("daily-jobs-tracking") + fun getAssignmentMessage(): String? { + if (player == null) return null + if (player.isArtificial) return null + + val instance = getInstance(player) + val job = instance.job ?: return null + + return when (job.type) { + JobType.PRODUCTION -> { + job as ProductionJobs + val itemName = Item(job.itemId).name.lowercase() + val itemNamePluralized = if (this.jobAmount > 1) StringUtils.plusS(itemName) else itemName + "Okay, your job is to bring me ${this.jobAmount} $itemNamePluralized." + } + + JobType.BONE_BURYING -> { + job as BoneBuryingJobs + val boneName = Item(job.boneIds.first()).name.lowercase() + val boneNamePluralized = if (this.jobAmount > 1) StringUtils.plusS(boneName) else boneName + "Okay, your job is to bury ${this.jobAmount} $boneNamePluralized in the ${job.buryArea.title}." + } + + JobType.COMBAT -> { + job as CombatJobs + val monsterName = NPC(job.monsterIds.first()).name.lowercase() + val monsterNamePluralized = if (this.jobAmount > 1) StringUtils.plusS(monsterName) else monsterName + "Okay, your job is to kill ${this.jobAmount} $monsterNamePluralized." + } + } + } + + /** + * TODO: Allow players to turn-in noted items (authentic to 2009) + */ + fun turnInItems() { + if (player == null) return + if (player.isArtificial) return + + val instance = getInstance(player) + val job = instance.job ?: return + if (job.type != JobType.PRODUCTION) return + + val itemId = (job as ProductionJobs).itemId + val amountHeld = amountInInventory(player, itemId) + val amountToTurnIn = min(amountHeld, instance.jobAmount) + removeItem(player, Item(itemId, amountToTurnIn)) + instance.jobAmount -= amountToTurnIn + } + + fun rewardPlayer() { + if (player == null) return + if (player.isArtificial) return + + val instance = getInstance(player) + + addItemOrDrop(player, Items.COINS_995, 250 * instance.jobOriginalAmount) + + instance.job = null + instance.jobAmount = -1 + instance.jobOriginalAmount = -1 + } + + override fun login(player: Player) { + if (player.isArtificial) return + + val jobManager = JobManager(player) + setAttribute(player, "job-manager", jobManager) + + player.hook(Event.BoneBuried, object : EventHook { + override fun process(entity: Entity, event: BoneBuryEvent) { + if (entity !is Player) return + if (entity.isArtificial) return + + val instance = getInstance(entity) + + if (!instance.hasJob()) return + if (instance.job?.type != JobType.BONE_BURYING) return + if (instance.jobAmount == 0) return + + val job = (instance.job as BoneBuryingJobs) + if (inBorders(entity, job.buryArea.zoneBorder) && event.boneId in job.boneIds) { + instance.jobAmount-- + } + } + }) + player.hook(Event.NPCKilled, object : EventHook { + override fun process(entity: Entity, event: NPCKillEvent) { + if (entity !is Player) return + if (entity.isArtificial) return + + val instance = getInstance(entity) + + if (!instance.hasJob()) return + if (instance.job?.type != JobType.COMBAT) return + if (instance.jobAmount == 0) return + + val job = (instance.job as CombatJobs) + if (event.npc.id in job.monsterIds) { + instance.jobAmount-- + } + } + }) + } + + // TODO: Implement proper saving of job data to JSONObject once player save migration is possible + override fun savePlayer(player: Player, save: JSONObject) { + val instance = getInstance(player) + val jobId: Int = instance.job?.let { + when (it.type) { + JobType.PRODUCTION -> ProductionJobs.values().indexOf(it) + JobType.COMBAT -> CombatJobs.values().indexOf(it) + JobType.BONE_BURYING -> BoneBuryingJobs.values().indexOf(it) + } + } ?: -1 + + setAttribute(player, "/save:jobs:id", jobId) + setAttribute(player, "/save:jobs:type", instance.job?.type?.ordinal ?: -1) + setAttribute(player, "/save:jobs:amount", instance.jobAmount) + setAttribute(player, "/save:jobs:original_amount", instance.jobOriginalAmount) + } + + // TODO: Implement proper loading of job data from JSONObject once player save migration is possible + override fun parsePlayer(player: Player, data: JSONObject) { + val instance = getInstance(player) + val jobId = getAttribute(player, "jobs:id", -1) + val jobType = getAttribute(player, "jobs:type", -1) + + instance.job = when (JobType.values().getOrNull(jobType)) { + JobType.PRODUCTION -> ProductionJobs.values().getOrNull(jobId) + JobType.COMBAT -> CombatJobs.values().getOrNull(jobId) + JobType.BONE_BURYING -> BoneBuryingJobs.values().getOrNull(jobId) + else -> null + } + instance.jobAmount = getAttribute(player, "jobs:amount", -1) + instance.jobOriginalAmount = getAttribute(player, "jobs:original_amount", -1) + } + + companion object { + @JvmStatic + fun getInstance(player: Player): JobManager { + return player.getAttribute("job-manager", JobManager(player)) + } + + fun getStoreFile(): JSONObject { + return ServerStore.getArchive("daily-jobs-tracking") + } } } \ No newline at end of file diff --git a/Server/src/main/content/global/activity/jobs/JobType.kt b/Server/src/main/content/global/activity/jobs/JobType.kt index fc1c6bb65..c34a67811 100644 --- a/Server/src/main/content/global/activity/jobs/JobType.kt +++ b/Server/src/main/content/global/activity/jobs/JobType.kt @@ -1,6 +1,13 @@ package content.global.activity.jobs +/** + * An enum of the different types of jobs that a player can be assigned. + * + * Note: Due to how player save files keep track of the player's current job, it is essential that + * new entries are only appended to the end of this enum, and the ordering of existing entries is not changed. + */ enum class JobType { - GATHERING, - SLAYING + PRODUCTION, + COMBAT, + BONE_BURYING } \ No newline at end of file diff --git a/Server/src/main/content/global/activity/jobs/StartJobDialogueFile.kt b/Server/src/main/content/global/activity/jobs/StartJobDialogueFile.kt new file mode 100644 index 000000000..b431663e7 --- /dev/null +++ b/Server/src/main/content/global/activity/jobs/StartJobDialogueFile.kt @@ -0,0 +1,139 @@ +package content.global.activity.jobs + +import content.global.activity.jobs.impl.BoneBuryingJobs +import content.global.activity.jobs.impl.CombatJobs +import content.global.activity.jobs.impl.ProductionJobs +import core.api.addItem +import core.api.hasAnItem +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 core.tools.START_DIALOGUE +import core.tools.StringUtils +import org.rs09.consts.Items + +/** + * The dialogue file to handle a player asking about work with any employer which they aren't currently working for. + */ +class StartJobDialogueFile : DialogueFile() { + companion object { + const val GET_TASK_LIST_1 = 1 + const val GET_TASK_LIST_2 = 2 + const val GET_TASK_LIST_YES = 3 + const val GET_JOB_1 = 101 + const val GET_JOB_2 = 102 + const val GET_JOB_3 = 103 + const val GET_JOB_4 = 104 + const val GET_JOB_5 = 105 + const val GET_JOB_NONE = 201 + const val GET_JOB_EMPLOYED_1 = 301 + const val GET_JOB_EMPLOYED_2 = 302 + const val GET_JOB_LIMIT_REACHED = 401 + } + + override fun handle(componentID: Int, buttonID: Int) { + val playerJobManager: JobManager = JobManager.getInstance(player!!) + + when (stage) { + START_DIALOGUE -> playerl(FacialExpression.ASKING, "Do you have any jobs for me?").also { + stage = if (hasAnItem(player!!, Items.TASK_LIST_13464).exists()) { + if (playerJobManager.hasJob()) { + GET_JOB_EMPLOYED_1 + } else { + GET_JOB_1 + } + } else { + GET_TASK_LIST_1 + } + } + + GET_TASK_LIST_1 -> npcl("Well, first of all, I can see that you don't have a task list to remind you about your current job. Would you like a task list now?").also { + stage = GET_TASK_LIST_2 + } + + GET_TASK_LIST_2 -> showTopics( + Topic("Yes, please.", GET_TASK_LIST_YES), + Topic("No, thanks.", if (playerJobManager.hasJob()) GET_JOB_EMPLOYED_1 else GET_JOB_1) + ) + + GET_TASK_LIST_YES -> { + when (addItem(player!!, Items.TASK_LIST_13464)) { + true -> npcl("Here you go then.").also { + stage = if (playerJobManager.hasJob()) GET_JOB_EMPLOYED_1 else GET_JOB_1 + } + + false -> dialogue("You don't have enough space in your inventory.").also { stage = END_DIALOGUE } + } + } + + GET_JOB_1 -> npcl("Let me see if I've got any work for you.").also { + if (playerJobManager.hasReachedJobLimit()) { + stage = GET_JOB_LIMIT_REACHED + } else { + playerJobManager.generate(npc!!) + stage = if (playerJobManager.hasJob()) GET_JOB_2 else GET_JOB_NONE + } + } + + GET_JOB_2 -> npcl(playerJobManager.getAssignmentMessage()).also { stage = GET_JOB_3 } + // Historically, the player would also have the option to ask for an easier task + GET_JOB_3 -> playerl("Okay, off I go!").also { stage = GET_JOB_4 } + GET_JOB_4 -> npcl("There, I've updated your task list to show your new job. Best of luck!").also { + stage = GET_JOB_5 + } + + GET_JOB_5 -> playerl("Thanks.").also { stage = END_DIALOGUE } + GET_JOB_EMPLOYED_1 -> { + val job = playerJobManager.job ?: return + val employerName = NPC(job.employer.npcId).name + val amount = playerJobManager.jobAmount + val originalAmount = playerJobManager.jobOriginalAmount + + if (amount == 0) { + npcl("Hang about. Aren't you working for $employerName? It looks like you've complete their job, return to them for your reward.") + stage = END_DIALOGUE + } else { + when (job.type) { + JobType.PRODUCTION -> { + job as ProductionJobs + val itemName = Item(job.itemId).name.lowercase() + val itemNamePluralized = if (originalAmount > 1) StringUtils.plusS(itemName) else itemName + npcl("Hang about. Aren't you working for $employerName? You were asked for $originalAmount $itemNamePluralized and still have $amount more to go.") + } + + JobType.BONE_BURYING -> { + job as BoneBuryingJobs + val boneName = Item(job.boneIds.first()).name.lowercase() + val boneNamePluralized = if (originalAmount > 1) StringUtils.plusS(boneName) else boneName + npcl("Hang about. Aren't you working for $employerName? You were asked to bury $originalAmount $boneNamePluralized in the ${job.buryArea.title} and still have $amount more to go.") + } + + JobType.COMBAT -> { + job as CombatJobs + val monsterName = NPC(job.monsterIds.first()).name.lowercase() + val monsterNamePluralized = if (originalAmount > 1) StringUtils.plusS(monsterName) else monsterName + npcl("Hang about. Aren't you working for $employerName? You were asked to kill $originalAmount $monsterNamePluralized and still have $amount more to go.") + } + } + stage = GET_JOB_EMPLOYED_2 + } + } + + GET_JOB_EMPLOYED_2 -> showTopics( + Topic("Oh yes. I better go and finish their job first.", END_DIALOGUE), + Topic("I'd like a new job instead, please.", GET_JOB_1) + ) + + GET_JOB_NONE -> npcl("I'm sorry, I don't currently have any jobs that you're qualified for.").also { + stage = END_DIALOGUE + } + + GET_JOB_LIMIT_REACHED -> npcl("You've hit your limit for the day. Come back tomorrow!").also { + stage = END_DIALOGUE + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/activity/jobs/TaskListReadListener.kt b/Server/src/main/content/global/activity/jobs/TaskListReadListener.kt new file mode 100644 index 000000000..2e3c06d7b --- /dev/null +++ b/Server/src/main/content/global/activity/jobs/TaskListReadListener.kt @@ -0,0 +1,70 @@ +package content.global.activity.jobs + +import content.global.activity.jobs.impl.BoneBuryingJobs +import content.global.activity.jobs.impl.CombatJobs +import content.global.activity.jobs.impl.ProductionJobs +import core.api.sendDialogue +import core.api.sendItemDialogue +import core.api.sendNPCDialogue +import core.game.interaction.InteractionListener +import core.game.node.entity.npc.NPC +import core.game.node.item.Item +import core.tools.StringUtils +import org.rs09.consts.Items + +/** + * Handles the read option of the Task List item. + */ +class TaskListReadListener : InteractionListener { + override fun defineListeners() { + on(Items.TASK_LIST_13464, ITEM, "read") { player, _ -> + val playerJobManager: JobManager = JobManager.getInstance(player) + val job = playerJobManager.job + val amount = playerJobManager.jobAmount + + if (job == null) { + sendDialogue(player, "I have not been assigned a job!") + return@on false + } + + if(amount == 0) { + sendDialogue( + player, "I have completed my job. I should return to ${NPC(job.employer.npcId).name} for my reward." + ) + return@on true + } + + when (job.type) { + JobType.PRODUCTION -> { + job as ProductionJobs + val itemName = Item(job.itemId).name.lowercase() + val itemNamePluralized = if (amount > 1) StringUtils.plusS(itemName) else itemName + sendItemDialogue( + player, job.itemId, "My job is to gather $amount more $itemNamePluralized." + ) + } + + JobType.BONE_BURYING -> { + job as BoneBuryingJobs + val boneName = Item(job.boneIds.first()).name.lowercase() + val boneNamePluralized = if (amount > 1) StringUtils.plusS(boneName) else boneName + sendItemDialogue( + player, + job.boneIds.first(), + "My job is to bury $amount more $boneNamePluralized in the ${job.buryArea.title}." + ) + } + + JobType.COMBAT -> { + job as CombatJobs + val monsterName = NPC(job.monsterIds.first()).name.lowercase() + val monsterNamePluralized = if (amount > 1) StringUtils.plusS(monsterName) else monsterName + sendNPCDialogue( + player, job.monsterIds.first(), "My job is to kill $amount more $monsterNamePluralized." + ) + } + } + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/activity/jobs/WorkForInteractionListener.kt b/Server/src/main/content/global/activity/jobs/WorkForInteractionListener.kt index ad987feea..8d09d7dfd 100644 --- a/Server/src/main/content/global/activity/jobs/WorkForInteractionListener.kt +++ b/Server/src/main/content/global/activity/jobs/WorkForInteractionListener.kt @@ -1,224 +1,45 @@ package content.global.activity.jobs -import content.global.activity.jobs.impl.GatheringJobs -import content.global.activity.jobs.impl.SlayingJobs -import core.api.* -import core.game.event.EventHook -import core.game.event.JobAssignmentEvent -import core.game.event.NPCKillEvent -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 org.rs09.consts.Items -import org.rs09.consts.NPCs -import core.ServerStore.Companion.getInt -import core.api.Event -import core.game.interaction.InteractionListener +import content.global.activity.jobs.impl.Employers +import core.api.openDialogue +import core.api.sendNPCDialogue import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.npc.NPC /** * Handles the work-for actions for the NPCs * @author Ceikry * @author vddCore */ -class WorkForInteractionListener : InteractionListener, LoginListener { - companion object { - private val WEAPON_ICONS = arrayListOf( - Items.BRONZE_SCIMITAR_1321, - Items.STEEL_SCIMITAR_1325, - Items.RUNE_SCIMITAR_1333, - Items.BRONZE_MACE_1422, - Items.MITHRIL_WARHAMMER_1343 - ) - - private val EMPLOYERS_AND_JOBS = mapOf( - NPCs.HANS_0 to listOf( - GatheringJobs.AIR_RUNE, - GatheringJobs.COWHIDES, - GatheringJobs.WATER_RUNE - ), - - NPCs.AGGIE_922 to listOf( - GatheringJobs.ASHES, - GatheringJobs.AIR_RUNE, - GatheringJobs.WATER_RUNE - ), - - NPCs.GILLIE_GROATS_3807 to listOf( - GatheringJobs.COWHIDES - ), - - NPCs.COOKING_TUTOR_4899 to listOf( - GatheringJobs.CAKE, - GatheringJobs.ANCHOVY_PIZZA, - GatheringJobs.COOKED_SALMON, - GatheringJobs.COOKED_TROUT, - GatheringJobs.MEAT_PIE, - GatheringJobs.MEAT_PIZZA, - GatheringJobs.PLAIN_PIZZA - ), - - NPCs.FISHING_TUTOR_4901 to listOf( - GatheringJobs.RAW_SALMON, - GatheringJobs.RAW_SHRIMP, - GatheringJobs.RAW_TROUT - ), - - NPCs.MINING_TUTOR_4902 to listOf( - GatheringJobs.COPPER_ORE, - GatheringJobs.TIN_ORE, - GatheringJobs.COAL, - GatheringJobs.IRON_ORE, - GatheringJobs.SILVER_ORE, - GatheringJobs.GOLD_ORE - ), - - NPCs.PRAYER_TUTOR_4903 to listOf( - GatheringJobs.SILVER_ORE - ), - - NPCs.SMELTING_TUTOR_4904 to listOf( - GatheringJobs.BRONZE_BAR, - GatheringJobs.IRON_BAR, - GatheringJobs.STEEL_BAR - ), - - NPCs.WOODCUTTING_TUTOR_4906 to listOf( - GatheringJobs.LOG, - GatheringJobs.OAK, - GatheringJobs.WILLOW - ) - ) - - private val EMPLOYER_JOB_TYPES = mapOf( - NPCs.HANS_0 to JobType.GATHERING, - NPCs.MELEE_TUTOR_705 to JobType.SLAYING, - NPCs.AGGIE_922 to JobType.GATHERING, - NPCs.RANGED_TUTOR_1861 to JobType.SLAYING, - NPCs.GILLIE_GROATS_3807 to JobType.GATHERING, - NPCs.MAGIC_TUTOR_4707 to JobType.SLAYING, - NPCs.COOKING_TUTOR_4899 to JobType.GATHERING, - NPCs.FISHING_TUTOR_4901 to JobType.GATHERING, - NPCs.MINING_TUTOR_4902 to JobType.GATHERING, - NPCs.PRAYER_TUTOR_4903 to JobType.GATHERING, - NPCs.SMELTING_TUTOR_4904 to JobType.GATHERING, - NPCs.WOODCUTTING_TUTOR_4906 to JobType.GATHERING - ) - - private val EMPLOYER_IDS = EMPLOYER_JOB_TYPES.keys.toIntArray() - } - - - override fun login(player: Player) { - val remaining = getAttribute(player, "jobs:amount", -1) - val jobType = JobType.values()[getAttribute(player, "jobs:type", 0)] - - if (jobType == JobType.SLAYING && remaining > 0) { - player.hook(Event.NPCKilled, JobKillHook) - } - } - +class WorkForInteractionListener : InteractionListener { override fun defineListeners() { - on(EMPLOYER_IDS, IntType.NPC, "work-for") { player, node -> - if (JobManager.getStoreFile().getInt(player.username.lowercase()) == 3) { - sendNPCDialogue(player, node.id, "You've hit your limit for the day. Come back tomorrow!") + on( + Employers.values().map { employer -> employer.npcId }.toIntArray(), + IntType.NPC, + "work-for" + ) { player, node -> + val playerJobManager = JobManager.getInstance(player) + val job = playerJobManager.job + + // Check if the player is talking to the NPC that gave them their job + if (job != null && job.employer.npcId == node.id) { + if (playerJobManager.jobAmount == 0) { + playerJobManager.rewardPlayer() + sendNPCDialogue( + player, + job.employer.npcId, + "There you go, thanks for your help. You're quite a skilled worker!" + ) + } else { + openDialogue(player, CheckJobDialogueFile(), node as NPC) + } + return@on true } - if (getAttribute(player, "jobs:id", -1) != -1) { - JobManager.rewardPlayer(player, node.asNpc()) - return@on true - } - - var amount: Int - val jobType = EMPLOYER_JOB_TYPES[node.id] ?: return@on false - val jobId = when (jobType) { - JobType.GATHERING -> { - val job = EMPLOYERS_AND_JOBS[node.id]?.filter { checkRequirement(player, it) }?.randomOrNull() - - if (job == null) { - sendNPCDialogue( - player, - node.id, - "I'm sorry, I don't currently have any jobs that you're qualified for.", - core.game.dialogue.FacialExpression.HALF_THINKING - ) - return@on true - } - - amount = job.getAmount() - job.ordinal - } - - JobType.SLAYING -> { - SlayingJobs.values().random().ordinal.also { - amount = SlayingJobs.values()[it].getAmount() - } - } - } - - when (jobType) { - JobType.GATHERING -> { - val job = GatheringJobs.values()[jobId] - - sendItemDialogue( - player, - job.itemId, - "You are assigned to gather $amount ${Item(job.itemId).name.lowercase()}" - ) - } - - JobType.SLAYING -> { - val job = SlayingJobs.values()[jobId] - - sendItemDialogue( - player, - WEAPON_ICONS.random(), - "You are assigned to kill $amount ${NPC(job.ids[0]).name.lowercase()}" - ) - - player.unhook(JobKillHook) //try to unhook just in case they already had it hooked, unlikely but possible. - player.hook(Event.NPCKilled, JobKillHook) - } - } - - setAttribute(player, "/save:jobs:id", jobId) - setAttribute(player, "/save:jobs:amount", amount) - setAttribute(player, "/save:jobs:original_amount", amount) - setAttribute(player, "/save:jobs:type", jobType.ordinal) - - player.dispatch(JobAssignmentEvent(jobType, node.asNpc())) + openDialogue(player, StartJobDialogueFile(), node as NPC) return@on true } } - - private fun checkRequirement(player: Player, jobs: GatheringJobs?): Boolean { - jobs ?: return true - - if (getStatLevel(player, jobs.skill) < jobs.lvlReq) { - return false - } - - return true - } - - private object JobKillHook : EventHook { - override fun process(entity: Entity, event: NPCKillEvent) { - if (entity !is Player) return - if (entity.isArtificial) return - - val job = getAttribute(entity, "jobs:id", -1) - - if (job !in 0 until SlayingJobs.values().size) { - return - } - - val ids = SlayingJobs.values()[job].ids - - if (event.npc.id in ids) { - JobManager.updateJobRemaining(entity, 1) - } - } - } } \ No newline at end of file diff --git a/Server/src/main/content/global/activity/jobs/impl/BoneBuryingJobs.kt b/Server/src/main/content/global/activity/jobs/impl/BoneBuryingJobs.kt new file mode 100644 index 000000000..fad0448e0 --- /dev/null +++ b/Server/src/main/content/global/activity/jobs/impl/BoneBuryingJobs.kt @@ -0,0 +1,30 @@ +package content.global.activity.jobs.impl + +import content.global.activity.jobs.Job +import content.global.activity.jobs.JobType +import core.game.world.map.zone.ZoneBorders +import org.rs09.consts.Items + +/** + * An enum of the possible bone-burying jobs that can be assigned to a player. + * + * Note: Due to how player save files keep track of the player's current job, it is essential that + * new entries are only appended to the end of this enum, and the ordering of existing entries is not changed. + */ +enum class BoneBuryingJobs(val buryArea: BuryArea, val boneIds: List, override val employer: Employers) : Job { + BURY_DRAYNOR(BuryArea.DRAYNOR_WHEAT_FIELD, Bones.BONES, Employers.PRAYER_TUTOR), + BURY_LUMBRIDGE_GRAVEYARD(BuryArea.LUMBRIDGE_GRAVEYARD, Bones.BONES, Employers.PRAYER_TUTOR); + + override val type = JobType.BONE_BURYING + override val lower = 22 + override val upper = 26 + + enum class BuryArea(val zoneBorder: ZoneBorders, val title: String) { + DRAYNOR_WHEAT_FIELD(ZoneBorders(3107, 3265, 3132, 3293), "wheat field east of Draynor Village"), + LUMBRIDGE_GRAVEYARD(ZoneBorders(3238, 3191, 3252, 3204), "Lumbridge Graveyard"); + } + + object Bones { + val BONES = listOf(Items.BONES_526, Items.BONES_2530) + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/activity/jobs/impl/CombatJobs.kt b/Server/src/main/content/global/activity/jobs/impl/CombatJobs.kt new file mode 100644 index 000000000..cfe444f51 --- /dev/null +++ b/Server/src/main/content/global/activity/jobs/impl/CombatJobs.kt @@ -0,0 +1,209 @@ +package content.global.activity.jobs.impl + +import content.global.activity.jobs.Job +import content.global.activity.jobs.JobType +import org.rs09.consts.NPCs + +/** + * An enum of the possible combat jobs that can be assigned to a player. + * + * Note: Due to how player save files keep track of the player's current job, it is essential that + * new entries are only appended to the end of this enum, and the ordering of existing entries is not changed. + */ +enum class CombatJobs(val monsterIds: List, override val employer: Employers) : Job { + CHICKENS(Monsters.CHICKENS, Employers.RANGED_TUTOR), + COWS(Monsters.COWS, Employers.MAGIC_TUTOR), + GIANT_SPIDERS(Monsters.GIANT_SPIDERS, Employers.MAGIC_TUTOR), + SCORPIONS(Monsters.SCORPIONS, Employers.MELEE_TUTOR), + GOBLINS(Monsters.GOBLINS, Employers.MELEE_TUTOR); + + override val type = JobType.COMBAT + override val lower = 22 + override val upper = 28 + + object Monsters { + val GIANT_SPIDERS = listOf(NPCs.GIANT_SPIDER_59, NPCs.GIANT_SPIDER_60) + val SCORPIONS = listOf(NPCs.SCORPION_107, NPCs.SCORPION_1477, NPCs.SCORPION_4402, NPCs.SCORPION_4403) + val GOBLINS = listOf( + NPCs.GOBLIN_100, + NPCs.GOBLIN_101, + NPCs.GOBLIN_102, + NPCs.GOBLIN_1769, + NPCs.GOBLIN_1770, + NPCs.GOBLIN_1771, + NPCs.GOBLIN_1772, + NPCs.GOBLIN_1773, + NPCs.GOBLIN_1774, + NPCs.GOBLIN_1775, + NPCs.GOBLIN_1776, + NPCs.GOBLIN_2274, + NPCs.GOBLIN_2275, + NPCs.GOBLIN_2276, + NPCs.GOBLIN_2277, + NPCs.GOBLIN_2278, + NPCs.GOBLIN_2279, + NPCs.GOBLIN_2280, + NPCs.GOBLIN_2281, + NPCs.GOBLIN_2678, + NPCs.GOBLIN_2679, + NPCs.GOBLIN_2680, + NPCs.GOBLIN_2681, + NPCs.GOBLIN_3264, + NPCs.GOBLIN_3265, + NPCs.GOBLIN_3266, + NPCs.GOBLIN_3267, + NPCs.GOBLIN_3726, + NPCs.GOBLIN_4261, + NPCs.GOBLIN_4262, + NPCs.GOBLIN_4263, + NPCs.GOBLIN_4264, + NPCs.GOBLIN_4265, + NPCs.GOBLIN_4266, + NPCs.GOBLIN_4267, + NPCs.GOBLIN_4268, + NPCs.GOBLIN_4269, + NPCs.GOBLIN_4270, + NPCs.GOBLIN_4271, + NPCs.GOBLIN_4272, + NPCs.GOBLIN_4273, + NPCs.GOBLIN_4274, + NPCs.GOBLIN_4275, + NPCs.GOBLIN_4276, + NPCs.GOBLIN_4407, + NPCs.GOBLIN_4408, + NPCs.GOBLIN_4409, + NPCs.GOBLIN_4410, + NPCs.GOBLIN_4411, + NPCs.GOBLIN_4412, + NPCs.GOBLIN_444, + NPCs.GOBLIN_445, + NPCs.GOBLIN_4479, + NPCs.GOBLIN_4480, + NPCs.GOBLIN_4481, + NPCs.GOBLIN_4482, + NPCs.GOBLIN_4483, + NPCs.GOBLIN_4484, + NPCs.GOBLIN_4485, + NPCs.GOBLIN_4486, + NPCs.GOBLIN_4487, + NPCs.GOBLIN_4488, + NPCs.GOBLIN_4489, + NPCs.GOBLIN_4490, + NPCs.GOBLIN_4491, + NPCs.GOBLIN_4492, + NPCs.GOBLIN_4499, + NPCs.GOBLIN_4633, + NPCs.GOBLIN_4634, + NPCs.GOBLIN_4635, + NPCs.GOBLIN_4636, + NPCs.GOBLIN_4637, + NPCs.GOBLIN_5855, + NPCs.GOBLIN_5856, + NPCs.GOBLIN_6125, + NPCs.GOBLIN_6126, + NPCs.GOBLIN_6132, + NPCs.GOBLIN_6133, + NPCs.GOBLIN_6279, + NPCs.GOBLIN_6280, + NPCs.GOBLIN_6281, + NPCs.GOBLIN_6282, + NPCs.GOBLIN_6283, + NPCs.GOBLIN_6284, + NPCs.GOBLIN_6402, + NPCs.GOBLIN_6403, + NPCs.GOBLIN_6404, + NPCs.GOBLIN_6405, + NPCs.GOBLIN_6406, + NPCs.GOBLIN_6407, + NPCs.GOBLIN_6408, + NPCs.GOBLIN_6409, + NPCs.GOBLIN_6410, + NPCs.GOBLIN_6411, + NPCs.GOBLIN_6412, + NPCs.GOBLIN_6413, + NPCs.GOBLIN_6414, + NPCs.GOBLIN_6415, + NPCs.GOBLIN_6416, + NPCs.GOBLIN_6417, + NPCs.GOBLIN_6418, + NPCs.GOBLIN_6419, + NPCs.GOBLIN_6420, + NPCs.GOBLIN_6421, + NPCs.GOBLIN_6422, + NPCs.GOBLIN_6423, + NPCs.GOBLIN_6424, + NPCs.GOBLIN_6425, + NPCs.GOBLIN_6426, + NPCs.GOBLIN_6427, + NPCs.GOBLIN_6428, + NPCs.GOBLIN_6429, + NPCs.GOBLIN_6430, + NPCs.GOBLIN_6431, + NPCs.GOBLIN_6432, + NPCs.GOBLIN_6433, + NPCs.GOBLIN_6434, + NPCs.GOBLIN_6435, + NPCs.GOBLIN_6436, + NPCs.GOBLIN_6437, + NPCs.GOBLIN_6438, + NPCs.GOBLIN_6439, + NPCs.GOBLIN_6440, + NPCs.GOBLIN_6441, + NPCs.GOBLIN_6442, + NPCs.GOBLIN_6443, + NPCs.GOBLIN_6444, + NPCs.GOBLIN_6445, + NPCs.GOBLIN_6446, + NPCs.GOBLIN_6447, + NPCs.GOBLIN_6448, + NPCs.GOBLIN_6449, + NPCs.GOBLIN_6450, + NPCs.GOBLIN_6451, + NPCs.GOBLIN_6452, + NPCs.GOBLIN_6453, + NPCs.GOBLIN_6454, + NPCs.GOBLIN_6455, + NPCs.GOBLIN_6456, + NPCs.GOBLIN_6457, + NPCs.GOBLIN_6458, + NPCs.GOBLIN_6459, + NPCs.GOBLIN_6460, + NPCs.GOBLIN_6461, + NPCs.GOBLIN_6462, + NPCs.GOBLIN_6463, + NPCs.GOBLIN_6464, + NPCs.GOBLIN_6465, + NPCs.GOBLIN_6466, + NPCs.GOBLIN_6467, + NPCs.GOBLIN_6490, + NPCs.GOBLIN_6491, + NPCs.GOBLIN_6492, + NPCs.GOBLIN_6493, + NPCs.GOBLIN_6494, + NPCs.GOBLIN_6495, + NPCs.GOBLIN_7964, + NPCs.GOBLIN_7965 + ) + val COWS = listOf( + NPCs.COW_81, + NPCs.COW_397, + NPCs.COW_955, + NPCs.COW_1767, + NPCs.COW_3309, + NPCs.COW_CALF_1766, + NPCs.COW_CALF_1768, + NPCs.COW_CALF_2310 + ) + val CHICKENS = listOf( + NPCs.CHICKEN_1017, + NPCs.CHICKEN_1401, + NPCs.CHICKEN_1402, + NPCs.CHICKEN_2313, + NPCs.CHICKEN_2314, + NPCs.CHICKEN_2315, + NPCs.CHICKEN_288, + NPCs.CHICKEN_41, + NPCs.CHICKEN_951, + ) + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/activity/jobs/impl/Employers.kt b/Server/src/main/content/global/activity/jobs/impl/Employers.kt new file mode 100644 index 000000000..2d1fc165c --- /dev/null +++ b/Server/src/main/content/global/activity/jobs/impl/Employers.kt @@ -0,0 +1,24 @@ +package content.global.activity.jobs.impl + +import org.rs09.consts.NPCs + +/** + * An enum of NPCs that can assign the player a job. + * + * TODO: Add the rest of the era-correct employers + */ +enum class Employers(val npcId: Int) { + WOODCUTTING_TUTOR(NPCs.WOODCUTTING_TUTOR_4906), + MAGIC_TUTOR(NPCs.MAGIC_TUTOR_4707), + MELEE_TUTOR(NPCs.MELEE_TUTOR_705), + RANGED_TUTOR(NPCs.RANGED_TUTOR_1861), + COOKING_TUTOR(NPCs.COOKING_TUTOR_4899), + CRAFTING_TUTOR(NPCs.CRAFTING_TUTOR_4900), + FISHING_TUTOR(NPCs.FISHING_TUTOR_4901), + MINING_TUTOR(NPCs.MINING_TUTOR_4902), + SMELTING_TUTOR(NPCs.SMELTING_TUTOR_4904), + PRAYER_TUTOR(NPCs.PRAYER_TUTOR_4903), + HANS(NPCs.HANS_0), + GILLIE_GROATS(NPCs.GILLIE_GROATS_3807), + AGGIE(NPCs.AGGIE_922); +} \ No newline at end of file diff --git a/Server/src/main/content/global/activity/jobs/impl/GatheringJobs.kt b/Server/src/main/content/global/activity/jobs/impl/GatheringJobs.kt deleted file mode 100644 index ddb4c0973..000000000 --- a/Server/src/main/content/global/activity/jobs/impl/GatheringJobs.kt +++ /dev/null @@ -1,68 +0,0 @@ -package content.global.activity.jobs.impl - -import core.game.node.entity.skill.Skills -import core.tools.RandomFunction - -enum class GatheringJobs(val lower: Int, val upper: Int, val lvlReq: Int, val itemId: Int,val skill: Int) { - LOG(20, 28, 1, 1511, Skills.WOODCUTTING), - COWHIDES(20, 28, 1, 1739, 0), - OAK(22, 28, 15, 1521, Skills.WOODCUTTING), - WATER_RUNE(20, 28, 5, 555, Skills.RUNECRAFTING), - EARTH_RUNE(20, 28, 9, 557, Skills.RUNECRAFTING), - FIRE_RUNE(20, 28, 14, 554, Skills.RUNECRAFTING), - AIR_RUNE(20, 28, 1, 556, Skills.RUNECRAFTING), - RUNE_ESS(20, 28, 1, 1436, Skills.MINING), - BALL_OF_WOOL(20, 28, 1, 1759, Skills.CRAFTING), - LEATHER_GLOVE(20, 28, 1, 1059, Skills.CRAFTING), - WILLOW(22, 28, 30, 1519, Skills.WOODCUTTING), - LEATHER_BOOT(24, 24, 1, 1061, Skills.CRAFTING), - BRONZE_DAGGER(24, 24, 1, 1205, Skills.SMITHING), - BRONZE_HELMS(22, 22, 1, 1139, Skills.SMITHING), - BRONZE_FULL_HELM(10, 10, 7, 1155, Skills.SMITHING), - BRONZE_CHAINBODY(10, 10, 11, 1103, Skills.SMITHING), - BRONZE_2H(10, 10, 14, 1307, Skills.SMITHING), - BRONZE_SCIM(10, 10, 5, 1321, Skills.SMITHING), - BRONZE_PLATEBODY(9, 10, 18, 1117, Skills.SMITHING), - BRONZE_PLATELEGS(9, 10, 16, 1075, Skills.SMITHING), - BRONZE_PLATESKIRTS(9, 10, 16, 1087, Skills.SMITHING), - BRONZE_WARHAMMER(9, 9, 9, 1337, Skills.SMITHING), - IRON_DAGGER(13, 13, 15, 1203, Skills.SMITHING), - IRON_CHAINBODY(10, 10, 26, 1101, Skills.SMITHING), - IRON_2H(9, 9, 29, 1309, Skills.SMITHING), - STEEL_BATTLEAXE(9, 9, 40, 1365, Skills.SMITHING), - STEEL_SCIMITAR(9, 9, 35, 1325, Skills.SMITHING), - STEEL_PLATEBODY(9, 10, 48, 1119, Skills.SMITHING), - STEEL_WARHAMMER(9, 10, 39, 1339, Skills.SMITHING), - LEATHER_CHAPS(22, 28, 18, 1095, Skills.CRAFTING), - LEATHER_BODY(22, 28, 14, 1129, Skills.CRAFTING), - LEATHER_COWL(22, 28, 9, 1167, Skills.CRAFTING), - LEATHER_COIF(22, 28, 38, 1169, Skills.CRAFTING), - RAW_SHRIMP(22, 28, 1, 317, Skills.FISHING), - COOKED_SHRIMP(22, 28, 1, 315, Skills.COOKING), - RAW_CRAYFISH(22, 28, 1, 13435, Skills.FISHING), - COOKED_CRAYFISH(22, 28, 1, 13433, Skills.COOKING), - RAW_TROUT(22, 28, 20, 335, Skills.FISHING), - COOKED_TROUT(22, 28, 15, 333, Skills.COOKING), - RAW_SALMON(22, 28, 30, 331, Skills.FISHING), - COOKED_SALMON(22, 28, 25, 329, Skills.COOKING), - CAKE(12, 16, 40, 1891, Skills.COOKING), - MEAT_PIE(12, 16, 20, 2327, Skills.COOKING), - PLAIN_PIZZA(12, 16, 35, 2289, Skills.COOKING), - MEAT_PIZZA(12, 16, 45, 2293, Skills.COOKING), - ANCHOVY_PIZZA(12, 16, 55, 2297, Skills.COOKING), - REDBERRY_PIE(12, 16, 10, 2325, Skills.COOKING), - COPPER_ORE(22, 28, 1, 436, Skills.MINING), - TIN_ORE(23, 26, 1, 438, Skills.MINING), - IRON_ORE(24, 24, 15, 440, Skills.MINING), - SILVER_ORE(22, 28, 20, 442, Skills.MINING), - COAL(22, 26, 30, 453, Skills.MINING), - GOLD_ORE(22, 24, 40, 444, Skills.MINING), - BRONZE_BAR(10, 12, 1, 2349, Skills.SMITHING), - IRON_BAR(22, 28, 15, 2351, Skills.SMITHING), - STEEL_BAR(22, 28, 30, 2353, Skills.SMITHING), - ASHES(26, 26, 1, 592, 0); - - fun getAmount(): Int { - return RandomFunction.random(lower,upper + 1) - } -} \ No newline at end of file diff --git a/Server/src/main/content/global/activity/jobs/impl/ProductionJobs.kt b/Server/src/main/content/global/activity/jobs/impl/ProductionJobs.kt new file mode 100644 index 000000000..04bdd5467 --- /dev/null +++ b/Server/src/main/content/global/activity/jobs/impl/ProductionJobs.kt @@ -0,0 +1,80 @@ +package content.global.activity.jobs.impl + +import content.global.activity.jobs.Job +import content.global.activity.jobs.JobType +import core.game.node.entity.skill.Skills + +/** + * An enum of the possible production jobs that can be assigned to a player. + * + * Note: Due to how player save files keep track of the player's current job, it is essential that + * new entries are only appended to the end of this enum, and the ordering of existing entries is not changed. + */ +enum class ProductionJobs( + override val lower: Int, + override val upper: Int, + val lvlReq: Int, + val itemId: Int, + val skill: Int, + override val employer: Employers +) : Job { + LOG(20, 28, 1, 1511, Skills.WOODCUTTING, Employers.WOODCUTTING_TUTOR), + COWHIDES(20, 28, 1, 1739, 0, Employers.HANS), + OAK(22, 28, 15, 1521, Skills.WOODCUTTING, Employers.WOODCUTTING_TUTOR), + WATER_RUNE(20, 28, 5, 555, Skills.RUNECRAFTING, Employers.HANS), + EARTH_RUNE(20, 28, 9, 557, Skills.RUNECRAFTING, Employers.AGGIE), + FIRE_RUNE(20, 28, 14, 554, Skills.RUNECRAFTING, Employers.AGGIE), + AIR_RUNE(20, 28, 1, 556, Skills.RUNECRAFTING, Employers.HANS), + RUNE_ESS(20, 28, 1, 1436, Skills.MINING, Employers.MINING_TUTOR), + BALL_OF_WOOL(20, 28, 1, 1759, Skills.CRAFTING, Employers.GILLIE_GROATS), + LEATHER_GLOVE(20, 28, 1, 1059, Skills.CRAFTING, Employers.GILLIE_GROATS), + WILLOW(22, 28, 30, 1519, Skills.WOODCUTTING, Employers.WOODCUTTING_TUTOR), + LEATHER_BOOT(24, 24, 1, 1061, Skills.CRAFTING, Employers.HANS), + BRONZE_DAGGER(24, 24, 1, 1205, Skills.SMITHING, Employers.SMELTING_TUTOR), + BRONZE_HELMS(22, 22, 1, 1139, Skills.SMITHING, Employers.SMELTING_TUTOR), + BRONZE_FULL_HELM(10, 10, 7, 1155, Skills.SMITHING, Employers.SMELTING_TUTOR), + BRONZE_CHAINBODY(10, 10, 11, 1103, Skills.SMITHING, Employers.SMELTING_TUTOR), + BRONZE_2H(10, 10, 14, 1307, Skills.SMITHING, Employers.SMELTING_TUTOR), + BRONZE_SCIM(10, 10, 5, 1321, Skills.SMITHING, Employers.SMELTING_TUTOR), + BRONZE_PLATEBODY(9, 10, 18, 1117, Skills.SMITHING, Employers.SMELTING_TUTOR), + BRONZE_PLATELEGS(9, 10, 16, 1075, Skills.SMITHING, Employers.SMELTING_TUTOR), + BRONZE_PLATESKIRTS(9, 10, 16, 1087, Skills.SMITHING, Employers.SMELTING_TUTOR), + BRONZE_WARHAMMER(9, 9, 9, 1337, Skills.SMITHING, Employers.SMELTING_TUTOR), + IRON_DAGGER(13, 13, 15, 1203, Skills.SMITHING, Employers.SMELTING_TUTOR), + IRON_CHAINBODY(10, 10, 26, 1101, Skills.SMITHING, Employers.SMELTING_TUTOR), + IRON_2H(9, 9, 29, 1309, Skills.SMITHING, Employers.SMELTING_TUTOR), + STEEL_BATTLEAXE(9, 9, 40, 1365, Skills.SMITHING, Employers.SMELTING_TUTOR), + STEEL_SCIMITAR(9, 9, 35, 1325, Skills.SMITHING, Employers.SMELTING_TUTOR), + STEEL_PLATEBODY(9, 10, 48, 1119, Skills.SMITHING, Employers.SMELTING_TUTOR), + STEEL_WARHAMMER(9, 10, 39, 1339, Skills.SMITHING, Employers.SMELTING_TUTOR), + LEATHER_CHAPS(22, 28, 18, 1095, Skills.CRAFTING, Employers.CRAFTING_TUTOR), + LEATHER_BODY(22, 28, 14, 1129, Skills.CRAFTING, Employers.CRAFTING_TUTOR), + LEATHER_COWL(22, 28, 9, 1167, Skills.CRAFTING, Employers.CRAFTING_TUTOR), + LEATHER_COIF(22, 28, 38, 1169, Skills.CRAFTING, Employers.CRAFTING_TUTOR), + RAW_SHRIMP(22, 28, 1, 317, Skills.FISHING, Employers.FISHING_TUTOR), + COOKED_SHRIMP(22, 28, 1, 315, Skills.COOKING, Employers.COOKING_TUTOR), + RAW_CRAYFISH(22, 28, 1, 13435, Skills.FISHING, Employers.FISHING_TUTOR), + COOKED_CRAYFISH(22, 28, 1, 13433, Skills.COOKING, Employers.COOKING_TUTOR), + RAW_TROUT(22, 28, 20, 335, Skills.FISHING, Employers.FISHING_TUTOR), + COOKED_TROUT(22, 28, 15, 333, Skills.COOKING, Employers.COOKING_TUTOR), + RAW_SALMON(22, 28, 30, 331, Skills.FISHING, Employers.FISHING_TUTOR), + COOKED_SALMON(22, 28, 25, 329, Skills.COOKING, Employers.COOKING_TUTOR), + CAKE(12, 16, 40, 1891, Skills.COOKING, Employers.GILLIE_GROATS), + MEAT_PIE(12, 16, 20, 2327, Skills.COOKING, Employers.COOKING_TUTOR), + PLAIN_PIZZA(12, 16, 35, 2289, Skills.COOKING, Employers.COOKING_TUTOR), + MEAT_PIZZA(12, 16, 45, 2293, Skills.COOKING, Employers.COOKING_TUTOR), + ANCHOVY_PIZZA(12, 16, 55, 2297, Skills.COOKING, Employers.COOKING_TUTOR), + REDBERRY_PIE(12, 16, 10, 2325, Skills.COOKING, Employers.GILLIE_GROATS), + COPPER_ORE(22, 28, 1, 436, Skills.MINING, Employers.MINING_TUTOR), + TIN_ORE(23, 26, 1, 438, Skills.MINING, Employers.MINING_TUTOR), + IRON_ORE(24, 24, 15, 440, Skills.MINING, Employers.MINING_TUTOR), + SILVER_ORE(22, 28, 20, 442, Skills.MINING, Employers.PRAYER_TUTOR), + COAL(22, 26, 30, 453, Skills.MINING, Employers.MINING_TUTOR), + GOLD_ORE(22, 24, 40, 444, Skills.MINING, Employers.MINING_TUTOR), + BRONZE_BAR(10, 12, 1, 2349, Skills.SMITHING, Employers.SMELTING_TUTOR), + IRON_BAR(22, 28, 15, 2351, Skills.SMITHING, Employers.SMELTING_TUTOR), + STEEL_BAR(22, 28, 30, 2353, Skills.SMITHING, Employers.SMELTING_TUTOR), + ASHES(26, 26, 1, 592, 0, Employers.AGGIE); + + override val type = JobType.PRODUCTION +} \ No newline at end of file diff --git a/Server/src/main/content/global/activity/jobs/impl/SlayingJobs.kt b/Server/src/main/content/global/activity/jobs/impl/SlayingJobs.kt deleted file mode 100644 index 5afdbf90b..000000000 --- a/Server/src/main/content/global/activity/jobs/impl/SlayingJobs.kt +++ /dev/null @@ -1,15 +0,0 @@ -package content.global.activity.jobs.impl - -import core.tools.RandomFunction - -enum class SlayingJobs(val lower: Int, val upper: Int, vararg val ids: Int) { - CHICKEN(25,26,41,1017), - COW(25,25,397,1766,81,1767), - GIANT_SPIDER(22,25,59,60), - SCORPION(25,25,107,108,109,144,4402,4403), - GOBLIN(25,25,100,101,102,298,299,444,445,489,745,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); - - fun getAmount(): Int { - return RandomFunction.random(lower,upper + 1) - } -} \ No newline at end of file diff --git a/Server/src/main/content/global/activity/penguinhns/LarryHandler.kt b/Server/src/main/content/global/activity/penguinhns/LarryHandler.kt index 842e85e83..4a4b3ba90 100644 --- a/Server/src/main/content/global/activity/penguinhns/LarryHandler.kt +++ b/Server/src/main/content/global/activity/penguinhns/LarryHandler.kt @@ -74,7 +74,7 @@ class LarryHandler(player: Player? = null) : core.game.dialogue.DialoguePlugin(p } val level = getStatLevel(player, skill) - System.out.println("Level: $level") + val expGained = points?.toDouble()?.times((level * 25)) System.out.print("exp: $expGained") player.skills.addExperience(skill,expGained!!) diff --git a/Server/src/main/content/global/activity/penguinhns/PenguinHNSEvent.kt b/Server/src/main/content/global/activity/penguinhns/PenguinHNSEvent.kt index b88d5d38f..39bc2419b 100644 --- a/Server/src/main/content/global/activity/penguinhns/PenguinHNSEvent.kt +++ b/Server/src/main/content/global/activity/penguinhns/PenguinHNSEvent.kt @@ -1,39 +1,19 @@ package content.global.activity.penguinhns -import org.json.simple.JSONObject import core.ServerStore -import core.game.worldevents.PluginSet -import core.game.worldevents.WorldEvent -import core.game.world.GameWorld +import core.api.StartupListener +import core.api.log +import core.plugin.ClassScanner +import core.tools.Log +import org.json.simple.JSONObject -class PenguinHNSEvent : WorldEvent("penguin-hns"){ +class PenguinHNSEvent : StartupListener { val manager = PenguinManager() - var lastTrigger: Int = 0 - var tickDelay = if(GameWorld.settings?.isDevMode == true) 100 else 100000 - override fun checkActive(): Boolean { - return true //this event is always active. - } - - override fun checkTrigger(): Boolean { - return PenguinManager.penguins.isEmpty() - } - - override fun initialize() { - plugins = PluginSet( - LarryHandler(), - NotebookHandler(), - ) - super.initialize() - fireEvent() - log("Penguin HNS initialized.") - } - - override fun fireEvent() { - log("Loading penguins...") + override fun startup() { manager.rebuildVars() - lastTrigger = GameWorld.ticks - log("Penguins loaded.") + ClassScanner.definePlugins(LarryHandler(), NotebookHandler()) + log(this::class.java, Log.FINE, "Penguin HNS initialized.") } companion object { diff --git a/Server/src/main/content/global/activity/penguinhns/PenguinManager.kt b/Server/src/main/content/global/activity/penguinhns/PenguinManager.kt index 51cdcdecc..c284214a7 100644 --- a/Server/src/main/content/global/activity/penguinhns/PenguinManager.kt +++ b/Server/src/main/content/global/activity/penguinhns/PenguinManager.kt @@ -7,6 +7,8 @@ import core.game.world.map.Location import org.json.simple.JSONArray import org.json.simple.JSONObject import core.ServerStore.Companion.toJSONArray +import core.api.log +import core.tools.Log import java.util.* class PenguinManager{ @@ -31,7 +33,7 @@ class PenguinManager{ private fun updateStoreFile(){ val jsonTags = JSONArray() tagMapping.filter { it.value.isNotEmpty() }.forEach { (ordinal,taggers) -> - SystemLogger.logInfo(this::class.java, "$ordinal - ${taggers.first()}") + log(this::class.java, Log.FINE, "$ordinal - ${taggers.first()}") val tag = JSONObject() tag["ordinal"] = ordinal tag["taggers"] = taggers @@ -64,8 +66,4 @@ class PenguinManager{ penguins = spawnedOrdinals.toMutableList() } } - - fun log(message: String){ - SystemLogger.logInfo(this::class.java, "[Penguins] $message") - } } \ No newline at end of file diff --git a/Server/src/main/content/global/activity/penguinhns/PenguinSpyingHandler.kt b/Server/src/main/content/global/activity/penguinhns/PenguinSpyingHandler.kt index f39b9e41f..386220027 100644 --- a/Server/src/main/content/global/activity/penguinhns/PenguinSpyingHandler.kt +++ b/Server/src/main/content/global/activity/penguinhns/PenguinSpyingHandler.kt @@ -1,5 +1,6 @@ package content.global.activity.penguinhns +import core.api.* import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player import core.game.system.task.Pulse @@ -17,6 +18,7 @@ class PenguinSpyingHandler : InteractionListener { player.sendMessage("You've already tagged this penguin.") } else { GameWorld.submit(SpyPulse(player, npc)) + playJingle(player, 345) } return@on true } diff --git a/Server/src/main/content/global/activity/shootingstar/ShootingStar.kt b/Server/src/main/content/global/activity/shootingstar/ShootingStar.kt index 1ea776e49..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) { @@ -122,38 +115,55 @@ class ShootingStar(var level: ShootingStarType = ShootingStarType.values().rando selfBots.filter { it.isIdle() }.forEach { it.activate(true) } } isSpawned = true - Repository.sendNews("A shooting star level ${level.ordinal + 1} just crashed near ${location.toLowerCase()}!") + 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 } /** @@ -220,4 +231,4 @@ enum class ShootingStarType(val objectId: Int, val exp: Int, val totalStardust: LEVEL_7(38662, 114, 40, 0.80), LEVEL_8(38661, 145, 25, 0.85), LEVEL_9(38660, 210, 15, 0.95); -} \ No newline at end of file +} diff --git a/Server/src/main/content/global/activity/shootingstar/ShootingStarMiningPulse.kt b/Server/src/main/content/global/activity/shootingstar/ShootingStarMiningPulse.kt index 6bfa6d2b0..5f5ee3649 100644 --- a/Server/src/main/content/global/activity/shootingstar/ShootingStarMiningPulse.kt +++ b/Server/src/main/content/global/activity/shootingstar/ShootingStarMiningPulse.kt @@ -48,9 +48,10 @@ class ShootingStarMiningPulse(player: Player?, node: Scenery?, val star: Shootin val bonusXp = 75 * player.skills.getStaticLevel(Skills.MINING) player.incrementAttribute("/save:shooting-star:bonus-xp", bonusXp) 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.") + player.sendMessage("You have ${player.skills.experienceMultiplier * 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 } @@ -83,14 +84,13 @@ class ShootingStarMiningPulse(player: Player?, node: Scenery?, val star: Shootin if (GameWorld.settings?.isDevMode == true) { star.dustLeft = 1 } - star.decDust() val bonusXp = player.getAttribute("shooting-star:bonus-xp", 0).toDouble() var xp = star.level.exp.toDouble() if(bonusXp > 0) { val delta = Math.min(bonusXp, xp) player.incrementAttribute("/save:shooting-star:bonus-xp", (-delta).toInt()) - xp += delta; + xp += delta if(player.getAttribute("shooting-star:bonus-xp", 0) <= 0) { player.sendMessage("You have obtained all of your bonus xp from the star.") } @@ -103,6 +103,8 @@ class ShootingStarMiningPulse(player: Player?, node: Scenery?, val star: Shootin if(!inInventory(player, Items.ANCIENT_BLUEPRINT_14651) && !inBank(player, Items.ANCIENT_BLUEPRINT_14651)){ rollBlueprint(player) } + + star.decDust() return false } diff --git a/Server/src/main/content/global/activity/shootingstar/ShootingStarPlugin.kt b/Server/src/main/content/global/activity/shootingstar/ShootingStarPlugin.kt index fb8e1df9f..1e993e1bb 100644 --- a/Server/src/main/content/global/activity/shootingstar/ShootingStarPlugin.kt +++ b/Server/src/main/content/global/activity/shootingstar/ShootingStarPlugin.kt @@ -12,10 +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) { @@ -26,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() { @@ -65,34 +82,52 @@ class ShootingStarPlugin : LoginListener, InteractionListener, TickListener, Com return@on true } - val condition: (Player) -> Boolean = when(star.location.toLowerCase()){ - "canifis bank" -> { p -> p.questRepository.isComplete("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 -> p.questRepository.isComplete("Roving Elves")} - "jatizso mine" -> {p -> p.questRepository.isComplete("Fremennik Trials")} - "south crandor mining site" -> {p -> p.questRepository.isComplete("Dragon Slayer")} - 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) || player.skullManager.isWilderness){ - sendDialogue(player, "Magical forces prevent your teleportation.") - return@on true - } - - val shouldWarn = when(star.location){ - "North Edgeville mining site", - "Southern wilderness mine", - "Wilderness Volcano bank", - "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 } } @@ -103,6 +138,7 @@ class ShootingStarPlugin : LoginListener, InteractionListener, TickListener, Com } define("submit", Privilege.ADMIN) { _, _ -> + star.rebuildVars() star.fire() } @@ -112,30 +148,11 @@ class ShootingStarPlugin : LoginListener, InteractionListener, TickListener, Com } override fun startup() { - SystemLogger.logInfo(this::class.java, "Shooting Stars initialized.") + log(this::class.java, Log.FINE, "Shooting Stars initialized.") } 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){ - 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/activity/shootingstar/ShootingStarState.kt b/Server/src/main/content/global/activity/shootingstar/ShootingStarState.kt deleted file mode 100644 index 0adbf564d..000000000 --- a/Server/src/main/content/global/activity/shootingstar/ShootingStarState.kt +++ /dev/null @@ -1,47 +0,0 @@ -package content.global.activity.shootingstar - -import core.game.node.entity.player.Player -import core.game.node.entity.state.PlayerState -import core.game.node.entity.state.State -import core.game.system.task.Pulse -import core.tools.ticksToSeconds -import org.json.simple.JSONObject - -@PlayerState("shooting-star") -class ShootingStarState(player: Player? = null) : State(player) { - var ticksLeft = 1500 - - override fun save(root: JSONObject) { - root["ticksLeft"] = ticksLeft - } - - override fun parse(_data: JSONObject) { - if(_data.containsKey("ticksLeft")){ - ticksLeft = _data["ticksLeft"].toString().toInt() - } - } - - override fun newInstance(player: Player?): State { - return ShootingStarState(player) - } - - override fun createPulse() { - player ?: return - if(ticksLeft <= 0) return - pulse = object : Pulse(){ - override fun pulse(): Boolean { - val minutes = ticksToSeconds(ticksLeft) / 60.0 - if(minutes % 5.0 == 0.0){ - player.sendMessage("You have $minutes minutes of your mining bonus left") - } - if(ticksLeft-- <= 0){ - player.sendMessage("Your mining bonus has run out!") - pulse = null - return true - } - return false - } - } - } - -} \ No newline at end of file diff --git a/Server/src/main/content/global/activity/shootingstar/StarBonus.kt b/Server/src/main/content/global/activity/shootingstar/StarBonus.kt new file mode 100644 index 000000000..d0c764e7d --- /dev/null +++ b/Server/src/main/content/global/activity/shootingstar/StarBonus.kt @@ -0,0 +1,28 @@ +package content.global.activity.shootingstar + +import core.api.* +import core.game.system.timer.* +import core.game.node.entity.Entity +import core.game.node.entity.player.Player +import org.json.simple.* + +class StarBonus : PersistTimer (1, "shootingstar:bonus") { + var ticksLeft = 1500 + + override fun save (root: JSONObject, entity: Entity) { + root["ticksLeft"] = ticksLeft.toString() + } + + override fun parse (root: JSONObject, entity: Entity) { + ticksLeft = root["ticksLeft"].toString().toInt() + } + + override fun run (entity: Entity) : Boolean { + if (entity is Player && ticksLeft == 500) { + entity.sendMessage("You have 5 minutes of your mining bonus left") + } else if (entity is Player && ticksLeft == 0) { + entity.sendMessage("Your mining bonus has run out!") + } + return ticksLeft-- > 0 + } +} diff --git a/Server/src/main/content/global/activity/shootingstar/StarSpriteDialogue.kt b/Server/src/main/content/global/activity/shootingstar/StarSpriteDialogue.kt index c1a1e6a75..7eebc668e 100644 --- a/Server/src/main/content/global/activity/shootingstar/StarSpriteDialogue.kt +++ b/Server/src/main/content/global/activity/shootingstar/StarSpriteDialogue.kt @@ -82,7 +82,7 @@ class StarSpriteDialogue(player: Player? = null) : core.game.dialogue.DialoguePl override fun handle(interfaceId: Int, buttonId: Int): Boolean { when (stage) { 0 -> { - npc("I'm a star sprite! I Was in my star in the sky, when it", "lost control and crashed into the ground. With half my", "star sticking into the ground, I became stuck.", "Fortunately, I was mined out by the kind creatures of") + npc("I'm a star sprite! I was in my star in the sky, when it", "lost control and crashed into the ground. With half my", "star sticking into the ground, I became stuck.", "Fortunately, I was mined out by the kind creatures of") stage++ } 1 -> { @@ -198,7 +198,8 @@ class StarSpriteDialogue(player: Player? = null) : core.game.dialogue.DialoguePl getStoreFile()[player.username.toLowerCase()] = true //flag daily as completed - player.registerState("shooting-star")?.init() + val timer = getOrStartTimer (player) + timer.ticksLeft = 1500 if(wearingRing){ val item = intArrayOf(Items.COSMIC_RUNE_564, Items.ASTRAL_RUNE_9075, Items.GOLD_ORE_445, Items.COINS_995).random() @@ -263,8 +264,8 @@ class StarSpriteDialogue(player: Player? = null) : core.game.dialogue.DialoguePl fun rollForRingBonus(player: Player, bonusId: Int, bonusBaseAmt: Int){ if(RandomFunction.roll(3)){ - val state = player.states["shooting-star"] as? ShootingStarState ?: return - state.ticksLeft += secondsToTicks(TimeUnit.MINUTES.toSeconds(5).toInt()) + var bonus = getOrStartTimer (player) + bonus.ticksLeft += 500 sendMessage(player, colorize("%RYour ring shines dimly as if imbued with energy.")) } else if(RandomFunction.roll(5)){ addItem(player, bonusId, bonusBaseAmt) diff --git a/Server/src/main/content/global/activity/ttrail/ClueScrollPlugin.java b/Server/src/main/content/global/activity/ttrail/ClueScrollPlugin.java index 5eeedb3c7..c049c6dab 100644 --- a/Server/src/main/content/global/activity/ttrail/ClueScrollPlugin.java +++ b/Server/src/main/content/global/activity/ttrail/ClueScrollPlugin.java @@ -3,6 +3,7 @@ package content.global.activity.ttrail; import core.game.component.Component; import core.game.node.entity.player.Player; import core.game.node.item.Item; +import core.tools.Log; import core.tools.SystemLogger; import core.game.world.map.zone.MapZone; import core.game.world.map.zone.ZoneBorders; @@ -15,6 +16,8 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import static core.api.ContentAPIKt.log; + /** * Represents a clue scroll plugin. * @author Vexia @@ -138,7 +141,7 @@ public abstract class ClueScrollPlugin extends MapZone implements Plugin if(clue.getClueId() == 2681) return; if (CLUE_SCROLLS.containsKey(clue.getClueId())) { - SystemLogger.logErr(this.getClass(), "Error! Plugin already registered with clue id - " + clue.getClueId() + ", trying to register " + clue.getClass().getCanonicalName() + " the real plugin using the id is " + CLUE_SCROLLS.get(clue.getClueId()).getClass().getCanonicalName() + "!"); + log(this.getClass(), Log.ERR, "Error! Plugin already registered with clue id - " + clue.getClueId() + ", trying to register " + clue.getClass().getCanonicalName() + " the real plugin using the id is " + CLUE_SCROLLS.get(clue.getClueId()).getClass().getCanonicalName() + "!"); return; } List organized = (List) ORGANIZED.get(clue.getLevel()); @@ -169,7 +172,7 @@ public abstract class ClueScrollPlugin extends MapZone implements Plugin public static Item getClue(ClueLevel clueLevel) { List clues = ORGANIZED.get(clueLevel); if (clues == null) { - SystemLogger.logErr(ClueScrollPlugin.class, "Error! There are no clues for level " + clueLevel + "!"); + log(ClueScrollPlugin.class, Log.ERR, "Error! There are no clues for level " + clueLevel + "!"); return null; } ClueScrollPlugin clue = clues.get(RandomFunction.random(clues.size())); @@ -189,7 +192,7 @@ public abstract class ClueScrollPlugin extends MapZone implements Plugin int hasAmt = 0; for (int i = 0; i < equipment.length; i++) { for (int k = 0; k < equipment[i].length; k++) { - if (player.getEquipment().contains(equipment[i][k], 1)) { + if (player.getEquipment().contains(equipment[i][k], 1) || player.costumeRoomBank.contains(equipment[i][k], 1)) { hasAmt++; break; } diff --git a/Server/src/main/content/global/activity/ttrail/EmoteCluePlugin.java b/Server/src/main/content/global/activity/ttrail/EmoteCluePlugin.java index 9a327e41a..afcb7601d 100644 --- a/Server/src/main/content/global/activity/ttrail/EmoteCluePlugin.java +++ b/Server/src/main/content/global/activity/ttrail/EmoteCluePlugin.java @@ -57,7 +57,7 @@ public final class EmoteCluePlugin extends EmoteClueScroll { public Plugin newInstance(Object arg) throws Throwable { Emotes emote = Emotes.BECKON; register(new EmoteCluePlugin("digsite-beckon", 2677, ClueLevel.MEDIUM, emote, Emotes.BOW, new int[][] { { 6328 }, { 1267 }, { 658 } }, "Beckon in the Digsite, near the
eastern winch. Bow or curtsy
before you talk to me.
Equip a green gnome hat,
snakeskin boots and an
iron pickaxe.", new ZoneBorders(3368, 3425, 3371, 3429))); - register(new EmoteCluePlugin("tai-bwo-beckon", 2678, ClueLevel.MEDIUM, emote, Emotes.CLAP, new int[][] { { 1099 }, { 2552, 2554, 2556, 2558, 2560, 2562, 2564, 2568 }, { 1143 } }, "Beckon in Tai Bwo
Wannai. Clap before
you talk to me.
Equip green
dragonhide chaps, a
ring of dueling and a
mithril medium helmet.", new ZoneBorders(2771, 3053, 2814, 3074))); + register(new EmoteCluePlugin("tai-bwo-beckon", 2678, ClueLevel.MEDIUM, emote, Emotes.CLAP, new int[][] { { 1099 }, { 2552, 2554, 2556, 2558, 2560, 2562, 2564, 2566, 2568 }, { 1143 } }, "Beckon in Tai Bwo
Wannai. Clap before
you talk to me.
Equip green
dragonhide chaps, a
ring of dueling and a
mithril medium helmet.", new ZoneBorders(2771, 3053, 2814, 3074))); emote = Emotes.BLOW_KISS; //register(new EmoteCluePlugin("shilo-kiss", 2681, ClueLevel.HARD, emote, new int[][] { { 4089 }, { 5016 }, { 1127 } }, "Blow a kiss between
the tables in Shilo
Village bank. Beware
of double agents!
Equip a blue mystic
hat, bone spear and
rune plate body.", new ZoneBorders(2849, 2950, 2855, 2957))); emote = Emotes.BOW; @@ -99,7 +99,7 @@ public final class EmoteCluePlugin extends EmoteClueScroll { //Fixed JFJ and Laugh Clues //When Vexia emote is removed below. //emote = Emotes.JUMP_FOR_JOY; - register(new EmoteCluePlugin("beehive-jump", 2703, ClueLevel.EASY, Emotes.JUMP_FOR_JOY, new int[][] { { 1833 }, { 648 }, { 1353 } }, "Jump for joy at the beehives.
Equip a desert shirt, green
gnome robe bottoms and a
steel axe.", new ZoneBorders(2762, 3439, 2764, 3441))); + register(new EmoteCluePlugin("beehive-jump", 2703, ClueLevel.EASY, Emotes.JUMP_FOR_JOY, new int[][] { { 1833 }, { 648 }, { 1353 } }, "Jump for joy at the beehives.
Equip a desert shirt, green
gnome robe bottoms and a
steel axe.", new ZoneBorders(2752, 3437,2766, 3450))); // And emote, is replaced with what it is shortcutting Emotes.JUMP_FOR_JOY // the clues work perfect. It seems to only be a problem with multiple emote clues. // If you look around EmoteCluePlugin you will see there are multiple emote, shortcuts 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..5cef011ba --- /dev/null +++ b/Server/src/main/content/global/ame/KidnapHelper.kt @@ -0,0 +1,28 @@ +package content.global.ame + +import core.ServerConstants +import core.api.* +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.TeleportManager.TeleportType +import core.game.world.map.Location + +fun kidnapPlayer(player: Player, loc: Location, type: TeleportType) { + setAttribute(player, "kidnapped-by-random", true) + if (getAttribute(player, "/save:original-loc", null) == null) { + setAttribute(player, "/save:original-loc", player.location) + } + teleport(player, loc, type) +} + +fun returnPlayer(player: Player) { + // Prevent returning more than once and sending the player back to HOME_LOCATION + if (getAttribute(player, "kidnapped-by-random", null) == null) { + return + } + + player.locks.unlockTeleport() + val destination = getAttribute(player, "/save:original-loc", ServerConstants.HOME_LOCATION ?: Location.create(3222, 3218, 0)) + teleport(player, destination) + unlock(player) + removeAttributes(player, "/save:original-loc", "kidnapped-by-random") +} \ No newline at end of file diff --git a/Server/src/main/content/global/ame/RandomEventManager.kt b/Server/src/main/content/global/ame/RandomEventManager.kt deleted file mode 100644 index 24cb490ee..000000000 --- a/Server/src/main/content/global/ame/RandomEventManager.kt +++ /dev/null @@ -1,99 +0,0 @@ -package content.global.ame - -import core.api.Commands -import core.api.LoginListener -import core.game.event.EventHook -import core.game.event.TickEvent -import core.api.getAttribute -import core.api.setAttribute -import core.game.node.entity.Entity -import core.game.node.entity.player.Player -import core.game.world.map.zone.ZoneRestriction -import core.tools.RandomFunction -import core.api.Event -import core.tools.SystemLogger -import core.game.system.command.Privilege -import core.game.world.GameWorld -import core.game.world.repository.Repository -import kotlin.random.Random - -class RandomEventManager(val player: Player? = null) : LoginListener, EventHook, Commands { - var event: content.global.ame.RandomEventNPC? = null - var enabled: Boolean = false - var nextSpawn = 0 - val skills = arrayOf("WoodcuttingSkillPulse","FishingPulse","MiningSkillPulse","BoneBuryingOptionPlugin") - - override fun login(player: Player) { - if(player.isArtificial) return - val instance = content.global.ame.RandomEventManager(player) - player.hook(Event.Tick, instance) - setAttribute(player, "random-manager", instance) - instance.rollNextSpawn() - instance.enabled = true - SystemLogger.logRE("Initialized REManager for ${player.username}.") - } - - override fun process(entity: Entity, event: TickEvent) { - if (GameWorld.ticks > nextSpawn && getAttribute(player!!, "tutorial:complete", false)) fireEvent() - } - - fun fireEvent() { - if(!enabled){ - rollNextSpawn() - return - } - if (player!!.zoneMonitor.isRestricted(ZoneRestriction.RANDOM_EVENTS)) { - nextSpawn = GameWorld.ticks + 3000 - return - } - if (getAttribute(player, "re-npc", null) != null) return - val currentAction = player.pulseManager.current?.toString() ?: "None" - val ame: content.global.ame.RandomEvents = if(currentAction.contains("WoodcuttingSkillPulse") && Random.nextBoolean()){ - content.global.ame.RandomEvents.TREE_SPIRIT - } else if(currentAction.contains("FishingPulse") && Random.nextBoolean()){ - content.global.ame.RandomEvents.RIVER_TROLL - } else if(currentAction.contains("MiningSkillPulse") && Random.nextBoolean()){ - content.global.ame.RandomEvents.ROCK_GOLEM - } else if(currentAction.contains("BoneBuryingOptionPlugin") && Random.nextBoolean()){ - content.global.ame.RandomEvents.SHADE - } else { - content.global.ame.RandomEvents.values().filter { it.type != "skill" }.random() - } - event = ame.npc.create(player,ame.loot,ame.type) - if (event!!.spawnLocation == null) { - nextSpawn = GameWorld.ticks + 3000 - SystemLogger.logWarn(this::class.java, "Tried to spawn random event for ${player.username} but spawn location was null!") - return - } - event!!.init() - rollNextSpawn() - SystemLogger.logRE("Fired ${event!!.name} for ${player.username}") - } - - private fun rollNextSpawn() { - nextSpawn = GameWorld.ticks + RandomFunction.random(content.global.ame.RandomEventManager.Companion.MIN_DELAY_TICKS, content.global.ame.RandomEventManager.Companion.MAX_DELAY_TICKS) - } - - override fun defineCommands() { - define("targeted-ame", Privilege.ADMIN, "targeted-ame username", "Summons a random for the given user") {player, args -> - val username = args[1] - val target = Repository.getPlayerByName(username) - - if (target == null) - reject(player, "Unable to find player $username!") - - content.global.ame.RandomEventManager.Companion.getInstance(target!!)?.fireEvent() - } - } - - companion object { - const val AVG_DELAY_TICKS = 6000 // 60 minutes - const val MIN_DELAY_TICKS = content.global.ame.RandomEventManager.Companion.AVG_DELAY_TICKS / 2 - const val MAX_DELAY_TICKS = content.global.ame.RandomEventManager.Companion.MIN_DELAY_TICKS + content.global.ame.RandomEventManager.Companion.AVG_DELAY_TICKS // window of 60 min centered on 60 min (30 to 90 min) - - @JvmStatic fun getInstance(player: Player): content.global.ame.RandomEventManager? - { - return getAttribute(player, "random-manager", null) - } - } -} \ No newline at end of file diff --git a/Server/src/main/content/global/ame/RandomEventNPC.kt b/Server/src/main/content/global/ame/RandomEventNPC.kt index 507f24a8a..716420e25 100644 --- a/Server/src/main/content/global/ame/RandomEventNPC.kt +++ b/Server/src/main/content/global/ame/RandomEventNPC.kt @@ -1,10 +1,17 @@ package content.global.ame import content.global.ame.events.MysteriousOldManNPC +import core.api.playGlobalAudio import core.api.poofClear +import core.api.sendMessage +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 @@ -12,8 +19,11 @@ 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.secondsToTicks +import core.tools.ticksToCycles +import org.rs09.consts.Sounds +import kotlin.math.ceil +import kotlin.math.min import kotlin.random.Random import kotlin.reflect.full.createInstance @@ -37,12 +47,12 @@ abstract class RandomEventNPC(id: Int) : NPC(id) { } open fun terminate() { - finalized = true pulseManager.clear(PulseType.STANDARD) - content.global.ame.RandomEventManager.getInstance(player)?.event = null - if (initialized) { + if (initialized && !finalized) { poofClear(this) + playGlobalAudio(this.location, Sounds.SMOKEPUFF_1930, ticksToCycles(1)) } + finalized = true } open fun follow() { @@ -63,7 +73,6 @@ abstract class RandomEventNPC(id: Int) : NPC(id) { if (!player.getAttribute("random:pause", false)) { ticksLeft-- } - if (!pulseManager.hasPulseRunning() && !finalized) { follow() } @@ -84,6 +93,11 @@ 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() { @@ -92,6 +106,7 @@ abstract class RandomEventNPC(id: Int) : NPC(id) { } fun noteAndTeleport() { + player.pulseManager.clear() for (item in player.inventory.toArray()) { if (item == null) continue if (item.definition.isUnnoted) { @@ -114,4 +129,19 @@ 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] + } +} diff --git a/Server/src/main/content/global/ame/RandomEvents.kt b/Server/src/main/content/global/ame/RandomEvents.kt index 7c586596d..49bac5151 100644 --- a/Server/src/main/content/global/ame/RandomEvents.kt +++ b/Server/src/main/content/global/ame/RandomEvents.kt @@ -3,23 +3,33 @@ package content.global.ame import org.rs09.consts.Items import content.global.ame.events.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.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.api.utils.WeightBasedTable import core.api.utils.WeightedItem +import core.game.node.entity.skill.Skills -enum class RandomEvents(val npc: RandomEventNPC, val loot: WeightBasedTable? = null) { - SANDWICH_LADY(SandwichLadyRENPC()), - GENIE(GenieNPC()), - CERTER(CerterNPC(), WeightBasedTable.create( +enum class RandomEvents(val npc: RandomEventNPC, val loot: WeightBasedTable? = null, val skillIds: IntArray = intArrayOf(), val type: String = "") { + SANDWICH_LADY(npc = SandwichLadyRENPC()), + GENIE(npc = GenieNPC()), + CERTER(npc = CerterNPC(), loot = WeightBasedTable.create( WeightedItem(Items.UNCUT_SAPPHIRE_1623,1,1,3.4), WeightedItem(Items.KEBAB_1971,1,1,1.7), WeightedItem(Items.UNCUT_EMERALD_1621,1,1,1.7), @@ -36,25 +46,50 @@ 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(SeargentDamienNPC()), - EVIL_CHICKEN(EvilChickenNPC()), - SURPRISE_EXAM(MysteriousOldManNPC(),"sexam"), - TREE_SPIRIT(TreeSpiritRENPC(),"skill"), - RIVER_TROLL(RiverTrollRENPC(),"skill"), - ROCK_GOLEM(RockGolemRENPC(),"skill"), - SHADE(ShadeRENPC(),"skill"), - ZOMBIE(ZombieRENPC(),"skill"); - - var type: String = "" - private set - - constructor(npc: RandomEventNPC, type: String) : this(npc,null){ - this.type = type - } + MAZE(npc = MazeNPC()), + DRILL_DEMON(npc = SergeantDamienNPC()), + EVIL_CHICKEN(npc = EvilChickenNPC()), + STRANGE_PLANT(npc = StrangePlantNPC()), + SWARM(npc = SwarmNPC()), + EVIL_BOB(npc = EvilBobNPC(), skillIds = intArrayOf(Skills.FISHING, Skills.MAGIC)), + DRUNKEN_DWARF(npc = DrunkenDwarfNPC()), + 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)), + ZOMBIE(ZombieRENPC(), skillIds = intArrayOf(Skills.PRAYER)); companion object { @JvmField val randomIDs = values().map { it.npc.id }.toList() + val skillMap = HashMap>() + val nonSkillList = ArrayList() + + init { populateMappings() } + + fun getSkillBasedRandomEvent (skill: Int) : RandomEvents? { + return skillMap[skill]?.random() + } + + fun getNonSkillRandom() : RandomEvents { + return nonSkillList.random() + } + + private fun populateMappings() { + for (event in values()) { + for (id in event.skillIds) { + val list = skillMap[id] ?: ArrayList().also { skillMap[id] = it } + list.add (event) + } + if (event.skillIds.isEmpty()) + nonSkillList.add (event) + } + } } -} \ No newline at end of file +} 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..37083b045 --- /dev/null +++ b/Server/src/main/content/global/ame/events/HostileRandomEventBehavior.kt @@ -0,0 +1,19 @@ +package content.global.ame.events + +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.ROCK_GOLEM_413, NPCs.ROCK_GOLEM_414, NPCs.ROCK_GOLEM_415, NPCs.ROCK_GOLEM_416, NPCs.ROCK_GOLEM_417, NPCs.ROCK_GOLEM_418, + NPCs.SHADE_425, NPCs.SHADE_426, NPCs.SHADE_427, NPCs.SHADE_428, NPCs.SHADE_429, NPCs.SHADE_430, NPCs.SHADE_431, + NPCs.TREE_SPIRIT_438, NPCs.TREE_SPIRIT_439, NPCs.TREE_SPIRIT_440, NPCs.TREE_SPIRIT_441, NPCs.TREE_SPIRIT_442, NPCs.TREE_SPIRIT_443, + NPCs.ZOMBIE_419, NPCs.ZOMBIE_420, NPCs.ZOMBIE_421, NPCs.ZOMBIE_422, NPCs.ZOMBIE_423, NPCs.ZOMBIE_424 +) { + override fun getXpMultiplier(self: NPC, attacker: Entity): Double { + return super.getXpMultiplier(self, attacker) / 16.0 + } +} diff --git a/Server/src/main/content/global/ame/events/certer/CerterDialogue.kt b/Server/src/main/content/global/ame/events/certer/CerterDialogue.kt index 25ac73fdf..27f72a317 100644 --- a/Server/src/main/content/global/ame/events/certer/CerterDialogue.kt +++ b/Server/src/main/content/global/ame/events/certer/CerterDialogue.kt @@ -5,6 +5,7 @@ import core.game.component.Component import core.game.node.entity.impl.PulseType import core.game.node.entity.player.link.emote.Emotes import core.game.dialogue.DialogueFile +import core.game.system.timer.impl.AntiMacro import core.tools.END_DIALOGUE class CerterDialogue(val initial: Boolean) : DialogueFile() { @@ -30,13 +31,13 @@ class CerterDialogue(val initial: Boolean) : DialogueFile() { npc("Sorry, I don't think so.").also { player!!.setAttribute("certer:reward", true) stage = END_DIALOGUE - content.global.ame.RandomEventManager.getInstance(player!!)?.event?.terminate() + AntiMacro.terminateEventNpc(player!!) } } else { npc("Thank you, I hope you like your present. I must be", "leaving now though.").also { player!!.setAttribute("certer:reward", true) stage = END_DIALOGUE - content.global.ame.RandomEventManager.getInstance(player!!)!!.event!!.loot!!.roll().forEach { addItemOrDrop(player!!, it.id, it.amount) } + AntiMacro.rollEventLoot(player!!).forEach { addItemOrDrop(player!!, it.id, it.amount) } } } } @@ -51,7 +52,7 @@ class CerterDialogue(val initial: Boolean) : DialogueFile() { // Wave goodbye npc!!.animate(Emotes.WAVE.animation) // Terminate the event - content.global.ame.RandomEventManager.getInstance(player!!)?.event?.terminate() + AntiMacro.terminateEventNpc(player!!) } else { player!!.setAttribute("random:pause", false) } diff --git a/Server/src/main/content/global/ame/events/certer/CerterEventInterface.kt b/Server/src/main/content/global/ame/events/certer/CerterEventInterface.kt index e64cf3403..96d8f313e 100644 --- a/Server/src/main/content/global/ame/events/certer/CerterEventInterface.kt +++ b/Server/src/main/content/global/ame/events/certer/CerterEventInterface.kt @@ -1,10 +1,10 @@ package content.global.ame.events.certer -import content.global.ame.RandomEventManager import core.game.node.entity.player.Player import core.game.node.item.Item import org.rs09.consts.Items import core.game.interaction.InterfaceListener +import core.game.system.timer.impl.AntiMacro class CerterEventInterface : InterfaceListener { val CERTER_INTERFACE = 184 @@ -37,7 +37,7 @@ class CerterEventInterface : InterfaceListener { val correctAnswer = player.getAttribute("certer:correctIndex", 0) player.setAttribute("certer:correct", correctAnswer == answer) player.interfaceManager.close() - player.dialogueInterpreter.open(CerterDialogue(false), RandomEventManager.getInstance(player)!!.event?.asNpc()) + player.dialogueInterpreter.open(CerterDialogue(false), AntiMacro.getEventNpc(player)) return@on true } 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 5f7881710..2287de37d 100644 --- a/Server/src/main/content/global/ame/events/drilldemon/DrillDemonListeners.kt +++ b/Server/src/main/content/global/ame/events/drilldemon/DrillDemonListeners.kt @@ -1,50 +1,93 @@ package content.global.ame.events.drilldemon +import core.api.* import core.game.node.entity.npc.NPC -import core.game.system.task.Pulse 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.Entity +import core.game.node.entity.player.Player +import core.game.world.map.zone.ZoneBorders +import core.game.world.map.zone.ZoneRestriction +import core.tools.secondsToTicks -class DrillDemonListeners : InteractionListener { +import org.rs09.consts.Sounds + +class DrillDemonListeners : InteractionListener, MapArea { val MATS = intArrayOf(10076,10077,10078,10079) override fun defineListeners() { - on(MATS, IntType.SCENERY, "use"){ player, node -> - val correctTask = player.getAttribute(DrillDemonUtils.DD_KEY_TASK,-1) - if(correctTask == -1){ - player.sendMessage("You can't do that right now.") - return@on true + on(DrillDemonUtils.DD_NPC, IntType.NPC, "talk-to") { player, _ -> + if (inBorders(player, DrillDemonUtils.DD_AREA)) { + openDialogue(player, SeargentDamienDialogue(isCorrect = true), DrillDemonUtils.DD_NPC) + } else { + sendMessage(player, "They aren't interested in talking to you.") } - - val task = DrillDemonUtils.getMatTask(node.id,player) - val npc = NPC(NPCs.SERGEANT_DAMIEN_2790) - - player.lock() - val anim = DrillDemonUtils.animationForTask(task) - player.walkingQueue.reset() - player.walkingQueue.addPath(node.location.x,4820) - player.pulseManager.run(object : Pulse(){ - var counter = 0 - override fun pulse(): Boolean { - when(counter++){ - 2 -> player.faceLocation(player.location.transform(0,-1,0)) - 3 -> player.animator.animate(DrillDemonUtils.animationForTask(task)).also { delay = anim.duration / 2 } - 6 -> { - if(task == correctTask){ - player.incrementAttribute(DrillDemonUtils.DD_CORRECT_COUNTER) - player.dialogueInterpreter.open(SeargentDamienDialogue(true),npc) - } else { - player.dialogueInterpreter.open(SeargentDamienDialogue(false),npc) - } - return true - } - } - return false - } - }) return@on true } + on(MATS, IntType.SCENERY, "use") { player, node -> + val correctTask = getAttribute(player, DrillDemonUtils.DD_KEY_TASK, -1) + if (correctTask == -1) { + sendMessage(player,"You can't do that right now.") + return@on true + } + + val task = DrillDemonUtils.getMatTask(node.id, player) + val npc = NPC(NPCs.SERGEANT_DAMIEN_2790) + val anim = DrillDemonUtils.animationForTask(task) + + lock(player, secondsToTicks(30)) + player.walkingQueue.reset() + player.walkingQueue.addPath(node.location.x,4820) + queueScript(player, 0, QueueStrength.SOFT) { stage: Int -> + when (stage) { + 0 -> { + player.faceLocation(player.location.transform(0, -1, 0)) + return@queueScript delayScript(player, 2) + } + 1 -> { + animate(player, DrillDemonUtils.animationForTask(task)) + when (task) { + DrillDemonUtils.DD_SIGN_JOG -> playAudio(player, Sounds.RUNONSPOT_2484, 0, 5) + DrillDemonUtils.DD_SIGN_SITUP -> playAudio(player, Sounds.SITUPS_2486, 40, 5) + DrillDemonUtils.DD_SIGN_PUSHUP -> playAudio(player, Sounds.PRESSUPS_2481, 25, 5) + DrillDemonUtils.DD_SIGN_JUMP -> playAudio(player, Sounds.STAR_JUMP_2492, 0, 5) + } + return@queueScript delayScript(player, anim.duration + 2) + } + 2 -> { + DrillDemonUtils.changeSignsAndAssignTask(player) + if (task == correctTask) { + player.incrementAttribute(DrillDemonUtils.DD_CORRECT_COUNTER) + openDialogue(player, SeargentDamienDialogue(true), npc) + } else { + openDialogue(player, SeargentDamienDialogue(false), npc) + } + return@queueScript stopExecuting(player) + } + else -> return@queueScript stopExecuting(player) + } + } + return@on true + } } -} \ No newline at end of file + + override fun defineAreaBorders(): Array { + return arrayOf(DrillDemonUtils.DD_AREA) + } + + override fun getRestrictions(): Array { + return arrayOf(ZoneRestriction.RANDOM_EVENTS, ZoneRestriction.CANNON, ZoneRestriction.FOLLOWERS, ZoneRestriction.OFF_MAP) + } + + override fun areaEnter(entity: Entity) { + if (entity is Player) { + entity.asPlayer().interfaceManager.closeDefaultTabs() + entity.locks.lockTeleport(1000000) + setComponentVisibility(entity.asPlayer(), 548, 69, true) + setComponentVisibility(entity.asPlayer(), 746, 12, true) + } + } +} 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 c1331420e..a6638fecb 100644 --- a/Server/src/main/content/global/ame/events/drilldemon/DrillDemonUtils.kt +++ b/Server/src/main/content/global/ame/events/drilldemon/DrillDemonUtils.kt @@ -1,97 +1,78 @@ package content.global.ame.events.drilldemon +import content.global.ame.kidnapPlayer +import content.global.ame.returnPlayer import core.api.* -import core.game.dialogue.FacialExpression +import core.game.interaction.QueueStrength import core.game.node.entity.player.Player -import core.game.node.item.GroundItemManager -import core.game.node.item.Item +import core.game.node.entity.player.link.TeleportManager import core.game.world.map.Location +import core.game.world.map.zone.ZoneBorders import core.game.world.update.flag.context.Animation import org.rs09.consts.Items import org.rs09.consts.NPCs object DrillDemonUtils { - val DD_KEY_NPC = "drilldemon:npc" - val DD_KEY_TASK = "drilldemon:task" - val DD_KEY_RETURN_LOC = "drilldemon:original-loc" + val DD_KEY_TASK = "/save:drilldemon:task" val DD_SIGN_VARP = 531 val DD_SIGN_JOG = 0 val DD_SIGN_SITUP = 1 val DD_SIGN_PUSHUP = 2 val DD_SIGN_JUMP = 3 - val DD_CORRECT_OFFSET = "drilldemon:offset" - val DD_CORRECT_COUNTER = "drilldemon:numcorrect" + val DD_CORRECT_OFFSET = "/save:drilldemon:offset" + val DD_CORRECT_COUNTER = "/save:drilldemon:numcorrect" + val DD_AREA = ZoneBorders(3158, 4817, 3168, 4823) + val DD_NPC = NPCs.SERGEANT_DAMIEN_2790 - fun teleport(player: Player){ - player.setAttribute(DD_KEY_RETURN_LOC,player.location) - player.properties.teleportLocation = Location.create(3163, 4819, 0) + fun teleport(player: Player) { + kidnapPlayer(player, Location.create(3163, 4819, 0), TeleportManager.TeleportType.INSTANT) player.interfaceManager.closeDefaultTabs() - player.packetDispatch.sendInterfaceConfig(548, 69, true) - player.packetDispatch.sendInterfaceConfig(746, 12, true) - - registerLogoutListener(player, "drilldemon"){p -> - teleport(p, player.getAttribute(DD_KEY_RETURN_LOC, p.location)) - } - - changeSignsAndAssignTask(player) + setComponentVisibility(player, 548, 69, true) + setComponentVisibility(player, 746, 12, true) } - fun changeSignsAndAssignTask(player: Player){ - player.varpManager.get(DD_SIGN_VARP).clearBitRange(0,31) + 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() - val tempOffsetList = arrayListOf(0,2,4,6).shuffled().toMutableList() + val tempOffsetList = arrayListOf(1335, 1336, 1337, 1338).shuffled().toMutableList() val task = tempList.random() val taskOffset = tempOffsetList.random() - player.setAttribute(DD_KEY_TASK,task) - player.setAttribute(DD_CORRECT_OFFSET,taskOffset) + setAttribute(player, DD_KEY_TASK, task) + setAttribute(player, DD_CORRECT_OFFSET, taskOffset) tempList.remove(task) tempOffsetList.remove(taskOffset) - player.varpManager.get(DD_SIGN_VARP).setVarbit(taskOffset,task).send(player) - for(i in 0 until tempList.size){ - player.varpManager.get(DD_SIGN_VARP).setVarbit(tempOffsetList[i],tempList[i]).send(player) + setVarbit(player, taskOffset, task) + for (i in 0 until tempList.size) { + setVarbit(player, tempOffsetList[i], tempList[i], true) } - - player.dialogueInterpreter.sendDialogues(NPCs.SERGEANT_DAMIEN_2790, core.game.dialogue.FacialExpression.OLD_NORMAL,when(task){ - DD_SIGN_JOG -> "Get over there and jog in place!" - DD_SIGN_JUMP -> "I need 40 jumping jacks stat!" - DD_SIGN_PUSHUP -> "Get over there and give me 20 pushups!" - DD_SIGN_SITUP -> "I need 30 situps pronto!" - else -> "REEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE" - }) - player.unlock() } - fun getOffsetForID(id: Int): Int{ - return when(id){ - 10076 -> 0 - 10077 -> 2 - 10078 -> 4 - 10079 -> 6 + fun getVarbitForId(id: Int): Int { + return when (id) { + 10076 -> 1335 + 10077 -> 1336 + 10078 -> 1337 + 10079 -> 1338 else -> 0 } } - fun getMatTask(id: Int, player: Player): Int{ - return player.varpManager.get(DD_SIGN_VARP).getVarbitValue(getOffsetForID(id)) ?: 0 + fun getMatTask(id: Int, player: Player): Int { + return getVarbit(player, getVarbitForId(id)) } - fun cleanup(player: Player){ - clearLogoutListener(player, "drilldemon") - player.properties.teleportLocation = player.getAttribute(DD_KEY_RETURN_LOC) - player.removeAttribute(DD_KEY_NPC) - player.removeAttribute(DD_KEY_RETURN_LOC) - player.removeAttribute(DD_KEY_TASK) - player.removeAttribute(DD_CORRECT_OFFSET) - player.removeAttribute(DD_CORRECT_COUNTER) + fun cleanup(player: Player) { + returnPlayer(player) + removeAttributes(player, DD_KEY_TASK, DD_CORRECT_OFFSET, DD_CORRECT_COUNTER) player.interfaceManager.openDefaultTabs() - player.packetDispatch.sendInterfaceConfig(548, 69, false) - player.packetDispatch.sendInterfaceConfig(746, 12, false) + setComponentVisibility(player, 548, 69, false) + setComponentVisibility(player, 746, 12, false) } fun animationForTask(task: Int): Animation { - return when(task){ + return when (task) { DD_SIGN_SITUP -> Animation(2763) DD_SIGN_PUSHUP -> Animation(2762) DD_SIGN_JUMP -> Animation(2761) @@ -100,21 +81,19 @@ object DrillDemonUtils { } } - fun reward(player: Player){ - val hasHat = player.inventory.contains(Items.CAMO_HELMET_6656,1) || player.bank.contains(Items.CAMO_HELMET_6656,1) || player.equipment.contains(Items.CAMO_HELMET_6656,1) - val hasShirt = player.inventory.contains(Items.CAMO_TOP_6654,1) || player.bank.contains(Items.CAMO_TOP_6654,1) || player.equipment.contains(Items.CAMO_TOP_6654,1) - val hasPants = player.inventory.contains(Items.CAMO_BOTTOMS_6655,1) || player.bank.contains(Items.CAMO_BOTTOMS_6655,1) || player.equipment.contains(Items.CAMO_BOTTOMS_6655,1) - val reward = if(!hasHat){ - Item(Items.CAMO_HELMET_6656) - } else if(!hasShirt){ - Item(Items.CAMO_TOP_6654) - } else if(!hasPants){ - Item(Items.CAMO_BOTTOMS_6655) - } else { - Item(Items.COINS_995, 500) - } - if(!player.inventory.add(reward)){ - GroundItemManager.create(reward,player) + fun reward(player: Player) { + queueScript(player, 2, QueueStrength.SOFT) { + val hasHat = hasAnItem(player, Items.CAMO_HELMET_6656).container != null + val hasShirt = hasAnItem(player, Items.CAMO_TOP_6654).container != null + val hasPants = hasAnItem(player, Items.CAMO_BOTTOMS_6655).container != null + when { + !hasHat -> addItemOrDrop(player, Items.CAMO_HELMET_6656) + !hasShirt -> addItemOrDrop(player, Items.CAMO_TOP_6654) + !hasPants -> addItemOrDrop(player, Items.CAMO_BOTTOMS_6655) + else -> addItemOrDrop(player, Items.COINS_995, 500) + } + return@queueScript stopExecuting(player) } + } -} \ No newline at end of file +} diff --git a/Server/src/main/content/global/ame/events/drilldemon/SeargentDamienDefaultPlugin.kt b/Server/src/main/content/global/ame/events/drilldemon/SeargentDamienDefaultPlugin.kt deleted file mode 100644 index 67971e71b..000000000 --- a/Server/src/main/content/global/ame/events/drilldemon/SeargentDamienDefaultPlugin.kt +++ /dev/null @@ -1,29 +0,0 @@ -package content.global.ame.events.drilldemon - -import core.game.dialogue.DialoguePlugin -import core.game.node.entity.player.Player -import core.plugin.Initializable -import org.rs09.consts.NPCs -import core.tools.END_DIALOGUE - -@Initializable -class SeargentDamienDefaultPlugin(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { - return SeargentDamienDefaultPlugin(player) - } - - override fun open(vararg args: Any?): Boolean { - npc("GET BACK TO WORK MAGGOT!") - stage = END_DIALOGUE - return true - } - - override fun handle(interfaceId: Int, buttonId: Int): Boolean { - return true - } - - override fun getIds(): IntArray { - return intArrayOf(NPCs.SERGEANT_DAMIEN_2790) - } - -} \ No newline at end of file diff --git a/Server/src/main/content/global/ame/events/drilldemon/SeargentDamienDialogue.kt b/Server/src/main/content/global/ame/events/drilldemon/SeargentDamienDialogue.kt index 6ab770f21..d9bff58af 100644 --- a/Server/src/main/content/global/ame/events/drilldemon/SeargentDamienDialogue.kt +++ b/Server/src/main/content/global/ame/events/drilldemon/SeargentDamienDialogue.kt @@ -1,54 +1,54 @@ package content.global.ame.events.drilldemon -import core.game.dialogue.FacialExpression -import core.game.system.task.Pulse +import core.api.getAttribute +import core.api.sendItemDialogue +import core.api.unlock import core.game.dialogue.DialogueFile +import core.game.dialogue.FacialExpression +import core.game.node.entity.npc.NPC import core.tools.END_DIALOGUE -import core.tools.START_DIALOGUE +import org.rs09.consts.Items +import org.rs09.consts.NPCs -class SeargentDamienDialogue(val isCorrect: Boolean = false) : DialogueFile() { +class SeargentDamienDialogue(var isCorrect: Boolean = false, var eventStart: Boolean = false) : DialogueFile() { override fun handle(componentID: Int, buttonID: Int) { - var correctAmt = player!!.getAttribute(DrillDemonUtils.DD_CORRECT_COUNTER,0) - if(correctAmt == 4 && content.global.ame.RandomEventManager.getInstance(player!!)!!.event == null) { - when(stage){ - 0 -> npc(core.game.dialogue.FacialExpression.OLD_NORMAL,"My god you actually did it, you limp","wristed worm-bodied MAGGOT! Take this","and get out of my sight.").also { stage++ } - 1 -> { - end() - player!!.unlock() - DrillDemonUtils.cleanup(player!!) - player!!.pulseManager.run(object : Pulse(2){ - override fun pulse(): Boolean { - DrillDemonUtils.reward(player!!) - return true - } - }) - } - } - } else if(content.global.ame.RandomEventManager.getInstance(player!!)!!.event == null){ - when(stage){ - START_DIALOGUE -> if(isCorrect) npc(core.game.dialogue.FacialExpression.OLD_NORMAL,"Good! Now...").also { stage++ } else npc(core.game.dialogue.FacialExpression.OLD_ANGRY1,"WRONG, MAGGOT!").also { stage++ } - 1 -> { - end() + npc = NPC(NPCs.SERGEANT_DAMIEN_2790) + val correctCounter = player!!.getAttribute(DrillDemonUtils.DD_CORRECT_COUNTER,0) + when(stage) { + 0 -> { + if (correctCounter >= 4) { + npcl(FacialExpression.OLD_NORMAL, "Well I'll be, you actually did it private. Now take this and get yourself out of my sight.") + stage = 100 + } else if (eventStart) { + npcl(FacialExpression.OLD_NORMAL, "Move yourself private! Follow my orders and you may, just may, leave here in a fit state for my corps!") DrillDemonUtils.changeSignsAndAssignTask(player!!) + stage = 0 + eventStart = false + } else { + npcl(FacialExpression.OLD_NORMAL, when (getAttribute(player!!, DrillDemonUtils.DD_KEY_TASK, -1)) { + DrillDemonUtils.DD_SIGN_JOG -> if (!isCorrect) "Wrong exercise, worm! Get yourself over there and jog on that mat private!" else "Get yourself over there and jog on that mat private!" + DrillDemonUtils.DD_SIGN_JUMP -> if (!isCorrect) "Wrong exercise, worm! I want to see you on that mat doing star jumps private!" else "I want to see you on that mat doing star jumps private!" + DrillDemonUtils.DD_SIGN_PUSHUP -> if (!isCorrect) "Wrong exercise, worm! Drop and give me push ups on that mat private!" else "Drop and give me push ups on that mat private!" + DrillDemonUtils.DD_SIGN_SITUP -> if (!isCorrect) "Wrong exercise, worm! Get on that mat and give me sit ups private!" else "Get on that mat and give me sit ups private!" + else -> "REEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE" + }) + stage = 1 + unlock(player!!) } + } + 1 -> { + when (getAttribute(player!!, DrillDemonUtils.DD_KEY_TASK, -1)) { + DrillDemonUtils.DD_SIGN_JOG -> sendItemDialogue(player!!, Items.RUN_10947, "Go to this mat and jog on the spot!") + DrillDemonUtils.DD_SIGN_JUMP -> sendItemDialogue(player!!, Items.STARJUMP_10949, "Go to this mat and do some starjumps!") + DrillDemonUtils.DD_SIGN_PUSHUP -> sendItemDialogue(player!!, Items.PUSHUP_10946, "Go to this mat and do some pushups!") + DrillDemonUtils.DD_SIGN_SITUP -> sendItemDialogue(player!!, Items.SITUP_10948, "Go to this mat and do some sit ups!") } - } else { - when(stage){ - START_DIALOGUE -> npc(core.game.dialogue.FacialExpression.OLD_NORMAL,"Would you like to come work out?").also { stage++ } - 1 -> options("Yes, please.", "No, thanks.").also { stage++ } - 2 -> when(buttonID){ - 1 -> { - end() - DrillDemonUtils.teleport(player!!) - content.global.ame.RandomEventManager.getInstance(player!!)!!.event?.terminate() - stage = END_DIALOGUE - } - 2 -> { - end() - content.global.ame.RandomEventManager.getInstance(player!!)!!.event?.terminate() - stage = END_DIALOGUE - } - } + stage = END_DIALOGUE + } + 100 -> { + end() + DrillDemonUtils.cleanup(player!!) + DrillDemonUtils.reward(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 36d8cb9c5..000000000 --- a/Server/src/main/content/global/ame/events/drilldemon/SeargentDamienNPC.kt +++ /dev/null @@ -1,26 +0,0 @@ -package content.global.ame.events.drilldemon - -import core.game.node.entity.npc.NPC -import core.tools.RandomFunction -import org.rs09.consts.NPCs -import content.global.ame.RandomEventNPC -import core.api.utils.WeightBasedTable - -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!") - } - - override fun tick() { - super.tick() - if(RandomFunction.random(1,10) == 5){ - sendChat(player.username.capitalize() + "! Drop and give me 20!") - } - } - - override fun talkTo(npc: NPC) { - player.dialogueInterpreter.open(SeargentDamienDialogue(),npc) - } -} \ No newline at end of file diff --git a/Server/src/main/content/global/ame/events/drilldemon/SergeantDamienBehavior.kt b/Server/src/main/content/global/ame/events/drilldemon/SergeantDamienBehavior.kt new file mode 100644 index 000000000..c52c17e2d --- /dev/null +++ b/Server/src/main/content/global/ame/events/drilldemon/SergeantDamienBehavior.kt @@ -0,0 +1,22 @@ +package content.global.ame.events.drilldemon + +import core.api.inBorders +import core.game.node.entity.npc.NPC +import core.game.node.entity.npc.NPCBehavior +import core.game.world.map.Location +import org.rs09.consts.NPCs + +class SeargentDamienBehavior: NPCBehavior(NPCs.SERGEANT_DAMIEN_2790) { + + override fun onCreation(self: NPC) { + + // Sergeant Damien is supposed to walk around the perimeter of the exercise mats + if (inBorders(self, DrillDemonUtils.DD_AREA)) { + val movementPath = arrayOf(Location.create(3167, 4822, 0), Location.create(3167, 4818, 0), Location.create(3159, 4818, 0), Location.create(3159, 4822, 0)) + self.configureMovementPath(*movementPath) + self.isWalks = true + } + } + + +} \ 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..af4e8fe3c --- /dev/null +++ b/Server/src/main/content/global/ame/events/drilldemon/SergeantDamienNPC.kt @@ -0,0 +1,38 @@ +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 SergeantDamienNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.SERGEANT_DAMIEN_2790) { + + override fun init() { + super.init() + sendChat(player.username+ "! Drop and give me 20!") + queueScript(player, 4, QueueStrength.SOFT) { stage: Int -> + when (stage) { + 0 -> { + lock(player, secondsToTicks(30)) + DrillDemonUtils.teleport(player) + AntiMacro.terminateEventNpc(player) + return@queueScript delayScript(player, 2) + } + 1 -> { + openDialogue(player, SeargentDamienDialogue(isCorrect = true, eventStart = true), NPCs.SERGEANT_DAMIEN_2790) + return@queueScript stopExecuting(player) + } + else -> return@queueScript stopExecuting(player) + } + } + } + + 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/DrunkenDwarfBehavior.kt b/Server/src/main/content/global/ame/events/drunkendwarf/DrunkenDwarfBehavior.kt new file mode 100644 index 000000000..a92c554f4 --- /dev/null +++ b/Server/src/main/content/global/ame/events/drunkendwarf/DrunkenDwarfBehavior.kt @@ -0,0 +1,14 @@ +package content.global.ame.events.drunkendwarf + +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.tools.RandomFunction +import org.rs09.consts.NPCs + +class DrunkenDwarfBehavior : NPCBehavior(NPCs.DRUNKEN_DWARF_956) { + override fun beforeAttackFinalized(self: NPC, victim: Entity, state: BattleState) { + state.estimatedHit = RandomFunction.getRandom(3) + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/ame/events/drunkendwarf/DrunkenDwarfDialogue.kt b/Server/src/main/content/global/ame/events/drunkendwarf/DrunkenDwarfDialogue.kt new file mode 100644 index 000000000..010f5c366 --- /dev/null +++ b/Server/src/main/content/global/ame/events/drunkendwarf/DrunkenDwarfDialogue.kt @@ -0,0 +1,21 @@ +package content.global.ame.events.drunkendwarf + +import core.api.addItemOrDrop +import core.game.dialogue.DialogueFile +import core.game.dialogue.FacialExpression +import core.game.system.timer.impl.AntiMacro +import org.rs09.consts.Items + +class DrunkenDwarfDialogue : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + 0 -> npcl(FacialExpression.OLD_DRUNK_RIGHT, "I 'new it were you matey! 'Ere, have some ob the good stuff!").also { stage++ } + 1 -> { + addItemOrDrop(player!!, Items.BEER_1917) + addItemOrDrop(player!!, Items.KEBAB_1971) + AntiMacro.terminateEventNpc(player!!) + end() + } + } + } +} \ 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 new file mode 100644 index 000000000..1485dd8c4 --- /dev/null +++ b/Server/src/main/content/global/ame/events/drunkendwarf/DrunkenDwarfNPC.kt @@ -0,0 +1,50 @@ +package content.global.ame.events.drunkendwarf + +import content.global.ame.RandomEventNPC +import core.api.* +import core.api.utils.WeightBasedTable +import core.game.node.entity.npc.NPC +import core.tools.RandomFunction +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 + 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*") + } + + override fun tick() { + if (RandomFunction.roll(20) && !attackPhrase) + sendPhrases() + if (ticksLeft <= 10) { + ticksLeft = 10 + if (!attackPhrase) + sendChat("I hates you, ${player.username.capitalize()}!").also { attackPhrase = true } + if (attackDelay <= getWorldTicks()) + this.attack(player) + } + super.tick() + } + + override fun talkTo(npc: NPC) { + attackDelay = getWorldTicks() + 10 + this.pulseManager.clear() + openDialogue(player, DrunkenDwarfDialogue(), this.asNpc()) + } +} \ 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 new file mode 100644 index 000000000..f8d132a7f --- /dev/null +++ b/Server/src/main/content/global/ame/events/evilbob/EvilBobDialogue.kt @@ -0,0 +1,113 @@ +package content.global.ame.events.evilbob + +import content.global.ame.events.evilbob.EvilBobUtils.giveEventFishingSpot +import core.api.* +import core.game.dialogue.DialogueFile +import core.game.dialogue.FacialExpression +import core.game.node.entity.npc.NPC +import core.game.node.entity.skill.Skills +import core.tools.END_DIALOGUE +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +class EvilBobDialogue(val rewardDialogue: Boolean = false, val rewardXpSkill: Int = Skills.FISHING) : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + npc = NPC(NPCs.EVIL_BOB_2479) + if (getAttribute(player!!, EvilBobUtils.assignedFishingZone, "none") == "none") giveEventFishingSpot(player!!) + when (stage) { + 0 -> { + if (rewardDialogue) { + sendPlayerDialogue(player!!, "That was the strangest dream I've ever had! Assuming it was a dream...", FacialExpression.HALF_ASKING) + stage = if (rewardXpSkill == Skills.FISHING) 900 else 901 + } 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)) { + 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.attentive, true) + setAttribute(player!!, EvilBobUtils.attentiveNewSpot, true) + playerl(FacialExpression.NEUTRAL, "Here, I've brought you some fish.").also { stage = 600 } + } else if (inInventory(player!!, Items.FISHLIKE_THING_6202) || inInventory(player!!, Items.FISHLIKE_THING_6206)) { + npcl(FacialExpression.CHILD_NORMAL, "What, are you giving me cooked fish? What am I going to do with that? Uncook it first!").also { stage = 700 } + } else if (!getAttribute(player!!, EvilBobUtils.startingDialogueSeen, false)) { + playerl(FacialExpression.ASKING, "Huh?").also { stage = 800 } + setAttribute(player!!, EvilBobUtils.startingDialogueSeen, true) + } else playerl(FacialExpression.ANNOYED, "Let me out of here!").also { stage++ } + } + 1 -> npcl(FacialExpression.CHILD_NORMAL, "I will never let you go, ${player!!.username}!").also { stage++ } + 2 -> options("Why not?", "What's it all about?", "How is it possible that you're talking?", "What did you do to Bob?").also { stage++ } + 3 -> when (buttonID) { + 1 -> playerl(FacialExpression.ASKING, "Why not?").also { stage = 100 } + 2 -> playerl(FacialExpression.ASKING, "What's it all about?").also { stage = 200 } + 3 -> playerl(FacialExpression.ASKING, "How is it possible that you're talking?").also { stage = 300 } + 4 -> playerl(FacialExpression.ASKING, "What did you do to Bob?").also { stage = 400 } + } + + 100 -> npcl(FacialExpression.CHILD_NORMAL, "Uhm...").also { stage++ } + 101 -> npcl(FacialExpression.CHILD_NORMAL, "Because I say so! And because I can never have enough servants!").also { stage++ } + 102 -> npcl(FacialExpression.CHILD_NORMAL, "Now catch me some fish, I'm hungry.").also { stage++ } + 103 -> playerl(FacialExpression.ASKING, "What fish, where?").also { stage++ } + 104 -> npcl(FacialExpression.CHILD_NORMAL, "Talk to my other servants, and hurry it up!").also { stage = END_DIALOGUE } + + 200 -> npcl(FacialExpression.CHILD_NORMAL, "Sit down and I'll tell you. It's a question of servant shortage.").also { stage++ } + 201 -> playerl(FacialExpression.NEUTRAL, "Go on.").also { stage++ } + 202 -> npcl(FacialExpression.CHILD_NORMAL, "You are a skilled worker. A human like you is worth a great deal as a slave.").also { stage++ } + 203 -> playerl(FacialExpression.ANGRY, "A slave?? I will have nothing to do with you. Is that clear? Absolutely nothing.").also { stage++ } + 204 -> npcl(FacialExpression.CHILD_NORMAL, "Now be reasonable little human. It's just a matter of time before you will do everything I ask you to. Play along and this can be a very nice place.").also { stage++ } + 205 -> playerl(FacialExpression.ANGRY, "I will not make any deals with you. I'm a human. I will not be pushed, numbered, meowed at, teleported or enslaved. My life is my own.").also { stage++ } + 206 -> npcl(FacialExpression.CHILD_NORMAL, "Is it?").also { stage++ } + 207 -> playerl(FacialExpression.ANNOYED, "Yes. You won't hold me.").also { stage++ } + 208 -> npcl(FacialExpression.CHILD_NORMAL, "Won't I?").also { stage++ } + 209 -> npcl(FacialExpression.CHILD_NORMAL, "Let me assure you there's no way out.").also { stage++ } + 210 -> npcl(FacialExpression.CHILD_NORMAL, "Just ask my servants!").also { stage = END_DIALOGUE } + + 300 -> npcl(FacialExpression.CHILD_NORMAL, "How is it possible that you're not meowing?").also { stage++ } + 301 -> playerl(FacialExpression.HALF_ASKING, "Meowing?? Why would I be meowing?").also { stage++ } + 302 -> npcl(FacialExpression.CHILD_NORMAL, "Most humans do, that's why I'm wearing this amulet of Man speak... but I do try to train my staff in civilised speech.").also { stage++ } + 303 -> npcl(FacialExpression.CHILD_NORMAL, "Ah, but I suppose things are slightly different in your world... what a dreadful place that must be.").also { stage++ } + 304 -> playerl(FacialExpression.NEUTRAL, "I think I'm getting a headache...").also { stage = END_DIALOGUE } + + 400 -> npcl(FacialExpression.CHILD_NORMAL, "Bob? I am Bob!").also { stage++ } + 401 -> npcl(FacialExpression.CHILD_NORMAL, "Oh, you mean the Bob in your world? Nothing. I am an incarnation of Bob here on Scape2009.").also { stage++ } + 402 -> playerl(FacialExpression.NEUTRAL, "What, you mean this is... like some kind of mirror land?").also { stage++ } + 403 -> npcl(FacialExpression.CHILD_NORMAL, " Something like that... somewhere in Scape2009 there must be a human servant called ${player!!.username}.").also { stage++ } + 404 -> npcl(FacialExpression.CHILD_NORMAL, "But you work just as well for me! Now get to work, human! Fish for me!").also { stage = END_DIALOGUE } + + 500 ->{ + if (getAttribute(player!!, EvilBobUtils.attentive , false)) stage = 505 else stage++ + npcl(FacialExpression.CHILD_NORMAL, "Mmm, mmm...that's delicious.") + } + 501 -> npcl(FacialExpression.CHILD_NORMAL, "Now, let me take...a little...catnap.").also { stage++ } + 502 -> { + end() + setAttribute(player!!, EvilBobUtils.eventComplete, true) + findNPC(NPCs.EVIL_BOB_2479)!!.sendChat("ZZZzzz") + } + 505 -> { + setAttribute(player!!, EvilBobUtils.attentive , false) + setAttribute(player!!, EvilBobUtils.attentiveNewSpot , true) + giveEventFishingSpot(player!!) + npcl(FacialExpression.CHILD_NORMAL, "Now get me another, you no-good human.").also { stage++ } + } + 506 -> sendDialogue(player!!, "Evil Bob seems slightly less attentive of you.").also { stage = END_DIALOGUE } + + 600 -> npcl(FacialExpression.CHILD_NORMAL, "What was this? That was absolutely disgusting!").also { stage++ } + 601 -> npcl(FacialExpression.CHILD_NORMAL, "Don't you know what kind of fish I like? Talk to my other servants for some advice.").also { stage++ } + 602 -> sendDialogue(player!!, "Evil Bob seems more attentive of you.").also { stage = END_DIALOGUE } + + 700 -> playerl(FacialExpression.HALF_ASKING, "Errr... Uncook it?").also { stage++ } + 701 -> npcl(FacialExpression.CHILD_NORMAL,"You heard me! There's the cold fire, by the trees, now get uncooking!").also { stage = END_DIALOGUE } + + 800 -> playerl(FacialExpression.ANGRY, "Where am I?").also { stage++ } + 801 -> npcl(FacialExpression.CHILD_NORMAL, "On my island.").also { stage++ } + 802 -> playerl(FacialExpression.ANGRY, "Who brought me here?").also { stage++ } + 803 -> npcl(FacialExpression.CHILD_NORMAL, "That would be telling.").also { stage++ } + 804 -> playerl(FacialExpression.ANGRY, "Take me to your leader!").also { stage++ } + 805 -> npcl(FacialExpression.CHILD_NORMAL,"I am your leader, you are but a slave.").also { stage++ } + 806 -> playerl(FacialExpression.ANGRY, "I am not a slave, I am a free man!").also { stage++ } + 807 -> npcl(FacialExpression.CHILD_NORMAL, "Ah-ha-ha-ha-ha-ha!").also { stage = END_DIALOGUE } + 900 -> sendDialogue(player!!, "You feel somehow that you've become better at fishing.").also { stage = END_DIALOGUE } + 901 -> sendDialogue(player!!, "You feel somehow that you've become better at magic.").also { stage = END_DIALOGUE } + } + } +} diff --git a/Server/src/main/content/global/ame/events/evilbob/EvilBobListeners.kt b/Server/src/main/content/global/ame/events/evilbob/EvilBobListeners.kt new file mode 100644 index 000000000..f2a3e9134 --- /dev/null +++ b/Server/src/main/content/global/ame/events/evilbob/EvilBobListeners.kt @@ -0,0 +1,148 @@ +package content.global.ame.events.evilbob + +import content.global.ame.returnPlayer +import core.ServerConstants +import core.api.* +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.player.link.emote.Emotes +import core.game.world.map.Location +import core.game.world.map.zone.ZoneBorders +import core.game.world.map.zone.ZoneRestriction +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import org.rs09.consts.Sounds + +class EvilBobListeners : InteractionListener, MapArea { + + override fun defineListeners() { + on(EvilBobUtils.evilBob, IntType.NPC, "talk-to") { player, node -> + openDialogue(player, EvilBobDialogue(), node.asNpc()) + return@on true + } + + on(EvilBobUtils.servant, IntType.NPC, "talk-to") { player, node -> + openDialogue(player, ServantDialogue(), node.asNpc()) + return@on true + } + + on(EvilBobUtils.fishingSpot, IntType.SCENERY, "net") { player, _ -> + if (getAttribute(player!!, EvilBobUtils.attentiveNewSpot, false) || getAttribute(player!!, EvilBobUtils.eventComplete, false)) { + sendDialogue(player, "You don't know if this is a good place to go fishing. Perhaps you should ask someone, like one of the human servants.") + } else if (!inInventory(player, Items.SMALL_FISHING_NET_303)) { + 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 (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) + animate(player, EvilBobUtils.fishAnim) + sendMessage(player, "You cast out your net...") + runTask(player, 6) { + when (getAttribute(player, EvilBobUtils.assignedFishingZone, EvilBobUtils.northFishingZone.toString())) { + EvilBobUtils.northFishingZone.toString() -> { + if (inBorders(player, EvilBobUtils.northFishingZone)) { + addItem(player, Items.FISHLIKE_THING_6202) + } else addItem(player, Items.FISHLIKE_THING_6206) + } + EvilBobUtils.southFishingZone.toString() -> { + if (inBorders(player, EvilBobUtils.southFishingZone)) { + addItem(player, Items.FISHLIKE_THING_6202) + } else addItem(player, Items.FISHLIKE_THING_6206) + } + EvilBobUtils.eastFishingZone.toString() -> { + if (inBorders(player, EvilBobUtils.eastFishingZone)) { + addItem(player, Items.FISHLIKE_THING_6202) + } else addItem(player, Items.FISHLIKE_THING_6206) + } + EvilBobUtils.westFishingZone.toString() -> { + if (inBorders(player, EvilBobUtils.westFishingZone)) { + addItem(player, Items.FISHLIKE_THING_6202) + } else addItem(player, Items.FISHLIKE_THING_6206) + } + } + sendItemDialogue(player, Items.FISHLIKE_THING_6202, "You catch a... what is this?? Is this a fish?? And it's cooked already??") + resetAnimator(player) + } + } + return@on true + } + + onUseWith(IntType.SCENERY, EvilBobUtils.fishlikeThings, EvilBobUtils.uncookingPot) { player, _, _ -> + lock(player, 2) + animate(player, EvilBobUtils.cookAnim) + playAudio(player, Sounds.UNCOOKING_2322) + if (removeItem(player, Items.FISHLIKE_THING_6202)) addItem(player, Items.RAW_FISHLIKE_THING_6200) + if (removeItem(player, Items.FISHLIKE_THING_6206)) addItem(player, Items.RAW_FISHLIKE_THING_6204) + return@onUseWith true + } + + onUseWith(IntType.NPC, EvilBobUtils.fishlikeThings, EvilBobUtils.evilBob) { player, _, _ -> + openDialogue(player, EvilBobDialogue(), NPCs.EVIL_BOB_2479) + return@onUseWith true + } + + onUseWith(IntType.NPC, EvilBobUtils.rawFishlikeThings, EvilBobUtils.evilBob) { player, _, _ -> + openDialogue(player, EvilBobDialogue(), NPCs.EVIL_BOB_2479) + return@onUseWith true + } + + on(EvilBobUtils.fishlikeThings, IntType.ITEM, "Eat") { player, _ -> + sendMessage(player, "It looks vile and smells even worse. You're not eating that!") + return@on true + } + + on(EvilBobUtils.exitPortal, IntType.SCENERY, "enter") { player, portal -> + if (getAttribute(player, EvilBobUtils.eventComplete, false)) { + lock(player, 12) + queueScript(player, 0, QueueStrength.SOFT) { stage: Int -> + when (stage) { + 0 -> { + forceMove(player, player.location, portal.location, 0, 50, null, 819) + return@queueScript delayScript(player, 3) + } + 1 -> { + player.faceLocation(Location.create(3421, 4777, 0)) + emote(player, Emotes.RASPBERRY) + sendChat(player, "Be seeing you!") + return@queueScript delayScript(player,2) + } + 2 -> { + animate(player, EvilBobUtils.teleAnim) + player.graphics(EvilBobUtils.telegfx) + playAudio(player, Sounds.TELEPORT_ALL_200) + return@queueScript delayScript(player, 2) + } + 3 -> { + sendMessage(player, "Welcome back to ${ServerConstants.SERVER_NAME}.") + returnPlayer(player) + EvilBobUtils.reward(player) + EvilBobUtils.cleanup(player) + resetAnimator(player) + return@queueScript stopExecuting(player) + } + else -> return@queueScript stopExecuting(player) + } + } + } else sendNPCDialogue(player, NPCs.EVIL_BOB_2479, "You're going nowhere, human!", FacialExpression.CHILD_NEUTRAL) + return@on true + } + } + override fun defineAreaBorders(): Array { + return arrayOf(ZoneBorders(3400, 4762, 3443, 4793)) + } + + override fun getRestrictions(): Array { + return arrayOf(ZoneRestriction.RANDOM_EVENTS, ZoneRestriction.CANNON, ZoneRestriction.FOLLOWERS, ZoneRestriction.OFF_MAP) + } + + override fun areaEnter(entity: Entity) { + entity.locks.lockTeleport(1000000) + } + +} diff --git a/Server/src/main/content/global/ame/events/evilbob/EvilBobNPC.kt b/Server/src/main/content/global/ame/events/evilbob/EvilBobNPC.kt new file mode 100644 index 000000000..fc0fe2826 --- /dev/null +++ b/Server/src/main/content/global/ame/events/evilbob/EvilBobNPC.kt @@ -0,0 +1,44 @@ +package content.global.ame.events.evilbob + +import content.global.ame.RandomEventNPC +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 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) + } + } + } + + override fun talkTo(npc: NPC) { + openDialogue(player, EvilBobDialogue(), this.asNpc()) + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/ame/events/evilbob/EvilBobUtils.kt b/Server/src/main/content/global/ame/events/evilbob/EvilBobUtils.kt new file mode 100644 index 000000000..d70eab940 --- /dev/null +++ b/Server/src/main/content/global/ame/events/evilbob/EvilBobUtils.kt @@ -0,0 +1,90 @@ +package content.global.ame.events.evilbob + +import content.global.ame.kidnapPlayer +import content.global.ame.returnPlayer +import core.api.* +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.TeleportManager +import core.game.node.entity.skill.Skills +import core.game.world.map.Location +import core.game.world.map.zone.ZoneBorders +import core.game.world.update.flag.context.Animation +import core.game.world.update.flag.context.Graphics +import core.tools.RandomFunction +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import org.rs09.consts.Scenery + +object EvilBobUtils { + const val eventComplete = "/save:evilbob:eventcomplete" + const val assignedFishingZone = "/save:evilbob:fishingzone" + const val attentive = "/save:evilbob:attentive" + const val servantHelpDialogueSeen = "/save:evilbob:servantdialogeseen" + const val attentiveNewSpot = "/save:evilbob:attentivenewspot" + const val startingDialogueSeen = "/save:evilbob:startingdialogueseen" + + const val evilBob = NPCs.EVIL_BOB_2479 + const val servant = NPCs.SERVANT_2481 + const val exitPortal = Scenery.PORTAL_8987 + + const val uncookingPot = Scenery.UNCOOKING_POT_8985 + const val fishingSpot = 8986 + + val fishlikeThings = intArrayOf(Items.FISHLIKE_THING_6202, Items.FISHLIKE_THING_6206) + val rawFishlikeThings = intArrayOf(Items.RAW_FISHLIKE_THING_6200, Items.RAW_FISHLIKE_THING_6204) + + val cookAnim = Animation(897) + val fishAnim = Animation(620) + val teleAnim = Animation(714) + val telegfx = Graphics(308, 100, 50) + + val northFishingZone = ZoneBorders(3421, 4789, 3427, 4792) + val eastFishingZone = ZoneBorders(3437, 4774, 3440, 4780) + val southFishingZone = ZoneBorders(3419, 4763, 3426, 4765) + val westFishingZone = ZoneBorders(3405, 4773, 3408, 4779) + + fun giveEventFishingSpot(player: Player) { + when (RandomFunction.getRandom(3)) { + 0 -> setAttribute(player, assignedFishingZone, northFishingZone.toString()) + 1 -> setAttribute(player, assignedFishingZone, southFishingZone.toString()) + 2 -> setAttribute(player, assignedFishingZone, eastFishingZone.toString()) + 3 -> setAttribute(player, assignedFishingZone, westFishingZone.toString()) + else -> setAttribute(player, assignedFishingZone, northFishingZone.toString()) + } + } + + fun teleport(player: Player) { + kidnapPlayer(player, Location.create(3419, 4776, 0), TeleportManager.TeleportType.INSTANT) + } + + fun cleanup(player: Player) { + removeAttributes(player, assignedFishingZone, eventComplete, attentive, servantHelpDialogueSeen, attentiveNewSpot, startingDialogueSeen) + removeAll(player, Items.FISHLIKE_THING_6202) + removeAll(player, Items.FISHLIKE_THING_6202, Container.BANK) + removeAll(player, Items.FISHLIKE_THING_6206) + removeAll(player, Items.FISHLIKE_THING_6206, Container.BANK) + removeAll(player, Items.RAW_FISHLIKE_THING_6200) + removeAll(player, Items.RAW_FISHLIKE_THING_6200, Container.BANK) + removeAll(player, Items.RAW_FISHLIKE_THING_6204) + removeAll(player, Items.RAW_FISHLIKE_THING_6204, Container.BANK) + } + + fun reward(player: Player) { + val experience = 650.0 + if (getStatLevel(player, Skills.MAGIC) > 50 ) { + when (RandomFunction.getRandom(1)) { + 0 -> { + player.skills.addExperience(Skills.FISHING, experience) + openDialogue(player, EvilBobDialogue(rewardDialogue = true, rewardXpSkill = Skills.FISHING), NPCs.EVIL_BOB_2479) + } + 1 -> { + player.skills.addExperience(Skills.MAGIC, experience) + openDialogue(player, EvilBobDialogue(rewardDialogue = true, rewardXpSkill = Skills.MAGIC), NPCs.EVIL_BOB_2479) + } + } + } else { + player.skills.addExperience(Skills.FISHING, experience) + openDialogue(player, EvilBobDialogue(rewardDialogue = true, rewardXpSkill = Skills.FISHING), NPCs.EVIL_BOB_2479) + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/ame/events/evilbob/ServantCutscene.kt b/Server/src/main/content/global/ame/events/evilbob/ServantCutscene.kt new file mode 100644 index 000000000..db54e0db9 --- /dev/null +++ b/Server/src/main/content/global/ame/events/evilbob/ServantCutscene.kt @@ -0,0 +1,150 @@ +package content.global.ame.events.evilbob + +import core.api.* +import core.game.activity.Cutscene +import core.game.node.entity.player.Player + +class ServantCutsceneN(player: Player) : Cutscene(player) { + + override fun setup() { + setExit(player.location.transform(0, 0, 0)) + loadRegion(13642) + } + + override fun runStage(stage: Int) { + when (stage) { + 0 -> { + fadeToBlack() + timedUpdate(4) + } + 1 -> { + teleport(player, 29, 41) + moveCamera(30, 43) // +7 from statue + openInterface(player, 186) + timedUpdate(2) + } + 2 -> { + timedUpdate(2) + rotateCamera(30, 51, 300, 100) // the statue loc + fadeFromBlack() + } + 3 -> { + timedUpdate(9) + moveCamera(30, 46, 300, 2) // +4 from statue + } + 4 -> { + end{ player.locks.lockTeleport(1000000) } + closeInterface(player) + } + } + } +} + +class ServantCutsceneS(player: Player) : Cutscene(player) { + + override fun setup() { + setExit(player.location.transform(0, 0, 0)) + loadRegion(13642) + } + + override fun runStage(stage: Int) { + when (stage) { + 0 -> { + fadeToBlack() + timedUpdate(4) + } + 1 -> { + teleport(player, 29, 41) + moveCamera(29, 38) // +7 from statue + openInterface(player, 186) + timedUpdate(2) + } + 2 -> { + timedUpdate(2) + rotateCamera(29, 30, 300, 100) // the statue loc + fadeFromBlack() + } + 3 -> { + timedUpdate(9) + moveCamera(29, 35, 300, 2) // +4 from statue + } + 4 -> { + end{ player.locks.lockTeleport(1000000) } + closeInterface(player) + } + } + } +} + +class ServantCutsceneE(player: Player) : Cutscene(player) { + + override fun setup() { + setExit(player.location.transform(0, 0, 0)) + loadRegion(13642) + } + + override fun runStage(stage: Int) { + when (stage) { + 0 -> { + fadeToBlack() + timedUpdate(4) + } + 1 -> { + teleport(player, 29, 41) + moveCamera(35, 41) // +7 from statue + openInterface(player, 186) + timedUpdate(2) + } + 2 -> { + timedUpdate(2) + rotateCamera(43, 41, 300, 100) // the statue loc + fadeFromBlack() + } + 3 -> { + timedUpdate(9) + moveCamera(38, 41, 300, 2) // +4 from statue + } + 4 -> { + end{ player.locks.lockTeleport(1000000) } + closeInterface(player) + } + } + } +} + +class ServantCutsceneW(player: Player) : Cutscene(player) { + + override fun setup() { + setExit(player.location.transform(0, 0, 0)) + loadRegion(13642) + } + + override fun runStage(stage: Int) { + when (stage) { + 0 -> { + fadeToBlack() + timedUpdate(4) + } + 1 -> { + teleport(player, 29, 41) + moveCamera(25, 40) // +7 from statue + openInterface(player, 186) + timedUpdate(2) + } + 2 -> { + timedUpdate(2) + rotateCamera(18, 40, 300, 100) // the statue loc + fadeFromBlack() + } + 3 -> { + timedUpdate(9) + moveCamera(22, 40, 300, 2) // +4 from statue + } + 4 -> { + end{ player.locks.lockTeleport(1000000) } + closeInterface(player) + } + + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/ame/events/evilbob/ServantDialogue.kt b/Server/src/main/content/global/ame/events/evilbob/ServantDialogue.kt new file mode 100644 index 000000000..c177f8dab --- /dev/null +++ b/Server/src/main/content/global/ame/events/evilbob/ServantDialogue.kt @@ -0,0 +1,58 @@ +package content.global.ame.events.evilbob + +import core.ServerConstants +import core.api.getAttribute +import core.api.setAttribute +import core.game.dialogue.DialogueFile +import core.game.dialogue.FacialExpression +import core.game.node.entity.npc.NPC +import core.tools.END_DIALOGUE +import org.rs09.consts.NPCs + +class ServantDialogue : DialogueFile() { + + override fun handle(componentID: Int, buttonID: Int) { + npc = NPC(NPCs.SERVANT_2481) + when (stage) { + 0 -> { + if (getAttribute(player!!, EvilBobUtils.eventComplete, false)) { + playerl(FacialExpression.NEUTRAL, "Evil Bob has fallen asleep, come quickly!").also { stage = 2 } + } else if (!getAttribute(player!!, EvilBobUtils.servantHelpDialogueSeen, false)) { + playerl(FacialExpression.ANGRY, "I need help, I've been kidnapped by an evil cat!").also { stage = 200 } + } else if (getAttribute(player!!, EvilBobUtils.attentiveNewSpot, false)) { + npcl(FacialExpression.SAD, "Look... over t-t-there! That fishing spot c-c-contains the f-f-f-fish he likes.").also { stage = 1 } + } else npcl(FacialExpression.SAD, "F-f-f-fish... give him the f-f-f-fish he likes and he might f- f-f-fall asleep.").also { stage = END_DIALOGUE } + + } + 1 -> { + end() + setAttribute(player!!, EvilBobUtils.attentiveNewSpot, false) + when (getAttribute(player!!, EvilBobUtils.assignedFishingZone, EvilBobUtils.northFishingZone.toString())) { + EvilBobUtils.northFishingZone.toString() -> ServantCutsceneN(player!!).start() + EvilBobUtils.southFishingZone.toString() -> ServantCutsceneS(player!!).start() + EvilBobUtils.eastFishingZone.toString() -> ServantCutsceneE(player!!).start() + EvilBobUtils.westFishingZone.toString() -> ServantCutsceneW(player!!).start() + } + } + 2 -> npcl(FacialExpression.SAD, "Come? Come where?").also { stage++ } + 3 -> playerl(FacialExpression.FRIENDLY, "Away from this place! To ${ServerConstants.SERVER_NAME} proper!").also { stage++ } + 4 -> npcl(FacialExpression.SAD, "You go, ${player!!.username}, I don't belong there... I belong here, in Scape2009. This is the only place I can ever go...").also { stage++ } + 5 -> options("But I love you!", "Oh alright then.").also { stage++ } + 6 -> when(buttonID) { + 1 -> playerl(FacialExpression.SAD, "But I love you!").also { stage = 100 } + 2 -> playerl(FacialExpression.NEUTRAL, "Oh, alright then, I'll be off! See you around!").also { stage = END_DIALOGUE} + } + + 100 -> npcl(FacialExpression.NEUTRAL, "Our love can never be, sweet ${player!!.username}.").also { stage++ } + 101 -> npcl(FacialExpression.NEUTRAL, "Go now! Go, and don't look back!").also { stage = END_DIALOGUE } + + 200 -> npcl(FacialExpression.SAD, "Meow! Errr... I c-c-c-can't help you... He'll kill us all!").also { stage++ } + 201 -> playerl(FacialExpression.ANGRY, "Now you listen to me! He's just a little cat! There must be something I can do!").also { stage++ } + 202 -> npcl(FacialExpression.SAD, "F-f-f-fish... give him the f-f-f-fish he likes and he might f- f-f-fall asleep.").also { stage++ } + 203 -> { + npcl(FacialExpression.SAD, "Look... over t-t-there! That fishing spot c-c-contains the f-f-f-fish he likes.").also { stage = 1 } + setAttribute(player!!, EvilBobUtils.servantHelpDialogueSeen, true) + } + } + } +} 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 56131f8fb..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,11 +18,10 @@ 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.name.capitalize())) + sendChat(phrases.random().replace("@name",player.username.capitalize())) this.isRespawn = false } @@ -37,9 +35,9 @@ class EvilChickenNPC(override var loot: WeightBasedTable? = null) : RandomEventN this.terminate() } if(getWorldTicks() % 10 == 0){ - sendChat(phrases.random().replace("@name",player.name.capitalize())) + sendChat(phrases.random().replace("@name",player.username.capitalize())) } super.tick() if(!player.viewport.currentPlane.npcs.contains(this)) this.clear() } -} \ No newline at end of file +} diff --git a/Server/src/main/content/global/ame/events/freakyforester/FreakListeners.kt b/Server/src/main/content/global/ame/events/freakyforester/FreakListeners.kt new file mode 100644 index 000000000..8754a8d08 --- /dev/null +++ b/Server/src/main/content/global/ame/events/freakyforester/FreakListeners.kt @@ -0,0 +1,66 @@ +package content.global.ame.events.freakyforester + +import core.api.* +import org.rs09.consts.NPCs +import org.rs09.consts.Scenery +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.Entity +import core.game.system.task.Pulse +import core.game.world.map.zone.ZoneBorders +import core.game.world.map.zone.ZoneRestriction +import org.rs09.consts.Items + +class FreakListeners : InteractionListener, MapArea { + + private val exitPortal = Scenery.PORTAL_8972 + private val freakNpc = NPCs.FREAKY_FORESTER_2458 + private val pheasants = intArrayOf(NPCs.PHEASANT_2459,NPCs.PHEASANT_2460,NPCs.PHEASANT_2461,NPCs.PHEASANT_2462) + override fun defineListeners() { + on(freakNpc,IntType.NPC,"talk-to") { player, node -> + if (inBorders(player, FreakUtils.freakArea)) { + if (getAttribute(player, FreakUtils.freakTask, -1) == -1) FreakUtils.giveFreakTask(player) + openDialogue(player, FreakyForesterDialogue(), node.asNpc()) + } else { + sendMessage(player, "They aren't interested in talking to you.") + } + return@on true + } + + on(pheasants,IntType.NPC,"attack") { player, node -> + if (getAttribute(player,FreakUtils.freakComplete, false)) { + sendMessage(player,"You don't need to attack any more pheasants. You're allowed to leave.") + } else if (inInventory(player, Items.RAW_PHEASANT_6178) || inInventory(player, Items.RAW_PHEASANT_6179) || getAttribute(player, FreakUtils.pheasantKilled, false)){ + sendMessage(player,"You don't need to attack any more pheasants.") + } else { + player.attack(node.asNpc()) + } + return@on true + } + + on(exitPortal,IntType.SCENERY,"enter") { player, _ -> + if (getAttribute(player,FreakUtils.freakComplete,false)) { + FreakUtils.cleanup(player) + submitWorldPulse(object : Pulse(2) { + override fun pulse(): Boolean { + FreakUtils.reward(player) + return true + } + }) + return@on true + } else sendMessage(player,"A supernatural force prevents you from leaving.") + return@on true + } + } + override fun defineAreaBorders(): Array { + return arrayOf(FreakUtils.freakArea) + } + + override fun getRestrictions(): Array { + return arrayOf(ZoneRestriction.RANDOM_EVENTS, ZoneRestriction.CANNON, ZoneRestriction.FOLLOWERS, ZoneRestriction.OFF_MAP) + } + + override fun areaEnter(entity: Entity) { + entity.locks.lockTeleport(1000000) + } +} diff --git a/Server/src/main/content/global/ame/events/freakyforester/FreakUtils.kt b/Server/src/main/content/global/ame/events/freakyforester/FreakUtils.kt new file mode 100644 index 000000000..e247bfeb1 --- /dev/null +++ b/Server/src/main/content/global/ame/events/freakyforester/FreakUtils.kt @@ -0,0 +1,59 @@ +package content.global.ame.events.freakyforester + +import content.global.ame.kidnapPlayer +import content.global.ame.returnPlayer +import core.api.* +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.TeleportManager +import core.game.world.map.Location +import core.game.world.map.zone.ZoneBorders +import core.tools.RandomFunction + +object FreakUtils{ + const val freakNpc = NPCs.FREAKY_FORESTER_2458 + const val freakTask = "/save:freakyf:task" + const val freakComplete = "/save:freakyf:complete" + const val pheasantKilled = "freakyf:killed" + val freakArea = ZoneBorders(2587, 4758, 2616, 4788) + fun giveFreakTask(player: Player) { + when(RandomFunction.getRandom(4)) { + 0 -> setAttribute(player, freakTask, NPCs.PHEASANT_2459) + 1 -> setAttribute(player, freakTask, NPCs.PHEASANT_2460) + 2 -> setAttribute(player, freakTask, NPCs.PHEASANT_2461) + 3 -> setAttribute(player, freakTask, NPCs.PHEASANT_2462) + else -> setAttribute(player, freakTask, NPCs.PHEASANT_2459) + } + player.dialogueInterpreter.open(FreakyForesterDialogue(), freakNpc) + } + + fun teleport(player: Player) { + kidnapPlayer(player, Location.create(2599, 4777 ,0), TeleportManager.TeleportType.INSTANT) + } + + fun cleanup(player: Player) { + returnPlayer(player) + removeAttributes(player, freakTask, freakComplete, pheasantKilled) + removeAll(player, Items.RAW_PHEASANT_6178) + removeAll(player, Items.RAW_PHEASANT_6178, Container.BANK) + removeAll(player, Items.RAW_PHEASANT_6179) + removeAll(player, Items.RAW_PHEASANT_6179, Container.BANK) + } + + fun reward(player: Player){ + val hasHat = hasAnItem(player, Items.LEDERHOSEN_HAT_6182).container != null + val hasTop = hasAnItem(player, Items.LEDERHOSEN_TOP_6180).container != null + val hasShort = hasAnItem(player, Items.LEDERHOSEN_SHORTS_6181).container != null + sendNPCDialogue(player, freakNpc, "You get a lederhosen item as a reward for your help, many thanks!") + when{ + (!hasHat) -> addItemOrDrop(player, Items.LEDERHOSEN_HAT_6182, 1) + (!hasTop) -> addItemOrDrop(player, Items.LEDERHOSEN_TOP_6180, 1) + (!hasShort) -> addItemOrDrop(player, Items.LEDERHOSEN_SHORTS_6181, 1) + else ->{ + sendNPCDialogue(player, freakNpc, "You get some money for your help, many thanks!") + addItemOrDrop(player, Items.COINS_995, 500) + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/ame/events/freakyforester/FreakyForesterDialogue.kt b/Server/src/main/content/global/ame/events/freakyforester/FreakyForesterDialogue.kt new file mode 100644 index 000000000..c9ff3cc0a --- /dev/null +++ b/Server/src/main/content/global/ame/events/freakyforester/FreakyForesterDialogue.kt @@ -0,0 +1,27 @@ +package content.global.ame.events.freakyforester + +import core.api.* +import core.game.dialogue.DialogueFile +import core.game.dialogue.FacialExpression +import core.tools.END_DIALOGUE +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +class FreakyForesterDialogue : DialogueFile(){ + + override fun handle(componentID: Int, buttonID: Int) { + if (removeItem(player!!, Items.RAW_PHEASANT_6179) && !getAttribute(player!!, FreakUtils.freakComplete, false)) { + npcl(FacialExpression.NEUTRAL, "That's not the right one.").also { stage = END_DIALOGUE } + setAttribute(player!!, FreakUtils.pheasantKilled, false) + } else if (removeItem(player!!, Items.RAW_PHEASANT_6178) || getAttribute(player!!, FreakUtils.freakComplete, false)) { + npcl(FacialExpression.NEUTRAL, "Thanks, ${player!!.username}, you may leave the area now.").also { stage = END_DIALOGUE } + sendChat(findNPC(FreakUtils.freakNpc)!!, "Thanks, ${player!!.username}, you may leave the area now.") + setAttribute(player!!, FreakUtils.freakComplete, true) + } else when (getAttribute(player!!, FreakUtils.freakTask, -1)) { + NPCs.PHEASANT_2459 -> sendNPCDialogue(player!!, FreakUtils.freakNpc, "Hey there ${player!!.username}. Can you kill the one tailed pheasant please. Bring me the raw pheasant when you're done.").also { stage = END_DIALOGUE } + NPCs.PHEASANT_2460 -> sendNPCDialogue(player!!, FreakUtils.freakNpc, "Hey there ${player!!.username}. Can you kill the two tailed pheasant please. Bring me the raw pheasant when you're done.").also { stage = END_DIALOGUE } + NPCs.PHEASANT_2461 -> sendNPCDialogue(player!!, FreakUtils.freakNpc, "Hey there ${player!!.username}. Can you kill the three tailed pheasant please. Bring me the raw pheasant when you're done.").also { stage = END_DIALOGUE } + NPCs.PHEASANT_2462 -> sendNPCDialogue(player!!, FreakUtils.freakNpc, "Hey there ${player!!.username}. Can you kill the four tailed pheasant please. Bring me the raw pheasant when you're done.").also { stage = END_DIALOGUE } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/ame/events/freakyforester/FreakyForesterNPC.kt b/Server/src/main/content/global/ame/events/freakyforester/FreakyForesterNPC.kt new file mode 100644 index 000000000..4b0366263 --- /dev/null +++ b/Server/src/main/content/global/ame/events/freakyforester/FreakyForesterNPC.kt @@ -0,0 +1,43 @@ +package content.global.ame.events.freakyforester + +import content.global.ame.RandomEventNPC +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 + +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) + } + } + } + + override fun talkTo(npc: NPC) { + player.dialogueInterpreter.open(FreakyForesterDialogue(),npc) + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/ame/events/freakyforester/PheasantBehavior.kt b/Server/src/main/content/global/ame/events/freakyforester/PheasantBehavior.kt new file mode 100644 index 000000000..ad49212d2 --- /dev/null +++ b/Server/src/main/content/global/ame/events/freakyforester/PheasantBehavior.kt @@ -0,0 +1,37 @@ +package content.global.ame.events.freakyforester + +import core.api.getAttribute +import core.api.sendMessage +import core.api.sendNPCDialogue +import core.api.setAttribute +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 org.rs09.consts.Items +import org.rs09.consts.NPCs + +class PheasantBehavior : NPCBehavior(NPCs.PHEASANT_2459, NPCs.PHEASANT_2460, NPCs.PHEASANT_2461, NPCs.PHEASANT_2462) { + override fun onDropTableRolled(self: NPC, killer: Entity, drops: ArrayList) { + val assignedPheasant = getAttribute(killer, FreakUtils.freakTask, -1) + if (assignedPheasant == -1) return + drops.removeLast() + if (assignedPheasant == self.id) { + drops.add(Item (Items.RAW_PHEASANT_6178)) + } else { + drops.add(Item (Items.RAW_PHEASANT_6179)) + } + setAttribute(killer, FreakUtils.pheasantKilled, true) + } + + override fun beforeDamageReceived(self: NPC, attacker: Entity, state: BattleState) { + state.estimatedHit = 5 + state.secondaryHit = 0 + } + + override fun getXpMultiplier(self: NPC, attacker: Entity): Double { + return 0.0 + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/ame/events/genie/GenieDialogue.kt b/Server/src/main/content/global/ame/events/genie/GenieDialogue.kt index 829e0ecc4..7bcaa3c4d 100644 --- a/Server/src/main/content/global/ame/events/genie/GenieDialogue.kt +++ b/Server/src/main/content/global/ame/events/genie/GenieDialogue.kt @@ -3,14 +3,18 @@ package content.global.ame.events.genie import core.api.* import core.game.dialogue.FacialExpression import core.game.dialogue.DialogueFile -import core.tools.END_DIALOGUE +import core.game.system.timer.impl.AntiMacro +import org.rs09.consts.Items -class GenieDialogue : DialogueFile() { +class GenieDialogue() : DialogueFile() { override fun handle(componentID: Int, buttonID: Int) { - val assigned = player!!.getAttribute("genie:item",0) - npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Ah, so you are there, ${player!!.name.capitalize()}. I'm so glad you summoned me. Please take this lamp and make your wish.") - addItemOrDrop(player!!, assigned) - content.global.ame.RandomEventManager.getInstance(player!!)!!.event?.terminate() - stage = END_DIALOGUE + when (stage) { + 0 -> npcl(FacialExpression.NEUTRAL, "Ah, so you are there, ${player!!.username.capitalize()}. I'm so glad you summoned me. Please take this lamp and make your wish."). also { stage++ } + 1 -> { + end() + addItemOrDrop(player!!, Items.LAMP_2528) + AntiMacro.terminateEventNpc(player!!) + } + } } -} \ No newline at end of file +} 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 1f1f270e8..6f3cf39a1 100644 --- a/Server/src/main/content/global/ame/events/genie/GenieNPC.kt +++ b/Server/src/main/content/global/ame/events/genie/GenieNPC.kt @@ -2,35 +2,29 @@ package content.global.ame.events.genie import core.game.node.entity.npc.NPC import core.tools.RandomFunction -import org.rs09.consts.Items import org.rs09.consts.NPCs import content.global.ame.RandomEventNPC +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!") - var assigned_item = 0 - val items = arrayOf(Items.LAMP_2528) override fun tick() { if(RandomFunction.random(1,15) == 5){ - sendChat(phrases.random().replace("@name",player.name.capitalize())) + sendChat(phrases.random().replace("@name",player.username.capitalize())) } super.tick() } override fun init() { super.init() - assignItem() - sendChat(phrases.random().replace("@name",player.name.capitalize())) - } - - fun assignItem(){ - assigned_item = items.random() - player.setAttribute("genie:item",assigned_item) + playAudio(player, Sounds.GENIE_APPEAR_2301) + sendChat(phrases.random().replace("@name",player.username.capitalize())) } override fun talkTo(npc: NPC) { player.dialogueInterpreter.open(GenieDialogue(),npc) } -} \ No newline at end of file +} 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..108d456ca --- /dev/null +++ b/Server/src/main/content/global/ame/events/maze/MazeInterface.kt @@ -0,0 +1,277 @@ +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) + 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) + closeOverlay(player) + queueScript(player, 0, QueueStrength.SOFT) { stage: Int -> + when (stage) { + 0 -> { + lock(player, 6) + sendGraphics(Graphics(86, 0, 3), player.location) + animate(player,862) + return@queueScript delayScript(player, 6) + } + 1 -> { + lock(player, 6) + 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) + } + } + +} \ No newline at end of file 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..e4befe00e --- /dev/null +++ b/Server/src/main/content/global/ame/events/maze/MazeNPC.kt @@ -0,0 +1,57 @@ +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.interaction.QueueStrength +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.link.TeleportManager +import core.game.system.timer.impl.AntiMacro +import core.game.world.map.Location +import core.game.world.map.build.DynamicRegion +import core.game.world.update.flag.context.Graphics +import org.rs09.consts.NPCs +import org.rs09.consts.Sounds + +class MazeNPC(var type: String = "", override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.MYSTERIOUS_OLD_MAN_410) { + + override fun init() { + super.init() + sendChat("Aha, you'll do ${player.username}!") + face(player) + queueScript(player, 4, QueueStrength.SOFT) { stage: Int -> + when (stage) { + 0 -> { + lock(player, 6) + sendGraphics(Graphics(1576, 0, 0), player.location) + animate(player,8939) + playAudio(player, Sounds.TELEPORT_ALL_200) + return@queueScript delayScript(player, 3) + } + 1 -> { + MazeInterface.initMaze(player) + // Note: This event is NOT instanced: + // Sources: + // https://youtu.be/2gpzn9oNdy0 (2007) + // https://youtu.be/Tni1HURgnxg (2008) + // https://youtu.be/igdwDZOv9LU (2008) + // https://youtu.be/0oBCkLArUmc (2011 - even with personal Mysterious Old Man) - "Sorry, this is not the old man you are looking for." + // https://youtu.be/FMuKZm-Ikgs (2011) + // val region = DynamicRegion.create(11591) + kidnapPlayer(player, MazeInterface.STARTING_POINTS.random(), TeleportManager.TeleportType.INSTANT) // 10 random spots + AntiMacro.terminateEventNpc(player) + sendGraphics(Graphics(1577, 0, 0), player.location) + animate(player,8941) + removeAttribute(player, MazeInterface.MAZE_ATTRIBUTE_CHESTS_OPEN) + return@queueScript stopExecuting(player) + } + else -> return@queueScript stopExecuting(player) + } + } + } + + override fun talkTo(npc: NPC) { + // Do nothing. + } +} \ 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..f833d9ce2 --- /dev/null +++ b/Server/src/main/content/global/ame/events/pillory/PilloryNPC.kt @@ -0,0 +1,50 @@ +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.interaction.QueueStrength +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.link.TeleportManager +import core.game.system.timer.impl.AntiMacro +import core.game.world.map.Location +import core.game.world.update.flag.context.Graphics +import org.rs09.consts.NPCs +import org.rs09.consts.Sounds + +// "::revent [-p] player name [-e event name]" +class PilloryNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.PILLORY_GUARD_2791) { + + override fun init() { + super.init() + sendChat("${player.username}, you're under arrest!") + face(player) + player.dialogueInterpreter.sendPlainMessage(true, "", "Solve the pillory puzzle to be returned to where you came from.") + queueScript(player, 4, QueueStrength.SOFT) { stage: Int -> + when (stage) { + 0 -> { + lock(player, 6) + sendGraphics(Graphics(1576, 0, 0), player.location) + animate(player,8939) + playAudio(player, Sounds.TELEPORT_ALL_200) + return@queueScript delayScript(player, 3) + } + 1 -> { + PilloryInterface.initPillory(player) + val dest = PilloryInterface.LOCATIONS.random() //9 random spots! + kidnapPlayer(player, dest, TeleportManager.TeleportType.INSTANT) + AntiMacro.terminateEventNpc(player) + sendGraphics(Graphics(1577, 0, 0), player.location) + animate(player,8941) + return@queueScript stopExecuting(player) + } + else -> return@queueScript stopExecuting(player) + } + } + } + + override fun talkTo(npc: NPC) { + //player.dialogueInterpreter.open(FreakyForesterDialogue(),npc) + } +} \ 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..4790a8550 --- /dev/null +++ b/Server/src/main/content/global/ame/events/quizmaster/QuizMasterDialogueFile.kt @@ -0,0 +1,129 @@ +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.dialogue.DialogueFile +import core.game.dialogue.FacialExpression +import core.game.interaction.QueueStrength +import core.game.node.entity.player.Player +import core.tools.END_DIALOGUE +import org.rs09.consts.Components +import org.rs09.consts.Items + +class QuizMasterDialogueFile : DialogueFile() { + 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 handle(componentID: Int, buttonID: Int) { + when (stage) { + 0 -> 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").also { stage++ } + 1 -> player(FacialExpression.THINKING, "I'm sure I didn't ask to take part in a quiz show...").also { stage++ } + 2 -> npc(FacialExpression.FRIENDLY,"Please welcome our newest contestant:", "${player?.username}!", "Just pick the O D D O N E O U T.", "Four questions right, and then you win!").also { stage++ } + 3 -> { + setAttribute(player!!, QUIZMASTER_ATTRIBUTE_RANDOM_ANSWER, randomQuestion(player!!)) + player!!.interfaceManager.openChatbox(QUIZMASTER_INTERFACE) + stage++ + } + 4-> { + if (buttonID == getAttribute(player!!, QUIZMASTER_ATTRIBUTE_RANDOM_ANSWER, 0)) { + // Correct Answer + setAttribute(player!!, QUIZMASTER_ATTRIBUTE_QUESTIONS_CORRECT, getAttribute(player!!, QUIZMASTER_ATTRIBUTE_QUESTIONS_CORRECT, 0) + 1) + if (getAttribute(player!!, QUIZMASTER_ATTRIBUTE_QUESTIONS_CORRECT, 0) >= 4) { + npc(FacialExpression.FRIENDLY,"CONGRATULATIONS!", "You are a WINNER!", "Please choose your PRIZE!") + stage = 5 + } else { + npc(FacialExpression.FRIENDLY,"Wow, you're a smart one!", "You're absolutely RIGHT!", "Okay, next question!") + stage = 3 + } + } else { + // Wrong Answer + npc(FacialExpression.FRIENDLY,"WRONG!", "That's just WRONG!", "Okay, next question!") + stage = 3 + } + } + // Random Item should be "Mystery Box", but the current MYSTERY_BOX_6199 is already inauthentically used by Giftmas. + 5 -> options("1000 Coins", "Random Item").also { stage++ } + 6 -> { + resetAnimator(player!!) + returnPlayer(player!!) + when (buttonID) { + 1 -> { + queueScript(player!!, 0, QueueStrength.SOFT) { stage: Int -> + addItemOrDrop(player!!, Items.COINS_995, 1000) + return@queueScript stopExecuting(player!!) + } + } + 2 -> { + queueScript(player!!, 0, QueueStrength.SOFT) { stage: Int -> + addItemOrDrop(player!!, tableRoll.roll()[0].id) + return@queueScript stopExecuting(player!!) + } + } + } + removeAttributes(player!!, QUIZMASTER_ATTRIBUTE_QUESTIONS_CORRECT, QUIZMASTER_ATTRIBUTE_RANDOM_ANSWER) + stage = END_DIALOGUE + end() + } + } + } +} \ No newline at end of file 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..26407a1dc --- /dev/null +++ b/Server/src/main/content/global/ame/events/quizmaster/QuizMasterNPC.kt @@ -0,0 +1,65 @@ +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.interaction.QueueStrength +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.link.TeleportManager +import core.game.system.timer.impl.AntiMacro +import core.game.world.map.Location +import core.game.world.update.flag.context.Graphics +import org.rs09.consts.NPCs +import org.rs09.consts.Sounds + +/** + * Quiz Master NPC: + * + * 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!") + queueScript(player, 4, QueueStrength.SOFT) { stage: Int -> + when (stage) { + 0 -> { + lock(player, 6) + sendGraphics(Graphics(1576, 0, 0), player.location) + animate(player,8939) + playAudio(player, Sounds.TELEPORT_ALL_200) + return@queueScript delayScript(player, 3) + } + 1 -> { + kidnapPlayer(player, Location(1952, 4764, 1), TeleportManager.TeleportType.INSTANT) + setAttribute(player, QuizMasterDialogueFile.QUIZMASTER_ATTRIBUTE_QUESTIONS_CORRECT, 0) + AntiMacro.terminateEventNpc(player) + sendGraphics(Graphics(1577, 0, 0), player.location) + animate(player,8941) + sendMessage(player, "Answer four questions correctly in a row to be teleported back where you came from.") + sendMessage(player, "You will need to relog in if you lose the quiz dialog.") // Inauthentic, but there to notify the player in case. + return@queueScript delayScript(player, 6) + } + 2 -> { + face(player, Location(1952, 4768, 1)) + animate(player,2378) + // This is not needed as when you enter the QuizMasterBorders, it should fire off the dialogue + // openDialogue(player, QuizMasterDialogueFile(), this.asNpc()) + return@queueScript stopExecuting(player) + } + else -> return@queueScript stopExecuting(player) + } + + } + } + + 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/rickturpentine/RickTurpentineBehavior.kt b/Server/src/main/content/global/ame/events/rickturpentine/RickTurpentineBehavior.kt new file mode 100644 index 000000000..dbf6a8616 --- /dev/null +++ b/Server/src/main/content/global/ame/events/rickturpentine/RickTurpentineBehavior.kt @@ -0,0 +1,14 @@ +package content.global.ame.events.rickturpentine + +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.tools.RandomFunction +import org.rs09.consts.NPCs + +class RickTurpentineBehavior : NPCBehavior(NPCs.RICK_TURPENTINE_2476) { + override fun beforeAttackFinalized(self: NPC, victim: Entity, state: BattleState) { + state.estimatedHit = RandomFunction.getRandom(3) + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/ame/events/rickturpentine/RickTurpentineDialogue.kt b/Server/src/main/content/global/ame/events/rickturpentine/RickTurpentineDialogue.kt new file mode 100644 index 000000000..0b24b3ef7 --- /dev/null +++ b/Server/src/main/content/global/ame/events/rickturpentine/RickTurpentineDialogue.kt @@ -0,0 +1,19 @@ +package content.global.ame.events.rickturpentine + +import core.api.addItemOrDrop +import core.game.dialogue.DialogueFile +import core.game.dialogue.FacialExpression +import core.game.system.timer.impl.AntiMacro + +class RickTurpentineDialogue : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + 0 -> npcl(FacialExpression.NEUTRAL, "Today is your lucky day, " + (if (player!!.isMale) "sirrah!" else "madam!") + " I am donating to the victims of crime to atone for my past actions!").also { stage++ } + 1 -> { + AntiMacro.rollEventLoot(player!!).forEach { addItemOrDrop(player!!, it.id, it.amount) } + AntiMacro.terminateEventNpc(player!!) + end() + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/ame/events/rickturpentine/RickTurpentineNPC.kt b/Server/src/main/content/global/ame/events/rickturpentine/RickTurpentineNPC.kt new file mode 100644 index 000000000..ba731de03 --- /dev/null +++ b/Server/src/main/content/global/ame/events/rickturpentine/RickTurpentineNPC.kt @@ -0,0 +1,31 @@ +package content.global.ame.events.rickturpentine + +import content.global.ame.RandomEventNPC +import core.api.* +import core.api.utils.WeightBasedTable +import core.game.node.entity.npc.NPC +import org.rs09.consts.NPCs + +class RickTurpentineNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.RICK_TURPENTINE_2476) { + private var attackDelay = 0 + + override fun init() { + super.init() + sendChat("Good day to you, " + (if(player.isMale) "milord " else "milady ") + player.username.capitalize() + ".") + } + + override fun tick() { + if (ticksLeft <= 10) { + ticksLeft = 10 + if (attackDelay <= getWorldTicks()) + this.attack(player) + } + super.tick() + } + + override fun talkTo(npc: NPC) { + attackDelay = getWorldTicks() + 10 + this.pulseManager.clear() + openDialogue(player, RickTurpentineDialogue(), 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/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/SandwichLadyDialogue.kt b/Server/src/main/content/global/ame/events/sandwichlady/SandwichLadyDialogue.kt index 7cfc456ea..8374daf41 100644 --- a/Server/src/main/content/global/ame/events/sandwichlady/SandwichLadyDialogue.kt +++ b/Server/src/main/content/global/ame/events/sandwichlady/SandwichLadyDialogue.kt @@ -6,6 +6,7 @@ import core.game.node.entity.combat.ImpactHandler import core.game.node.item.GroundItemManager import core.game.node.item.Item import core.game.dialogue.DialogueFile +import core.game.system.timer.impl.AntiMacro import core.tools.END_DIALOGUE class SandwichLadyDialogue(val isChoice: Boolean) : DialogueFile() { @@ -26,13 +27,13 @@ class SandwichLadyDialogue(val isChoice: Boolean) : DialogueFile() { 0 -> if(choice != assigned){ npc!!.sendChat("That's not what I said you could have!") player!!.impactHandler.manualHit(npc,3,ImpactHandler.HitsplatType.NORMAL) - content.global.ame.RandomEventManager.getInstance(player!!)!!.event?.terminate() + AntiMacro.terminateEventNpc(player!!) } else { npc("Here you are, dear. I hope you enjoy it!") if(!player!!.inventory.add(Item(assigned))){ GroundItemManager.create(Item(assigned),player) } - content.global.ame.RandomEventManager.getInstance(player!!)!!.event?.terminate() + AntiMacro.terminateEventNpc(player!!) stage = END_DIALOGUE } } diff --git a/Server/src/main/content/global/ame/events/sandwichlady/SandwichLadyInterface.kt b/Server/src/main/content/global/ame/events/sandwichlady/SandwichLadyInterface.kt index 9b3c607df..c0d4773bd 100644 --- a/Server/src/main/content/global/ame/events/sandwichlady/SandwichLadyInterface.kt +++ b/Server/src/main/content/global/ame/events/sandwichlady/SandwichLadyInterface.kt @@ -3,6 +3,7 @@ package content.global.ame.events.sandwichlady import core.game.node.item.Item import org.rs09.consts.Items import core.game.interaction.InterfaceListener +import core.game.system.timer.impl.AntiMacro class SandwichLadyInterface : InterfaceListener { @@ -17,6 +18,11 @@ class SandwichLadyInterface : InterfaceListener { override fun defineInterfaceListeners() { on(SANDWICH_INTERFACE){player, _, _, buttonID, _, _ -> + val event = AntiMacro.getEventNpc(player) + if (event == null) { + player.interfaceManager.close() + return@on true + } val item = when(buttonID) { 7 -> {Item(baguette)} @@ -31,8 +37,8 @@ class SandwichLadyInterface : InterfaceListener { player.setAttribute("sandwich-lady:choice",item.id) player.interfaceManager.close() - player.dialogueInterpreter.open(SandwichLadyDialogue(true), content.global.ame.RandomEventManager.getInstance(player)!!.event) + player.dialogueInterpreter.open(SandwichLadyDialogue(true), event) return@on true } } -} \ No newline at end of file +} 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 0eef0f2f0..1c5a14956 100644 --- a/Server/src/main/content/global/ame/events/sandwichlady/SandwichLadyRENPC.kt +++ b/Server/src/main/content/global/ame/events/sandwichlady/SandwichLadyRENPC.kt @@ -8,13 +8,13 @@ import content.global.ame.RandomEventNPC 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?") + 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?") 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) override fun tick() { if(RandomFunction.random(1,15) == 5){ - sendChat(phrases.random().replace("@name",player.name.capitalize())) + sendChat(phrases.random().replace("@name",player.username.capitalize())) } super.tick() } @@ -22,7 +22,7 @@ class SandwichLadyRENPC(override var loot: WeightBasedTable? = null) : RandomEve override fun init() { super.init() assignItem() - sendChat(phrases.random().replace("@name",player.name.capitalize())) + sendChat(phrases.random().replace("@name",player.username.capitalize())) } fun assignItem(){ @@ -33,4 +33,4 @@ class SandwichLadyRENPC(override var loot: WeightBasedTable? = null) : RandomEve override fun talkTo(npc: NPC) { player.dialogueInterpreter.open(SandwichLadyDialogue(false),npc) } -} \ No newline at end of file +} 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 8fecda80c..60a0f57ae 100644 --- a/Server/src/main/content/global/ame/events/shade/ShadeRENPC.kt +++ b/Server/src/main/content/global/ame/events/shade/ShadeRENPC.kt @@ -5,15 +5,12 @@ import core.game.node.entity.npc.NPC import content.global.ame.RandomEventNPC import core.api.utils.WeightBasedTable - -val ids = 425..430 - 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 = (player.properties.combatLevel / 20) - 1 - val id = ids.toList()[index] + val id = idForCombatLevel(ids, player) this.transform(id) this.attack(player) sendChat("Leave this place!") @@ -29,4 +26,4 @@ class ShadeRENPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(4 super.tick() if(!player.viewport.currentPlane.npcs.contains(this)) this.clear() } -} \ No newline at end of file +} diff --git a/Server/src/main/content/global/ame/events/strangeplant/StrangePlantBehavior.kt b/Server/src/main/content/global/ame/events/strangeplant/StrangePlantBehavior.kt new file mode 100644 index 000000000..0491e01bc --- /dev/null +++ b/Server/src/main/content/global/ame/events/strangeplant/StrangePlantBehavior.kt @@ -0,0 +1,39 @@ +package content.global.ame.events.strangeplant + +import core.api.applyPoison +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.system.timer.impl.AntiMacro +import core.tools.RandomFunction +import org.rs09.consts.NPCs + +class StrangePlantBehavior() : NPCBehavior(NPCs.STRANGE_PLANT_408) { + override fun canBeAttackedBy(self: NPC, attacker: Entity, style: CombatStyle, shouldSendMessage: Boolean): Boolean { + return !(attacker !is Player || AntiMacro.getEventNpc(attacker.asPlayer()) != self) + } + + override fun beforeAttackFinalized(self: NPC, victim: Entity, state: BattleState) { + state.estimatedHit = RandomFunction.getRandom(3) + if (RandomFunction.roll(10)) + applyPoison(victim, self, 10) + } + + override fun beforeDamageReceived(self: NPC, attacker: Entity, state: BattleState) { + if (state.estimatedHit > 3) + state.estimatedHit = RandomFunction.getRandom(3) + if (state.secondaryHit > 3) + state.secondaryHit = RandomFunction.getRandom(3) + } + + override fun onDeathStarted(self: NPC, killer: Entity) { + AntiMacro.terminateEventNpc(killer.asPlayer()) + } + + 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/strangeplant/StrangePlantListener.kt b/Server/src/main/content/global/ame/events/strangeplant/StrangePlantListener.kt new file mode 100644 index 000000000..be182fd47 --- /dev/null +++ b/Server/src/main/content/global/ame/events/strangeplant/StrangePlantListener.kt @@ -0,0 +1,33 @@ +package content.global.ame.events.strangeplant + +import core.api.addItemOrDrop +import core.api.getAttribute +import core.api.sendMessage +import core.api.setAttribute +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.system.timer.impl.AntiMacro +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +class StrangePlantListener : InteractionListener { + override fun defineListeners() { + on(NPCs.STRANGE_PLANT_407, IntType.NPC, "pick") { player, node -> + if (AntiMacro.getEventNpc(player) != node) { + sendMessage(player, "This isn't your strange plant.") + return@on true + } + + if (!getAttribute(node.asNpc(), "fruit-grown", false)) { + sendMessage(player, "The fruit isn't ready to be picked.") + return@on true + } + + if (!getAttribute(node.asNpc(), "fruit-picked", false)) { + setAttribute(node.asNpc(), "fruit-picked", true) + addItemOrDrop(player, Items.STRANGE_FRUIT_464) + } + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/ame/events/strangeplant/StrangePlantNPC.kt b/Server/src/main/content/global/ame/events/strangeplant/StrangePlantNPC.kt new file mode 100644 index 000000000..a1eca2fd8 --- /dev/null +++ b/Server/src/main/content/global/ame/events/strangeplant/StrangePlantNPC.kt @@ -0,0 +1,85 @@ +package content.global.ame.events.strangeplant + +import content.global.ame.RandomEventNPC +import core.api.* +import core.api.utils.WeightBasedTable +import core.game.interaction.QueueStrength +import core.game.node.entity.npc.NPC +import core.game.world.update.flag.context.Animation +import core.tools.minutesToTicks +import core.tools.secondsToTicks +import org.rs09.consts.NPCs + +class StrangePlantNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.STRANGE_PLANT_407) { + private val strangePlantGrowAnim = Animation(348) + private val strangePlantTransformAnim = Animation(351) + private val strangePlantPickedAnim = Animation(350) + private var fruitPicked = false + private var transformed = false + private var attacking = false + + override fun init() { + spawnLocation = getPathableCardinal(player, player.location) + super.init() + ticksLeft = minutesToTicks(1) + animate(this, strangePlantGrowAnim) + queueScript(this, strangePlantGrowAnim.duration + 2, QueueStrength.SOFT) { + setAttribute(this, "fruit-grown", true) + return@queueScript stopExecuting(this) + } + } + + override fun tick() { + super.tick() + if (getAttribute(this, "fruit-picked", false) && !fruitPicked) { + fruitPicked = true + ticksLeft = secondsToTicks(60) + queueScript(this, 1, QueueStrength.SOFT) { stage: Int -> + when (stage) { + 0 -> { + this.animate(strangePlantPickedAnim) + return@queueScript delayScript(this, strangePlantPickedAnim.duration - 1) + } + 1 -> { + this.isInvisible = true + terminate() + return@queueScript stopExecuting(this) + } + else -> return@queueScript stopExecuting(this) + } + } + } + if (ticksLeft <= secondsToTicks(10)) { + ticksLeft = secondsToTicks(10) + if (!attacking) { + attacking = true + queueScript(this, 1, QueueStrength.SOFT) { stage: Int -> + when (stage) { + 0 -> { + animate(this, strangePlantTransformAnim) + return@queueScript delayScript(this, strangePlantTransformAnim.duration) + } + 1 -> { + this.transform(NPCs.STRANGE_PLANT_408) + this.behavior = StrangePlantBehavior() + this.attack(player) + transformed = true + return@queueScript stopExecuting(this) + } + else -> return@queueScript stopExecuting(this) + } + } + } + if (transformed && !this.inCombat()) + this.attack(player) + } + } + + override fun follow() { + if (transformed) + super.follow() + } + + override fun talkTo(npc: NPC) { + } +} \ No newline at end of file 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 94% 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 652055335..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 @@ -60,6 +60,6 @@ class MordautDialogue(val examComplete: Boolean, val questionCorrect: Boolean = } override fun npc(vararg messages: String?): Component? { - return super.npc(core.game.dialogue.FacialExpression.OLD_NORMAL,*messages) + return super.npc(FacialExpression.OLD_NORMAL,*messages) } } \ 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/surpriseexam/MysteriousOldManDialogue.kt similarity index 79% rename from Server/src/main/content/global/ame/events/MysteriousOldManDialogue.kt rename to Server/src/main/content/global/ame/events/surpriseexam/MysteriousOldManDialogue.kt index 5366ee17b..1415317a6 100644 --- a/Server/src/main/content/global/ame/events/MysteriousOldManDialogue.kt +++ b/Server/src/main/content/global/ame/events/surpriseexam/MysteriousOldManDialogue.kt @@ -1,8 +1,9 @@ package content.global.ame.events import core.game.node.entity.player.Player -import content.global.ame.events.supriseexam.SurpriseExamUtils +import content.global.ame.events.surpriseexam.SurpriseExamUtils import core.game.dialogue.DialogueFile +import core.game.system.timer.impl.AntiMacro class MysteriousOldManDialogue(val type: String) : DialogueFile() { @@ -22,11 +23,11 @@ class MysteriousOldManDialogue(val type: String) : DialogueFile() { 1 -> { end() teleport(player!!,type) - content.global.ame.RandomEventManager.getInstance(player!!)?.event?.terminate() + AntiMacro.terminateEventNpc(player!!) } 2 -> { end() - content.global.ame.RandomEventManager.getInstance(player!!)?.event?.terminate() + AntiMacro.terminateEventNpc(player!!) } } } diff --git a/Server/src/main/content/global/ame/events/MysteriousOldManNPC.kt b/Server/src/main/content/global/ame/events/surpriseexam/MysteriousOldManNPC.kt similarity index 100% rename from Server/src/main/content/global/ame/events/MysteriousOldManNPC.kt rename to Server/src/main/content/global/ame/events/surpriseexam/MysteriousOldManNPC.kt 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 59% 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..6d8ad6b4f 100644 --- a/Server/src/main/content/global/ame/events/supriseexam/SupriseExamListeners.kt +++ b/Server/src/main/content/global/ame/events/surpriseexam/SupriseExamListeners.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.node.entity.player.Player @@ -9,20 +9,22 @@ 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 +41,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 +56,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.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 71% 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 eabb3d60c..01a722343 100644 --- a/Server/src/main/content/global/ame/events/supriseexam/SurpriseExamUtils.kt +++ b/Server/src/main/content/global/ame/events/surpriseexam/SurpriseExamUtils.kt @@ -1,20 +1,18 @@ -package content.global.ame.events.supriseexam +package content.global.ame.events.surpriseexam +import content.global.ame.kidnapPlayer +import content.global.ame.returnPlayer import core.api.* import core.game.node.entity.impl.PulseType import core.game.node.entity.player.Player -import core.game.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.game.node.entity.player.link.TeleportManager 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) @@ -28,28 +26,16 @@ 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 -> - teleport(p, p.getAttribute(SE_KEY_LOC, p.location)) - } - - player.properties.teleportLocation = Location.create(1886, 5025, 0) + fun teleport(player: Player) { + kidnapPlayer(player, Location.create(1886, 5025, 0), TeleportManager.TeleportType.INSTANT) } 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) @@ -81,4 +67,4 @@ object SurpriseExamUtils { fun pickRandomDoor(player: Player){ player.setAttribute(SE_DOOR_KEY, SE_DOORS.random()) } -} \ No newline at end of file +} diff --git a/Server/src/main/content/global/ame/events/swarm/SwarmNPC.kt b/Server/src/main/content/global/ame/events/swarm/SwarmNPC.kt new file mode 100644 index 000000000..30b29815f --- /dev/null +++ b/Server/src/main/content/global/ame/events/swarm/SwarmNPC.kt @@ -0,0 +1,28 @@ +package content.global.ame.events.swarm + +import content.global.ame.RandomEventNPC +import core.api.playGlobalAudio +import core.api.utils.WeightBasedTable +import core.game.node.entity.npc.NPC +import core.tools.minutesToTicks +import org.rs09.consts.NPCs +import org.rs09.consts.Sounds + +class SwarmNPC(override var loot: WeightBasedTable? = null) : RandomEventNPC(NPCs.SWARM_411) { + override fun init() { + super.init() + this.setAttribute("no-spawn-return", true) + this.attack(player) + playGlobalAudio(this.location, Sounds.SWARM_APPEAR_818) + this.ticksLeft = minutesToTicks(60) + } + + override fun tick() { + super.tick() + if (!this.inCombat()) + this.attack(player) + } + + override fun talkTo(npc: NPC) { + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/ame/events/swarm/SwarmNPCBehavior.kt b/Server/src/main/content/global/ame/events/swarm/SwarmNPCBehavior.kt new file mode 100644 index 000000000..3cf390747 --- /dev/null +++ b/Server/src/main/content/global/ame/events/swarm/SwarmNPCBehavior.kt @@ -0,0 +1,14 @@ +package content.global.ame.events.swarm + +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.tools.RandomFunction +import org.rs09.consts.NPCs + +class SwarmNPCBehavior : NPCBehavior(NPCs.SWARM_411) { + override fun beforeAttackFinalized(self: NPC, victim: Entity, state: BattleState) { + state.estimatedHit = RandomFunction.getRandom(1) + } +} \ No newline at end of file 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 1fed431b9..6b43d54b4 100644 --- a/Server/src/main/content/global/ame/events/zombie/ZombieRENPC.kt +++ b/Server/src/main/content/global/ame/events/zombie/ZombieRENPC.kt @@ -5,15 +5,12 @@ import core.game.node.entity.npc.NPC import content.global.ame.RandomEventNPC import core.api.utils.WeightBasedTable - -val ids = 419..424 - 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 = (player.properties.combatLevel / 20) - 1 - val id = ids.toList()[index] + val id = idForCombatLevel(ids, player) this.transform(id) this.attack(player) sendChat("Brainsssss!") @@ -29,4 +26,4 @@ class ZombieRENPC(override var loot: WeightBasedTable? = null) : RandomEventNPC( super.tick() if(!player.viewport.currentPlane.npcs.contains(this)) this.clear() } -} \ No newline at end of file +} diff --git a/Server/src/main/content/global/bots/Adventurer.kt b/Server/src/main/content/global/bots/Adventurer.kt index d47e23145..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" } - private var state = State.START + 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 -> @@ -582,7 +677,7 @@ class Adventurer(val style: CombatStyle): Script() { // At the Crumbling Wall in Falador ZoneBorders(2937,3356,2936,3353) to { it: Adventurer -> // Interact with the Crumbling Wall - val wall = it.scriptAPI.getNearestNode("Crumbling wall", true); + val wall = it.scriptAPI.getNearestNode("Crumbling wall", true) if (wall == null) { it.refresh() it.ticks = 0 diff --git a/Server/src/main/content/global/bots/CannonballSmelter.kt b/Server/src/main/content/global/bots/CannonballSmelter.kt new file mode 100644 index 000000000..8ac77f7e1 --- /dev/null +++ b/Server/src/main/content/global/bots/CannonballSmelter.kt @@ -0,0 +1,277 @@ +package content.global.bots + +import content.global.skill.gather.mining.MiningNode +import content.global.skill.smithing.smelting.Bar +import content.global.skill.smithing.smelting.SmeltingPulse +import core.api.* +import core.game.bots.* +import core.game.ge.GrandExchange +import core.game.interaction.DestinationFlag +import core.game.interaction.MovementPulse +import core.game.node.Node +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 +import content.data.Quests + +@PlayerCompatible +@ScriptName("Falador Cannonball Smelter") +@ScriptDescription("Start in Falador East Bank with a pick equipped","or in your inventory.") +@ScriptIdentifier("fally_cballs") +class CannonballSmelter : Script() { + var state = State.INIT + val bottomLadder = ZoneBorders(3016,9736,3024,9742) + val topLadder = ZoneBorders(3016,3336,3022,3342) + val coalMine = ZoneBorders(3027,9733,3054,9743) + //val coalMine = ZoneBorders(3056, 9729, 3018, 9758) + val ironMine = ZoneBorders(3052,9768,3035,9777) + val northMineEntrance = ZoneBorders(3062, 3375, 3058, 3381) + val bank = ZoneBorders(3009,3355,3018,3358) + var overlay: ScriptAPI.BottingOverlay? = null + var coalAmount = 0 + + override fun tick() { + when(state){ + + State.INIT -> { + overlay = scriptAPI.getOverlay() + overlay!!.init() + overlay!!.setTitle("Mining") + overlay!!.setTaskLabel("Coal Mined:") + overlay!!.setAmount(0) + + if (coalMine.insideBorder(bot)){ + state = State.MINING + } else { + state = State.TO_BANK + } + } + + State.MINING -> { + bot.interfaceManager.close() + if(bot.inventory.freeSlots() == 0){ + state = State.TO_BANK + } else if(amountInInventory(bot, Items.COAL_453) >= 18) { + state = State.TO_IRONMINE + } else if(!coalMine.insideBorder(bot)){ + scriptAPI.walkTo(coalMine.randomLoc) + } else { + val rock = scriptAPI.getNearestObjectByPredicate({node -> node?.name?.equals("rocks", true)!! && MiningNode.forId(node?.id!!).reward == Items.COAL_453 }) + if(rock != null) { + scriptAPI.interact(bot, rock, "mine") + } else { + scriptAPI.walkTo(coalMine.randomLoc) + } + } + overlay!!.setAmount(amountInInventory(bot, Items.COAL_453)) + } + State.MINING_IRON -> { + bot.interfaceManager.close() + if(bot.inventory.freeSlots() == 0 || amountInInventory(bot, Items.IRON_ORE_440) >= 9) { + state = State.TO_BANK + } else if(!ironMine.insideBorder(bot)){ + var loc = ironMine.randomLoc + scriptAPI.walkTo(loc) + } else { + val rock = scriptAPI.getNearestObjectByPredicate({node -> node?.name?.equals("rocks", true)!! && MiningNode.forId(node?.id!!).reward == Items.IRON_ORE_440 }) + //rock?.let { InteractionListeners.run(rock.id, IntType.SCENERY,"mine",bot,rock) } + if(rock != null) { + scriptAPI.interact(bot, rock, "mine") + } else { + scriptAPI.walkTo(ironMine.randomLoc) + } + } + overlay!!.setAmount(amountInInventory(bot, Items.IRON_ORE_440)) + } + + State.TO_BANK -> { + if(bank.insideBorder(bot)){ + val bank = scriptAPI.getNearestNode("bank booth",true) + if(bank != null) { + state = State.BANKING + bot.pulseManager.run(object : BankingPulse(this, bank){ + override fun pulse(): Boolean { + return super.pulse() + } + }) + } + } else { + if(bot.location.y > 3400) { + if(bot.location.y < 9757) { + val ladder = scriptAPI.getNearestNode(30941, true) + //ladder ?: scriptAPI.walkTo(bottomLadder.randomLoc).also { return } + //ladder?.interaction?.handle(bot, ladder.interaction[0]).also { ladderSwitch = true } + scriptAPI.interact(bot, ladder, "climb-up") + } else { + val stairs = scriptAPI.getNearestNode(30943, true) + scriptAPI.interact(bot, stairs, "climb-up") + + } + } else { + if(northMineEntrance.insideBorder(bot)) { + val door = scriptAPI.getNearestNode(11714, true) + if(door != null) { + scriptAPI.interact(bot, door, "open") + } else { + scriptAPI.walkTo(bank.randomLoc) + } + } else { + scriptAPI.walkTo(bank.randomLoc) + } + } + } + } + + State.BANKING -> { + scriptAPI.bankAll({ + if(amountInBank(bot, Items.CANNONBALL_2) >= 500) { + val total = GrandExchange.getBotstockForId(Items.CANNONBALL_2) + bot.interfaceManager.close() + if(total < 5000) { + state = State.TO_GE + } + } + if(state != State.TO_GE) { + if(amountInBank(bot, Items.IRON_ORE_440) >= 9 && amountInBank(bot, Items.COAL_453) >= 18) { + scriptAPI.withdraw(Items.IRON_ORE_440, 9) + scriptAPI.withdraw(Items.COAL_453, 18) + scriptAPI.withdraw(Items.AMMO_MOULD_4, 1) + state = State.TO_FURNACE + bot.interfaceManager.close() + } else { + state = State.TO_MINE + } + } + }) + } + + State.TO_FURNACE -> { + if(bot.location.x > 2978) { + scriptAPI.walkTo(Location.create(2974, 3369, 0)) + } else if(amountInInventory(bot, Items.STEEL_BAR_2353) < 9) { + val furnace = scriptAPI.getNearestNode(11666, true) + scriptAPI.interact(bot, furnace, "smelt") + // TODO: should bots use real interfaces? + bot.pulseManager.run(SmeltingPulse(bot, null, Bar.STEEL, 9)) + } else { + state = State.SMELTING_CBALLS + } + } + + State.SMELTING_CBALLS -> { + if(amountInInventory(bot, Items.STEEL_BAR_2353) > 0) { + val furnace = scriptAPI.getNearestNode(11666, true) + scriptAPI.useWith(bot, Items.STEEL_BAR_2353, furnace) + if(bot.dialogueInterpreter.dialogue != null) { + bot.dialogueInterpreter.handle(309, 32) + } + } else { + state = State.TO_BANK + } + } + + State.TO_MINE -> { + if(bot.location.y < 3400) { + bot.interfaceManager.close() + if(!topLadder.insideBorder(bot.location)){ + scriptAPI.walkTo(topLadder.randomLoc) + } else { + val ladder = scriptAPI.getNearestNode("Ladder",true) + if(ladder != null){ + ladder.interaction.handle(bot,ladder.interaction[0]) + } else { + scriptAPI.walkTo(topLadder.randomLoc) + } + } + } else { + if(!coalMine.insideBorder(bot)){ + scriptAPI.walkTo(coalMine.randomLoc) + } else { + state = State.MINING + } + } + } + + State.TO_IRONMINE -> { + if(ironMine.insideBorder(bot.location)) { + state = State.MINING_IRON + } else if(bot.location.y < 9757) { + if (bot.location.regionId == ((47 shl 8) or 152)) { + val door = scriptAPI.getNearestNode(2112,true) + if(door != null) { + scriptAPI.interact(bot, door, "open") + } else { + scriptAPI.walkTo(Location.create(3046, 9756, 0)) + } + } else { + scriptAPI.walkTo(Location.create(3046, 9756, 0)) + } + } else { + scriptAPI.walkTo(ironMine.randomLoc) + } + } + + State.TO_GE -> { + scriptAPI.teleportToGE() + state = State.SELLING + } + + State.SELLING -> { + scriptAPI.sellOnGE(Items.CANNONBALL_2) + state = State.GO_BACK + } + + State.GO_BACK -> { + scriptAPI.teleport(bank.randomLoc) + state = State.TO_MINE + } + } + } + + open class BankingPulse(val script: Script, val bank: Node) : MovementPulse(script.bot,bank, DestinationFlag.OBJECT){ + override fun pulse(): Boolean { + script.bot.faceLocation(bank.location) + return true + } + } + + override fun newInstance(): Script { + val script = CannonballSmelter() + script.bot = SkillingBotAssembler().produce(SkillingBotAssembler.Wealth.POOR,bot.startLocation) + return script + } + + enum class State { + MINING, + TO_MINE, + TO_BANK, + TO_FURNACE, + SMELTING_CBALLS, + BANKING, + TO_GE, + SELLING, + GO_BACK, + TO_IRONMINE, + MINING_IRON, + INIT + } + + init { + equipment.add(Item(Items.RUNE_PICKAXE_1275)) + inventory.add(Item(Items.AMMO_MOULD_4)) + if(false) { + // spawn initial iron/coal to debug smelting + inventory.add(Item(Items.COAL_453, 18)) + inventory.add(Item(Items.IRON_ORE_440, 9)) + } + skills.put(Skills.ATTACK,40) + skills.put(Skills.STRENGTH,60) + skills.put(Skills.MINING,75) + skills.put(Skills.HITPOINTS,99) + skills.put(Skills.DEFENCE,99) + skills.put(Skills.SMITHING,35) + quests.add(Quests.DWARF_CANNON) + } +} diff --git a/Server/src/main/content/global/bots/ChickenKiller.kt b/Server/src/main/content/global/bots/ChickenKiller.kt index b74c18ab7..ee817573f 100644 --- a/Server/src/main/content/global/bots/ChickenKiller.kt +++ b/Server/src/main/content/global/bots/ChickenKiller.kt @@ -23,6 +23,7 @@ class ChickenKiller : Script(){ override fun tick() { when(state){ + State.CONFIG -> {} State.INIT -> { overlay = scriptAPI.getOverlay() overlay!!.init() @@ -103,8 +104,6 @@ class ChickenKiller : Script(){ IDLE, INIT, KILLING, - LOOTING, - RETURN, CONFIG, LOOTFEATHER, LOOTBONES, diff --git a/Server/src/main/content/global/bots/CosmicCrafter.kt b/Server/src/main/content/global/bots/CosmicCrafter.kt new file mode 100644 index 000000000..911dc2b5d --- /dev/null +++ b/Server/src/main/content/global/bots/CosmicCrafter.kt @@ -0,0 +1,181 @@ +package content.global.bots + +import content.global.skill.runecrafting.MysteriousRuin +import core.cache.def.impl.SceneryDefinition +import core.game.bots.* +import core.game.node.item.Item +import core.game.node.scenery.Scenery +import core.game.world.map.Location +import core.game.world.map.RegionManager +import core.game.world.map.zone.ZoneBorders +import core.net.packet.PacketProcessor +import org.rs09.consts.Items + + +@PlayerCompatible +@ScriptName("Cosmic Rune Crafter") +@ScriptDescription("Crafts cosmic runes. Start in Zanaris w/ cosmic tiara.") +@ScriptIdentifier("cosmic_crafter") +class CosmicCrafter : Script() { + var state = State.INIT + var runeCounter = 0 + var overlay: ScriptAPI.BottingOverlay? = null + var startLocation = Location(0,0,0) + var timer = 0 + var bank_deposit = true + var bank = Location(2384, 4457) + var squeezeZone = ZoneBorders(2413, 4400, 2416, 4406) + var endZone = ZoneBorders(2405, 4392, 2410, 4397) + var agility_part = 0 + + + var ruinsZone = ZoneBorders(2400, 4370, 2410, 4385) + var cosmicZone = ZoneBorders(2160, 4830, 2170, 4840) + + var ruinPoint = Location(2407, 4379, 0) + var agility2ExitPoint = Location(2408, 4394, 0) + var agility2EntryPoint = Location(2408, 4396, 0) + var agility1ExitPoint = Location(2415, 4401, 0) + var agility1EntryPoint = Location(2415, 4403, 0) + + override fun tick() { + if (timer-- > 0) { + return + } + if (bot.settings.runEnergy > 10.0) { + bot.settings.isRunToggled = true + } + + when(state){ + State.INIT -> { + if (!bot.equipment.containsAtLeastOneItem(Items.COSMIC_TIARA_5539)) { + bot.sendMessage("Please equip a cosmic tiara first.") + state = State.INVALID + } else { + overlay = scriptAPI.getOverlay() + overlay!!.init() + overlay!!.setTitle("Cosmic Runes") + overlay!!.setTaskLabel("Runes Crafted:") + overlay!!.setAmount(0) + startLocation = bot.location + state = State.BANKING + } + } + + State.BANKING -> { + if(bot.location != bank) { + scriptAPI.walkTo(bank) + return + } + val runes = bot.inventory.getAmount(Item(Items.COSMIC_RUNE_564)) + if (runes > 0) { + runeCounter += runes + overlay!!.setAmount(runeCounter) + bot.sendMessage("You have crafted a total of: $runeCounter runes.") + scriptAPI.bankItem(Items.COSMIC_RUNE_564) + } else { + scriptAPI.withdraw(Items.PURE_ESSENCE_7936, 28) + state = State.FIRST_AGILITY + } + } + + State.FIRST_AGILITY -> { + val squeezeWall = scriptAPI.getNearestNode(12127, true) + if (agility_part == 0) { + if (!squeezeZone.insideBorder(bot)) + scriptAPI.walkTo(agility1EntryPoint) + else { + scriptAPI.interact(bot, squeezeWall, "squeeze-past") + agility_part = 1 + timer = 6 + } + } else if (agility_part == 1) { + if (!endZone.insideBorder(bot)) + scriptAPI.walkTo(agility2EntryPoint) + else { + scriptAPI.interact(bot, squeezeWall, "squeeze-past") + state = State.RUNNING_TO_ALTER + timer = 6 + } + } + } + + State.RUNNING_TO_ALTER -> { + if (cosmicZone.insideBorder(bot)) + state = State.CRAFTING + + val ruins = scriptAPI.getNearestNode(2458,true) + if (!ruinsZone.insideBorder(bot)) { + scriptAPI.walkTo(ruinPoint) + } else if (ruins != null && ruins.location.withinDistance(bot.location, 20)) { + if (!bot.equipment.containsAtLeastOneItem(Items.COSMIC_TIARA_5539)) { + bot.sendMessage("Please equip a cosmic tiara first.") + state = State.INVALID + } else { + val ruinsChild = (ruins as Scenery).getChild(bot) + scriptAPI.interact(bot, ruinsChild, "enter") + timer = 4 + } + } + } + + State.CRAFTING -> { + val alter = scriptAPI.getNearestNode(2484,true) + scriptAPI.interact(bot, alter, "craft-rune") + if(bot.inventory.containsAtLeastOneItem(Item(Items.COSMIC_RUNE_564))) + state = State.LEAVING_ALTER + } + + State.LEAVING_ALTER -> { + var portalOut = scriptAPI.getNearestNode(2471, true) + scriptAPI.interact(bot, portalOut, "use") + if (ruinsZone.insideBorder(bot)) { + state = State.RETURN_AGILITY + timer = 2 + } + } + + State.RETURN_AGILITY -> { + val squeezeWall = scriptAPI.getNearestNode(12127, true) + if (agility_part == 1) { + if (!endZone.insideBorder(bot)) + scriptAPI.walkTo(agility2ExitPoint) + else { + scriptAPI.interact(bot, squeezeWall, "squeeze-past") + agility_part = 0 + timer = 6 + } + } else if (agility_part == 0) { + if (!squeezeZone.insideBorder(bot)) + scriptAPI.walkTo(agility1ExitPoint) + else { + scriptAPI.interact(bot, squeezeWall, "squeeze-past") + state = State.BANKING + timer = 6 + } + } + } + + State.INVALID -> { + timer = 25 + state = State.INIT + } + } + } + + override fun newInstance(): Script { + return this + } + + enum class State { + INIT, + BANKING, + FIRST_AGILITY, + RETURN_AGILITY, + RUNNING_TO_ALTER, + CRAFTING, + LEAVING_ALTER, + INVALID + } + +} diff --git a/Server/src/main/content/global/bots/DoublingMoney.kt b/Server/src/main/content/global/bots/DoublingMoney.kt index c230879ea..785fd8690 100644 --- a/Server/src/main/content/global/bots/DoublingMoney.kt +++ b/Server/src/main/content/global/bots/DoublingMoney.kt @@ -9,7 +9,7 @@ import core.game.node.item.Item import core.game.world.map.Location import core.game.world.map.RegionManager import core.game.world.update.flag.context.ChatMessage -import core.game.world.update.flag.player.ChatFlag +import core.game.world.update.flag.* import core.integrations.discord.Discord import org.json.simple.JSONArray import org.json.simple.JSONObject @@ -132,14 +132,14 @@ class DoublingMoney : Script() { val message = arrayOf("Doubling Money", "Doubling Money", "Doubling Money!", "Doubling moneyy").random() val messageEffect = arrayOf(0, 256).random() val ctx = ChatMessage(bot, message, messageEffect, message.length) - bot.updateMasks.register(ChatFlag(ctx)) + bot.updateMasks.register(EntityFlag.Chat, ctx) sleepTime = 8 } Effort.VERY_HIGH -> { val message = arrayOf("Doubling money!", "Doubling money").random() val messageEffect = arrayOf(771, 2818, 2562, 768, 512, 2304, 2560, 769, 1792).random() val ctx = ChatMessage(bot, message, messageEffect, message.length) - bot.updateMasks.register(ChatFlag(ctx)) + bot.updateMasks.register(EntityFlag.Chat, ctx) sleepTime = 9 } } @@ -198,7 +198,7 @@ class DoublingMoney : Script() { if (botTradeModule.getInterface() == TradeModule.ACCEPT_INTERFACE && coinsFromBot > 0 && effort == Effort.VERY_HIGH) { val message = "Payed ${(if (coinsFromBot < 1000) "${coinsFromBot}gp" else "${coinsFromBot / 1000}k")}" val ctx = ChatMessage(bot, message, 512, message.length) - bot.updateMasks.register(ChatFlag(ctx)) + bot.updateMasks.register(EntityFlag.Chat, ctx) sleepTime = 7 } @@ -325,7 +325,7 @@ class DoublingMoney : Script() { if (effort == Effort.VERY_HIGH) { val message = "Received ${(if (coins.amount < 1000) "${coins.amount}gp" else "${coins.amount / 1000}k")}" val ctx = ChatMessage(bot, message, 256, message.length) - bot.updateMasks.register(ChatFlag(ctx)) + bot.updateMasks.register(EntityFlag.Chat, ctx) } } 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/GenericSlayerBot.kt b/Server/src/main/content/global/bots/GenericSlayerBot.kt index 2ee9d2b1d..5b4a7e31e 100644 --- a/Server/src/main/content/global/bots/GenericSlayerBot.kt +++ b/Server/src/main/content/global/bots/GenericSlayerBot.kt @@ -64,7 +64,7 @@ class GenericSlayerBot : Script() { } } } - + else -> {} } } @@ -144,7 +144,7 @@ class GenericSlayerBot : Script() { bot.inventory.clear() for(item in inventory) bot.inventory.add(item) - scriptAPI.withdraw(org.rs09.consts.Items.LOBSTER_379,10) + scriptAPI.withdraw(Items.LOBSTER_379,10) bot.fullRestore() if(assignment.amount <= 0){ @@ -220,7 +220,6 @@ class GenericSlayerBot : Script() { GETTING_TASK, GOING_TO_HUB, KILLING_ENEMY, - LOOTING, GOING_TO_BANK, BANKING, GOING_TO_GE, diff --git a/Server/src/main/content/global/bots/GlassBlowingBankstander.kt b/Server/src/main/content/global/bots/GlassBlowingBankstander.kt index ac1635748..2e9e636c2 100644 --- a/Server/src/main/content/global/bots/GlassBlowingBankstander.kt +++ b/Server/src/main/content/global/bots/GlassBlowingBankstander.kt @@ -1,12 +1,12 @@ package content.global.bots -import content.global.handlers.iface.GlassInterface +import content.global.skill.crafting.glass.GlassCraftingPulse +import content.global.skill.crafting.glass.GlassProduct +import core.game.bots.Script +import core.game.bots.SkillingBotAssembler import core.game.node.entity.skill.Skills -import content.global.skill.crafting.GlassProduct import core.game.node.item.Item import org.rs09.consts.Items -import core.game.bots.SkillingBotAssembler -import core.game.bots.Script class GlassBlowingBankstander : Script(){ var state = State.BLOWING @@ -17,7 +17,7 @@ class GlassBlowingBankstander : Script(){ State.BLOWING -> { bot.inventory.add(Item(Items.GLASSBLOWING_PIPE_1785)) bot.inventory.add(Item(Items.MOLTEN_GLASS_1775,27)) - GlassInterface.make(bot, GlassProduct.ORB,27) + bot.pulseManager.run(GlassCraftingPulse(bot, GlassProduct.ORB, 27)) State.BANKING } diff --git a/Server/src/main/content/global/bots/GnomeBowstring.kt b/Server/src/main/content/global/bots/GnomeBowstring.kt index ae1d99555..a835aa37f 100644 --- a/Server/src/main/content/global/bots/GnomeBowstring.kt +++ b/Server/src/main/content/global/bots/GnomeBowstring.kt @@ -155,6 +155,8 @@ class GnomeBowstring : Script() { state = State.PICKING } } + + else -> {} } } override fun newInstance(): Script { diff --git a/Server/src/main/content/global/bots/GreenDragonKiller.kt b/Server/src/main/content/global/bots/GreenDragonKiller.kt index 6c8dcd82e..740fb7cd0 100644 --- a/Server/src/main/content/global/bots/GreenDragonKiller.kt +++ b/Server/src/main/content/global/bots/GreenDragonKiller.kt @@ -1,5 +1,7 @@ package content.global.bots +import content.global.skill.prayer.BoneBuryListener +import core.api.submitIndividualPulse import core.game.interaction.DestinationFlag import core.game.interaction.MovementPulse import core.game.node.entity.Entity @@ -7,7 +9,6 @@ import core.game.node.entity.combat.CombatStyle import core.game.node.entity.combat.InteractionType import core.game.node.entity.player.Player import core.game.node.entity.skill.Skills -import content.global.skill.prayer.BoneBuryingOptionPlugin import core.game.node.item.Item import core.game.system.task.Pulse import core.game.world.map.Location @@ -21,6 +22,7 @@ import core.game.bots.CombatBotAssembler import core.game.bots.Script import core.game.interaction.IntType import core.game.interaction.InteractionListeners +import core.game.node.Node import core.game.node.entity.combat.CombatSwingHandler import core.game.node.entity.combat.MeleeSwingHandler import kotlin.random.Random @@ -224,7 +226,7 @@ class GreenDragonKiller(val style: CombatStyle, area: ZoneBorders? = null) : Scr private fun attemptToBuryBone() { if (bot.inventory.containsAtLeastOneItem(Items.DRAGON_BONES_536)) { - BoneBuryingOptionPlugin().handle(bot, bot.inventory.get(Item(Items.DRAGON_BONES_536)), "bury") + InteractionListeners.run(Items.DRAGON_BONES_536, IntType.ITEM, "bury", bot, bot.inventory.get(Item(Items.DRAGON_BONES_536))) } } diff --git a/Server/src/main/content/global/bots/LawCrafter.kt b/Server/src/main/content/global/bots/LawCrafter.kt new file mode 100644 index 000000000..16ff4a574 --- /dev/null +++ b/Server/src/main/content/global/bots/LawCrafter.kt @@ -0,0 +1,210 @@ +package content.global.bots + +import content.global.travel.ship.Ships +import core.cache.def.impl.ItemDefinition +import core.game.bots.* +import core.game.node.item.Item +import core.game.node.scenery.Scenery +import core.game.world.map.Location +import core.game.world.map.zone.ZoneBorders +import org.rs09.consts.Items + + +@PlayerCompatible +@ScriptName("Law Rune Crafter") +@ScriptDescription("Crafts law runes. Start near Draynor bank w/ law tiara.") +@ScriptIdentifier("law_crafter") +class LawCrafter : Script() { + var state = State.INIT + var runeCounter = 0 + var overlay: ScriptAPI.BottingOverlay? = null + var startLocation = Location(0,0,0) + var timer = 0 + var bank = ZoneBorders(3092, 3242, 3092, 3245) + var boatNPC = Location(3047, 3234, 0) + var ruinsZone = ZoneBorders(2850, 3375, 2860, 3382) + var ruinPoint = Location(2857, 3380, 0) + var onBoat = ZoneBorders(2824, 3328, 2840, 3333) + var offBoat = ZoneBorders(2827, 3335, 2836, 3336) + var lawLocation = Location(2464, 4830, 0) + var lawZone = ZoneBorders(2439, 4808, 2488, 4855) + var returnNPC = Location(2835, 3335, 0) + var halfBank = Location(3069, 3275, 0) + + override fun tick() { + if (timer-- > 0) { + return + } + if (bot.settings.runEnergy > 10.0) { + bot.settings.isRunToggled = true + } + + when(state){ + State.INIT -> { + if (!bot.equipment.containsAtLeastOneItem(Items.LAW_TIARA_5545) || !ItemDefinition.canEnterEntrana(bot)) { + bot.sendMessage("Please equip a law tiara first.") + bot.sendMessage("AND REMOVE ALL WEAPONS AND ARMOR.") + state = State.INVALID + } else { + overlay = scriptAPI.getOverlay() + overlay!!.init() + overlay!!.setTitle("Law Runes") + overlay!!.setTaskLabel("Runes Crafted:") + overlay!!.setAmount(0) + startLocation = bot.location + state = State.BANKING + } + } + + State.BANKING -> { + endDialogue = true + bot.interfaceManager.closeChatbox() + bot.interfaceManager.openChatbox(137) + bot.interfaceManager.closeChatbox() + bot.dialogueInterpreter.close() + if(!bank.insideBorder(bot)) { + scriptAPI.walkTo(bank.randomLoc) + return + } + val runes = bot.inventory.getAmount(Item(Items.LAW_RUNE_563)) + if (runes > 0) { + runeCounter += runes + overlay!!.setAmount(runeCounter) + bot.sendMessage("You have crafted a total of: $runeCounter runes.") + scriptAPI.bankItem(Items.LAW_RUNE_563) + } else { + scriptAPI.withdraw(Items.PURE_ESSENCE_7936, 28) + state = State.HALF_BANK + } + } + + State.TO_BOAT_GUY -> { + var boatGuy = scriptAPI.getNearestNode(2729, false) + if (boatGuy != null){ + if (boatGuy.location.withinDistance(bot.location,2)) { + if (ItemDefinition.canEnterEntrana(bot)) { + endDialogue = false + Ships.PORT_SARIM_TO_ENTRANA.sail(bot) + state = State.CROSS_GANGPLANK + } else { + state = State.INVALID + } + } else { + scriptAPI.walkTo(boatGuy.location) + } + + } else { + scriptAPI.walkTo(boatNPC) + } + } + + State.CROSS_GANGPLANK -> { + if (onBoat.insideBorder(bot)) { + var gangplank = scriptAPI.getNearestNode(2415, true) + if (gangplank != null) { + scriptAPI.interact(bot, gangplank, "cross") + } + } else if (offBoat.insideBorder(bot)) { + state = State.RUNNING_TO_ALTER + endDialogue = true + } + } + + State.RUNNING_TO_ALTER -> { + if (lawZone.insideBorder(bot)) + state = State.CRAFTING + + val ruins = scriptAPI.getNearestNode(2459,true) + if (!ruinsZone.insideBorder(bot)) { + scriptAPI.walkTo(ruinPoint) + } else if (ruins != null && ruins.location.withinDistance(bot.location, 20)) { + val ruinsChild = (ruins as Scenery).getChild(bot) + scriptAPI.interact(bot, ruinsChild, "enter") + timer = 4 + } + } + + State.CRAFTING -> { + if (!lawZone.insideBorder(bot)) { + return + } + + if (bot.location != lawLocation) { + scriptAPI.walkTo(lawLocation) + } + + val alter = scriptAPI.getNearestNode(2485,true) + scriptAPI.interact(bot, alter, "craft-rune") + if(bot.inventory.containsAtLeastOneItem(Item(Items.LAW_RUNE_563))) + state = State.LEAVING_ALTER + } + + State.LEAVING_ALTER -> { + var portalOut = scriptAPI.getNearestNode(2472, true) + scriptAPI.interact(bot, portalOut, "use") + if (ruinsZone.insideBorder(bot)) { + state = State.RETURN_TO_BOAT_GUY + timer = 2 + } + } + + State.RETURN_TO_BOAT_GUY -> { + var boatGuy = scriptAPI.getNearestNode(2730, false) + if (boatGuy != null){ + if (boatGuy.location.withinDistance(bot.location,2)) { + endDialogue = false + Ships.ENTRANA_TO_PORT_SARIM.sail(bot) + state = State.HALF_BANK + } else { + scriptAPI.walkTo(boatGuy.location) + } + + } else { + scriptAPI.walkTo(returnNPC) + } + } + + // Splits up the journey into two parts. + // This is because the dialogue can't be ended until you are safely on the + // mainland side. But new chunks won't load while dialogue is still displayed. + State.HALF_BANK -> { + if (bot.inventory.containsAtLeastOneItem(Items.PURE_ESSENCE_7936)) { + if ( (bot.location.x - 2) > halfBank.x) { + scriptAPI.walkTo(halfBank) + } else { + state = State.TO_BOAT_GUY + } + } else { + if ( (bot.location.x + 2) < halfBank.x) { + scriptAPI.walkTo(halfBank) + } else { + state = State.BANKING + } + } + } + + State.INVALID -> { + timer = 25 + state = State.INIT + } + } + } + + override fun newInstance(): Script { + return this + } + + enum class State { + INIT, + BANKING, + TO_BOAT_GUY, + CROSS_GANGPLANK, + RUNNING_TO_ALTER, + CRAFTING, + LEAVING_ALTER, + RETURN_TO_BOAT_GUY, + HALF_BANK, + INVALID + } + +} diff --git a/Server/src/main/content/global/bots/LobsterCatcher.kt b/Server/src/main/content/global/bots/LobsterCatcher.kt index 1f3a494a1..9661d41f1 100644 --- a/Server/src/main/content/global/bots/LobsterCatcher.kt +++ b/Server/src/main/content/global/bots/LobsterCatcher.kt @@ -97,9 +97,9 @@ class LobsterCatcher : Script() { state = State.FISHING } else { if (bot.location.x < 2837) { - Pathfinder.find(bot, Location.create(2837, 3435, 0)).walk(bot) + scriptAPI.walkTo(Location.create(2837, 3435, 0)) } else { - Pathfinder.find(bot, Location.create(2854, 3427, 0)).walk(bot) + scriptAPI.walkTo(Location.create(2854, 3427, 0)) } } } @@ -194,4 +194,4 @@ class LobsterCatcher : Script() { } return newInstance() } -} \ No newline at end of file +} diff --git a/Server/src/main/content/global/bots/NatureCrafter.kt b/Server/src/main/content/global/bots/NatureCrafter.kt new file mode 100644 index 000000000..02f8248e1 --- /dev/null +++ b/Server/src/main/content/global/bots/NatureCrafter.kt @@ -0,0 +1,173 @@ +package content.global.bots + +import content.global.skill.runecrafting.MysteriousRuin +import core.api.teleport +import core.game.bots.* +import core.game.interaction.IntType +import core.game.interaction.InteractionListeners +import core.game.node.entity.player.link.TeleportManager +import core.game.node.item.Item +import core.game.node.scenery.Scenery +import core.game.world.map.Location +import core.game.world.map.zone.ZoneBorders +import org.rs09.consts.Items + + +@PlayerCompatible +@ScriptName("Nature Rune Crafter") +@ScriptDescription("Crafts nat runes. Start in Zanaris w/ dramen staff + Nat tiara.") +@ScriptIdentifier("nature_crafter") +class NatureCrafter : Script() { + var state = State.INIT + var runeCounter = 0 + var overlay: ScriptAPI.BottingOverlay? = null + var startLocation = Location(0,0,0) + var timer = 0 + var teleWaitTime = 16 // 18 ticks needed to rotate the clocks, 2 to teleport + var bank = Location(2384, 4457) + var ruinsZone = ZoneBorders(2866, 3017, 2873, 3022) + var faeRingDestination = Location(2412, 4435, 0) // You don't walk to the fae rings + // you can only walk next to them. so path to the nearest point that isn't in the ring. + var faeRingCenter = Location(2412, 4434, 0) + var karamjaRingDestination = Location(2801, 3002, 0) + var karamjaRingCenter = Location(2801, 3003, 0) + var natureZone = ZoneBorders(2387, 4828, 2416, 4856) + override fun tick() { + if (timer-- > 0) { + return + } + if (bot.settings.runEnergy > 10.0) { + bot.settings.isRunToggled = true + } + + when(state){ + State.INIT -> { + if (checkValid()) { + overlay = scriptAPI.getOverlay() + overlay!!.init() + overlay!!.setTitle("Nature Runes") + overlay!!.setTaskLabel("Runes Crafted:") + overlay!!.setAmount(0) + startLocation = bot.location + state = State.BANKING + } + } + + State.BANKING -> { + if(bot.location != bank) { + scriptAPI.walkTo(bank) + return + } + val runes = bot.inventory.getAmount(Item(Items.NATURE_RUNE_561)) + if (runes > 0) { + runeCounter += runes + overlay!!.setAmount(runeCounter) + bot.sendMessage("You have crafted a total of: $runeCounter runes.") + scriptAPI.bankItem(Items.NATURE_RUNE_561) + } else { + scriptAPI.withdraw(Items.PURE_ESSENCE_7936, 28) + state = State.RUNNING_TO_TELE + } + } + + State.RUNNING_TO_TELE -> { + if (bot.location != faeRingDestination) { + scriptAPI.walkTo(faeRingDestination) + } else { + if (checkValid()) { + timer = teleWaitTime + state = State.TELE_WAIT + bot.sendMessage("Entering Fairy Ring Codes. Please wait...") + } + } + } + + State.TELE_WAIT -> { + teleport(bot, karamjaRingCenter, TeleportManager.TeleportType.FAIRY_RING) + state = State.RUNNING_TO_ALTER + // tele takes 4 ticks so wait that long. + timer = 4 + } + + State.RETURN_WAIT -> { + teleport(bot, faeRingCenter, TeleportManager.TeleportType.FAIRY_RING) + state = State.BANKING + } + + State.RUNNING_TO_ALTER -> { + val ruins = scriptAPI.getNearestNode(2460,true) + if (natureZone.insideBorder(bot)) + state = State.CRAFTING + + if (!ruinsZone.insideBorder(bot)) { + scriptAPI.walkTo(ruinsZone.randomLoc) + } else if (ruins != null && ruins.location.withinDistance(bot.location, 20) && checkValid()) { + val ruinsChild = (ruins as Scenery).getChild(bot) + scriptAPI.interact(bot, ruinsChild, "enter") + timer = 4 + } + } + + State.CRAFTING -> { + val alter = scriptAPI.getNearestNode(2486,true) + scriptAPI.interact(bot, alter, "craft-rune") + if(bot.inventory.containsAtLeastOneItem(Item(Items.NATURE_RUNE_561))) + state = State.LEAVING_ALTER + } + + State.LEAVING_ALTER -> { + var portalOut = scriptAPI.getNearestNode(2473, true) + scriptAPI.interact(bot, portalOut, "use") + if (ruinsZone.insideBorder(bot)) + state = State.RETURNING_TO_TELE + } + + State.RETURNING_TO_TELE -> { + if (bot.location != karamjaRingDestination) { + scriptAPI.walkTo(karamjaRingDestination) + } else { + if (checkValid()) { + var portalOut = scriptAPI.getNearestNode(14130, true) + portalOut?.interaction?.handle(bot, portalOut.interaction[0]) + timer = 4 + state = State.RETURN_WAIT + } + } + } + + State.INVALID -> { + timer = 25 + state = State.INIT + } + } + } + + + // Terminates if you have invalid inventory to avoid cheating + private fun checkValid(): Boolean { + if (!bot.equipment.containsAtLeastOneItem(Items.DRAMEN_STAFF_772) or !bot.equipment.containsAtLeastOneItem(Items.NATURE_TIARA_5541)) { + bot.sendMessage("Please equip a dramen staff and nature tiara first.") + state = State.INVALID + return false + } + return true + } + + override fun newInstance(): Script { + return this + } + + enum class State { + INIT, + BANKING, + RUNNING_TO_TELE, + TELE_WAIT, + RUNNING_TO_ALTER, + CRAFTING, + LEAVING_ALTER, + RETURNING_TO_TELE, + RETURN_WAIT, + INVALID + } + +} 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/SeersFlax.kt b/Server/src/main/content/global/bots/SeersFlax.kt index 30d8ffee3..d39942005 100644 --- a/Server/src/main/content/global/bots/SeersFlax.kt +++ b/Server/src/main/content/global/bots/SeersFlax.kt @@ -65,8 +65,8 @@ class SeersFlax : Script(){ State.FIND_BANK -> { when(bot.location){ Location.create(2711, 3471, 1) -> { - val ladder = scriptAPI.getNearestNode(25939,true) - ladder?.interaction?.handle(bot,ladder?.interaction[0]) + val ladder = scriptAPI.getNearestNode(25939,true) ?: return + ladder.interaction?.handle(bot,ladder.interaction[0]) } Location.create(2714, 3470, 0) -> Pathfinder.find(bot,Location.create(2715, 3472, 0)).walk(bot) Location.create(2715, 3472, 0) -> { diff --git a/Server/src/main/content/global/bots/VarrockEssenceMiner.kt b/Server/src/main/content/global/bots/VarrockEssenceMiner.kt index 3dafd687f..39ce02c55 100644 --- a/Server/src/main/content/global/bots/VarrockEssenceMiner.kt +++ b/Server/src/main/content/global/bots/VarrockEssenceMiner.kt @@ -2,12 +2,12 @@ package content.global.bots 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.world.map.Location import core.game.world.map.zone.ZoneBorders import org.rs09.consts.Items -import core.game.interaction.IntType -import core.game.interaction.InteractionListeners @PlayerCompatible @ScriptDescription("Start in varrock bank with rune mysteries complete and a pickaxe equipped/in inventory") @@ -18,6 +18,7 @@ class VarrockEssenceMiner : Script(){ var state = State.TO_ESSENCE val auburyZone = ZoneBorders(3252, 3398, 3254, 3402) val bankZone = ZoneBorders(3251, 3420,3254, 3422) + // Used for automatic failure prevention eg from levelup override fun tick() { when(state){ @@ -49,10 +50,13 @@ class VarrockEssenceMiner : Script(){ } State.MINING -> { - val essence = scriptAPI.getNearestNode(2491,true) - essence?.let { InteractionListeners.run(essence.id, IntType.SCENERY,"mine",bot,essence) } - if(bot.inventory.isFull) + if(bot.inventory.isFull) { state = State.TO_BANK + } + else { + val essence = scriptAPI.getNearestNode(2491,true) + essence?.let { InteractionListeners.run(essence.id, IntType.SCENERY,"mine",bot,essence) } + } } State.TO_BANK -> { @@ -114,4 +118,4 @@ class VarrockEssenceMiner : Script(){ script.bot = SkillingBotAssembler().produce(SkillingBotAssembler.Wealth.POOR,bot.startLocation) return script } -} \ No newline at end of file +} diff --git a/Server/src/main/content/global/dialogue/BankerDialogue.kt b/Server/src/main/content/global/dialogue/BankerDialogue.kt index e913ce4f0..7b857da7d 100644 --- a/Server/src/main/content/global/dialogue/BankerDialogue.kt +++ b/Server/src/main/content/global/dialogue/BankerDialogue.kt @@ -45,18 +45,7 @@ class BankerDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin 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 switch my default bank account.", 7), 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), @@ -78,6 +67,53 @@ class BankerDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin "Leave your valuables with us if you want to keep them safe." ).also { stage = END_DIALOGUE } + 7 -> showTopics( + IfTopic( + core.game.dialogue.FacialExpression.FRIENDLY, + "I'd like to turn on the Clan bank, please.", + 8, + !(player.getAttribute("clanbank:enabled",false)) + ), + IfTopic( + core.game.dialogue.FacialExpression.FRIENDLY, + "I'd like to turn off the Clan bank, please.", + 9, + player.getAttribute("clanbank:enabled",false) + ), + 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) + ), + ) + + 8 -> { + player.setAttribute("/save:clanbank:enabled", true) + + npcl( + core.game.dialogue.FacialExpression.FRIENDLY, + "The Clan bank has been enabled. " + + "If the Clan owner is online, you will access their primary bank account instead of your own." + ).also { stage = 2 } + } + + 9 -> { + player.removeAttribute("clanbank:enabled") + + npcl( + core.game.dialogue.FacialExpression.FRIENDLY, + "The Clan bank has been disabled. " + + "You will now always open your personal bank account." + ).also { stage = 2 } + } + 10 -> { openBankAccount(player) end() diff --git a/Server/src/main/content/global/dialogue/FurTradePlugin.java b/Server/src/main/content/global/dialogue/FurTradePlugin.java deleted file mode 100644 index b079a3b87..000000000 --- a/Server/src/main/content/global/dialogue/FurTradePlugin.java +++ /dev/null @@ -1,77 +0,0 @@ -package content.global.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 plugin used for the fur trader. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class FurTradePlugin extends DialoguePlugin { - - /** - * Constructs a new {@code FurTradePlugin} {@code Object}. - */ - public FurTradePlugin() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code FurTradePlugin} {@code Object}. - * @param player the player. - */ - public FurTradePlugin(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new FurTradePlugin(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Would you like to trade in fur?"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendOptions("Select an Option", "Yes.", "No."); - stage = 1; - break; - case 1: - switch (buttonId) { - case 1: - end(); - npc.openShop(player); - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No, thanks."); - stage = 20; - break; - } - break; - case 20: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 573 }; - } -} 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/dialogue/ShopkeeperDialogue.java b/Server/src/main/content/global/dialogue/ShopkeeperDialogue.java deleted file mode 100644 index e132605d4..000000000 --- a/Server/src/main/content/global/dialogue/ShopkeeperDialogue.java +++ /dev/null @@ -1,81 +0,0 @@ -package content.global.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 generic shop keeper dialogue. - * @author Vexia - */ -@Initializable -public final class -ShopkeeperDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code ShopkeeperDialogue} {@code Object}. - */ - public ShopkeeperDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code ShopkeeperDialogue} {@code Object}. - * @param player the player. - */ - public ShopkeeperDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new ShopkeeperDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Can I help you at all?"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendOptions("Select an Option", "Yes, please. What are you selling?", "No, thanks."); - stage = 1; - break; - case 1: - switch (buttonId) { - case 1: - player("Yes, please. I'd like to see your stock."); - stage = 11; - break; - case 2: - player("No thanks, I must be going now."); - stage = 10; - break; - } - break; - case 10: - end(); - break; - case 11: - end(); - npc.openShop(player); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 523, 522, 520, 521, 1699,555 }; - } -} diff --git a/Server/src/main/content/global/dialogue/TownCrierDialogue.java b/Server/src/main/content/global/dialogue/TownCrierDialogue.java index ae4e258c7..e740fbfa2 100644 --- a/Server/src/main/content/global/dialogue/TownCrierDialogue.java +++ b/Server/src/main/content/global/dialogue/TownCrierDialogue.java @@ -1,5 +1,6 @@ package content.global.dialogue; +import content.global.handlers.item.book.GeneralRuleBook; import core.game.dialogue.DialoguePlugin; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; @@ -122,7 +123,7 @@ public final class TownCrierDialogue extends DialoguePlugin { GameWorld.getPulser().submit(new Pulse(4) { @Override public boolean pulse() { - //player.getDialogueInterpreter().open(496107759); // TODO rulebook broken + GeneralRuleBook.Companion.openBook(player); return true; } }); diff --git a/Server/src/main/content/global/handlers/iface/BoltEnchantingInterface.java b/Server/src/main/content/global/handlers/iface/BoltEnchantingInterface.java index de8c22f87..5d4a567ff 100644 --- a/Server/src/main/content/global/handlers/iface/BoltEnchantingInterface.java +++ b/Server/src/main/content/global/handlers/iface/BoltEnchantingInterface.java @@ -1,5 +1,6 @@ package content.global.handlers.iface; +import core.api.ContentAPIKt; import core.game.component.Component; import core.game.component.ComponentDefinition; import core.game.component.ComponentPlugin; @@ -13,6 +14,9 @@ import core.game.system.task.Pulse; import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Graphics; import core.plugin.Plugin; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.playAudio; /** * Represents the component plugin used to handle the bolt enchanting interface. @@ -87,6 +91,7 @@ public final class BoltEnchantingInterface extends ComponentPlugin { } player.getSkills().addExperience(Skills.MAGIC, bolts.getExp(), true); player.getPacketDispatch().sendMessage("The magic of the runes coaxes out the true nature of the gem tips."); + playAudio(player, Sounds.ENCHANTED_TIPPING_2921); return true; } }); diff --git a/Server/src/main/content/global/handlers/iface/BookInterface.kt b/Server/src/main/content/global/handlers/iface/BookInterface.kt new file mode 100644 index 000000000..070b83ebd --- /dev/null +++ b/Server/src/main/content/global/handlers/iface/BookInterface.kt @@ -0,0 +1,233 @@ +package content.global.handlers.iface + +import core.api.closeInterface +import core.api.getAttribute +import core.api.openInterface +import core.api.setAttribute +import core.game.interaction.InterfaceListener +import core.game.node.entity.player.Player + +/** + * Interface listener for Books + * Use this to listen and interact with buttons on book interfaces. + * + * This will handle component(26), component(27) and component(49) globally. + * DO NOT extend this class or override on(26), on(27), on(49) for defineInterfaceListeners. + * + * Instead, simply call BookInterface.openBook(...) in the listener and set the following attributes: + * bookInterfaceCallback - callback function for the page to display(player: Player, pageNum: Int, buttonID: Int) : Boolean + * You must create that display() function so that can be passed in the callback and display contents of each page. + * + * You may at any time after pageSetup, + * - call any functions below + * - call player.packetDispatch.sendInterfaceConfig + * - call player.packetDispatch.sendString + * + * @see content.region.desert.quest.thegolem.VarmensNotes for a quest-like example. + * @see content.global.handlers.item.book.GeneralRuleBook for an interactive page-jumping book. + * + * @author ovenbreado + */ +class BookInterface : InterfaceListener { + + companion object { + const val CALLBACK_ATTRIBUTE = "bookInterfaceCallback" + const val CURRENT_PAGE_ATTRIBUTE = "bookInterfaceCurrentPage" + + /* These should be in org.rs09.consts.Components but currently are not. */ + const val FANCY_BOOK_26 = 26 // This is a 15-Lines per page book. + const val FANCY_BOOK_2_27 = 27 // This is a 15-Lines per page book with index and row clickable listeners. + const val FANCY_BOOK_3_49 = 49 // This is an 11-Lines per page book. + + /* Book Lines. [Title, Left button text, Right button text, ...lines 1 to X] */ + val FANCY_BOOK_26_LINE_IDS = arrayOf(101, 65, 66, 97, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96) + val FANCY_BOOK_2_27_LINE_IDS = arrayOf(163, 5, 6, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67) + val FANCY_BOOK_3_49_LINE_IDS = arrayOf(6, 77, 78, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76) + + /* Button IDs. [Left button, Right button, (opt)index button, ...(opt)click lines 1 to X] */ + val FANCY_BOOK_26_BUTTON_IDS = arrayOf(61, 63) + val FANCY_BOOK_2_27_BUTTON_IDS = arrayOf(1, 3, 159, 100, 102, 104, 106, 108, 110, 112, 114, 116, 118, 120, 122, 124, 126, 128, 130, 132, 134, 136, 138, 140, 142, 144, 146, 148, 150, 152, 154, 156, 158) + val FANCY_BOOK_3_49_BUTTON_IDS = arrayOf(51, 53) + + /* Image IDs. [...lines 1 to X] */ + val FANCY_BOOK_2_27_IMAGE_ENABLE_DRAW_IDS = arrayOf(9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35, 37, 69, 71, 73, 75, 77, 79, 81, 83, 85, 87, 89, 91, 93, 95, 97) + val FANCY_BOOK_2_27_IMAGE_DRAW_IDS = arrayOf(10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98) + + /** Opens the book interface. Call this only once in the defineListeners() at the start of opening a book. */ + fun openBook(player: Player, bookComponent: Int, displayCallback: (player: Player, pageNum: Int, buttonId: Int) -> Boolean) { + closeInterface(player) // Important: Closes the previous interface. + setAttribute(player, CURRENT_PAGE_ATTRIBUTE, 0) // Resets the book to the first page. + setAttribute(player, CALLBACK_ATTRIBUTE, displayCallback) // Sets the display callback + if (bookComponent == FANCY_BOOK_26) { + openInterface(player, FANCY_BOOK_26) // Important: Opens the current interface. + } else if (bookComponent == FANCY_BOOK_2_27) { + openInterface(player, FANCY_BOOK_2_27) // Important: Opens the current interface. + } else if (bookComponent == FANCY_BOOK_3_49) { + openInterface(player, FANCY_BOOK_3_49) // Important: Opens the current interface. + } + displayCallback.invoke(player, 0, 0) + } + + /** Sets up title, pagination and content. Call this in the display callback every time the page changes. */ + fun pageSetup(player: Player, bookComponent: Int, title: String, contents: Array, hasPagination: Boolean = true) { + val currentPage = getAttribute(player, CURRENT_PAGE_ATTRIBUTE, 0) + if (bookComponent == FANCY_BOOK_26) { + clearBookLines(player, FANCY_BOOK_26, FANCY_BOOK_26_LINE_IDS) + clearButtons(player, FANCY_BOOK_26, FANCY_BOOK_26_BUTTON_IDS) + setTitle(player, FANCY_BOOK_26, FANCY_BOOK_26_LINE_IDS, title) + if (hasPagination) { + setPagination(player, FANCY_BOOK_26, FANCY_BOOK_26_LINE_IDS, FANCY_BOOK_26_BUTTON_IDS, currentPage, contents.size, contents[currentPage].pages.size == 1) + } + setPageContent(player, FANCY_BOOK_26, FANCY_BOOK_26_LINE_IDS, FANCY_BOOK_26_BUTTON_IDS, currentPage, contents) + } else if (bookComponent == FANCY_BOOK_2_27) { + clearBookLines(player, FANCY_BOOK_2_27, FANCY_BOOK_2_27_LINE_IDS) + clearButtons(player, FANCY_BOOK_2_27, FANCY_BOOK_2_27_BUTTON_IDS) + setTitle(player, FANCY_BOOK_2_27, FANCY_BOOK_2_27_LINE_IDS, title) + if (hasPagination) { + setPagination(player, FANCY_BOOK_2_27, FANCY_BOOK_2_27_LINE_IDS, FANCY_BOOK_2_27_BUTTON_IDS, currentPage, contents.size, contents[currentPage].pages.size == 1) + } + setPageContent(player, FANCY_BOOK_2_27, FANCY_BOOK_2_27_LINE_IDS, FANCY_BOOK_2_27_BUTTON_IDS, currentPage, contents) + } else if (bookComponent == FANCY_BOOK_3_49) { + clearBookLines(player, FANCY_BOOK_3_49, FANCY_BOOK_3_49_LINE_IDS) + clearButtons(player, FANCY_BOOK_3_49, FANCY_BOOK_3_49_BUTTON_IDS) + setTitle(player, FANCY_BOOK_3_49, FANCY_BOOK_3_49_LINE_IDS, title) + if (hasPagination) { + setPagination(player, FANCY_BOOK_3_49, FANCY_BOOK_3_49_LINE_IDS, FANCY_BOOK_3_49_BUTTON_IDS, currentPage, contents.size, contents[currentPage].pages.size == 1) + } + setPageContent(player, FANCY_BOOK_3_49, FANCY_BOOK_3_49_LINE_IDS, FANCY_BOOK_3_49_BUTTON_IDS, currentPage, contents) + } + } + + /** Clear all lines rather than leaving "Line X" on all visible rows. */ + fun clearBookLines(player: Player, componentId: Int, bookLineIds: Array) { + openInterface(player, componentId) // Important: Opens the current interface. + for (i in bookLineIds) { + player.packetDispatch.sendString("", componentId, i) + } + } + + /** Clear all buttons rather than leaving invisible button rows. */ + fun clearButtons(player: Player, componentId: Int, bookButtonIds: Array) { + for (i in bookButtonIds) { + player.packetDispatch.sendInterfaceConfig(componentId, i, true) + } + } + + /** Set title of book. */ + fun setTitle(player: Player, componentId: Int, bookLineIds: Array, title: String) { + player.packetDispatch.sendString(title, componentId, bookLineIds[0]) + } + + /** Set pagination numbers of current page in book. CurrentPage is 0 index. */ + fun setPagination(player: Player, componentId: Int, bookLineIds: Array, bookButtonIds: Array, currentPage: Int, totalPages: Int, hasRightPage: Boolean) { + player.packetDispatch.sendInterfaceConfig(componentId, bookButtonIds[0], currentPage <= 0) + player.packetDispatch.sendInterfaceConfig(componentId, bookButtonIds[1], currentPage >= totalPages - 1) + player.packetDispatch.sendString("" + (currentPage * 2 + 1), componentId, bookLineIds[1]) + player.packetDispatch.sendString("" + (currentPage * 2 + 2), componentId, bookLineIds[2]) + if (hasRightPage) { + // If there's no right side page, remove the page number. Usually for odd paged books. + if (componentId == FANCY_BOOK_26) { + player.packetDispatch.sendString("", componentId, FANCY_BOOK_26_LINE_IDS[2]) + } else if (componentId == FANCY_BOOK_2_27) { + player.packetDispatch.sendString("", componentId, FANCY_BOOK_2_27_LINE_IDS[2]) + } else if (componentId == FANCY_BOOK_3_49) { + player.packetDispatch.sendString("", componentId, FANCY_BOOK_3_49_LINE_IDS[2]) + } + } + } + + /** Set text contents of book. CurrentPage is 0 index. */ + fun setPageContent(player: Player, componentId: Int, bookLineIds: Array, bookButtonIds: Array, currentPage: Int, contents: Array) { + for (page in contents[currentPage].pages) { + for (line in page.lines) { + // This is to prevent error child lines being set and crashing the client. + if (bookLineIds.contains(line.child)) { + player.packetDispatch.sendString(line.message, componentId, line.child) + } + if (bookButtonIds.contains(line.child)) { + player.packetDispatch.sendInterfaceConfig(componentId, line.child, false) + player.packetDispatch.sendString(line.message, componentId, line.child) + } + } + + } + } + + /** Sets models(pictures) on lineId of pageSet (0 index). Call this in the display function after pageSetup. */ + fun setModelOnPage(player: Player, pageSet: Int, modelId: 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.sendModelOnInterface(modelId, componentId, drawLineId, 0) + player.packetDispatch.sendAngleOnInterface(componentId, drawLineId, zoom, pitch, yaw) + } else { + player.packetDispatch.sendInterfaceConfig(componentId, enableLineId, true) + } + } + + /** Function to check if player read to the last page. For quest triggers. */ + fun isLastPage(pageNum: Int, totalPages: Int): Boolean { + return pageNum == totalPages - 1 + } + + /** PRIVATE: Increments the current page and invokes the callback function. */ + private fun changePageAndCallback(player: Player, increment: Int, buttonId: Int) { + val callback: ((player: Player, pageNum: Int, buttonId: Int) -> Boolean)? = + getAttribute(player, CALLBACK_ATTRIBUTE, null) + val currentPage = getAttribute(player, CURRENT_PAGE_ATTRIBUTE, 0) + setAttribute(player, CURRENT_PAGE_ATTRIBUTE, currentPage + increment) + + callback?.invoke(player, currentPage + increment, buttonId) + } + } + + override fun defineInterfaceListeners() { + on(FANCY_BOOK_26) { player, _, _, buttonID, _, _ -> + when (buttonID) { + FANCY_BOOK_26_BUTTON_IDS[0] -> { changePageAndCallback(player, -1, buttonID) } + FANCY_BOOK_26_BUTTON_IDS[1] -> { changePageAndCallback(player, 1, buttonID) } + else -> (changePageAndCallback(player, 0, buttonID)) + } + return@on true + } + on(FANCY_BOOK_2_27) { player, _, _, buttonID, _, _ -> + when (buttonID) { + FANCY_BOOK_2_27_BUTTON_IDS[0] -> { changePageAndCallback(player, -1, buttonID) } + FANCY_BOOK_2_27_BUTTON_IDS[1] -> { changePageAndCallback(player, 1, buttonID) } + else -> (changePageAndCallback(player, 0, buttonID)) + } + return@on true + } + on(FANCY_BOOK_3_49) { player, _, _, buttonID, _, _ -> + when (buttonID) { + FANCY_BOOK_3_49_BUTTON_IDS[0] -> { changePageAndCallback(player, -1, buttonID) } + FANCY_BOOK_3_49_BUTTON_IDS[1] -> { changePageAndCallback(player, 1, buttonID) } + else -> (changePageAndCallback(player, 0, buttonID)) + } + return@on true + } + } +} + +/** Constructs a new PageSet object. */ +class PageSet(vararg pages: Page) { + val pages: Array + + init { + this.pages = pages as Array + } +} + +/** Constructs a new Page object. */ +class Page(vararg lines: BookLine) { + val lines: Array + + init { + this.lines = lines as Array + } +} + +/** Constructs a new BookLine object. */ +class BookLine( + val message: String, + val child: Int +) diff --git a/Server/src/main/content/global/handlers/iface/ClanInterfacePlugin.java b/Server/src/main/content/global/handlers/iface/ClanInterfacePlugin.java index 1ad3857e8..5003507c2 100644 --- a/Server/src/main/content/global/handlers/iface/ClanInterfacePlugin.java +++ b/Server/src/main/content/global/handlers/iface/ClanInterfacePlugin.java @@ -36,17 +36,21 @@ public final class ClanInterfacePlugin extends ComponentPlugin { 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'"); + 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; - } - ClanRepository.openSettings(player); - return true; - case 14: - player.getDetails().getCommunication().toggleLootshare(player); - return true; + case 14: + if (player.getIronmanManager().checkRestriction()) { + return false; + } + player.getDetails().getCommunication().toggleLootshare(player); + return true; } + break; case 590: final ClanRepository clan = ClanRepository.get(player.getName(), 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 099ac46f4..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("Alernatively 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/EquipmentInterface.java b/Server/src/main/content/global/handlers/iface/EquipmentInterface.java index 3461a0236..f8cfa2660 100644 --- a/Server/src/main/content/global/handlers/iface/EquipmentInterface.java +++ b/Server/src/main/content/global/handlers/iface/EquipmentInterface.java @@ -1,5 +1,7 @@ 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; @@ -24,6 +26,7 @@ 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. @@ -35,6 +38,7 @@ 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); @@ -111,20 +115,46 @@ public final class EquipmentInterface extends ComponentPlugin { if (p.getInterfaceManager().isOpened() && p.getInterfaceManager().getOpened().getId() == 102) { return true; } - boolean skulled = p.getSkullManager().isSkulled(); - boolean usingProtect = p.getPrayer().get(PrayerType.PROTECT_ITEMS); - p.getInterfaceManager().openComponent(102); - p.getPacketDispatch().sendIfaceSettings(211, 0, 2, 6684690, 4); - p.getPacketDispatch().sendIfaceSettings(212, 0, 2, 6684693, 42); + + // (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 state = 0; // 1=familiar carrying items - int keptItems = skulled ? (usingProtect ? 1 : 0) : (usingProtect ? 4 : 3); - int zoneType = p.getZoneMonitor().getType(); - int pvpType = p.getSkullManager().isWilderness() ? 0 : 1; - Object[] params = new Object[] { 11510, 12749, "", state, pvpType, kept.getId(3), kept.getId(2), kept.getId(1), kept.getId(0), keptItems, zoneType }; - PacketRepository.send(ContainerPacket.class, new ContainerContext(p, 149, 0, 91, itemArray[1], false)); - p.getPacketDispatch().sendRunScript(118, "iiooooiisii", params); + 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) { diff --git a/Server/src/main/content/global/handlers/iface/ExperienceInterface.kt b/Server/src/main/content/global/handlers/iface/ExperienceInterface.kt index 9e93c3cef..c7003ea83 100644 --- a/Server/src/main/content/global/handlers/iface/ExperienceInterface.kt +++ b/Server/src/main/content/global/handlers/iface/ExperienceInterface.kt @@ -5,11 +5,12 @@ 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.audio.Audio import core.game.node.entity.skill.Skills import core.plugin.Initializable import core.plugin.Plugin -import core.tools.SystemLogger +import core.tools.Log +import org.rs09.consts.Sounds +import content.data.Quests /** * Represents the experience interface. @@ -70,7 +71,7 @@ class ExperienceInterface() : ComponentPlugin() { 46 -> Skills.WOODCUTTING 40 -> Skills.FLETCHING 51 -> Skills.CONSTRUCTION - else -> Skills.SLAYER.also { SystemLogger.logWarn(this::class.java, "EXP_INTERFACE: Invalid SKILL CHOICE BUTTON: $button") } + else -> Skills.SLAYER.also { log(this::class.java, Log.WARN, "EXP_INTERFACE: Invalid SKILL CHOICE BUTTON: $button") } } player.setAttribute("exp_interface:skill",skill) } @@ -78,22 +79,22 @@ 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 { /** * Represents the sound to send. */ - private val SOUND = Audio(1270, 12, 1) + private val SOUND = Sounds.TBCU_FINDGEM_1270 @JvmField public val COMPONENT_ID = 134 } diff --git a/Server/src/main/content/global/handlers/iface/FairyRingInterface.kt b/Server/src/main/content/global/handlers/iface/FairyRingInterface.kt index f580ba099..69ad63e21 100644 --- a/Server/src/main/content/global/handlers/iface/FairyRingInterface.kt +++ b/Server/src/main/content/global/handlers/iface/FairyRingInterface.kt @@ -2,110 +2,130 @@ 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") - clearVarp(player, 816) - setVarbit(player, 816, 0, 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) { - setVarbit(player, 816, 0, 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) { + /* Snowscape: show all fairy ring destinations, whether they have been there or not. + 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()) @@ -161,7 +181,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), @@ -173,7 +193,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), @@ -181,7 +201,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), @@ -190,7 +210,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), @@ -209,23 +229,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 1020b6247..c395840e6 100644 --- a/Server/src/main/content/global/handlers/iface/FurClothingInterface.kt +++ b/Server/src/main/content/global/handlers/iface/FurClothingInterface.kt @@ -192,8 +192,8 @@ class FurClothingInterface : ComponentPlugin(){ KYATT_HEAD(Item(10103),1000, KYATT_HAT,17,"Kyatt"), KYATT_CHEST(Item(10103),200, KYATT_TOP,17,"Kyatt"), KYATT_BOT(Item(10103),200, KYATT_LEGS,17,"Kyatt"), - GLOVES_SILENCE(Item(10115),600, GLOVES_OF_SILENCE,22,"Dark kebbit"), - SPOT_CAPE(Item(10125),400, SPOTTED_CAPE,23,"Spotted kebbit"), - DASH_CAPE(Item(10127),800, SPOTTIER_CAPE,24,"Dashing kebbit") + GLOVES_SILENCE(Item(10115,2),600, GLOVES_OF_SILENCE,22,"Dark kebbit"), + SPOT_CAPE(Item(10125,2),400, SPOTTED_CAPE,23,"Spotted kebbit"), + DASH_CAPE(Item(10127,2),800, SPOTTIER_CAPE,24,"Dashing kebbit") } } \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/iface/GenericItemSelect.kt b/Server/src/main/content/global/handlers/iface/GenericItemSelect.kt new file mode 100644 index 000000000..beb2a297e --- /dev/null +++ b/Server/src/main/content/global/handlers/iface/GenericItemSelect.kt @@ -0,0 +1,67 @@ +package content.global.handlers.iface + +import core.api.* +import core.tools.* +import core.game.interaction.* +import core.game.system.task.Pulse +import core.game.node.entity.player.Player + +class GenericItemSelect : InterfaceListener { + val GENERIC_ITEM_SELECT_IFACE = 12 + + override fun defineInterfaceListeners() { + onOpen(GENERIC_ITEM_SELECT_IFACE) {player, _ -> + player.pulseManager.run(object : Pulse() { + override fun pulse() : Boolean { + return false + } + override fun stop() { + super.stop() + player.interfaceManager.closeSingleTab() + } + }) + return@onOpen true + } + + on(GENERIC_ITEM_SELECT_IFACE){player, _, opcode, buttonID, slot, _ -> + processResponse(player, opcode, slot) + return@on true + } + + onClose(GENERIC_ITEM_SELECT_IFACE) {player, _ -> + removeAttribute(player, "itemselect-callback") + removeAttribute(player, "itemselect-keepalive") + return@onClose true + } + } + + private fun processResponse (player: Player, opcode: Int, slot: Int) { + val callback = getAttribute<((Int, Int) -> Unit)?>(player, "itemselect-callback", null) + if (callback == null) { + log (this::class.java, Log.WARN, "${player.name} is trying to use an item select prompt with no callback!") + return + } + + val optionIndex = when (opcode) { + 155 -> 0 + 196 -> 1 + 124 -> 2 + 199 -> 3 + 234 -> 4 + 9 -> 10 + else -> -1 + } + if (optionIndex == -1) { + log (this::class.java, Log.WARN, "${player.name} is clicking a right-click index that we don't know the opcode for yet, lol. Here's the opcode: $opcode") + return + } + + callback.invoke(slot, optionIndex) + + if (!getAttribute(player, "itemselect-keepalive", false)) { + removeAttribute (player, "itemselect-callback") + removeAttribute (player, "itemselect-keepalive") + player.interfaceManager.closeSingleTab() + } + } +} diff --git a/Server/src/main/content/global/handlers/iface/GlassInterface.java b/Server/src/main/content/global/handlers/iface/GlassInterface.java deleted file mode 100644 index eedf8158d..000000000 --- a/Server/src/main/content/global/handlers/iface/GlassInterface.java +++ /dev/null @@ -1,118 +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.game.node.entity.player.Player; -import core.game.node.entity.skill.Skills; -import content.global.skill.crafting.GlassProduct; -import core.game.node.item.Item; -import core.game.system.task.Pulse; -import core.game.world.update.flag.context.Animation; -import core.plugin.Initializable; -import core.plugin.Plugin; -import kotlin.Unit; - -/** - * Represents the glass making interface plugin. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class GlassInterface extends ComponentPlugin { - - /** - * Represents the animation to use. - */ - private static final Animation ANIMATION = new Animation(884); - - /** - * Represents the molten glass item. - */ - private static final Item MOLTEN_GLASS = new Item(1775); - - /** - * Represents the soda ash item. - */ - private static final Item SODA_ASH = new Item(1781, 1); - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ComponentDefinition.put(542, this); - return this; - } - - @Override - public boolean handle(final Player p, Component component, int opcode, int button, int slot, int itemId) { - final GlassProduct def = GlassProduct.forId(button); - if (def == null) { - return true; - } - if (!p.getInventory().contains(1785, 1)) { - p.getPacketDispatch().sendMessage("You need a glassblowing pipe to do this."); - return true; - } - if (!p.getInventory().contains(1775, 1)) { - p.getPacketDispatch().sendMessage("You need molten glass to do this."); - return true; - } - if (p.getSkills().getLevel(Skills.CRAFTING) < def.getLevel()) { - p.getPacketDispatch().sendMessage("You need a crafting level of " + def.getLevel() + " to make this."); - return true; - } - int real = -1; - switch (opcode) { - case 230: - real = 1; - break; - case 205: - real = 5; - break; - case 127: - real = p.getInventory().getAmount(MOLTEN_GLASS); - break; - case 211: - sendInputDialogue(p, true, "Enter the amount:", (value) -> { - make(p, def, (int) value); - return Unit.INSTANCE; - }); - break; - } - if (opcode == 211) { - return true; - } - make(p, def, real); - return true; - } - - /** - * Method used to make a glass product. - * @param player the player. - * @param glass the glass. - * @param amount the amount. - */ - public static void make(final Player player, final GlassProduct glass, final int amount) { - player.getInterfaceManager().close(); - player.animate(ANIMATION); - player.getPulseManager().clear(); - player.getPulseManager().run(new Pulse(2, player) { - int amt = amount; - - @Override - public boolean pulse() { - if (!player.getInventory().contains(1775, 1) || amt == 0) { - return true; - } - player.animate(ANIMATION); - player.getInventory().remove(MOLTEN_GLASS); - player.getInventory().add(new Item(glass.getProduct(), 1)); - player.getSkills().addExperience(Skills.CRAFTING, glass.getExperience(), true); - player.getPacketDispatch().sendMessage("You make a " + new Item(glass.getProduct()).getName().toLowerCase().replace("unpowered", "").trim() + "."); - amt--; - return false; - } - - }); - } -} diff --git a/Server/src/main/content/global/handlers/iface/HairDresserInterface.kt b/Server/src/main/content/global/handlers/iface/HairDresserInterface.kt index 1d7eca270..b1750ae63 100644 --- a/Server/src/main/content/global/handlers/iface/HairDresserInterface.kt +++ b/Server/src/main/content/global/handlers/iface/HairDresserInterface.kt @@ -144,7 +144,7 @@ class HairDresserInterface : ComponentPlugin(){ } sendPlayerOnInterface(player, usedInterface, player_model_child) sendPlayerOnInterface(player, usedInterface, player_head_child) - sendAnimationOnInterface(player, core.game.dialogue.FacialExpression.HAPPY.animationId, usedInterface, player_head_child) + sendAnimationOnInterface(player, FacialExpression.HAPPY.animationId, usedInterface, player_head_child) player.toggleWardrobe(true) component?.setCloseEvent{pl,_ -> diff --git a/Server/src/main/content/global/handlers/iface/JewelleryInterface.java b/Server/src/main/content/global/handlers/iface/JewelleryInterface.java index 11eb23415..ea45a538a 100644 --- a/Server/src/main/content/global/handlers/iface/JewelleryInterface.java +++ b/Server/src/main/content/global/handlers/iface/JewelleryInterface.java @@ -165,6 +165,7 @@ public final class JewelleryInterface extends ComponentPlugin { amount = 5; break; case 124: + /* No need to calculate the maximum amount here, because the make() function will perform that step if (data.name().contains("GOLD")) { amount = player.getInventory().getAmount(new Item(GOLD_BAR)); } else { @@ -178,6 +179,8 @@ public final class JewelleryInterface extends ComponentPlugin { amount = first; } } + */ + amount = 99; break; case 199: final JewelleryItem d = data; diff --git a/Server/src/main/content/global/handlers/iface/LogoutInterface.java b/Server/src/main/content/global/handlers/iface/LogoutInterface.java index ba1784c8f..d3a7c9c1a 100644 --- a/Server/src/main/content/global/handlers/iface/LogoutInterface.java +++ b/Server/src/main/content/global/handlers/iface/LogoutInterface.java @@ -7,6 +7,7 @@ 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. @@ -31,10 +32,11 @@ public final class LogoutInterface extends ComponentPlugin { player.getPacketDispatch().sendMessage("You can't log out until 10 seconds after the end of combat."); return true; } - if (player.getAttribute("logoutDelay", 0) < GameWorld.getTicks()) { - player.getPacketDispatch().sendLogout(); - player.setAttribute("logoutDelay", GameWorld.getTicks() + 3); - } + 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 index e29e7f114..6832efb43 100644 --- a/Server/src/main/content/global/handlers/iface/MagicBookInterface.java +++ b/Server/src/main/content/global/handlers/iface/MagicBookInterface.java @@ -14,6 +14,9 @@ import core.game.node.entity.player.link.SpellBookManager.SpellBook; import core.game.world.GameWorld; import core.plugin.Plugin; +import content.data.Quests; +import static core.api.ContentAPIKt.hasRequirement; + /** * Represents the magic book interface handling of non-combat spells. * @author 'Vexia @@ -42,10 +45,44 @@ public final class MagicBookInterface extends ComponentPlugin { ? SpellBook.ANCIENT : SpellBook.LUNAR; + // Snowscape: Allow switching between unlocked spellbooks with the magic interface sort buttons + if (spellBook == SpellBook.MODERN) { + if (button == 65 && swapSpellBook(player, SpellBook.ANCIENT)) { + return true; + } else if (button == 66 && swapSpellBook(player, SpellBook.LUNAR)) { + return true; + } + } else if (spellBook == SpellBook.ANCIENT) { + if (button == 29 && swapSpellBook(player, SpellBook.MODERN)) { + return true; + } else if (button == 31 && swapSpellBook(player, SpellBook.LUNAR)) { + return true; + } + } else if (spellBook == SpellBook.LUNAR) { + if (button == 40 && swapSpellBook(player, SpellBook.MODERN)) { + return true; + } else if (button == 41 && swapSpellBook(player, SpellBook.ANCIENT)) { + return true; + } + } + + SpellListeners.run(button, SpellListener.NONE, SpellUtils.getBookFromInterface(component.getId()),player,null); boolean result = MagicSpell.castSpell(player, spellBook, button, player); - player.dispatch(new SpellCastEvent(spellBook, button)); return result; } + + // Snowscape custom function + private boolean swapSpellBook(final Player player, SpellBook spellBook) { + if (spellBook == SpellBook.ANCIENT && !hasRequirement(player, Quests.DESERT_TREASURE)) { + return false; + } else if (spellBook == SpellBook.LUNAR && !hasRequirement(player, Quests.LUNAR_DIPLOMACY)) { + return false; + } + player.getSpellBookManager().setSpellBook(spellBook); + player.getSpellBookManager().update(player); + return true; + } } + diff --git a/Server/src/main/content/global/handlers/iface/MakeOverInterface.kt b/Server/src/main/content/global/handlers/iface/MakeOverInterface.kt index 9c911007d..58aaea101 100644 --- a/Server/src/main/content/global/handlers/iface/MakeOverInterface.kt +++ b/Server/src/main/content/global/handlers/iface/MakeOverInterface.kt @@ -33,8 +33,8 @@ class MakeOverInterface : ComponentPlugin(){ player.packetDispatch.sendNpcOnInterface(1,component.id, MALE_CHILD_ID) player.packetDispatch.sendNpcOnInterface(5,component.id, FEMALE_CHILD_ID) //Send chathead animations to interface - player.packetDispatch.sendAnimationInterface(core.game.dialogue.FacialExpression.SILENT.animationId,component.id, MALE_CHILD_ID) - player.packetDispatch.sendAnimationInterface(core.game.dialogue.FacialExpression.SILENT.animationId,component.id, FEMALE_CHILD_ID) + player.packetDispatch.sendAnimationInterface(FacialExpression.SILENT.animationId,component.id, MALE_CHILD_ID) + player.packetDispatch.sendAnimationInterface(FacialExpression.SILENT.animationId,component.id, FEMALE_CHILD_ID) //Check for makeover voucher and then change interface text if it's there if(player.inventory.containsAtLeastOneItem(Items.MAKEOVER_VOUCHER_5606)){ diff --git a/Server/src/main/content/global/handlers/iface/MysticStaffEnchantingPlugin.java b/Server/src/main/content/global/handlers/iface/MysticStaffEnchantingPlugin.java index af798008d..c8d752141 100644 --- a/Server/src/main/content/global/handlers/iface/MysticStaffEnchantingPlugin.java +++ b/Server/src/main/content/global/handlers/iface/MysticStaffEnchantingPlugin.java @@ -20,7 +20,6 @@ import java.util.HashMap; 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), @@ -28,7 +27,8 @@ public final class MysticStaffEnchantingPlugin extends ComponentPlugin { 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); + MUD(Items.MYSTIC_MUD_STAFF_6563, Items.MUD_BATTLESTAFF_6562, 26), + STEAM(Items.MYSTIC_STEAM_STAFF_11738, Items.STEAM_BATTLESTAFF_11736, 27); public final int enchanted; public final int basic; @@ -68,7 +68,6 @@ public final class MysticStaffEnchantingPlugin extends ComponentPlugin { @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)); @@ -78,9 +77,10 @@ public final class MysticStaffEnchantingPlugin extends ComponentPlugin { player.getPacketDispatch().sendMessage("You don't have a" + (StringUtils.isPlusN(basicStaff.getName()) ? "n " : " ") + basicStaff.getName() + " to enchant."); return true; } + int cost = player.getEquipment().contains(Items.SEERS_HEADBAND_14631, 1)? 27000 : 40000; 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!"); + player.getDialogueInterpreter().sendDialogues(389, null, "I need " + String.format("%,d", cost) + " coins for materials. Come", "back when you have the money!"); return true; } if (player.getInventory().remove(basicStaff, new Item(995, cost))) { diff --git a/Server/src/main/content/global/handlers/iface/OrbViewingInterface.java b/Server/src/main/content/global/handlers/iface/OrbViewingInterface.java deleted file mode 100644 index aa21b7cce..000000000 --- a/Server/src/main/content/global/handlers/iface/OrbViewingInterface.java +++ /dev/null @@ -1,181 +0,0 @@ -package content.global.handlers.iface; - -import core.ServerConstants; -import core.cache.def.impl.SceneryDefinition; -import core.game.component.CloseEvent; -import core.game.component.Component; -import core.game.component.ComponentDefinition; -import core.game.component.ComponentPlugin; -import core.game.interaction.OptionHandler; -import core.game.node.Node; -import core.game.node.entity.player.Player; -import core.game.system.task.Pulse; -import core.game.world.map.Location; -import core.plugin.Initializable; -import core.plugin.Plugin; -import core.plugin.ClassScanner; - -/** - * Handles an orb viewing interface. - * @author 'Vexia - * @author Emperor - * @version 1.0 - */ -@Initializable -public final class OrbViewingInterface extends ComponentPlugin { - - /* - * Order: South-west, South-east, North-west, North-east, Centre - */ - - /** - * The fight pit locations. - */ - private static final Location[] FIGHT_PITS = { Location.create(2388, 5138, 0), Location.create(2411, 5137, 0), Location.create(2409, 5158, 0), Location.create(2384, 5157, 0), Location.create(2398, 5150, 0) }; - - /** - * The clan war locations. - */ - private static final Location[] CLAN_WARS = { Location.create(3277, 3725, 0), Location.create(3315, 3725, 0), Location.create(3316, 3829, 0), Location.create(3277, 3827, 0), Location.create(3296, 3776, 0) }; - - /** - * The bounty hunter viewing orb locations. - */ - private static final Location[][] BOUNTY_HUNTER = { { // Low level crater - Location.create(2752, 5695, 0), Location.create(2816, 5695, 0), Location.create(2783, 5783, 0), Location.create(2826, 5785, 0), Location.create(2784, 5727, 0) }, { // Mid - // level - // crater - Location.create(3008, 5695, 0), Location.create(3072, 5695, 0), Location.create(3039, 5783, 0), Location.create(3082, 5785, 0), Location.create(3040, 5727, 0) }, { // High - // level - // crater - Location.create(3264, 5695, 0), Location.create(3328, 5695, 0), Location.create(3295, 5783, 0), Location.create(3338, 5785, 0), Location.create(3296, 5727, 0) } }; - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ComponentDefinition.put(374, this); - ComponentDefinition.put(649, this); - ClassScanner.definePlugin(new OptionHandler() { - @Override - public Plugin newInstance(Object arg) throws Throwable { - SceneryDefinition.forId(9391).getHandlers().put("option:look-into", this); - SceneryDefinition.forId(28194).getHandlers().put("option:look-into", this); - SceneryDefinition.forId(28209).getHandlers().put("option:view", this); - SceneryDefinition.forId(28210).getHandlers().put("option:view", this); - SceneryDefinition.forId(28211).getHandlers().put("option:view", this); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - int interfaceId = 649; - switch (node.getId()) { - case 9391: - interfaceId = 374; - player.setAttribute("viewing_orb", FIGHT_PITS); - break; - case 28194: - player.setAttribute("viewing_orb", CLAN_WARS); - break; - case 28209: - case 28210: - case 28211: - player.setAttribute("viewing_orb", BOUNTY_HUNTER[node.getId() - 28209]); - break; - } - viewOrb(player, interfaceId); - return true; - } - - @Override - public Location getDestination(Node node, Node n) { - switch (n.getId()) { - case 28194: - return n.getLocation().transform(1, 0, 0); - } - return null; - } - }); - return this; - } - - @Override - public boolean handle(Player player, Component component, int opcode, int button, int slot, int itemId) { - Location[] locations = player.getAttribute("viewing_orb"); - if (locations == null) { - return false; - } - if (button != 5) { - move(player, locations[15 - button]); - } else { - stopViewing(player, true); - } - return true; - } - - /** - * Method used to move to a viewing location. - * @param location the location. - */ - private void move(final Player player, final Location location) { - player.getLocks().lockMovement(100000000); - player.getProperties().setTeleportLocation(location); - if (player.getAppearance().getNpcId() == -1) { - player.getAppearance().transformNPC(-2); - player.getAppearance().sync(); - } - } - - /** - * Views an orb. - * @param player the player. - * @param interfaceId the interface id. - */ - private void viewOrb(final Player player, final int interfaceId) { - final Component component = new Component(interfaceId).setCloseEvent(new ViewCloseEvent()); - player.setAttribute("view-location", player.getLocation()); - player.getInterfaceManager().openSingleTab(component); - player.getPulseManager().run(new Pulse(1, player) { - @Override - public boolean pulse() { - return false; - } - - @Override - public void stop() { - super.stop(); - stopViewing(player, true); - } - }); - } - - /** - * Ends the viewing session. - * @param player the player. - * @param close if we should close the interface. - */ - private static void stopViewing(final Player player, boolean close) { - if (close) { - player.getInterfaceManager().closeSingleTab(); - } - player.removeAttribute("viewing_orb"); - player.unlock(); - player.getAppearance().transformNPC(-1); - player.getAppearance().sync(); - player.getPulseManager().clear(); - player.getProperties().setTeleportLocation(player.getAttribute("view-location", ServerConstants.HOME_LOCATION)); - } - - /** - * Handles the close event of a viewing interface. - * @author 'Vexia - */ - public static final class ViewCloseEvent implements CloseEvent { - - @Override - public boolean close(Player player, Component c) { - stopViewing(player, false); - return true; - } - - } -} diff --git a/Server/src/main/content/global/handlers/iface/PrayerTabInterface.java b/Server/src/main/content/global/handlers/iface/PrayerTabInterface.java index b1c9de0db..2c31b11ce 100644 --- a/Server/src/main/content/global/handlers/iface/PrayerTabInterface.java +++ b/Server/src/main/content/global/handlers/iface/PrayerTabInterface.java @@ -8,6 +8,9 @@ import core.game.node.entity.player.link.prayer.PrayerType; import core.plugin.Initializable; import core.plugin.Plugin; +import static core.api.ContentAPIKt.hasRequirement; +import content.data.Quests; + /** * Represents the prayer interface. * @author 'Vexia @@ -24,6 +27,9 @@ public final class PrayerTabInterface extends ComponentPlugin { @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, Quests.KINGS_RANSOM)) + return true; if (type == null) { return true; } diff --git a/Server/src/main/content/global/handlers/iface/QuestTabInterface.java b/Server/src/main/content/global/handlers/iface/QuestTabInterface.java index 313e98b30..90d580af2 100644 --- a/Server/src/main/content/global/handlers/iface/QuestTabInterface.java +++ b/Server/src/main/content/global/handlers/iface/QuestTabInterface.java @@ -41,13 +41,13 @@ public class QuestTabInterface extends ComponentPlugin { break; default: // if (GameWorld.isEconomyWorld()) { - //System.out.println("Quest button: " + button); + 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; } @@ -68,4 +68,4 @@ public class QuestTabInterface extends ComponentPlugin { return true; } -} \ No newline at end of file +} diff --git a/Server/src/main/content/global/handlers/iface/QuestTabUtils.kt b/Server/src/main/content/global/handlers/iface/QuestTabUtils.kt new file mode 100644 index 000000000..3af184404 --- /dev/null +++ b/Server/src/main/content/global/handlers/iface/QuestTabUtils.kt @@ -0,0 +1,245 @@ +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.* +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 + } +} diff --git a/Server/src/main/content/global/handlers/iface/RulesAndInfo.kt b/Server/src/main/content/global/handlers/iface/RulesAndInfo.kt index f52b725e6..420c70d71 100644 --- a/Server/src/main/content/global/handlers/iface/RulesAndInfo.kt +++ b/Server/src/main/content/global/handlers/iface/RulesAndInfo.kt @@ -34,7 +34,7 @@ object RulesAndInfo { val pin = getAttribute(player, "rules:pin", RandomFunction.random(1000,9999)) setAttribute(player, "/save:rules:pin", pin) - var ln = setBaseRulesAndInfo(player); + var ln = setBaseRulesAndInfo(player) setInterfaceText(player, "If you agree to the above, type ::confirmrules $pin", 384, ln++) setInterfaceText(player, "", 384, ln) diff --git a/Server/src/main/content/global/handlers/iface/ScrollInterface.kt b/Server/src/main/content/global/handlers/iface/ScrollInterface.kt new file mode 100644 index 000000000..5e9bfc26d --- /dev/null +++ b/Server/src/main/content/global/handlers/iface/ScrollInterface.kt @@ -0,0 +1,47 @@ +package content.global.handlers.iface + +import core.api.closeInterface +import core.api.openInterface +import core.game.node.entity.player.Player +import org.rs09.consts.Components + +/** + * Interface listener for Scrolls + * Use this to set up a simple scroll display. + * + * Currently, scrolls do not happen to have any button interactions, so no listeners are here yet. + * This is generally a helper class until certain scrolls require interactions. + * + * @author ovenbreado + */ +class ScrollInterface { + + companion object { + /** This is a 15-Lines scroll */ + private val MESSAGESCROLL_220_LINE_IDS = arrayOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15) + + fun scrollSetup(player: Player, scrollComponent: Int, contents: Array) { + closeInterface(player) // Important: Close previous interfaces. + if (scrollComponent == Components.MESSAGESCROLL_220) { + openInterface(player, Components.MESSAGESCROLL_220) + setPageContent(player, Components.MESSAGESCROLL_220, MESSAGESCROLL_220_LINE_IDS, contents) + } + } + + /** Set text contents of scroll */ + fun setPageContent(player: Player, componentId: Int, scrollLineIds: Array, contents: Array) { + for (line in contents) { + // This is to prevent error child lines being set and crashing the client. + if (scrollLineIds.contains(line.child)) { + player.packetDispatch.sendString(line.message, componentId, line.child) + } + } + } + } +} + +/** Constructs a new ScrollLine object. */ +class ScrollLine( + val message: String, + val child: Int +) \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/iface/SettingTabInterface.java b/Server/src/main/content/global/handlers/iface/SettingTabInterface.java index 9c660554b..c8bac8cb7 100644 --- a/Server/src/main/content/global/handlers/iface/SettingTabInterface.java +++ b/Server/src/main/content/global/handlers/iface/SettingTabInterface.java @@ -1,5 +1,7 @@ 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; @@ -78,7 +80,7 @@ public class SettingTabInterface extends ComponentPlugin { break; case 8:// house p.getInterfaceManager().close(); - p.getConfigManager().set(261, p.getConfigManager().get(261) & 0x1); + setVarp(p, 261, getVarp(p, 261) & 0x1); p.getInterfaceManager().openSingleTab(new Component(398)); break; } diff --git a/Server/src/main/content/global/handlers/iface/ShoppingPlugin.java b/Server/src/main/content/global/handlers/iface/ShoppingPlugin.java index 6f05c691f..175f20f33 100644 --- a/Server/src/main/content/global/handlers/iface/ShoppingPlugin.java +++ b/Server/src/main/content/global/handlers/iface/ShoppingPlugin.java @@ -51,7 +51,7 @@ public final class ShoppingPlugin extends ComponentPlugin { return true; } if (viewer == null) { - System.out.println("shop is null"); + return true; } final Container container = button == 0 ? player.getInventory() : viewer.getShop().getContainer(viewer.getTabIndex()); diff --git a/Server/src/main/content/global/handlers/iface/SkillInterface.java b/Server/src/main/content/global/handlers/iface/SkillInterface.java index b9d5e5da4..a0ac47d21 100644 --- a/Server/src/main/content/global/handlers/iface/SkillInterface.java +++ b/Server/src/main/content/global/handlers/iface/SkillInterface.java @@ -7,6 +7,8 @@ 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 @@ -23,9 +25,8 @@ public final class SkillInterface extends ComponentPlugin { @Override public boolean handle(Player player, Component component, int opcode, int button, int slot, int itemId) { - player.varpManager.get(965) - .setVarbit(0,player.getAttribute("skillMenu",-1)) - .setVarbit(10, button - 10).send(player); + 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 index f2255d97c..9dba5877b 100644 --- a/Server/src/main/content/global/handlers/iface/SkillTabInterface.java +++ b/Server/src/main/content/global/handlers/iface/SkillTabInterface.java @@ -10,6 +10,8 @@ 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 @@ -35,12 +37,12 @@ public final class SkillTabInterface extends ComponentPlugin { if (p.getAttribute("levelup:" + config.getSkillId(), false)) { p.removeAttribute("levelup:" + config.getSkillId()); LevelUp.sendFlashingIcons(p, -1); - p.getConfigManager().set(1230, ADVANCE_CONFIGS[config.getSkillId()]); + setVarp(p, 1230, ADVANCE_CONFIGS[config.getSkillId()]); p.getInterfaceManager().open(new Component(741)); } else { p.getPulseManager().clear(); p.getInterfaceManager().open(new Component(499)); - p.getConfigManager().set(965, config.getConfig()); + setVarp(p, 965, config.getConfig()); p.getAttributes().put("skillMenu", config.getConfig()); } } else { diff --git a/Server/src/main/content/global/handlers/iface/SmeltingInterface.java b/Server/src/main/content/global/handlers/iface/SmeltingInterface.java index 4032ff524..b7fadd28c 100644 --- a/Server/src/main/content/global/handlers/iface/SmeltingInterface.java +++ b/Server/src/main/content/global/handlers/iface/SmeltingInterface.java @@ -49,7 +49,7 @@ public class SmeltingInterface extends ComponentPlugin { * @author 'Vexia */ public enum BarButton { - BRONZE_1(16, Bar.BRONZE, 1), BRONZE_5(15, Bar.BRONZE, 5), BRONZE_10(14, Bar.BRONZE, 10), BRONZE_X(13, Bar.BRONZE, -1), BLURITE_1(20, Bar.BLURITE, 1), BLURITE_5(19, Bar.BLURITE, 5), BLURITE_10(18, Bar.BLURITE, 10), BLURITE_X(17, Bar.BLURITE, -1), IRON_1(24, Bar.IRON, 1), IRON_5(23, Bar.IRON, 5), IRON_10(22, Bar.IRON, 10), IRON_X(21, Bar.IRON, -1), SILVER_1(28, Bar.SILVER, 1), SILVER_5(27, Bar.SILVER, 5), SILVER_10(26, Bar.SILVER, 10), SILVER_X(25, Bar.SILVER, -1), STEEL_1(32, Bar.STEEL, 1), STEEL_5(31, Bar.STEEL, 5), STEEL_10(30, Bar.STEEL, 10), STEEL_X(29, Bar.STEEL, -1), GOLD_1(36, Bar.GOLD, 1), GOLD_5(35, Bar.GOLD, 5), GOLD_10(34, Bar.GOLD, 10), GOLD_X(33, Bar.GOLD, -1), MITHRIL_1(40, Bar.MITHRIL, 1), MITHRIL_5(39, Bar.MITHRIL, 5), MITHRIL_10(38, Bar.MITHRIL, 10), MITHRIL_X(37, Bar.MITHRIL, -1), ADAMANT_1(44, Bar.ADAMANT, 1), ADAMANT_5(43, Bar.ADAMANT, 5), ADAMANT_10(42, Bar.ADAMANT, 10), ADAMANT_X(41, Bar.ADAMANT, -1), RUNE_1(48, Bar.RUNITE, 1), RUNE_5(47, Bar.RUNITE, 5), RUNE_10(46, Bar.RUNITE, 10), RUNE_X(45, Bar.RUNITE, -1); + BRONZE_1(16, Bar.BRONZE, 1), BRONZE_5(15, Bar.BRONZE, 5), BRONZE_10(14, Bar.BRONZE, 30), BRONZE_X(13, Bar.BRONZE, -1), BLURITE_1(20, Bar.BLURITE, 1), BLURITE_5(19, Bar.BLURITE, 5), BLURITE_10(18, Bar.BLURITE, 30), BLURITE_X(17, Bar.BLURITE, -1), IRON_1(24, Bar.IRON, 1), IRON_5(23, Bar.IRON, 5), IRON_10(22, Bar.IRON, 30), IRON_X(21, Bar.IRON, -1), SILVER_1(28, Bar.SILVER, 1), SILVER_5(27, Bar.SILVER, 5), SILVER_10(26, Bar.SILVER, 30), SILVER_X(25, Bar.SILVER, -1), STEEL_1(32, Bar.STEEL, 1), STEEL_5(31, Bar.STEEL, 5), STEEL_10(30, Bar.STEEL, 30), STEEL_X(29, Bar.STEEL, -1), GOLD_1(36, Bar.GOLD, 1), GOLD_5(35, Bar.GOLD, 5), GOLD_10(34, Bar.GOLD, 30), GOLD_X(33, Bar.GOLD, -1), MITHRIL_1(40, Bar.MITHRIL, 1), MITHRIL_5(39, Bar.MITHRIL, 5), MITHRIL_10(38, Bar.MITHRIL, 30), MITHRIL_X(37, Bar.MITHRIL, -1), ADAMANT_1(44, Bar.ADAMANT, 1), ADAMANT_5(43, Bar.ADAMANT, 5), ADAMANT_10(42, Bar.ADAMANT, 30), ADAMANT_X(41, Bar.ADAMANT, -1), RUNE_1(48, Bar.RUNITE, 1), RUNE_5(47, Bar.RUNITE, 5), RUNE_10(46, Bar.RUNITE, 30), RUNE_X(45, Bar.RUNITE, -1); /** * Constructs a new {@code BarButton} {@code Object}. diff --git a/Server/src/main/content/global/handlers/iface/ThessaliaInterface.kt b/Server/src/main/content/global/handlers/iface/ThessaliaInterface.kt index ec6dcf98e..4c98f7e0b 100644 --- a/Server/src/main/content/global/handlers/iface/ThessaliaInterface.kt +++ b/Server/src/main/content/global/handlers/iface/ThessaliaInterface.kt @@ -1,5 +1,6 @@ package content.global.handlers.iface +import core.api.playJingle import core.game.component.Component import core.game.component.ComponentDefinition import core.game.component.ComponentPlugin @@ -154,6 +155,7 @@ class ThessaliaInterface : ComponentPlugin(){ component.setCloseEvent{pl,_ -> pl?.toggleWardrobe(false) pl.attributes.remove("thes-type") + playJingle(pl, 266) if(!pl.getAttribute("thes-paid",false)){ pl.appearance.torso.changeLook(pl.getAttribute("orig-torso",0)) pl.appearance.torso.changeColor(pl.getAttribute("orig-torso-color",0)) diff --git a/Server/src/main/content/global/handlers/iface/WorldMapInterface.java b/Server/src/main/content/global/handlers/iface/WorldMapInterface.java index 756600db3..113c28bfa 100644 --- a/Server/src/main/content/global/handlers/iface/WorldMapInterface.java +++ b/Server/src/main/content/global/handlers/iface/WorldMapInterface.java @@ -31,7 +31,7 @@ public final class WorldMapInterface extends ComponentPlugin { player.updateSceneGraph(true); return true; default: - //SystemLogger.logErr(this.getClass(), "World map: buttonid: " + button + ", opcode: " + opcode + ", slot: " + slot); + //log(this.getClass(), Log.ERR, "World map: buttonid: " + button + ", opcode: " + opcode + ", slot: " + slot); return true; } } 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 bb55b6d2e..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. @@ -54,12 +54,10 @@ class BankInterface : InterfaceListener { private const val OP_SET_TAB = 155 private const val OP_COLLAPSE_TAB = 196 - private const val THRESHOLD_TO_DISPLAY_EXACT_QUANTITY_ON_EXAMINE = 100000; + private const val THRESHOLD_TO_DISPLAY_EXACT_QUANTITY_ON_EXAMINE = 100000 } 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 a6fa904c9..9b615b305 100644 --- a/Server/src/main/content/global/handlers/iface/ge/GrandExchangeInterface.java +++ b/Server/src/main/content/global/handlers/iface/ge/GrandExchangeInterface.java @@ -8,7 +8,6 @@ import core.game.component.ComponentPlugin; import core.game.ge.GEGuidePrice; import core.game.ge.GEItemSet; import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.audio.Audio; import core.game.node.item.Item; import core.game.system.task.Pulse; import core.net.packet.PacketRepository; @@ -18,8 +17,10 @@ import core.plugin.Initializable; import core.plugin.Plugin; import core.game.ge.GrandExchangeOffer; import core.game.ge.GrandExchangeRecords; -import content.region.kandarin.feldip.gutanoth.handlers.BogrogPouchSwapper; import core.game.world.GameWorld; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.playAudio; /** * Handles the Grand Exchange interface options. @@ -111,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; } @@ -132,12 +133,20 @@ public class GrandExchangeInterface extends ComponentPlugin { if (slot < 0 || slot >= (inventory ? 28 : GEItemSet.values().length)) { return; } - GEItemSet set = GEItemSet.values()[slot]; - Item item = inventory ? player.getInventory().get(slot) : new Item(set.getItemId()); - if (item == null) { - return; - } - if (opcode != 127 && inventory && ((set = GEItemSet.forId(item.getId())) == null) && !BogrogPouchSwapper.handle(player,opcode,slot,itemId)) { + + Item item; + GEItemSet set; + if (inventory) { + item = player.getInventory().get(slot); + if (item == null) return; + set = GEItemSet.forId(item.getId()); + if (set == null) return; + } else { + set = GEItemSet.values()[slot]; + item = new Item(set.getItemId()); + } + + if (opcode != 127 && inventory && set == null) { player.getPacketDispatch().sendMessage("This isn't a set item."); return; } @@ -171,7 +180,7 @@ public class GrandExchangeInterface extends ComponentPlugin { player.getInventory().refresh(); player.getPacketDispatch().sendMessage("You successfully traded your item components for a set!"); } - player.getAudioManager().send(new Audio(4044, 1, 1)); + playAudio(player, Sounds.GE_TRADE_OK_4044); PacketRepository.send(ContainerPacket.class, new ContainerContext(player, -1, -2, player.getAttribute("container-key", 93), player.getInventory(), false)); break; case 155: 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 c952e9086..eba0a2435 100644 --- a/Server/src/main/content/global/handlers/iface/ge/StockMarket.kt +++ b/Server/src/main/content/global/handlers/iface/ge/StockMarket.kt @@ -4,7 +4,6 @@ import core.api.* import core.game.component.Component import core.game.ge.OfferState import core.game.node.entity.player.Player -import core.game.node.entity.player.link.audio.Audio import core.game.node.item.Item import core.net.packet.PacketRepository import core.net.packet.context.ConfigContext @@ -17,7 +16,10 @@ import core.game.ge.GrandExchangeOffer import core.game.ge.GrandExchangeRecords import core.game.ge.PriceIndex import core.game.interaction.InterfaceListener +import core.tools.Log import core.tools.SystemLogger +import org.rs09.consts.Sounds +import kotlin.math.min /** * Handles the grand exchange interface (Stock Market) @@ -29,7 +31,7 @@ class StockMarket : InterfaceListener { player.packetDispatch.sendInterfaceConfig(105, 193, true) player.packetDispatch.sendIfaceSettings(6, 211, 105, -1, -1) player.packetDispatch.sendIfaceSettings(6, 209, 105, -1, -1) - player.varpManager.get(1112).setVarbit(0, -1).send(player) + setVarp(player, 1112, -1) return@onOpen true } @@ -96,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) @@ -158,15 +160,11 @@ class StockMarket : InterfaceListener { 183 -> updateOfferValue(player, tempOffer, (GrandExchange.getRecommendedPrice(tempOffer.itemID) * 1.05).toInt()) 171 -> updateOfferValue(player, tempOffer, tempOffer.offeredValue - 1) 173 -> updateOfferValue(player, tempOffer, tempOffer.offeredValue + 1) - 185 -> sendInputDialogue(player, false, "Enter the amount:"){value -> + 185 -> sendInputDialogue(player, InputType.AMOUNT, "Enter the amount:"){value -> if (player.interfaceManager.chatbox.id == 389) { player.interfaceManager.openChatbox(Components.OBJDIALOG_389) } var s = value.toString() - s = s.replace("k", "000") - s = s.replace("K", "000") - s = s.replace("m", "000000") - s = s.replace("M", "000000") updateOfferValue(player, tempOffer, s.toInt()) setAttribute(player, "ge-temp", tempOffer) } @@ -186,20 +184,20 @@ class StockMarket : InterfaceListener { fun updateOfferValue(player: Player, offer: GrandExchangeOffer, newAmt: Int) { offer.offeredValue = newAmt - player.varpManager.get(1111).setVarbit(0, newAmt).send(player) + setVarp(player, 1111, newAmt) } fun updateOfferAmount(player: Player, offer: GrandExchangeOffer, newAmt: Int) { offer.amount = newAmt - player.varpManager.get(1110).setVarbit(0, newAmt).send(player) + setVarp(player, 1110, newAmt) } fun abortOffer(player: Player, offer: GrandExchangeOffer?) { if(offer == null) { - SystemLogger.logWarn(this::class.java, "Opened offer was null and was attempted to be aborted!") + log(this::class.java, Log.WARN, "Opened offer was null and was attempted to be aborted!") return } sendMessage(player, "Abort request acknowledged. Please be aware that your offer may") @@ -207,7 +205,7 @@ class StockMarket : InterfaceListener { if(!offer.isActive) { - SystemLogger.logWarn(this::class.java, "Offer ${offer.uid}[${offer.index}]: ${if(offer.sell) "s" else "b"} ${offer.itemID}x ${offer.amount} was NO LONGER active when abort attempted") + log(this::class.java, Log.WARN, "Offer ${offer.uid}[${offer.index}]: ${if(offer.sell) "s" else "b"} ${offer.itemID}x ${offer.amount} was NO LONGER active when abort attempted") return } offer.offerState = OfferState.ABORTED @@ -219,19 +217,28 @@ class StockMarket : InterfaceListener { offer.visualize(player) } - fun confirmOffer(player: Player, offer: GrandExchangeOffer, index: Int) + enum class OfferConfirmResult { + Success, + ZeroCoins, + TooManyCoins, + NotEnoughItemsOrCoins, + ItemRemovalFailure, + OfferPlacementError + } + + fun confirmOffer(player: Player, offer: GrandExchangeOffer, index: Int) : OfferConfirmResult { if(offer.offeredValue < 1) { - playAudio(player, Audio(4039, 1, 1)) + playAudio(player, Sounds.GE_TRADE_ERROR_4039) sendMessage(player, "You can't make an offer for 0 coins.") - return + return OfferConfirmResult.ZeroCoins } if(offer.amount > Int.MAX_VALUE / offer.offeredValue) { - playAudio(player, Audio(4039, 1, 1)) + playAudio(player, Sounds.GE_TRADE_ERROR_4039) sendMessage(player, "You can't ${if(offer.sell) "sell" else "buy"} this much!") - return + return OfferConfirmResult.TooManyCoins } offer.index = index if(offer.sell) @@ -239,30 +246,28 @@ class StockMarket : InterfaceListener { val maxAmt = getInventoryAmount(player, offer.itemID) if(offer.amount > maxAmt) { - playAudio(player, Audio(4039, 1, 1)) + playAudio(player, Sounds.GE_TRADE_ERROR_4039) sendMessage(player, "You do not have enough of this item in your inventory to cover the") sendMessage(player, "offer.") - return - } - var item: Item - val amountLeft: Int = offer.amount - amountInInventory(player, offer.itemID) - val remove = removeItem(player, Item(offer.itemID, offer.amount).also { item = it }) - var note: Int - if (amountLeft > 0) { - if (item.noteChange.also { note = it } > 0) { - removeItem(player, Item(note, amountLeft)) - } else if (remove) { - addItem(player, offer.itemID, offer.amount - amountLeft) - return - } + return OfferConfirmResult.NotEnoughItemsOrCoins } + val amountUnnoted = min(amountInInventory(player, offer.itemID), offer.amount) + val amountLeft = offer.amount - amountUnnoted + val removedUnnoted = if (amountUnnoted > 0) removeItem(player, Item(offer.itemID, amountUnnoted)) else true + val removeNoted = if (amountLeft > 0) removeItem(player, Item(itemDefinition(offer.itemID).noteId, amountLeft)) else true + if (!removedUnnoted || !removeNoted) return OfferConfirmResult.ItemRemovalFailure if(GrandExchange.dispatch(player, offer)) { player.removeAttribute("ge-temp") } else { - addItem(player, item.id, item.amount) + if (amountUnnoted > 0) + addItem(player, offer.itemID, offer.amount - amountLeft) + if (amountLeft > 0) + addItem(player, itemDefinition(offer.itemID).noteId, amountLeft) + sendMessage(player, "Unable to place GE offer. Please try again.") + return OfferConfirmResult.OfferPlacementError } } else @@ -270,18 +275,19 @@ class StockMarket : InterfaceListener { val total = offer.amount * offer.offeredValue if(!inInventory(player, 995, total)) { - playAudio(player, Audio(4039, 1, 1)) + playAudio(player, Sounds.GE_TRADE_ERROR_4039) sendMessage(player, "You do not have enough coins to cover the offer.") - return + return OfferConfirmResult.NotEnoughItemsOrCoins } if(GrandExchange.dispatch(player, offer) && removeItem(player, Item(995, total))) { player.removeAttribute("ge-temp") } } - playAudio(player, Audio(4043, 1, 1)) + playAudio(player, Sounds.GE_PLACE_ITEM_4043) offer.visualize(player) toMainInterface(player) + return OfferConfirmResult.Success } fun getInventoryAmount(player: Player, itemId: Int): Int { @@ -319,14 +325,14 @@ class StockMarket : InterfaceListener { lowPrice = (recommendedPrice * 0.95).toInt() highPrice = (recommendedPrice * 1.05).toInt() } - player.varpManager.get(1109).setVarbit(0, offer?.itemID ?: -1).send(player) - player.varpManager.get(1110).setVarbit(0, offer?.amount ?: 0).send(player) - player.varpManager.get(1111).setVarbit(0, offer?.offeredValue ?: 0).send(player) - player.varpManager.get(1112).setVarbit(0, index).send(player) - player.varpManager.get(1113).setVarbit(0, if (isSale) 1 else 0).send(player) - player.varpManager.get(1114).setVarbit(0, recommendedPrice).send(player) - player.varpManager.get(1115).setVarbit(0, lowPrice).send(player) - player.varpManager.get(1116).setVarbit(0, highPrice).send(player) + setVarp(player, 1109, offer?.itemID ?: -1) + setVarp(player, 1110, offer?.amount ?: 0) + setVarp(player, 1111, offer?.offeredValue ?: 0) + setVarp(player, 1112, index) + setVarp(player, 1113, if (isSale) 1 else 0) + setVarp(player, 1114, recommendedPrice) + setVarp(player, 1115, lowPrice) + setVarp(player, 1116, highPrice) if (offer != null) { PacketRepository.send( ContainerPacket::class.java, @@ -336,29 +342,41 @@ 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) { - SystemLogger.logWarn(this::class.java, "Offer withdraw[$index] is null!") + log(this::class.java, Log.WARN, "Offer withdraw[$index] is null!") 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, Audio(4039, 1, 1)) - 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 @@ -391,4 +409,4 @@ class StockMarket : InterfaceListener { } } -} \ No newline at end of file +} diff --git a/Server/src/main/content/global/handlers/item/BirdNestPlugin.java b/Server/src/main/content/global/handlers/item/BirdNestPlugin.java index 30d4e64de..5499ba690 100644 --- a/Server/src/main/content/global/handlers/item/BirdNestPlugin.java +++ b/Server/src/main/content/global/handlers/item/BirdNestPlugin.java @@ -12,7 +12,6 @@ import core.plugin.Plugin; * Handles the searching of a bird nest item. * @author Vexia */ -@Initializable public final class BirdNestPlugin extends OptionHandler { @Override diff --git a/Server/src/main/content/global/handlers/item/BirdNestScript.kt b/Server/src/main/content/global/handlers/item/BirdNestScript.kt new file mode 100644 index 000000000..fb34fb63a --- /dev/null +++ b/Server/src/main/content/global/handlers/item/BirdNestScript.kt @@ -0,0 +1,22 @@ +package content.global.handlers.item + +import content.data.tables.BirdNest +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 + +class BirdNestScript : InteractionListener { + val nestIds = BirdNest.values().map { it.nest.id }.toIntArray() + + override fun defineListeners() { + on(nestIds, IntType.ITEM, "search", handler = ::handleNest) + } + + private fun handleNest(player: Player, node: Node) : Boolean { + val nest = BirdNest.forNest(node as? Item ?: return false) + nest.search(player, node as? Item ?: return false) + return true + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/item/BookreadOption.java b/Server/src/main/content/global/handlers/item/BookreadOption.java deleted file mode 100644 index 02ad8f1a8..000000000 --- a/Server/src/main/content/global/handlers/item/BookreadOption.java +++ /dev/null @@ -1,74 +0,0 @@ -package content.global.handlers.item; - -import core.cache.def.impl.ItemDefinition; -import core.game.interaction.OptionHandler; -import core.game.node.Node; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; -import core.plugin.Initializable; -import core.plugin.Plugin; -import org.rs09.consts.Items; - -/** - * Represents the plugin used to handle the "read" option of a book. - * - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public class BookreadOption extends OptionHandler { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ItemDefinition.forId(292).getHandlers().put("option:read", this); - ItemDefinition.forId(757).getHandlers().put("option:read", this); - ItemDefinition.forId(1856).getHandlers().put("option:read", this); - ItemDefinition.forId(Items.BATTERED_BOOK_2886).getHandlers().put("option:read", this); - ItemDefinition.forId(Items.SLASHED_BOOK_9715).getHandlers().put("option:read", this); - ItemDefinition.forId(Items.VARMENS_NOTES_4616).getHandlers().put("option:read", this); - ItemDefinition.forId(9003).getHandlers().put("option:read", this); - ItemDefinition.forId(9004).getHandlers().put("option:read", this); - ItemDefinition.forId(11710).getHandlers().put("option:read", this); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - final int id = getDialId(((Item) node).getId()); - player.getInterfaceManager().close(); - return player.getDialogueInterpreter().open(id, node); - } - - @Override - public boolean isWalk() { - return false; - } - - /** - * Gets the dialogue id from the item id. - * - * @param item the item. - * @return the dial id. - */ - public int getDialId(int item) { - switch (item) { - case 757: - return 49610758; - case 9003: - return 49610759; - case Items.BATTERED_BOOK_2886: - return 49610760; - case Items.SLASHED_BOOK_9715: - return 49610761; - /*case 9004: - return 423943;*/ - case 11710: - return 2739823; - case 292: - return 183764; - case 1856: - return 387454; - } - return item; - } -} diff --git a/Server/src/main/content/global/handlers/item/CasketListener.kt b/Server/src/main/content/global/handlers/item/CasketListener.kt new file mode 100644 index 000000000..78d7d99be --- /dev/null +++ b/Server/src/main/content/global/handlers/item/CasketListener.kt @@ -0,0 +1,43 @@ +package content.global.handlers.item + +import core.api.* +import core.api.utils.WeightBasedTable +import core.api.utils.WeightedItem +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.item.Item +import core.tools.StringUtils +import org.rs09.consts.Items +import java.util.* +import kotlin.collections.ArrayList + +class CasketListener : InteractionListener { + companion object { + val loot = WeightBasedTable.create( + WeightedItem(Items.COINS_995, 20, 640, 55.0, false), + WeightedItem(Items.UNCUT_SAPPHIRE_1623, 1, 1, 32.0, false), + WeightedItem(Items.UNCUT_EMERALD_1621, 1, 1, 16.0, false), + WeightedItem(Items.UNCUT_RUBY_1619, 1, 1, 9.0, false), + WeightedItem(Items.UNCUT_DIAMOND_1617, 1, 1, 2.0, false), + WeightedItem(Items.COSMIC_TALISMAN_1454, 1, 1, 8.0, false), + WeightedItem(Items.LOOP_HALF_OF_A_KEY_987, 1, 1, 1.0, false), + WeightedItem(Items.TOOTH_HALF_OF_A_KEY_985, 1, 1, 1.0, false) + ) + } + + override fun defineListeners() { + on(Items.CASKET_405, IntType.ITEM, "open"){ player, node -> + val casket = node.asItem() + + if(removeItem(player, casket, Container.INVENTORY)) { + val finalLoot : ArrayList = loot.roll() + finalLoot.forEach { player.inventory.add(it) } + player.dialogueInterpreter.sendItemMessage(finalLoot[0], + "You open the casket. Inside you find " + + (if (finalLoot[0].amount > 1) "some" else if (StringUtils.isPlusN(finalLoot[0].name)) "an" else "a") + + " " + finalLoot[0].name.lowercase(Locale.getDefault()) + ".") + } + return@on true + } + } +} diff --git a/Server/src/main/content/global/handlers/item/CasketPlugin.java b/Server/src/main/content/global/handlers/item/CasketPlugin.java deleted file mode 100644 index 5311e87af..000000000 --- a/Server/src/main/content/global/handlers/item/CasketPlugin.java +++ /dev/null @@ -1,78 +0,0 @@ -package content.global.handlers.item; - -import java.util.ArrayList; -import java.util.List; - -import static core.api.ContentAPIKt.*; -import core.cache.def.impl.ItemDefinition; -import core.game.interaction.OptionHandler; -import core.game.node.Node; -import core.game.node.entity.player.Player; -import core.game.node.item.ChanceItem; -import core.game.node.item.Item; -import core.plugin.Initializable; -import core.plugin.Plugin; -import core.tools.RandomFunction; -import core.tools.StringUtils; -import org.rs09.consts.Items; -import core.api.utils.WeightBasedTable; -import core.api.utils.WeightedItem; - -/** - * Represents the casket handling plugin. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class CasketPlugin extends OptionHandler { - - /** - * Represents the casket rewards. - */ - private WeightBasedTable table = WeightBasedTable.create( - new WeightedItem(Items.COINS_995, 20, 640, 55, false), - new WeightedItem(Items.UNCUT_SAPPHIRE_1623, 1, 1, 32, false), - new WeightedItem(Items.UNCUT_EMERALD_1621, 1, 1, 16, false), - new WeightedItem(Items.UNCUT_RUBY_1619, 1, 1, 9, false), - new WeightedItem(Items.UNCUT_DIAMOND_1617, 1, 1, 2, false), - new WeightedItem(Items.COSMIC_TALISMAN_1454, 1, 1, 8, false), - new WeightedItem(Items.LOOP_HALF_OF_A_KEY_987, 1, 1, 1, false), - new WeightedItem(Items.TOOTH_HALF_OF_A_KEY_985, 1, 1, 1, false) - ); - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ItemDefinition.forId(405).getHandlers().put("option:open", this); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - final Item reward = table.roll(player).get(0); - player.getInventory().remove((Item) node); - player.getDialogueInterpreter().sendItemMessage(reward, "You open the casket. Inside you find " + (reward.getAmount() > 1 ? "some" : (StringUtils.isPlusN(reward.getName()) ? "an" : "a")) + " " + reward.getName().toLowerCase() + "."); - addItemOrDrop(player, reward.getId(), reward.getAmount()); - return true; - } - - /** - * Gets the chance item from the array. - * @param items the items. - * @return the chance item. - */ - private ChanceItem getChanceItem(ChanceItem[] items) { - final int chance = RandomFunction.random(100); - final List chances = new ArrayList<>(20); - for (ChanceItem c : items) { - if (chance > c.getChanceRate()) { - chances.add(c); - } - } - return chances.size() == 0 ? items[0] : chances.get(RandomFunction.random(chances.size())); - } - - @Override - public boolean isWalk() { - return false; - } -} diff --git a/Server/src/main/content/global/handlers/item/ConsumableListener.kt b/Server/src/main/content/global/handlers/item/ConsumableListener.kt new file mode 100644 index 000000000..e1da66439 --- /dev/null +++ b/Server/src/main/content/global/handlers/item/ConsumableListener.kt @@ -0,0 +1,51 @@ +package content.global.handlers.item + +import content.data.consumables.Consumables +import core.api.delayAttack +import core.api.getUsedOption +import core.api.getWorldTicks +import core.api.stopExecuting +import core.game.interaction.Clocks +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.world.GameWorld + +class ConsumableListener : InteractionListener { + override fun defineListeners() { + on(IntType.ITEM, "eat", "drink", handler = ::handleConsumable) + } + + private fun handleConsumable(player: Player, node: Node) : Boolean { + val consumable = Consumables.getConsumableById(node.id) ?: return true + + val food = getUsedOption(player) == "eat" + val isIgnoreMainClock = consumable.isIgnoreMainClock + + if (food) { + if (isIgnoreMainClock && player.clocks[Clocks.NEXT_CONSUME] < GameWorld.ticks) { + consumable.consumable.consume(node as? Item ?: return true, player) + player.clocks[Clocks.NEXT_CONSUME] = getWorldTicks() + 2 + player.clocks[Clocks.NEXT_EAT] = getWorldTicks() + 2 + delayAttack(player, 3) + } else if (player.clocks[Clocks.NEXT_CONSUME] < getWorldTicks() && player.clocks[Clocks.NEXT_EAT] < getWorldTicks()) { + consumable.consumable.consume(node as? Item ?: return true, player) + player.clocks[Clocks.NEXT_EAT] = getWorldTicks() + 2 + delayAttack(player, 3) + } + } else { + if (isIgnoreMainClock && player.clocks[Clocks.NEXT_CONSUME] < getWorldTicks()) { + consumable.consumable.consume(node as? Item ?: return true, player) + player.clocks[Clocks.NEXT_CONSUME] = getWorldTicks() + 1 + player.clocks[Clocks.NEXT_DRINK] = getWorldTicks() + 1 + } else if (player.clocks[Clocks.NEXT_CONSUME] < getWorldTicks() && player.clocks[Clocks.NEXT_DRINK] < getWorldTicks()) { + consumable.consumable.consume(node as? Item ?: return true, player) + player.clocks[Clocks.NEXT_DRINK] = getWorldTicks() + 1 + } + } + + return true + } +} diff --git a/Server/src/main/content/global/handlers/item/ConsumableOptionPlugin.java b/Server/src/main/content/global/handlers/item/ConsumableOptionPlugin.java index 1eff2635b..3b74cd867 100644 --- a/Server/src/main/content/global/handlers/item/ConsumableOptionPlugin.java +++ b/Server/src/main/content/global/handlers/item/ConsumableOptionPlugin.java @@ -17,7 +17,6 @@ import core.plugin.Plugin; * @author Emperor * @version 1.0 */ -@Initializable public final class ConsumableOptionPlugin extends OptionHandler { @Override @@ -35,7 +34,7 @@ public final class ConsumableOptionPlugin extends OptionHandler { @Override public boolean handle(final Player player, final Node node, final String option) { - if (player.getLocks().isLocked(option)) { +/* if (player.getLocks().isLocked(option)) { return true; } boolean food = option.equals("eat"); @@ -61,7 +60,7 @@ public final class ConsumableOptionPlugin extends OptionHandler { if (food) { player.getProperties().getCombatPulse().delayNextAttack(3); } - lastEaten = node.asItem().getId(); + lastEaten = node.asItem().getId();*/ return true; } } diff --git a/Server/src/main/content/global/handlers/item/DestroyItemPlugin.java b/Server/src/main/content/global/handlers/item/DestroyItemPlugin.java deleted file mode 100644 index ced363027..000000000 --- a/Server/src/main/content/global/handlers/item/DestroyItemPlugin.java +++ /dev/null @@ -1,75 +0,0 @@ -package content.global.handlers.item; - -import core.game.dialogue.DialoguePlugin; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.audio.Audio; -import core.plugin.Initializable; -import core.game.node.item.Item; - -/** - * Represents the destroy item plugin. - * @author Vexia - * - */ -@Initializable -public final class DestroyItemPlugin extends DialoguePlugin { - - /** - * Represents the sound to send for destroying an item. - */ - private static final Audio SOUND = new Audio(4500, 1, 0); - - /** - * Represents the item parameter. - */ - private Item item; - - /** - * Constructs a new {@code DestroyItemPlugin} {@code Object}. - */ - public DestroyItemPlugin() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code DestroyItemPlugin} {@code Object}. - * @param player the player. - */ - public DestroyItemPlugin(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new DestroyItemPlugin(player); - } - - @Override - public boolean open(Object... args) { - item = (Item) args[0]; - interpreter.sendDestroyItem(item.getId(), item.getName()); - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - if (buttonId == 2) { - if (player.getInventory().remove(item)) { - player.getAudioManager().send(SOUND); - end(); - } - } - if (buttonId == 3) { - end(); - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 9878 }; - } - -} diff --git a/Server/src/main/content/global/handlers/item/DiangoItemPlugin.java b/Server/src/main/content/global/handlers/item/DiangoItemPlugin.java deleted file mode 100644 index a1ef85b6d..000000000 --- a/Server/src/main/content/global/handlers/item/DiangoItemPlugin.java +++ /dev/null @@ -1,69 +0,0 @@ -package content.global.handlers.item; - -import core.cache.def.impl.ItemDefinition; -import core.game.interaction.OptionHandler; -import core.game.node.Node; -import core.game.node.entity.player.Player; -import core.game.system.task.Pulse; -import core.game.world.update.flag.context.Animation; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Represents the diano item plugin. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class DiangoItemPlugin extends OptionHandler { - - /** - * Represents the animations. - */ - private static final Animation[] ANIMATIONS = new Animation[] { new Animation(8990), new Animation(1902), new Animation(1904) }; - - @Override - public boolean handle(final Player player, Node node, String option) { - switch (option) { - case "fly": - player.animate(ANIMATIONS[0]); - break; - case "spin": - player.animate(ANIMATIONS[1]); - player.getPulseManager().run(new Pulse(1) { - int count = 0; - - @Override - public boolean pulse() { - if (count++ >= 9) { - player.animate(ANIMATIONS[2]); - return true; - } - return false; - } - - @Override - public void stop() { - super.stop(); - player.animate(ANIMATIONS[2]); - } - - }); - break; - } - return true; - } - - @Override - public boolean isWalk() { - return false; - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ItemDefinition.setOptionHandler("fly", this); - ItemDefinition.setOptionHandler("spin", this); - return this; - } - -} diff --git a/Server/src/main/content/global/handlers/item/DragonfireShieldListener.kt b/Server/src/main/content/global/handlers/item/DragonfireShieldListener.kt new file mode 100644 index 000000000..c195a6f98 --- /dev/null +++ b/Server/src/main/content/global/handlers/item/DragonfireShieldListener.kt @@ -0,0 +1,147 @@ +package content.global.handlers.item + +import content.global.handlers.item.equipment.special.DragonfireSwingHandler +import core.ServerConstants +import core.api.* +import core.game.container.impl.EquipmentContainer +import core.game.global.action.DropListener +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.Entity +import core.game.node.entity.combat.BattleState +import core.game.node.entity.combat.CombatPulse.Companion.swing +import core.game.node.entity.combat.InteractionType +import core.game.node.entity.combat.equipment.SwitchAttack +import core.game.node.entity.impl.Projectile +import core.game.node.entity.player.Player +import core.game.node.item.Item +import core.game.node.item.ItemPlugin +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.minutesToTicks +import core.tools.secondsToTicks +import org.rs09.consts.Items +import org.rs09.consts.Sounds + +/** + * Handles the dragonfire shield options. + */ + +class DragonfireShieldListener: InteractionListener { + + val dragonfireShields = intArrayOf(Items.DRAGONFIRE_SHIELD_11283, Items.DRAGONFIRE_SHIELD_11284) + val dfsEmptyAnim = 6700 + val dfsEmptyGfx = 1160 + val dfsRecharge = if (ServerConstants.BETTER_DFS) secondsToTicks(30) else minutesToTicks(2) + + override fun defineListeners() { + + on(dragonfireShields, IntType.ITEM, "operate") { player, node -> + val usingAttack = !getAttribute(player, "dfs_spec", false) + if (!usingAttack) { + if (!player.settings.isSpecialToggled) { + setVarp(player, 301, 0) + } + player.removeAttribute("dfs_spec") + player.properties.combatPulse.temporaryHandler = null + return@on true + } + val notCharged = node.asItem().id == Items.DRAGONFIRE_SHIELD_11284 || node.asItem().charge < 20 + if (notCharged) { + sendMessage(player, "Your shield has no charges left.") + return@on true + } + if (player.locks.isLocked("dfs_recharge")) { + sendMessage(player, "Your dragonfire shield is recharging.") + return@on true + } + setVarp(player, 301, 1) + player.setAttribute("dfs_spec", true) + val attack = SwitchAttack(null, Animation.create(6696), Graphics.create(1165), Graphics(1167, 96), Projectile.create(player, null, 1166, 36, 36, 80, 70, 0, 11)) + val handler: DragonfireSwingHandler = object : DragonfireSwingHandler(false, 25, attack, true) { + override fun swing(entity: Entity?, victim: Entity?, state: BattleState?): Int { + if (entity is Player) { + if (!player.settings.isSpecialToggled) { + setVarp(player, 301, 0) + } + removeAttribute(player, "dfs_spec") + val shield = player.equipment[EquipmentContainer.SLOT_SHIELD] + if (shield == null || shield.id != Items.DRAGONFIRE_SHIELD_11283) { + return -1 + } + playGlobalAudio(entity.getLocation(), Sounds.DRAGONSLAYER_SHIELDFIRE_3761) + delayEntity(player, 3) + shield.charge -= 20 + if (shield.charge < 20 && node.asItem().slot == EquipmentContainer.SLOT_SHIELD) { + replaceSlot(player, node.asItem().slot, Item(Items.DRAGONFIRE_SHIELD_11284), Item(Items.DRAGONFIRE_SHIELD_11283), Container.EQUIPMENT) + } + EquipmentContainer.updateBonuses(player) + player.locks.lock("dfs_recharge", dfsRecharge) + } + return super.swing(entity, victim, state) + } + + override fun visualizeImpact(entity: Entity?, victim: Entity?, state: BattleState?) { + playGlobalAudio(victim!!.location, Sounds.FIRESTRIKE_HIT_161, 20) + super.visualizeImpact(entity, victim, state) + } + } + attack.handler = handler + val victim = player.properties.combatPulse.getVictim() + if (player.properties.combatPulse.isAttacking && handler.canSwing(player, victim!!) == InteractionType.STILL_INTERACT) { + swing(player, victim, handler) + return@on true + } + player.properties.combatPulse.temporaryHandler = handler + return@on true + } + + on(dragonfireShields, IntType.ITEM, "empty") { player, node -> + replaceSlot(player, node.asItem().slot, Item(Items.DRAGONFIRE_SHIELD_11284), Item(Items.DRAGONFIRE_SHIELD_11283)) + visualize(player, dfsEmptyAnim, dfsEmptyGfx) + sendMessage(player, "You release the charges.") + playGlobalAudio(player.location, Sounds.DRAGONSLAYER_SHIELD_EMPTY_3760) + return@on true + } + + on(dragonfireShields, IntType.ITEM, "inspect") { player, node -> + if (node.asItem().id == Items.DRAGONFIRE_SHIELD_11284) { + sendMessage(player, "The shield has no charges.") + return@on true + } + sendMessage(player, "The shield has " + node.asItem().charge / 20 + " charges.") + return@on true + } + + on(dragonfireShields, IntType.ITEM, "drop") { player, node -> + var shield = node.asItem() + val slot = shield.slot + if (shield.id == Items.DRAGONFIRE_SHIELD_11283) { + replaceSlot(player, shield.slot, Item(Items.DRAGONFIRE_SHIELD_11284), Item(Items.DRAGONFIRE_SHIELD_11283)) + shield = player.inventory.get(slot) + } + DropListener.drop(player, shield) + return@on true + } + } +} + +/** + * Drops the uncharged dragonfire shield item id on death. + */ + +@Initializable +class DFSItemPlugin : ItemPlugin() { + + @Throws(Throwable::class) + override fun newInstance(arg: Any?): Plugin { + register(Items.DRAGONFIRE_SHIELD_11283) + return this + } + + override fun getDeathItem(item: Item?): Item { + return Item(Items.DRAGONFIRE_SHIELD_11284) + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/item/DragonfireShieldPlugin.java b/Server/src/main/content/global/handlers/item/DragonfireShieldPlugin.java deleted file mode 100644 index c2ada0374..000000000 --- a/Server/src/main/content/global/handlers/item/DragonfireShieldPlugin.java +++ /dev/null @@ -1,138 +0,0 @@ -package content.global.handlers.item; - -import core.cache.def.impl.ItemDefinition; -import core.game.container.impl.EquipmentContainer; -import core.game.interaction.OptionHandler; -import core.game.node.Node; -import core.game.node.entity.Entity; -import core.game.node.entity.combat.BattleState; -import core.game.node.entity.combat.InteractionType; -import core.game.node.entity.combat.equipment.SwitchAttack; -import content.global.handlers.item.equipment.special.DragonfireSwingHandler; -import core.game.node.entity.impl.Projectile; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; -import core.game.node.item.ItemPlugin; -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.game.node.entity.combat.CombatPulse; -import core.game.world.GameWorld; -import core.plugin.ClassScanner; - -import java.util.concurrent.TimeUnit; - -/** - * Handles the dragonfire shield options. - * @author Emperor - */ -@Initializable -public final class DragonfireShieldPlugin extends OptionHandler { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ItemDefinition.forId(11283).getHandlers().put("option:empty", this); - ItemDefinition.forId(11283).getHandlers().put("option:inspect", this); - ItemDefinition.forId(11283).getHandlers().put("option:operate", this); - ItemDefinition.forId(11284).getHandlers().put("option:inspect", this); - ItemDefinition.forId(11284).getHandlers().put("option:operate", this); - ClassScanner.definePlugin(new DFSItemPlugin()); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - Item item = (Item) node; - switch (option) { - case "operate": - boolean usingAttack = !player.getAttribute("dfs_spec", false); - if (!usingAttack) { - if (!player.getSettings().isSpecialToggled()) { - player.getConfigManager().set(301, 0); - } - player.removeAttribute("dfs_spec"); - player.getProperties().getCombatPulse().setTemporaryHandler(null); - return true; - } - boolean notCharged = item.getId() == 11284 || item.getCharge() < 20; - if (player.getSavedData().getGlobalData().getOverChargeDelay() <= System.currentTimeMillis()) { - item.setCharge(1020); - player.sendMessage("You use the power from the overcharge lords & charge your shield."); - notCharged = false; - player.getSavedData().getGlobalData().setOverChargeDelay(System.currentTimeMillis() + (GameWorld.getSettings().isDevMode() ? TimeUnit.SECONDS.toMillis(10) : TimeUnit.MINUTES.toMicros(10))); - } - if (notCharged) { - player.getPacketDispatch().sendMessage("Your shield has no charges left."); - return true; - } - if (player.getLocks().isLocked("dfs_recharge")) { - player.getPacketDispatch().sendMessage("Your dragonfire shield is recharging."); - return true; - } - player.getConfigManager().set(301, 1); - player.setAttribute("dfs_spec", true); - SwitchAttack attack = new SwitchAttack(null, Animation.create(6696), Graphics.create(1165), new Graphics(1167, 96), Projectile.create(player, null, 1166, 36, 36, 80, 70, 0, 11)); - DragonfireSwingHandler handler = new DragonfireSwingHandler(false, 26, attack, true) { - @Override - public int swing(Entity entity, Entity victim, BattleState state) { - if (entity instanceof Player) { - Player player = (Player) entity; - if (!player.getSettings().isSpecialToggled()) { - player.getConfigManager().set(301, 0); - } - player.removeAttribute("dfs_spec"); - Item shield = player.getEquipment().get(EquipmentContainer.SLOT_SHIELD); - if (shield == null || shield.getId() != 11283) { - return -1; - } - shield.setCharge(shield.getCharge() - 20); - if (shield.getCharge() < 1) { - player.getEquipment().replace(new Item(11284), EquipmentContainer.SLOT_SHIELD); - } - EquipmentContainer.updateBonuses(player); - player.getLocks().lock("dfs_recharge",50); - } - return super.swing(entity, victim, state); - } - }; - attack.setHandler(handler); - Entity victim = player.getProperties().getCombatPulse().getVictim(); - if (player.getProperties().getCombatPulse().isAttacking() && handler.canSwing(player, victim) == InteractionType.STILL_INTERACT) { - CombatPulse.Companion.swing(player, victim, handler); - return true; - } - player.getProperties().getCombatPulse().setTemporaryHandler(handler); - return true; - case "empty": - player.getInventory().replace(new Item(11284), item.getSlot()); - player.graphics(Graphics.create(1160)); - player.getPacketDispatch().sendMessage("You release the charges."); - return true; - case "inspect": - if (item.getId() == 11284) { - player.getPacketDispatch().sendMessage("The shield has no charges."); - return true; - } - player.getPacketDispatch().sendMessage("The shield has " + (item.getCharge() / 20) + " charges."); - return true; - } - return false; - } - - public class DFSItemPlugin extends ItemPlugin { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - register(11283); - return this; - } - - @Override - public Item getDeathItem(Item item) { - return new Item(11284); - } - - } - -} diff --git a/Server/src/main/content/global/handlers/item/DustHandPlugin.java b/Server/src/main/content/global/handlers/item/DustHandPlugin.java deleted file mode 100644 index 8e4728040..000000000 --- a/Server/src/main/content/global/handlers/item/DustHandPlugin.java +++ /dev/null @@ -1,42 +0,0 @@ -package content.global.handlers.item; - -import core.cache.def.impl.ItemDefinition; -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 plugin used to dust your hands. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class DustHandPlugin extends OptionHandler { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ItemDefinition.forId(8865).getHandlers().put("option:dust-hands", this); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - switch (option) { - case "dust-hands": - if (player.getInventory().remove((Item) node)) { - player.getPacketDispatch().sendMessage("You dust your hands with the finely ground ash."); - player.setAttribute("hand_dust", true); - } - break; - } - return true; - } - - @Override - public boolean isWalk() { - return false; - } -} diff --git a/Server/src/main/content/global/handlers/item/EctophialListener.kt b/Server/src/main/content/global/handlers/item/EctophialListener.kt new file mode 100644 index 000000000..111db96c5 --- /dev/null +++ b/Server/src/main/content/global/handlers/item/EctophialListener.kt @@ -0,0 +1,75 @@ +package content.global.handlers.item + +import core.api.* +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.player.link.TeleportManager.TeleportType +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 org.rs09.consts.Scenery +import org.rs09.consts.Sounds +import content.data.Quests + +@Suppress("unused") +class EctophialListener : InteractionListener { + private val fillAnimation = Animation(832) + private val emptyAnimation = Animation(9609) + private val emptyGraphics = Graphics(1688) + + private fun refillEctophial(player: Player) { + delayEntity(player, fillAnimation.duration) + animate(player, fillAnimation) + playAudio(player, Sounds.FILL_ECTOPLASM_1132) + if (removeItem(player, Items.ECTOPHIAL_4252)) { + addItem(player, Items.ECTOPHIAL_4251) + sendMessage(player, "You refill the ectophial from the Ectofuntus.") + } + } + + override fun defineListeners() { + onUseWith(IntType.SCENERY, Items.ECTOPHIAL_4252, Scenery.ECTOFUNTUS_5282) { player, _, _ -> + refillEctophial(player) + return@onUseWith true + } + + on(Items.ECTOPHIAL_4251, IntType.ITEM, "empty") { player, node -> + if (!hasRequirement(player, Quests.GHOSTS_AHOY)) + return@on true + + if (player.isTeleBlocked) { + sendMessage(player, "A magical force has stopped you from teleporting.") + return@on true + } + delayEntity(player, 10) + 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 new file mode 100644 index 000000000..12771ec0b --- /dev/null +++ b/Server/src/main/content/global/handlers/item/EmptyOptionListener.kt @@ -0,0 +1,91 @@ +package content.global.handlers.item + +import core.api.* +import core.cache.def.impl.ItemDefinition +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.Sounds +import java.util.* + +/** + * Handles items with "empty" options + */ +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")) { + replaceSlot(player, node.asItem().slot, Item(EmptyItem.getEmpty(Items.POTION_195)!!), node.asItem()) + playAudio(player, EmptyItem.getEmptyAudio(Items.POTION_195)!!) + return@on true + } + if (EmptyItem.emptyItemMap[node.id] != null) { + replaceSlot(player, node.asItem().slot, Item(EmptyItem.getEmpty(node.id)!!), node.asItem()) + if (EmptyItem.getEmptyAudio(node.id) != -1) playAudio(player, EmptyItem.getEmptyAudio(node.id)!!) + EmptyItem.getEmptyMessage(node.id)?.let { sendMessage(player, it) } + } + return@on true + } + } + + enum class EmptyItem(var fullId: Int, var emptyId: Int, var emptyMessage: String, var audioId: Int = -1) { + POT_OF_FLOUR(Items.POT_OF_FLOUR_1933, Items.EMPTY_POT_1931, "You empty the contents of the pot onto the floor."), + POT_OF_CORNFLOUR(Items.POT_OF_CORNFLOUR_7468, Items.EMPTY_POT_1931, "You empty the contents of the pot onto the floor."), + BONE_MEAL(Items.BONEMEAL_4255, Items.EMPTY_POT_1931, "You empty the pot of crushed bones."), + BUCKET_OF_SAND(Items.BUCKET_OF_SAND_1783, Items.BUCKET_1925, "You empty the contents of the bucket onto the floor.", Sounds.SAND_BUCKET_2584), + BUCKET_OF_MILK(Items.BUCKET_OF_MILK_1927, Items.BUCKET_1925, "You empty the contents of the bucket onto the floor.", Sounds.LIQUID_2401), + BUCKET_OF_WATER(Items.BUCKET_OF_WATER_1929, Items.BUCKET_1925, "You empty the contents of the bucket on the floor.", Sounds.LIQUID_2401), + BUCKET_OF_COMPOST(Items.COMPOST_6032, Items.BUCKET_1925, "You empty the bucket of compost."), + BUCKET_OF_SUPERCOMPOST(Items.SUPERCOMPOST_6034, Items.BUCKET_1925, "You empty the bucket of supercompost."), + BUCKET_OF_SLIME(Items.BUCKET_OF_SLIME_4286, Items.BUCKET_1925, "You empty the contents of the bucket on the floor.", Sounds.LIQUID_2401), + VIAL_OF_WATER(Items.VIAL_OF_WATER_227, Items.VIAL_229, "You empty the vial.", Sounds.LIQUID_2401), + BOWL_OF_WATER(Items.BOWL_OF_WATER_1921, Items.BOWL_1923, "You empty the contents of the bowl onto the floor.", Sounds.LIQUID_2401), + JUG_OF_WATER(Items.JUG_OF_WATER_1937, Items.JUG_1935, "You empty the contents of the jug onto the floor.", Sounds.LIQUID_2401), + BURNT_PIE(Items.BURNT_PIE_2329, Items.PIE_DISH_2313, "You empty the pie dish."), + POTION(Items.POTION_195, Items.VIAL_229, "You empty the vial.", Sounds.LIQUID_2401), + BURNT_STEW(Items.BURNT_STEW_2005, Items.BOWL_1923, "You empty the contents of the bowl onto the floor.", Sounds.LIQUID_2401), + NETTLE_TEA(Items.NETTLE_TEA_4239, Items.BOWL_1923, "You empty the contents of the bowl onto the floor.", Sounds.LIQUID_2401), + 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), + BURNT_GNOMEBOWL(Items.BURNT_GNOMEBOWL_2175, Items.GNOMEBOWL_MOULD_2166, "You empty the contents of the gnomebowl onto the floor."), + BURNT_EGG(Items.BURNT_EGG_7090, Items.BOWL_1923, "You empty the contents of the bowl onto the floor."), + BURNT_ONION(Items.BURNT_ONION_7092, Items.BOWL_1923, "You empty the contents of the bowl onto the floor."), + BURNT_MUSHROOM(Items.BURNT_MUSHROOM_7094, Items.BOWL_1923, "You empty the contents of the bowl onto the floor."); + + companion object { + var emptyItemMap = HashMap() + var emptyMessageMap = HashMap() + var emptyAudioMap = HashMap() + var emptyItemList = ArrayList() + + init { + for (item in values()) { + emptyItemMap.putIfAbsent(item.fullId, item.emptyId) + emptyMessageMap.putIfAbsent(item.fullId, item.emptyMessage) + emptyAudioMap.putIfAbsent(item.fullId, item.audioId) + 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")) { + emptyItemList.add(item.id) + } + } + + fun getEmpty(id: Int): Int? { + return emptyItemMap[id] + } + + fun getEmptyMessage(id: Int): String? { + return emptyMessageMap[id] + } + + fun getEmptyAudio(id: Int): Int? { + return emptyAudioMap[id] + } + } + } +} diff --git a/Server/src/main/content/global/handlers/item/EmptyOptionPlugin.java b/Server/src/main/content/global/handlers/item/EmptyOptionPlugin.java deleted file mode 100644 index f5e8f9624..000000000 --- a/Server/src/main/content/global/handlers/item/EmptyOptionPlugin.java +++ /dev/null @@ -1,102 +0,0 @@ -package content.global.handlers.item; - -import core.cache.def.impl.ItemDefinition; -import core.game.interaction.OptionHandler; -import core.game.node.Node; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; -import core.plugin.Initializable; -import core.plugin.Plugin; - -import java.util.HashMap; - -/** - * Handles items with "empty" options - * @author ceik - */ -@Initializable -public final class EmptyOptionPlugin extends OptionHandler { - public static final int BUCKET = 1925; - public static final int VIAL = 229; - public static final String BUCKET_EMPTY_MSG = "You empty the contents of the bucket onto the floor."; - @Override - public boolean handle(Player player, Node node, String option) { - if (node.getName().contains("potion") || node.getName().toLowerCase().contains("antipoison")) { - player.getInventory().remove(node.asItem()); - player.getInventory().add(EmptyItem.getEmpty(91)); - return true; - } - if(EmptyItem.emptyItemMap.get(node.getId()) != null){ - player.getInventory().remove(node.asItem()); - player.getInventory().add(EmptyItem.getEmpty(node.getId())); - player.getPacketDispatch().sendMessage(EmptyItem.getEmptyMessage(node.getId())); - } else { - player.debug("Unhandled empty option! ITEM ID: " + node.getId()); - } - return true; - } - - @Override - public boolean isWalk() { - return false; - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ItemDefinition.setOptionHandler("empty", this); - ItemDefinition.setOptionHandler("empty dish", this); - ItemDefinition.setOptionHandler("empty bowl", this); - return this; - } - public enum EmptyItem{ - POT_OF_FLOUR(1933, 1931, "You empty the contents of the pot onto the floor."), - POT_OF_CORNFLOUR(7468, 1931, "You empty the contents of the pot onto the floor."), - BONE_MEAL(4255, 1931, "You empty the pot of crushed bones."), - BUCKET_OF_SAND(1783, BUCKET, BUCKET_EMPTY_MSG), - BUCKET_OF_MILK(1927, BUCKET, BUCKET_EMPTY_MSG), - BUCKET_OF_WATER(1929, BUCKET, BUCKET_EMPTY_MSG), - BUCKET_OF_COMPOST(6032, BUCKET, "You empty the bucket of compost."), - BUCKET_OF_SUPERCOMPOST(6034, BUCKET, "You empty the bucket of supercompost."), - BUCKET_OF_SLIME(4286, BUCKET, "You empty the contents of the bucket on the floor."), - VIAL_OF_WATER(227, VIAL, "You empty the vial."), - BOWL_OF_WATER(1921, 1923, "You empty the contents of the bowl onto the floor."), - JUG_OF_WATER(1937, 1935, "You empty the contents of the jug onto the floor."), - BURNT_PIE(2329, 2313, "You empty the pie dish."), - POTION(91, VIAL,null), - BURNT_STEW(2005, 1923, "You empty the contents of the bowl onto the floor."), - NETTLE_TEA(4239, 1923, "You empty the contents of the bowl onto the floor."), - NETTLE_WATER(4237, 1923, "You empty the contents of the bowl onto the floor."), - NETTLE_TEA_MILKY(4240, 1923, "You empty the contents of the bowl onto the floor."), - BURNT_CURRY(2013, 1923, "You empty the contents of the bowl onto the floor."), - BURNT_GNOMEBOWL(2175, 2166, "You empty the contents of the gnomebowl onto the floor."), - BURNT_EGG(7090, 1923, "You empty the contents of the bowl onto the floor."), - BURNT_ONION(7092, 1923, "You empty the contents of the bowl onto the floor."), - BURNT_MUSHROOM(7094, 1923, "You empty the contents of the bowl onto the floor."); - - int fullId, emptyId; - String emptyMessage; - EmptyItem(int fullId, int emptyId, String emptyMessage){ - this.fullId = fullId; - this.emptyId = emptyId; - this.emptyMessage = emptyMessage; - } - - public static HashMap emptyItemMap = new HashMap<>(); - public static HashMap emptyMessageMap = new HashMap<>(); - - static{ - for(EmptyItem item : EmptyItem.values()){ - emptyItemMap.putIfAbsent(item.fullId,item.emptyId); - emptyMessageMap.putIfAbsent(item.fullId,item.emptyMessage); - } - } - - public static Item getEmpty(int id){ - return new Item(emptyItemMap.get(id)); - } - - public static String getEmptyMessage(int id){ - return emptyMessageMap.get(id); - } - } -} diff --git a/Server/src/main/content/global/handlers/item/EnchantJewelleryTabListener.kt b/Server/src/main/content/global/handlers/item/EnchantJewelleryTabListener.kt new file mode 100644 index 000000000..b37ed1b1e --- /dev/null +++ b/Server/src/main/content/global/handlers/item/EnchantJewelleryTabListener.kt @@ -0,0 +1,92 @@ +package content.global.handlers.item + +import core.api.* +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.interaction.QueueStrength +import org.rs09.consts.Items +import org.rs09.consts.Sounds + +class EnchantJewelleryTabListener : InteractionListener { + + private val LVL_1_ENCHANT = mapOf( + Items.BLUE_DRAGONHIDE_1751 to Items.PLAIN_SATCHEL_10877, + 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.GREEN_DRAGONHIDE_1753 to Items.GREEN_SATCHEL_10878, + 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.RED_DRAGONHIDE_1749 to Items.RED_SATCHEL_10879, + 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.DAGANNOTH_HIDE_6155 to Items.GOLD_SATCHEL_10881, + 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.SUQAH_HIDE_9080 to Items.RUNE_SATCHEL_10882, + 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 + ) + + private val LVL_6_ENCHANT = mapOf( + Items.BLACK_DRAGONHIDE_1747 to Items.BLACK_SATCHEL_10880, + 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 + ) + + override fun defineListeners() { + on(IntType.ITEM, "break") {player, node -> + closeAllInterfaces(player) + delayEntity(player, 1) + queueScript(player, strength = QueueStrength.SOFT) { + + val items = when (node.id) { + 8016 -> LVL_1_ENCHANT //Sapphire + 8017 -> LVL_2_ENCHANT + 8018 -> LVL_3_ENCHANT + 8019 -> LVL_4_ENCHANT + 8020 -> LVL_5_ENCHANT + 8021 -> LVL_6_ENCHANT + else -> return@queueScript stopExecuting(player) + } + + if (inInventory(player, node.id)) { + for (item in player.inventory.toArray()) { + if (item == null) continue + val product = items[item.id] ?: continue + if (removeItem(player, node.id) && (removeItem(player, item.id))) { + addItem(player, product) + playAudio(player, Sounds.POH_TABLET_BREAK_979) + animate(player, 4069, true) + break + } + } + } + return@queueScript stopExecuting(player) + } + return@on true + } + } +} diff --git a/Server/src/main/content/global/handlers/item/EnchantJewelleryTabOption.kt b/Server/src/main/content/global/handlers/item/EnchantJewelleryTabOption.kt deleted file mode 100644 index 987b32a5c..000000000 --- a/Server/src/main/content/global/handlers/item/EnchantJewelleryTabOption.kt +++ /dev/null @@ -1,97 +0,0 @@ -package content.global.handlers.item - -import core.cache.def.impl.ItemDefinition -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.update.flag.context.Animation -import core.plugin.Initializable -import core.plugin.Plugin -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.launch -import org.rs09.consts.Items -import core.tools.SystemLogger - -private val LVL_1_ENCHANT = mapOf( - Items.SAPPHIRE_RING_1637 to Item(Items.RING_OF_RECOIL_2550), - Items.SAPPHIRE_NECKLACE_1656 to Item(Items.GAMES_NECKLACE8_3853), - Items.SAPPHIRE_AMULET_1694 to Item(Items.AMULET_OF_MAGIC_1727), - Items.SAPPHIRE_BRACELET_11072 to Item(Items.BRACELET_OF_CLAY_11074) -) -private val LVL_2_ENCHANT = mapOf( - Items.EMERALD_RING_1639 to Item(Items.RING_OF_DUELLING8_2552), - Items.EMERALD_NECKLACE_1658 to Item(Items.BINDING_NECKLACE_5521), - Items.EMERALD_AMULET_1696 to Item(Items.AMULET_OF_DEFENCE_1729), - Items.EMERALD_BRACELET_11076 to Item(Items.CASTLEWAR_BRACE3_11079) -) - -private val LVL_3_ENCHANT = mapOf( - Items.RUBY_RING_1641 to Item(Items.RING_OF_FORGING_2568), - Items.RUBY_NECKLACE_1660 to Item(Items.DIGSITE_PENDANT_5_11194), - Items.RUBY_AMULET_1698 to Item(Items.AMULET_OF_STRENGTH_1725), - Items.RUBY_BRACELET_11085 to Item(Items.INOCULATION_BRACE_11088) -) - -private val LVL_4_ENCHANT = mapOf( - Items.DIAMOND_RING_1643 to Item(Items.RING_OF_LIFE_2570), - Items.DIAMOND_NECKLACE_1662 to Item(Items.PHOENIX_NECKLACE_11090), - Items.DIAMOND_AMULET_1700 to Item(Items.AMULET_OF_POWER_1731), - Items.DIAMOND_BRACELET_11092 to Item(Items.FORINTHRY_BRACE5_11095) -) - -private val LVL_5_ENCHANT = mapOf( - 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) -) - - -@Initializable -class EnchantJewelleryTabOption : OptionHandler(){ - override fun handle(player: Player?, node: Node?, option: String?): Boolean { - player ?: return false - - 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 - else -> null - } - - if(items == null){ - SystemLogger.logWarn(this::class.java, "UNHANDLED ENCHANT ITEM TABLET [ID: ${node?.id}") - return true - } - - player.audioManager.send(979) - player.animator.forceAnimation(Animation(4069)) - - GlobalScope.launch { - for(item in player.inventory.toArray()){ - item ?: continue - if(items.containsKey(item.id)){ - val product = items[item.id] - player.inventory.remove(item) - player.inventory.remove(node as Item) - player.inventory.add(product) - break; - } - } - } - return true - } - - override fun newInstance(arg: Any?): Plugin { - ItemDefinition.forId(8016).handlers["option:break"] = this - ItemDefinition.forId(8017).handlers["option:break"] = this - ItemDefinition.forId(8018).handlers["option:break"] = this - ItemDefinition.forId(8019).handlers["option:break"] = this - ItemDefinition.forId(8020).handlers["option:break"] = this - return this - } - -} \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/item/EnchantedGemListener.kt b/Server/src/main/content/global/handlers/item/EnchantedGemListener.kt index 584b5d88c..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,9 +42,10 @@ 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.") } - setVarbit(player!!, 2502, 0, getSlayerTaskFlags(player!!) shr 4) + setVarp(player!!, 2502, getSlayerTaskFlags(player!!) shr 4) stage = 1 } } @@ -63,4 +65,4 @@ class EnchantedGemDialogue() : DialogueFile() { 401 -> player(core.game.dialogue.FacialExpression.HAPPY, "Great, thanks!").also { stage = 1 } } } -} \ No newline at end of file +} 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 912045136..79208d80f 100644 --- a/Server/src/main/content/global/handlers/item/ExplorersRingPlugin.kt +++ b/Server/src/main/content/global/handlers/item/ExplorersRingPlugin.kt @@ -1,9 +1,6 @@ package content.global.handlers.item -import core.api.hasLevelStat -import core.api.sendMessage -import core.api.teleport -import core.api.visualize +import core.api.* import core.game.node.entity.player.Player import core.game.node.entity.player.link.TeleportManager.TeleportType import core.game.node.entity.skill.Skills @@ -11,16 +8,11 @@ import core.game.world.map.Location import org.json.simple.JSONObject import org.rs09.consts.Items import core.ServerStore -import core.ServerStore.Companion.getBoolean import core.ServerStore.Companion.getInt import core.game.interaction.IntType import core.game.interaction.InteractionListener +import content.global.skill.magic.modern.ModernListeners -/** - * Handles the explorers ring. - * - * @author Vexia - */ class ExplorersRingPlugin : InteractionListener { val RINGS = intArrayOf(Items.EXPLORERS_RING_1_13560, Items.EXPLORERS_RING_2_13561, Items.EXPLORERS_RING_3_13562) @@ -31,11 +23,15 @@ class ExplorersRingPlugin : InteractionListener { on(RINGS, IntType.ITEM, "run-replenish"){ player, node -> val charges = getStoreFile().getInt(player.username.toLowerCase() + ":run") if (charges >= getRingLevel(node.id)) { - sendMessage(player,"You have used all the charges you can for one day.") + sendDialogue(player,"Your ring appears to have no more run energy recharges left for today.") + return@on true + } + if (player.settings.runEnergy == 100.0) { + sendMessage(player, "You are fully rested. You do not need to use the ring's power for the moment.") return@on true } player.settings.updateRunEnergy(-50.0) - player.audioManager.send(5035) + playAudio(player, 5035) getStoreFile()[player.username.toLowerCase() + ":run"] = charges + 1 @@ -44,19 +40,30 @@ class ExplorersRingPlugin : InteractionListener { return@on true } + //Sources Used + // - https://www.youtube.com/watch?v=PEA0fkRK0-A&t=64s&pp=ygUcMjAwOSBydW5lc2NhcGUgZXhwbG9yZXIgcmluZw%3D%3D + // - https://runescape.wiki/w/Explorer%27s_ring?oldid=899927 on(RINGS, IntType.ITEM, "low-alchemy"){ player, _ -> if (!hasLevelStat(player, Skills.MAGIC, 21)) { sendMessage(player,"You need a Magic level of 21 in order to do that.") return@on true } - if(getStoreFile().getBoolean(player.username.toLowerCase() + ":alchs")){ - sendMessage(player, "You have claimed all the charges you can for one day.") + val remaining = getStoreFile().getInt(player.username.lowercase() + ":alchs", 30) + if (remaining <= 0) { + sendMessage(player, "You have used up all of your charges for the day.") return@on true } - sendMessage(player,"You grant yourself with 30 free low alchemy charges.") // todo this implementation is not correct, see https://www.youtube.com/watch?v=UbUIF2Kw_Dw - - getStoreFile()[player.username.toLowerCase() + ":alchs"] = true - + //sendDialogue(player, "Choose the item that you wish to convert to coins.") + //addDialogueAction (player) {_,_ -> + sendItemSelect (player, "Cast Low Level Alchemy on:") { slot, optionIndex -> + val item = player.inventory[slot] + if (item == null) + return@sendItemSelect + if (!ModernListeners().alchemize(player, item, false, explorersRing = true)) + return@sendItemSelect + getStoreFile()[player.username.lowercase() + ":alchs"] = remaining - 1 + } + //} return@on true } diff --git a/Server/src/main/content/global/handlers/item/FishbowlPlugin.java b/Server/src/main/content/global/handlers/item/FishbowlPlugin.java index 86f1112bc..09cbfb5b2 100644 --- a/Server/src/main/content/global/handlers/item/FishbowlPlugin.java +++ b/Server/src/main/content/global/handlers/item/FishbowlPlugin.java @@ -20,6 +20,9 @@ import core.tools.RandomFunction; import core.game.dialogue.DialogueInterpreter; import core.game.dialogue.DialoguePlugin; import core.game.node.entity.skill.Skills; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.*; // Sources: // https://www.youtube.com/watch?v=xu7Err9YUgw @@ -71,6 +74,7 @@ public class FishbowlPlugin extends OptionHandler { if (player.getInventory().remove(item)) { player.lock(2); player.getInventory().add(new Item(FISHBOWL_EMPTY)); + playAudio(player, Sounds.LIQUID_2401); player.getPacketDispatch().sendMessage("You empty the contents of the fishbowl onto the ground."); } break; 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 6d360b832..992d1009f 100644 --- a/Server/src/main/content/global/handlers/item/GrandSeedPodHandler.kt +++ b/Server/src/main/content/global/handlers/item/GrandSeedPodHandler.kt @@ -2,14 +2,18 @@ package content.global.handlers.item import content.global.travel.glider.GliderPulse import content.global.travel.glider.Gliders +import core.api.* +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.system.task.Pulse import core.game.world.map.Location +import core.net.packet.PacketRepository +import core.net.packet.context.MinimapStateContext +import core.net.packet.out.MinimapState import org.rs09.consts.Items -import core.game.interaction.InteractionListener -import core.game.interaction.IntType -import core.api.* private const val SQUASH_GRAPHICS_BEGIN = 767 private const val SQUASH_GRAPHICS_END = 769 @@ -25,41 +29,69 @@ private const val LAUNCH_ANIMATION = 4547 class GrandSeedPodHandler : InteractionListener { override fun defineListeners() { - on(Items.GRAND_SEED_POD_9469, IntType.ITEM, "squash", "launch"){ player, _ -> - when(getUsedOption(player)){ - "launch" -> submitWorldPulse(LaunchPulse(player)) - "squash" -> submitWorldPulse(SquashPulse(player)) + on(intArrayOf(Items.GRAND_SEED_POD_9469), IntType.ITEM, "squash", "launch") { player, _ -> + val opt = getUsedOption(player) + if (!removeItem(player, Items.GRAND_SEED_POD_9469)) return@on false + if (opt == "launch") { + visualize(player, LAUNCH_ANIMATION, LAUNCH_GRAPHICS) + delayEntity(player, 7) + queueScript(player, 3, QueueStrength.SOFT) {stage: Int -> + if (stage == 0) { + rewardXP(player, Skills.FARMING, 100.0) + openOverlay(player, 115) + return@queueScript keepRunning(player) + } + + if (stage == 1) { + PacketRepository.send(MinimapState::class.java, MinimapStateContext(player, 2)) + return@queueScript delayScript(player, 3) + } + + if (stage == 2) { + teleport(player, Gliders.TA_QUIR_PRIW.location) + return@queueScript delayScript(player, 2) + } + + if (stage == 3) { + closeOverlay(player) + PacketRepository.send(MinimapState::class.java, MinimapStateContext(player, 0)) + } + + return@queueScript stopExecuting(player) + } } - removeItem(player, Items.GRAND_SEED_POD_9469, Container.INVENTORY) - lock(player, 50) + + if (opt == "squash") { + closeAllInterfaces(player) + visualize(player, SQUASH_ANIM_BEGIN, SQUASH_GRAPHICS_BEGIN) + delayEntity(player, 12) + queueScript(player, 3, QueueStrength.SOFT) {stage: Int -> + if (stage == 0) { + animate(player, 1241, true) + return@queueScript keepRunning(player) + } + + if (stage == 1) { + teleport(player, Location.create(2464, 3494, 0)) + return@queueScript keepRunning(player) + } + + if (stage == 2) { + visualize(player, 1241, SQUASH_GRAPHICS_END) + return@queueScript delayScript(player, 2) + } + + if (stage == 3) { + animate(player, SQUASH_ANIM_END, true) + adjustLevel(player, Skills.FARMING, -5) + return@queueScript keepRunning(player) + } + + return@queueScript stopExecuting(player) + } + } + return@on true } } - - class LaunchPulse(val player: Player): Pulse(){ - var counter = 0 - override fun pulse(): Boolean { - when(counter++){ - 1 -> visualize(player, LAUNCH_ANIMATION, LAUNCH_GRAPHICS) - 3 -> rewardXP(player, Skills.FARMING, 100.0) - 4 -> submitWorldPulse(GliderPulse(2, player, Gliders.TA_QUIR_PRIW)).also { return true } - } - return false - } - } - - class SquashPulse(val player: Player) : Pulse(){ - var counter = 0 - override fun pulse(): Boolean { - when(counter++){ - 1 -> visualize(player, SQUASH_ANIM_BEGIN, SQUASH_GRAPHICS_BEGIN) - 4 -> animate(player, 1241, true) - 5 -> teleport(player, Location.create(2464, 3494, 0)) - 6 -> visualize(player, anim = 1241, gfx = SQUASH_GRAPHICS_END) - 8 -> animate(player, SQUASH_ANIM_END, true).also { adjustLevel(player, Skills.FARMING, -5) } - 9 -> unlock(player).also { return true } - } - return false - } - } -} \ No newline at end of file +} diff --git a/Server/src/main/content/global/handlers/item/HouseTeleTabOptionListener.kt b/Server/src/main/content/global/handlers/item/HouseTeleTabOptionListener.kt new file mode 100644 index 000000000..ee6e3f1b8 --- /dev/null +++ b/Server/src/main/content/global/handlers/item/HouseTeleTabOptionListener.kt @@ -0,0 +1,41 @@ +package content.global.handlers.item + +import core.api.* +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.player.link.TeleportManager +import core.game.node.item.Item +import core.game.node.entity.player.Player +import core.game.world.repository.Repository + +class HouseTeleTabOptionListener : InteractionListener { + override fun defineListeners() { + val homeTabID = 8013 + on(homeTabID, IntType.ITEM, "break") {player, node -> + //Snowscape: teleport to the last house you entered, if possible + var owner: Player + val lastHouseOwner = Repository.getPlayerByName(player.getAttribute("lasthouseentered", null)) + if (lastHouseOwner != null && !lastHouseOwner.getHouseManager().isLocked()) { + owner = lastHouseOwner + } else { + var hasHouse = player.houseManager.location.exitLocation != null + if (!hasHouse) { + sendMessage( player, "You must have a house to teleport to before attempting that.") + return@on false + } + owner = player + } + closeInterface(player) + lock(player, 5) + if (inInventory(player, node.id)) { + owner.houseManager.preEnter(player, false) + val location = owner.houseManager.getEnterLocation() + if (teleport(player, location, TeleportManager.TeleportType.TELETABS)) { + removeItem(player, Item(node.id, 1)) + owner.houseManager.postEnter(player, false) + } + } + return@on true + } + } +} diff --git a/Server/src/main/content/global/handlers/item/HouseTeleTabOptionPlugin.java b/Server/src/main/content/global/handlers/item/HouseTeleTabOptionPlugin.java deleted file mode 100644 index 7d13edc5d..000000000 --- a/Server/src/main/content/global/handlers/item/HouseTeleTabOptionPlugin.java +++ /dev/null @@ -1,41 +0,0 @@ -package content.global.handlers.item; - -import core.cache.def.impl.ItemDefinition; -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.TeleportType; -import core.game.node.item.Item; -import core.game.world.map.Location; -import core.plugin.Initializable; -import core.plugin.Plugin; -import core.tools.RandomFunction; - -@Initializable -public class HouseTeleTabOptionPlugin extends OptionHandler { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ItemDefinition.forId(8013).getHandlers().put("option:break", this); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - if (player.getHouseManager().getLocation().getExitLocation() == null) { - player.sendMessage("You must have a house to teleport to before attempting that."); - return false; - } - player.getInterfaceManager().close(); - player.lock(5); - Location location = player.getHouseManager().getLocation().getExitLocation(); - if (player.getInventory().contains(node.asItem().getId(), 1)) { - if (player.getTeleporter().send(location.transform(0, RandomFunction.random(3), 0), TeleportType.TELETABS, 1)) { - player.getInventory().remove(new Item(node.asItem().getId(),1)); - return true; - } - } - return false; - } - -} diff --git a/Server/src/main/content/global/handlers/item/ImplingJarListener.kt b/Server/src/main/content/global/handlers/item/ImplingJarListener.kt index d3b1b921c..66158c78a 100644 --- a/Server/src/main/content/global/handlers/item/ImplingJarListener.kt +++ b/Server/src/main/content/global/handlers/item/ImplingJarListener.kt @@ -184,7 +184,7 @@ enum class ImplingLoot(val jarId: Int, val table: WeightBasedTable){ MAGPIE_IMPLING(Items.MAGPIE_IMPLING_JAR_11252, WeightBasedTable.create( WeightedItem(Items.BLACK_DRAGONHIDE_1748, 6, 6, 10.0), - WeightedItem(Items.DIAMOND_AMULET_1682, 3, 3, 5.0), + WeightedItem(Items.DIAMOND_AMULET_1701, 3, 3, 5.0), WeightedItem(Items.AMULET_OF_POWER_1732, 3, 3, 5.0), WeightedItem(Items.RING_OF_FORGING_2569, 3, 3, 5.0), WeightedItem(Items.SPLITBARK_GAUNTLETS_3391, 1, 1, 5.0), @@ -231,7 +231,7 @@ enum class ImplingLoot(val jarId: Int, val table: WeightBasedTable){ WeightedItem(Items.DRAGON_BOLT_TIPS_9193, 36, 36, 2.0), WeightedItem(Items.MYSTIC_ROBE_BOTTOM_4093, 1, 1, 2.0), WeightedItem(Items.AMULET_OF_GLORY_1705, 3, 3, 2.0), - WeightedItem(Items.DRAGONSTONE_AMMY_1684, 2, 2, 2.0), + WeightedItem(Items.DRAGONSTONE_AMMY_1703, 2, 2, 2.0), WeightedItem(Items.DRAGON_ARROW_11212, 100, 250, 2.0), WeightedItem(Items.DRAGON_BOLTS_9341, 10, 40, 2.0), WeightedItem(Items.DRAGON_LONGSWORD_1305, 1, 1, 2.0), @@ -240,8 +240,8 @@ enum class ImplingLoot(val jarId: Int, val table: WeightBasedTable){ WeightedItem(Items.DRAGONSTONE_1616, 3, 3, 2.0), WeightedItem(Items.DRAGON_DART_TIP_11232, 100, 350, 2.0), WeightedItem(Items.DRAGON_ARROWTIPS_11237, 100, 350, 2.0), - WeightedItem(Items.BABYDRAGON_BONES_535, 1, 25, 2.0), - WeightedItem(Items.DRAGON_BONES_537, 1, 25, 2.0), + WeightedItem(Items.BABYDRAGON_BONES_535, 100, 300, 2.0), + WeightedItem(Items.DRAGON_BONES_537, 50, 100, 2.0), WeightedItem(Items.MAGIC_SEED_5316, 1, 1, 2.0), WeightedItem(Items.SNAPDRAGON_SEED_5300, 6, 6, 2.0), WeightedItem(Items.SUMMER_PIE_7219, 15, 15, 2.0) diff --git a/Server/src/main/content/global/handlers/item/ItemQuestRequirementListener.kt b/Server/src/main/content/global/handlers/item/ItemQuestRequirementListener.kt index f02f0ca02..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 { @@ -89,11 +90,24 @@ class ItemQuestRequirementListener : InteractionListener { private val questCapes = intArrayOf(9813,9814) + private val godBooks = intArrayOf ( + Items.HOLY_BOOK_3840, + Items.UNHOLY_BOOK_3842 + ) + + private val pharaohScepters = (9044..9051).toIntArray() + + private val initiateArmour = (5574..5576).toIntArray() + + private val proselyteArmour = (9672..9678).toIntArray() + + private val spiritShields = (13734..13745).toIntArray() + override fun defineListeners() { /* 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 } @@ -101,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 } @@ -109,20 +123,24 @@ class ItemQuestRequirementListener : InteractionListener { } */ - onEquip(fremennikTrialsEquipment.plus(fremennikIslesEquipment)) { player, _ -> - return@onEquip requireQuest(player, "Fremennik Trials", "to equip this.") + onEquip(fremennikTrialsEquipment) { player, _ -> + return@onEquip hasRequirement(player, Quests.THE_FREMENNIK_TRIALS) + } + + onEquip(fremennikIslesEquipment) {player, _ -> + return@onEquip hasRequirement(player, Quests.THE_FREMENNIK_ISLES) } onEquip(avasBackpacks){ player, _ -> - return@onEquip requireQuest(player, "Animal Magnetism", "to equip this.") + return@onEquip hasRequirement(player, Quests.ANIMAL_MAGNETISM) } onEquip(lostCityWeapons){ player, _ -> - return@onEquip requireQuest(player, "Lost City", "to equip this.") + return@onEquip hasRequirement(player, Quests.LOST_CITY) } onEquip(Items.CAPE_OF_LEGENDS_1052) { player, _ -> - return@onEquip requireQuest(player, "Legends' Quest", "to equip this.") + return@onEquip hasRequirement(player, Quests.LEGENDS_QUEST) } onEquip(questCapes) { player, _ -> @@ -135,27 +153,84 @@ class ItemQuestRequirementListener : InteractionListener { } onEquip(Items.WOLFBANE_2952){ player, _ -> - return@onEquip requireQuest(player, "Priest in Peril", "to equip this.") + return@onEquip hasRequirement(player, Quests.PRIEST_IN_PERIL) } onEquip(Items.ANCIENT_MACE_11061){ player, _ -> - return@onEquip requireQuest(player, "Another Slice of H.A.M.", "to equip this.") + return@onEquip hasRequirement(player, Quests.ANOTHER_SLICE_OF_HAM) } onEquip(Items.ANCIENT_STAFF_4675){ player, _ -> - return@onEquip requireQuest(player, "Desert Treasure", "to equip this.") + return@onEquip hasRequirement(player, Quests.DESERT_TREASURE) } onEquip(Items.ELEMENTAL_SHIELD_2890) { player, _ -> - return@onEquip requireQuest(player, "Elemental Workshop I", "to equip this.") + return@onEquip hasRequirement(player, Quests.ELEMENTAL_WORKSHOP_I) } onEquip(crystalEquipment){ player, _ -> - return@onEquip requireQuest(player, "Roving Elves", "to equip this.") + return@onEquip hasRequirement(player, Quests.ROVING_ELVES) } onEquip(dragonSlayerEquipment) {player, _ -> - return@onEquip requireQuest(player, "Dragon Slayer", "to equip this.") + return@onEquip hasRequirement(player, Quests.DRAGON_SLAYER) + } + + onEquip(Items.DRAGON_SCIMITAR_4587) {player, _ -> + return@onEquip hasRequirement(player, Quests.MONKEY_MADNESS) + } + + onEquip(Items.GLOVES_7462) {player, _ -> + return@onEquip hasRequirement(player, Quests.RECIPE_FOR_DISASTER) + } + + onEquip(Items.SLAYER_HELMET_13263) {player, _ -> + return@onEquip hasRequirement(player, Quests.SMOKING_KILLS) + } + + onEquip (Items.DRAGON_HALBERD_3204) {player, _ -> + return@onEquip hasRequirement(player, Quests.REGICIDE) + } + + onEquip (Items.CLIMBING_BOOTS_3105) {player, _ -> + return@onEquip hasRequirement(player, Quests.DEATH_PLATEAU) + } + + onEquip (godBooks) {player, _ -> + return@onEquip hasRequirement(player, Quests.HORROR_FROM_THE_DEEP) + } + + onEquip (pharaohScepters) {player, _ -> + 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, Quests.LEGENDS_QUEST) + } + + onEquip (initiateArmour) {player, _ -> + return@onEquip hasRequirement(player, Quests.RECRUITMENT_DRIVE) + } + + onEquip (proselyteArmour) {player, _ -> + return@onEquip hasRequirement(player, Quests.THE_SLUG_MENACE) + } + + onEquip (spiritShields) {player, _ -> + return@onEquip hasRequirement(player, Quests.SUMMERS_END) + } + + onEquip (Items.DRAGON_MACE_1434) {player, _ -> + return@onEquip hasRequirement(player, Quests.HEROES_QUEST) + } + + onEquip (Items.DRAGON_BATTLEAXE_1377) {player, _ -> + return@onEquip hasRequirement(player, Quests.HEROES_QUEST) + } + + onEquip (Items.DARKLIGHT_6746) {player, _ -> + return@onEquip hasRequirement(player, Quests.SHADOW_OF_THE_STORM) } } } diff --git a/Server/src/main/content/global/handlers/item/KeldagrimVotingBond.java b/Server/src/main/content/global/handlers/item/KeldagrimVotingBond.java deleted file mode 100644 index dd4612ee0..000000000 --- a/Server/src/main/content/global/handlers/item/KeldagrimVotingBond.java +++ /dev/null @@ -1,188 +0,0 @@ -//package core.game.interaction.item; -// -//import java.text.DecimalFormat; -//import java.util.concurrent.TimeUnit; -// -//import core.cache.def.impl.ItemDefinition; -//import core.game.dialogue.DialogueInterpreter; -//import core.game.dialogue.DialoguePlugin; -//import core.game.content.ttrail.ClueLevel; -//import core.game.content.ttrail.ClueScrollPlugin; -//import core.game.interaction.OptionHandler; -//import core.game.node.Node; -//import core.game.node.entity.player.Player; -//import core.game.node.entity.state.EntityState; -//import core.game.node.item.Item; -//import core.game.world.repository.Repository; -//import core.plugin.Plugin; -//import core.plugin.PluginManager; -//import core.plugin.InitializablePlugin; -//import core.tools.RandomFunction; -// -///** -// * Handles the keldagrim voting bond item. -// * @author Vexia -// * -// */ -//@InitializablePlugin -//public class KeldagrimVotingBond extends OptionHandler { -// -// /** -// * The keldagrim bond item. -// */ -// private static final Item BOND = new Item(14807); -// -// /** -// * The ultra lamp item. -// */ -// private static final Item ULTRA_LAMP = new Item(14820); -// -// @Override -// public Plugin newInstance(Object arg) throws Throwable { -// ItemDefinition.forId(14807).getConfigurations().put("option:redeem", this); -// ItemDefinition.forId(14807).getConfigurations().put("option:deposit", this); -// PluginManager.definePlugin(new keldagrimVotingBondDialogue()); -// return this; -// } -// -// @Override -// public boolean handle(Player player, Node node, String option) { -// Item item = node.asItem(); -// switch (option) { -// case "redeem": -// player.getDialogueInterpreter().open(DialogueInterpreter.getDialogueKey("keldagrim-bond")); -// break; -// case "deposit": -// if (!player.getBank().hasSpaceFor(item)) { -// player.sendMessage("You don't have enough space in your bank for that item."); -// return true; -// } -// if (player.getInventory().remove(item)) { -// player.getBank().add(item); -// player.sendMessage("You deposit your Reward bond into your bank."); -// } -// break; -// } -// return true; -// } -// -// @Override -// public boolean isWalk() { -// return false; -// } -// -// /** -// * Handles the keldagrim voting bond dialogue. -// * @author Vexia -// * -// */ -// public class keldagrimVotingBondDialogue extends DialoguePlugin { -// -// /** -// * Constructs the {@code keldagrimVotingBondDialogue} -// */ -// public keldagrimVotingBondDialogue() { -// /** -// * empty. -// */ -// } -// -// /** -// * Constructs the {@code keldagrimVotingBondDialogue} -// * @param player The player. -// */ -// public keldagrimVotingBondDialogue(Player player) { -// super(player); -// } -// -// @Override -// public DialoguePlugin newInstance(Player player) { -// return new keldagrimVotingBondDialogue(player); -// } -// -// @Override -// public boolean open(Object... args) { -// options("Double Experience (1 Hour)", "30K Experience Lamp", "10k-75k Coins", "Clue Scroll"); -// stage = 0; -// return true; -// } -// -// @Override -// public boolean handle(int interfaceId, int buttonId) { -// switch (stage) { -// case 0: -// stage = 1; -// switch (buttonId) { -// case 1: -// if (player.getSavedData().getGlobalData().hasDoubleExp()) { -// interpreter.sendItemMessage(14807, "You already have double EXP active!"); -// return true; -// } -// if (player.getInventory().remove(BOND)) { -// player.getSavedData().getGlobalData().setDoubleExp(System.currentTimeMillis() + TimeUnit.HOURS.toMillis(1)); -// player.getStateManager().set(EntityState.DOUBLE_EXPERIENCE, 6000, 0); -// interpreter.sendItemMessage(14807, "You redeemed an hour of double EXP!"); -// Repository.sendNews("" + player.getUsername() + " redeemed an hour of double EXP from a Reward bond!", 15, ""); -// } -// break; -// case 2: -// if (!player.getInventory().hasSpaceFor(ULTRA_LAMP)) { -// interpreter.sendItemMessage(14807, "Sorry, you don't have enough inventory space."); -// return true; -// } -// if (player.getInventory().remove(BOND)) { -// player.getInventory().add(ULTRA_LAMP); -// interpreter.sendItemMessage(14807, "You redeem an ultra lamp."); -// Repository.sendNews("" + player.getUsername() + " redeemed an ultra lamp from a Reward bond!", 15, ""); -// return true; -// } -// break; -// case 3: -// Item coins = new Item(995, RandomFunction.random(10000, 75000)); -// if (!player.getInventory().hasSpaceFor(coins)) { -// interpreter.sendItemMessage(14807, "Sorry, you don't have enough inventory space."); -// return true; -// } -// if (player.getInventory().remove(BOND)) { -// DecimalFormat formatter = new DecimalFormat("#,###"); -// player.getInventory().add(coins); -// interpreter.sendItemMessage(14807, "You redeem " + formatter.format(coins.getAmount()) + " gold coins."); -// Repository.sendNews("" + player.getUsername() + " redeemed " + formatter.format(coins.getAmount()) + " gold coins from a Reward bond!", 15, ""); -// } -// break; -// case 4: -// if (player.getInventory().freeSlots() < 1) { -// interpreter.sendItemMessage(14807, "Sorry, you don't have enough inventory space."); -// return true; -// } -// if (TreasureTrailManager.getInstance(player).hasClue()) { -// interpreter.sendItemMessage(14807, "Sorry, you already have a clue scroll."); -// break; -// } -// if (TreasureTrailManager.getInstance(player).hasTrail()) { -// TreasureTrailManager.getInstance(player).clearTrail(); -// } -// Item clue = ClueScrollPlugin.getClue(RandomFunction.getRandomElement(ClueLevel.values())); -// if (player.getInventory().remove(BOND)) { -// player.getInventory().add(clue); -// interpreter.sendItemMessage(14807, "You redeem a clue scroll."); -// Repository.sendNews("" + player.getUsername() + " redeemed a clue scroll from a Reward bond!", 15, ""); -// } -// break; -// } -// break; -// case 1: -// end(); -// break; -// } -// return true; -// } -// -// @Override -// public int[] getIds() { -// return new int[] {DialogueInterpreter.getDialogueKey("keldagrim-bond")}; -// } -// -// } -// -//} diff --git a/Server/src/main/content/global/handlers/item/LampPlugin.java b/Server/src/main/content/global/handlers/item/LampPlugin.java index 5e5b70f34..32a091c3d 100644 --- a/Server/src/main/content/global/handlers/item/LampPlugin.java +++ b/Server/src/main/content/global/handlers/item/LampPlugin.java @@ -43,12 +43,12 @@ public final class LampPlugin extends OptionHandler { public void handleSelectionCallback(int skill, Player player){ Lamps lamp = Lamps.forItem(player.getAttribute("lamp",new Item(2528))); if(player.getSkills().getStaticLevel(skill) < lamp.getLevelRequirement()){ - player.sendMessage("Your need at least" + lamp.getLevelRequirement() + " " + Skills.SKILL_NAME[skill] + " to do this."); + player.sendMessage("You need at least " + lamp.getLevelRequirement() + " " + Skills.SKILL_NAME[skill] + " to do this."); return; } else { if(player.getInventory().remove((Item) player.getAttribute("lamp"))) { if (lamp == Lamps.GENIE_LAMP) { - player.getSkills().addExperience(skill, player.getSkills().getLevel(skill) * 10); + player.getSkills().addExperience(skill, player.getSkills().getStaticLevel(skill) * 10); } else { player.getSkills().addExperience(skill, lamp.getExp()); } 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/NewsPaperPlugin.java b/Server/src/main/content/global/handlers/item/NewsPaperPlugin.java deleted file mode 100644 index 0e28fcabe..000000000 --- a/Server/src/main/content/global/handlers/item/NewsPaperPlugin.java +++ /dev/null @@ -1,45 +0,0 @@ -package content.global.handlers.item; - -import core.cache.def.impl.ItemDefinition; -import core.game.component.Component; -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; - -/** - * Handles the news paper. - * @author 'Vexia - */ -@Initializable -public class NewsPaperPlugin extends OptionHandler { - - @Override - public boolean handle(Player player, Node node, String option) { - int id = ((Item) node).getId(); - if (id == 11169) { - player.getInterfaceManager().open(new Component(530)); - final String page1 = "Varrock gets Makeover! The city of Varrock is the latest recipient of a complete makeover. When interviewed, King Roald said, 'In order to keep visitors coming to see the sights of our beautiful capital, we felt that tidying-up the city would be more effective than just issuing a decree - make sure you visit the new museum while you are here.'"; - final String page2 = "Obituaries Goblin-Died Giant Rat-Died Unicorn-Died Varrock Guard-Died Varrock Guard-Died Bear-Died. Classifieds."; - player.getPacketDispatch().sendString(page1, 530, 7); - player.getPacketDispatch().sendString(page2, 530, 8); - return true; - } else - player.getDialogueInterpreter().open(70099, "Come to the Al Kharid Market place! Highquality", "produce at low, low prices! Show this flyer to a", "merchant for money off your next purchase,", "courtesy of Ali Morrisane!"); - return true; - } - - @Override - public boolean isWalk() { - return false; - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ItemDefinition.forId(7922).getHandlers().put("option:read", this); - ItemDefinition.forId(11169).getHandlers().put("option:read", this); - return this; - } -} diff --git a/Server/src/main/content/global/handlers/item/NewspaperListener.kt b/Server/src/main/content/global/handlers/item/NewspaperListener.kt new file mode 100644 index 000000000..bb32667c3 --- /dev/null +++ b/Server/src/main/content/global/handlers/item/NewspaperListener.kt @@ -0,0 +1,80 @@ +package content.global.handlers.item + +import core.api.* +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import org.rs09.consts.Items + +/** + * Interaction and interface listener for the Varrock newspaper. + * This handles component(530) globally. + * youtu.be/ePVNOiSzOS4 + */ +class NewspaperListener : InteractionListener { + companion object { + + const val NEWSPAPER_INTERFACE_530 = 530 /* Should be in org.rs09.consts.Components but isn't. */ + + val leftPage = "" + + "Varrock gets " + + "Makeover" + + "

" + + "The city of Varrock " + + "is the latest recipient " + + "of a complete " + + "makeover. When " + + "interviewed, King " + + "Roald said, 'In order " + + "to keep visitors " + + "coming to see the " + + "sights of our " + + "beautiful capital, we " + + "felt that tidying-up " + + "the city would be " + + "more effective than " + + "just issuing a decree " + + "- make sure you visit " + + "the new museum " + + "while you are here.'" + + val rightPage = "" + + "Obituaries " + + "

" + + "Goblin-Died
" + + "Giant Rat-Died
" + + "Unicorn-Died
" + + "Varrock Guard-Died
" + + "Varrock Guard-Died
" + + "Varrock Guard-Died
" + + "Bear-Died." + + "

" + + "Classifieds" + + "

" + + "Lowe's Archery " + + "Emporium for the " + + "finest ranging " + + "weapons in town!" + + "

" + + "Time to party! Visit " + + "the Fancy Dress " + + "Shop for all your " + + "party outfits." + + "

" + + "The Dancing " + + "Donkey - cold beer " + + "always in stock." + } + override fun defineListeners() { + on(Items.NEWSPAPER_11169, IntType.ITEM, "read") { player, _ -> + openInterface(player, NEWSPAPER_INTERFACE_530) + setInterfaceText(player, leftPage ,NEWSPAPER_INTERFACE_530, 7) + setInterfaceText(player, rightPage ,NEWSPAPER_INTERFACE_530, 8) + return@on true + } + + on(Items.AL_KHARID_FLYER_7922, IntType.ITEM, "read") { player, _ -> + sendDialogueLines(player, "Come to the Al Kharid Market place! High quality", "produce at low, low prices! Show this flyer to a", "merchant for money off your next purchase,", "courtesy of Ali Morrisane!") + return@on true + } + } +} diff --git a/Server/src/main/content/global/handlers/item/PetRock.kt b/Server/src/main/content/global/handlers/item/PetRock.kt index d91763171..355ea3742 100644 --- a/Server/src/main/content/global/handlers/item/PetRock.kt +++ b/Server/src/main/content/global/handlers/item/PetRock.kt @@ -1,56 +1,66 @@ package content.global.handlers.item -import core.api.openDialogue -import org.rs09.consts.Items +import core.api.* import core.game.dialogue.DialogueFile -import core.game.interaction.InteractionListener +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.impl.Projectile.getLocation +import core.game.world.map.Location +import core.game.world.update.flag.context.Animation import core.tools.END_DIALOGUE +import org.rs09.consts.Items -class PetRockPlugin : InteractionListener { +class PetRockListener : InteractionListener { override fun defineListeners() { - on(Items.PET_ROCK_3695, IntType.ITEM, "interact"){ player, _ -> - openDialogue(player, PetRockDialogue()) - return@on true - } + on(Items.PET_ROCK_3695, IntType.ITEM, "interact") { player, _ -> + openDialogue(player, PetRockDialogue()) + return@on true + } } } -class PetRockDialogue() : DialogueFile() { +class PetRockDialogue : DialogueFile() { override fun handle(componentID: Int, buttonID: Int) { when(stage) { - 0 -> options("Talk", "Stroke", "Feed", "Fetch", "Stay").also { stage++ } - 1 -> { - when(buttonID) { - 1 -> { - playerl(core.game.dialogue.FacialExpression.FRIENDLY, "Who's a good rock then? Yes you are... You're such a good rock... ooga booga googa.") - player!!.sendMessage("Your rock seems a little happier.") - stage = END_DIALOGUE - } - 2 -> { - player!!.sendMessage("You stroke your pet rock.") - // Missing animation - player!!.sendMessage("Your rock seems much happier.") - end() - } - 3 -> { - player!!.sendMessage("You try and feed the rock.") - player!!.sendMessage("Your rock doesn't seem hungry.") - end() - } - 4 -> { - playerl(core.game.dialogue.FacialExpression.FRIENDLY, "Want to fetch the stick, rock? Of course you do...") - // Missing animation - stage = END_DIALOGUE - } - 5 -> { - playerl(core.game.dialogue.FacialExpression.FRIENDLY, "Be a good rock...") - player!!.sendMessage("You wait a few seconds and pick your rock back up and pet it.") - // Missing animation - stage = END_DIALOGUE - } + 0 -> options("Talk", "Stroke", "Feed", "Fetch", "Stay").also { stage++ } + 1 -> when(buttonID) { + 1 -> { + sendPlayerDialogue(player!!, "Who's a good rock, then? Yes, you are! You're such a good rock. Ooga booga googa.", FacialExpression.FRIENDLY) + sendMessage(player!!, "Your rock seems a little happier.") + stage = END_DIALOGUE + } + 2 -> { + end() + sendMessage(player!!, "You stroke your pet rock.") + animate(player!!, 1333, false) + //waits for the animation to finish before sending the message + queueScript(player!!, animationDuration(Animation(1333)), QueueStrength.SOFT) { + sendMessage(player!!, "Your rock seems much happier.") + return@queueScript stopExecuting(player!!) + } + } + 3 -> { + end() + sendMessage(player!!, "You try and feed the rock.") + sendMessage(player!!, "Your rock doesn't seem hungry.") + } + 4 -> { + sendPlayerDialogue(player!!, "Want to fetch the stick, rock? Of course you do.", FacialExpression.FRIENDLY) + face(player!!, Location.getRandomLocation(getLocation(player), 2, true)) + visualize(player!!, 6665, 1157) + spawnProjectile(getLocation(player), Location.getRandomLocation(getLocation(player), 5, true), 1158, 40, 0,150, 250, 25) + stage = END_DIALOGUE + } + 5 -> { + face(player!!, Location.getRandomLocation(getLocation(player), 2, true)) + sendPlayerDialogue(player!!, "Be a good rock...", FacialExpression.FRIENDLY) + sendMessage(player!!, "You wait a few seconds and pick your rock back up and pet it.") + visualize(player!!, anim = 6664, gfx = 1156) + stage = END_DIALOGUE + } } - } } } } \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/item/PlayerPeltables.kt b/Server/src/main/content/global/handlers/item/PlayerPeltables.kt index fc1a63790..931a39ef2 100644 --- a/Server/src/main/content/global/handlers/item/PlayerPeltables.kt +++ b/Server/src/main/content/global/handlers/item/PlayerPeltables.kt @@ -1,8 +1,7 @@ package content.global.handlers.item import core.api.* -import core.game.interaction.Interaction -import core.game.interaction.Option +import core.game.interaction.* import core.game.node.Node import core.game.node.entity.impl.Projectile import core.game.node.entity.player.Player @@ -11,8 +10,6 @@ import core.game.system.task.Pulse import core.game.world.map.path.Pathfinder import core.game.world.update.flag.context.Graphics import org.rs09.consts.Items -import core.game.interaction.InteractionListener -import core.game.interaction.IntType class PlayerPeltables : InteractionListener { @@ -40,7 +37,7 @@ class PlayerPeltables : InteractionListener { } private fun removePlayerOps(player: Player, _node: Node) : Boolean { - Interaction.sendOption(player, 0, "null") + InteractPlugin.sendOption(player, 0, "null") return true } 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/SnowscapeSatchelListener.kt b/Server/src/main/content/global/handlers/item/SnowscapeSatchelListener.kt new file mode 100755 index 000000000..872c94341 --- /dev/null +++ b/Server/src/main/content/global/handlers/item/SnowscapeSatchelListener.kt @@ -0,0 +1,183 @@ +package content.global.handlers.item + +import core.api.* +import core.game.container.Container +import content.global.skill.summoning.familiar.BurdenContainerListener +import core.game.node.Node +import core.game.node.entity.player.Player +//import core.game.node.entity.player.info.login.PlayerSaver +import core.game.node.item.Item +import core.game.interaction.InteractionListener +import core.game.interaction.IntType +import org.json.simple.JSONArray +import org.json.simple.JSONObject +import org.json.simple.parser.JSONParser +import org.rs09.consts.Items + +import core.game.component.Component +import core.game.component.CloseEvent +import core.game.container.access.InterfaceContainer +import content.global.handlers.scenery.BankBoothListener +import content.global.handlers.npc.BankerNPC + +/** + * Listener for satchels from Creature Creation, repurposed for Snowscape + * They are repurposed as item pouches, using the summoning familiar inventory interface + * @author + */ +class SnowscapeSatchelListener : InteractionListener { + + companion object { + + val plainSatchelId = 10877 + val greenSatchelId = 10878 + val redSatchelId = 10879 + val blackSatchelId = 10880 + val goldSatchelId = 10881 + val runeSatchelId = 10882 + val satchelIds = intArrayOf(plainSatchelId,greenSatchelId,redSatchelId,blackSatchelId,goldSatchelId,runeSatchelId) + + + + + private fun transferItem(player: Player, satchelId: Int, item: Item, amount: Int, withdraw: Boolean, asNote: Boolean = false) { + if (withdraw == false && !isAllowed(player, satchelId, unnote(item).getId())) { + player.sendMessage(item.getName() + " cannot be stored in the " + Item(satchelId).getName() + ".") + return + } + if (withdraw == true && asNote == false && satchelId == plainSatchelId) { + player.sendMessage("This satchel can only be emptied by a banker. Use it on a bank booth to get the items.") + return + } + val to = if (withdraw) player.getInventory() else getSatchel(player, satchelId) + val from = if (withdraw) getSatchel(player, satchelId) else player.getInventory() + val maximum = if (asNote) to.getMaximumAdd(note(item)) else to.getMaximumAdd(unnote(item)) + if (maximum < 1 && withdraw == false) { + player.sendMessage("There is not enough space for the " + item.getName() + " in the satchel.") + return + } + val finalAmount = kotlin.comparisons.minOf(amount, from.getAmount(item), maximum) + val finalItem = Item(item.getId(), finalAmount) + if (finalAmount > 0 && from.remove(finalItem)) { + if (asNote) to.add(note(finalItem)) else to.add(unnote(finalItem)) + } + } + + private fun emptySatchel(player: Player, satchelId: Int, asNote: Boolean = false) { + if (asNote == false && satchelId == plainSatchelId) { + player.sendMessage("This satchel can only be emptied by a banker. Use it on a Bank Booth to get the items.") + return + } + val satchel = getSatchel(player, satchelId) + repeat(satchel.capacity()) { index -> + var item = satchel.get(index) + if (item != null) { + transferItem(player, satchelId, item, satchel.getAmount(item), true, asNote) + } + } + } + + + //Function to handle constructing the correct interfaces when the satchel is opened + private fun openSatchel(player: Player, node: Node) { + val satchelId = node.asItem().getId() + val satchel = getSatchel(player, satchelId) + if (satchel.getListeners().count() < 1) satchel.register(BurdenContainerListener(player)) + + player.getInterfaceManager().open(Component(671)).setCloseEvent(CloseEvent { player, component -> + player.getInterfaceManager().closeSingleTab() + removeAttribute(player, "openSatchel") + return@CloseEvent true + } + ) + setAttribute(player, "openSatchel", satchelId) + satchel.shift() + player.getInterfaceManager().openSingleTab(Component(665)) + InterfaceContainer.generateItems(player, player.getInventory().toArray(), arrayOf("Store-X","Store-All","Store-10","Store-5","Store-1"),665,0,7,4,93) + InterfaceContainer.generateItems(player, satchel.toArray(), arrayOf("Withdraw-X","Withdraw-All","Withdraw-10","Withdraw-5","Withdraw-1"),671,27,5,6,30) + } + + // This is called by BurdenInterfacePlugin.java when the player interacts with the familiar interface and the "openSatchel" attribute exists + fun satchelInterfaceAction(player: Player, component: Component, opcode: Int, button: Int, slot: Int, itemId: Int) { + val satchelId = getAttribute(player, "openSatchel", 0) + val withdraw = component.getId() == 671 + val container = if (withdraw) getSatchel(player, satchelId) else player.getInventory() + val item = if (slot >= 0 && slot < container.capacity()) container.get(slot) else null + if (item == null && button != 29) return + when (opcode) { + 155 -> if (button == 29) emptySatchel(player, satchelId) else transferItem(player, satchelId, item!!, 1, withdraw) + 196 -> transferItem(player, satchelId, item!!, 5, withdraw) + 124 -> transferItem(player, satchelId, item!!, 10, withdraw) + 199 -> transferItem(player, satchelId, item!!, container.getAmount(item), withdraw) + 234 -> sendInputDialogue(player, true, "Enter the amount:") { value -> transferItem(player, satchelId, item!!, value as Int, withdraw) } + 9 -> player.sendMessage(item!!.getDefinition().getExamine()) + } + } + + + + fun getSatchel(player: Player, satchelId: Int): Container { + var satchel: Container? = null + when (satchelId) { + plainSatchelId -> satchel = player.plainSatchel + greenSatchelId -> satchel = player.greenSatchel + redSatchelId -> satchel = player.redSatchel + blackSatchelId -> satchel = player.blackSatchel + goldSatchelId -> satchel = player.goldSatchel + runeSatchelId -> satchel = player.runeSatchel + } + return satchel!! + } + + // Checks if the item is allowed in the satchel. The NPCDropTables.java function calls this on satchels the player is carrying. + fun isAllowed(player: Player, satchelId: Int, itemId: Int): Boolean { + when (satchelId) { + plainSatchelId -> return itemId !in satchelIds + greenSatchelId -> return (Item(itemId).getName().contains(" seed") || Item(itemId).getName().contains("Grimy") || Item(itemId).getName().contains("Clean ")) + redSatchelId -> return itemId in intArrayOf(*(12158..12168).toList().toIntArray()) || Item(itemId).getName().equals("Clue scroll") + blackSatchelId -> return itemId in intArrayOf(995) + goldSatchelId -> return itemId in intArrayOf(436,438,440,442,444,446,447,449,451,453,668,2892,2349,2351,2353,2355,2357,2359,2361,2363,2365,1601,1603,1605,1607,1609,1611,1613,1615,1617,1619,1621,1623,1625,1627,1629,1631,6571,6573) + runeSatchelId -> return itemId in intArrayOf(556,555,557,554,558,559,564,562,9075,561,563,560,565,566,4694,4695,4696,4697,4698,4699) + } + return false + } + } + + + + override fun defineListeners() { + + onUseAnyWith(IntType.ITEM, *satchelIds) { player, used, with -> + val satchelId = with.asItem().getId() + transferItem(player, satchelId, used.asItem(), player.getInventory().getAmount(used.asItem()),false) + return@onUseAnyWith true + } + + on(satchelIds, IntType.ITEM, "inspect","operate") { player, node -> + openSatchel(player, node) + return@on true + } + + on(satchelIds, IntType.ITEM, "empty") { player, node -> + emptySatchel(player, node.asItem().getId()) + return@on true + } + + // BankBoothListener values are public but BankChestListener values are not, so rather than edit the BankChestListener file I have copied the IDs here. 12309 is the Culinomancer's chest. + val bankChests = intArrayOf(3194,4483,10562,14382,16695,16696,21301,27662,27663) + val bankUseWiths = intArrayOf(*BankBoothListener.BANK_BOOTHS, 12309, *bankChests) + onUseWith(IntType.SCENERY, satchelIds, *bankUseWiths) { player, used, with -> + emptySatchel(player, used.asItem().getId(), true) + return@onUseWith true + } + + //6362 is the Ourania Altar banker. + val bankers = intArrayOf(*BankerNPC.NPC_IDS, *BankerNPC.SPECIAL_NPC_IDS, 6362) + + onUseWith(IntType.NPC, satchelIds, *bankers) { player, used, with -> + emptySatchel(player, used.asItem().getId(), true) + return@onUseWith true + } + + } +} diff --git a/Server/src/main/content/global/handlers/item/SnowscapeTeleorb.kt b/Server/src/main/content/global/handlers/item/SnowscapeTeleorb.kt new file mode 100644 index 000000000..789827f2c --- /dev/null +++ b/Server/src/main/content/global/handlers/item/SnowscapeTeleorb.kt @@ -0,0 +1,146 @@ +package content.global.handlers.item + +import core.api.* +import core.game.node.Node +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.game.node.item.Item +import core.game.interaction.InteractionListener +import core.game.interaction.IntType +import core.game.world.map.Location +import core.game.world.map.build.DynamicRegion +import core.game.world.map.zone.ZoneRestriction +import core.game.world.update.flag.context.Animation +import core.game.world.update.flag.context.Graphics +import core.game.node.entity.player.link.TeleportManager +import core.game.dialogue.* +import core.tools.START_DIALOGUE +import org.rs09.consts.Items +import org.rs09.consts.Sounds + +import core.game.component.Component + +/** + * Listener for the strange teleorb object. Normally this object is part of the While Guthix Sleeps quest, but it has been repurposed. + * The new purpose is a custom teleport artifact, allowing players to save a location and teleport back to it. + * @author + */ +class SnowscapeTeleorb : InteractionListener { + + // The teleorb item id + val teleorb = 14534 + + // How many law runes are required for one teleport charge + val runesPerTeleportNormal = 20 + val runesPerTeleportBoosted = 2 + + override fun defineListeners() { + on(teleorb, IntType.ITEM, "inspect") { player, node -> + inspect(player, node) + return@on true + } + on(teleorb, IntType.ITEM, "activate") { player, node -> + activate(player, node) + return@on true + } + } + private fun inspect(player: Player, node: Node) { + openDialogue(player,SnowscapeTeleorbDialogue()) + } + + private fun activate(player: Player, node: Node) { + val charges = getAttribute(player, "teleorb:charges",0) as Int + var runesPerTeleport = if (getAttribute(player, "snowscape:boostedmode", false)) runesPerTeleportBoosted else runesPerTeleportNormal + if (charges >= runesPerTeleport) { + val destination = getAttribute(player, "teleorb:location", Location.create(3236,3218,0)) + player.getTeleporter().send(destination, TeleportManager.TeleportType.ENTRANA_MAGIC_DOOR) + setAttribute(player, "/save:teleorb:charges", charges - runesPerTeleport) + player.sendMessage("Charges remaining: " + ((getAttribute(player, "teleorb:charges",0) as Int) / runesPerTeleport)) + rewardXP(player, Skills.MAGIC, 15.0 * runesPerTeleport) + } else { + player.sendMessage("The teleorb is out of charges.") + } + } + + +} + + + + +class SnowscapeTeleorbDialogue : DialogueFile() { + + // How many law runes are required for one teleport charge + val runesPerTeleportNormal = 20 + val runesPerTeleportBoosted = 2 + + val teleorb = 14534 + val lawRune = 563 + + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + START_DIALOGUE -> options("Update Location","Recharge","Show Info").also {stage++} + + 1 -> when (buttonID) { + 1 -> player?.let { + end() + val region = it.getViewport().getRegion() + if (region == null || region is DynamicRegion || it.getZoneMonitor().isRestricted(ZoneRestriction.OFF_MAP)) { + it.sendMessage("This location cannot be saved!") + } else { + setAttribute(it, "/save:teleorb:location", it.getLocation()) + sendInputDialogue(it, false, "Location saved. Enter a description of this location:",) { value -> + setAttribute(it, "/save:teleorb:description", value as String) + } + it.sendMessage("Location saved.") + it.graphics(Graphics(343)) + it.animate(Animation(1818)) + playAudio(it, Sounds.TELE_OTHER_CAST_199) + } + } + 2 -> options("Recharge with Law Runes","Recharge with additional orbs").also {stage++} + 3 -> player?.let { + end() + var runesPerTeleport = if (getAttribute(it, "snowscape:boostedmode", false)) runesPerTeleportBoosted else runesPerTeleportNormal + it.sendMessage("Current saved location: " + getAttribute(it, "teleorb:description", "Unknown").replace("_"," ")) + it.sendMessage("Charges remaining: " + ((getAttribute(it, "teleorb:charges",0) as Int) / runesPerTeleport)) + } + } + 2 -> when (buttonID) { + 1 -> player?.let { + end() + var runesPerTeleport = if (getAttribute(it, "snowscape:boostedmode", false)) runesPerTeleportBoosted else runesPerTeleportNormal + sendInputDialogue(it, true, "Each charge requires $runesPerTeleport Law Runes. How many runes would you like to use?",) { value -> + var amount = kotlin.math.min(amountInInventory(it,lawRune), value as Int) + if (removeItem(it, Item(lawRune, amount))) { + it.sendMessage("You charge the teleorb with $amount Law Runes.") + amount += (getAttribute(it, "teleorb:charges",0) as Int) + setAttribute(it, "/save:teleorb:charges", amount) + it.sendMessage("Current charges: " + ((getAttribute(it, "teleorb:charges",0) as Int) / runesPerTeleport)) + it.graphics(Graphics(141,96)) + it.animate(Animation(722)) + playAudio(it, Sounds.LUNAR_EMBUE_RUNES_2888) //Yes, Imbue is mispelled in the sounds list. + } + } + } + 2 -> player?.let { + end() + var runesPerTeleport = if (getAttribute(it, "snowscape:boostedmode", false)) runesPerTeleportBoosted else runesPerTeleportNormal + sendInputDialogue(it, true, "Each additional orb adds one charge. How many would you like to use?",) { value -> + var amount = kotlin.math.min(amountInInventory(it,teleorb) - 1, value as Int) + if (amount > 0 && removeItem(it, Item(teleorb, amount))) { + it.sendMessage("You destroy $amount teleorbs, fusing their energy into one.") + amount = amount * runesPerTeleport + amount += (getAttribute(it, "teleorb:charges",0) as Int) + setAttribute(it, "/save:teleorb:charges", amount) + it.sendMessage("Current charges: " + ((getAttribute(it, "teleorb:charges",0) as Int) / runesPerTeleport)) + it.graphics(Graphics(141,96)) + it.animate(Animation(722)) + playAudio(it, Sounds.LUNAR_EMBUE_RUNES_2888) + } + } + } + } + } + } +} diff --git a/Server/src/main/content/global/handlers/item/SpadeDigListener.kt b/Server/src/main/content/global/handlers/item/SpadeDigListener.kt index a19cbb1e7..4d9bad6f0 100644 --- a/Server/src/main/content/global/handlers/item/SpadeDigListener.kt +++ b/Server/src/main/content/global/handlers/item/SpadeDigListener.kt @@ -7,7 +7,7 @@ object SpadeDigListener { val listeners = HashMap Unit>() fun registerListener(location: Location, method: (Player) -> Unit){ - listeners.putIfAbsent(location,method); + listeners.putIfAbsent(location,method) } @JvmStatic diff --git a/Server/src/main/content/global/handlers/item/SpadeDigOptionPlugin.java b/Server/src/main/content/global/handlers/item/SpadeDigOptionPlugin.java index 88228fe35..70d478faa 100644 --- a/Server/src/main/content/global/handlers/item/SpadeDigOptionPlugin.java +++ b/Server/src/main/content/global/handlers/item/SpadeDigOptionPlugin.java @@ -7,6 +7,9 @@ import core.game.node.Node; import core.game.node.entity.player.Player; import core.plugin.Initializable; import core.plugin.Plugin; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.playAudio; /** * Represents the plugin used to handle the dig option on a spade. @@ -24,7 +27,7 @@ public class SpadeDigOptionPlugin extends OptionHandler { @Override public boolean handle(final Player player, Node node, String option) { - player.getAudioManager().send(1470,1,1); + playAudio(player, Sounds.DIGSPADE_1470); if (!DigSpadeHandler.dig(player)) { player.sendMessage("You dig but find nothing."); } diff --git a/Server/src/main/content/global/handlers/item/TeleTabsListener.kt b/Server/src/main/content/global/handlers/item/TeleTabsListener.kt new file mode 100644 index 000000000..acf1ee45b --- /dev/null +++ b/Server/src/main/content/global/handlers/item/TeleTabsListener.kt @@ -0,0 +1,87 @@ +package content.global.handlers.item + +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 +import content.data.Quests + +import core.cache.def.impl.ItemDefinition; + +class TeleTabsListener : InteractionListener { + + 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, { + player -> hasRequirement(player, Quests.LUNAR_DIPLOMACY); + }), + BLOOD_ALTAR_TELEPORT(13610, Location.create(3559, 9778, 0), 0.0, { + player -> hasRequirement(player, Quests.LEGACY_OF_SEERGAZE); + }), + BODY_ALTAR_TELEPORT(13604, Location.create(3055, 3443, 0), 0.0), + CAMELOT_TELEPORT(8010, Location.create(2757, 3477, 0), 55.5), + CHAOS_ALTAR_TELEPORT(13606, Location.create(3058, 3593, 0), 0.0), + COSMIC_ALTAR_TELEPORT(13605, Location.create(2411, 4380, 0), 0.0, { + player -> hasRequirement(player, Quests.LOST_CITY); + }), + DEATH_ALTAR_TELEPORT(13609, Location.create(1863, 4639, 0), 0.0, { + player -> hasRequirement(player, Quests.MOURNINGS_END_PART_II); + }), + EARTH_ALTAR_TELEPORT(13602, Location.create(3304, 3472, 0), 0.0), + FALADOR_TELEPORT(8009, Location.create(2966, 3380, 0), 47.0), + FIRE_ALTAR_TELEPORT(13603, Location.create(3311, 3252, 0), 0.0), + LAW_ALTAR_TELEPORT(13608, Location.create(2857, 3378, 0), 0.0, { + player -> if (!ItemDefinition.canEnterEntrana(player)) (player.sendMessage("The power of Saradomin prevents you from taking armour or weaponry to Entrana.") !is Unit) else true; // ugly way of doing this, but the sendMessage function returns a Unit type, so by using !is Unit it becomes false. + }), + LUMBRIDGE_TELEPORT(8008, Location.create(3222, 3218, 0), 41.0), + MIND_ALTAR_TELEPORT(13600, Location.create(2979, 3510, 0), 0.0), + NATURE_ALTAR_TELEPORT(13607, Location.create(2868, 3013, 0), 0.0), + TELEKINETIC_GRAB(8022, Location.create(2836, 3285, 0), 0.0, { + player -> hasRequirement(player, Quests.DRAGON_SLAYER); + }), + VARROCK_TELEPORT(8007, Location.create(3212, 3423, 0), 35.00), + WATCH_TOWER_TELEPORT(8012, Location.create(2548, 3114, 0), 68.00, { + player -> hasRequirement(player, Quests.WATCHTOWER); + }), + WATER_ALTAR_TELEPORT(13601, Location.create(3182, 3162, 0), 0.0); + + companion object { + val idMap = values().map { it.item to it }.toMap() + fun forId(id: Int): TeleTabs? { + return idMap[id] + } + } + } + + override fun defineListeners() { + val tabIDs = TeleTabs.values().map { it.item }.toIntArray() + on(tabIDs, IntType.ITEM, "break") {player, node -> + val tab = node.id + val tabEnum = TeleTabs.forId(tab) + if (tabEnum != null && inInventory(player,tab)) { + val tabloc = tabEnum.location + if (inInventory(player, tab)) { + 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.") + } + } + } + } + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/item/TeleTabsOptionPlugin.java b/Server/src/main/content/global/handlers/item/TeleTabsOptionPlugin.java deleted file mode 100644 index 73de7500e..000000000 --- a/Server/src/main/content/global/handlers/item/TeleTabsOptionPlugin.java +++ /dev/null @@ -1,160 +0,0 @@ -package content.global.handlers.item; - -import core.cache.def.impl.ItemDefinition; -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.TeleportType; -import core.game.node.item.Item; -import core.game.world.map.Location; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Represents the plugin used to handle teletabs. - * @author 'Vexia - */ -@Initializable -public class TeleTabsOptionPlugin extends OptionHandler { - - /** - * Represents the enum of tele tabs. - * @author 'Vexia - */ - public enum TeleTabs { - ADDOUGNE_TELEPORT(8011, Location.create(2662, 3307, 0), 61), - AIR_ALTAR_TELEPORT(13599, Location.create(2978, 3296, 0), 0), - ASTRAL_ALTAR_TELEPORT(13611, Location.create(2156, 3862, 0), 0), - BLOOD_ALTAR_TELEPORT(13610, Location.create(3559, 9778, 0), 0), - BODY_ALTAR_TELEPORT(13604, Location.create(3055, 3443, 0), 0), - CAMELOT_TELEPORT(8010, Location.create(2757, 3477, 0), 55.5), - CHAOS_ALTAR_TELEPORT(13606, Location.create(3058, 3593, 0), 0), - COSMIC_ALTAR_TELEPORT(13605, Location.create(2411, 4380, 0), 0), - DEATH_ALTAR_TELEPORT(13609, Location.create(1863, 4639, 0), 0), - EARTH_ALTAR_TELEPORT(13602, Location.create(3304, 3472, 0), 0), - FALADOR_TELEPORT(8009, Location.create(2966, 3380, 0), 47), - FIRE_ALTAR_TELEPORT(13603, Location.create(3311, 3252, 0), 0), - LAW_ALTAR_TELEPORT(13608, Location.create(2857, 3378, 0), 0), - LUMBRIDGE_TELEPORT(8008, Location.create(3222, 3218, 0), 41), - MIND_ALTAR_TELEPORT(13600, Location.create(2979, 3510, 0), 0), - NATURE_ALTAR_TELEPORT(13607, Location.create(2868, 3013, 0), 0), - VARROCK_TELEPORT(8007, Location.create(3212, 3423, 0), 35), - WATCH_TOWER_TELEPORT(8012, Location.create(2548, 3114, 0), 68), - WATER_ALTAR_TELEPORT(13601, Location.create(3182, 3162, 0), 0); - - /** - * Method used to get the teletab by the id. - * @param id the id. - * @return the teletab. - */ - public static TeleTabs forId(int id) { - for (TeleTabs tab : TeleTabs.values()) { - if (tab == null) - continue; - if (tab.getItem() == id) - return tab; - } - return null; - } - - /** - * The location. - */ - private Location location; - - /** - * The item id. - */ - private int item; - - /** - * The experience gained. - */ - private double exp; - - /** - * Constructs a new {@code TeleTabsOptionPlugin} {@code Object}. - * @param item the item. - * @param location the location. - * @param exp the exp. - */ - TeleTabs(int item, Location location, double exp) { - setItem(item); - setLocation(location); - this.exp = exp; - } - - /** - * @return the item - */ - public int getItem() { - return item; - } - - /** - * @return the location - */ - public Location getLocation() { - return location; - } - - /** - * @param item the item to set - */ - private void setItem(int item) { - this.item = item; - } - - /** - * @param location the location to set - */ - private void setLocation(Location location) { - this.location = location; - } - - /** - * @return the exp. - */ - public double getExp() { - return exp; - } - - /** - * @param exp the exp to set. - */ - private void setExp(double exp) { - this.exp = exp; - } - } - - @Override - public boolean handle(final Player player, Node node, String option) { - final Item item = (Item) node; - if (item == null) { - return false; - } - final TeleTabs tab = TeleTabs.forId(item.getId()); - if (tab == null) { - return false; - } - player.getInterfaceManager().close(); - player.lock(5); - if (player.getInventory().contains(item.getId(), 1)) { - if (player.getTeleporter().send(tab.getLocation(), TeleportType.TELETABS, 1)) { - player.getInventory().remove(new Item(item.getId()), item.getSlot(), true); - } - } - return true; - } - - @Override - public boolean isWalk() { - return false; - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ItemDefinition.setOptionHandler("break", this); - return this; - } -} diff --git a/Server/src/main/content/global/handlers/item/TeleportCrystalPlugin.java b/Server/src/main/content/global/handlers/item/TeleportCrystalPlugin.java index dc779e885..5bbda0ffc 100644 --- a/Server/src/main/content/global/handlers/item/TeleportCrystalPlugin.java +++ b/Server/src/main/content/global/handlers/item/TeleportCrystalPlugin.java @@ -13,6 +13,9 @@ import core.game.world.map.Location; 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. * @author 'Vexia @@ -32,6 +35,8 @@ public final class TeleportCrystalPlugin extends OptionHandler { @Override public boolean handle(Player player, Node node, String option) { + if (!hasRequirement(player, Quests.MOURNINGS_END_PART_I)) + return true; if (!WildernessZone.checkTeleport(player, 20)) { player.getPacketDispatch().sendMessage("The crystal is unresponsive."); return true; diff --git a/Server/src/main/content/global/handlers/item/ToyListeners.kt b/Server/src/main/content/global/handlers/item/ToyListeners.kt index 130ecae84..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,14 +21,19 @@ 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 private val SNOWGLOBE_SNOW = Graphics(1284) //When Animation STOMP is playing this gfx also plays + private val SPINNING_PLATE_SPIN = Animation(1902) + private val SPINNING_PLATE_PUT_DOWN = Animation(1904) + private val TOY_KITE_FLY = Animation(8990) val YOYO_PLAY = Animation(1457) val YOYO_LOOP = Animation(1458) val YOYO_WALK = Animation(1459) @@ -31,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 @@ -41,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 } @@ -61,18 +73,33 @@ class ToyListeners : InteractionListener { animate(player, SNOWGLOBE_SHAKE) runTask(player, 3) { animate(player, SNOWGLOBE_HOLDFACE) - runTask(player){ + runTask(player) { openInterface(player, SNOWGLOBE_INTERFACE) } } return@on true } - on(Items.YO_YO_4079, IntType.ITEM, "play", "loop", "walk", "crazy"){ player, _ -> + on(Items.SPINNING_PLATE_4613, IntType.ITEM, "spin") { player, _ -> + lockInteractions(player, 2) + animate(player, SPINNING_PLATE_SPIN) + runTask(player, 2) { + animate(player, SPINNING_PLATE_PUT_DOWN) + } + return@on true + } + + 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, _ -> 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) @@ -81,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!") @@ -98,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/book/GeneralRuleBook.kt b/Server/src/main/content/global/handlers/item/book/GeneralRuleBook.kt new file mode 100644 index 000000000..12a6be03f --- /dev/null +++ b/Server/src/main/content/global/handlers/item/book/GeneralRuleBook.kt @@ -0,0 +1,209 @@ +package content.global.handlers.item.book + +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.* +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.player.Player +import core.game.world.GameWorld +import org.rs09.consts.Items + +/** + * General Rule Book + * This is not an item book, but opened when asking a Town Crier for the Rules + * @author ovenbreado + * @author 'Vexia + */ +class GeneralRuleBook { + companion object { + private const val BLUE = "" + private val SERVER_NAME = GameWorld.settings!!.name + private val TITLE = "$SERVER_NAME Rules" + private val CONTENTS = arrayOf( + PageSet( + Page( + BookLine("Rules", 38), + BookLine("1 - Offensive Language", 104), + BookLine("2 - Item Scamming", 106), + BookLine("3 - Password Scamming", 108), + BookLine("4 - Cheating/Bug Abuse", 110), + BookLine("5 - Staff Impersonation", 112), + BookLine("6 - Account Sharing/Trading", 114), + BookLine("7 - Using Third Party", 116), + BookLine("Software", 118), + BookLine("8 - Multi Logging-in", 120), + BookLine("9 - Encouraging Others to", 122), + BookLine("Break Rules", 124), + BookLine("10 - False Representation", 126), + BookLine("11 - Website Advertising", 128) + ), + Page( + BookLine("12 - Read World Item", 134), + BookLine("Trading", 136), + BookLine("13 - Asking For Personal", 138), + BookLine("Details", 140), + BookLine("14 - Misuse of Official", 142), + BookLine("Forums", 144), + BookLine("15 - Advert Blocking", 146) + ) + ), + PageSet( + Page( + BookLine(BLUE + "1 - Offensive Language", 38), + BookLine("You must not use any", 40), + BookLine("language which is offensive,", 41), + BookLine("racist or obscene. Remember,", 42), + BookLine("it's nice to be nice!", 43) + ), Page( + BookLine(BLUE + "2 - Item Scamming", 53 ), + BookLine("You must not scam or", 55), + BookLine("deceive other players. This", 56), + BookLine("includes claiming that items", 57), + BookLine("are rare when they are not,", 58), + BookLine("team scamming or telling", 59), + BookLine("players that you can", 60), + BookLine("upgrade' their armour in", 61), + BookLine("any way!", 62) + ) + ), + PageSet( + Page( + BookLine(BLUE + "3 - Password Scamming", 38), + BookLine("Asking for - or trying to", 40), + BookLine("obtain - another player's", 41), + BookLine("password in any way will not", 42), + BookLine("be tolerated, even in jest!", 43) + ), Page( + BookLine(BLUE + "4 - Cheating/Bug Abuse", 53), + BookLine("A bug is a technical glitch", 55), + BookLine("found in the game. You", 56), + BookLine("must not use or attempt to", 57), + BookLine("use any cheats or errors that", 58), + BookLine("you find in our software.", 59), + BookLine("Any bugs found must be", 60), + BookLine("reported to " + SERVER_NAME, 61), + BookLine("immediately, by clicking the", 62), + BookLine("'Report a bug/fault' link", 63), + BookLine("found on the main page.", 64) + ) + ), + PageSet( + Page( + BookLine(BLUE + "5 - Staff Impersonation", 38), + BookLine("You should not attempt to", 40), + BookLine("impersonate " + SERVER_NAME + " staff in", 41), + BookLine("any way.", 42) + ), Page( + BookLine(BLUE + "6 - Account Sharing/Trading", 53), + BookLine("Each account should only be", 55), + BookLine("used by ONE person and", 56), + BookLine("ONE person alone.", 57), + BookLine("Remember that trying to", 58), + BookLine("buy, sell, borrow or give", 59), + BookLine("away an account is against", 60), + BookLine("the rules and will land you in", 61), + BookLine("trouble when caught!", 62) + ) + ), + PageSet( + Page( + BookLine( + BLUE + "7 - Using Third Party
" + BLUE + "Software

You must not use other
programs to gain an unfair
advantage in the game.", + 38 + ) + ), Page( + BookLine( + BLUE + "8 - Multi Logging-in

If you create more than one
" + SERVER_NAME + " account, they
must not interact. This
includes giving items to a
friend to transfer to another
account you own.", + 53 + ) + ) + ), + PageSet( + Page( + BookLine( + BLUE + "9 - Encouraging Others to
" + BLUE + "Break Rules

You must not encourage
others to break any of the
" + SERVER_NAME + " rules.", + 38 + ), BookLine( + BLUE + "10 - False Representation

You must not misuse
" + SERVER_NAME + " Customer
Support.Trying to get
another player into trouble
by reporting them for no
reason or framing them is an
abuse of the Customer
Support Team's service.
want to help as many
players as possible and so the
Customer Support Team's
service must be used
appropriately and treated with", + 53 + ) + ) + ), + PageSet( + Page( + BookLine("respect at all times.", 38), + BookLine( + BLUE + "11 - Website Advertising

You are not allowed to
actively advertise any
websites, fan-sites, IRC
channels or product
anywhere in " + SERVER_NAME + ",
including the " + SERVER_NAME + "
forums.", + 53 + ) + ), Page() + ), + PageSet( + Page( + BookLine( + BLUE + "12 - Real World Item
" + BLUE + "Trading

" + SERVER_NAME + " items must only
be exchanged for other items
or service within the game.
Buying or selling items
outside of the " + SERVER_NAME + "
environment is not in the
spirit of the game and is easy
for " + SERVER_NAME + " to trace!", + 38 + ), BookLine( + BLUE + "13 - Asking for Personal
" + BLUE + "Details

To protect player's safety
and privacy, you must not
ask for personal details. This
includes full names, phone
numbers, MSN, AIM, email
and home/school addresses!", + 53 + ) + ) + ), + PageSet( + Page( + BookLine( + BLUE + "14 - Misuse of Official
" + BLUE + "Forums

Forums must be used in
accordance with the Forum
Code of Conduct and treated
with respect at all times.", + 38 + ), BookLine( + BLUE + "15 - Advert Blocking

Blocking the adverts in the
free to play version of
" + SERVER_NAME + " is against the
rules.", + 53 + ) + ) + ) + ) + private fun display(player: Player, pageNum: Int, buttonID: Int) : Boolean { + if (pageNum == 0) { + when (buttonID) { + 104 -> setAttribute(player, "bookInterfaceCurrentPage", 1) + 106 -> setAttribute(player, "bookInterfaceCurrentPage", 1) + 108 -> setAttribute(player, "bookInterfaceCurrentPage", 2) + 110 -> setAttribute(player, "bookInterfaceCurrentPage", 2) + 112 -> setAttribute(player, "bookInterfaceCurrentPage", 3) + 114 -> setAttribute(player, "bookInterfaceCurrentPage", 3) + 116 -> setAttribute(player, "bookInterfaceCurrentPage", 4) + 118 -> setAttribute(player, "bookInterfaceCurrentPage", 4) + 120 -> setAttribute(player, "bookInterfaceCurrentPage", 4) + 122 -> setAttribute(player, "bookInterfaceCurrentPage", 5) + 124 -> setAttribute(player, "bookInterfaceCurrentPage", 5) + 126 -> setAttribute(player, "bookInterfaceCurrentPage", 5) + 128 -> setAttribute(player, "bookInterfaceCurrentPage", 6) + 134 -> setAttribute(player, "bookInterfaceCurrentPage", 7) + 136 -> setAttribute(player, "bookInterfaceCurrentPage", 7) + 138 -> setAttribute(player, "bookInterfaceCurrentPage", 7) + 140 -> setAttribute(player, "bookInterfaceCurrentPage", 7) + 142 -> setAttribute(player, "bookInterfaceCurrentPage", 8) + 144 -> setAttribute(player, "bookInterfaceCurrentPage", 8) + 146 -> setAttribute(player, "bookInterfaceCurrentPage", 8) + } + } else { + when (buttonID) { + 159 -> setAttribute(player, "bookInterfaceCurrentPage", 0) + } + } + BookInterface.pageSetup(player, BookInterface.FANCY_BOOK_2_27, TITLE, CONTENTS) + if (getAttribute(player,"bookInterfaceCurrentPage", 0) != 0) { + player.packetDispatch.sendInterfaceConfig(BookInterface.FANCY_BOOK_2_27, 159, false) + player.packetDispatch.sendString("Index", BookInterface.FANCY_BOOK_2_27, 159) + } + return true + } + + /** Since the Town Crier shows you the book, there is no item here. */ + fun openBook(player: Player) { + BookInterface.openBook(player, BookInterface.FANCY_BOOK_2_27, ::display) + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/item/book/GoblinBook.kt b/Server/src/main/content/global/handlers/item/book/GoblinBook.kt new file mode 100644 index 000000000..5f9661fa6 --- /dev/null +++ b/Server/src/main/content/global/handlers/item/book/GoblinBook.kt @@ -0,0 +1,252 @@ +package content.global.handlers.item.book + +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.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.player.Player +import org.rs09.consts.Items + +/** + * Goblin Book + * https://youtu.be/6tdNqNa4zGw?t=155 April 23, 2009 + * @author ovenbreado + */ +class GoblinBook : InteractionListener { + companion object { + private const val RED = "" + private val TITLE = "The Book of the Big High War God" + private val CONTENTS = arrayOf( + PageSet( + Page( + BookLine(RED + "The Creation of the Goblins", 97), + BookLine("", 68), + BookLine("In beginning all gods have", 69), + BookLine("big war. Each god find army", 70), + BookLine("to fight for him.", 71), + BookLine("", 72), + BookLine("Big High War God not have", 73), + BookLine("army. Big High War God go", 74), + BookLine("to beardy-short-people and", 75), + BookLine("ask, Will you fight in my", 76), + BookLine("army? But beardy-short-", 77), + BookLine("people say, No, we fight for", 78), + BookLine("God of Shiny Light.", 79), + BookLine("", 80), + BookLine("Then Big High War God go", 81), + ), + Page( + BookLine("to demons and ask, Will you", 82), + BookLine("fight in my army? But", 83), + BookLine("demons say, No, we fight for", 84), + BookLine("God of Dark Fire.", 85), + BookLine("", 86), + BookLine("Then Big High War God go", 87), + BookLine("to tall people with keen blades", 88), + BookLine("and say, Will you fight in", 89), + BookLine("my army? But tall people", 90), + BookLine("with keen blades say, No,", 91), + BookLine("some of us fight for God of", 92), + BookLine("Shiny Light and some of us", 93), + BookLine("fight for God of Dark Fire,", 94), + BookLine("but none of us fight for you!", 95), + BookLine("", 96), + ), + ), + PageSet( + Page( + BookLine("Big High War God very sad.", 97), + BookLine("He travel east and west,", 68), + BookLine("north and south, across land", 69), + BookLine("looking for army to fight for", 70), + BookLine("him.", 71), + BookLine("", 72), + BookLine("Then goblins say, We fight", 73), + BookLine("for you! At that time goblins", 74), + BookLine("very weak, very small, soft", 75), + BookLine("skin. Not like goblins today!", 76), + BookLine("But Big High War God say,", 77), + BookLine("I will make you my army.", 78), + BookLine("So Big High War God train", 79), + BookLine("goblins so they very strong.", 80), + BookLine("He give them good armour", 81), + ), + Page( + BookLine("so they not be harmed. He", 82), + BookLine("make them strong in spirit so", 83), + BookLine("they not afraid of battle. He", 84), + BookLine("give them commanders so", 85), + BookLine("they know which way to go.", 86), + BookLine("He divide goblins into twelve", 87), + BookLine("tribes and send them into", 88), + BookLine("battle!", 89), + BookLine("", 90), + BookLine("Goblin armies fight very", 91), + BookLine("good! When other gods see", 92), + BookLine("this, they very jealous. So", 93), + BookLine("they say, We want goblins to", 94), + BookLine("fight for us too! So Big High", 95), + BookLine("War God take some tribes", 96), + ), + ), + PageSet( + Page( + BookLine("and sell them, one to God of", 97), + BookLine("Shiny Light and one to God", 68), + BookLine("of Dark Fire, and other", 69), + BookLine("tribes to other gods. But", 70), + BookLine("most still worship Big High", 71), + BookLine("War God who created them!", 72), + BookLine("", 73), + BookLine("", 74), + BookLine("", 75), + BookLine("", 76), + BookLine("", 77), + BookLine("", 78), + BookLine("", 79), + BookLine("", 80), + BookLine("", 81), + ), + Page( + BookLine(RED + "The Several Commandments", 82), + BookLine("", 83), + BookLine("These are commands of Big", 84), + BookLine("High War God! Obey all", 85), + BookLine("commands all time or Big", 86), + BookLine("High War God kill you very", 87), + BookLine("bad!", 88), + BookLine("", 89), + BookLine("Always to slay enemies of", 90), + BookLine("Big High War God. Enemies", 91), + BookLine("must die!", 92), + BookLine("", 93), + BookLine("Not to run from battle.", 94), + BookLine("Cowards must die!", 95), + BookLine("", 96), + ), + ), + PageSet( + Page( + BookLine("Not to show mercy. Merciful", 97), + BookLine("must die!", 68), + BookLine("", 69), + BookLine("Not to doubt Big High War", 70), + BookLine("God. Doubters must die!", 71), + BookLine("", 72), + BookLine("Not to make own plans.", 73), + BookLine("Thinkers must die!", 74), + BookLine("", 75), + BookLine("", 76), + BookLine("", 77), + BookLine("", 78), + BookLine("", 79), + BookLine("", 80), + BookLine("", 81), + ), + Page( + BookLine(RED + "The end of the war and the", 82), + BookLine(RED + "prophecy", 83), + BookLine("", 84), + BookLine("The war of gods last many", 85), + BookLine("lifetimes. Battle is glorious", 86), + BookLine("and many heroes live and", 87), + BookLine("die! Then all gods leave", 88), + BookLine("world, leave their armies", 89), + BookLine("behind. But goblins still", 90), + BookLine("soldiers, still fight! Goblins", 91), + BookLine("fight against tall people with", 92), + BookLine("keen blades. But tall people", 93), + BookLine("build cities with walls, they", 94), + BookLine("not want to fight. They not", 95), + BookLine("true soldiers like goblins! But", 96), + ), + ), + PageSet( + Page( + BookLine("now goblins not have enough", 97), + BookLine("to eat, and have no", 68), + BookLine("commanders to tell them who", 69), + BookLine("to fight. So goblin tribes fight", 70), + BookLine("one another.", 71), + BookLine("", 72), + BookLine("At last all goblin tribes have", 73), + BookLine("big battle on plain of mud.", 74), + BookLine("Battle last many days and", 75), + BookLine("many goblins die, battle is", 76), + BookLine("glorious! But goblin corpses", 77), + BookLine("cover the ground and it look", 78), + BookLine("like all die. ", 79), + BookLine("", 80), + BookLine("Then Hopespear of the", 81), + ), + Page( + BookLine("Narogoshunn tribe have", 82), + BookLine("vision of Big High War God.", 83), + BookLine("In night while soldiers rest he", 84), + BookLine("call leaders of all tribes", 85), + BookLine("together to give them", 86), + BookLine("message.", 87), + BookLine("", 88), + BookLine("This is the word of Big High", 89), + BookLine("War God: Battle is indeed", 90), + BookLine("glorious and goblins are", 91), + BookLine("soldiers but if there too", 92), + BookLine("much battle all goblins die!", 93), + BookLine("No more must goblin fight", 94), + BookLine("against goblin or tribe against", 95), + BookLine("tribe. Goblins must find other", 96), + ), + ), + PageSet( + Page( + BookLine("enemies to fight, but not fight", 97), + BookLine("each other!", 68), + BookLine("", 69), + BookLine("And this is the word of Big", 70), + BookLine("High War God: Today I", 71), + BookLine("cannot lead you, but", 72), + BookLine("someday I will send a new", 73), + BookLine("Commander to lead you.", 74), + BookLine("Under new Commander", 75), + BookLine("goblins will conquer all of", 76), + BookLine(ServerConstants.SERVER_NAME + ", every race and", 77), + BookLine("every god! And then Big", 78), + BookLine("High War God will return", 79), + BookLine("and sit on throne of bronze", 80), + BookLine("and rule over all. War will", 81), + ), + Page( + BookLine("end in victory and victory", 82), + BookLine("will last forever!", 83), + BookLine("", 84), + BookLine("So leaders of tribes stop", 85), + BookLine("battle. And on plain of mud", 86), + BookLine("all tribes build temple to Big", 87), + BookLine("High War God and offer", 88), + BookLine("sacrifices.", 89), + BookLine("", 90), + BookLine("", 91), + BookLine("", 92), + BookLine("", 93), + BookLine("", 94), + BookLine("", 95), + BookLine("", 96), + ), + ), + ) + private fun display(player: Player, pageNum: Int, buttonID: Int) : Boolean { + BookInterface.pageSetup(player, BookInterface.FANCY_BOOK_26, TITLE, CONTENTS) + return true + } + } + + override fun defineListeners() { + on(Items.GOBLIN_BOOK_10999, IntType.ITEM, "read") { player, _ -> + BookInterface.openBook(player, BookInterface.FANCY_BOOK_26, ::display) + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/item/book/GrimDiary.java b/Server/src/main/content/global/handlers/item/book/GrimDiary.java deleted file mode 100644 index d8aca6937..000000000 --- a/Server/src/main/content/global/handlers/item/book/GrimDiary.java +++ /dev/null @@ -1,97 +0,0 @@ -package content.global.handlers.item.book; - -import core.game.component.Component; -import core.game.dialogue.DialogueInterpreter; -import core.game.dialogue.DialoguePlugin; -import core.game.dialogue.book.Book; -import core.game.dialogue.book.BookLine; -import core.game.dialogue.book.Page; -import core.game.dialogue.book.PageSet; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * The grim diary book. - * @author Vexia - */ -@Initializable -public class GrimDiary extends Book { - - /** - * Represents the component interface. - */ - private static final Component INTERFACE = new Component(26); - - /** - * Represents the array of pages for this book. - */ - private static final PageSet[] PAGES = new PageSet[] { new PageSet(new Page(new BookLine("My Diary

by Grim

" + RED + "'''''''''''''''''''''''''''''''
" + RED + "''''12th Bennath
" + RED + "'''''''''''''''''''''''''''''''
I had such a busy day
dealing out death today
It's not as easy being grim.
Realised that Alfonse is
such a good servant. He





", 102)), new Page(new BookLine("seems to have the house
in full working order. I
shall have to congratulate
him tomorrow. Spent
some well-deserved time
sharpening my scythe -
Alfonse kindly reminded
me to put the sharpener
back in the cabinet. Such
a good chap.", 87))), new PageSet(new Page(new BookLine("

" + RED + "'''''''''''''''''''''''''''''''
" + RED + "'''13th Bennath
" + RED + "'''''''''''''''''''''''''''''''
Went to the Wilderness
today. Plenty of foolish
people surrendering their
lives to me without
thought. My back is
killing me from all the
standing around waiting.", 102)), new Page(new BookLine("Must get that seen to.
Got my robes stained
from one victim. Simply
ruined! I decided to
throw them in the
fireplace - will light the
fire soon. Oh, and I must
remember to call mother.", 87))), new PageSet(new Page(new BookLine("

" + RED + "'''''''''''''''''''''''''''''''
" + RED + "'''14th Bennath
" + RED + "'''''''''''''''''''''''''''''''
A tragic day. Alfonse and
I were in the garden
looking at the state of the
spider nest. I patted my
trusty servant on the
back in thanks for all his
hard work. Sadly, my
touch of death killed him", 102)), new Page(new BookLine("instantly. Feel quite guilty.", 87))), new PageSet(new Page(new BookLine("

" + RED + "'''''''''''''''''''''''''''''''
" + RED + "'''20th Bennath
" + RED + "'''''''''''''''''''''''''''''''
House is getting into
quite a state without
Alfonse - things strewn all
over the place. Almost
trod on the eye of my
mentor, eww. I put the
eye back on the shelf so
he can watch over me", 102)), new Page(new BookLine("and make sure I stay
true to his teachings.
Went into Varrock to
buy some new robes -
people kept running away
in fear, so it was difficult
to find a sale.", 87))), new PageSet(new Page(new BookLine("

" + RED + "'''''''''''''''''''''''''''''''
" + RED + "'''21st Bennath
" + RED + "'''''''''''''''''''''''''''''''
Decided to spend a bit of
time tidying today - it
really isn't an easy job.
In my activities I found
my old 'Voice of Doom'
potion on the bookcase -
perfect for giving people a
good scare. Oh, I do love", 102)), new Page(new BookLine("my job.


" + RED + "'''''''''''''''''''''''''''''''
" + RED + "'''22nd Bennath
" + RED + "'''''''''''''''''''''''''''''''
Ordered a new servant
today from the agency
and got a 10% discount
for getting past the
1000th servant mark.", 87))), new PageSet(new Page(new BookLine("Woo hoo! The agency
sent me his Last Will and
Testement. Shall have to
sit on that for a while.

" + RED + "'''''''''''''''''''''''''''''''
" + RED + "'''23rd Bennath
" + RED + "'''''''''''''''''''''''''''''''
Aquired some bones
today. Muncher should
appreciate them as a treat", 102)), new Page(new BookLine("the next time he behaves.
The problem is there
aren't many barriers he
can't devour, so I decided
to lock the bones up in
the chest.

" + RED + "'''''''''''''''''''''''''''''''" + RED + "
" + RED + "''''24th Bennath
" + RED + "'''''''''''''''''''''''''''''''
My plan to make undead", 87))), new PageSet(new Page(new BookLine("fish is going quite well. I
managed to obtain a
resurrection hourglass
today,so have added that
to the fishtank to finish
off the process. It's so
difficult to have pets when
everything you touch dies
horrifically. I remember
having a rabbit once that
exploded when I fed it a", 102)), new Page(new BookLine("carrot!

" + RED + "'''''''''''''''''''''''''''''''
" + RED + "'''25th Bennath
" + RED + "'''''''''''''''''''''''''''''''
Got back home today to
find Muncher has run
around the house
creating havok - he even
ate the postman! I", 87))), new PageSet(new Page(new BookLine("scolded him. So hopefully
he won't do it again
anytime soon. All my
things are in such a
mess. I'm surely going to
have to find someone to
tidy things up before my
new servant arrives.
Don't want to seem
totally incapable of looking
after myself.", 102)), new Page(new BookLine("", 87))), }; - - /** - * Represents all the array of all configs to hide. - */ - private static final int[] ALL = new int[] { 102, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101 }; - - /** - * Constructs a new {@code GrimDiary} {@code Object} - * @param player the player. - */ - public GrimDiary(final Player player) { - super(player, "Diary of Death", 2739823, PAGES); - } - - /** - * Constructs a new {@code GrimDiary} {@code Object} - */ - public GrimDiary() { - /** - * empty. - */ - } - - /** - * Method used to display a set of pages. - * @param set the set. - */ - public void display(Page[] set) { - player.lock(2); - player.getInterfaceManager().open(getInterface()); - player.getPacketDispatch().sendInterfaceConfig(getInterface().getId(), 65, index < 1 ? true : false); - boolean lastPage = index == 7; - player.getPacketDispatch().sendInterfaceConfig(getInterface().getId(), 67, lastPage ? true : false); - if (lastPage) { - finish(); - } - for (int i : ALL) { - player.getPacketDispatch().sendString("", 26, i); - } - player.getPacketDispatch().sendString(getName(), 26, 3); - player.getPacketDispatch().sendString("" + (index == 0 ? (index + 1) : (index + 2)), 26, 68); - player.getPacketDispatch().sendString("" + (index == 0 ? (index + 2) : (index + 3)), 26, 69); - for (Page page : set) { - for (BookLine line : page.getLines()) { - player.getPacketDispatch().sendString(line.getMessage(), getInterface().getId(), line.getChild()); - } - } - player.unlock(); - } - - /** - * Gets the interface. - * @return the interface. - */ - public Component getInterface() { - return INTERFACE; - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new GrimDiary(player); - } - - @Override - public int[] getIds() { - return new int[] { DialogueInterpreter.getDialogueKey("grim-diary") }; - } - -} diff --git a/Server/src/main/content/global/handlers/item/book/GrimDiary.kt b/Server/src/main/content/global/handlers/item/book/GrimDiary.kt new file mode 100644 index 000000000..1567391c3 --- /dev/null +++ b/Server/src/main/content/global/handlers/item/book/GrimDiary.kt @@ -0,0 +1,139 @@ +package content.global.handlers.item.book + +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.* +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.player.Player +import org.rs09.consts.Items + +/** + * Grim Diary Book + * https://www.youtube.com/watch?v=Vhmm_Gg0584 + * @author ovenbreado + * @author Vexia + */ +class GrimDiary : InteractionListener { + companion object { + private const val RED = "" + private const val LEFT_PAGE_CHILD = 97 + private const val RIGHT_PAGE_CHILD = 82 + private const val TITLE = "Diary of Death" + private val CONTENTS = arrayOf( + PageSet( + Page( + BookLine( + "My Diary

by Grim

" + RED + "'''''''''''''''''''''''''''''''
" + RED + "''''12th Bennath
" + RED + "'''''''''''''''''''''''''''''''
I had such a busy day
dealing out death today
It's not as easy being grim.
Realised that Alfonse is
such a good servant. He





", + LEFT_PAGE_CHILD + ) + ), + Page( + BookLine( + "seems to have the house
in full working order. I
shall have to congratulate
him tomorrow. Spent
some well-deserved time
sharpening my scythe -
Alfonse kindly reminded
me to put the sharpener
back in the cabinet. Such
a good chap.", + RIGHT_PAGE_CHILD + ) + ) + ), + PageSet( + Page( + BookLine( + "

" + RED + "'''''''''''''''''''''''''''''''
" + RED + "'''13th Bennath
" + RED + "'''''''''''''''''''''''''''''''
Went to the Wilderness
today. Plenty of foolish
people surrendering their
lives to me without
thought. My back is
killing me from all the
standing around waiting.", + LEFT_PAGE_CHILD + ) + ), + Page( + BookLine( + "Must get that seen to.
Got my robes stained
from one victim. Simply
ruined! I decided to
throw them in the
fireplace - will light the
fire soon. Oh, and I must
remember to call mother.", + RIGHT_PAGE_CHILD + ) + ) + ), + PageSet( + Page( + BookLine( + "

" + RED + "'''''''''''''''''''''''''''''''
" + RED + "'''14th Bennath
" + RED + "'''''''''''''''''''''''''''''''
A tragic day. Alfonse and
I were in the garden
looking at the state of the
spider nest. I patted my
trusty servant on the
back in thanks for all his
hard work. Sadly, my
touch of death killed him", + LEFT_PAGE_CHILD + ) + ), Page(BookLine("instantly. Feel quite guilty.", RIGHT_PAGE_CHILD)) + ), + PageSet( + Page( + BookLine( + "

" + RED + "'''''''''''''''''''''''''''''''
" + RED + "'''20th Bennath
" + RED + "'''''''''''''''''''''''''''''''
House is getting into
quite a state without
Alfonse - things strewn all
over the place. Almost
trod on the eye of my
mentor, eww. I put the
eye back on the shelf so
he can watch over me", + LEFT_PAGE_CHILD + ) + ), + Page( + BookLine( + "and make sure I stay
true to his teachings.
Went into Varrock to
buy some new robes -
people kept running away
in fear, so it was difficult
to find a sale.", + RIGHT_PAGE_CHILD + ) + ) + ), + PageSet( + Page( + BookLine( + "

" + RED + "'''''''''''''''''''''''''''''''
" + RED + "'''21st Bennath
" + RED + "'''''''''''''''''''''''''''''''
Decided to spend a bit of
time tidying today - it
really isn't an easy job.
In my activities I found
my old 'Voice of Doom'
potion on the bookcase -
perfect for giving people a
good scare. Oh, I do love", + LEFT_PAGE_CHILD + ) + ), + Page( + BookLine( + "my job.


" + RED + "'''''''''''''''''''''''''''''''
" + RED + "'''22nd Bennath
" + RED + "'''''''''''''''''''''''''''''''
Ordered a new servant
today from the agency
and got a 10% discount
for getting past the
1000th servant mark.", + RIGHT_PAGE_CHILD + ) + ) + ), + PageSet( + Page( + BookLine( + "Woo hoo! The agency
sent me his Last Will and
Testement. Shall have to
sit on that for a while.

" + RED + "'''''''''''''''''''''''''''''''
" + RED + "'''23rd Bennath
" + RED + "'''''''''''''''''''''''''''''''
Aquired some bones
today. Muncher should
appreciate them as a treat", + LEFT_PAGE_CHILD + ) + ), + Page( + BookLine( + "the next time he behaves.
The problem is there
aren't many barriers he
can't devour, so I decided
to lock the bones up in
the chest.

" + RED + "'''''''''''''''''''''''''''''''" + RED + "
" + RED + "''''24th Bennath
" + RED + "'''''''''''''''''''''''''''''''
My plan to make undead", + RIGHT_PAGE_CHILD + ) + ) + ), + PageSet( + Page( + BookLine( + "fish is going quite well. I
managed to obtain a
resurrection hourglass
today,so have added that
to the fishtank to finish
off the process. It's so
difficult to have pets when
everything you touch dies
horrifically. I remember
having a rabbit once that
exploded when I fed it a", + LEFT_PAGE_CHILD + ) + ), + Page( + BookLine( + "carrot!

" + RED + "'''''''''''''''''''''''''''''''
" + RED + "'''25th Bennath
" + RED + "'''''''''''''''''''''''''''''''
Got back home today to
find Muncher has run
around the house
creating havok - he even
ate the postman! I", + RIGHT_PAGE_CHILD + ) + ) + ), + PageSet( + Page( + BookLine( + "scolded him. So hopefully
he won't do it again
anytime soon. All my
things are in such a
mess. I'm surely going to
have to find someone to
tidy things up before my
new servant arrives.
Don't want to seem
totally incapable of looking
after myself.", + LEFT_PAGE_CHILD + ) + ) + ) + ) + private fun display(player:Player, pageNum: Int, buttonID: Int) : Boolean { + BookInterface.pageSetup(player, BookInterface.FANCY_BOOK_26, TITLE, CONTENTS) + return true + } + } + + override fun defineListeners() { + on(Items.THE_GRIM_REAPERS_DIARY_11780, IntType.ITEM, "read") { player, _ -> + BookInterface.openBook(player, BookInterface.FANCY_BOOK_26, ::display) + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/item/book/KeldagrimRuleBook.java b/Server/src/main/content/global/handlers/item/book/KeldagrimRuleBook.java deleted file mode 100644 index 1e8315d68..000000000 --- a/Server/src/main/content/global/handlers/item/book/KeldagrimRuleBook.java +++ /dev/null @@ -1,110 +0,0 @@ -package content.global.handlers.item.book; - -import core.game.component.Component; -import core.game.dialogue.DialoguePlugin; -import core.game.dialogue.book.Book; -import core.game.dialogue.book.BookLine; -import core.game.dialogue.book.Page; -import core.game.dialogue.book.PageSet; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Represents the strong hold of security book. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class KeldagrimRuleBook extends Book { - - /** - * Represents the id of the book. - */ - public static final int ID = 496107759; - - /** - * Represents the component interface. - */ - private static final Component INTERFACE = new Component(26); - - /** - * Represents the array of pages for this book. - */ - private static final PageSet[] PAGES = new PageSet[] { new PageSet(new Page(new BookLine("Rules", 102), new BookLine("1 - Offensive Language", 74), new BookLine("2 - Item Scamming", 75), new BookLine("3 - Password Scamming", 76), new BookLine("4 - Cheating/Bug Abuse", 77), new BookLine("5 - Staff Impersonation", 78), new BookLine("6 - Account Sharing/Trading", 79), new BookLine("7 - Using Third Party", 80), new BookLine("Software", 81), new BookLine("8 - Multi Logging-in", 82), new BookLine("9 - Encouraging Others to", 83), new BookLine("Break Rules", 84), new BookLine("10 - False Representation", 85), new BookLine("11 - Website Advertising", 86)), new Page(new BookLine("12 - Read World Item", 87), new BookLine("Trading", 88), new BookLine("13 - Asking For Personal", 89), new BookLine("Details", 90), new BookLine("14 - Misuse of Official", 91), new BookLine("Forums", 92), new BookLine("15 - Advert Blocking", 93))), new PageSet(new Page(new BookLine(BLUE + "1 - Offensive Language", 102), new BookLine("You must not use any", 74), new BookLine("language which is offensive,", 75), new BookLine("racist or obscene. Remember,", 76), new BookLine("it's nice to be nice!", 77)), new Page(new BookLine(BLUE + "2 - Item Scamming", 87), new BookLine("You must not scam or", 89), new BookLine("deceive other players. This", 90), new BookLine("includes claiming that items", 91), new BookLine("are rare when they are not,", 92), new BookLine("team scamming or telling", 93), new BookLine("players that you can", 94), new BookLine("upgrade' their armour in", 95), new BookLine("any way!", 96))), new PageSet(new Page(new BookLine(BLUE + "3 - Password Scamming", 102), new BookLine("Asking for - or trying to", 74), new BookLine("obtain - another player's", 75), new BookLine("password in any way will not", 76), new BookLine("be tolerated, even in jest!", 77)), new Page(new BookLine(BLUE + "4 - Cheating/Bug Abuse", 87), new BookLine("A bug is a technical glitch", 89), new BookLine("found in the game. You", 90), new BookLine("must not use or attempt to", 91), new BookLine("use any cheats or errors that", 92), new BookLine("you find in our software.", 93), new BookLine("Any bugs found must be", 94), new BookLine("reported to Keldagrim", 95), new BookLine("immediately, by clicking the", 96), new BookLine("'Report a bug/fault' link", 97), new BookLine("found on the main page.", 98))), new PageSet(new Page(new BookLine(BLUE + "5 - Staff Impersonation", 102), new BookLine("You should not attempt to", 74), new BookLine("impersonate Keldagrim staff in", 75), new BookLine("any way.", 76)), new Page(new BookLine(BLUE + "6 - Account Sharing/Trading", 87), new BookLine("Each account should only be", 89), new BookLine("used by ONE person and", 90), new BookLine("ONE person alone.", 91), new BookLine("Remember that trying to", 92), new BookLine("buy, sell, borrow or give", 93), new BookLine("away an account is against", 94), new BookLine("the rules and will land you in", 95), new BookLine("trouble when caught!", 96))), new PageSet(new Page(new BookLine(BLUE + "7 - Using Third Party
" + BLUE + "Software

You must not use other
programs to gain an unfair
advantage in the game.", 102)), new Page(new BookLine(BLUE + "8 - Multi Logging-in

If you create more than one
Keldagrim account, they
must not interact. This
includes giving items to a
friend to transfer to another
account you own.", 87))), new PageSet(new Page(new BookLine(BLUE + "9 - Encouraging Others to
" + BLUE + "Break Rules

You must not encourage
others to break any of the
Keldagrim rules.", 102), new BookLine(BLUE + "10 - False Representation

You must not misuse
Keldagrim Customer
Support.Trying to get
another player into trouble
by reporting them for no
reason or framing them is an
abuse of the Customer
Support Team's service.
want to help as many
players as possible and so the
Customer Support Team's
service must be used
appropriately and treated with", 87))), new PageSet(new Page(new BookLine("respect at all times.", 102), new BookLine(BLUE + "11 - Website Advertising

You are not allowed to
actively advertise any
websites, fan-sites, IRC
channels or product
anywhere in Keldagrim,
including the Keldagrim
forums.", 87)), new Page()), new PageSet(new Page(new BookLine(BLUE + "12 - Real World Item
" + BLUE + "Trading

Keldagrim items must only
be exchanged for other items
or service within the game.
Buying or selling items
outside of the Keldagrim
enviironment is not in the
spirit of the game and is easy
for Keldagrim to trace!", 102), new BookLine(BLUE + "13 - Asking for Personal
" + BLUE + "Details

To protect player's safety
and privacy, you must not
ask for personal details. This
includes full names, phone
numbers, MSN, AIM, email
and home/school addresses!", 87))), new PageSet(new Page(new BookLine(BLUE + "14 - Misuse of Official
" + BLUE + "Forums

Forums must be used in
accordance with the Forum
Code of Conduct and treated
with respect at all times.", 102), new BookLine(BLUE + "15 - Advert Blocking

Blocking the adverts in the
free to play version of
Keldagrim is against the
rules.", 87))) }; - - /** - * Constructs a new {@code ShieldofArravBook} {@code Object}. - */ - public KeldagrimRuleBook(final Player player) { - super(player, "Keldagrim Rules", -1, PAGES); - } - - /** - * Constructs a new {@code ShieldofArravBook} {@code Object}. - */ - public KeldagrimRuleBook() { - /** - * empty. - */ - } - - /** - * Method used to display a set of pages. - * @param set the set. - */ - @Override - public void display(Page[] set) { - player.lock(2); - player.getInterfaceManager().open(getInterface()); - player.getPacketDispatch().sendInterfaceConfig(getInterface().getId(), 65, index < 1 ? true : false); - boolean lastPage = index == 8; - player.getPacketDispatch().sendInterfaceConfig(getInterface().getId(), 67, lastPage ? true : false); - if (lastPage) { - finish(); - } - for (int i : getHiddenChilds()) { - player.getPacketDispatch().sendString("", 26, i); - } - int page1 = index == 0 ? 1 : index == 1 ? 3 : index == 2 ? 5 : index == 3 ? 7 : index == 4 ? 9 : index == 5 ? 11 : index == 6 ? 13 : index == 7 ? 15 : index == 8 ? 17 : index == 9 ? 19 : 21; - player.getPacketDispatch().sendString(getName(), 26, 3); - player.getPacketDispatch().sendString("" + page1, 26, 68); - player.getPacketDispatch().sendString("" + (page1 + 1), 26, 69); - for (Page page : set) { - for (BookLine line : page.getLines()) { - player.getPacketDispatch().sendString(line.getMessage(), getInterface().getId(), line.getChild()); - } - } - player.unlock(); - } - - /** - * Gets the interface. - * @return the interface. - */ - public Component getInterface() { - return INTERFACE; - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new KeldagrimRuleBook(player); - } - - /** - * Represents all the array of all configs to hide. - */ - private static final int[] ALL = new int[] { 102, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101 }; - - /** - * Gets the hidden childs. - * @return the childs. - */ - private int[] getHiddenChilds() { - return ALL; - } - - @Override - public int[] getIds() { - return new int[] { ID }; - } -} diff --git a/Server/src/main/content/global/handlers/item/book/SecurityBookPlugin.java b/Server/src/main/content/global/handlers/item/book/SecurityBookPlugin.java deleted file mode 100644 index 8e5bb4577..000000000 --- a/Server/src/main/content/global/handlers/item/book/SecurityBookPlugin.java +++ /dev/null @@ -1,139 +0,0 @@ -package content.global.handlers.item.book; - -import core.game.component.Component; -import core.game.dialogue.DialoguePlugin; -import core.game.dialogue.book.Book; -import core.game.dialogue.book.BookLine; -import core.game.dialogue.book.Page; -import core.game.dialogue.book.PageSet; -import core.game.node.entity.player.Player; -import core.plugin.Initializable; -import core.game.world.GameWorld; - -/** - * Represents the strong hold of security book. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class SecurityBookPlugin extends Book { - - /** - * Represents the strong hold of security book. - */ - public static int ID = 49610759; - - /** - * Represents the component interface. - */ - private static final Component INTERFACE = new Component(26); - - /** - * Represents the array of pages for this book. - */ - private static final PageSet[] PAGES = new PageSet[] { new PageSet(new Page(new BookLine("Chapters", 102), new BookLine("

" + BLUE + "Password Tips

", 74), new BookLine("

" + BLUE + "Recovery Questions

", 75), new BookLine("

" + BLUE + "Password Tips

", 74), new BookLine("

" + BLUE + "Recovery Questions

", 75), new BookLine("

" + BLUE + "Other Security Tips

", 76), new BookLine("

" + BLUE + "Stringhold of Security

", 77)), new Page(new BookLine("

" + BLUE + "Password Tips

", 87), new BookLine("A good password should be", 88), new BookLine("easily remembered by", 89), new BookLine("yourself but not easily", 90), new BookLine("guessed by anyone else.", 91), new BookLine("Choose a password that has", 93), new BookLine("both letters and numbers in", 94), new BookLine("it for the best security but", 95), new BookLine("don't make it so hard that", 96), new BookLine("you'll forget it!", 97), new BookLine("Never write your password", 99), new BookLine("down or leave it in a text file", 100), new BookLine("on your computer, someone", 101))), new PageSet(new Page(new BookLine("could find it easily!", 102), new BookLine("Never tell anyone your", 74), new BookLine("password in " + GameWorld.getSettings().getName() + ", not", 75), new BookLine("even a Moderator of any", 76), new BookLine("kind.", 77)), new Page(new BookLine("

" + BLUE + "Recovery Questions

", 87), new BookLine("Ideally your recovery", 88), new BookLine("questions should be easily", 89), new BookLine("remembered by you but not", 90), new BookLine("guessable by anyone who", 91), new BookLine("may know you or given", 92), new BookLine("away in conversation. Choose", 93), new BookLine("things that do not change,", 94), new BookLine("like dates or names but don't", 95), new BookLine("choose obvious ones like your", 96), new BookLine("birthday and your sister or", 97), new BookLine("bother's name because lots", 98), new BookLine("of people will know that.", 99))), new PageSet(new Page(new BookLine("

" + BLUE + "Recovery Questions

", 102), new BookLine("Bear in mind that recovery", 73), new BookLine("questions will take 14 days to", 74), new BookLine("become active after you have", 75), new BookLine("applied for them to be", 76), new BookLine("changed. This is to protect", 77), new BookLine("your account from hijackers", 78), new BookLine("who may change them.", 79), new BookLine("Never give your password to", 81), new BookLine("ANYONE. This includes", 82), new BookLine("your friends, family, and", 83), new BookLine("moderators in game.", 84), new BookLine("Never leave your account", 86)), new Page(new BookLine("logged on if you are away", 87), new BookLine("from the computer, it only", 88), new BookLine("takes 5 seconds to steal your", 89), new BookLine("account!", 90))), new PageSet(new Page(new BookLine("

" + BLUE + "Stronghold of Security

", 102), new BookLine("Location: The Stronghold of", 73), new BookLine("Security, as we call it, is", 74), new BookLine("located under the village filled", 75), new BookLine("with Barbarians. It was", 76), new BookLine("found after they moved their", 77), new BookLine("mining operations and a", 78), new BookLine("miner fell through. The", 79), new BookLine("Stronghold contains many", 80), new BookLine("challenges. Both for those", 81), new BookLine("who enjoy combat and those", 82), new BookLine("who enjoy challenges of the", 83), new BookLine("mind. This book will be very", 84), new BookLine("useful to you in your travels", 85), new BookLine("there.", 86)), new Page(new BookLine("You can find the Stronghold", 87), new BookLine("of Security by looking for a", 88), new BookLine("hole in Barbarian Village.", 89), new BookLine("Be sure to take your combat", 90), new BookLine("equipment though!", 91))) }; - - /** - * Constructs a new {@code ShieldofArravBook} {@code Object}. - */ - public SecurityBookPlugin(final Player player) { - super(player, "" + GameWorld.getSettings().getName() + " Account Security", 9003, PAGES); - } - - /** - * Constructs a new {@code ShieldofArravBook} {@code Object}. - */ - public SecurityBookPlugin() { - /** - * empty. - */ - } - - /** - * Method used to display a set of pages. - * @param set the set. - */ - public void display(Page[] set) { - /*player.lock(2); - player.getInterfaceManager().open(getInterface()); - player.getPacketDispatch().sendInterfaceConfig(getInterface().getId(), 65, index < 1 ? true : false); - boolean lastPage = index == 3; - player.getPacketDispatch().sendInterfaceConfig(getInterface().getId(), 67, lastPage ? true : false); - if (lastPage) { - finish(); - } - for (int i : getHiddenChilds()) { - player.getPacketDispatch().sendString("", 26, i); - } - player.getPacketDispatch().sendString(getName(), 26, 3); - player.getPacketDispatch().sendString("" + (index == 0 ? (index + 1) : (index + 2)), 26, 68); - player.getPacketDispatch().sendString("" + (index == 0 ? (index + 2) : (index + 3)), 26, 69); - for (Page page : set) { - for (BookLine line : page.getLines()) { - player.getPacketDispatch().sendString(line.getMessage(), getInterface().getId(), line.getChild()); - } - } - player.unlock();*/ - } - - /** - * Gets the interface. - * @return the interface. - */ - public Component getInterface() { - return INTERFACE; - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new SecurityBookPlugin(player); - } - - /** - * Represents all the array of all configs to hide. - */ - private static final int[] ALL = new int[] { 102, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101 }; - - /** - * Represents the first page hidden configs. - */ - private static final int[] FIRST = new int[] { 73, 78, 79, 80, 81, 82, 83, 84, 85, 86, 92, 98 }; - - /** - * Represents the second stage hidden configs. - */ - private static final int[] SECOND = new int[] { 73, 78, 79, 80, 81, 82, 83, 84, 85, 86, 100, 101 }; - - /** - * Represents the third page hidden configs. - */ - private static final int[] THIRD = new int[] { 85, 80, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101 }; - - /** - * Represents the fourth page hidden configs. - */ - private static final int[] FOURTH = new int[] { 92, 93, 94, 95, 96, 97, 98, 99, 100, 101 }; - - /** - * Gets the hidden childs. - * @return the childs. - */ - @SuppressWarnings("unused") - private int[] getHiddenChilds() { - if (index == 0) { - return FIRST; - } else if (index == 1) { - return SECOND; - } else if (index == 3) { - return THIRD; - } else if (index == 4) { - return FOURTH; - } - return ALL; - } - - @Override - public int[] getIds() { - return new int[] { ID }; - } -} diff --git a/Server/src/main/content/global/handlers/item/book/SecurityBookPlugin.kt b/Server/src/main/content/global/handlers/item/book/SecurityBookPlugin.kt new file mode 100644 index 000000000..4813fed18 --- /dev/null +++ b/Server/src/main/content/global/handlers/item/book/SecurityBookPlugin.kt @@ -0,0 +1,134 @@ +package content.global.handlers.item.book + +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.game.world.GameWorld +import org.rs09.consts.Items + +/** + * Security Book + * https://www.youtube.com/watch?v=yXyRUXYafO0 + * @author ovenbreado + */ +class SecurityBookPlugin : InteractionListener { + companion object { + private const val BLUE = "" + private val TITLE = "" + GameWorld.settings!!.name + " Account Security" + private val CONTENTS = arrayOf( + PageSet( + Page( + BookLine("Chapters", 38), + BookLine("

" + BLUE + "Password Tips

", 40), + BookLine("

" + BLUE + "Recovery Questions

", 41), + BookLine("

" + BLUE + "Other Security Tips

", 42), + BookLine("

" + BLUE + "Stringhold of Security

", 43) + ), + Page( + BookLine("

" + BLUE + "Password Tips

", 53), + BookLine("A good password should be", 54), + BookLine("easily remembered by", 55), + BookLine("yourself but not easily", 56), + BookLine("guessed by anyone else.", 57), + BookLine("Choose a password that has", 59), + BookLine("both letters and numbers in", 60), + BookLine("it for the best security but", 61), + BookLine("don't make it so hard that", 62), + BookLine("you'll forget it!", 63), + BookLine("Never write your password", 65), + BookLine("down or leave it in a text file", 66), + BookLine("on your computer, someone", 67) + ) + ), + PageSet( + Page( + BookLine("could find it easily!", 38), + BookLine("Never tell anyone your", 40), + BookLine("password in " + GameWorld.settings!!.name + ", not", 41), + BookLine("even a Moderator of any", 42), + BookLine("kind.", 43) + ), + Page( + BookLine("

" + BLUE + "Recovery Questions

", 53), + BookLine("Ideally your recovery", 54), + BookLine("questions should be easily", 55), + BookLine("remembered by you but not", 56), + BookLine("guessable by anyone who", 57), + BookLine("may know you or given", 58), + BookLine("away in conversation. Choose", 59), + BookLine("things that do not change,", 60), + BookLine("like dates or names but don't", 61), + BookLine("choose obvious ones like your", 62), + BookLine("birthday and your sister or", 63), + BookLine("bother's name because lots", 64), + BookLine("of people will know that.", 65) + ) + ), + PageSet( + Page( + BookLine("

" + BLUE + "Recovery Questions

", 38), + BookLine("Bear in mind that recovery", 39), + BookLine("questions will take 14 days to", 40), + BookLine("become active after you have", 41), + BookLine("applied for them to be", 42), + BookLine("changed. This is to protect", 43), + BookLine("your account from hijackers", 44), + BookLine("who may change them.", 45), + BookLine("Never give your password to", 47), + BookLine("ANYONE. This includes", 48), + BookLine("your friends, family, and", 49), + BookLine("moderators in game.", 50), + BookLine("Never leave your account", 52) + ), + Page( + BookLine("logged on if you are away", 53), + BookLine("from the computer, it only", 54), + BookLine("takes 5 seconds to steal your", 55), + BookLine("account!", 56) + ) + ), + PageSet( + Page( + BookLine("

" + BLUE + "Stronghold of Security

", 38), + BookLine("Location: The Stronghold of", 39), + BookLine("Security, as we call it, is", 40), + BookLine("located under the village filled", 41), + BookLine("with Barbarians. It was", 42), + BookLine("found after they moved their", 43), + BookLine("mining operations and a", 44), + BookLine("miner fell through. The", 45), + BookLine("Stronghold contains many", 46), + BookLine("challenges. Both for those", 47), + BookLine("who enjoy combat and those", 48), + BookLine("who enjoy challenges of the", 49), + BookLine("mind. This book will be very", 50), + BookLine("useful to you in your travels", 51), + BookLine("there.", 52) + ), + Page( + BookLine("You can find the Stronghold", 53), + BookLine("of Security by looking for a", 54), + BookLine("hole in Barbarian Village.", 55), + BookLine("Be sure to take your combat", 56), + BookLine("equipment though!", 57) + ) + ) + ) + private fun display(player: Player, pageNum: Int, buttonID: Int) : Boolean { + BookInterface.pageSetup(player, BookInterface.FANCY_BOOK_2_27, TITLE, CONTENTS) + return true + } + } + + override fun defineListeners() { + on(Items.SECURITY_BOOK_9003, 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/global/handlers/item/book/StrongholdNotes.java b/Server/src/main/content/global/handlers/item/book/StrongholdNotes.java deleted file mode 100644 index 78f155392..000000000 --- a/Server/src/main/content/global/handlers/item/book/StrongholdNotes.java +++ /dev/null @@ -1,100 +0,0 @@ -package content.global.handlers.item.book; - -import core.game.component.Component; -import core.game.dialogue.book.Book; -import core.game.dialogue.book.BookLine; -import core.game.dialogue.book.Page; -import core.game.dialogue.book.PageSet; -import core.plugin.Initializable; -import core.game.dialogue.DialoguePlugin; -import core.game.node.entity.player.Player; - -/** - * Represents the strong hold notes book. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class StrongholdNotes extends Book { - - /** - * Represents the strong hold of security book. - */ - public static int ID = 423943; - - /** - * Represents the component interface. - */ - private static final Component INTERFACE = new Component(26); - - /** - * Represents the array of pages for this book. - */ - private static final PageSet[] PAGES = new PageSet[] { new PageSet(new Page(new BookLine("Chapters

" + BLUE + "Description
" + BLUE + "Level 1
" + BLUE + "Level 2
" + BLUE + "Level 3
" + BLUE + "Level 4
" + BLUE + "Navigation
" + BLUE + "Diary









", 102)), new Page(new BookLine(BLUE + "Description
This stronghold was
unearthed by a miner
prospecting for new ores
around the Barbarian Village.
After gathering some
equipment he ventured into
the maze of tunnels and was
missing for a long time. He
finally emerged along with
copious notes refarding the
new beasts and strange
experiences which had befallen
him. He also mentioned that
there was treasure to be had,", 87))), new PageSet(new Page(new BookLine("but no one has been able to
wring a word from him about
this, he simply flapped his
arms and slapped his head.
This book details his notes
and my diary of exploration.
I am exploring to see if I
can find out more...", 102)), new Page(new BookLine(BLUE + "Level 1
As well as goblins, creatures
like a man but also like a cow
infest this place! I have never
seen anything like this before.
The area itself is reminiscent
of frontline castles, with
many walls, doors and
skeletons of dead enemies.
I'm sure I hear voices in my
head each time I pass
through the gates. I have
dubbed this level War as it
seems like an eternal
battleground. I found only", 87))), new PageSet(new Page(new BookLine("one small peaceful area here.", 102)), new Page(new BookLine(BLUE + "Level 2
My supplies are running low
and I find myself in barren
passages with seemingly
endless malnourished beasts
attacking me, revenous for
food. Nothing appears to be
able to grow, many
adventurers have died
through lack of food and the
very air appears to suck
vitality from me. I've come to
call this place famine.", 87))), new PageSet(new Page(new BookLine(BLUE + "Level 3
Just breathing in this place
makes me shudder at the
thought of what foul disease I
may contract. The walls and
floor ooze and pulsate like
something pox ridden. There
is a very strange beast whom
I narrowly escaped from. At
first I thought it to be a
cross between a cow and a
sheep, something
domesticated... but when it
looked up at me I was
overcome with weakness and", 102)), new Page(new BookLine("barely got way with my life!
Luckily I found a small place
where I could heal myself
and rest a while. I have
named this area pestilence for
it reeks with decay.", 87))), new PageSet(new Page(new BookLine(BLUE + "Level 4
On my first escapade into
this place I was utterly
shocked. The adventurers
who had come before me
must have made up a tiny
proportion of the skeletons of
the dead. Nothing truely
alive exists here, even those
beings who do wander the
halls are not alive as such,
but they do know that I am
and I get the distinct
impression that were they to
have their way, I would not", 102)), new Page(new BookLine("be for long! Death is
everywhere and thus I shall
name this place. There is one
small place of life, which was
gladdening to find and very
worth my while!", 87))), new PageSet(new Page(new BookLine(BLUE + "Navigation
After getting lost several
times I finally worked out the
key to all the ladders and
chains around this death
infested place. All ropes and
chains will take you to the
start of the level that you are
on. However most ladders will
simply take you to the level
above. The one esception is
the ladder in the bottom level
treasure room, which appears
to lead through several
extremely twisty passages.", 102)), new Page(new BookLine("and eventually takes you out
of the dungeon completely.
The portals may be used if
you are of sufficient level or
have already claimed your
reward from the treasure
room.", 102))), new PageSet(new Page(new BookLine(BLUE + "Diary
Day 1
Today I set out to find out
more about this place. From
my research I knew about
the sentient doors, imbued by
some unkown force to talk
to you and ask questions
before they will let you pass.
I have so far passed these
doors without incident, giving
the correct answer seems to
work a treat.

Day 2", 102)), new Page(new BookLine("I have fought my way
through the fearsome beasts
on the first level and am
preparing myself to journey
deeper. I hope that things are
not too difficult further on as
I am already sick of bread
and cheese for dinner.

Day 3
I ventured down into the
famine level today... I was
wounded and have returned
to the relative safety of the
level above. I am going to", 87))), new PageSet(new Page(new BookLine("try to make my way out
through the goblins and
mancow things... I hope I
make it.....", 102)), new Page(new BookLine("", 87))) }; - - /** - * Represents all the array of all configs to hide. - */ - private static final int[] ALL = new int[] { 102, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101 }; - - /** - * Constructs a new {@code ShieldofArravBook} {@code Object}. - */ - public StrongholdNotes(final Player player) { - super(player, "Stronghold of Security - Notes", 9004, PAGES); - } - - /** - * Constructs a new {@code ShieldofArravBook} {@code Object}. - */ - public StrongholdNotes() { - /** - * empty. - */ - } - - /** - * Method used to display a set of pages. - * @param set the set. - */ - public void display(Page[] set) { - player.lock(2); - player.getInterfaceManager().open(getInterface()); - player.getPacketDispatch().sendInterfaceConfig(getInterface().getId(), 65, index < 1 ? true : false); - boolean lastPage = index == 7; - player.getPacketDispatch().sendInterfaceConfig(getInterface().getId(), 67, lastPage ? true : false); - if (lastPage) { - finish(); - } - for (int i : ALL) { - player.getPacketDispatch().sendString("", 26, i); - } - player.getPacketDispatch().sendString(getName(), 26, 3); - player.getPacketDispatch().sendString("" + (index == 0 ? (index + 1) : (index + 2)), 26, 68); - player.getPacketDispatch().sendString("" + (index == 0 ? (index + 2) : (index + 3)), 26, 69); - for (Page page : set) { - for (BookLine line : page.getLines()) { - player.getPacketDispatch().sendString(line.getMessage(), getInterface().getId(), line.getChild()); - } - } - player.unlock(); - } - - /** - * Gets the interface. - * @return the interface. - */ - public Component getInterface() { - return INTERFACE; - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new StrongholdNotes(player); - } - - @Override - public int[] getIds() { - return new int[] { ID }; - } -} diff --git a/Server/src/main/content/global/handlers/item/book/StrongholdNotes.kt b/Server/src/main/content/global/handlers/item/book/StrongholdNotes.kt new file mode 100644 index 000000000..4a67ee93f --- /dev/null +++ b/Server/src/main/content/global/handlers/item/book/StrongholdNotes.kt @@ -0,0 +1,136 @@ +package content.global.handlers.item.book + +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 + +/** + * Stronghold Notes Book + * https://www.youtube.com/watch?v=gX1jCcfY0l8 + * @author ovenbreado + */ +class StrongholdNotes : InteractionListener { + companion object { + private const val BLUE = "" + private const val TITLE = "Stronghold of Security - Notes" + private val CONTENTS = arrayOf( + PageSet( + Page( + BookLine( + "Chapters

" + BLUE + "Description
" + BLUE + "Level 1
" + BLUE + "Level 2
" + BLUE + "Level 3
" + BLUE + "Level 4
" + BLUE + "Navigation
" + BLUE + "Diary









", + 38 + ) + ), Page( + BookLine( + BLUE + "Description
This stronghold was
unearthed by a miner
prospecting for new ores
around the Barbarian Village.
After gathering some
equipment he ventured into
the maze of tunnels and was
missing for a long time. He
finally emerged along with
copious notes refarding the
new beasts and strange
experiences which had befallen
him. He also mentioned that
there was treasure to be had,", + 53 + ) + ) + ), + PageSet( + Page( + BookLine( + "but no one has been able to
wring a word from him about
this, he simply flapped his
arms and slapped his head.
This book details his notes
and my diary of exploration.
I am exploring to see if I
can find out more...", + 38 + ) + ), Page( + BookLine( + BLUE + "Level 1
As well as goblins, creatures
like a man but also like a cow
infest this place! I have never
seen anything like this before.
The area itself is reminiscent
of frontline castles, with
many walls, doors and
skeletons of dead enemies.
I'm sure I hear voices in my
head each time I pass
through the gates. I have
dubbed this level War as it
seems like an eternal
battleground. I found only", + 53 + ) + ) + ), + PageSet( + Page(BookLine("one small peaceful area here.", 38)), + Page( + BookLine( + BLUE + "Level 2
My supplies are running low
and I find myself in barren
passages with seemingly
endless malnourished beasts
attacking me, revenous for
food. Nothing appears to be
able to grow, many
adventurers have died
through lack of food and the
very air appears to suck
vitality from me. I've come to
call this place famine.", + 53 + ) + ) + ), + PageSet( + Page( + BookLine( + BLUE + "Level 3
Just breathing in this place
makes me shudder at the
thought of what foul disease I
may contract. The walls and
floor ooze and pulsate like
something pox ridden. There
is a very strange beast whom
I narrowly escaped from. At
first I thought it to be a
cross between a cow and a
sheep, something
domesticated... but when it
looked up at me I was
overcome with weakness and", + 38 + ) + ), + Page( + BookLine( + "barely got way with my life!
Luckily I found a small place
where I could heal myself
and rest a while. I have
named this area pestilence for
it reeks with decay.", + 53 + ) + ) + ), + PageSet( + Page( + BookLine( + BLUE + "Level 4
On my first escapade into
this place I was utterly
shocked. The adventurers
who had come before me
must have made up a tiny
proportion of the skeletons of
the dead. Nothing truely
alive exists here, even those
beings who do wander the
halls are not alive as such,
but they do know that I am
and I get the distinct
impression that were they to
have their way, I would not", + 38 + ) + ), + Page( + BookLine( + "be for long! Death is
everywhere and thus I shall
name this place. There is one
small place of life, which was
gladdening to find and very
worth my while!", + 53 + ) + ) + ), + PageSet( + Page( + BookLine( + BLUE + "Navigation
After getting lost several
times I finally worked out the
key to all the ladders and
chains around this death
infested place. All ropes and
chains will take you to the
start of the level that you are
on. However most ladders will
simply take you to the level
above. The one esception is
the ladder in the bottom level
treasure room, which appears
to lead through several
extremely twisty passages.", + 38 + ) + ), + Page( + BookLine( + "and eventually takes you out
of the dungeon completely.
The portals may be used if
you are of sufficient level or
have already claimed your
reward from the treasure
room.", + 38 + ) + ) + ), + PageSet( + Page( + BookLine( + BLUE + "Diary
Day 1
Today I set out to find out
more about this place. From
my research I knew about
the sentient doors, imbued by
some unkown force to talk
to you and ask questions
before they will let you pass.
I have so far passed these
doors without incident, giving
the correct answer seems to
work a treat.

Day 2", + 38 + ) + ), + Page( + BookLine( + "I have fought my way
through the fearsome beasts
on the first level and am
preparing myself to journey
deeper. I hope that things are
not too difficult further on as
I am already sick of bread
and cheese for dinner.

Day 3
I ventured down into the
famine level today... I was
wounded and have returned
to the relative safety of the
level above. I am going to", + 53 + ) + ) + ), + PageSet( + Page( + BookLine( + "try to make my way out
through the goblins and
mancow things... I hope I
make it.....", + 38 + ) + ) + ) + ) + + private fun display(player: Player, pageNum: Int, buttonID: Int) : Boolean { + BookInterface.pageSetup(player, BookInterface.FANCY_BOOK_2_27, TITLE, CONTENTS) + return true + } + } + + override fun defineListeners() { + on(Items.STRONGHOLD_NOTES_9004, 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/global/handlers/item/equipment/CrystalEquipmentRegister.kt b/Server/src/main/content/global/handlers/item/equipment/CrystalEquipmentRegister.kt index 2bb7bb48d..4504e9dfd 100644 --- a/Server/src/main/content/global/handlers/item/equipment/CrystalEquipmentRegister.kt +++ b/Server/src/main/content/global/handlers/item/equipment/CrystalEquipmentRegister.kt @@ -7,7 +7,7 @@ class CrystalEquipmentRegister : StartupListener { val shield: Array = arrayOf(Items.NEW_CRYSTAL_SHIELD_4224, Items.CRYSTAL_SHIELD_FULL_4225, Items.CRYSTAL_SHIELD_9_10_4226, Items.CRYSTAL_SHIELD_8_10_4227, Items.CRYSTAL_SHIELD_7_10_4228, Items.CRYSTAL_SHIELD_6_10_4229, Items.CRYSTAL_SHIELD_5_10_4230, Items.CRYSTAL_SHIELD_4_10_4231, Items.CRYSTAL_SHIELD_3_10_4232, Items.CRYSTAL_SHIELD_2_10_4233, Items.CRYSTAL_SHIELD_1_10_4234, Items.CRYSTAL_SEED_4207) val bow: Array = arrayOf(Items.NEW_CRYSTAL_BOW_4212, Items.CRYSTAL_BOW_FULL_4214, Items.CRYSTAL_BOW_9_10_4215, Items.CRYSTAL_BOW_8_10_4216, Items.CRYSTAL_BOW_7_10_4217, Items.CRYSTAL_BOW_6_10_4218, Items.CRYSTAL_BOW_5_10_4219, Items.CRYSTAL_BOW_4_10_4220, Items.CRYSTAL_BOW_3_10_4221, Items.CRYSTAL_BOW_2_10_4222, Items.CRYSTAL_BOW_1_10_4223, Items.CRYSTAL_SEED_4207) override fun startup() { - EquipmentDegrader.registerSet(250, shield) - EquipmentDegrader.registerSet(250, bow) + EquipmentDegrader.registerSet(1200, shield) + EquipmentDegrader.registerSet(1200, bow) } } \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/item/equipment/fistofguthixgloves/FOGGlovesListener.kt b/Server/src/main/content/global/handlers/item/equipment/fistofguthixgloves/FOGGlovesListener.kt new file mode 100644 index 000000000..9b0166e2e --- /dev/null +++ b/Server/src/main/content/global/handlers/item/equipment/fistofguthixgloves/FOGGlovesListener.kt @@ -0,0 +1,23 @@ +package content.global.handlers.item.equipment.fistofguthixgloves + +import core.api.toIntArray +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import org.rs09.consts.Items +import kotlin.math.min + +private val FOG_GLOVES = (Items.IRIT_GLOVES_12856..Items.EARTH_RUNECRAFTING_GLOVES_12865).toIntArray() +private val MAX_CHARGES = intArrayOf(100, 100, 100, 100, 1000, 1000, 1000, 1000, 1000, 1000) + +/** + * Listener for Fist of Guthix gloves inspect option. + * @author RiL + */ +class FOGGlovesListener : InteractionListener { + override fun defineListeners() { + on(FOG_GLOVES, IntType.ITEM, "inspect") { player, node -> + player.sendMessage("${node.name}: ${min(node.asItem().charge, MAX_CHARGES[node.id - Items.IRIT_GLOVES_12856])} charge left.") + return@on true + } + } +} diff --git a/Server/src/main/content/global/handlers/item/equipment/fistofguthixgloves/FOGGlovesManager.kt b/Server/src/main/content/global/handlers/item/equipment/fistofguthixgloves/FOGGlovesManager.kt new file mode 100644 index 000000000..768013c51 --- /dev/null +++ b/Server/src/main/content/global/handlers/item/equipment/fistofguthixgloves/FOGGlovesManager.kt @@ -0,0 +1,37 @@ +package content.global.handlers.item.equipment.fistofguthixgloves + +import core.api.Container +import core.api.EquipmentSlot +import core.api.getItemFromEquipment +import core.api.removeItem +import core.game.node.entity.player.Player +import core.tools.colorize +import org.rs09.consts.Items +import kotlin.math.min + +private val MAX_CHARGES = intArrayOf(100, 100, 100, 100, 1000, 1000, 1000, 1000, 1000, 1000) + +/** + * Manager to help updating Fist of Guthix glove charges. + * @author RiL + */ +class FOGGlovesManager { + companion object{ + /** + * Reduces the charge of the hand equip by [charges]. + * Return the number of used charges. + */ + @JvmStatic + fun updateCharges(player: Player, charges: Int = 1): Int { + val gloves = getItemFromEquipment(player, EquipmentSlot.HANDS) ?: return 0 + gloves.charge = min(gloves.charge, MAX_CHARGES[gloves.id - Items.IRIT_GLOVES_12856]) + if (gloves.charge - charges <= 0) { + removeItem(player, gloves, Container.EQUIPMENT) + player.sendMessage(colorize("%RThe charges in your gloves have been used up and they crumble to dust.")) + return gloves.charge + } + gloves.charge -= charges + return charges + } + } +} 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 fc5268698..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 @@ -11,6 +11,9 @@ import core.game.world.update.flag.context.Graphics; import core.plugin.Plugin; import core.plugin.Initializable; import core.tools.RandomFunction; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.playGlobalAudio; /** * Handles the ancient mace special attack "Favour of the War God". @@ -54,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); } @@ -72,6 +75,7 @@ public final class AncientMaceSpecialHandler extends MeleeSwingHandler implement @Override public void visualize(Entity entity, Entity victim, BattleState state) { + playGlobalAudio(entity.getLocation(), Sounds.GOBLIN_MACE_3592); entity.visualize(ANIMATION, GRAPHIC); } } 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 0e60dbdda..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 @@ -12,6 +12,9 @@ import core.game.world.update.flag.context.Graphics; import core.plugin.Plugin; import core.plugin.Initializable; import core.tools.RandomFunction; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.playGlobalAudio; /** * Handles the Bone dagger special attack "Backstab". @@ -56,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; @@ -70,6 +70,7 @@ public final class BackstabSpecialHandler extends MeleeSwingHandler implements P @Override public void visualize(Entity entity, Entity victim, BattleState state) { + playGlobalAudio(entity.getLocation(), Sounds.DTTD_BONE_DAGGER_STAB_1084); entity.visualize(ANIMATION, GRAPHIC); } } 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 838caf5a2..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,13 +15,15 @@ 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; import java.util.Iterator; import java.util.List; +import static core.api.ContentAPIKt.playGlobalAudio; + /** * Handles the Rune throwing axe special attack "Chain-hit". * @author Emperor @@ -31,6 +31,13 @@ import java.util.List; @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. */ @@ -104,6 +111,7 @@ public final class ChainhitSpecialHandler extends RangeSwingHandler implements P @Override public void visualize(Entity entity, Entity victim, BattleState state) { + playGlobalAudio(entity.getLocation(), Sounds.CHAINSHOT_2528); entity.visualize(ANIMATION, GRAPHIC); int speed = (int) (32 + (entity.getLocation().getDistance(victim.getLocation()) * 5)); Projectile.create(entity, victim, 258, 40, 36, 32, speed, 5, 11).send(); @@ -155,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 6f962cfad..0bd2e6d33 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 @@ -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.InteractionType; +import core.game.node.entity.combat.*; import core.game.node.entity.combat.equipment.Ammunition; import core.game.node.entity.combat.equipment.RangeWeapon; import core.game.node.entity.combat.equipment.Weapon; @@ -14,7 +12,6 @@ import core.game.node.entity.skill.Skills; import core.game.world.map.RegionManager; import core.game.world.update.flag.context.Graphics; import core.tools.RandomFunction; -import core.game.node.entity.combat.RangeSwingHandler; import java.util.List; @@ -34,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}. - */ - private ChinchompaSwingHandler() { - super(); - } - @Override public int swing(Entity entity, Entity victim, BattleState state) { boolean multi = entity.getProperties().isMultiZone() && victim.getProperties().isMultiZone(); @@ -75,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); } 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 c1b856eeb..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 @@ -6,12 +6,14 @@ import core.game.node.entity.combat.CombatStyle; 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.audio.Audio; 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.Sounds; + +import static core.api.ContentAPIKt.playGlobalAudio; /** * Represents the cleave special handler. @@ -55,16 +57,16 @@ 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); - entity.asPlayer().getAudioManager().send(new Audio(2529), true); return 1; } @Override public void visualize(Entity entity, Entity victim, BattleState state) { + playGlobalAudio(entity.getLocation(), Sounds.CLEAVE_2529); entity.visualize(ANIMATION, GRAPHIC); } } diff --git a/Server/src/main/content/global/handlers/item/equipment/special/ClobberSpecialHandler.java b/Server/src/main/content/global/handlers/item/equipment/special/ClobberSpecialHandler.java deleted file mode 100644 index 1c6ad1100..000000000 --- a/Server/src/main/content/global/handlers/item/equipment/special/ClobberSpecialHandler.java +++ /dev/null @@ -1,65 +0,0 @@ -package content.global.handlers.item.equipment.special; - -import core.game.node.entity.skill.Skills; -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.MeleeSwingHandler; -import core.game.node.entity.impl.Animator.Priority; -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.plugin.Plugin; - -/** - * Handles the Dragon axe special attack. - * @author Crash - */ -@Initializable -public final class ClobberSpecialHandler extends MeleeSwingHandler implements Plugin { - - /** - * The special energy required. - */ - private static final int SPECIAL_ENERGY = 100; - - /** - * The attack animation. - */ - private static final Animation ANIMATION = new Animation(2876, Priority.HIGH); - - /** - * The graphic. - */ - private static final Graphics GRAPHIC = new Graphics(479, 96); - - - @Override - public Object fireEvent(String identifier, Object... args) { - switch (identifier) { - case "instant_spec": - case "ncspec": - return true; - } - return null; - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - CombatStyle.MELEE.getSwingHandler().register(6739, this); - return this; - } - - @Override - public int swing(Entity entity, Entity victim, BattleState state) { - Player p = (Player) entity; - if (!p.getSettings().drainSpecial(SPECIAL_ENERGY)) - return -1; - p.sendChat("Chop chop!"); - p.visualize(ANIMATION, GRAPHIC); - p.getSkills().updateLevel(Skills.WOODCUTTING, 3, p.getSkills().getStaticLevel(Skills.WOODCUTTING) + 3); - return -1; - } - -} diff --git a/Server/src/main/content/global/handlers/item/equipment/special/ClobberSpecialHandler.kt b/Server/src/main/content/global/handlers/item/equipment/special/ClobberSpecialHandler.kt new file mode 100644 index 000000000..31629189e --- /dev/null +++ b/Server/src/main/content/global/handlers/item/equipment/special/ClobberSpecialHandler.kt @@ -0,0 +1,114 @@ +package content.global.handlers.item.equipment.special + +import core.ServerConstants +import core.api.playAudio +import core.game.container.impl.EquipmentContainer +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.MeleeSwingHandler +import core.game.node.entity.combat.equipment.Weapon +import core.game.node.entity.impl.Animator.Priority +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.audio.Audio +import core.game.node.entity.skill.Skills +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 core.tools.RandomFunction +import org.rs09.consts.Items +import org.rs09.consts.Sounds + + +/** + * Handles the Dragon axe special attack. + * The OSRS special attack can be used instead by setting dragon_axe_use_osrs_spec server constant to true + * @author Crash + */ +@Initializable +class ClobberSpecialHandler : MeleeSwingHandler(), Plugin { + override fun newInstance(arg: Any?): Plugin { + CombatStyle.MELEE.swingHandler.register(ITEM.id, this) + return this + } + + override fun fireEvent(identifier: String, vararg args: Any): Any { + return if (ServerConstants.DRAGON_AXE_USE_OSRS_SPEC && identifier == "instant_spec") true else Unit + } + + override fun visualize(entity: Entity, victim: Entity?, state: BattleState?) { + entity.visualize(ANIMATION, GRAPHIC) + } + + override fun swing(entity: Entity?, victim: Entity?, state: BattleState?): Int { + return if (ServerConstants.DRAGON_AXE_USE_OSRS_SPEC) { + swingOSRS(entity) + } else { + swingAuthentic(entity, victim, state) + } + } + + /** + * https://runescape.wiki/w/Dragon_hatchet?oldid=1107166 + */ + private fun swingAuthentic(entity: Entity?, victim: Entity?, state: BattleState?): Int { + val player = entity as? Player ?: return -1 + if (victim == null) return -1 + if (state == null) return -1 + if (!player.settings.drainSpecial(SPECIAL_ENERGY)) return -1 + + state.style = CombatStyle.MELEE + state.weapon = Weapon(player.equipment[EquipmentContainer.SLOT_WEAPON]) + var hit = 0 + if (isAccurateImpact(entity, victim, CombatStyle.MELEE)) { + val max = calculateHit(entity, victim, 1.0) + state.maximumHit = max + hit = RandomFunction.random(max + 1) + } + state.estimatedHit = hit + + victim.skills.updateLevel(Skills.DEFENCE, -hit, 0) + victim.skills.updateLevel(Skills.MAGIC, -hit, 0) + + playAudio(player, Sounds.CLOBBER_2531, 20) + return 1 + } + + /** + * https://oldschool.runescape.wiki/w/Dragon_axe + */ + private fun swingOSRS(entity: Entity?): Int { + val player = entity as? Player ?: return -1 + if (!player.settings.drainSpecial(SPECIAL_ENERGY)) return -1 + + player.sendChat("Chop chop!") + player.skills.updateLevel(Skills.WOODCUTTING, 3, player.skills.getStaticLevel(Skills.WOODCUTTING) + 3) + visualize(player, null, null) + playAudio(player, Sounds.CLOBBER_2531, 20) + return -1 + } + + companion object { + /** + * The special energy required. + */ + private const val SPECIAL_ENERGY = 100 + + /** + * The attack animation. + */ + private val ANIMATION = Animation(2876, Priority.HIGH) + + /** + * The graphic. + */ + private val GRAPHIC = Graphics(479, 96) + + /** + * The item. + */ + private val ITEM = Item(Items.DRAGON_AXE_6739) + } +} \ No newline at end of file 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 faeca021b..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 @@ -10,12 +10,14 @@ import core.game.node.entity.combat.equipment.Weapon; import core.game.node.entity.combat.equipment.Weapon.WeaponType; import core.game.node.entity.impl.Projectile; import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.audio.Audio; 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 org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.playGlobalAudio; /** * Represents the descent of darkness sepcial handler. @@ -28,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. @@ -100,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); } @@ -121,8 +123,6 @@ public final class DescentOfDarknessSpecialHandler extends RangeSwingHandler imp start = state.getAmmunition().getStartGraphics(); if (state.isFrozen()) { if (entity instanceof Player) { - entity.asPlayer().getAudioManager().send(new Audio(3736), true); - entity.asPlayer().getAudioManager().send(new Audio(3737), true); } DRAGON_PROJECTILE.copy(entity, victim, 5).send(); } else { @@ -145,6 +145,8 @@ public final class DescentOfDarknessSpecialHandler extends RangeSwingHandler imp @Override public void visualizeImpact(Entity entity, Entity victim, BattleState state) { + playGlobalAudio(victim.getLocation(), Sounds.DARKBOW_SHADOW_ATTACK_3736, 20); + playGlobalAudio(victim.getLocation(), Sounds.DARKBOW_SHADOW_IMPACT_3737, 40); victim.visualize(victim.getProperties().getDefenceAnimation(), state.isFrozen() ? DRAGON_IMPACT : DARKNESS_IMPACT); } } diff --git a/Server/src/main/content/global/handlers/item/equipment/special/DragonfireSwingHandler.java b/Server/src/main/content/global/handlers/item/equipment/special/DragonfireSwingHandler.java index be9b18dcf..0497836a8 100644 --- a/Server/src/main/content/global/handlers/item/equipment/special/DragonfireSwingHandler.java +++ b/Server/src/main/content/global/handlers/item/equipment/special/DragonfireSwingHandler.java @@ -10,12 +10,14 @@ 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.player.Player; -import core.game.node.entity.state.EntityState; import core.game.node.item.Item; import core.game.world.GameWorld; import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Graphics; import core.tools.RandomFunction; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.*; /** * Handles dragonfire combat. @@ -23,198 +25,185 @@ import core.tools.RandomFunction; */ public class DragonfireSwingHandler extends CombatSwingHandler { - /** - * If the NPC has to be in melee range. - */ - private boolean meleeRange; + /** + * If the NPC has to be in melee range. + */ + private boolean meleeRange; - /** - * The maximum hit. - */ - private int maximumHit; + /** + * The maximum hit. + */ + private int maximumHit; - /** - * The attack data. - */ - private SwitchAttack attack; + /** + * The attack data. + */ + private SwitchAttack attack; - /** - * IF the dragon attack is firey or icey. - */ - private boolean fire; + /** + * IF the dragon attack is firey or icey. + */ + private boolean fire; - /** - * Constructs a new {@code DragonfireSwingHandler} {@code Object}. - * @param meleeRange If the NPC has to be in melee range. - * @param maximumHit The maximum hit. - * @param fire if firey. - */ - public DragonfireSwingHandler(boolean meleeRange, int maximumHit, SwitchAttack attack, boolean fire) { - super(CombatStyle.MAGIC); - this.meleeRange = meleeRange; - this.maximumHit = maximumHit; - this.attack = attack; - this.fire = fire; - } + /** + * Constructs a new {@code DragonfireSwingHandler} {@code Object}. + * @param meleeRange If the NPC has to be in melee range. + * @param maximumHit The maximum hit. + * @param fire if firey. + */ + public DragonfireSwingHandler(boolean meleeRange, int maximumHit, SwitchAttack attack, boolean fire) { + super(CombatStyle.MAGIC); + this.meleeRange = meleeRange; + this.maximumHit = maximumHit; + this.attack = attack; + this.fire = fire; + } - /** - * Gets the switch attack instance for a dragonfire attack. - * @param meleeRange If the attack is melee range. - * @param maximumHit The maximum hit. - * @param animation The animation. - * @param startGraphic The start graphic. - * @param endGraphic The end graphic. - * @param projectile The projectile. - * @return The switch attack instance. - */ - public static SwitchAttack get(boolean meleeRange, int maximumHit, Animation animation, Graphics startGraphic, Graphics endGraphic, Projectile projectile) { - SwitchAttack attack = new SwitchAttack(null, animation, startGraphic, endGraphic, projectile).setUseHandler(true); - attack.setHandler(new DragonfireSwingHandler(meleeRange, maximumHit, attack, true)); - return attack; - } + /** + * Gets the switch attack instance for a dragonfire attack. + * @param meleeRange If the attack is melee range. + * @param maximumHit The maximum hit. + * @param animation The animation. + * @param startGraphic The start graphic. + * @param endGraphic The end graphic. + * @param projectile The projectile. + * @return The switch attack instance. + */ + public static SwitchAttack get(boolean meleeRange, int maximumHit, Animation animation, Graphics startGraphic, Graphics endGraphic, Projectile projectile) { + SwitchAttack attack = new SwitchAttack(null, animation, startGraphic, endGraphic, projectile).setUseHandler(true); + attack.setHandler(new DragonfireSwingHandler(meleeRange, maximumHit, attack, true)); + return attack; + } - /** - * Gets the switch attack instance for a dragonfire attack. - * @param meleeRange If the attack is melee range. - * @param maximumHit The maximum hit. - * @param animation The animation. - * @param startGraphic The start graphic. - * @param endGraphic The end graphic. - * @param projectile The projectile. - * @return The switch attack instance. - */ - public static SwitchAttack get(boolean meleeRange, int maximumHit, Animation animation, Graphics startGraphic, Graphics endGraphic, Projectile projectile, boolean fire) { - SwitchAttack attack = new SwitchAttack(null, animation, startGraphic, endGraphic, projectile).setUseHandler(true); - attack.setHandler(new DragonfireSwingHandler(meleeRange, maximumHit, attack, fire)); - return attack; - } + /** + * Gets the switch attack instance for a dragonfire attack. + * @param meleeRange If the attack is melee range. + * @param maximumHit The maximum hit. + * @param animation The animation. + * @param startGraphic The start graphic. + * @param endGraphic The end graphic. + * @param projectile The projectile. + * @return The switch attack instance. + */ + public static SwitchAttack get(boolean meleeRange, int maximumHit, Animation animation, Graphics startGraphic, Graphics endGraphic, Projectile projectile, boolean fire) { + SwitchAttack attack = new SwitchAttack(null, animation, startGraphic, endGraphic, projectile).setUseHandler(true); + attack.setHandler(new DragonfireSwingHandler(meleeRange, maximumHit, attack, fire)); + return attack; + } - @Override - public InteractionType canSwing(Entity entity, Entity victim) { - if (meleeRange) { - return CombatStyle.MELEE.getSwingHandler().canSwing(entity, victim); - } - return CombatStyle.MAGIC.getSwingHandler().canSwing(entity, victim); - } + @Override + public InteractionType canSwing(Entity entity, Entity victim) { + if (meleeRange) { + return CombatStyle.MELEE.getSwingHandler().canSwing(entity, victim); + } + return CombatStyle.MAGIC.getSwingHandler().canSwing(entity, victim); + } - @Override - public int swing(Entity entity, Entity victim, BattleState state) { - int max = calculateHit(entity, victim, 1.0); - int hit = RandomFunction.random(max); - assert state != null; - state.setMaximumHit(max); - state.setStyle(CombatStyle.MAGIC); - state.setEstimatedHit(hit); - if (meleeRange) { - return 1; - } - int ticks = 2 + (int) Math.floor(entity.getLocation().getDistance(victim.getLocation()) * 0.5); - entity.setAttribute("fireBreath", GameWorld.getTicks() + (ticks + 2)); - return ticks; - } + @Override + public int swing(Entity entity, Entity victim, BattleState state) { + int max = calculateHit(entity, victim, 1.0); + int hit = RandomFunction.random(max + 1); + assert state != null; + state.setMaximumHit(max); + state.setStyle(CombatStyle.MAGIC); + state.setEstimatedHit(hit); + if (meleeRange) { + return 1; + } + int ticks = 2 + (int) Math.floor(entity.getLocation().getDistance(victim.getLocation()) * 0.5); + entity.setAttribute("fireBreath", GameWorld.getTicks() + (ticks + 2)); + return ticks; + } - @Override - public void visualize(Entity entity, Entity victim, BattleState state) { - entity.visualize(attack.getAnimation(), attack.getStartGraphic()); - if (attack.getProjectile() != null) { - attack.getProjectile().copy(entity, victim, 5).send(); - } - } + @Override + public void visualize(Entity entity, Entity victim, BattleState state) { + entity.visualize(attack.getAnimation(), attack.getStartGraphic()); + if (attack.getProjectile() != null) { + attack.getProjectile().copy(entity, victim, 5).send(); + } + } - @Override - public void visualizeImpact(Entity entity, Entity victim, BattleState state) { - if (entity instanceof NPC && victim instanceof Player) { - Player p = (Player) victim; - p.getPacketDispatch().sendMessage(getDragonfireMessage(victim.getAttribute("fire_resistance", 0), !fire ? "icy breath" : "fiery breath")); - Item shield = p.getEquipment().get(EquipmentContainer.SLOT_SHIELD); - if (shield != null && (shield.getId() == 11283 || shield.getId() == 11284)) { - if (shield.getId() == 11284) { - p.getEquipment().replace(new Item(11283), EquipmentContainer.SLOT_SHIELD); - shield = p.getEquipment().get(EquipmentContainer.SLOT_SHIELD); - shield.setCharge(0); - } - if (shield.getCharge() < 1000) { - shield.setCharge(shield.getCharge() + 20); - EquipmentContainer.updateBonuses(p); - p.getPacketDispatch().sendMessage("Your dragonfire shield glows more brightly."); - } else { - p.getPacketDispatch().sendMessage("Your dragonfire shield is already fully charged."); - } - p.faceLocation(entity.getCenterLocation()); - victim.visualize(Animation.create(6695), Graphics.create(1163)); - return; - } - } - if (!fire && victim.getAttribute("freeze_immunity", -1) < GameWorld.getTicks() && RandomFunction.random(4) == 2) { - victim.getStateManager().set(EntityState.FROZEN, 16); - victim.graphics(Graphics.create(502)); - } - Graphics graphic = attack != null ? attack.getEndGraphic() : null; - victim.visualize(victim.getProperties().getDefenceAnimation(), graphic); - } + @Override + public void visualizeImpact(Entity entity, Entity victim, BattleState state) { + if (entity instanceof NPC && victim instanceof Player) { + Player p = (Player) victim; + Item shield = p.getEquipment().get(EquipmentContainer.SLOT_SHIELD); + if (shield != null && (shield.getId() == 11283 || shield.getId() == 11284)) { + if (shield.getId() == 11284) { + p.getEquipment().replace(new Item(11283), EquipmentContainer.SLOT_SHIELD); + shield = p.getEquipment().get(EquipmentContainer.SLOT_SHIELD); + shield.setCharge(0); + } + if (shield.getCharge() < 1000) { + shield.setCharge(shield.getCharge() + 20); + EquipmentContainer.updateBonuses(p); + p.getPacketDispatch().sendMessage("Your dragonfire shield glows more brightly."); + playAudio(p, Sounds.DRAGONSLAYER_ABSORB_FIRE_3740); + p.faceLocation(entity.getCenterLocation()); + victim.visualize(Animation.create(6695), Graphics.create(1163)); + } else { + p.getPacketDispatch().sendMessage("Your dragonfire shield is already fully charged."); + } + return; + } + } + if (!fire && !hasTimerActive(victim, "frozen:immunity") && RandomFunction.random(4) == 2) { + registerTimer(victim, spawnTimer("frozen", 16, true)); + victim.graphics(Graphics.create(502)); + } + Graphics graphic = attack != null ? attack.getEndGraphic() : null; + victim.visualize(victim.getProperties().getDefenceAnimation(), graphic); + } - @Override - public void impact(Entity entity, Entity victim, BattleState state) { - assert state != null; - int hit = state.getEstimatedHit(); - if (hit > -1) { - victim.getImpactHandler().handleImpact(entity, hit, CombatStyle.MAGIC, state); - } - hit = state.getSecondaryHit(); - if (hit > -1) { - victim.getImpactHandler().handleImpact(entity, hit, CombatStyle.MAGIC, state); - } - } + @Override + public void impact(Entity entity, Entity victim, BattleState state) { + assert state != null; + int hit = state.getEstimatedHit(); + if (hit > -1) { + victim.getImpactHandler().handleImpact(entity, hit, CombatStyle.MAGIC, state); + } + hit = state.getSecondaryHit(); + if (hit > -1) { + victim.getImpactHandler().handleImpact(entity, hit, CombatStyle.MAGIC, state); + } + } - @Override - public void adjustBattleState(Entity entity, Entity victim, BattleState state) { - if (victim.isPlayer() && !fire) { - Item item = victim.asPlayer().getEquipment().get(EquipmentContainer.SLOT_SHIELD); - if (item != null && (item.getId() == 2890 || item.getId() == 9731) && state.getEstimatedHit() > 10) { - state.setEstimatedHit(RandomFunction.random(10)); - } - } - CombatStyle style = state.getStyle(); - super.adjustBattleState(entity, victim, state); - state.setStyle(style); - } + @Override + public void adjustBattleState(Entity entity, Entity victim, BattleState state) { + if (victim.isPlayer() && !fire) { + Item item = victim.asPlayer().getEquipment().get(EquipmentContainer.SLOT_SHIELD); + if (item != null && (item.getId() == 2890 || item.getId() == 9731) && state.getEstimatedHit() > 10) { + state.setEstimatedHit(RandomFunction.random(10)); + } + } + CombatStyle style = state.getStyle(); + super.adjustBattleState(entity, victim, state); + state.setStyle(style); + } - @Override - protected int getFormattedHit(Entity entity, Entity victim, BattleState state, int hit) { - return formatHit(victim, hit); - } + @Override + protected int getFormattedHit(Entity entity, Entity victim, BattleState state, int hit) { + return formatHit(victim, hit); + } - @Override - public int calculateAccuracy(Entity entity) { - return 4000; - } + @Override + public int calculateAccuracy(Entity entity) { + return 4000; + } - @Override - public int calculateHit(Entity entity, Entity victim, double modifier) { - int damage = maximumHit; - if (victim instanceof Player) { - int val = victim.getDragonfireProtection(fire); - if ((val & 0x2) != 0) { - damage *= 0.1; - } - if ((val & 0x4) != 0) { - damage *= 0.1; - } - if ((val & 0x8) != 0) { - damage *= 0.6; - } - } - return damage; - } + @Override + public int calculateHit(Entity entity, Entity victim, double modifier) { + return calculateDragonfireMaxHit(victim, maximumHit, !fire, 0, true); + } - @Override - public int calculateDefence(Entity victim, Entity attacker) { - return CombatStyle.MAGIC.getSwingHandler().calculateDefence(victim, attacker); - } + @Override + public int calculateDefence(Entity victim, Entity attacker) { + return CombatStyle.MAGIC.getSwingHandler().calculateDefence(victim, attacker); + } - @Override - public double getSetMultiplier(Entity e, int skillId) { - return 1.0; - } + @Override + public double getSetMultiplier(Entity e, int skillId) { + return 1.0; + } -} \ No newline at end of file +} 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 00f8d7913..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 @@ -12,6 +12,9 @@ import core.game.world.update.flag.context.Graphics; import core.plugin.Initializable; import core.plugin.Plugin; import core.tools.RandomFunction; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.playGlobalAudio; /** * Handles the Abyssal whip's Energy drain special attack. @@ -54,20 +57,20 @@ 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); ((Player) entity).getSettings().updateRunEnergy(-10); } state.setEstimatedHit(hit); - entity.asPlayer().getAudioManager().send(new Audio(2713), true); return 1; } @Override public void visualize(Entity entity, Entity victim, BattleState state) { + playGlobalAudio(entity.getLocation(), Sounds.ENERGYDRAIN_2713); entity.animate(ANIMATION); victim.graphics(GRAPHIC); } diff --git a/Server/src/main/content/global/handlers/item/equipment/special/ExcaliburSpecialHandler.java b/Server/src/main/content/global/handlers/item/equipment/special/ExcaliburSpecialHandler.java index 96357d23a..3ba8c9992 100644 --- a/Server/src/main/content/global/handlers/item/equipment/special/ExcaliburSpecialHandler.java +++ b/Server/src/main/content/global/handlers/item/equipment/special/ExcaliburSpecialHandler.java @@ -5,7 +5,6 @@ import core.game.node.entity.skill.Skills; 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.state.EntityState; import core.game.node.entity.combat.MeleeSwingHandler; import core.game.node.entity.impl.Animator.Priority; import core.game.node.entity.player.Player; @@ -13,6 +12,9 @@ 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 excalibur special attack. @@ -61,13 +63,14 @@ public final class ExcaliburSpecialHandler extends MeleeSwingHandler implements if (!p.getSettings().drainSpecial(SPECIAL_ENERGY)) return -1; p.sendChat("For Camelot!"); + playAudio(entity.asPlayer(), Sounds.SANCTUARY_2539); p.visualize(ANIMATION, GRAPHIC); switch(p.getEquipment().get(EquipmentContainer.SLOT_WEAPON).getId()) { case 35: // Regular ol' excalibur p.getSkills().updateLevel(Skills.DEFENCE, 8, p.getSkills().getStaticLevel(Skills.DEFENCE) + 8); break; case 14632: // enhanced excalibur - p.getStateManager().set(EntityState.HEALOVERTIME,p,(int)15,(int)20,(int)5); + registerTimer(p, spawnTimer("healovertime", 3, 20, 4)); p.getSkills().updateLevel(Skills.DEFENCE, (int)(p.getSkills().getStaticLevel(Skills.DEFENCE)*0.15), (int)(p.getSkills().getStaticLevel(Skills.DEFENCE)*1.15)); 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 775ae3eb6..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 @@ -10,6 +10,9 @@ import core.game.world.update.flag.context.Animation; import core.plugin.Initializable; import core.plugin.Plugin; import core.tools.RandomFunction; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.playGlobalAudio; /** * Handles Vesta's Longsword special attack, feint. @@ -49,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, 2.50))); + 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; @@ -58,6 +62,7 @@ public final class FeintSpecialHandler extends MeleeSwingHandler implements Plug @Override public void visualize(Entity entity, Entity victim, BattleState state) { + playGlobalAudio(entity.getLocation(), Sounds.CLEAVE_2529); entity.animate(ANIMATION); } } 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 new file mode 100644 index 000000000..6a89af4fc --- /dev/null +++ b/Server/src/main/content/global/handlers/item/equipment/special/HamstringSpecialHandler.java @@ -0,0 +1,71 @@ +package content.global.handlers.item.equipment.special; + +import core.api.ContentAPIKt; +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.ImpactHandler; +import core.game.node.entity.combat.RangeSwingHandler; +import core.game.node.entity.player.Player; +import core.game.system.timer.RSTimer; +import core.game.system.timer.TimerFlag; +import core.plugin.Initializable; +import core.plugin.Plugin; +import core.tools.RandomFunction; +import org.rs09.consts.Items; + +@Initializable +public final class HamstringSpecialHandler extends RangeSwingHandler implements Plugin { + private static final int SPECIAL_ENERGY = 50; + + @Override + public Object fireEvent(String identifier, Object... args) { + return null; + } + + @Override + public Plugin newInstance(Object arg) throws Throwable { + CombatStyle.RANGE.getSwingHandler().register(Items.MORRIGANS_THROWING_AXE_13883, this); + return this; + } + + @Override + public int swing(Entity entity, Entity victim, BattleState state) { + Player p = (Player) entity; + configureRangeData(p, state); + if (state.getWeapon() == null || !Companion.hasAmmo(entity, state)) { + entity.getProperties().getCombatPulse().stop(); + p.getSettings().toggleSpecialBar(); + return -1; + } + if (!((Player) entity).getSettings().drainSpecial(SPECIAL_ENERGY)) { + return -1; + } + state.setStyle(CombatStyle.RANGE); + int max = calculateHit(entity, victim, 1.2); + state.setMaximumHit(max); + int hit = 0; + if (isAccurateImpact(entity, victim)) { + 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()); + ContentAPIKt.registerTimer(victim, new RSTimer(100, "hamstrung", true, false, new TimerFlag[] { TimerFlag.ClearOnDeath } ) { + @Override + public void onRegister(Entity entity) { + if(entity instanceof Player) { + ((Player)entity).sendMessage("You've been hamstrung! For the next minute, your run energy will drain 4x faster."); + } + } + + @Override + public boolean run(Entity entity) { + return false; + } + + }); + return 1 + (int) Math.ceil(entity.getLocation().getDistance(victim.getLocation()) * 0.3); + } +} + 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 8cbf175af..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 @@ -6,12 +6,14 @@ import core.game.node.entity.combat.CombatStyle; 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.audio.Audio; import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Graphics; import core.plugin.Plugin; import core.plugin.Initializable; import core.tools.RandomFunction; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.playGlobalAudio; /** * Handles the healing blade special attack. @@ -55,26 +57,26 @@ 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); - entity.asPlayer().getAudioManager().send(new Audio(3857), true); return 1; } @Override public void visualize(Entity entity, Entity victim, BattleState state) { + playGlobalAudio(entity.getLocation(), Sounds.GODWARS_SARADOMIN_SPECIAL_3857); entity.visualize(ANIMATION, GRAPHIC); } 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 e2db7e221..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 @@ -6,12 +6,14 @@ import core.game.node.entity.combat.CombatStyle; 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.state.EntityState; import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Graphics; import core.plugin.Plugin; import core.plugin.Initializable; import core.tools.RandomFunction; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.*; /** * Handles the Ice cleave special attack. @@ -55,11 +57,10 @@ 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); - entity.asPlayer().getAudioManager().send(3846); return 1; } @@ -67,13 +68,14 @@ public final class IceCleaveSpecialHandler extends MeleeSwingHandler implements public void adjustBattleState(Entity entity, Entity victim, BattleState state) { super.adjustBattleState(entity, victim, state); if (state.getEstimatedHit() > 0) { - victim.getStateManager().set(EntityState.FROZEN, 33); + registerTimer(victim, spawnTimer("frozen", 33, true)); victim.graphics(Graphics.create(369)); } } @Override public void visualize(Entity entity, Entity victim, BattleState state) { + playGlobalAudio(entity.getLocation(), Sounds.GODWARS_GODSWORD_SLASH_3846); entity.visualize(ANIMATION, GRAPHIC); } } 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 bcb681ba9..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 @@ -12,6 +12,9 @@ import core.game.world.update.flag.context.Graphics; import core.plugin.Plugin; import core.plugin.Initializable; import core.tools.RandomFunction; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.playGlobalAudio; /** * Handles the Rune claws special attack "Impale". @@ -56,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; @@ -64,6 +67,7 @@ public final class ImpaleSpecialHandler extends MeleeSwingHandler implements Plu @Override public void visualize(Entity entity, Entity victim, BattleState state) { + playGlobalAudio(entity.getLocation(), Sounds.IMPALE_2534); if (((Player) entity).getDetails().getRights() == Rights.ADMINISTRATOR) { entity.animate(Animation.create(2068)); return; 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 9699b5520..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 @@ -6,12 +6,14 @@ import core.game.node.entity.combat.CombatStyle; 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.audio.Audio; 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.Sounds; + +import static core.api.ContentAPIKt.playGlobalAudio; /** * Handles the Judgement special attack. @@ -55,16 +57,16 @@ 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); - entity.asPlayer().getAudioManager().send(new Audio(3865), true); return 1; } @Override public void visualize(Entity entity, Entity victim, BattleState state) { + playGlobalAudio(entity.getLocation(), Sounds.GODWARS_GODSWORD_SPECIAL_ATTACK_3865); entity.visualize(ANIMATION, GRAPHIC); } } 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 new file mode 100644 index 000000000..82f5b3eb3 --- /dev/null +++ b/Server/src/main/content/global/handlers/item/equipment/special/PhantomStrikeSpecialHandler.java @@ -0,0 +1,82 @@ +package content.global.handlers.item.equipment.special; + +import core.api.ContentAPIKt; +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.ImpactHandler; +import core.game.node.entity.combat.RangeSwingHandler; +import core.game.node.entity.player.Player; +import core.game.system.timer.RSTimer; +import core.game.system.timer.TimerFlag; +import core.plugin.Initializable; +import core.plugin.Plugin; +import core.tools.RandomFunction; +import org.rs09.consts.Items; + +@Initializable +public final class PhantomStrikeSpecialHandler extends RangeSwingHandler implements Plugin { + private static final int SPECIAL_ENERGY = 50; + + @Override + public Object fireEvent(String identifier, Object... args) { + return null; + } + + @Override + public Plugin newInstance(Object arg) throws Throwable { + CombatStyle.RANGE.getSwingHandler().register(Items.MORRIGANS_JAVELIN_13879, this); + CombatStyle.RANGE.getSwingHandler().register(Items.MORRIGANS_JAVELINP_13880, this); + CombatStyle.RANGE.getSwingHandler().register(Items.MORRIGANS_JAVELINP_PLUS_13881, this); + CombatStyle.RANGE.getSwingHandler().register(Items.MORRIGANS_JAVELINP_PLUS_PLUS_13882, this); + return this; + } + + @Override + public int swing(Entity entity, Entity victim, BattleState state) { + Player p = (Player) entity; + configureRangeData(p, state); + if (state.getWeapon() == null || !Companion.hasAmmo(entity, state)) { + entity.getProperties().getCombatPulse().stop(); + p.getSettings().toggleSpecialBar(); + return -1; + } + if (!((Player) entity).getSettings().drainSpecial(SPECIAL_ENERGY)) { + return -1; + } + state.setStyle(CombatStyle.RANGE); + int max = calculateHit(entity, victim, 1.0); + state.setMaximumHit(max); + int hit = 0; + if (isAccurateImpact(entity, victim)) { + hit = RandomFunction.random(max + 1); + } + state.setEstimatedHit(hit); + Companion.useAmmo(entity, state, victim.getLocation()); + final Entity attacker = entity; + final int initialDamage = hit; + ContentAPIKt.registerTimer(victim, new RSTimer(3, "phantom-strike", true, false, new TimerFlag[] { TimerFlag.ClearOnDeath } ) { + int remainingDamage = initialDamage; + + @Override + public void onRegister(Entity entity) { + if(entity instanceof Player) { + ((Player)entity).sendMessage("You start to bleed as the result of the javelin strike."); + } + } + + @Override + public boolean run(Entity entity) { + if(entity instanceof Player) { + ((Player)entity).sendMessage("You continue to bleed as the result of the javelin strike."); + } + int tickDamage = Math.min(remainingDamage, 5); + remainingDamage -= tickDamage; + entity.getImpactHandler().manualHit(attacker, tickDamage, ImpactHandler.HitsplatType.NORMAL); + return remainingDamage > 0; + } + + }); + return 1 + (int) Math.ceil(entity.getLocation().getDistance(victim.getLocation()) * 0.3); + } +} 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 418a7bf05..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 @@ -10,6 +10,9 @@ import core.plugin.Initializable; import core.plugin.Plugin; import core.tools.RandomFunction; import core.game.node.entity.combat.RangeSwingHandler; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.playGlobalAudio; /** * Handles the Magic longbow special attack "Powershot". @@ -53,11 +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)); - } - entity.asPlayer().getAudioManager().send(2536); + 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); @@ -65,6 +64,7 @@ public final class PowershotSpecialHandler extends RangeSwingHandler implements @Override public void visualize(Entity entity, Entity victim, BattleState state) { + playGlobalAudio(entity.getLocation(), Sounds.POWERSHOT_2536); entity.visualize(state.getRangeWeapon().getAnimation(), GRAPHIC); int speed = (int) (46 + (entity.getLocation().getDistance(victim.getLocation()) * 5)); Projectile.create(entity, victim, 249, 40, 36, 45, speed, 5, 11).send(); 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 0921b0c67..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 @@ -16,6 +16,9 @@ import core.game.world.update.flag.context.Graphics; import core.plugin.Plugin; import core.plugin.Initializable; import core.tools.RandomFunction; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.playGlobalAudio; /** * Handles the Powerstab special attack. @@ -82,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); @@ -94,6 +97,7 @@ public final class PowerstabSpecialHandler extends MeleeSwingHandler implements @Override public void visualize(Entity entity, Entity victim, BattleState state) { + playGlobalAudio(entity.getLocation(), Sounds.DRAGON_AXE_THUNDER_2530); entity.visualize(ANIMATION, GRAPHIC); } 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 2a3385f9e..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 @@ -11,6 +11,9 @@ import core.game.world.update.flag.context.Graphics; import core.plugin.Initializable; import core.plugin.Plugin; import core.tools.RandomFunction; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.playGlobalAudio; /** * Handles the puncture special attack combat swing. @@ -58,28 +61,23 @@ 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; } - entity.asPlayer().getAudioManager().send(2537); state.setSecondaryHit(hit); return 1; } @Override public void visualize(Entity entity, Entity victim, BattleState state) { + playGlobalAudio(entity.getLocation(), Sounds.PUNCTURE_2537); entity.visualize(ANIMATION, GRAPHIC); } } 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 2680f4503..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,16 +3,20 @@ 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.audio.Audio; +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; import core.plugin.Initializable; import core.plugin.Plugin; import core.tools.RandomFunction; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.playGlobalAudio; /** * Handles the granite maul special attack. @@ -63,22 +67,28 @@ 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); - entity.asPlayer().getAudioManager().send(new Audio(2715), true); + 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; } @Override public void visualize(Entity entity, Entity victim, BattleState state) { + playGlobalAudio(entity.getLocation(), Sounds.QUICKSMASH_2715); entity.visualize(ANIMATION, GRAPHIC); victim.animate(victim.getProperties().getDefenceAnimation()); } 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 c4f7e8d08..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 @@ -12,6 +12,9 @@ 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.playAudio; /** * Handles the Rampage special attack. @@ -58,19 +61,19 @@ public final class RampageSpecialHandler extends MeleeSwingHandler implements Pl return -1; } p.sendChat("Raarrrrrgggggghhhhhhh!"); + playAudio(entity.asPlayer(), Sounds.RAMPAGE_2538); 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)); - p.getAudioManager().send(new Audio(386), true); + 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 0da74b2a9..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 @@ -52,7 +52,7 @@ class SalamanderSwingHandler(private var style: CombatStyle) : CombatSwingHandle if (entity is Player) { state.weapon = Weapon(entity.equipment[3]) } - if (state!!.weapon == null || !SalamanderSwingHandler.hasAmmo(entity, state)) { + if (state!!.weapon == null || !hasAmmo(entity, state)) { entity!!.properties.combatPulse.stop() return -1 } @@ -67,7 +67,7 @@ class SalamanderSwingHandler(private var style: CombatStyle) : CombatSwingHandle return -1 } if(state.estimatedHit > victim.skills.lifepoints) state.estimatedHit = victim.skills.lifepoints - SalamanderSwingHandler.useAmmo(entity, state) + useAmmo(entity, state) return 1 } @@ -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 21cb7f485..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 @@ -6,12 +6,14 @@ import core.game.node.entity.combat.CombatStyle; 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.audio.Audio; import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Graphics; import core.plugin.Plugin; import core.plugin.Initializable; import core.tools.RandomFunction; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.playGlobalAudio; /** * Handles the Saradomin sword special attack. @@ -50,13 +52,12 @@ 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); - entity.asPlayer().getAudioManager().send(new Audio(3853), true); return 1; } @@ -75,9 +76,11 @@ public final class SaradominsLightningHandler extends MeleeSwingHandler implemen @Override public void visualize(Entity entity, Entity victim, BattleState state) { + playGlobalAudio(entity.getLocation(), Sounds.GODWARS_SARADOMIN_MAGIC_IMPACT_3853); entity.visualize(ANIMATION, GRAPHIC); } + @Override public Object fireEvent(String identifier, Object... args) { return null; 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 104748a42..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; @@ -11,6 +12,9 @@ import core.game.world.update.flag.context.Graphics; import core.plugin.Plugin; import core.plugin.Initializable; import core.tools.RandomFunction; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.playGlobalAudio; /** * Represents the Seercull's special attack which lowers the opponent's magic @@ -21,6 +25,13 @@ import core.tools.RandomFunction; @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. */ @@ -54,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; @@ -66,6 +75,7 @@ public final class SeercullSpecialHandler extends RangeSwingHandler implements P @Override public void visualize(Entity entity, Entity victim, BattleState state) { + playGlobalAudio(entity.getLocation(), Sounds.SOULSHOT_2546); victim.graphics(new Graphics(474)); int speed = (int) (35 + (entity.getLocation().getDistance(victim.getLocation()) * 10)); entity.visualize(entity.getProperties().getAttackAnimation(), DRAWBACK_GFX); 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 2ca75c4a1..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 @@ -12,6 +12,9 @@ import core.game.world.update.flag.context.Graphics; import core.plugin.Plugin; import core.plugin.Initializable; import core.tools.RandomFunction; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.playGlobalAudio; /** * Handles the Sever special attack. @@ -53,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)) { @@ -71,13 +74,13 @@ public final class SeverSpecialHandler extends MeleeSwingHandler implements Plug } } } - entity.asPlayer().getAudioManager().send(2540); state.setEstimatedHit(hit); return 1; } @Override public void visualize(Entity entity, Entity victim, BattleState state) { + playGlobalAudio(entity.getLocation(), Sounds.SEVER_2540); entity.visualize(ANIMATION, GRAPHIC); } } 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 afd9f1baa..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 @@ -6,12 +6,14 @@ import core.game.node.entity.combat.CombatStyle; 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.audio.Audio; 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.Sounds; + +import static core.api.ContentAPIKt.playGlobalAudio; /** * Handles the Shatter special attack. @@ -54,16 +56,16 @@ 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); } - entity.asPlayer().getAudioManager().send(new Audio(2541), true); state.setEstimatedHit(hit); return 1; } @Override public void visualize(Entity entity, Entity victim, BattleState state) { + playGlobalAudio(entity.getLocation(), Sounds.SHATTER_2541); entity.visualize(ANIMATION, GRAPHIC); } } diff --git a/Server/src/main/content/global/handlers/item/equipment/special/ShoveSpecialHandler.java b/Server/src/main/content/global/handlers/item/equipment/special/ShoveSpecialHandler.java index 22bea6e0d..12d92744b 100644 --- a/Server/src/main/content/global/handlers/item/equipment/special/ShoveSpecialHandler.java +++ b/Server/src/main/content/global/handlers/item/equipment/special/ShoveSpecialHandler.java @@ -6,7 +6,6 @@ import core.game.node.entity.combat.CombatStyle; 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.state.EntityState; import core.game.world.map.Direction; import core.game.world.map.Location; import core.game.world.map.Point; @@ -15,6 +14,9 @@ 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 dragon spear special attack. @@ -95,8 +97,7 @@ public final class ShoveSpecialHandler extends MeleeSwingHandler implements Plug } victim.getWalkingQueue().reset(); victim.getPulseManager().clear(); - victim.animate(STUN_ANIM); - victim.getStateManager().set(EntityState.STUNNED, 5); + stun(victim, 5); if (dir != null) { Point p = Direction.getWalkPoint(dir); Location dest = victim.getLocation().transform(p.getX(), p.getY(), 0); @@ -104,12 +105,12 @@ public final class ShoveSpecialHandler extends MeleeSwingHandler implements Plug victim.getWalkingQueue().addPath(dest.getX(), dest.getY()); } } - entity.asPlayer().getAudioManager().send(2533); return 1; } @Override public void visualize(Entity entity, Entity victim, BattleState state) { + playGlobalAudio(entity.getLocation(), Sounds.SHOVE_2544); entity.visualize(ANIMATION, GRAPHIC); } 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 d06305855..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 @@ -11,6 +11,9 @@ import core.game.world.update.flag.context.Graphics; import core.plugin.Initializable; import core.plugin.Plugin; import core.tools.RandomFunction; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.playGlobalAudio; /** * Handles the Dragon claws special attack "Slice and Dice". @@ -53,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}; } } } @@ -92,15 +96,16 @@ 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; } @Override public void visualize(Entity entity, Entity victim, BattleState state) { + playGlobalAudio(entity.getLocation(), Sounds.PUNCTURE_2537); entity.visualize(ANIMATION, GRAPHIC); } } 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 e5eb6e102..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 @@ -12,6 +12,9 @@ import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Graphics; import core.plugin.Plugin; import core.tools.RandomFunction; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.playGlobalAudio; /** * Handles Statius' Warhammer special attack - Smash. @@ -56,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); } @@ -66,6 +70,7 @@ public final class SmashSpecialHandler extends MeleeSwingHandler implements Plug @Override public void visualize(Entity entity, Entity victim, BattleState state) { + playGlobalAudio(entity.getLocation(), Sounds.TZHAAR_KET_OM_CRUSH_2520); entity.visualize(ANIMATION, GRAPHIC); } } 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 fceec555d..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; @@ -14,6 +15,9 @@ import core.plugin.Plugin; import core.tools.RandomFunction; import core.game.node.entity.combat.RangeSwingHandler; import core.game.world.GameWorld; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.playGlobalAudio; /** * Handles the magic shortbow special attack "Snapshot". @@ -22,6 +26,13 @@ import core.game.world.GameWorld; @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. */ @@ -64,15 +75,14 @@ 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); } - entity.asPlayer().getAudioManager().send(2545); state.setSecondaryHit(hit); Companion.useAmmo(entity, state, victim.getLocation()); return 1 + (int) Math.ceil(entity.getLocation().getDistance(victim.getLocation()) * 0.3); @@ -100,6 +110,7 @@ public final class SnapshotSpecialHandler extends RangeSwingHandler implements P @Override public void visualize(Entity entity, Entity victim, BattleState state) { + playGlobalAudio(entity.getLocation(), Sounds.SNAPSHOT_2545); entity.visualize(ANIMATION, GRAPHIC); int speed = (int) (27 + (entity.getLocation().getDistance(victim.getLocation()) * 5)); Projectile.create(entity, victim, 249, 40, 36, 20, speed, 15, 11).send(); 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 7c57c86d6..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 @@ -12,6 +12,9 @@ import core.plugin.Initializable; import core.plugin.Plugin; import core.tools.RandomFunction; import core.game.node.entity.combat.RangeSwingHandler; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.playGlobalAudio; /** * Represents the Dorgeshuun crossbow's special attack - snipe. @@ -57,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); @@ -68,6 +72,7 @@ public final class SnipeSpecialHandler extends RangeSwingHandler implements Plug @Override public void visualize(Entity entity, Entity victim, BattleState state) { + playGlobalAudio(entity.getLocation(), Sounds.DTTD_BONE_CROSSBOW_SA_1080); entity.animate(ANIMATION); Projectile.create(entity, victim, 698, 36, 25, 35, 72).send(); } 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 1b15db249..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 @@ -16,6 +16,9 @@ import core.game.world.update.flag.context.Graphics; import core.plugin.Plugin; import core.plugin.Initializable; import core.tools.RandomFunction; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.playGlobalAudio; /** * Handles Vesta's Spear special attack - Spear Wall. @@ -82,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); } @@ -93,7 +96,8 @@ public final class SpearWallSpecialHandler extends MeleeSwingHandler implements @Override public void visualize(Entity entity, Entity victim, BattleState state) { - entity.visualize(ANIMATION, GRAPHIC); + playGlobalAudio(entity.getLocation(), Sounds.CLEAVE_2529); + entity.visualize(ANIMATION, GRAPHIC); } @Override @@ -103,6 +107,7 @@ public final class SpearWallSpecialHandler extends MeleeSwingHandler implements if (s != null) { s.getVictim().animate(victim.getProperties().getDefenceAnimation()); } + } return; } 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 70c6cf229..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 @@ -19,6 +19,9 @@ import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Graphics; import core.plugin.Plugin; import core.tools.RandomFunction; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.playGlobalAudio; /** * Handles the Dragon halberd special attack. @@ -63,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); } @@ -156,6 +159,7 @@ public final class SweepSpecialHandler extends MeleeSwingHandler implements Plug @Override public void visualize(Entity entity, Entity victim, BattleState state) { + playGlobalAudio(entity.getLocation(), Sounds.HALBERD_SWIPE_2533); entity.visualize(ANIMATION, GRAPHIC); } 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 028f356cc..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 @@ -7,12 +7,14 @@ import core.game.node.entity.combat.CombatStyle; 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.audio.Audio; import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Graphics; import core.plugin.Plugin; import core.plugin.Initializable; import core.tools.RandomFunction; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.playGlobalAudio; /** * Handles the Warstrike special attack. @@ -55,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) { @@ -66,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) @@ -78,12 +80,12 @@ public final class WarstrikeSpecialHandler extends MeleeSwingHandler implements } } } - entity.asPlayer().getAudioManager().send(new Audio(3834), true); return 1; } @Override public void visualize(Entity entity, Entity victim, BattleState state) { + playGlobalAudio(entity.getLocation(), Sounds.GODWARS_SARADOMIN_MAGIC_CASTANDFIRE_3834); entity.visualize(ANIMATION, GRAPHIC); } } 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 cc1298ff0..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 @@ -7,12 +7,14 @@ import core.game.node.entity.combat.CombatStyle; 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.audio.Audio; import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Graphics; import core.plugin.Plugin; import core.plugin.Initializable; import core.tools.RandomFunction; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.playGlobalAudio; /** * Handles Darklight's special attack, Weaken. @@ -55,24 +57,25 @@ 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; } @Override public void visualize(Entity entity, Entity victim, BattleState state) { entity.visualize(ANIMATION, GRAPHIC); - entity.asPlayer().getAudioManager().send(new Audio(225), true); + playGlobalAudio(entity.getLocation(), Sounds.DARKLIGHT_WEAKEN_225); } } 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 71d5294de..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 @@ -43,14 +43,10 @@ public class DiangoReclaimInterface extends ComponentPlugin { if(curOpen != null){ curOpen.close(player); } + Item[] reclaimables = getEligibleItems(player); + player.setAttribute("diango-reclaimables", reclaimables); //filter out items the player already has in their bank, inventory, or equipped - Item[] reclaimables = ITEMS.stream().filter(Objects::nonNull) - .filter(item -> !player.getEquipment().containsItem(item) && !player.getInventory().containsItem(item) && !player.getBank().containsItem(item) - && (item.getId() != 14654 - || (!(inInventory(player, 14655, 1) || inEquipment(player, 14656, 1)) && player.getAttribute("sotr:purchased",false)) - )) - .toArray(Item[]::new); //only send items if there are some to send if(reclaimables.length > 0) { @@ -68,18 +64,36 @@ public class DiangoReclaimInterface extends ComponentPlugin { @Override public boolean handle(Player player, Component component, int opcode, int button, int slot, int itemId) { - Item[] reclaimables = ITEMS.stream().filter(Objects::nonNull).filter(item -> !player.getEquipment().containsItem(item) && !player.getInventory().containsItem(item) && !player.getBank().containsItem(item)).toArray(Item[]::new); + Item[] reclaimables = player.getAttribute("diango-reclaimables", null); + if (reclaimables == null) + reclaimables = getEligibleItems(player); + + Item reclaimItem = reclaimables[slot]; + if (reclaimItem == null) { + player.sendMessage("Something went wrong there. Please try again."); + return true; + } + switch(opcode){ case 155: //interface item option 1 == take //add the clicked item to the player's inventory and refresh the interface - player.getInventory().add(reclaimables[slot]); + player.getInventory().add(reclaimItem); refresh(player); break; case 196: //interface item option 2 == examine //send the examine text for the item to the player - player.getPacketDispatch().sendMessage(reclaimables[slot].getDefinition().getExamine()); + player.getPacketDispatch().sendMessage(reclaimItem.getDefinition().getExamine()); break; } return false; } + + public static Item[] getEligibleItems (Player player) { + return ITEMS.stream().filter(Objects::nonNull) + .filter(item -> !player.getEquipment().containsItem(item) && !player.getInventory().containsItem(item) && !player.getBank().containsItem(item) + && (item.getId() != 14654 + || (!(inInventory(player, 14655, 1) || inEquipment(player, 14656, 1)) && player.getAttribute("sotr:purchased",false)) + )) + .toArray(Item[]::new); + } } diff --git a/Server/src/main/content/global/handlers/item/withitem/FruitCutting.kt b/Server/src/main/content/global/handlers/item/withitem/FruitCutting.kt new file mode 100644 index 000000000..ca6db9081 --- /dev/null +++ b/Server/src/main/content/global/handlers/item/withitem/FruitCutting.kt @@ -0,0 +1,87 @@ +package content.global.handlers.item.withitem + +import core.api.* +import core.game.dialogue.DialogueFile +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.item.Item +import core.game.world.update.flag.context.Animation +import core.tools.START_DIALOGUE +import org.rs09.consts.Items + +class FruitCuttingListener : InteractionListener { + val anim = Animation(1192) // TODO Add fruit cutting animation to ConstLib + + enum class Fruit(val fruit: Int, val chunk: Int, val slice: Item) { + PINEAPPLE(Items.PINEAPPLE_2114, Items.PINEAPPLE_CHUNKS_2116, Item(Items.PINEAPPLE_RING_2118, 4)), + BANANA(Items.BANANA_1963, -1, Item(Items.SLICED_BANANA_3162)), + LEMON(Items.LEMON_2102, Items.LEMON_CHUNKS_2104, Item(Items.LEMON_SLICES_2106)), + LIME(Items.LIME_2120, Items.LIME_CHUNKS_2122, Item(Items.LIME_SLICES_2124)), + ORANGE(Items.ORANGE_2108, Items.ORANGE_CHUNKS_2110, Item(Items.ORANGE_SLICES_2112)); + + companion object { + val productOfChunk = values().associate { it.fruit to it.chunk } + val productOfSlice = values().associate { it.fruit to it.slice } + val cutable = intArrayOf(PINEAPPLE.fruit, BANANA.fruit, LEMON.fruit, LIME.fruit, ORANGE.fruit) + + fun forChunkId(id: Int) : Int { + return productOfChunk[id]!! + } + + fun forSliceId(id: Int) : Item { + return productOfSlice[id]!! + } + } + } + + override fun defineListeners() { + onUseWith(IntType.ITEM, Fruit.cutable, Items.KNIFE_946) { player, used, _ -> + openDialogue( + player, + FruitCuttingDialogue(used.id, Fruit.forChunkId(used.id), Fruit.forSliceId(used.id)) + ) + return@onUseWith true + } + } +} + +class FruitCuttingDialogue(val fruit: Int, val chunk: Int, val slice: Item) : DialogueFile() { + val anim = Animation(1192) + override fun handle(componentID: Int, buttonID: Int) { + when(stage) { + START_DIALOGUE -> { + if(chunk == -1) { + if(removeItem(player!!, fruit) && addItem(player!!, slice.id)) { + animate(player!!, anim, true) + sendMessage(player!!, "You deftly chop the ${getItemName(fruit).lowercase()} into slices.") + } + } else { + setInterfaceText(player!!, "Would you like to...", 140, 4) + sendItemOnInterface(player!!, 140, 6, chunk, 1) + sendItemOnInterface(player!!, 140, 5, slice.id, 1) + setInterfaceText(player!!, "Slice the ${getItemName(fruit).lowercase()}", 140, 2) + setInterfaceText(player!!, "Dice the ${getItemName(fruit).lowercase()}", 140, 3) + openInterface(player!!, 140) + when(buttonID) { + 1 -> { + closeInterface(player!!) + if(removeItem(player!!, fruit) && addItem(player!!, slice.id, slice.amount)) { + animate(player!!, anim, true) + sendMessage(player!!, "You cut the ${getItemName(fruit).lowercase()} into slices.") + } + } + 2 -> { + closeInterface(player!!) + if(removeItem(player!!, fruit) && addItem(player!!, chunk)) { + animate(player!!, anim, true) + sendMessage(player!!, "You cut the ${getItemName(fruit).lowercase()} into chunks.") + } + } + + } + } + } + } + } + +} \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/item/withitem/FruitCuttingPlugin.java b/Server/src/main/content/global/handlers/item/withitem/FruitCuttingPlugin.java deleted file mode 100644 index a9f7fd003..000000000 --- a/Server/src/main/content/global/handlers/item/withitem/FruitCuttingPlugin.java +++ /dev/null @@ -1,273 +0,0 @@ -package content.global.handlers.item.withitem; - -import static core.api.ContentAPIKt.*; -import core.game.component.Component; -import core.plugin.Initializable; -import kotlin.Unit; -import org.rs09.consts.Items; -import core.game.dialogue.DialoguePlugin; -import core.game.interaction.NodeUsageEvent; -import core.game.interaction.UseWithHandler; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.RunScript; -import core.game.node.item.Item; -import core.game.world.update.flag.context.Animation; -import core.plugin.Plugin; - -/** - * Represents the fruit cutting plugin. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class FruitCuttingPlugin extends UseWithHandler { - - /** - * Represents the cutting banana animation. - */ - private static final Animation ANIMATION = new Animation(1192); - - /** - * Constructs a new {@code FruitCuttingPlugin} {@code Object}. - */ - public FruitCuttingPlugin() { - super(946); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - for (Fruit fruit : Fruit.values()) { - addHandler(fruit.getBase().getId(), ITEM_TYPE, this); - } - new FruitCuttingDialogue().init(); - return this; - } - - @Override - public boolean handle(NodeUsageEvent event) { - final Player player = event.getPlayer(); - final Fruit fruit = Fruit.forBase(event.getUsedItem()); - if (fruit == Fruit.BANANA || fruit == Fruit.LEMON) { - if (player.getInventory().remove(fruit.getBase())) { - player.lock(2); - player.animate(ANIMATION); - player.getInventory().add(fruit.getSliced()); - player.getPacketDispatch().sendMessage("You deftly chop the "+ fruit.name().toLowerCase() + " into slices."); - } - return true; - } - player.getDialogueInterpreter().open(31237434, fruit); - return true; - } - - /** - * Represents the a fruit to cut. - * @author 'Vexia - * @date 30/11/2013 - */ - public enum Fruit { - PINEAPPLE(new Item(2114), new Item(2116), new Item(2118, 4)), - BANANA(new Item(1963), null, new Item(3162)), - LEMON(new Item(2102), new Item(Items.LEMON_CHUNKS_2104), new Item(2106)), - LIME(new Item(Items.LIME_2120),new Item(Items.LIME_CHUNKS_2122), new Item(Items.LIME_SLICES_2124)), - ORANGE(new Item(2108), new Item(2110), new Item(2112)); - - /** - * Constructs a new {@code FruitCuttingPlugin.java} {@code Object}. - * @param base the base item. - * @param diced the diced item. - * @param sliced the sliced item. - */ - Fruit(Item base, Item diced, Item sliced) { - this.base = base; - this.diced = diced; - this.sliced = sliced; - } - - /** - * Represents the base item. - */ - private final Item base; - - /** - * Represents the diced item. - */ - private final Item diced; - - /** - * Represents the sliced item. - */ - private final Item sliced; - - /** - * Gets the base. - * @return The base. - */ - public Item getBase() { - return base; - } - - /** - * Gets the diced. - * @return The diced. - */ - public Item getDiced() { - return diced; - } - - /** - * Gets the sliced. - * @return The sliced. - */ - public Item getSliced() { - return sliced; - } - - /** - * Method used to get the fruit for the base item. - * @param item the item. - * @return the fruit. - */ - public static Fruit forBase(final Item item) { - for (Fruit fruit : values()) { - if (fruit.getBase().getId() == item.getId()) { - return fruit; - } - } - return null; - } - } - - /** - * Represents the dialogue plugin used to cut fruit. - * @author 'Vexia - * @version 1.0 - */ - public static final class FruitCuttingDialogue extends DialoguePlugin { - - /** - * Represents the component interface. - */ - private static final Component COMPONENT = new Component(140); - - /** - * Represents the fruit. - */ - private Fruit fruit; - - /** - * Represents the fruit to cut. - */ - private Item item; - - /** - * Represents if its a sliced cut. - */ - private boolean slice; - - /** - * Constructs a new {@code FruitCuttingDialogue} {@code Object}. - */ - public FruitCuttingDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code FruitCuttingDialogue} {@code Object}. - * @param player the player. - */ - public FruitCuttingDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new FruitCuttingDialogue(player); - } - - @Override - public boolean open(Object... args) { - fruit = ((Fruit) args[0]); - player.getPacketDispatch().sendString("Would you like to...", 140, 4); - player.getPacketDispatch().sendItemOnInterface(fruit.getSliced().getId(), 1, 140, 5); - player.getPacketDispatch().sendItemOnInterface(fruit.getDiced().getId(), 1, 140, 6); - player.getPacketDispatch().sendString("Slice the " + fruit.getBase().getName().toLowerCase() + ".", 140, 2); - player.getPacketDispatch().sendString("Dice the " + fruit.getBase().getName().toLowerCase() + ".", 140, 3); - player.getInterfaceManager().openChatbox(COMPONENT); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - player.getInterfaceManager().openChatbox(309); - player.getPacketDispatch().sendItemZoomOnInterface(1, 160, 309, 2); - slice = buttonId == 2 ? false : true; - item = buttonId == 2 ? fruit.getDiced() : fruit.getSliced(); - player.getPacketDispatch().sendString("



" + item.getName(), 309, 6); - player.getPacketDispatch().sendItemZoomOnInterface(item.getId(), 175, 309, 2); - stage = 1; - break; - case 1: - int amount = 0; - switch (buttonId) { - case 6: - amount = 1; - break; - case 5: - amount = 5; - break; - case 4: - sendInputDialogue(player, false, "Enter the amount:", (value) -> { - String s = value.toString(); - s = s.replace("k","000"); - s = s.replace("K","000"); - int val = Integer.parseInt(s); - cut(player, val, slice); - return Unit.INSTANCE; - }); - return true; - case 3: - amount = player.getInventory().getAmount(fruit.getBase()); - break; - } - cut(player, amount, slice); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 31237434 }; - } - - /** - * Method used to cut a fruit. - * @param player the player. - * @param amount the amount. - * @param slice if its sliced. - */ - private final void cut(final Player player, int amount, boolean slice) { - if (amount > player.getInventory().getAmount(fruit.getBase())) { - amount = player.getInventory().getAmount(fruit.getBase()); - } - end(); - final Item remove = new Item(fruit.getBase().getId(), amount); - if (!player.getInventory().containsItem(remove)) { - return; - } - if (player.getInventory().remove(remove)) { - player.getPacketDispatch().sendMessage("You cut the " + fruit.name().toLowerCase() + " into " + (slice ? fruit == Fruit.PINEAPPLE ? "rings" : "slices" : "chunks") + "."); - for (int i = 0; i < amount; i++) { - player.getInventory().add(slice ? fruit.getSliced() : fruit.getDiced()); - } - } - } - - } -} diff --git a/Server/src/main/content/global/handlers/item/withitem/GodswordHiltAttachPlugin.java b/Server/src/main/content/global/handlers/item/withitem/GodswordHiltAttachPlugin.java index 9b027a835..2e0522b3c 100644 --- a/Server/src/main/content/global/handlers/item/withitem/GodswordHiltAttachPlugin.java +++ b/Server/src/main/content/global/handlers/item/withitem/GodswordHiltAttachPlugin.java @@ -7,6 +7,9 @@ import core.game.node.item.Item; import core.plugin.Plugin; import core.plugin.Initializable; import core.tools.StringUtils; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.playAudio; /** * Handles the attaching of a hilt on the godsword blade. @@ -40,6 +43,7 @@ public final class GodswordHiltAttachPlugin extends UseWithHandler { item = new Item(item.getId() - 8); player.getInventory().add(item); String name = item.getDefinition().getName(); + playAudio(player, Sounds.GODWARS_GODSWORD_FIX_3889); player.getPacketDispatch().sendMessage("You attach the hilt to the blade and make a" + (StringUtils.isPlusN(name) ? "n " : " ") + name + "."); return true; } diff --git a/Server/src/main/content/global/handlers/item/withitem/ImpJarCreatePlugin.java b/Server/src/main/content/global/handlers/item/withitem/ImpJarCreatePlugin.java index 881f0e1ab..e6d61a6be 100644 --- a/Server/src/main/content/global/handlers/item/withitem/ImpJarCreatePlugin.java +++ b/Server/src/main/content/global/handlers/item/withitem/ImpJarCreatePlugin.java @@ -8,6 +8,8 @@ import core.game.node.scenery.Scenery; import core.plugin.Initializable; import core.plugin.Plugin; +import static core.api.ContentAPIKt.*; + /** * Handles the creating of an imp jar. * @author Vexia @@ -62,12 +64,12 @@ public final class ImpJarCreatePlugin extends UseWithHandler { * @param player the player. */ private void fillOilStill(Player player, Item used) { - int configValue = player.getConfigManager().get(425); + int configValue = getVarp(player, 425); if (used.getId() == 11262 || used.getId() == 10012) { if (configValue == 0 && used.getId() == 11262) { player.getInventory().replace(new Item(229), used.getSlot()); player.sendMessage("You refine some imp repellent."); - player.getConfigManager().set(425, 64, true); + setVarp(player, 425, 64, true); return; } else if (configValue == 32) { player.sendMessage("There is already lamp oil in the still."); @@ -77,7 +79,7 @@ public final class ImpJarCreatePlugin extends UseWithHandler { player.sendMessage("There is already imp repellent in the still."); } else { player.getInventory().replace(new Item(11260), used.getSlot()); - player.getConfigManager().set(425, 0, true); + setVarp(player, 425, 0, true); player.sendMessage("You turn the butterfly jar into an impling jar."); } return; @@ -95,7 +97,7 @@ public final class ImpJarCreatePlugin extends UseWithHandler { } if (used.getId() == 1939) { if (player.getInventory().remove(new Item(1939))) { - player.getConfigManager().set(425, 32, true); + setVarp(player, 425, 32, true); player.sendMessage("You refine some swamp tar into lamp oil."); } } else { @@ -105,7 +107,7 @@ public final class ImpJarCreatePlugin extends UseWithHandler { if (player.getInventory().contains(4525, 1) || player.getInventory().contains(4535, 1) || player.getInventory().contains(4546, 1) || player.getInventory().contains(4700, 1)) { Item replace = new Item(used.getId() == 4525 ? 4522 : used.getId() == 4535 ? 4537 : used.getId() == 4546 ? 4548 : 4701); player.getInventory().replace(replace, used.getSlot()); - player.getConfigManager().set(425, 0, true); + setVarp(player, 425, 0, true); player.sendMessage("You fill the item with oil."); } } diff --git a/Server/src/main/content/global/handlers/item/withitem/MakeGlassInterfacePlugin.java b/Server/src/main/content/global/handlers/item/withitem/MakeGlassInterfacePlugin.java deleted file mode 100644 index b10b05a26..000000000 --- a/Server/src/main/content/global/handlers/item/withitem/MakeGlassInterfacePlugin.java +++ /dev/null @@ -1,71 +0,0 @@ -package content.global.handlers.item.withitem; - -import core.game.component.Component; -import core.plugin.Initializable; -import core.game.node.entity.skill.Skills; -import content.global.skill.crafting.GlassProduct; -import core.game.interaction.NodeUsageEvent; -import core.game.interaction.UseWithHandler; -import core.game.node.entity.player.Player; -import core.plugin.Plugin; - -/** - * Represents the plugin used to make glass. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public class MakeGlassInterfacePlugin extends UseWithHandler { - - /** - * Constructs a new {@code MakeGlassInterfacePlugin} {@code Object}. - */ - public MakeGlassInterfacePlugin() { - super(1775); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - addHandler(1785, ITEM_TYPE, this); - return this; - } - - @Override - public boolean handle(final NodeUsageEvent event) { - event.getPlayer().getInterfaceManager().open(new Component(542)); - final Player player = event.getPlayer(); - switch (GlassProduct.BEER) { - case BEER: - break; - case CANDLE: - if (player.getSkills().getLevel(Skills.CRAFTING) < 4) - player.getPacketDispatch().sendString("Candle lantern", 542, 25); - break; - case FISHBOWL: - if (player.getSkills().getLevel(Skills.CRAFTING) < 42) - player.getPacketDispatch().sendString("Fish Bowl", 542, 28); - break; - case LANTERN_LENS: - if (player.getSkills().getLevel(Skills.CRAFTING) < 49) - player.getPacketDispatch().sendString("Lantern lens", 542, 27); - break; - case OIL_LAMP: - if (player.getSkills().getLevel(Skills.CRAFTING) < 12) - player.getPacketDispatch().sendString("Oil lamp", 542, 26); - break; - case ORB: - if (player.getSkills().getLevel(Skills.CRAFTING) < 46) - player.getPacketDispatch().sendString("Unpowered orb", 542, 23); - break; - case VIAL: - if (player.getSkills().getLevel(Skills.CRAFTING) < 33) - player.getPacketDispatch().sendString("Vial", 542, 22); - break; - default: - break; - - } - return true; - } - -} diff --git a/Server/src/main/content/global/handlers/item/withitem/PoisonedWeaponListeners.kt b/Server/src/main/content/global/handlers/item/withitem/PoisonedWeaponListeners.kt index 61add9ec9..c3b6816c4 100644 --- a/Server/src/main/content/global/handlers/item/withitem/PoisonedWeaponListeners.kt +++ b/Server/src/main/content/global/handlers/item/withitem/PoisonedWeaponListeners.kt @@ -61,6 +61,16 @@ class PoisonedWeaponListeners : InteractionListener { MITHRIL_DART(809, 815, 5632, 5639), ADAMANT_DART(810, 816, 5633, 5640), + BRONZE_BOLT(877, 878, 6061, 6062), + BLURITE_BOLT(9139, 9286, 9293, 9300), + IRON_BOLT(9140, 9287, 9294, 9301), + STEEL_BOLT(9141, 9288, 9295, 9302), + BLACK_BOLT(13083, 13084, 13085, 13086), + MITHRIL_BOLT(9142, 9289, 9296, 9303), + ADAMANT_BOLT(9143, 9290, 9297, 9304), + RUNE_BOLT(9144, 9291, 9298, 9305), + SILVER_BOLT(9145, 9292, 9299, 9306), + RUNE_DART(811, 817, 5634, 5641), IRON_JAVELIN(825, 831, Items.IRON_JAVELINP_PLUS_5643, 5648), BRONZE_JAVELIN(826, 832, Items.BRONZE_JAVELINP_PLUS_5642, 5649), @@ -68,6 +78,7 @@ class PoisonedWeaponListeners : InteractionListener { MITHRIL_JAVELIN(828, 834, Items.MITHRIL_JAVELINP_PLUS_5645, 5651), ADAMANT_JAVELIN(829, 835, Items.ADAMANT_JAVELINP_PLUS_5646, 5652), RUNE_JAVELIN(830, 836, Items.RUNE_JAVELINP_PLUS_5647, 5653), + MORRIGAN_JAVELIN(13879, 13880, 13881, 13882), IRON_DAGGER(1203, 1219, 5668, 5686), BRONZE_DAGGER(1205, 1221, 5670, 5688), @@ -77,6 +88,7 @@ class PoisonedWeaponListeners : InteractionListener { RUNE_DAGGER(1213, 1229, 5678, 5696), DRAGON_DAGGER(1215, 1231, 5680, 5698), BLACK_DAGGER(1217, 1233, 5682, 5700), + BONE_DAGGER(8872, 8874, 8876, 8878), BRONZE_SPEAR(1237, 1251, 5704, Items.BRONZE_SPEARP_PLUS_PLUS_5718), IRON_SPEAR(1239, 1253, 5706, Items.IRON_SPEARP_PLUS_PLUS_5720), @@ -98,4 +110,4 @@ class PoisonedWeaponListeners : InteractionListener { } } } -} \ No newline at end of file +} diff --git a/Server/src/main/content/global/handlers/item/withnpc/ArchaeologicalExpertListener.kt b/Server/src/main/content/global/handlers/item/withnpc/ArchaeologicalExpertListener.kt deleted file mode 100644 index b6bfb4851..000000000 --- a/Server/src/main/content/global/handlers/item/withnpc/ArchaeologicalExpertListener.kt +++ /dev/null @@ -1,50 +0,0 @@ -package content.global.handlers.item.withnpc - -import core.api.openDialogue -import org.rs09.consts.Items -import org.rs09.consts.NPCs -import content.region.misthalin.digsite.dialogue.ArchaeologistcalExpertUsedOnDialogueFile -import core.game.interaction.InteractionListener -import core.game.interaction.IntType - -open class ArchaeologicalExpertListener() : InteractionListener { - val staff = Items.ANCIENT_STAFF_4675 - val unidentifiedLiquid = Items.UNIDENTIFIED_LIQUID_702 - val nitroglycerin = Items.NITROGLYCERIN_703 - val ammoniumNitrate = Items.AMMONIUM_NITRATE_701 - val nuggets = Items.NUGGETS_680 - val needle = Items.NEEDLE_1733 - val rottenApple = Items.ROTTEN_APPLE_1984 - val brokenGlass = Items.BROKEN_GLASS_1469 - val brokenArrow = Items.BROKEN_ARROW_687 - val panningTray = Items.PANNING_TRAY_677 - val bones = Items.BONES_526 - val buttons = Items.BUTTONS_688 - val crackedSample = Items.CRACKED_SAMPLE_674 - val oldTooth = Items.OLD_TOOTH_695 - val rustySword = Items.RUSTY_SWORD_686 - val brokenStaff = Items.BROKEN_STAFF_689 - val brokenArmour = Items.BROKEN_ARMOUR_698 - val damagedArmour = Items.DAMAGED_ARMOUR_697 - val ceramicRemains = Items.CERAMIC_REMAINS_694 - val beltBuckle = Items.BELT_BUCKLE_684 - val animalSkull = Items.ANIMAL_SKULL_671 - val specialCup = Items.SPECIAL_CUP_672 - val teddy = Items.TEDDY_673 - val stoneTablet = Items.STONE_TABLET_699 - - val items = intArrayOf(staff, unidentifiedLiquid, nitroglycerin, ammoniumNitrate, nuggets, needle, rottenApple, - brokenGlass, brokenArrow, panningTray, bones, buttons, crackedSample, oldTooth, rustySword, brokenStaff, brokenArmour, - damagedArmour, ceramicRemains, beltBuckle, animalSkull, specialCup, teddy, stoneTablet) - - val archy = NPCs.ARCHAEOLOGICAL_EXPERT_619 - - val lol = arrayOf(Items) - - override fun defineListeners() { - onUseWith(IntType.NPC, items, archy) { - player, used, with -> openDialogue(player, ArchaeologistcalExpertUsedOnDialogueFile(used.id)) - return@onUseWith false - } - } -} \ No newline at end of file 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 f89b011c7..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.id) - 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 415a47c9e..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(questStage(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(questStage(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/MistagEasterEgg.kt b/Server/src/main/content/global/handlers/item/withnpc/MistagEasterEgg.kt index e763a4963..10d7aa15d 100644 --- a/Server/src/main/content/global/handlers/item/withnpc/MistagEasterEgg.kt +++ b/Server/src/main/content/global/handlers/item/withnpc/MistagEasterEgg.kt @@ -7,12 +7,15 @@ import org.rs09.consts.NPCs import core.game.dialogue.DialogueFile import core.game.interaction.InteractionListener import core.game.interaction.IntType +import core.game.world.update.flag.context.Animation import core.tools.END_DIALOGUE +import core.api.* class MistagEasterEgg : InteractionListener { val DIAMOND = Items.DIAMOND_1601 val MISTAG = NPCs.MISTAG_2084 val ZANIK_RING = 14649 + val DRUNK_RENDER = 982 override fun defineListeners() { onUseWith(IntType.NPC,DIAMOND,MISTAG){ player, _, with -> @@ -30,6 +33,18 @@ class MistagEasterEgg : InteractionListener { player.appearance.transformNPC(-1) return@onUnequip true } + + onEquip(Items.BEER_1917){player, _ -> + setAttribute(player, "render-anim-override", DRUNK_RENDER) + return@onEquip true + } + + onUnequip(Items.BEER_1917){player, _ -> + removeAttribute(player, "render-anim-override") + player.appearance.setDefaultAnimations() + player.appearance.sync() + return@onUnequip true + } } } @@ -55,4 +70,4 @@ class MistagEasterEggDialogue(val hasRing: Boolean): DialogueFile(){ override fun npc(vararg messages: String?): Component? { return super.npc(core.game.dialogue.FacialExpression.OLD_HAPPY,*messages) } -} \ No newline at end of file +} 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 9a4e05f04..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(questStage(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 (questStage(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 837d1f774..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 @@ -10,6 +10,7 @@ import org.rs09.consts.Items import core.game.dialogue.SkillDialogueHandler import core.game.interaction.InteractionListener import core.game.interaction.IntType +import org.rs09.consts.Sounds class AmmoMouldOnFurnace : InteractionListener { private val furnaces = intArrayOf(4304, 6189, 11010, 11666, 12100, 12809, 18497, 26814, 30021, 30510, 36956, 37651) // abstract when smelting converted to kotlin @@ -18,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) { @@ -39,6 +40,7 @@ class AmmoMouldOnFurnace : InteractionListener { 0 -> { sendMessage(player,"You heat the steel bar into a liquid state.") animate(player, 3243) // 899 would be preferable but the arms spaz out + playAudio(player, Sounds.FURNACE_2725) } 3 -> { sendMessage(player,"You pour the molten metal into your cannonball mould.") diff --git a/Server/src/main/content/global/handlers/item/withobject/EctophialFillListener.kt b/Server/src/main/content/global/handlers/item/withobject/EctophialFillListener.kt deleted file mode 100644 index c60d9f822..000000000 --- a/Server/src/main/content/global/handlers/item/withobject/EctophialFillListener.kt +++ /dev/null @@ -1,45 +0,0 @@ -package content.global.handlers.item.withobject - -import core.api.* -import core.game.node.entity.player.link.audio.Audio -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 filling empty ectophial - * @author Byte - */ -@Suppress("unused") -class EctophialFillListener : InteractionListener { - - companion object { - private val ANIMATION = Animation(1652) - private val AUDIO = Audio(1132) - } - - override fun defineListeners() { - onUseWith(IntType.SCENERY, Items.ECTOPHIAL_4252, Scenery.ECTOFUNTUS_5282) { player, used, _ -> - lock(player, 5) - animate(player, ANIMATION) - playAudio(player, AUDIO) - - submitIndividualPulse(player, object: Pulse(3) { - override fun pulse(): Boolean { - if (removeItem(player, used)) { - addItem(player, Items.ECTOPHIAL_4251) - sendMessage(player, "You refill the ectophial from the Ectofuntus.") - unlock(player) - return true - } - return false - } - }) - - return@onUseWith true - } - } -} diff --git a/Server/src/main/content/global/handlers/item/withobject/GWDEntranceRopeListener.kt b/Server/src/main/content/global/handlers/item/withobject/GWDEntranceRopeListener.kt index a90652138..324364998 100644 --- a/Server/src/main/content/global/handlers/item/withobject/GWDEntranceRopeListener.kt +++ b/Server/src/main/content/global/handlers/item/withobject/GWDEntranceRopeListener.kt @@ -20,7 +20,7 @@ class GWDEntranceRopeListener : InteractionListener { return@onUseWith false } - setVarbit(player,1048,0,1,true) + setVarbit(player, 3932, 1, true) return@onUseWith true } diff --git a/Server/src/main/content/global/handlers/item/withobject/HatStand.kt b/Server/src/main/content/global/handlers/item/withobject/HatStand.kt index c9f3a3dde..91205e535 100644 --- a/Server/src/main/content/global/handlers/item/withobject/HatStand.kt +++ b/Server/src/main/content/global/handlers/item/withobject/HatStand.kt @@ -8,7 +8,7 @@ import core.game.interaction.IntType class HatStand : InteractionListener { - val hats = ItemDefinition.getDefinitions().values.filter { it.getConfiguration("equipment_slot",0) == EquipmentSlot.HAT.ordinal }.map { it.id }.toIntArray() + val hats = ItemDefinition.getDefinitions().values.filter { it.getConfiguration("equipment_slot",0) == EquipmentSlot.HEAD.ordinal }.map { it.id }.toIntArray() val hat_stand = 374 override fun defineListeners() { diff --git a/Server/src/main/content/global/handlers/item/withobject/IncubatorPlugin.java b/Server/src/main/content/global/handlers/item/withobject/IncubatorPlugin.java deleted file mode 100644 index 665f93b48..000000000 --- a/Server/src/main/content/global/handlers/item/withobject/IncubatorPlugin.java +++ /dev/null @@ -1,118 +0,0 @@ -package content.global.handlers.item.withobject; - -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.player.Player; -import core.game.node.entity.skill.Skills; -import content.global.skill.summoning.pet.IncubatorEgg; -import core.game.node.item.GroundItemManager; -import core.plugin.Initializable; -import core.plugin.Plugin; -import core.tools.StringUtils; -import content.global.skill.summoning.pet.IncubatorState; -import core.plugin.ClassScanner; - -/** - * Handles the incubator. - * @author Vexia - */ -@Initializable -public class IncubatorPlugin extends OptionHandler { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ClassScanner.definePlugin(new IncubatorEggHandler()); - SceneryDefinition.forId(28359).getHandlers().put("option:take-egg", this); - SceneryDefinition.forId(28359).getHandlers().put("option:inspect", this); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - int inc = player.getAttribute("inc", -1); - switch (option) { - case "take-egg": - if (inc == -1) { - player.sendMessage("The egg is still incubating."); - return true; - } - IncubatorEgg egg = IncubatorEgg.values()[inc]; - if (!player.getInventory().hasSpaceFor(egg.getProduct())) { - player.sendMessage("You don't have enough inventory space."); - return true; - } - { - String name = egg.getProduct().getName().toLowerCase(); - player.varpManager.get(1160).setVarbit(4,0).send(player); - player.sendMessage("You take your " + name + " out of the incubator."); - if(!player.getInventory().add(egg.getProduct())){ - GroundItemManager.create(egg.getProduct(),player); - } - player.removeAttribute("inc"); - } - return true; - case "inspect": - if (player.states.get("incubator") != null || inc != -1) { - IncubatorState p = (IncubatorState) player.states.get("incubator"); - if(p != null && p.getPulse() == null){ - player.varpManager.get(1160).setVarbit(4,0).send(player); - return true; - } - String name = p == null ? IncubatorEgg.values()[inc].getProduct().getName().toLowerCase() : p.getEgg().getProduct().getName().toLowerCase(); - player.sendMessage("There is " + (StringUtils.isPlusN(name) ? "an" : "a") + " " + name + " incubating in there."); - } - return true; - } - return true; - } - - /** - * Handles the reward of using an egg on the incubator. - * @author Vexia - */ - public class IncubatorEggHandler extends UseWithHandler { - - /** - * Constructs a new {@Code IncubatorEggHandler} {@Code - * Object} - */ - public IncubatorEggHandler() { - super(12483, 11964, 5077, 5076, 5078, 12494, 12477, 12480, 12478, 12479); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - addHandler(28336, OBJECT_TYPE, this); - addHandler(28352, OBJECT_TYPE, this); - return this; - } - - @Override - public boolean handle(NodeUsageEvent event) { - final Player player = event.getPlayer(); - final IncubatorEgg egg = IncubatorEgg.forItem(event.getUsedItem()); - if (egg == null) { - return false; - } - if (player.hasActiveState("incubator")) { - player.sendMessage("You already have an egg in there."); - return true; - } - if (player.getSkills().getStaticLevel(Skills.SUMMONING) < egg.getLevel()) { - player.getDialogueInterpreter().sendDialogue("You need a Summoning level of at least " + egg.getLevel() + " in order to do this."); - return true; - } - if(player.getInventory().remove(egg.getEgg())) { - IncubatorState state = (IncubatorState) player.registerState("incubator"); - state.setEgg(egg); - state.setTicksLeft(egg.getInucbationTime() * 100); - state.init(); - } - return true; - } - - } -} 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 197c6e126..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. @@ -44,6 +45,7 @@ public final class SmithingPlugin extends UseWithHandler { addHandler(6150, OBJECT_TYPE, this); addHandler(22725, OBJECT_TYPE, this); addHandler(26817, OBJECT_TYPE, this); + addHandler(26822, OBJECT_TYPE, this); addHandler(37622, OBJECT_TYPE, this); addHandler(42027, OBJECT_TYPE, this); ClassScanner.definePlugin(new OptionHandler() { @@ -67,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 78f95113f..45b0dad76 100644 --- a/Server/src/main/content/global/handlers/item/withobject/SpiritShieldBlessListener.kt +++ b/Server/src/main/content/global/handlers/item/withobject/SpiritShieldBlessListener.kt @@ -58,6 +58,7 @@ class SpiritShieldBlessListener : InteractionListener { Scenery.ANVIL_6150, Scenery.ANVIL_22725, Scenery.LATHE_26817, + Scenery.ANVIL_26822, Scenery.ANVIL_37622 ) } 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 caf12172e..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,14 +7,18 @@ 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 /** * Handles filling most water sources. * @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, 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) @@ -30,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) { @@ -44,6 +53,13 @@ class WaterSourceListener : InteractionListener { animate(player, animation) sendMessage(player, formatMsgText(used.name, vessel.fillMsg)) addItemOrDrop(player, vessel.output) + if(with.name.contains("well", ignoreCase = true)) { + playAudio(player, Sounds.WELL_FILL_2615) + } + else { + playAudio(player, Sounds.TAP_FILL_2609) + } + } return !vessel.autofill || amountInInventory(player, used.id) == 0 } @@ -51,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/ChromaticDragonBehavior.kt b/Server/src/main/content/global/handlers/npc/ChromaticDragonBehavior.kt new file mode 100644 index 000000000..57c0fda75 --- /dev/null +++ b/Server/src/main/content/global/handlers/npc/ChromaticDragonBehavior.kt @@ -0,0 +1,43 @@ +package content.global.handlers.npc + +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.skill.Skills +import core.game.node.item.Item +import core.tools.RandomFunction +import org.rs09.consts.* +import kotlin.collections.ArrayList + +class ChromaticDragonBehavior : NPCBehavior(*greenDragons, *blueDragons, *redDragons, *blackDragons) +{ + override fun onDropTableRolled (self: NPC, killer: Entity, drops: ArrayList) { + val removeList = ArrayList() + for (item in drops) { + when (item.id) { + Items.BLACK_DRAGON_EGG_12480, + Items.RED_DRAGON_EGG_12477, + Items.BLUE_DRAGON_EGG_12478, + Items.GREEN_DRAGON_EGG_12479 -> removeList.add(item) + } + } + drops.removeAll(removeList) + + if (killer.skills.getStaticLevel(Skills.SUMMONING) >= 99 && RandomFunction.roll(EGG_RATE)) + drops.add(when(self.id) { + in greenDragons -> Item(Items.GREEN_DRAGON_EGG_12479) + in blueDragons -> Item(Items.BLUE_DRAGON_EGG_12478) + in redDragons -> Item(Items.RED_DRAGON_EGG_12477) + in blackDragons -> Item(Items.BLACK_DRAGON_EGG_12480) + else -> Item(Items.DRAGON_BONES_536) + }) + } + + companion object { + val greenDragons = intArrayOf (NPCs.GREEN_DRAGON_941, NPCs.GREEN_DRAGON_4677, NPCs.GREEN_DRAGON_4678, NPCs.GREEN_DRAGON_4679, NPCs.GREEN_DRAGON_4680) + val blueDragons = intArrayOf (NPCs.BLUE_DRAGON_55, NPCs.BLUE_DRAGON_4681, NPCs.BLUE_DRAGON_4682, NPCs.BLUE_DRAGON_4683, NPCs.BLUE_DRAGON_4684) + val redDragons = intArrayOf (NPCs.RED_DRAGON_53, NPCs.RED_DRAGON_4669, NPCs.RED_DRAGON_4670, NPCs.RED_DRAGON_4671, NPCs.RED_DRAGON_4672) + val blackDragons = intArrayOf (NPCs.BLACK_DRAGON_54, NPCs.BLACK_DRAGON_4673, NPCs.BLACK_DRAGON_4674, NPCs.BLACK_DRAGON_4675, NPCs.BLACK_DRAGON_4676) + var EGG_RATE = 1000 + } +} diff --git a/Server/src/main/content/global/handlers/npc/CowMilkingPlugin.java b/Server/src/main/content/global/handlers/npc/CowMilkingPlugin.java index 452c809bd..833fea4cf 100644 --- a/Server/src/main/content/global/handlers/npc/CowMilkingPlugin.java +++ b/Server/src/main/content/global/handlers/npc/CowMilkingPlugin.java @@ -13,6 +13,9 @@ import core.game.world.update.flag.context.Animation; import core.plugin.Plugin; import core.plugin.Initializable; import core.plugin.ClassScanner; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.*; /** * Represents the plugin used to milk a cow. @@ -55,6 +58,7 @@ public final class CowMilkingPlugin extends OptionHandler { } player.animate(ANIMATION); + playAudio(player, Sounds.MILK_COW_372); player.getPulseManager().run(new Pulse(8, player) { @Override public boolean pulse() { @@ -64,6 +68,7 @@ public final class CowMilkingPlugin extends OptionHandler { } if (player.getInventory().contains(1925, 1) || player.getInventory().contains(3727, 1)) { player.animate(ANIMATION); + playAudio(player, Sounds.MILK_COW_372); return false; } return true; diff --git a/Server/src/main/content/global/handlers/npc/DecantListener.kt b/Server/src/main/content/global/handlers/npc/DecantListener.kt index b9c741c4f..9a63c78c2 100644 --- a/Server/src/main/content/global/handlers/npc/DecantListener.kt +++ b/Server/src/main/content/global/handlers/npc/DecantListener.kt @@ -1,46 +1,87 @@ package content.global.handlers.npc import content.data.consumables.Consumables +import core.api.* import core.game.consumable.Potion -import core.game.node.entity.player.Player -import core.game.node.item.Item import core.game.dialogue.DialogueFile -import core.game.interaction.InteractionListener import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.item.Item import core.tools.END_DIALOGUE +import org.rs09.consts.Items class DecantListener : InteractionListener { + companion object { + val potions = Consumables.potions.toIntArray() + } + override fun defineListeners() { on(IntType.NPC,"decant"){ player, node -> - decant(player) + val (toRemove, toAdd) = decantContainer(player.inventory) + for (item in toRemove) + removeItem(player, item) + for (item in toAdd) + addItem(player, item.id, item.amount) player.dialogueInterpreter.open(DecantingDialogue(),node.asNpc()) return@on true } - } + onUseWith(IntType.ITEM, potions, *potions) { player, used, with -> + if (used !is Item) return@onUseWith false + if (with !is Item) return@onUseWith false - fun decant(p: Player) { - val potcounts = HashMap() - val results: List - for (i in 0..27) { - val pot = (Consumables.getConsumableById(p.inventory.getId(i)) ?: continue) as Potion - if (pot != null) { - val dosage = p.inventory[i].name.replace("[^\\d.]".toRegex(), "").toInt() - if (potcounts[pot] != null) { - potcounts.replace(pot, potcounts[pot]!! + dosage) - } else { - potcounts.putIfAbsent(pot, dosage) - } - p.inventory.remove(Item(p.inventory.getId(i))) + // Verify these are both the same potion types + val potionUsed = Consumables.getConsumableById(used.id)?.consumable as? Potion ?: return@onUseWith false + val potionWith = Consumables.getConsumableById(with.id)?.consumable as? Potion ?: return@onUseWith false + + if (potionUsed != potionWith) { + return@onUseWith false } - } - potcounts.keys.forEach{ pot: Potion -> - val full_count = potcounts[pot]!! / pot.ids.size - val partial_dose_amt = potcounts[pot]!! % pot.ids.size - p.inventory.add(Item(pot.ids[0], full_count)) - if (partial_dose_amt > 0) p.inventory - .add(Item(pot.ids[pot.ids.size - partial_dose_amt])) + + // Dosage math + val usedDose = potionUsed.getDose(used) + val withDose = potionWith.getDose(with) + + // Shouldn't be able to combine a 4 dose potion + if (usedDose == 4 || withDose == 4) { + return@onUseWith false + } + + val totalDosage = usedDose + withDose + val fullDoses = totalDosage / 4 + val leftoverDose = totalDosage % 4 + + // Replace the targeted potion item with a (4) dose potion + if (fullDoses != 0) { + replaceSlot(player, with.slot, Item(potionWith.ids.first()), with, Container.INVENTORY) + } + + // Replace the targeted potion item with the updated dosage amount + if (leftoverDose != 0 && fullDoses == 0) { + replaceSlot(player, with.slot, Item(potionUsed.ids[potionUsed.ids.size - totalDosage]), with, Container.INVENTORY) + + // Replace the used with potion item with the updated dosage amount + } else if (leftoverDose != 0) { + replaceSlot(player, used.slot, Item(potionUsed.ids[potionUsed.ids.size - leftoverDose]), used, Container.INVENTORY) + } + + // Replace the used with potion item with an empty vial + if (leftoverDose == 0 || fullDoses == 0) { + replaceSlot(player, used.slot, Item(Items.VIAL_229), used, Container.INVENTORY) + } + + // Send message/Play sound + val amountString = when { + totalDosage >= 4 -> "four" + totalDosage == 3 -> "three" + else -> "two" + } + + sendMessage(player, "You have combined the liquid into $amountString doses.") + playAudio(player, 2401) + + return@onUseWith true } } @@ -52,5 +93,4 @@ class DecantListener : InteractionListener { } } } - } \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/npc/NPCDepositListener.kt b/Server/src/main/content/global/handlers/npc/NPCDepositListener.kt index 33c1f886a..31a928df7 100644 --- a/Server/src/main/content/global/handlers/npc/NPCDepositListener.kt +++ b/Server/src/main/content/global/handlers/npc/NPCDepositListener.kt @@ -1,6 +1,6 @@ package content.global.handlers.npc -import core.api.isEquipped +import core.api.anyInEquipment import core.api.openDepositBox import core.api.sendNPCDialogue import core.api.setInterfaceText @@ -14,9 +14,7 @@ class NPCDepositListener : InteractionListener { override fun defineListeners() { on(NPCs.PEER_THE_SEER_1288, IntType.NPC, "deposit") { player, _ -> - if (isEquipped(player, Items.FREMENNIK_SEA_BOOTS_1_14571) || - isEquipped(player, Items.FREMENNIK_SEA_BOOTS_2_14572) || - isEquipped(player, Items.FREMENNIK_SEA_BOOTS_3_14573)) { + if (anyInEquipment(player, Items.FREMENNIK_SEA_BOOTS_1_14571, Items.FREMENNIK_SEA_BOOTS_2_14572, Items.FREMENNIK_SEA_BOOTS_3_14573)) { openDepositBox(player) setInterfaceText(player, "Peer the Seer's Deposits", 11, 12) } else { diff --git a/Server/src/main/content/global/handlers/npc/NPCTalkListener.kt b/Server/src/main/content/global/handlers/npc/NPCTalkListener.kt index b5e3b3090..5235fc82a 100644 --- a/Server/src/main/content/global/handlers/npc/NPCTalkListener.kt +++ b/Server/src/main/content/global/handlers/npc/NPCTalkListener.kt @@ -5,8 +5,13 @@ import content.region.kandarin.barcrawl.BarcrawlManager import content.region.kandarin.barcrawl.BarcrawlType import content.global.ame.RandomEvents import content.minigame.gnomecooking.* +import core.api.getAttribute import core.game.interaction.InteractionListener import core.game.interaction.IntType +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 /** * Handles the NPC talk-to option. @@ -31,13 +36,25 @@ class NPCTalkListener : InteractionListener { on(IntType.NPC,"talk-to","talk","talk to"){ player, node -> val npc = node.asNpc() if(RandomEvents.randomIDs.contains(node.id)){ - if(content.global.ame.RandomEventManager.getInstance(player)!!.event == null || content.global.ame.RandomEventManager.getInstance(player)!!.event!! != node.asNpc()){ + 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.") } else { - content.global.ame.RandomEventManager.getInstance(player)!!.event!!.talkTo(node.asNpc()) + AntiMacro.getEventNpc(player)?.talkTo(node.asNpc()) } return@on true } + if (HolidayRandomEvents.holidayRandomIDs.contains(node.id) && node is HolidayRandomEventNPC) { + if(HolidayRandoms.getEventNpc(player) == null || HolidayRandoms.getEventNpc(player) != node.asNpc() || HolidayRandoms.getEventNpc(player)?.finalized == true) { + player.sendMessage("They aren't interested in talking to you.") + } else { + HolidayRandoms.getEventNpc(player)?.talkTo(node.asNpc()) + } + return@on true + } + if (getAttribute(npc, "holiday_random_extra_npc", false) && HolidayRandoms.getEventNpc(player) != null) { + HolidayRandoms.getEventNpc(player)?.talkTo(node.asNpc()) + return@on true + } if (!npc.getAttribute("facing_booth", false)) { npc.faceLocation(player.location) } 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 new file mode 100644 index 000000000..b329d4622 --- /dev/null +++ b/Server/src/main/content/global/handlers/npc/SheepBehavior.kt @@ -0,0 +1,139 @@ +package content.global.handlers.npc + +import content.region.misthalin.lumbridge.quest.sheepshearer.SheepShearer.Companion.ATTR_IS_PENGUIN_SHEEP_SHEARED +import core.api.* +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +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.system.task.Pulse +import core.game.world.GameWorld +import core.game.world.update.flag.context.Animation +import core.tools.RandomFunction +import org.rs09.consts.Items +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, + NPCs.SHEEP_43, + NPCs.GOLDEN_SHEEP_1271, + NPCs.GOLDEN_SHEEP_1272, + NPCs.SHEEP_1529, + NPCs.SHEEP_1762, + NPCs.SHEEP_1763, + NPCs.SHEEP_1764, + NPCs.SHEEP_1765, + NPCs.SICK_LOOKING_SHEEP_1_2377, + NPCs.SICK_LOOKING_SHEEP_2_2378, + NPCs.SICK_LOOKING_SHEEP_3_2379, + NPCs.SICK_LOOKING_SHEEP_4_2380, + NPCs.SHEEP_3310, + NPCs.SHEEP_3311, + NPCs.SHEEP_3579, + NPCs.SHEEP_5148, + NPCs.SHEEP_5149, + NPCs.SHEEP_5150, + NPCs.SHEEP_5151, + NPCs.SHEEP_5152, + NPCs.SHEEP_5153, + NPCs.SHEEP_5154, + NPCs.SHEEP_5155, + NPCs.SHEEP_5156, + NPCs.SHEEP_5157, + NPCs.SHEEP_5158, + NPCs.SHEEP_5159, + NPCs.SHEEP_5160, + NPCs.SHEEP_5161, + NPCs.SHEEP_5162, + NPCs.SHEEP_5163, + NPCs.SHEEP_5164, + NPCs.SHEEP_5165, + NPCs.GOLDEN_SHEEP_5172, + NPCs.GOLDEN_SHEEP_5173 +) + +class SheepBehavior : NPCBehavior(*sheepIds), InteractionListener { + override fun tick(self: NPC): Boolean { + if (self.properties.combatPulse.isAttacking || DeathTask.isDead(self)) { + return true + } + if (RandomFunction.random(35) == 5) { + sendChat(self, "Baa!") + } + + return true + } + + override fun defineListeners() { + on(IntType.NPC, "shear") { player, node -> + val sheep = node as NPC + sheep.faceTemporary(player, 1) + if (sheep.id == NPCs.SHEEP_3579) { + if (player.questRepository.getQuest(Quests.SHEEP_SHEARER).isStarted(player)) { + setAttribute(player, ATTR_IS_PENGUIN_SHEEP_SHEARED, true) + } + animate(player, Animation(893)) + playAudio(player, Sounds.PENGUINSHEEP_ESCAPE_686) + animate(sheep, Animation(3570)) + + sheepBackAway(player, sheep, "The... whatever it is... manages to get away from you!") + return@on true + } + if (!inInventory(player, Items.SHEARS_1735)) { + sendMessage(player, "You need shears to shear a sheep.") + return@on true + } + if (hasOption(sheep, "attack")) { + sendMessage(player, "That one looks a little too violent to shear...") + return@on true + } + if (freeSlots(player) == 0) { + sendMessage(player, "You don't have enough space in your inventory to carry any wool you would shear.") + return@on true + } + lock(sheep, 3) + stopWalk(sheep) + animate(player, Animation(893)) + val random = RandomFunction.random(1, 5) + if (random != 4) { + sheep.locks.lockMovement(2) + sheep.transform(NPCs.SHEEP_5153) + playAudio(player, Sounds.SHEAR_SHEEP_761) + 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() + return true + } + }) + } else { + sheepBackAway(player, sheep, "The sheep manages to get away from you!") + } + return@on true + } + } + + fun sheepBackAway(player: Player, sheep: NPC, messagePlayer: String) + { + val playerLocation = player.getLocation() + val sheepLocation = sheep.getLocation() + val sheepDirection = Direction.getDirection(sheepLocation, playerLocation) // Get direction sheep is facing, from the player's location + val sheepOppositeDirection = sheepDirection.getOpposite() // Switch to opposite direction + + val xWalkLocation = sheepLocation.getX() + (sheepOppositeDirection.getStepX() * 3) // Gets x location, if set, 3 steps away from player + val yWalkLocation = sheepLocation.getY() + (sheepOppositeDirection.getStepY() * 3) // Gets y location, if set, 3 steps away from player + val sheepWalkToLocation = Location(xWalkLocation, yWalkLocation, sheepLocation.getZ()); // New location for pathfinding + + sendMessage(player, messagePlayer) + + unlock(sheep) //Force unlock of entity movement, to allow moving away + forceWalk(sheep, sheepWalkToLocation, "dumb") + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/npc/SheepNPC.java b/Server/src/main/content/global/handlers/npc/SheepNPC.java deleted file mode 100644 index ef9003540..000000000 --- a/Server/src/main/content/global/handlers/npc/SheepNPC.java +++ /dev/null @@ -1,59 +0,0 @@ -package content.global.handlers.npc; - -import core.game.node.entity.combat.DeathTask; -import core.game.node.entity.npc.AbstractNPC; -import core.game.world.map.Location; -import core.plugin.Initializable; -import core.tools.RandomFunction; - -/** - * Handles the sheep npc's. - * @author 'Vexia - */ -@Initializable -public class SheepNPC extends AbstractNPC { - - /** - * The NPC ids of NPCs using this plugin. - */ - private static final int[] ID = { 42, 43, 1271, 1272, 1529, 1762, 1763, 1764, 1765, 2377, 2378, 2379, 2380, 3310, 3311, 3579, 5148, 5149, 5150, 5151, 5152, 5153, 5154, 5155, 5156, 5157, 5158, 5159, 5160, 5161, 5162, 5163, 5164, 5165, 5172, 5173 }; - - /** - * Constructs a new {@code AlKharidWarriorPlugin} {@code Object}. - */ - public SheepNPC() { - super(0, null); - } - - /** - * Constructs a new {@code AlKharidWarriorPlugin} {@code Object}. - * @param id The NPC id. - * @param location The location. - */ - private SheepNPC(int id, Location location) { - super(id, location); - } - - @Override - public AbstractNPC construct(int id, Location location, Object... objects) { - return new SheepNPC(id, location); - } - - @Override - public void tick() { - if (getProperties().getCombatPulse().isAttacking() || DeathTask.isDead(this)) { - super.tick(); - return; - } - if (RandomFunction.random(35) == 5) { - sendChat("Baa!"); - } - super.tick(); - } - - @Override - public int[] getIds() { - return ID; - } - -} diff --git a/Server/src/main/content/global/handlers/npc/ShopNPC.java b/Server/src/main/content/global/handlers/npc/ShopNPC.java index 187e4f017..2e352f58d 100644 --- a/Server/src/main/content/global/handlers/npc/ShopNPC.java +++ b/Server/src/main/content/global/handlers/npc/ShopNPC.java @@ -14,7 +14,7 @@ public class ShopNPC extends AbstractNPC { /** * The NPC ids of NPCs using this plugin. */ - private static final int[] ID = { 410, 528, 529, 525, 524, 1436, 590, 591, 971, 1917, 597, 1083, 3541, 7396, 1040, 563, 5798, 582, 526, 527, 873, 532, 533, 568, 2154, 1334, 2352, 4516, 520, 521, 4716, 3922, 1254, 2086, 1866, 530, 531, 836, 516, 560, 471, 522, 523, 4651, 4652, 4653, 4654, 4655, 4656, 4650, 534, 588, 2356, 1860, 550, 575, 683, 682, 4563, 4558, 4559, 519, 559, 562, 581, 554, 601, 1301, 1039, 2353, 3166, 2161, 2162, 600, 593, 545, 585, 5268, 2305, 2307, 2304, 1783, 557, 1038, 1433, 7053, 571, 5487, 536, 4294, 4293, 584, 570, 540, 2157, 69669, 569, 572, 573, 1303, 595, 297, 704, 587, 5110, 5109, 556, 1865, 543, 2198, 580, 1862, 583, 553, 461, 4513, 3097, 903, 1435, 606, 2623, 594, 579, 2160, 6750, 6898, 6893, 589, 5485, 542, 549, 3038, 544, 1434, 209, 1980, 546, 6988, 3671, 558, 576, 5266, 586, 602, 552, 551, 541, 692, 797, 564, 2152, 1208, 2233, 3205, 70, 1598, 1596, 1597, 1599, 2259, 3162, 1167, 2620, 2151, 5486, 4250, 1079, 970, 793, 2572 }; + private static final int[] ID = { 410, 528, 529, 525, 524, 1436, 590, 591, 971, 1917, 597, 1083, 3541, 7396, 1040, 563, 5798, 582, 526, 527, 873, 532, 533, 568, 2154, 1334, 2352, 4516, 520, 521, 4716, 3922, 1254, 2086, 1866, 530, 531, 836, 516, 560, 471, 522, 523, 4651, 4652, 4653, 4654, 4655, 4656, 4650, 534, 588, 2356, 1860, 550, 575, 683, 682, 4563, 4558, 4559, 519, 559, 562, 581, 554, 601, 1301, 1039, 2353, 3166, 2161, 2162, 600, 593, 545, 585, 5268, 2305, 2307, 2304, 1783, 557, 1038, 1433, 7053, 571, 5487, 536, 4294, 4293, 584, 570, 540, 2157, 69669, 569, 572, 573, 1303, 595, 297, 704, 587, 5110, 5109, 556, 1865, 543, 2198, 580, 1862, 583, 553, 461, 4513, 3097, 903, 1435, 606, 2623, 594, 579, 2160, 6750, 6898, 6893, 589, 5485, 542, 549, 3038, 544, 1434, 209, 1980, 546, 6988, 558, 576, 5266, 586, 602, 552, 551, 541, 692, 797, 564, 2152, 1208, 2233, 3205, 70, 1598, 1596, 1597, 1599, 2259, 3162, 1167, 2620, 2151, 5486, 4250, 1079, 793, 2572 }; /** * Constructs a new {@code ShopNPC} {@code Object}. diff --git a/Server/src/main/content/global/handlers/player/LoginValidationPlugin.java b/Server/src/main/content/global/handlers/player/LoginValidationPlugin.java index 5a417c302..642d33f07 100644 --- a/Server/src/main/content/global/handlers/player/LoginValidationPlugin.java +++ b/Server/src/main/content/global/handlers/player/LoginValidationPlugin.java @@ -1,17 +1,21 @@ package content.global.handlers.player; +import core.api.Container; import core.game.activity.ActivityManager; import core.game.node.entity.player.Player; import core.game.node.item.Item; import core.game.system.SystemManager; +import core.game.world.GameWorld; import core.plugin.Initializable; import core.plugin.Plugin; import core.plugin.PluginManifest; import core.plugin.PluginType; -import core.game.world.GameWorld; +import org.rs09.consts.Items; import java.util.concurrent.TimeUnit; +import static core.api.ContentAPIKt.*; + /** * Validates a player login. * @author Emperor @@ -25,7 +29,7 @@ public final class LoginValidationPlugin implements Plugin { /** * Represents the quest point items to remove. */ - private static final Item[] QUEST_ITEMS = new Item[] { new Item(9813), new Item(9814)}; + private static final Item[] QUEST_ITEMS = new Item[] { new Item(Items.QUEST_POINT_CAPE_9813), new Item(Items.QUEST_POINT_HOOD_9814)}; /** * Constructs a new {@Code LoginValidationPlugin} {@Code Object} @@ -60,7 +64,7 @@ public final class LoginValidationPlugin implements Plugin { ActivityManager.start(player, "falconry", true); } if (player.getSavedData().getQuestData().getDragonSlayerAttribute("repaired")) { - player.getConfigManager().set(177, 1967876); + setVarp(player, 177, 1967876); //lol? } if (player.getSavedData().getGlobalData().getLootShareDelay() < System.currentTimeMillis() && player.getSavedData().getGlobalData().getLootShareDelay() != 0L) { player.getGlobalData().setLootSharePoints((int) (player.getGlobalData().getLootSharePoints() - player.getGlobalData().getLootSharePoints() * 0.10)); @@ -76,19 +80,43 @@ public final class LoginValidationPlugin implements Plugin { * @param player the player. */ private static void checkQuestPointsItems(final Player player) { - if (!player.getQuestRepository().hasCompletedAll() && - (player.getEquipment().contains(9813, 1) || player.getEquipment().contains(9814, 1)) - ) { + if (!player.getQuestRepository().hasCompletedAll() && anyInEquipment(player, Items.QUEST_POINT_CAPE_9813, Items.QUEST_POINT_HOOD_9814)) { + String location1 = null; + String location2 = null; + int item1 = 0; + int item2 = 0; + int amt = 0; for (Item i : QUEST_ITEMS) { - if (player.getEquipment().remove(i)) { - player.getDialogueInterpreter().sendItemMessage(i, "As you no longer have completed all the quests, your " + i.getName() + " unequips itself to your " + (player.getInventory().freeSlots() < 1 ? "bank" : "inventory") + "!"); - if (player.getInventory().freeSlots() < 1) { - player.getBank().add(i); + if (removeItem(player, i, Container.EQUIPMENT)) { + amt++; + String location; + if (addItem(player, i.getId(), i.getAmount(), Container.INVENTORY)) { + location = "your inventory"; + } else if (addItem(player, i.getId(), i.getAmount(), Container.BANK)) { + location = "your bank"; } else { - player.getInventory().add(i); + location = "the Wise Old Man"; + if (i.getId() == Items.QUEST_POINT_CAPE_9813) { + setAttribute(player, "/save:reclaim-qp-cape", true); + } else { + setAttribute(player, "/save:reclaim-qp-hood", true); + } + } + if (amt == 1) { + item1 = i.getId(); + location1 = location; + } + if (amt == 2) { + item2 = i.getId(); + location2 = location; } } } + if (amt == 2) { + sendDoubleItemDialogue(player, item1, item2, "As you no longer have completed all the quests, your " + getItemName(item1) + " unequips itself to " + location1 + " and your " + getItemName(item2) + " unequips itself to " + location2 + "!"); + } else { + sendItemDialogue(player, item1, "As you no longer have completed all the quests, your " + getItemName(item1) + " unequips itself to " + location1 + "!"); + } } } -} +} \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/player/RequestOption.kt b/Server/src/main/content/global/handlers/player/RequestOption.kt index aecf7515b..33707b358 100644 --- a/Server/src/main/content/global/handlers/player/RequestOption.kt +++ b/Server/src/main/content/global/handlers/player/RequestOption.kt @@ -7,6 +7,7 @@ import core.game.node.entity.player.link.request.RequestType import core.plugin.Initializable import core.game.interaction.IntType import core.game.interaction.InteractionListener +import core.game.bots.* /** * Represents the plugin used to handle the player option interacting. @@ -22,8 +23,10 @@ class RequestOption : InteractionListener { return@on true } on(_P_ASSIST.name, IntType.PLAYER) { player, node -> + if (node is AIPlayer) + AIRepository.sendBotInfo(player, node) player.requestManager.request((node as Player), RequestType.ASSIST) return@on true } } -} \ No newline at end of file +} diff --git a/Server/src/main/content/global/handlers/scenery/BankBoothListener.kt b/Server/src/main/content/global/handlers/scenery/BankBoothListener.kt index e624fa7ad..b725c8cac 100644 --- a/Server/src/main/content/global/handlers/scenery/BankBoothListener.kt +++ b/Server/src/main/content/global/handlers/scenery/BankBoothListener.kt @@ -38,6 +38,36 @@ class BankBoothListener : InteractionListener { Scenery.BANK_BOOTH_29085, Scenery.BANK_BOOTH_30015, Scenery.BANK_BOOTH_30016, Scenery.BANK_BOOTH_34205, Scenery.BANK_BOOTH_34752, Scenery.BANK_BOOTH_35647, Scenery.BANK_BOOTH_36786, Scenery.BANK_BOOTH_37474 ) + + public fun convertToNotes (used: Node, player: Player) { + val item = used as Item + + if (item.noteChange != item.id) { + if (item.definition.isUnnoted) { + val amount = amountInInventory(player, item.id) + if (removeItem(player, Item(item.id, amount))) { + addItem(player, item.noteChange, amount) + } + } else { + var amount = item.amount + val freeSlotCount = freeSlots(player) + + // If there is exactly one more note than free slots + // the note disappearing can be used as the last slot + if (amount > freeSlotCount && amount != freeSlotCount + 1) { + amount = freeSlotCount + } + + if (removeItem(player, Item(item.id, amount))) { + addItem(player, item.noteChange, amount) + } + } + + return + } + + sendMessage(player, "This item can't be noted.") + } } /** @@ -93,7 +123,7 @@ class BankBoothListener : InteractionListener { } } - private fun quickBankBoothUse(player: Player, node: Node): Boolean { + private fun quickBankBoothUse(player: Player, node: Node, state: Int): Boolean { if (player.ironmanManager.checkRestriction(IronmanMode.ULTIMATE)) { return true } @@ -107,20 +137,20 @@ class BankBoothListener : InteractionListener { return true } - private fun regularBankBoothUse(player: Player, node: Node): Boolean { + private fun regularBankBoothUse(player: Player, node: Node, state: Int): Boolean { if (player.ironmanManager.checkRestriction(IronmanMode.ULTIMATE)) { return true } if (ServerConstants.BANK_BOOTH_QUICK_OPEN) { - return quickBankBoothUse(player, node) + return quickBankBoothUse(player, node, state) } tryInvokeBankerDialogue(player, node) return true } - private fun collectBankBoothUse(player: Player, node: Node): Boolean { + private fun collectBankBoothUse(player: Player, node: Node, state: Int): Boolean { if (BankerNPC.checkLunarIsleRestriction(player, node)) { tryInvokeBankerDialogue(player, node) return true @@ -146,42 +176,18 @@ class BankBoothListener : InteractionListener { tryInvokeBankerDialogue(player, with) return true } - - val item = used as Item - - if (item.noteChange != item.id) { - if (item.definition.isUnnoted) { - val amount = amountInInventory(player, item.id) - if (removeItem(player, Item(item.id, amount))) { - addItem(player, item.noteChange, amount) - } - } else { - var amount = item.amount - val freeSlotCount = freeSlots(player) - - if (amount > freeSlotCount) { - amount = freeSlotCount - } - - if (removeItem(player, Item(item.id, amount))) { - addItem(player, item.noteChange, amount) - } - } - - return true - } - - sendMessage(player, "This item can't be noted.") + + convertToNotes (used, player) return true } override fun defineListeners() { - on(BANK_BOOTHS, IntType.SCENERY, "use-quickly", "bank", handler = ::quickBankBoothUse) - on(BANK_BOOTHS, IntType.SCENERY, "use", handler = ::regularBankBoothUse) - on(BANK_BOOTHS, IntType.SCENERY, "collect", handler = ::collectBankBoothUse) + defineInteraction(IntType.SCENERY, BANK_BOOTHS, "use-quickly", "bank", handler = ::quickBankBoothUse) + defineInteraction(IntType.SCENERY, BANK_BOOTHS, "use", handler = ::regularBankBoothUse) + defineInteraction(IntType.SCENERY, BANK_BOOTHS, "collect", handler = ::collectBankBoothUse) if (ServerConstants.BANK_BOOTH_NOTE_ENABLED) { onUseAnyWith(IntType.SCENERY, *BANK_BOOTHS, handler = ::attemptToConvertItems) } } -} \ No newline at end of file +} diff --git a/Server/src/main/content/global/handlers/scenery/BankChestListener.kt b/Server/src/main/content/global/handlers/scenery/BankChestListener.kt index e6e093d34..9fb26831b 100644 --- a/Server/src/main/content/global/handlers/scenery/BankChestListener.kt +++ b/Server/src/main/content/global/handlers/scenery/BankChestListener.kt @@ -20,9 +20,9 @@ private val BANK_CHESTS = intArrayOf( */ class BankChestListener : InteractionListener { override fun defineListeners() { - on(BANK_CHESTS, IntType.SCENERY, "bank", "use") { player, node -> + defineInteraction(IntType.SCENERY, BANK_CHESTS, "bank", "use") {player, node, state -> openBankAccount(player) - return@on true + return@defineInteraction true } } } \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/scenery/BankDepositBoxListener.kt b/Server/src/main/content/global/handlers/scenery/BankDepositBoxListener.kt index 5455c60c1..68579bfb6 100644 --- a/Server/src/main/content/global/handlers/scenery/BankDepositBoxListener.kt +++ b/Server/src/main/content/global/handlers/scenery/BankDepositBoxListener.kt @@ -29,7 +29,7 @@ private val BANK_DEPOSIT_BOXES = intArrayOf( */ class BankDepositBoxListener : InteractionListener { - private fun openDepositBox(player: Player, node: Node) : Boolean { + private fun openDepositBox(player: Player, node: Node, state: Int) : Boolean { restrictForIronman(player, IronmanMode.ULTIMATE) { player.interfaceManager.open(Component(Components.BANK_DEPOSIT_BOX_11)).closeEvent = CloseEvent { p, _ -> p.interfaceManager.openDefaultTabs() @@ -55,6 +55,6 @@ class BankDepositBoxListener : InteractionListener { } override fun defineListeners() { - on(BANK_DEPOSIT_BOXES, IntType.SCENERY, "deposit", handler = ::openDepositBox) + defineInteraction(IntType.SCENERY, BANK_DEPOSIT_BOXES, "deposit", handler = ::openDepositBox) } } \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/scenery/DoogleLeafInteraction.kt b/Server/src/main/content/global/handlers/scenery/DoogleLeafInteraction.kt new file mode 100644 index 000000000..fdf7d9891 --- /dev/null +++ b/Server/src/main/content/global/handlers/scenery/DoogleLeafInteraction.kt @@ -0,0 +1,24 @@ +package content.global.handlers.scenery + +import core.api.addItem +import core.api.sendMessage +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.Node +import core.game.node.entity.player.Player +import org.rs09.consts.Items + +class DoogleLeafInteraction : InteractionListener { + override fun defineListeners() { + defineInteraction(IntType.SCENERY, intArrayOf(31155), "pick-leaf", handler = ::handleDoogle) + } + + fun handleDoogle(player: Player, node: Node, state: Int) : Boolean { + if (!addItem(player, Items.DOOGLE_LEAVES_1573)) { + sendMessage(player, "You don't have enough space in your inventory.") + return true + } + sendMessage(player, "You pick some doogle leaves.") + return true + } +} \ No newline at end of file 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 5006404c0..000000000 --- a/Server/src/main/content/global/handlers/scenery/DoogleLeafPlugin.java +++ /dev/null @@ -1,40 +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 - */ -@Initializable -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 4fd17c260..1d0fa5c07 100644 --- a/Server/src/main/content/global/handlers/scenery/FieldPickingPlugin.java +++ b/Server/src/main/content/global/handlers/scenery/FieldPickingPlugin.java @@ -2,6 +2,7 @@ package content.global.handlers.scenery; import core.cache.def.impl.SceneryDefinition; import core.game.container.impl.EquipmentContainer; +import core.game.event.ResourceProducedEvent; import core.game.interaction.OptionHandler; import core.game.node.Node; import core.game.node.entity.combat.ImpactHandler.HitsplatType; @@ -16,6 +17,9 @@ import core.game.world.update.flag.context.Animation; import core.plugin.Plugin; import core.plugin.Initializable; import core.tools.RandomFunction; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.playAudio; /** * Handles the pick option of interactive scenery (non-farming cabbages, @@ -64,9 +68,11 @@ public final class FieldPickingPlugin extends OptionHandler { player.lock(1); player.setAttribute("delay:picking", GameWorld.getTicks() + (plant == PickingPlant.FLAX ? 2 : 3)); player.animate(ANIMATION); + playAudio(player, Sounds.PICK_2581, 30); + 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) { @@ -104,12 +110,6 @@ public final class FieldPickingPlugin extends OptionHandler { } else { player.getPacketDispatch().sendMessage("You pick a handful of nettles."); } - if (banana && !player.getAchievementDiaryManager().hasCompletedTask(DiaryType.KARAMJA, 0, 0)) { - int picked = player.getAttribute("b-picked", 0); - picked++; - player.setAttribute("b-picked", picked); - player.getAchievementDiaryManager().updateTask(player, DiaryType.KARAMJA, 0, 0, picked == 5); - } return true; } }); @@ -124,20 +124,9 @@ public final class FieldPickingPlugin extends OptionHandler { */ private void handleFlaxPick(final Player player, final Scenery object, final PickingPlant plant) { int charge = object.getCharge(); - player.getAudioManager().send(2581); + playAudio(player, Sounds.PICK_2581); player.getPacketDispatch().sendMessage("You pick some flax."); - // Seers achievement diary - if (player.getViewport().getRegion().getId() == 10805 - && !player.getAchievementDiaryManager().getDiary(DiaryType.SEERS_VILLAGE).isComplete(0,0)) { - if (player.getAttribute("diary:seers:flax-picked", 0) >= 4) { - player.setAttribute("/save:diary:seers:flax-picked", 5); - player.getAchievementDiaryManager().getDiary(DiaryType.SEERS_VILLAGE).updateTask(player,0,0,true); - } else { - player.setAttribute("/save:diary:seers:flax-picked", player.getAttribute("diary:seers:flax-picked", 0) + 1); - } - } - if (charge > 1000 + RandomFunction.random(2, 8)) { object.setActive(false); object.setCharge(1000); 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 077cc40ea..c61a8c82a 100644 --- a/Server/src/main/content/global/handlers/scenery/MillingListener.kt +++ b/Server/src/main/content/global/handlers/scenery/MillingListener.kt @@ -1,13 +1,14 @@ package content.global.handlers.scenery 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.audio.Audio import core.game.node.item.Item import core.game.world.update.flag.context.Animation import org.rs09.consts.Items import org.rs09.consts.Scenery +import org.rs09.consts.Sounds private const val GRAIN = Items.GRAIN_1947 private const val SWEETCORN = Items.SWEETCORN_5986 @@ -19,7 +20,7 @@ private val HOPPER_CONTROLS = intArrayOf(Scenery.HOPPER_CONTROLS_2718, Scenery.H private val FLOUR_BINS = intArrayOf(Scenery.FLOUR_BIN_1782, Scenery.FLOUR_BIN_5792, Scenery.FLOUR_BIN_22420, Scenery.FLOUR_BIN_22421, Scenery.FLOUR_BIN_24070, Scenery.FLOUR_BIN_36878) private const val VARP = 695 private val ANIMATION = Animation(3571) -private val SOUND = Audio(3189) +private val SOUND = Sounds.GIANT_ROC_APPROACHES_3189 /** * Handles interactions with windmills @@ -27,13 +28,13 @@ private val SOUND = Audio(3189) */ class MillingListener : InteractionListener { override fun defineListeners() { - on(HOPPER_CONTROLS, SCENERY, "operate", "pull") { player, _ -> + defineInteraction(IntType.SCENERY, HOPPER_CONTROLS, "operate", "pull") { player, _, _ -> useHopperControl(player) - return@on true + return@defineInteraction true } - on(FLOUR_BINS, SCENERY, "empty") { player, _ -> + defineInteraction(IntType.SCENERY, FLOUR_BINS, "empty") {player, _, _ -> fillPot(player) - return@on true + return@defineInteraction true } onUseWith(SCENERY, intArrayOf(GRAIN, SWEETCORN), *HOPPERS) { player, used, _ -> fillHopper(player, used.asItem()) @@ -64,7 +65,7 @@ class MillingListener : InteractionListener { } } playAudio(player, SOUND) - setVarbit(player, VARP, 0, 1, true) + setVarp(player, VARP, 1, true) } private fun fillHopper(player: Player, used: Item) { @@ -93,8 +94,8 @@ class MillingListener : InteractionListener { 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) { - setVarbit(player, VARP, 0, 0, true) + setVarp(player, VARP, 0, true) } } } -} \ No newline at end of file +} diff --git a/Server/src/main/content/global/handlers/scenery/ReadSignPostPlugin.java b/Server/src/main/content/global/handlers/scenery/ReadSignPostPlugin.java deleted file mode 100644 index 7239c140b..000000000 --- a/Server/src/main/content/global/handlers/scenery/ReadSignPostPlugin.java +++ /dev/null @@ -1,120 +0,0 @@ -package content.global.handlers.scenery; - -import core.cache.def.impl.SceneryDefinition; -import core.game.component.CloseEvent; -import core.game.component.Component; -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.net.packet.PacketRepository; -import core.net.packet.context.MinimapStateContext; -import core.net.packet.out.MinimapState; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Handles the reading of a sign post. - * @author 'Vexia - */ -@Initializable -public class ReadSignPostPlugin extends OptionHandler { - - /** - * Represents the areas on the sign. - * @author 'Vexia - */ - public enum Signs { - NEAR_LUMBRIDGE(18493, "North to farms and
Varrock.", "The River Lum lies to
the south.", "West to
Lumbridge.", "East to Al
Kharid - toll
gate; bring some
money."), NEAR_VARROCK(24263, "Sheep lay this way.", "South through farms
to Al Kharid and
Lumbridge", "West to Champion's Guild
and Varrock south
gate.", "East to Al Kharid mine and
follow the path north to
Varrock east gate."); - - public static Signs forId(int id) { - for (Signs sign : Signs.values()) { - if (sign == null) { - continue; - } - if (sign.object == id) { - return sign; - } - } - return null; - } - - /** - * The object id. - */ - private int object; - - /** - * The directions. - */ - private String directions[]; - - /** - * Constructs a new {@code ReadSignPostPlugin.java} {@code Object}. - * @param object the object. - * @param directions the directions. - */ - Signs(int object, String... directions) { - this.object = object; - this.directions = directions; - } - } - - @Override - public boolean handle(Player player, Node node, String option) { - PacketRepository.send(MinimapState.class, new MinimapStateContext(player, 2)); - player.getInterfaceManager().open(new Component(135)).setCloseEvent(new CloseEvent() { - @Override - public boolean close(Player player, Component c) { - PacketRepository.send(MinimapState.class, new MinimapStateContext(player, 0)); - return true; - } - }); - final Scenery object = (Scenery) node; - Signs sign = Signs.forId(object.getId()); - if (sign == null) { - return false; - } - String[] dirs = sign.directions; - if (object.getLocation().getX() == 3107 && object.getLocation().getY() == 3296) { - dirs[0] = "North to Draynor
Manor"; - dirs[1] = "South to Draynor
Village"; - dirs[2] = "West to Port
Sarim"; - dirs[3] = "East to
Lumbridge"; - } - player.getPacketDispatch().sendString(dirs[0], 135, 3); // North - player.getPacketDispatch().sendString(dirs[1], 135, 9); // South - player.getPacketDispatch().sendString(dirs[2], 135, 12); // West - player.getPacketDispatch().sendString(dirs[3], 135, 8); // East - return true; - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - SceneryDefinition.forId(2366).getHandlers().put("option:read", this); - SceneryDefinition.forId(2367).getHandlers().put("option:read", this); - SceneryDefinition.forId(2368).getHandlers().put("option:read", this); - SceneryDefinition.forId(2369).getHandlers().put("option:read", this); - SceneryDefinition.forId(2370).getHandlers().put("option:read", this); - SceneryDefinition.forId(2371).getHandlers().put("option:read", this); - SceneryDefinition.forId(4132).getHandlers().put("option:read", this); - SceneryDefinition.forId(4133).getHandlers().put("option:read", this); - SceneryDefinition.forId(4134).getHandlers().put("option:read", this); - SceneryDefinition.forId(4135).getHandlers().put("option:read", this); - SceneryDefinition.forId(5164).getHandlers().put("option:read", this); - SceneryDefinition.forId(10090).getHandlers().put("option:read", this); - SceneryDefinition.forId(13873).getHandlers().put("option:read", this); - SceneryDefinition.forId(15522).getHandlers().put("option:read", this); - SceneryDefinition.forId(18493).getHandlers().put("option:read", this); - SceneryDefinition.forId(24263).getHandlers().put("option:read", this); - SceneryDefinition.forId(25397).getHandlers().put("option:read", this); - SceneryDefinition.forId(30039).getHandlers().put("option:read", this); - SceneryDefinition.forId(30040).getHandlers().put("option:read", this); - SceneryDefinition.forId(31296).getHandlers().put("option:read", this); - SceneryDefinition.forId(31298).getHandlers().put("option:read", this); - SceneryDefinition.forId(31299).getHandlers().put("option:read", this); - SceneryDefinition.forId(31300).getHandlers().put("option:read", this); -// ObjectDefinition.forId(31301).getConfigurations().put("option:read", this);//goblin village - return this; - } -} diff --git a/Server/src/main/content/global/handlers/scenery/SearchOptionPlugin.java b/Server/src/main/content/global/handlers/scenery/SearchOptionPlugin.java index 3b2144efb..f637107f1 100644 --- a/Server/src/main/content/global/handlers/scenery/SearchOptionPlugin.java +++ b/Server/src/main/content/global/handlers/scenery/SearchOptionPlugin.java @@ -7,6 +7,9 @@ import core.game.node.entity.player.Player; import core.game.node.item.Item; import core.plugin.Initializable; import core.plugin.Plugin; +import content.data.Quests; + +import static core.api.ContentAPIKt.*; /** * Handles the search option. @@ -80,6 +83,10 @@ public class SearchOptionPlugin extends OptionHandler { player.getInventory().add(new Item(946)); return true; } + if (node.getId() == 12799 && hasRequirement(player, Quests.IN_AID_OF_THE_MYREQUE)) { + openBankAccount(player); + return true; + } player.getPacketDispatch().sendMessage("You search the " + node.getName().toLowerCase() + " but find nothing."); return true; } diff --git a/Server/src/main/content/global/handlers/scenery/SignpostListener.kt b/Server/src/main/content/global/handlers/scenery/SignpostListener.kt new file mode 100644 index 000000000..583aa259d --- /dev/null +++ b/Server/src/main/content/global/handlers/scenery/SignpostListener.kt @@ -0,0 +1,236 @@ +package content.global.handlers.scenery + +import core.api.* +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.world.map.Location +import org.rs09.consts.Components +import org.rs09.consts.Scenery + +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 + setInterfaceText(player, "West to the Crafting Guild.", 135, 12) // West + openInterface(player, Components.AIDE_COMPASS_135) + } else if (node.asScenery().location.equals(Location(3107, 3296))) { + setInterfaceText(player, "North to Draynor Manor.", 135, 3) // North + setInterfaceText(player, "South to Draynor Village and the Wizards' Tower.", 135, 9) // South + setInterfaceText(player, "East to Lumbridge.", 135, 8) // East + setInterfaceText(player, "West to Port Sarim.", 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_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 + setInterfaceText(player, "West to Champion's Guild and Varrock south gate.", 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_4132, IntType.SCENERY, "read") { player, node -> + 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 + setInterfaceText(player, "West to Port Sarim and Draynor Village.", 135, 12) // West + openInterface(player, Components.AIDE_COMPASS_135) + } else if (node.asScenery().location.equals(Location(3285, 3430))) { + setInterfaceText(player, "North to the Lumber Yard.", 135, 3) // North + setInterfaceText(player, "South to Al Kharid and Lumbridge.", 135, 9) // South + setInterfaceText(player, "East to the Dig Site.", 135, 8) // East + setInterfaceText(player, "West to Varrock.", 135, 12) // West + openInterface(player, Components.AIDE_COMPASS_135) + } else if (node.asScenery().location.equals(Location(2734, 3485))) { + setInterfaceText(player, "North to Sinclair Mansion.", 135, 3) // North + setInterfaceText(player, "South to the Courthouse.", 135, 9) // South + setInterfaceText(player, "East to Camelot Castle and Catherby.", 135, 8) // East + setInterfaceText(player, "Follow the path west to Ardougne.", 135, 12) // West + openInterface(player, Components.AIDE_COMPASS_135) + } else if (node.asScenery().location.equals(Location(2604, 3240))) { + setInterfaceText(player, "North to the Ardougne City Zoo.", 135, 3) // North + setInterfaceText(player, "South to the Monastery.", 135, 9) // South + setInterfaceText(player, "East to the Tower of Life.", 135, 8) // East + setInterfaceText(player, "West to the Clocktower.", 135, 12) // West + openInterface(player, Components.AIDE_COMPASS_135) + } else if (node.asScenery().location.equals(Location(2605, 3298))) { + setInterfaceText(player, "North to the Fishing Guild and Hemenster.", 135, 3) // North + setInterfaceText(player, "South to the Ardougne City Zoo.", 135, 9) // South + setInterfaceText(player, "East to Ardougne Market.", 135, 8) // East + setInterfaceText(player, "West to Ardougne Castle and West Ardougne.", 135, 12) // West + openInterface(player, Components.AIDE_COMPASS_135) + } else if (node.asScenery().location.equals(Location(2646, 3404))) { + setInterfaceText(player, "North to the Ranging Guild and Seer's Village.", 135, 3) // North + setInterfaceText(player, "South to the Ardougne City.", 135, 9) // South + setInterfaceText(player, "East to the Sorcerer's Tower.", 135, 8) // East + setInterfaceText(player, "West to the Fishing Guild.", 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_4134, IntType.SCENERY, "read") { player, node -> + if (node.asScenery().location.equals(Location(2651, 3606))) { + setInterfaceText(player, "North to Rellekka.", 135, 3) // North + setInterfaceText(player, "South to Seers' Village.", 135, 9) // South + setInterfaceText(player, "East to Death Plateau.", 135, 8) // East + setInterfaceText(player, "West to the Lighthouse.", 135, 12) // West + openInterface(player, Components.AIDE_COMPASS_135) + } else if (node.asScenery().location.equals(Location(3100, 3418))) { + setInterfaceText(player, "North to Edgeville.", 135, 3) // North + setInterfaceText(player, "South to Draynor Manor.", 135, 9) // South + setInterfaceText(player, "East to Varrock west gate.", 135, 8) // East + setInterfaceText(player, "West to Barbarian Village.", 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_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. + * + * @Override + * public Plugin newInstance(Object arg) throws Throwable { + * SceneryDefinition.forId(4132).getHandlers().put("option:read", this); + * SceneryDefinition.forId(4133).getHandlers().put("option:read", this); + * SceneryDefinition.forId(4134).getHandlers().put("option:read", this); + * SceneryDefinition.forId(4135).getHandlers().put("option:read", this); + * SceneryDefinition.forId(5164).getHandlers().put("option:read", this); + * SceneryDefinition.forId(10090).getHandlers().put("option:read", this); + * SceneryDefinition.forId(13873).getHandlers().put("option:read", this); + * SceneryDefinition.forId(15522).getHandlers().put("option:read", this); + * SceneryDefinition.forId(25397).getHandlers().put("option:read", this); + * SceneryDefinition.forId(30039).getHandlers().put("option:read", this); + * SceneryDefinition.forId(30040).getHandlers().put("option:read", this); + * SceneryDefinition.forId(31296).getHandlers().put("option:read", this); + * SceneryDefinition.forId(31298).getHandlers().put("option:read", this); + * SceneryDefinition.forId(31299).getHandlers().put("option:read", this); + * SceneryDefinition.forId(31300).getHandlers().put("option:read", this); + * // ObjectDefinition.forId(31301).getConfigurations().put("option:read", this);//goblin village + * return this; + * } + * + * Was technically supposed to hide the map + * @Override + * public boolean handle(Player player, Node node, String option) { + * PacketRepository.send(MinimapState.class, new MinimapStateContext(player, 2)); + * player.getInterfaceManager().open(new Component(135)).setCloseEvent(new CloseEvent() { + * @Override + * public boolean close(Player player, Component c) { + * PacketRepository.send(MinimapState.class, new MinimapStateContext(player, 0)); + * return true; + * } + * }); + * return true; + * } + */ +} \ No newline at end of file diff --git a/Server/src/main/content/global/handlers/scenery/SlashWebPlugin.java b/Server/src/main/content/global/handlers/scenery/SlashWebPlugin.java index d9fefe4c5..70ea5105b 100644 --- a/Server/src/main/content/global/handlers/scenery/SlashWebPlugin.java +++ b/Server/src/main/content/global/handlers/scenery/SlashWebPlugin.java @@ -12,13 +12,15 @@ import core.game.node.entity.combat.equipment.WeaponInterface.AttackStyle; import core.game.node.entity.combat.equipment.WeaponInterface.WeaponInterfaces; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.diary.DiaryType; -import core.game.node.entity.player.link.audio.Audio; import core.game.node.item.Item; import core.game.node.scenery.Scenery; import core.game.node.scenery.SceneryBuilder; import core.game.world.update.flag.context.Animation; import core.plugin.Plugin; import core.tools.RandomFunction; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.playAudio; /** * Handles the slashing of a web object. @@ -74,7 +76,7 @@ public final class SlashWebPlugin extends OptionHandler { } final boolean success = RandomFunction.random(2) == 1; player.lock(2); - player.getAudioManager().send(new Audio(2548)); + playAudio(player, Sounds.STABSWORD_SLASH_2548); player.animate(weapon == KNIFE ? KNIFE_ANIMATION : ANIMATION); if (success) { player.getPacketDispatch().sendMessage("You slash the web apart."); diff --git a/Server/src/main/content/global/handlers/scenery/ThievingGuidePlugin.java b/Server/src/main/content/global/handlers/scenery/ThievingGuidePlugin.java index 71efac4a5..4d306fc5e 100644 --- a/Server/src/main/content/global/handlers/scenery/ThievingGuidePlugin.java +++ b/Server/src/main/content/global/handlers/scenery/ThievingGuidePlugin.java @@ -81,24 +81,33 @@ public class ThievingGuidePlugin extends OptionHandler { player.getDialogueInterpreter().sendDialogues(2266, null, "And where do you think you're going? A little too eager", "I think. Come and talk to me before you go wandering", "around in there."); break; case "crack": - if (player.getSkills().getLevel(Skills.THIEVING) < 50) { - player.getPacketDispatch().sendMessage("You need to be level " + level + " thief to crack this safe."); - return true; - } - if (player.getInventory().freeSlots() == 0) { - player.getPacketDispatch().sendMessage("Not enough inventory space."); - return true; - } - final boolean success = success(player, Skills.THIEVING); - player.lock(4); - player.getPacketDispatch().sendMessage("You start cracking the safe."); - player.animate(animations[success ? 1 : 0]); + GameWorld.getPulser().submit(new Pulse(3, player) { @Override public boolean pulse() { + if (player.getSkills().getLevel(Skills.THIEVING) < 50) { + player.getPacketDispatch().sendMessage("You need to be level " + level + " thief to crack this safe."); + return true; + } + if (player.getInventory().freeSlots() == 0) { + player.getPacketDispatch().sendMessage("Not enough inventory space."); + return true; + } + if (player.getSkills().getLifepoints() <= 6) { + player.getPacketDispatch().sendMessage("You're too injured to be dealing with traps right now."); + return true; + } + if (player.getLocation().getDistance(node.getLocation()) >= 1) { + return true; + } + + final boolean success = success(player, Skills.THIEVING); + //player.lock(4); + player.getPacketDispatch().sendMessage("You start cracking the safe."); + player.animate(animations[success ? 1 : 0]); if (success) { handleSuccess(player, (Scenery) node); - return true; + return false; } final boolean trapped = RandomFunction.random(3) == 1; if (trapped) { @@ -109,11 +118,12 @@ public class ThievingGuidePlugin extends OptionHandler { @Override public boolean pulse() { player.animate(new Animation(-1, Priority.HIGH)); - return true; + return false; } }); } - return true; + + return false; } }); break; diff --git a/Server/src/main/content/global/skill/AttackListener.kt b/Server/src/main/content/global/skill/AttackListener.kt index 78b21386f..cba5a7e0e 100644 --- a/Server/src/main/content/global/skill/AttackListener.kt +++ b/Server/src/main/content/global/skill/AttackListener.kt @@ -6,6 +6,7 @@ import core.game.interaction.IntType class AttackListener : InteractionListener { override fun defineListeners() { + flagInstant() on(IntType.NPC, "attack"){ player, npc -> //Makes sure player uses correct attack styles for lumbridge dummies if (npc.id == 4474 && player.getSwingHandler(false).type != CombatStyle.MAGIC) { diff --git a/Server/src/main/content/global/skill/agility/AgilityHandler.java b/Server/src/main/content/global/skill/agility/AgilityHandler.java index 4a2ecea10..722de4afe 100644 --- a/Server/src/main/content/global/skill/agility/AgilityHandler.java +++ b/Server/src/main/content/global/skill/agility/AgilityHandler.java @@ -59,7 +59,7 @@ public final class AgilityHandler { player.getProperties().setTeleportLocation(dest); if (hit > 0) { player.getImpactHandler().setDisabledTicks(0); - player.getImpactHandler().manualHit(player, hit, HitsplatType.NORMAL); + impact(player, hit, HitsplatType.NORMAL); } if (message != null) { player.getPacketDispatch().sendMessage(message); diff --git a/Server/src/main/content/global/skill/agility/BarbarianOutpostCourse.kt b/Server/src/main/content/global/skill/agility/BarbarianOutpostCourse.kt index a5a05d8eb..0e4a36fea 100644 --- a/Server/src/main/content/global/skill/agility/BarbarianOutpostCourse.kt +++ b/Server/src/main/content/global/skill/agility/BarbarianOutpostCourse.kt @@ -31,7 +31,7 @@ class BarbarianOutpostCourse */ @JvmOverloads constructor(player: Player? = null) : AgilityCourse(player, 6, 46.2) { override fun createInstance(player: Player): AgilityCourse { - return content.global.skill.agility.BarbarianOutpostCourse(player) + return BarbarianOutpostCourse(player) } override fun handle(player: Player, node: Node, option: String): Boolean { @@ -88,7 +88,7 @@ class BarbarianOutpostCourse sendMessage(player, "You cannot do that from here.") return } - if (content.global.skill.agility.BarbarianOutpostCourse.Companion.ropeDelay > GameWorld.ticks) { + if (ropeDelay > GameWorld.ticks) { sendMessage(player, "The rope is being used.") return } @@ -96,7 +96,7 @@ class BarbarianOutpostCourse AgilityHandler.fail(player, 0, Location.create(2549, 9951, 0), null, getHitAmount(player), "You slip and fall to the pit below.") return } - content.global.skill.agility.BarbarianOutpostCourse.Companion.ropeDelay = GameWorld.ticks + 2 + ropeDelay = GameWorld.ticks + 2 player.packetDispatch.sendSceneryAnimation(`object`, Animation.create(497), true) AgilityHandler.forceWalk(player, 0, player.location, Location.create(2551, 3549, 0), Animation.create(751), 50, 22.0, "You skillfully swing across.", 1) } @@ -176,7 +176,7 @@ class BarbarianOutpostCourse NPCDefinition.forId(385).handlers["option:pick-up"] = this NPCDefinition.forId(386).handlers["option:pick-up"] = this NPCDefinition.forId(387).handlers["option:pick-up"] = this - ClassScanner.definePlugin(content.global.skill.agility.BarbarianOutpostCourse.BarbarianGuardDialogue()) + ClassScanner.definePlugin(BarbarianGuardDialogue()) } override fun getDestination(node: Node, n: Node): Location? { @@ -209,7 +209,7 @@ class BarbarianOutpostCourse constructor(player: Player?) : super(player) {} override fun newInstance(player: Player): core.game.dialogue.DialoguePlugin { - return content.global.skill.agility.BarbarianOutpostCourse.BarbarianGuardDialogue(player) + return BarbarianGuardDialogue(player) } override fun open(vararg args: Any): Boolean { diff --git a/Server/src/main/content/global/skill/agility/GnomeStrongholdCourse.kt b/Server/src/main/content/global/skill/agility/GnomeStrongholdCourse.kt index 5c160b86b..d34a40d61 100644 --- a/Server/src/main/content/global/skill/agility/GnomeStrongholdCourse.kt +++ b/Server/src/main/content/global/skill/agility/GnomeStrongholdCourse.kt @@ -30,25 +30,25 @@ class GnomeStrongholdCourse val `object` = node as Scenery when (`object`.id) { 2295 -> { - content.global.skill.agility.GnomeStrongholdCourse.Companion.TRAINERS[0]!!.sendChat("Okay get over that log, quick quick!") + TRAINERS[0]!!.sendChat("Okay get over that log, quick quick!") sendMessage(player, "You walk carefully across the slippery log...") AgilityHandler.walk(player, 0, Location.create(2474, 3436, 0), Location.create(2474, 3429, 0), Animation.create(155), 7.5, "...You make it safely to the other side.") return true } 2285 -> { - content.global.skill.agility.GnomeStrongholdCourse.Companion.TRAINERS[1]!!.sendChat("Move it, move it, move it!") + TRAINERS[1]!!.sendChat("Move it, move it, move it!") sendMessage(player, "You climb the netting...") AgilityHandler.climb(player, 1, Animation.create(828), `object`.location.transform(0, -1, 1), 7.5, null) return true } 35970 -> { - content.global.skill.agility.GnomeStrongholdCourse.Companion.TRAINERS[2]!!.sendChat("That's it - straight up.") + TRAINERS[2]!!.sendChat("That's it - straight up.") sendMessage(player, "You climb the tree..") AgilityHandler.climb(player, 2, Animation.create(828), Location.create(2473, 3420, 2), 5.0, "...To the platform above.") return true } 2312 -> { - content.global.skill.agility.GnomeStrongholdCourse.Companion.TRAINERS[3]!!.sendChat("Come on scaredy cat, get across that rope!") + TRAINERS[3]!!.sendChat("Come on scaredy cat, get across that rope!") sendMessage(player, "You carefully cross the tightrope.") AgilityHandler.walk(player, 3, Location.create(2477, 3420, 2), Location.create(2483, 3420, 2), Animation.create(155), 7.5, null) return true @@ -63,7 +63,7 @@ class GnomeStrongholdCourse return true } 2286 -> { - content.global.skill.agility.GnomeStrongholdCourse.Companion.TRAINERS[4]!!.sendChat("My Granny can move faster than you.") + TRAINERS[4]!!.sendChat("My Granny can move faster than you.") player.faceLocation(player.location.transform(0, 2, 0)) sendMessage(player, "You climb the netting...") AgilityHandler.climb(player, 5, Animation.create(828), player.location.transform(0, 2, 0), 7.5, null) @@ -76,11 +76,11 @@ class GnomeStrongholdCourse sendMessage(player, "You can't do that from here.") return true } - if (content.global.skill.agility.GnomeStrongholdCourse.Companion.USED_PIPES[index] > GameWorld.ticks) { + if (USED_PIPES[index] > GameWorld.ticks) { sendMessage(player, "The pipe is being used.") return true } - content.global.skill.agility.GnomeStrongholdCourse.Companion.USED_PIPES[index] = GameWorld.ticks + 10 + USED_PIPES[index] = GameWorld.ticks + 10 player.lock() //Animations and force walking @@ -117,12 +117,12 @@ class GnomeStrongholdCourse } override fun configure() { - content.global.skill.agility.GnomeStrongholdCourse.Companion.TRAINERS[0] = NPC.create(162, Location.create(2473, 3438, 0)) - content.global.skill.agility.GnomeStrongholdCourse.Companion.TRAINERS[1] = NPC.create(162, Location.create(2478, 3426, 0)) - content.global.skill.agility.GnomeStrongholdCourse.Companion.TRAINERS[2] = NPC.create(162, Location.create(2474, 3422, 1)) - content.global.skill.agility.GnomeStrongholdCourse.Companion.TRAINERS[3] = NPC.create(162, Location.create(2472, 3419, 2)) - content.global.skill.agility.GnomeStrongholdCourse.Companion.TRAINERS[4] = NPC.create(162, Location.create(2489, 3425, 0)) - for (npc in content.global.skill.agility.GnomeStrongholdCourse.Companion.TRAINERS) { + TRAINERS[0] = NPC.create(162, Location.create(2473, 3438, 0)) + TRAINERS[1] = NPC.create(162, Location.create(2478, 3426, 0)) + TRAINERS[2] = NPC.create(162, Location.create(2474, 3422, 1)) + TRAINERS[3] = NPC.create(162, Location.create(2472, 3419, 2)) + TRAINERS[4] = NPC.create(162, Location.create(2489, 3425, 0)) + for (npc in TRAINERS) { npc!!.init() npc.walkRadius = 3 } @@ -139,7 +139,7 @@ class GnomeStrongholdCourse } override fun createInstance(player: Player): AgilityCourse { - return content.global.skill.agility.GnomeStrongholdCourse(player) + return GnomeStrongholdCourse(player) } companion object { diff --git a/Server/src/main/content/global/skill/agility/WildernessCourse.kt b/Server/src/main/content/global/skill/agility/WildernessCourse.kt index bd052c523..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 } } @@ -153,7 +147,7 @@ class WildernessCourse sendMessage(player, "You cannot do that from here.") return } - if (content.global.skill.agility.WildernessCourse.Companion.ropeDelay > GameWorld.ticks) { + if (ropeDelay > GameWorld.ticks) { sendMessage(player, "The rope is being used.") return } @@ -161,7 +155,7 @@ class WildernessCourse AgilityHandler.fail(player, 0, Location.create(3005, 10357, 0), null, getHitAmount(player), "You slip and fall to the pit below.") return } - content.global.skill.agility.WildernessCourse.Companion.ropeDelay = GameWorld.ticks + 2 + ropeDelay = GameWorld.ticks + 2 player.packetDispatch.sendSceneryAnimation(`object`, Animation.create(497), true) AgilityHandler.forceWalk(player, 1, player.location, Location.create(3005, 3958, 0), Animation.create(751), 50, 20.0, "You skillfully swing across.", 1) } @@ -176,7 +170,7 @@ class WildernessCourse val fail = AgilityHandler.hasFailed(player, 1, 0.3) val origLoc = player.location registerLogoutListener(player, "steppingstone"){p -> - teleport(p, origLoc) + player.location = origLoc } submitWorldPulse(object : Pulse(2, player){ var counter = 0 @@ -254,7 +248,7 @@ class WildernessCourse } override fun createInstance(player: Player): AgilityCourse { - return content.global.skill.agility.WildernessCourse(player) + return WildernessCourse(player) } companion object { diff --git a/Server/src/main/content/global/skill/agility/brimhaven/BrimhavenArena.java b/Server/src/main/content/global/skill/agility/brimhaven/BrimhavenArena.java index 6a60b2f43..13a6a4ad6 100644 --- a/Server/src/main/content/global/skill/agility/brimhaven/BrimhavenArena.java +++ b/Server/src/main/content/global/skill/agility/brimhaven/BrimhavenArena.java @@ -27,6 +27,8 @@ import core.tools.RandomFunction; import java.util.HashMap; import java.util.Map; +import static core.api.ContentAPIKt.*; + /** * Handles the Brimhaven agility arena zone. * @@ -77,7 +79,7 @@ public final class BrimhavenArena extends MapZone implements Plugin { player.setAttribute("brim-tagcount", 0); } player.setAttribute("brim-tagged", false); - player.getConfigManager().set(309, 0); + setVarp(player, 309, 0); int index = RandomFunction.randomize(DISPENSERS.length); if (index == player.getAttribute("brim-tag", -1)) { index = (index + 1) % DISPENSERS.length; @@ -137,7 +139,7 @@ public final class BrimhavenArena extends MapZone implements Plugin { player.setAttribute("brim-tagged", true); int tags = player.getAttribute("brim-tagcount", 0) + 1; player.setAttribute("brim-tagcount", tags); - player.getConfigManager().set(309, 4); + setVarp(player, 309, 4); if (tags > 1) { int amount = 1; if (!player.getInventory().add(new Item(TICKET.getId(), amount))) { diff --git a/Server/src/main/content/global/skill/agility/pyramid/AgilityPyramidCourse.java b/Server/src/main/content/global/skill/agility/pyramid/AgilityPyramidCourse.java index 943a77563..06cae7b1b 100644 --- a/Server/src/main/content/global/skill/agility/pyramid/AgilityPyramidCourse.java +++ b/Server/src/main/content/global/skill/agility/pyramid/AgilityPyramidCourse.java @@ -18,6 +18,9 @@ import core.game.world.map.Direction; import core.game.world.map.Location; import core.game.world.update.flag.context.Animation; import core.plugin.ClassScanner; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.*; /** * Handles the agility pryamid course. @@ -147,7 +150,7 @@ public final class AgilityPyramidCourse extends AgilityCourse { case 10855: case 10856: addConfig(player, 10869, 0, true); - if (player.getConfigManager().get(640) == 505 || player.getConfigManager().get(640) == 515) { + if (getVarp(player, 640) == 505 || getVarp(player, 640) == 515) { player.getSkills().addExperience(Skills.AGILITY, 300, true); } player.getDialogueInterpreter().sendDialogue("You climb down the steep passage. It leads to the base of the", "pyramid."); @@ -193,6 +196,7 @@ public final class AgilityPyramidCourse extends AgilityCourse { player.getPacketDispatch().sendMessage("You must be level 30 agility or higher to climb down the rocks."); return; } + playAudio(player, Sounds.CLIMBING_LOOP_2454, 0, 6); if (!scale) { ForceMovement.run(player, player.getLocation(), end, Animation.create(740), Animation.create(740), Direction.WEST, 13).setEndAnimation(Animation.RESET); } else { @@ -219,6 +223,7 @@ public final class AgilityPyramidCourse extends AgilityCourse { } player.lock(4); player.getPacketDispatch().sendMessage("You climb the low wall..."); + playAudio(player, Sounds.CLIMB_WALL_2453, 40); if (fail) { Location end = player.getLocation().transform(d, 1); player.lock(3); @@ -242,8 +247,10 @@ public final class AgilityPyramidCourse extends AgilityCourse { final boolean fail = player.getSkills().getLevel(Skills.AGILITY) < 75 && hasFailed(player); final Location end = player.getLocation().transform(dir.getStepX() * (fail ? 3 : 5), dir.getStepY() * (fail ? 3 : 5), 0); player.getPacketDispatch().sendMessage("You put your foot on the ledge and try to edge across..."); + playAudio(player, Sounds.BALANCING_LEDGE_2451, 0, 5); if (fail) { player.lock(4); + playAudio(player, Sounds.FALL_LAND_2455, 125); AgilityHandler.walk(player, -1, player.getLocation(), end, Animation.create(157 - diff), 0.0, "You slip and fall to the level below."); GameWorld.getPulser().submit(new Pulse(3, player) { @Override @@ -290,11 +297,13 @@ public final class AgilityPyramidCourse extends AgilityCourse { final boolean fail = player.getSkills().getLevel(Skills.AGILITY) >= 75 ? false : hasFailed(player) ; final Location end = object.getLocation().transform(object.getId() != 10868 ? dir : dir.getOpposite(), fail ? 2 : 5); AgilityHandler.walk(player, fail ? -1 : 1, player.getLocation(), end, Animation.create(155), fail ? 0.0 : 56.4, fail ? null : "You walk carefully across the slippery plank..."); + playAudio(player, Sounds.PLANKWALK_2480, 0, 3); if (fail) { GameWorld.getPulser().submit(new Pulse(2, player) { @Override public boolean pulse() { final Location dest = transformLevel(end.transform(!custom ? 2 : 0, custom ? -2 : 0, 0)); + playAudio(player, Sounds.FALL_LAND_2455, 50); AgilityHandler.failWalk(player, 2, end, dest, dest, Animation.create(764), 10, 10, null); return true; } @@ -322,10 +331,13 @@ public final class AgilityPyramidCourse extends AgilityCourse { } dest = transformLevel(dest); player.lock(8); + playAudio(player, Sounds.JUMP_NO_LAND_2467, 30); + playAudio(player, Sounds.FALL_LAND_2455, 200); AgilityHandler.forceWalk(player, -1, player.getLocation(), end, Animation.create(3068), 10, 0.0, "... and miss your footing."); - AgilityHandler.fail(player, 8, dest, null, 8, null); + AgilityHandler.fail(player, 8, dest, Animation.create(3068), 8, null); return; } + playAudio(player, Sounds.JUMP2_2462, 30); player.lock(4); AgilityHandler.forceWalk(player, 2, player.getLocation(), player.getLocation().transform(dir, 3), Animation.create(3067), 20, 22, null); } @@ -342,15 +354,17 @@ public final class AgilityPyramidCourse extends AgilityCourse { final int rot = object.getRotation(); int mod = player.getLocation().equals(new Location(3359, 2849, 2)) ? 0 : player.getLocation().equals(new Location(3357, 2841, 2)) ? 1 : player.getLocation().equals(new Location(3367, 2832, 1)) ? 1 : player.getLocation().equals(new Location(3372, 2832, 1)) ? 0 : object.getLocation().equals(new Location(3370, 2831, 1)) ? 0 : rot == 1 && dir == Direction.EAST ? 0 : rot == 3 && (dir == Direction.WEST || dir == Direction.EAST) ? 1 : rot == 0 && dir == Direction.SOUTH ? 1 : dir == Direction.WEST && rot != 3 || dir == Direction.EAST ? 1 : 0; final Animation animation = Animation.create(387 - mod); + playAudio(player, Sounds.HANDHOLDS_GRAB_TO_SECOND_2450); + playAudio(player, Sounds.FALL_LAND_2455, 170); if (fail) { Location dest = object.getLocation().transform(dir, 1); - dest = rot == 1 && dir == Direction.EAST ? dest.transform(1, 2, 0) : rot == 1 && dir == Direction.WEST ? dest.transform(0, -2, 0) : dest.transform(dir == Direction.NORTH || dir == Direction.SOUTH ? -1 : dir == Direction.WEST ? 1 : 0, dir == Direction.SOUTH ? 1 : dir == Direction.WEST || dir == Direction.EAST ? -1 : 0, 0); + dest = rot == 1 && dir == Direction.EAST ? dest.transform(1, 2, 0) : rot == 1 && dir == Direction.WEST && player.getLocation().getY() < 2841 ? dest.transform(0, -2, 0) : rot == 1 && dir == Direction.WEST ? dest.transform(0, 2, 0): dest.transform(dir == Direction.NORTH || dir == Direction.SOUTH ? -1 : dir == Direction.WEST ? 1 : 0, dir == Direction.SOUTH ? 1 : dir == Direction.WEST || dir == Direction.EAST ? -1 : 0, 0); AgilityHandler.walk(player, -1, player.getLocation(), end, animation, 10, null); - AgilityHandler.fail(player, 3, transformLevel(dest), null, 8, null); - // player.animate(Animation.create(3056 - mod), 2); + Location finalDest = dest; GameWorld.getPulser().submit(new Pulse(3, player) { @Override public boolean pulse() { + AgilityHandler.fail(player, 3, transformLevel(finalDest), Animation.create(3056 - mod), 8, null); player.getAppearance().setDefaultAnimations(); player.getAppearance().sync(); return true; @@ -376,7 +390,7 @@ public final class AgilityPyramidCourse extends AgilityCourse { @Override public boolean pulse() { player.getSkills().addExperience(Skills.AGILITY, 1000); - if (player.getConfigManager().get(640) == 505 || player.getConfigManager().get(640) == 515) { + if (getVarp(player, 640) == 505 || getVarp(player, 640) == 515) { player.getPacketDispatch().sendMessage("You find nothing on top of the pyramid."); return true; } @@ -384,6 +398,7 @@ public final class AgilityPyramidCourse extends AgilityCourse { player.getSavedData().getActivityData().setTopGrabbed(true); player.getInventory().add(PYRAMID_TOP, player); player.getDialogueInterpreter().sendItemMessage(PYRAMID_TOP, "You find a golden pyramid!"); + playJingle(player, 151); return true; } }); @@ -499,7 +514,7 @@ public final class AgilityPyramidCourse extends AgilityCourse { final VarbitDefinition definition = VarbitDefinition.forObjectID(SceneryDefinition.forId(objectId).getVarbitID()); final int oldVal = (definition.getValue(player) << definition.getStartBit()); final int newVal = (value << definition.getStartBit()); - player.getConfigManager().set(CONFIG_ID, (player.getConfigManager().get(CONFIG_ID) - oldVal) + newVal, save); + setVarp(player, CONFIG_ID, (getVarp(player, CONFIG_ID) - oldVal) + newVal, save); } /** diff --git a/Server/src/main/content/global/skill/agility/pyramid/MovingBlockNPC.java b/Server/src/main/content/global/skill/agility/pyramid/MovingBlockNPC.java index c08ff2322..984fd532b 100644 --- a/Server/src/main/content/global/skill/agility/pyramid/MovingBlockNPC.java +++ b/Server/src/main/content/global/skill/agility/pyramid/MovingBlockNPC.java @@ -11,6 +11,9 @@ 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 org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.playAudio; /** * The moving block npc. @@ -58,8 +61,9 @@ public final class MovingBlockNPC extends AbstractNPC { Location loc = getLocation().transform(dir, 2); getWalkingQueue().reset(); getWalkingQueue().addPath(loc.getX(), loc.getY()); - for (Player p : RegionManager.getLocalPlayers(getTileLocations()[0], 2)) { + for (Player p : RegionManager.getLocalPlayers(getTileLocations()[0], 4)) { checkBlock(p); + playAudio(p, Sounds.PYRAMID_BLOCK_1395, 30); } moving = true; GameWorld.getPulser().submit(new Pulse(1, this) { @@ -133,6 +137,7 @@ public final class MovingBlockNPC extends AbstractNPC { } } player.lock(4); + playAudio(player, Sounds.LAND_FLAT_2469, 50); player.setAttribute("block-move", GameWorld.getTicks() + 4); if(dest != null) { AgilityHandler.failWalk(player, close ? 1 : 3, player.getLocation(), dest, AgilityPyramidCourse.transformLevel(dest), Animation.create(3066), 10, 8, null, getId() == 3124 ? Direction.WEST : Direction.SOUTH); diff --git a/Server/src/main/content/global/skill/agility/pyramid/RollingBlock.java b/Server/src/main/content/global/skill/agility/pyramid/RollingBlock.java index 438af8d1e..7573889aa 100644 --- a/Server/src/main/content/global/skill/agility/pyramid/RollingBlock.java +++ b/Server/src/main/content/global/skill/agility/pyramid/RollingBlock.java @@ -12,6 +12,9 @@ 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 org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.playAudio; /** * Represents the rolling block obstacle. @@ -29,6 +32,9 @@ public final class RollingBlock implements MovementHook { final boolean fail = backwards || AgilityHandler.hasFailed(player, 2, 0.3); player.lock(5); AgilityPyramidCourse.addConfig(player, stone, 1, false); + playAudio(player, Sounds.PYRAMID_SCRAPE_1396); + playAudio(player, Sounds.LAND_FLAT_2469, 40); + if(e.isPlayer()) { ((Player) e).logoutListeners.put("rolling-block", p -> { 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/BasaltRockShortcut.kt b/Server/src/main/content/global/skill/agility/shortcuts/BasaltRockShortcut.kt index 793374a05..2bc194611 100644 --- a/Server/src/main/content/global/skill/agility/shortcuts/BasaltRockShortcut.kt +++ b/Server/src/main/content/global/skill/agility/shortcuts/BasaltRockShortcut.kt @@ -9,7 +9,6 @@ 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.player.FaceLocationFlag import core.plugin.Initializable import core.plugin.Plugin @@ -43,16 +42,16 @@ class BasaltRockShortcut : AgilityShortcut { * 2522, 3600 R1, 3601, 3602 R2 */ override fun newInstance(arg: Any?): Plugin { - configure(content.global.skill.agility.shortcuts.BasaltRockShortcut(intArrayOf(4550), 1, 0.0, "jump-to")) //Beach South* - configure(content.global.skill.agility.shortcuts.BasaltRockShortcut(intArrayOf(4551), 1, 0.0, "jump-across")) //Beach South Rock 1* - configure(content.global.skill.agility.shortcuts.BasaltRockShortcut(intArrayOf(4552), 1, 0.0, "jump-across")) //South Rock 2 - configure(content.global.skill.agility.shortcuts.BasaltRockShortcut(intArrayOf(4553), 1, 0.0, "jump-across")) //South Rock 2 (other side) - configure(content.global.skill.agility.shortcuts.BasaltRockShortcut(intArrayOf(4554), 1, 0.0, "jump-across")) //Middle Rock 3 - configure(content.global.skill.agility.shortcuts.BasaltRockShortcut(intArrayOf(4555), 1, 0.0, "jump-across")) //Middle Rock 3 (other side) - configure(content.global.skill.agility.shortcuts.BasaltRockShortcut(intArrayOf(4556), 1, 0.0, "jump-across")) //North Rock 4 - configure(content.global.skill.agility.shortcuts.BasaltRockShortcut(intArrayOf(4557), 1, 0.0, "jump-across")) //North Rock 4 (other side) - configure(content.global.skill.agility.shortcuts.BasaltRockShortcut(intArrayOf(4558), 1, 0.0, "jump-across")) //Rocky Shore North Rock 5* - configure(content.global.skill.agility.shortcuts.BasaltRockShortcut(intArrayOf(4559), 1, 0.0, "jump-to")) //Rocky Shore North* + configure(BasaltRockShortcut(intArrayOf(4550), 1, 0.0, "jump-to")) //Beach South* + configure(BasaltRockShortcut(intArrayOf(4551), 1, 0.0, "jump-across")) //Beach South Rock 1* + configure(BasaltRockShortcut(intArrayOf(4552), 1, 0.0, "jump-across")) //South Rock 2 + configure(BasaltRockShortcut(intArrayOf(4553), 1, 0.0, "jump-across")) //South Rock 2 (other side) + configure(BasaltRockShortcut(intArrayOf(4554), 1, 0.0, "jump-across")) //Middle Rock 3 + configure(BasaltRockShortcut(intArrayOf(4555), 1, 0.0, "jump-across")) //Middle Rock 3 (other side) + configure(BasaltRockShortcut(intArrayOf(4556), 1, 0.0, "jump-across")) //North Rock 4 + configure(BasaltRockShortcut(intArrayOf(4557), 1, 0.0, "jump-across")) //North Rock 4 (other side) + configure(BasaltRockShortcut(intArrayOf(4558), 1, 0.0, "jump-across")) //Rocky Shore North Rock 5* + configure(BasaltRockShortcut(intArrayOf(4559), 1, 0.0, "jump-to")) //Rocky Shore North* return this } @@ -136,7 +135,7 @@ class BasaltRockShortcut : AgilityShortcut { player.sendMessage(noJump) } else { player.lock(3) - player.faceLocation(FaceLocationFlag.getFaceLocation(player, Location.create(2518, 3611, 0))) + player.faceLocation(Location.create(2518, 3611, 0)) AgilityHandler.forceWalk(player, -1, Location.create(2516, 3611, 0), Location.create(2518, 3611, 0), Animation.create(769), 20, 0.0, null, 1) } return true @@ -147,7 +146,7 @@ class BasaltRockShortcut : AgilityShortcut { player.sendMessage(noJump) } else { player.lock(3) - player.faceLocation(FaceLocationFlag.getFaceLocation(player, Location.create(2516, 3611, 0))) + player.faceLocation(Location.create(2516, 3611, 0)) AgilityHandler.forceWalk(player, -1, Location.create(2518, 3611, 0), Location.create(2516, 3611, 0), Animation.create(769), 20, 0.0, null, 1) } return true @@ -199,4 +198,4 @@ class BasaltRockShortcut : AgilityShortcut { } }) } -} \ No newline at end of file +} 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 90090ed16..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, content.global.skill.agility.shortcuts.CatherbyGrappleShortcut.Companion.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 " + - content.global.skill.agility.shortcuts.CatherbyGrappleShortcut.Companion.REQUIREMENTS[Skills.AGILITY] + " " + Skills.SKILL_NAME[Skills.AGILITY] + ", " + - content.global.skill.agility.shortcuts.CatherbyGrappleShortcut.Companion.REQUIREMENTS[Skills.RANGE] + " " + Skills.SKILL_NAME[Skills.RANGE] + ", ", - "and " + - content.global.skill.agility.shortcuts.CatherbyGrappleShortcut.Companion.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, content.global.skill.agility.shortcuts.CatherbyGrappleShortcut.Companion.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, content.global.skill.agility.shortcuts.CatherbyGrappleShortcut.Companion.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 isEquipped(player, Items.MITH_GRAPPLE_9419) && areAnyEquipped(player, *content.global.skill.agility.shortcuts.CatherbyGrappleShortcut.Companion.VALID_CROSSBOWS) - } - - private fun doesPlayerHaveRequiredLevels(player: Player): Boolean { - for ((skill, requiredLevel) in content.global.skill.agility.shortcuts.CatherbyGrappleShortcut.Companion.REQUIREMENTS) { - if (!hasLevelDyn(player, skill, requiredLevel)) { - return false - } - } - return true - } - - private fun isPlayerInRangeToGrapple(player: Player): Boolean { - return inBorders(player, content.global.skill.agility.shortcuts.CatherbyGrappleShortcut.Companion.START_LOCATION.x - 2, content.global.skill.agility.shortcuts.CatherbyGrappleShortcut.Companion.START_LOCATION.y - 2, content.global.skill.agility.shortcuts.CatherbyGrappleShortcut.Companion.START_LOCATION.x, content.global.skill.agility.shortcuts.CatherbyGrappleShortcut.Companion.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/PipeShortcut.kt b/Server/src/main/content/global/skill/agility/shortcuts/PipeShortcut.kt index eddc9b2e1..f120546f7 100644 --- a/Server/src/main/content/global/skill/agility/shortcuts/PipeShortcut.kt +++ b/Server/src/main/content/global/skill/agility/shortcuts/PipeShortcut.kt @@ -57,11 +57,8 @@ class PipeShortcut : AgilityShortcut { /** * Run for the main fire event - * Use this for unlocking achievement diaries + any other unlocks */ override fun run(player: Player, obj: Scenery, option: String, failed: Boolean) { - if (obj.id == 29370) - player.achievementDiaryManager.finishTask(player, DiaryType.VARROCK, 2, 1) /** * Pulse that starts the object interaction when clicked on one of these shortcuts diff --git a/Server/src/main/content/global/skill/agility/shortcuts/RopeSwing.java b/Server/src/main/content/global/skill/agility/shortcuts/RopeSwing.java index b43424d25..e6853fe0c 100644 --- a/Server/src/main/content/global/skill/agility/shortcuts/RopeSwing.java +++ b/Server/src/main/content/global/skill/agility/shortcuts/RopeSwing.java @@ -14,7 +14,7 @@ import content.global.skill.agility.AgilityHandler; public class RopeSwing extends UseWithHandler { @Override public boolean handle(NodeUsageEvent event) { - System.out.println("Trying to handle."); + if(event.getUsedWith() instanceof Scenery){ Scenery object = event.getUsedWith().asScenery(); int objId = object.getId(); 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 1b2ceb20f..035ed53d7 100644 --- a/Server/src/main/content/global/skill/agility/shortcuts/SteppingStoneShortcut.kt +++ b/Server/src/main/content/global/skill/agility/shortcuts/SteppingStoneShortcut.kt @@ -1,7 +1,9 @@ package content.global.skill.agility.shortcuts +import core.api.* import core.cache.def.impl.SceneryDefinition import core.game.interaction.OptionHandler +import core.game.interaction.QueueStrength import core.game.node.Node import core.game.node.entity.impl.ForceMovement import core.game.node.entity.player.Player @@ -38,15 +40,19 @@ class SteppingStoneShortcut : OptionHandler() { } val offset = getOffset(player,finalDest) player.debug("Offset: ${offset.first},${offset.second}") - player.pulseManager.run(object : Pulse(2){ - override fun pulse(): Boolean { - val there = player.location == finalDest - if(!there){ - ForceMovement.run(player,player.location,player.location.transform(offset.first,offset.second,0), ANIMATION,10) - } - return there + closeAllInterfaces(player) + lock(player, 3) + player.locks.lockTeleport(3) + queueScript(player, 2, QueueStrength.SOFT) { + val there = player.location == finalDest + if (!there) { + lock(player, 3) + player.locks.lockTeleport(3) + ForceMovement.run(player,player.location,player.location.transform(offset.first,offset.second,0), ANIMATION,10) + return@queueScript delayScript(player, 2) } - }) + return@queueScript stopExecuting(player) + } return true } @@ -82,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.java b/Server/src/main/content/global/skill/agility/shortcuts/StileShortcut.java deleted file mode 100644 index 78e5fcd8c..000000000 --- a/Server/src/main/content/global/skill/agility/shortcuts/StileShortcut.java +++ /dev/null @@ -1,108 +0,0 @@ -package content.global.skill.agility.shortcuts; - -import core.game.activity.ActivityManager; -import core.game.node.entity.impl.PulseType; -import core.plugin.Initializable; -import content.global.skill.agility.AgilityShortcut; -import core.game.interaction.MovementPulse; -import core.game.node.entity.impl.ForceMovement; -import core.game.node.entity.player.Player; -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; - -/** - * Handles a stile shortcut. - * @author Vexia - */ -@Initializable -public class StileShortcut extends AgilityShortcut { - - /** - * Constructs a new {@Code StileShortcut} {@Code Object} - */ - public StileShortcut() { - super(new int[] { 993, 3730, 7527, 12982, 19222, 22302, 29460, 33842, 34776, 39508, 39509, 39510 }, 1, 0.0, false, 0.0, new String[] { "climb-over" }); - - } - - @Override - public void run(final Player player, final Scenery object, String option, boolean failed) { - player.getWalkingQueue().reset(); - Location loc = getLocation(player, object, true); - player.getWalkingQueue().addPath(loc.getX(), loc.getY()); - player.getPulseManager().run(new MovementPulse(player, loc) { - @Override - public boolean pulse() { - climb(player, object); - return true; - } - }, PulseType.STANDARD); - } - - /** - * Method used to climb the stile. - * @param player the player. - * @param object the object. - */ - public static void climb(final Player player, final Scenery object) { - player.lock(1); - int delay = 0; - GameWorld.getPulser().submit(new Pulse(delay, player) { - @Override - public boolean pulse() { - ForceMovement movement = new ForceMovement(player, getLocation(player, object, true), getLocation(player, object, false), Animation.create(839)) { - @Override - public void stop() { - super.stop(); - if (object.getId() == 19222) {// falconry. - handleFalconry(player, object); - } - } - }; - movement.run(player, 5); - return true; - } - }); - } - - /** - * Method used to get the end location. - * @param player the player. - * @param object the object. - * @param start if getting the start loc. - * @return the location. - */ - public static Location getLocation(final Player player, final Scenery object, boolean start) { - if ((object.getDirection() == Direction.NORTH || object.getDirection() == Direction.SOUTH)) { - if (player.getLocation().getY() <= object.getLocation().getY()) { - return start ? object.getLocation() : object.getLocation().transform(0, 1, 0); - } else { - return start ? object.getLocation().transform(0, 1, 0) : object.getLocation(); - } - } else { - if (player.getLocation().getX() <= object.getLocation().getX()) { - return start ? object.getLocation() : object.getLocation().transform(1, 0, 0); - } else { - return start ? object.getLocation().transform(1, 0, 0) : object.getLocation(); - } - } - } - - /** - * Method used to handle the falconry stile. - * @param player the player. - * @param object the object. - */ - private static void handleFalconry(final Player player, Scenery object) { - if (player.getLocation().equals(Location.create(2371, 3621, 0))) { - ActivityManager.start(player, "falconry", false); - } else { - ActivityManager.getActivity("falconry").leave(player, false); - } - } - -} diff --git a/Server/src/main/content/global/skill/agility/shortcuts/StileShortcut.kt b/Server/src/main/content/global/skill/agility/shortcuts/StileShortcut.kt new file mode 100644 index 000000000..e492b6b9d --- /dev/null +++ b/Server/src/main/content/global/skill/agility/shortcuts/StileShortcut.kt @@ -0,0 +1,79 @@ +package content.global.skill.agility.shortcuts + +import core.api.* +import core.api.utils.Vector +import core.game.activity.ActivityManager +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.Direction +import core.game.world.map.Location + +class StileShortcut : InteractionListener { + val ids = intArrayOf(993, 3730, 7527, 12982, 19222, 22302, 29460, 33842, 34776, 39508, 39509, 39510) + val FALCONRY_STILE = 19222 + override fun defineListeners() { + on (ids, IntType.SCENERY, "climb-over") {p, n -> + val direction = Vector.betweenLocs(p.location, n.location).toDirection() + 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) + + queueScript(p, 5, QueueStrength.SOFT) {_ -> + val end = endLoc.transform(direction, 1) + p.walkingQueue.reset() + p.walkingQueue.addPath(end.x, end.y) + + if (n.id == FALCONRY_STILE) + handleFalconry(p, endLoc) + return@queueScript stopExecuting(p) + } + return@on true + } + + setDest(IntType.SCENERY, ids, "climb-over") {e, n -> + return@setDest getInteractLocation(e.location, n.location, getOrientation(n.direction)) + } + } + + + + companion object { + fun getInteractLocation (pLoc: Location, sLoc: Location, orientation: Orientation) : Location { + when (orientation) { + Orientation.Horizontal -> { + if (pLoc.x <= sLoc.x) return sLoc.transform(-1, 0, 0) + else return sLoc.transform(2, 0, 0) + } + Orientation.Vertical -> { + if (pLoc.y <= sLoc.y) return sLoc.transform(0, -1, 0) + else return sLoc.transform(0, 2, 0) + } + } + } + + fun getOrientation (rotation: Direction) : Orientation { + when (rotation) { + Direction.EAST, Direction.WEST -> return Orientation.Horizontal + else -> return Orientation.Vertical + } + } + + fun handleFalconry (p: Player, endLoc: Location) { + if (endLoc.y == 3619) + ActivityManager.start(p, "falconry", false) + else + ActivityManager.getActivity("falconry").leave(p, false) + } + } + + enum class Orientation { + Horizontal, + Vertical + } +} 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 529c32366..42cf77e34 100644 --- a/Server/src/main/content/global/skill/agility/shortcuts/TunnelShortcut.java +++ b/Server/src/main/content/global/skill/agility/shortcuts/TunnelShortcut.java @@ -14,6 +14,9 @@ import core.game.world.update.flag.context.Animation; import core.plugin.Initializable; import core.plugin.Plugin; +import static core.api.ContentAPIKt.hasRequirement; +import content.data.Quests; + /** * Handles a tunnel shortcut. * @author Vexia @@ -70,6 +73,10 @@ 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, Quests.SWAN_SONG)) + return; + } player.lock(6); final Scenery o = object; final Location start = player.getLocation(); 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 c452357e5..c5ca6c8e7 100644 --- a/Server/src/main/content/global/skill/construction/BuildHotspot.java +++ b/Server/src/main/content/global/skill/construction/BuildHotspot.java @@ -28,12 +28,12 @@ public enum BuildHotspot { * Low level Parlor hotspots. */ CHAIRS_1(15410, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_MID_ANIM, Decoration.CRUDE_CHAIR,Decoration.WOODEN_CHAIR, Decoration.ROCKING_CHAIR, Decoration.OAK_CHAIR, Decoration.OAK_ARMCHAIR, Decoration.TEAK_ARMCHAIR, Decoration.MAHOGANY_ARMCHAIR), - CHAIRS_2(15411, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_MID_ANIM, Decoration.CRUDE_CHAIR, Decoration.WOODEN_CHAIR,Decoration.ROCKING_CHAIR, Decoration.OAK_CHAIR, Decoration.OAK_ARMCHAIR, Decoration.TEAK_ARMCHAIR, Decoration.MAHOGANY_ARMCHAIR), + CHAIRS_2(15411, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_MID_ANIM, Decoration.CRUDE_CHAIR, Decoration.WOODEN_CHAIR,Decoration.ROCKING_CHAIR, Decoration.OAK_CHAIR, Decoration.OAK_ARMCHAIR, Decoration.TEAK_ARMCHAIR, Decoration.MAHOGANY_ARMCHAIR), CHAIRS_3(15412, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_MID_ANIM, Decoration.CRUDE_CHAIR, Decoration.WOODEN_CHAIR,Decoration.ROCKING_CHAIR, Decoration.OAK_CHAIR, Decoration.OAK_ARMCHAIR, Decoration.TEAK_ARMCHAIR, Decoration.MAHOGANY_ARMCHAIR), - 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), + 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.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), @@ -41,37 +41,37 @@ public enum BuildHotspot { /** * Low level Kitchen hotspots. */ - LARDER(15403, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_HIGH_ANIM, Decoration.WOODEN_LARDER, Decoration.OAK_LARDER,Decoration.TEAK_LARDER), - SINK(15404, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_MID_ANIM, Decoration.PUMP_AND_DRAIN, Decoration.PUMP_AND_TUB, Decoration.SINK), - KITCHEN_TABLE(15405, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_MID_ANIM, Decoration.KITCHEN_WOODEN_TABLE,Decoration.KITCHEN_OAK_TABLE, Decoration.KITCHEN_TEAK_TABLE), - CAT_BLANKET(15402, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_MID_ANIM, Decoration.CAT_BLANKET, Decoration.CAT_BASKET,Decoration.CAST_BASKET_CUSHIONED), - STOVE(15398, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_MID_ANIM, Decoration.BASIC_FIREPIT, Decoration.FIREPIT_WITH_HOOK, Decoration.FIREPIT_WITH_POT, Decoration.SMALL_OVEN, Decoration.LARGE_OVEN, Decoration.BASIC_RANGE, Decoration.FANCY_RANGE), - SHELVES(15400, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_HIGH_ANIM, Decoration.WOODEN_SHELVES_1,Decoration.WOODEN_SHELVES_2, Decoration.WOODEN_SHELVES_3,Decoration.OAK_SHELVES_1, Decoration.OAK_SHELVES_2, Decoration.TEAK_SHELVES_1, Decoration.TEAK_SHELVES_2), - SHELVES_2(15399, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_HIGH_ANIM, Decoration.WOODEN_SHELVES_1,Decoration.WOODEN_SHELVES_2, Decoration.WOODEN_SHELVES_3,Decoration.OAK_SHELVES_1, Decoration.OAK_SHELVES_2,Decoration.TEAK_SHELVES_1, Decoration.TEAK_SHELVES_2), + LARDER(15403, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_HIGH_ANIM, Decoration.WOODEN_LARDER, Decoration.OAK_LARDER,Decoration.TEAK_LARDER), + SINK(15404, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_MID_ANIM, Decoration.PUMP_AND_DRAIN, Decoration.PUMP_AND_TUB, Decoration.SINK), + KITCHEN_TABLE(15405, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_MID_ANIM, Decoration.KITCHEN_WOODEN_TABLE,Decoration.KITCHEN_OAK_TABLE, Decoration.KITCHEN_TEAK_TABLE), + CAT_BLANKET(15402, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_MID_ANIM, Decoration.CAT_BLANKET, Decoration.CAT_BASKET,Decoration.CAST_BASKET_CUSHIONED), + STOVE(15398, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_MID_ANIM, Decoration.BASIC_FIREPIT, Decoration.FIREPIT_WITH_HOOK, Decoration.FIREPIT_WITH_POT, Decoration.SMALL_OVEN, Decoration.LARGE_OVEN, Decoration.BASIC_RANGE, Decoration.FANCY_RANGE), + SHELVES(15400, BuildHotspotType.LINKED, BuildingUtils.BUILD_HIGH_ANIM, Decoration.WOODEN_SHELVES_1,Decoration.WOODEN_SHELVES_2, Decoration.WOODEN_SHELVES_3,Decoration.OAK_SHELVES_1, Decoration.OAK_SHELVES_2, Decoration.TEAK_SHELVES_1, Decoration.TEAK_SHELVES_2), + SHELVES_2(15399, BuildHotspotType.LINKED, BuildingUtils.BUILD_HIGH_ANIM, Decoration.WOODEN_SHELVES_1,Decoration.WOODEN_SHELVES_2, Decoration.WOODEN_SHELVES_3,Decoration.OAK_SHELVES_1, Decoration.OAK_SHELVES_2,Decoration.TEAK_SHELVES_1, Decoration.TEAK_SHELVES_2), BARRELS(15401, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_MID_ANIM, Decoration.BASIC_BEER_BARREL, Decoration.CIDER_BARREL,Decoration.ASGARNIAN_ALE_BARREL, Decoration.GREENMANS_ALE_BARREL,Decoration.DRAGON_BITTER_BARREL, Decoration.CHEFS_DELIGHT_BARREL), /** * Low-level Dining hotspots. */ - FIREPLACE_DINING(15301, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_MID_ANIM, Decoration.CLAY_FIREPLACE, Decoration.STONE_FIREPLACE, Decoration.MARBLE_FIREPLACE), - DINING_TABLE(15298, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_MID_ANIM, Decoration.DINING_TABLE_WOOD, Decoration.DINING_TABLE_OAK, Decoration.DINING_TABLE_CARVED_OAK, Decoration.DINING_TABLE_TEAK,Decoration.DINING_TABLE_CARVED_TEAK, Decoration.DINING_TABLE_MAHOGANY,Decoration.DINING_TABLE_OPULENT), - DINING_CURTAINS(15302, BuildHotspotType.RECURSIVE, BuildingUtils.BUILD_MID_ANIM, Decoration.TORN_CURTAINS, Decoration.CURTAINS, Decoration.OPULENT_CURTAINS), - 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), + FIREPLACE_DINING(15301, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_MID_ANIM, Decoration.CLAY_FIREPLACE, Decoration.STONE_FIREPLACE, Decoration.MARBLE_FIREPLACE), + DINING_TABLE(15298, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_MID_ANIM, Decoration.DINING_TABLE_WOOD, Decoration.DINING_TABLE_OAK, Decoration.DINING_TABLE_CARVED_OAK, Decoration.DINING_TABLE_TEAK,Decoration.DINING_TABLE_CARVED_TEAK, Decoration.DINING_TABLE_MAHOGANY,Decoration.DINING_TABLE_OPULENT), + DINING_CURTAINS(15302, BuildHotspotType.RECURSIVE, BuildingUtils.BUILD_MID_ANIM, Decoration.TORN_CURTAINS, Decoration.CURTAINS, Decoration.OPULENT_CURTAINS), + 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. */ - 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), + 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.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), /** @@ -81,7 +81,7 @@ public enum BuildHotspot { CLOCK(15268, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_HIGH_ANIM, Decoration.OAK_CLOCK, Decoration.TEAK_CLOCK, Decoration.GILDED_CLOCK), DRESSER(15262, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_MID_ANIM, Decoration.SHAVING_STAND, Decoration.OAK_SHAVING_STAND, Decoration.OAK_DRESSER, Decoration.TEAK_DRESSER, Decoration.FANCY_TEAK_DRESSER, Decoration.MAHOGANY_DRESSER, Decoration.GILDED_DRESSER), DRAWERS(15261, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_MID_ANIM, Decoration.SHOE_BOX, Decoration.OAK_DRAWERS, Decoration.OAK_WARDROBE, Decoration.TEAK_DRAWERS, Decoration.TEAK_WARDROBE, Decoration.MAHOGANY_WARDROBE, Decoration.GILDED_WARDROBE), - BEDROOM_CURTAINS(15263, BuildHotspotType.RECURSIVE, BuildingUtils.BUILD_MID_ANIM, Decoration.TORN_CURTAINS, Decoration.CURTAINS, Decoration.OPULENT_CURTAINS), + BEDROOM_CURTAINS(15263, BuildHotspotType.RECURSIVE, BuildingUtils.BUILD_MID_ANIM, Decoration.TORN_CURTAINS, Decoration.CURTAINS, Decoration.OPULENT_CURTAINS), BEDROOM_RUG(15264, BuildHotspotType.LINKED, BuildingUtils.BUILD_LOW_ANIM, Decoration.BROWN_RUG_CENTER, Decoration.RED_RUG_CENTER, Decoration.OPULENT_RUG_CENTER), BEDROOM_RUG2(15265, BuildHotspotType.LINKED, BuildingUtils.BUILD_LOW_ANIM, Decoration.BROWN_RUG_END, Decoration.RED_RUG_END, Decoration.OPULENT_RUG_END), BEDROOM_RUG3(15266, BuildHotspotType.LINKED, BuildingUtils.BUILD_LOW_ANIM, Decoration.BROWN_RUG_CORNER, Decoration.RED_RUG_CORNER, Decoration.OPULENT_RUG_CORNER), @@ -111,19 +111,19 @@ public enum BuildHotspot { * Portal room hotspots. */ TELEPORT_FOCUS(15409, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_MID_ANIM, Decoration.TELEPORT_FOCUS, Decoration.GREATER_TELEPORT_FOCUS, Decoration.SCRYING_POOL), - PORTAL1(15406, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_MID_ANIM, Decoration.TEAK_PORTAL, Decoration.MAHOGANY_PORTAL, Decoration.MARBLE_PORTAL, - Decoration.TEAK_VARROCK_PORTAL, - Decoration.MAHOGANY_VARROCK_PORTAL, - Decoration.MARBLE_VARROCK_PORTAL, + PORTAL1(15406, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_MID_ANIM, Decoration.TEAK_PORTAL, Decoration.MAHOGANY_PORTAL, Decoration.MARBLE_PORTAL, + Decoration.TEAK_VARROCK_PORTAL, + Decoration.MAHOGANY_VARROCK_PORTAL, + Decoration.MARBLE_VARROCK_PORTAL, Decoration.TEAK_LUMBRIDGE_PORTAL, Decoration.MAHOGANY_LUMBRIDGE_PORTAL, - Decoration.MARBLE_LUMBRIDGE_PORTAL, + Decoration.MARBLE_LUMBRIDGE_PORTAL, Decoration.TEAK_FALADOR_PORTAL, Decoration.MAHOGANY_FALADOR_PORTAL, - Decoration.MARBLE_FALADOR_PORTAL, + Decoration.MARBLE_FALADOR_PORTAL, Decoration.TEAK_CAMELOT_PORTAL, Decoration.MAHOGANY_CAMELOT_PORTAL, - Decoration.MARBLE_CAMELOT_PORTAL, + Decoration.MARBLE_CAMELOT_PORTAL, Decoration.TEAK_ARDOUGNE_PORTAL, Decoration.MAHOGANY_ARDOUGNE_PORTAL, Decoration.MARBLE_ARDOUGNE_PORTAL, @@ -134,18 +134,18 @@ public enum BuildHotspot { Decoration.MAHOGANY_KHARYRLL_PORTAL, Decoration.MARBLE_KHARYRLL_PORTAL), PORTAL2(15407, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_MID_ANIM, Decoration.TEAK_PORTAL, Decoration.MAHOGANY_PORTAL, Decoration.MARBLE_PORTAL, - Decoration.TEAK_VARROCK_PORTAL, - Decoration.MAHOGANY_VARROCK_PORTAL, - Decoration.MARBLE_VARROCK_PORTAL, + Decoration.TEAK_VARROCK_PORTAL, + Decoration.MAHOGANY_VARROCK_PORTAL, + Decoration.MARBLE_VARROCK_PORTAL, Decoration.TEAK_LUMBRIDGE_PORTAL, Decoration.MAHOGANY_LUMBRIDGE_PORTAL, - Decoration.MARBLE_LUMBRIDGE_PORTAL, + Decoration.MARBLE_LUMBRIDGE_PORTAL, Decoration.TEAK_FALADOR_PORTAL, Decoration.MAHOGANY_FALADOR_PORTAL, - Decoration.MARBLE_FALADOR_PORTAL, + Decoration.MARBLE_FALADOR_PORTAL, Decoration.TEAK_CAMELOT_PORTAL, Decoration.MAHOGANY_CAMELOT_PORTAL, - Decoration.MARBLE_CAMELOT_PORTAL, + Decoration.MARBLE_CAMELOT_PORTAL, Decoration.TEAK_ARDOUGNE_PORTAL, Decoration.MAHOGANY_ARDOUGNE_PORTAL, Decoration.MARBLE_ARDOUGNE_PORTAL, @@ -156,18 +156,18 @@ public enum BuildHotspot { Decoration.MAHOGANY_KHARYRLL_PORTAL, Decoration.MARBLE_KHARYRLL_PORTAL), PORTAL3(15408, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_MID_ANIM, Decoration.TEAK_PORTAL, Decoration.MAHOGANY_PORTAL, Decoration.MARBLE_PORTAL, - Decoration.TEAK_VARROCK_PORTAL, - Decoration.MAHOGANY_VARROCK_PORTAL, - Decoration.MARBLE_VARROCK_PORTAL, + Decoration.TEAK_VARROCK_PORTAL, + Decoration.MAHOGANY_VARROCK_PORTAL, + Decoration.MARBLE_VARROCK_PORTAL, Decoration.TEAK_LUMBRIDGE_PORTAL, Decoration.MAHOGANY_LUMBRIDGE_PORTAL, - Decoration.MARBLE_LUMBRIDGE_PORTAL, + Decoration.MARBLE_LUMBRIDGE_PORTAL, Decoration.TEAK_FALADOR_PORTAL, Decoration.MAHOGANY_FALADOR_PORTAL, - Decoration.MARBLE_FALADOR_PORTAL, + Decoration.MARBLE_FALADOR_PORTAL, Decoration.TEAK_CAMELOT_PORTAL, Decoration.MAHOGANY_CAMELOT_PORTAL, - Decoration.MARBLE_CAMELOT_PORTAL, + Decoration.MARBLE_CAMELOT_PORTAL, Decoration.TEAK_ARDOUGNE_PORTAL, Decoration.MAHOGANY_ARDOUGNE_PORTAL, Decoration.MARBLE_ARDOUGNE_PORTAL, @@ -186,23 +186,13 @@ 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), - STORAGE_SPACE(15296, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_MID_ANIM, Decoration.GLOVE_RACK, Decoration.WEAPONS_RACK, Decoration.EXTRA_WEAPONS_RACK), + 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), CR_CORNER2(15279, BuildHotspotType.LINKED, BuildingUtils.BUILD_MID_ANIM, Decoration.BOXING_RING, Decoration.FENCING_RING, Decoration.COMBAT_RING, Decoration.NOTHING, Decoration.NOTHING2), @@ -234,11 +224,11 @@ public enum BuildHotspot { /** * Study hotspots. */ - 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), + 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.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), /** @@ -254,12 +244,12 @@ public enum BuildHotspot { /** * Chapel hotspots. */ - ALTAR(15270, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_MID_ANIM, Decoration.OAK_ALTAR, Decoration.TEAK_ALTAR, Decoration.CLOTH_ALTAR, Decoration.MAHOGANY_ALTAR, Decoration.LIMESTONE_ALTAR, Decoration.MARBLE_ALTAR, Decoration.GILDED_ALTAR), - STATUE(15275, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_MID_ANIM, Decoration.SMALL_STATUE, Decoration.MEDIUM_STATUE, Decoration.LARGE_STATUE), - MUSICAL(15276, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_HIGH_ANIM, Decoration.WINDCHIMES, Decoration.BELLS, Decoration.ORGAN), - ICON(15269, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_MID_ANIM, Decoration.SARADOMIN_SYMBOL, Decoration.ZAMORAK_SYMBOL, Decoration.GUTHIX_SYMBOL, Decoration.SARADOMIN_ICON, Decoration.ZAMORAK_ICON, Decoration.GUTHIX_ICON, Decoration.ICON_OF_BOB), - BURNERS(15271, BuildHotspotType.RECURSIVE, BuildingUtils.BUILD_MID_ANIM, Decoration.STEEL_TORCHES, Decoration.WOODEN_TORCHES, Decoration.STEEL_CANDLESTICKS, Decoration.GOLD_CANDLESTICKS, Decoration.INCENSE_BURNERS, Decoration.MAHOGANY_BURNERS, Decoration.MARBLE_BURNERS), - CHAPEL_WINDOW(new int[] { 13730, 13728, 13732, 13729, 13733, 13731}, BuildHotspotType.RECURSIVE, BuildingUtils.BUILD_HIGH_ANIM, Decoration.SHUTTERED_WINDOW, Decoration.DECORATIVE_WINDOW, Decoration.STAINED_GLASS), + ALTAR(15270, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_MID_ANIM, Decoration.OAK_ALTAR, Decoration.TEAK_ALTAR, Decoration.CLOTH_ALTAR, Decoration.MAHOGANY_ALTAR, Decoration.LIMESTONE_ALTAR, Decoration.MARBLE_ALTAR, Decoration.GILDED_ALTAR), + STATUE(15275, BuildHotspotType.RECURSIVE, BuildingUtils.BUILD_MID_ANIM, Decoration.SMALL_STATUE, Decoration.MEDIUM_STATUE, Decoration.LARGE_STATUE), + MUSICAL(15276, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_HIGH_ANIM, Decoration.WINDCHIMES, Decoration.BELLS, Decoration.ORGAN), + ICON(15269, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_MID_ANIM, Decoration.SARADOMIN_SYMBOL, Decoration.ZAMORAK_SYMBOL, Decoration.GUTHIX_SYMBOL, Decoration.SARADOMIN_ICON, Decoration.ZAMORAK_ICON, Decoration.GUTHIX_ICON, Decoration.ICON_OF_BOB), + BURNERS(15271, BuildHotspotType.RECURSIVE, BuildingUtils.BUILD_MID_ANIM, Decoration.STEEL_TORCHES, Decoration.WOODEN_TORCHES, Decoration.STEEL_CANDLESTICKS, Decoration.GOLD_CANDLESTICKS, Decoration.INCENSE_BURNERS, Decoration.MAHOGANY_BURNERS, Decoration.MARBLE_BURNERS), + CHAPEL_WINDOW(new int[] { 13730, 13728, 13732, 13729, 13733, 13731}, BuildHotspotType.RECURSIVE, BuildingUtils.BUILD_HIGH_ANIM, Decoration.SHUTTERED_WINDOW, Decoration.DECORATIVE_WINDOW, Decoration.STAINED_GLASS), CHAPEL_RUG(15273, BuildHotspotType.LINKED, BuildingUtils.BUILD_LOW_ANIM, Decoration.BROWN_RUG_END, Decoration.RED_RUG_END, Decoration.OPULENT_RUG_END), CHAPEL_RUG2(15274, BuildHotspotType.LINKED, BuildingUtils.BUILD_LOW_ANIM, Decoration.BROWN_RUG_CORNER, Decoration.RED_RUG_CORNER, Decoration.OPULENT_RUG_CORNER), @@ -279,7 +269,7 @@ public enum BuildHotspot { /** * Throne room hotspots. */ - THRONE(15426, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_MID_ANIM, Decoration.OAK_THRONE, Decoration.TEAK_THRONE, Decoration.MAHOGANY_THRONE, Decoration.GILDED_THRONE, Decoration.SKELETON_THRONE, Decoration.CRYSTAL_THRONE, Decoration.DEMONIC_THRONE), + THRONE(15426, BuildHotspotType.RECURSIVE, BuildingUtils.BUILD_MID_ANIM, Decoration.OAK_THRONE, Decoration.TEAK_THRONE, Decoration.MAHOGANY_THRONE, Decoration.GILDED_THRONE, Decoration.SKELETON_THRONE, Decoration.CRYSTAL_THRONE, Decoration.DEMONIC_THRONE), LEVER(15435, BuildHotspotType.INDIVIDUAL, BuildingUtils.BUILD_MID_ANIM, Decoration.OAK_LEVER, Decoration.TEAK_LEVER, Decoration.MAHOGANY_LEVER), FLOOR(new int[] { 15427, 15428, 15429, 15430, 15431, 15432 }, BuildHotspotType.RECURSIVE, BuildingUtils.BUILD_LOW_ANIM, Decoration.FLOOR_DECORATION, Decoration.STEEL_CAGE, Decoration.FLOOR_TRAP, Decoration.MAGIC_CIRCLE, Decoration.MAGIC_CAGE), SEATING1(15436, BuildHotspotType.RECURSIVE, BuildingUtils.BUILD_MID_ANIM, Decoration.CARVED_TEAK_BENCH, Decoration.MAHOGANY_BENCH, Decoration.GILDED_BENCH), @@ -384,8 +374,8 @@ public enum BuildHotspot { linkedHotspots.add(new BuildHotspot[] { RUG, RUG2, RUG3 }); linkedHotspots.add(new BuildHotspot[] { BEDROOM_RUG, BEDROOM_RUG2, BEDROOM_RUG3 }); linkedHotspots.add(new BuildHotspot[] { HALL_RUG, HALL_RUG2, HALL_RUG3 }); - linkedHotspots.add(new BuildHotspot[] { CR_CORNER, CR_CORNER2, CR_CORNER3, CR_CORNER4, CR_RING, - CR_RING2, CR_RING3, CR_RING4, CR_FLOOR, CR_FLOOR2, CR_FLOOR3, CR_FLOOR4, CR_FLOOR5, + linkedHotspots.add(new BuildHotspot[] { CR_CORNER, CR_CORNER2, CR_CORNER3, CR_CORNER4, CR_RING, + CR_RING2, CR_RING3, CR_RING4, CR_FLOOR, CR_FLOOR2, CR_FLOOR3, CR_FLOOR4, CR_FLOOR5, CR_FLOOR6, CR_FLOOR7, CR_FLOOR8, CR_INVISIBLE_WALL, CR_INVISIBLE_WALL2, CR_INVISIBLE_WALL3}); linkedHotspots.add(new BuildHotspot[] { Q_HALL_RUG, Q_HALL_RUG2, Q_HALL_RUG3 }); linkedHotspots.add(new BuildHotspot[] { CHAPEL_RUG, CHAPEL_RUG2 }); @@ -394,8 +384,8 @@ 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 }); } /** @@ -523,4 +513,4 @@ public enum BuildHotspot { return buildingAnimation; } -} \ No newline at end of file +} diff --git a/Server/src/main/content/global/skill/construction/BuildHotspotType.java b/Server/src/main/content/global/skill/construction/BuildHotspotType.java index 5bff74c00..b477e826e 100644 --- a/Server/src/main/content/global/skill/construction/BuildHotspotType.java +++ b/Server/src/main/content/global/skill/construction/BuildHotspotType.java @@ -10,7 +10,7 @@ public enum BuildHotspotType { INDIVIDUAL, RECURSIVE, LINKED, - CREST, + CREST, STAIRCASE; BuildHotspotType() { diff --git a/Server/src/main/content/global/skill/construction/BuildOptionPlugin.java b/Server/src/main/content/global/skill/construction/BuildOptionPlugin.java index 6ce643e9a..69b0b1483 100644 --- a/Server/src/main/content/global/skill/construction/BuildOptionPlugin.java +++ b/Server/src/main/content/global/skill/construction/BuildOptionPlugin.java @@ -11,9 +11,12 @@ import core.game.node.entity.player.Player; import core.game.node.scenery.Scenery; import core.plugin.Initializable; import core.plugin.Plugin; +import core.tools.Log; import core.tools.SystemLogger; import core.plugin.ClassScanner; +import static core.api.ContentAPIKt.log; + /** * The build option handling plugin. * @author Emperor @@ -63,8 +66,8 @@ public final class BuildOptionPlugin extends OptionHandler { } Hotspot hotspot = player.getHouseManager().getHotspot(object); if (hotspot == null || !isBuildable(player, object, hotspot)) { - System.out.println(hotspot == null); - SystemLogger.logErr(this.getClass(), "Construction (building): " + hotspot + " : " + object + " chunkX = " + object.getLocation().getChunkX() + ", chunkY = " + object.getLocation().getChunkY()); + //log demoted from ERR to WARN June 16, 2023 -> It's the effect of an already-known issue. Reduced visibility to clean up server logs while still making the issue apparent in dev environs + log(this.getClass(), Log.WARN, "Construction (building): " + hotspot + " : " + object + " chunkX = " + object.getLocation().getChunkX() + ", chunkY = " + object.getLocation().getChunkY()); return true; } @@ -178,4 +181,4 @@ public final class BuildOptionPlugin extends OptionHandler { } -} \ No newline at end of file +} diff --git a/Server/src/main/content/global/skill/construction/BuildRoomDialogue.java b/Server/src/main/content/global/skill/construction/BuildRoomDialogue.java index e1b351365..b74e99cb3 100644 --- a/Server/src/main/content/global/skill/construction/BuildRoomDialogue.java +++ b/Server/src/main/content/global/skill/construction/BuildRoomDialogue.java @@ -12,6 +12,7 @@ import core.plugin.Initializable; import core.game.dialogue.DialogueInterpreter; import core.game.dialogue.DialoguePlugin; import core.game.node.entity.skill.Skills; +import org.rs09.consts.Items; import java.awt.*; import java.util.ArrayList; @@ -193,7 +194,7 @@ public final class BuildRoomDialogue extends DialoguePlugin { options("Rotate clockwise", "Rotate anticlockwise", "Build", "Cancel"); return true; case 3: - if (player.getInventory().remove(new Item(995, room.getProperties().getCost()))) { + if (player.getInventory().remove(new Item(Items.COINS_995, room.getProperties().getCost()))) { room.setRotation(directions[index]); boolean[] exit = new boolean[exits.length]; for (int i = 0; i < exit.length; i++) { diff --git a/Server/src/main/content/global/skill/construction/BuildingUtils.java b/Server/src/main/content/global/skill/construction/BuildingUtils.java index 27708ba49..02bfbd9c2 100644 --- a/Server/src/main/content/global/skill/construction/BuildingUtils.java +++ b/Server/src/main/content/global/skill/construction/BuildingUtils.java @@ -16,12 +16,16 @@ import core.game.world.update.flag.context.Animation; import core.net.packet.PacketRepository; import core.net.packet.context.ContainerContext; import core.net.packet.out.ContainerPacket; +import core.tools.Log; import org.jetbrains.annotations.NotNull; import core.tools.SystemLogger; +import org.rs09.consts.Items; import java.util.ArrayList; import java.util.Arrays; +import static core.api.ContentAPIKt.*; + /** * Utility class for building. * @author Emperor @@ -64,7 +68,7 @@ public final class BuildingUtils { /** * The plank item. */ - public static final Item PLANK = new Item(960); + public static final Item PLANK = new Item(Items.PLANK_960); /** * The watering can(8) item id. @@ -131,12 +135,12 @@ public final class BuildingUtils { if (hasRequirements) { c261Value += (1 << (menuIndex + 1)); } - player.getConfigManager().set(1485 + menuIndex, hasRequirements || player.isStaff() ? 1 : 0); + setVarp(player, 1485 + menuIndex, hasRequirements || player.isStaff() ? 1 : 0); player.getPacketDispatch().sendString("Level " + decoration.getLevel(), 396, 140 + menuIndex); //player.getPacketDispatch().sendItemZoomOnInterface(items[i].protocol(), 50000, 396, 49 + i); } - System.out.println(c261Value); - player.getConfigManager().set(261, c261Value); + + setVarp(player, 261, c261Value); PacketRepository.send(ContainerPacket.class, new ContainerContext(player, 396, 132, 8, items, false)); } @@ -201,7 +205,6 @@ public final class BuildingUtils { for (int i = 7; i >= 0; i--) { Item can = player.getInventory().getItem(new Item(WATERING_CAN - i, 1)); if (can != null && can.getSlot() > -1) { - System.out.println("Can index " + (i == 7 ? i + 2 : i + 1) + "."); player.getInventory().replace(new Item(WATERING_CAN - (i == 7 ? i + 2 : i + 1), 1), can.getSlot()); break; } @@ -288,7 +291,7 @@ public final class BuildingUtils { } } } else { - SystemLogger.logErr(BuildingUtils.class, "Couldn't find stairs! " + plane); + log(BuildingUtils.class, Log.ERR, "Couldn't find stairs! " + plane); } } } @@ -346,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(); @@ -366,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; } } @@ -408,7 +415,7 @@ public final class BuildingUtils { } } } else { - SystemLogger.logErr(BuildingUtils.class, "Couldn't find stairs! " + plane); + log(BuildingUtils.class, Log.ERR, "Couldn't find stairs! " + plane); } } } @@ -550,24 +557,17 @@ public final class BuildingUtils { } } //(0=east, 1=south, 2=west, 3=north). - // System.out.println("Roof information: index=" + index + ", exit count=" + count + "; array=" + Arrays.toString(exits)); + if (count == 4) { room = Room.create(player, RoomProperties.ROOF_4_EXIT); } else if (count == 3) { room = Room.create(player, RoomProperties.ROOF_3_EXIT); - room.setRotation(DIRECTIONS[(index + 2) % 4]); } else if ((exits[0] != exits[2]) || (exits[1] != exits[3])) { room = Room.create(player, RoomProperties.ROOF_3_EXIT); - if (exits[2]) { - room.setRotation(DIRECTIONS[exits[3] ? 3 : 1]); - } } else { room = Room.create(player, RoomProperties.ROOF_2_EXIT); - if (exits[0] || exits[2]) { - room.setRotation(DIRECTIONS[1]); - } } player.getHouseManager().getRooms()[z + 1][x][y] = room; } @@ -655,46 +655,25 @@ public final class BuildingUtils { * Gets the exit requirements for the given room. * @param roomX The room x-coordinate. * @param roomY The room y-coordinate. - * @return The disabled exit indexes. + * @return The requirements on the exit indexes. -1 if exit must be absent, +1 if exit must be present. */ private static int[] getExitRequirements(Player player, int z, int roomX, int roomY) { int[] exits = new int[4]; - if (roomX == 0) { - exits[2] = -1; - } - else if (player.getHouseManager().hasExit(z, roomX - 1, roomY, Direction.EAST)) { + int deltaX = roomX - player.getLocation().getChunkX(); + int deltaY = roomY - player.getLocation().getChunkY(); + if (deltaX > 0) { + // player is to our west exits[2] = 1; - } - else if (player.getHouseManager().hasRoomAt(z, roomX - 1, roomY)) { - exits[2] = -1; - } - if (roomY == 7) { - exits[3] = -1; - } - else if (player.getHouseManager().hasExit(z, roomX, roomY + 1, Direction.SOUTH)) { - exits[3] = 1; - } - else if (player.getHouseManager().hasRoomAt(z, roomX, roomY + 1)) { - exits[3] = -1; - } - if (roomX == 7) { - exits[0] = -1; - } - else if (player.getHouseManager().hasExit(z, roomX + 1, roomY, Direction.WEST)) { + } else if (deltaX < 0) { + // player is to our east exits[0] = 1; - } - else if (player.getHouseManager().hasRoomAt(z, roomX + 1, roomY)) { - exits[0] = -1; - } - if (roomY == 0) { - exits[1] = -1; - } - else if (player.getHouseManager().hasExit(z, roomX, roomY - 1, Direction.NORTH)) { + } else if (deltaY > 0) { + // player is to our south exits[1] = 1; - } - else if (player.getHouseManager().hasRoomAt(z, roomX, roomY - 1)) { - exits[1] = -1; + } else if (deltaY < 0) { + // player is to our north + exits[3] = 1; } return exits; } -} \ No newline at end of file +} diff --git a/Server/src/main/content/global/skill/construction/ConstructionGuideBook.java b/Server/src/main/content/global/skill/construction/ConstructionGuideBook.java deleted file mode 100644 index 50fc75abc..000000000 --- a/Server/src/main/content/global/skill/construction/ConstructionGuideBook.java +++ /dev/null @@ -1,104 +0,0 @@ -package content.global.skill.construction; - - -import core.game.dialogue.DialogueInterpreter; -import core.game.dialogue.DialoguePlugin; -import core.game.dialogue.book.Book; -import core.game.dialogue.book.Page; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; -import core.game.world.GameWorld; -import core.plugin.Initializable; - -/** - * Handles the construction guide book. - * @author Emperor - * - */ -@Initializable -public class ConstructionGuideBook extends Book { - - /** - * The resources used in beta. - */ - private static final Item[] RESOURCES = { - new Item(8794), - new Item(2347), - new Item(4819, 500000), - new Item(4820, 500000), - new Item(1539, 500000), - new Item(4821, 500000), - new Item(4822, 500000), - new Item(4823, 500000), - new Item(4824, 500000), - new Item(961, 500000), - new Item(8779, 500000), - new Item(8781, 500000), - new Item(8783, 500000), - new Item(8791, 500000), - new Item(8785, 500000), - new Item(8787, 500000), - new Item(8789, 500000), //16 - new Item(8418, 500000), - new Item(8420, 500000), - new Item(8422, 500000), - new Item(8424, 500000), - new Item(8426, 500000), - new Item(8428, 500000), - new Item(8430, 500000), - new Item(8432, 500000), - new Item(8434, 500000), - new Item(8436, 500000), - }; - - /** - * Constructs a new {@code ConstructionGuideBook} {@code Object}. - */ - public ConstructionGuideBook() { - /* - * empty. - */ - } - - /** - * Constructs a new {@code ConstructionGuideBook} {@code Object}. - * @param player - */ - public ConstructionGuideBook(Player player) { - super(player, "Construction guide book", DialogueInterpreter.getDialogueKey("book:conguide")); - } - - @Override - public boolean open(final Player player) { - if (GameWorld.getSettings().isDevMode() && GameWorld.getSettings().isBeta()) { - for (Item item : RESOURCES) { - if (!player.getInventory().contains(item.getId(), item.getAmount())) { - player.getInventory().add(item, player); - } - } - } - interpreter.sendPlainMessage(false, "Upon reading the book you discover you're supposed to", "use these resources to test out construction.", "Report all bugs on the forums."); - return true; - } - - @Override - public void finish() { - - } - - @Override - public void display(Page[] set) { - - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new ConstructionGuideBook(player); - } - - @Override - public int[] getIds() { - return new int[] { DialogueInterpreter.getDialogueKey("book:conguide") }; - } - -} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/construction/ConstructionGuideBook.kt b/Server/src/main/content/global/skill/construction/ConstructionGuideBook.kt new file mode 100644 index 000000000..a6c8beeae --- /dev/null +++ b/Server/src/main/content/global/skill/construction/ConstructionGuideBook.kt @@ -0,0 +1,68 @@ +package content.global.skill.construction + +import core.api.sendMessage +import core.game.dialogue.DialogueInterpreter +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.item.Item +import core.game.world.GameWorld.settings +import org.rs09.consts.Items + +/** + * Construction Guide Book + * Used to be book:conguide? + * @author ovenbreado + * @author Emperor + */ + +class ConstructionGuideBook : InteractionListener { + companion object { + private val TITLE = "Construction guide book" + /** The resources used in beta. */ + private val RESOURCES = arrayOf( + Item(Items.SAW_8794), + Item(Items.HAMMER_2347), + Item(Items.BRONZE_NAILS_4819, 500000), + Item(Items.IRON_NAILS_4820, 500000), + Item(Items.STEEL_NAILS_1539, 500000), + Item(Items.BLACK_NAILS_4821, 500000), + Item(Items.MITHRIL_NAILS_4822, 500000), + Item(Items.ADAMANTITE_NAILS_4823, 500000), + Item(Items.RUNE_NAILS_4824, 500000), + Item(Items.PLANK_961, 500000), + Item(Items.OAK_PLANK_8779, 500000), + Item(Items.TEAK_PLANK_8781, 500000), + Item(Items.MAHOGANY_PLANK_8783, 500000), + Item(Items.BOLT_OF_CLOTH_8791, 500000), + Item(Items.GOLD_LEAF_8785, 500000), + Item(Items.MARBLE_BLOCK_8787, 500000), + Item(Items.MAGIC_STONE_8789, 500000), // 16 + Item(Items.BAGGED_DEAD_TREE_8418, 500000), + Item(Items.BAGGED_NICE_TREE_8420, 500000), + Item(Items.BAGGED_OAK_TREE_8422, 500000), + Item(Items.BAGGED_WILLOW_TREE_8424, 500000), + Item(Items.BAGGED_MAPLE_TREE_8426, 500000), + Item(Items.BAGGED_YEW_TREE_8428, 500000), + Item(Items.BAGGED_MAGIC_TREE_8430, 500000), + Item(Items.BAGGED_PLANT_1_8432, 500000), + Item(Items.BAGGED_PLANT_2_8434, 500000), + Item(Items.BAGGED_PLANT_3_8436, 500000) + ) + } + + override fun defineListeners() { + // There is supposedly a book here. + on(Items.CONSTRUCTION_GUIDE_8463, IntType.ITEM, "read") { player, _ -> + if (settings!!.isDevMode && settings!!.isBeta) { + for (item in RESOURCES) { + if (!player.inventory.contains(item.id, item.amount)) { + player.inventory.add(item, player) + } + } + } + sendMessage(player, "Upon reading the book you discover you're supposed to use these resources to test out construction. Report all bugs on the forums.") + return@on true + } + } + +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/construction/ConstructionInterface.java b/Server/src/main/content/global/skill/construction/ConstructionInterface.java index e0dfe1c89..a36b0329b 100644 --- a/Server/src/main/content/global/skill/construction/ConstructionInterface.java +++ b/Server/src/main/content/global/skill/construction/ConstructionInterface.java @@ -10,8 +10,11 @@ import core.game.node.entity.skill.Skills; import core.game.node.scenery.Scenery; import core.plugin.Initializable; import core.plugin.Plugin; +import core.tools.Log; import core.tools.SystemLogger; +import static core.api.ContentAPIKt.log; + /** * Handles the creating of a decoration object. * @author Emperor @@ -38,12 +41,12 @@ public final class ConstructionInterface extends ComponentPlugin { Hotspot hotspot = player.getAttribute("con:hotspot"); Scenery object = player.getAttribute("con:hsobject"); if (hotspot == null || object == null) { - SystemLogger.logErr(this.getClass(), "Failed building decoration " + hotspot + " : " + object); + log(this.getClass(), Log.ERR, "Failed building decoration " + hotspot + " : " + object); break; } slot = ((slot % 2 != 0) ? 4 : 0) + (slot >> 1); if (slot >= hotspot.getHotspot().getDecorations().length) { - SystemLogger.logErr(this.getClass(), "Failed building decoration " + slot + "/" + hotspot.getHotspot().getDecorations().length); + log(this.getClass(), Log.ERR, "Failed building decoration " + slot + "/" + hotspot.getHotspot().getDecorations().length); break; } boolean debug = player.isStaff(); @@ -104,7 +107,7 @@ public final class ConstructionInterface extends ComponentPlugin { break; case 402: int index = button - 160; - SystemLogger.logErr(this.getClass(), "BuildRoom Interface Index: " + index); + log(this.getClass(), Log.FINE, "BuildRoom Interface Index: " + index); if (index > -1 && index < RoomProperties.values().length) { player.getDialogueInterpreter().open("con:room", RoomProperties.values()[index]); return true; 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 3d1c1159c..95ad2c9b3 100644 --- a/Server/src/main/content/global/skill/construction/Decoration.java +++ b/Server/src/main/content/global/skill/construction/Decoration.java @@ -1,1078 +1,696 @@ package content.global.skill.construction; - - import core.game.node.entity.player.Player; import core.game.node.item.Item; import core.game.node.scenery.Scenery; import core.game.world.map.Location; +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(2351, 10)), - ROCK(13406, 8169, 5, 100.0, new Item(3420, 5)), - POND(13407, 8170, 10, 100.0, new Item(1761, 10)), - IMP_STATUE(13408, 8171, 15, 150.0, new Item(3420, 5), new Item(1761, 5)), - SMALL_OBELISK(42004, 14657, 41, 676, new Item(8786, 1), new Item(12183, 1000), new Item(12160, 10), new Item(12163, 10)), - DUNGEON_ENTRANCE(13409, 8172, 70, 500.0, new Item(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(8417)), - BIG_TREE(13412, 8174, 10, 44.0, new int[] {BuildingUtils.WATERING_CAN}, new Item(8419)), - BIG_OAK_TREE(13413, 8175, 15, 70.0, new int[] {BuildingUtils.WATERING_CAN}, new Item(8421)), - BIG_WILLOW_TREE(13414, 8176, 30, 100.0, new int[] {BuildingUtils.WATERING_CAN}, new Item(8423)), - BIG_MAPLE_TREE(13415, 8177, 45, 122.0, new int[] {BuildingUtils.WATERING_CAN}, new Item(8425)), - BIG_YEW_TREE(13416, 8178, 60, 141.0, new int[] {BuildingUtils.WATERING_CAN}, new Item(8427)), - BIG_MAGIC_TREE(13417, 8179, 75, 223.0, new int[] {BuildingUtils.WATERING_CAN}, new Item(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(8417)), - TREE(13419, 8174, 10, 44.0, new int[] {BuildingUtils.WATERING_CAN}, new Item(8419)), - OAK_TREE(13420, 8175, 15, 70.0, new int[] {BuildingUtils.WATERING_CAN}, new Item(8421)), - WILLOW_TREE(13421, 8176, 30, 100.0, new int[] {BuildingUtils.WATERING_CAN}, new Item(8423)), - MAPLE_TREE(13423, 8177, 45, 122.0, new int[] {BuildingUtils.WATERING_CAN}, new Item(8425)), - YEW_TREE(13422, 8178, 60, 141.0, new int[] {BuildingUtils.WATERING_CAN}, new Item(8427)), - MAGIC_TREE(13424, 8179, 75, 223.0, new int[] {BuildingUtils.WATERING_CAN}, new Item(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(8431)), - BUSH(13426, 8187, 6, 70.0, new int[] {BuildingUtils.WATERING_CAN}, new Item(8433)), - TALL_PLANT(13427, 8188, 12, 100.0, new int[] {BuildingUtils.WATERING_CAN}, new Item(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(8431)), - LARGE_LEAF_PLANT(13429, 8190, 6, 70.0, new int[] {BuildingUtils.WATERING_CAN}, new Item(8433)), - HUGE_PLANT(13430, 8191, 12, 100.0, new int[] {BuildingUtils.WATERING_CAN}, new Item(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(8431)), - SMALL_FERN(13432, 8181, 6, 70.0, new int[] {BuildingUtils.WATERING_CAN}, new Item(8433)), - FERN_SP(13433, 8182, 12, 100.0, new int[] {BuildingUtils.WATERING_CAN}, new Item(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(8431)), - THISTLE(13435, 8184, 6, 70.0, new int[] {BuildingUtils.WATERING_CAN}, new Item(8433)), - REEDS(13436, 8185, 12, 100.0, new int[] {BuildingUtils.WATERING_CAN}, new Item(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(960, 2)), - WOODEN_CHAIR(13582, 8310, 8, 96.0, new Item(960, 3)), - ROCKING_CHAIR(13583, 8311, 14, 96.0, new Item(960, 3)), - OAK_CHAIR(13584, 8312, 19, 120.0, new Item(8778, 2)), - OAK_ARMCHAIR(13585, 8313, 26, 180.0, new Item(8778, 3)), - TEAK_ARMCHAIR(13586, 8314, 35, 180.0, new Item(8780, 2)), - MAHOGANY_ARMCHAIR(13587, 8315, 50, 280.0, new Item(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(8790, 2)), - RED_RUG_CORNER(13591, 8317, 13, 60.0, new Item(8790, 4)), - OPULENT_RUG_CORNER(13594, 8318, 65, 360.0, new Item(8790, 4), new Item(8784, 1)), - - BROWN_RUG_END(13589, 8316, 2, 30.0, new Item(8790, 2)), - RED_RUG_END(13592, 8317, 13, 60.0, new Item(8790, 4)), - OPULENT_RUG_END(13595, 8318, 65, 360.0, new Item(8790, 4), new Item(8784, 1)), - - BROWN_RUG_CENTER(13590, 8316, 2, 30.0, new Item(8790, 2)), - RED_RUG_CENTER(13593, 8317, 13, 60.0, new Item(8790, 4)), - OPULENT_RUG_CENTER(13596, 8318, 65, 360.0, new Item(8790, 4), new Item(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(1761, 3)), - STONE_FIREPLACE(13611, 8326, 33, 40.0, new Item(3420, 2)), - MARBLE_FIREPLACE(13613, 8327, 63, 500.0, new Item(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(960, 3), new Item(8790, 3)), - CURTAINS(13604, 8323, 18, 225.0, new Item(8778, 3), new Item(8790, 3)), - OPULENT_CURTAINS(13605, 8324, 40, 315.0, new Item(8780, 3), new Item(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(960, 4)), - OAK_BOOKCASE(13598, 8320, 29, 225.0, new Item(8778, 3)), - MAHOGANY_BOOKCASE(13599, 8321, 40, 315.0, new Item(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(960, 3)), - CIDER_BARREL(13569, 8240, 12, 91.0, new Item(960, 3), new Item(5763, 8)), - ASGARNIAN_ALE_BARREL(13570, 8241, 18, 184.0, new Item(8778, 3), new Item(1905, 8)), - GREENMANS_ALE_BARREL(13571, 8242, 26, 184.0, new Item(8778, 3), new Item(1909, 8)), - DRAGON_BITTER_BARREL(13572, 8243, 36, 224.0, new Item(8778, 3), new Item(1911, 8), new Item(2353, 2)), - CHEFS_DELIGHT_BARREL(13573, 8244, 48, 224.0, new Item(8778, 3), new Item(5755, 8), new Item(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(960, 3)), - KITCHEN_OAK_TABLE(13578, 8247, 32, 180.0, new Item(8778, 3)), - KITCHEN_TEAK_TABLE(13579, 8248, 52, 270.0, new Item(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(1761, 2), new Item(2353, 1)), - FIREPIT_WITH_HOOK(13529, 8217, 11, 60.0, new Item(1761, 2), new Item(2353, 2)), - FIREPIT_WITH_POT(13531, 8218, 17, 80.0, new Item(1761, 2), new Item(2353, 3)), - SMALL_OVEN(13533, 8219, 24, 80.0, new Item(2353, 4)), - LARGE_OVEN(13536, 8220, 29, 100.0, new Item(2353, 5)), - BASIC_RANGE(13539, 8221, 34, 120.0, new Item(2353, 6)), - FANCY_RANGE(13542, 8222, 42, 160.0, new Item(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(960, 8)), - OAK_LARDER(13566, 8234, 33, 480.0, new Item(8778, 8)), - TEAK_LARDER(13567, 8235, 43, 750.0, new Item(8780, 8), new Item(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(960, 3)), - WOODEN_SHELVES_2(13546, 8224, 12, 147.0, new Item(960, 3), new Item(1761, 6)), - WOODEN_SHELVES_3(13547, 8225, 23, 147.0, new Item(960, 3), new Item(1761, 6)), - OAK_SHELVES_1(13548, 8226, 34, 240.0, new Item(8778, 3), new Item(1761, 6)), - OAK_SHELVES_2(13549, 8227, 45, 240.0, new Item(8778, 3), new Item(1761, 6)), - TEAK_SHELVES_1(13550, 8228, 56, 330.0, new Item(8780, 3), new Item(1761, 6)), - TEAK_SHELVES_2(13551, 8229, 67, 930.0, new Item(8780, 3), new Item(1761, 6), new Item(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(2353, 5)), - PUMP_AND_TUB(13561, 8231, 27, 200.0, new Item(2353, 10)), - SINK(13563, 8232, 47, 300.0, new Item(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(8790, 1)), - CAT_BASKET(13575, 8237, 19, 58.0, new Item(960, 2)), - CAST_BASKET_CUSHIONED(13576, 8238, 33, 58.0, new Item(960, 2), new Item(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(960, 4)), - DINING_TABLE_OAK(13294, 8247, 22, 240.0, new Item(8778, 4)), - DINING_TABLE_CARVED_OAK(13295, 8247, 31, 360.0, new Item(8778, 6)), - DINING_TABLE_TEAK(13296, 8248, 38, 360.0, new Item(8780, 4)), - DINING_TABLE_CARVED_TEAK(13297, 8248, 45, 600.0, new Item(8780, 6), new Item(8790, 4)), - DINING_TABLE_MAHOGANY(13298, 8120, 52, 840.0, new Item(8782, 6)), - DINING_TABLE_OPULENT(13299, 8121, 72, 3100.0, new Item(8782, 6), new Item(8790, 4), - new Item(8784, 4), new Item(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(960, 4)), - BENCH_OAK(13301, 8109, 22, 240.0, new Item(8778, 4)), - BENCH_CARVED_OAK(13302, 8110, 31, 240.0, new Item(8778, 4)), - BENCH_TEAK(13303, 8111, 38, 360.0, new Item(8780, 4)), - BENCH_CARVED_TEAK(13304, 8112, 44, 360.0, new Item(8780, 4)), - BENCH_MAHOGANY(13305, 8113, 52, 560.0, new Item(8782, 6)), - BENCH_GILDED(13306, 8114, 61, 1760.0, new Item(8782, 4), new Item(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(954, 1), new Item(8778, 1)), - BELL_PULL(13308, 8100, 19, 58.0, new Item(8780, 1), new Item(8790, 2)), - FANCY_BELL_PULL(13309, 8101, 33, 58.0, new Item(8780, 1), new Item(8790, 2), new Item(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(960, 1)), - WORKBENCH_OAK(13705, 8376, 32, 300.0, new Item(8778, 5)), - WORKBENCH_STEEL_FRAME(13706, 8377, 46, 440.0, new Item(8778, 6), new Item(2353, 4)), - WORKBENCH_WITH_VICE(13707, 8378, 62, 750.0, new Item(8377, 1), new Item(8778, 2), new Item(2353, 1)), - WORKBENCH_WITH_LATHE(13708, 8379, 77, 1000.0, new Item(8376, 1), new Item(8778, 2), new Item(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.OAK_PLANK_8778, 8), new Item(Items.STEEL_BAR_2353, 5) }), + WORKBENCH_WITH_LATHE (13708, 8379, 77, 1000, new Item[] { new Item(Items.OAK_PLANK_8778, 8), new Item(Items.STEEL_BAR_2353, 6) }), + /** * Workshop repair benches/stands */ - REPAIR_BENCH(13713, 8389, 15, 120.0, new Item(8778, 2)), - WHETSTONE(13714, 8390, 35, 260.0, new Item(8778, 4), new Item(3420, 1)), - ARMOUR_STAND(13715, 8391, 55, 500.0, new Item(8778, 8), new Item(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(8778, 2)), - SHIELD_EASEL(13717, 8393, 41, 240.0, new Item(8778, 4)), - BANNER_EASEL(13718, 8394, 66, 510.0, new Item(8778, 8), new Item(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(8778, 4)), - CRAFTING_TABLE_2(13710, 8381, 25, 100.0, new Item(1775, 1)), - CRAFTING_TABLE_3(13711, 8382, 34, 175.0, new Item(1775, 2)), - CRAFTING_TABLE_4(13712, 8383, 42, 240.0, new Item(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(8778, 2)), - TOOL_STORE_2(13700, 8385, 25, 120.0, new Item(8778, 2)), - TOOL_STORE_3(13701, 8386, 35, 120.0, new Item(8778, 2)), - TOOL_STORE_4(13702, 8387, 44, 120.0, new Item(8778, 2)), - TOOL_STORE_5(13703, 8388, 55, 120.0, new Item(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(8778, 2)), - TEAK_DECORATION(13606, 8103, 36, 180.0, new Item(8780, 2)), - GILDED_DECORATION(13607, 8104, 56, 1020.0, new Item(8782, 3), new Item(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(8778, 10), new Item(2353, 4)), - TEAK_STAIRCASE(13499, 8252, 48, 980.0, new Item(8780, 10), new Item(2353, 4)), - SPIRAL_STAIRCASE(13503, 8258, 67, 1040.0, new Item(8780, 10), new Item(3420, 7)), - MARBLE_STAIRCASE(13501, 8257, 82, 3200.0, new Item(8782, 5), new Item(8786, 5)), - MARBLE_SPIRAL(13505, 8259, 97, 4400.0, new Item(8780, 10), new Item(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(8778, 10), new Item(2353, 4)), - TEAK_STAIRS_DOWN(13500, 8252, 48, 980.0, new Item(8780, 10), new Item(2353, 4)), - SPIRAL_STAIRS_DOWN(13504, 8258, 67, 1040.0, new Item(8780, 10), new Item(3420, 7)), - MARBLE_STAIRS_DOWN(13502, 8257, 82, 3200.0, new Item(8782, 5), new Item(8786, 5)), - MARBLE_SPIRAL_DOWN(13506, 8259, 97, 4400.0, new Item(8780, 10), new Item(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(8780, 3)), - MAHOGANY_PORTAL(13637, 8329, 65, 420.0, new Item(8782, 3)), - MARBLE_PORTAL(13638, 8330, 80, 1500.0, new Item(8786, 3)), - TELEPORT_FOCUS(13640, 8331, 50, 40, new Item(3420, 2)), - GREATER_TELEPORT_FOCUS(13641, 8332, 65, 500.0, new Item(8786, 1)), - SCRYING_POOL(13639, 8333, 80, 2000.0, new Item(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(8778, 2), new Item(1159, 1), new Item(1121, 1), new Item(1085, 1)), - ADAMANT_ARMOUR(13492, 8271, 28, 150.0, new Item(8778, 2), new Item(1161, 1), new Item(1123, 1), new Item(1091, 1)), - RUNE_ARMOUR(13493, 8272, 28, 165.0, new Item(8778, 2),new Item(1163, 1), new Item(1127, 1), new Item(1093, 1)), - CRAWLING_HAND(13481, 8260, 38, 211.0, new Item(8780, 2), new Item(7982, 1)), - COCKATRICE_HEAD(13482, 8261, 38, 224.0, new Item(8780, 2), new Item(7983, 1)), - BASILISK_HEAD(13483, 8262, 38, 243.0, new Item(8780, 2), new Item(7984, 1)), - KURASK_HEAD(13484, 8263, 58, 357.0, new Item(8782, 2), new Item(7985, 1)), - ABYSSAL_DEMON_HEAD(13485, 8264, 58, 389.0, new Item(8782, 2), new Item(7986, 1)), - KBD_HEAD(13486, 8265, 78, 1103.0, new Item(8782, 2), new Item(7987, 1)), - KQ_HEAD(13487, 8266, 78, 1103.0, new Item(8782, 2), new Item(7988, 1)), - MOUNTED_BASS(13488, 8267, 36, 151.0, new Item(8778, 2), new Item(7990, 1)), - MOUNTED_SWORDFISH(13489, 8268, 56, 230.0, new Item(8780, 2), new Item(7992, 1)), - MOUNTED_SHARK(13490, 8269, 76, 350.0, new Item(8782, 2), new Item(7994, 1)), - RUNE_CASE1(13507, 8095, 41, 190.0, new Item(8780, 2), new Item(1775, 2), new Item(554, 1), new Item(556, 1), new Item(557, 1), new Item(555, 1)), - RUNE_CASE2(13508, 8095, 41, 212.0, new Item(8780, 2), new Item(1775, 2), new Item(559, 1), new Item(564, 1), new Item(562, 1), new Item(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(1761, 10)), - LIMESTONE_STONE(13393, 8154, 59, 200.0, new Item(3420, 10)), - MARBLE_STONE(13394, 8155, 79, 2000.0, new Item(8786, 4)), - HOOP_AND_STICK(13398, 8162, 30, 120.0, new Item(8778, 2)), - DARTBOARD(13400, 8163, 54, 290.0, new Item(8780, 3), new Item(2353, 1)), - ARCHERY_TARGET(13402, 8164, 81, 600.0, new Item(8780, 6), new Item(2353, 3)), - BALANCE_1(13395, 8156, 37, 176.0, new Item(554, 500), new Item(556, 500), new Item(557, 500), new Item(555, 500)), - BALANCE_2(13396, 8157, 57, 252.0, new Item(554, 1000), new Item(556, 1000), new Item(557, 1000), new Item(555, 1000)), - BALANCE_3(13397, 8158, 77, 356.0, new Item(554, 2000), new Item(556, 2000), new Item(557, 2000), new Item(555, 2000)), - OAK_CHEST(13385, 8165, 34, 240.0, new Item(8778, 4)), - TEAK_CHEST(13387, 8166, 44, 660.0, new Item(8780, 4), new Item(8784, 1)), - MAHOGANY_CHEST(13389, 8167, 54, 860.0, new Item(8782, 4), new Item(8784, 1)), - JESTER(13390, 8159, 39, 360.0, new Item(8780, 4)), - TREASURE_HUNT(13379, 8160, 49, 800.0, new Item(8780, 8), new Item(2353, 4)), - HANGMAN(13404, 8161, 59, 1200.0, new Item(8780, 12), new Item(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(8778, 6), new Item(8790, 4)), - FENCING_RING(13133, 8024, 41, 570.0, new Item(8770, 8), new Item(8790, 6)), - COMBAT_RING(13137, 8025, 51, 630.0, new Item(8780, 6), new Item(8790, 6)), - BALANCE_BEAM_LEFT(13143, 8027, 81, 1000.0, new Item(8780, 10), new Item(2353, 5)), - BALANCE_BEAM_CENTER(13142, 8027, 81, 1000.0, new Item(8780, 10), new Item(2353, 5)), - BALANCE_BEAM_RIGHT(13144, 8027, 81, 1000.0, new Item(8780, 10), new Item(2353, 5)), - RANGING_PEDESTALS(13147, 8026, 71, 720.0, new Item(8780, 8)), - MAGIC_BARRIER(13145, 8026, 71, 720.0, new Item(8780, 8)), - NOTHING(13721, 8027, 81, 1000.0, new Item(8780, 10), new Item(2353, 5)), - NOTHING2(13721, 8026, 71, 720.0, new Item(8780, 8)), - INVISIBLE_WALL(15283, 8023, 32, 570.0, new Item(8778, 6), new Item(8790, 4)), - INVISIBLE_WALL2(15284, 8023, 32, 570.0, new Item(8778, 6), new Item(8790, 4)), - INVISIBLE_WALL3(15285, 8023, 32, 570.0, new Item(8778, 6), new Item(8790, 4)), - GLOVE_RACK(13381, 8028, 34, 120.0, new Item(8778, 2)), - WEAPONS_RACK(13382, 8029, 44, 180.0, new Item(8780, 2)), - EXTRA_WEAPONS_RACK(13383, 8030, 54, 440.0, new Item(8780, 4), new Item(2353, 4)), - BOXING_MAT_CORNER(13126, 8023, 32, 570.0, new Item(8778, 6), new Item(8790, 4)), - FENCING_MAT_CORNER(13135, 8024, 41, 570.0, new Item(8770, 8), new Item(7991, 6)), - COMBAT_MAT_CORNER(13138, 8025, 51, 630.0, new Item(8780, 6), new Item(7991, 6)), - BOXING_MAT_SIDE(13128, 8023, 32, 570.0, new Item(8778, 6), new Item(8790, 4)), - FENCING_MAT_SIDE(13134, 8024, 41, 570.0, new Item(8770, 8), new Item(7991, 6)), - COMBAT_MAT_SIDE(13139, 8025, 51, 630.0, new Item(8780, 6), new Item(7991, 6)), - BOXING_MAT(13127, 8023, 32, 570.0, new Item(8778, 6), new Item(8790, 4)), - FENCING_MAT(13136, 8024, 41, 570.0, new Item(8770, 8), new Item(7991, 6)), - COMBAT_MAT(13140, 8025, 51, 630.0, new Item(8780, 6), new Item(7991, 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(8782, 8), new Item(2353, 4)), - SMALL_FOUNTAIN(13478, 8193, 71, 500, new Item(8786, 1)), - LARGE_FOUNTAIN(13479, 8194, 75, 1000, new Item(8786, 2)), - POSH_FOUNTAIN(13480, 8195, 81, 1500, new Item(8786, 3)), - SUNFLOWER(13446, 8213, 66, 70, new int[] {BuildingUtils.WATERING_CAN}, new Item(8457, 1)), - MARIGOLDS(13447, 8214, 71, 100, new int[] {BuildingUtils.WATERING_CAN}, new Item(8459, 1)), - ROSES(13448, 8215, 76, 122, new int[] {BuildingUtils.WATERING_CAN}, new Item(8461, 1)), - SUNFLOWER_BIG(13443, 8213, 66, 70, new int[] {BuildingUtils.WATERING_CAN}, new Item(8457, 1)), - MARIGOLDS_BIG(13444, 8214, 71, 100, new int[] {BuildingUtils.WATERING_CAN}, new Item(8459, 1)), - ROSES_BIG(13445, 8215, 76, 122, new int[] {BuildingUtils.WATERING_CAN}, new Item(8461, 1)), - ROSEMARY(13440, 8210, 66, 70, new int[] {BuildingUtils.WATERING_CAN}, new Item(8451, 1)), - DAFFODILS(13441, 8211, 71, 100, new int[] {BuildingUtils.WATERING_CAN}, new Item(8453, 1)), - BLUEBELLS(13442, 8212, 76, 122, new int[] {BuildingUtils.WATERING_CAN}, new Item(8455, 1)), - ROSEMARY_BIG(13437, 8210, 66, 70, new int[] {BuildingUtils.WATERING_CAN}, new Item(8451, 1)), - DAFFODILS_BIG(13438, 8211, 71, 100, new int[] {BuildingUtils.WATERING_CAN}, new Item(8453, 1)), - BLUEBELLS_BIG(13439, 8212, 76, 122, new int[] {BuildingUtils.WATERING_CAN}, new Item(8455, 1)), - THORNY_HEDGE1(13456, 8203, 56, 70, new int[] {BuildingUtils.WATERING_CAN}, new Item(8437, 1)), - THORNY_HEDGE2(13457, 8203, 56, 70, new int[] {BuildingUtils.WATERING_CAN}, new Item(8437, 1)), - THORNY_HEDGE3(13458, 8203, 56, 70, new int[] {BuildingUtils.WATERING_CAN}, new Item(8437, 1)), - NICE_HEDGE1(13459, 8204, 60, 100, new int[] {BuildingUtils.WATERING_CAN}, new Item(8439, 1)), - NICE_HEDGE2(13461, 8204, 60, 100, new int[] {BuildingUtils.WATERING_CAN}, new Item(8439, 1)), - NICE_HEDGE3(13460, 8204, 60, 100, new int[] {BuildingUtils.WATERING_CAN}, new Item(8439, 1)), - SMALL_BOX_HEDGE1(13462, 8205, 64, 122, new int[] {BuildingUtils.WATERING_CAN}, new Item(8441, 1)), - SMALL_BOX_HEDGE2(13464, 8205, 64, 122, new int[] {BuildingUtils.WATERING_CAN}, new Item(8441, 1)), - SMALL_BOX_HEDGE3(13463, 8205, 64, 122, new int[] {BuildingUtils.WATERING_CAN}, new Item(8441, 1)), - TOPIARY_HEDGE1(13465, 8206, 68, 141, new int[] {BuildingUtils.WATERING_CAN}, new Item(8443, 1)), - TOPIARY_HEDGE2(13467, 8206, 68, 141, new int[] {BuildingUtils.WATERING_CAN}, new Item(8443, 1)), - TOPIARY_HEDGE3(13466, 8206, 68, 141, new int[] {BuildingUtils.WATERING_CAN}, new Item(8443, 1)), - FANCY_HEDGE1(13468, 8207, 72, 158, new int[] {BuildingUtils.WATERING_CAN}, new Item(8445, 1)), - FANCY_HEDGE2(13470, 8207, 72, 158, new int[] {BuildingUtils.WATERING_CAN}, new Item(8445, 1)), - FANCY_HEDGE3(13469, 8207, 72, 158, new int[] {BuildingUtils.WATERING_CAN}, new Item(8445, 1)), - TALL_FANCY_HEDGE1(13471, 8208, 76, 223, new int[] {BuildingUtils.WATERING_CAN}, new Item(8447, 1)), - TALL_FANCY_HEDGE2(13473, 8208, 76, 223, new int[] {BuildingUtils.WATERING_CAN}, new Item(8447, 1)), - TALL_FANCY_HEDGE3(13472, 8208, 76, 223, new int[] {BuildingUtils.WATERING_CAN}, new Item(8447, 1)), - TALL_BOX_HEDGE1(13474, 8209, 80, 316, new int[] {BuildingUtils.WATERING_CAN}, new Item(8449, 1)), - TALL_BOX_HEDGE2(13476, 8209, 80, 316, new int[] {BuildingUtils.WATERING_CAN}, new Item(8449, 1)), - TALL_BOX_HEDGE3(13475, 8209, 80, 316, new int[] {BuildingUtils.WATERING_CAN}, new Item(8449, 1)), - BOUNDARY_STONES(13449, 8196, 55, 100, new Item(1761, 10)), - WOODEN_FENCE(13450, 8197, 59, 280, new Item(960, 10)), - STONE_WALL(13451, 8198, 63, 200, new Item(3420, 10)), - IRON_RAILINGS(13452, 8199, 67, 220, new Item(2351, 10), new Item(3420, 6)), - PICKET_FENCE(13453, 8200, 71, 640, new Item(8778, 10), new Item(2353, 2)), - GARDEN_FENCE(13454, 8201, 75, 940, new Item(8780, 10), new Item(2353, 2)), - MARBLE_WALL(13455, 8202, 79, 4000, new Item(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(960, 3), new Item(8790, 2)), - OAK_BED(13149, 8032, 30, 210, new Item(8778, 3), new Item(8790, 2)), - LARGE_OAK_BED(13150, 8033, 34, 330, new Item(8778, 5), new Item(8790, 2)), - TEAK_BED(13151, 8034, 40, 300, new Item(8780, 3), new Item(8790, 2)), - LARGE_TEAK_BED(13152, 8035, 45, 480, new Item(8780, 5), new Item(8790, 2)), - FOUR_POSTER(13153, 8036, 53, 450, new Item(8782, 3), new Item(8790, 2)), - GILDED_FOUR_POSTER(13154, 8037, 60, 1330, new Item(8782, 5), new Item(8790, 2), new Item(8784, 2)), - OAK_CLOCK(13169, 8052, 25, 142, new Item(8778, 2), new Item(8792, 1)), - TEAK_CLOCK(13170, 8053, 55, 202, new Item(8780, 2), new Item(8792, 1)), - GILDED_CLOCK(13171, 8054, 85, 602, new Item(8782, 2), new Item(8792, 1), new Item(8784, 1)), - SHAVING_STAND(13162, 8045, 21, 30, new Item(960, 1), new Item(1775, 1)), - OAK_SHAVING_STAND(13163, 8046, 29, 61, new Item(8778, 1), new Item(1775, 1)), - OAK_DRESSER(13164, 8047, 37, 121, new Item(8778, 2), new Item(1775, 1)), - TEAK_DRESSER(13165, 8048, 46, 181, new Item(8780, 2), new Item(1775, 1)), - FANCY_TEAK_DRESSER(13166, 8049, 56, 182, new Item(8780, 2), new Item(1775, 2)), - MAHOGANY_DRESSER(13167, 8050, 64, 281, new Item(8782, 2), new Item(1775, 1)), - GILDED_DRESSER(13168, 8051, 74, 582, new Item(8782, 2), new Item(1775, 2), new Item(8784, 1)), - SHOE_BOX(13155, 8038, 20, 58, new Item(960, 2)), - OAK_DRAWERS(13156, 8039, 27, 120, new Item(8778, 2)), - OAK_WARDROBE(13157, 8040, 39, 180, new Item(8778, 3)), - TEAK_DRAWERS(13158, 8041, 51, 180, new Item(8780, 2)), - TEAK_WARDROBE(13159, 8042, 63, 270, new Item(8780, 3)), - MAHOGANY_WARDROBE(13160, 8043, 75, 420, new Item(8782, 2)), - GILDED_WARDROBE(13161, 8044, 87, 720, new Item(8782, 2), new Item(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(8780, 3), new Item(1540, 1)), - AMULET_OF_GLORY(13523, 8283, 47, 290, new Item(8780, 3), new Item(1704, 1)), - CAPE_OF_LEGENDS(13524, 8284, 47, 300, new Item(8780, 3), new Item(1052, 1)), - KING_ARTHUR(13510, 8285, 35, 211, new Item(8780, 3), new Item(7995, 1)), - ELENA(13511, 8286, 35, 211, new Item(8780, 3), new Item(7996, 1)), - GIANT_DWARF(13512, 8287, 35, 211, new Item(8780, 3), new Item(7997, 1)), - MISCELLANIANS(13513, 8288, 35, 311, new Item(8780, 3), new Item(7998, 1)), - LUMBRIDGE(13517, 8289, 44, 314, new Item(8780, 3), new Item(8002, 1)), - THE_DESERT(13514, 8290, 44, 314, new Item(8780, 3), new Item(7999, 1)), - MORYTANIA(13518, 8291, 44, 314, new Item(8780, 3), new Item(8003, 1)), - KARAMJA(13516, 8292, 65, 464, new Item(8782, 3), new Item(8001, 1)), - ISAFDAR(13515, 8293, 65, 464, new Item(8782, 3), new Item(8000, 1)), - SILVERLIGHT(13519, 8279, 42, 187, new Item(8780, 3), new Item(2402, 1)), - EXCALIBUR(13521, 8280, 42, 194, new Item(8780, 3), new Item(35, 1)), - DARKLIGHT(13520, 8281, 42, 202, new Item(8780, 3), new Item(6746, 1)), - SMALL_MAP(13525, 8294, 38, 211, new Item(8780, 3), new Item(8004, 1)), - MEDIUM_MAP(13526, 8295, 58, 451, new Item(8782, 3), new Item(8005, 1)), - LARGE_MAP(13527, 8296, 78, 591, new Item(8782, 4), new Item(8006, 1)), - - /** - * Menagerie Decorations - */ - //OBELISK - MINI_OBELISK(44837, 15236, 41, 676, new Item(8786, 1), new Item(12183, 1000), new Item(12158, 10), new Item(12159, 10), new Item(12160, 10), new Item(12163, 10)) - //PET_FEEDER - , - OAK_PET_FEEDER(44834, 15233, 37, 240, new Item(8778, 4)), - TEAK_PET_FEEDER(44835, 15234, 52, 380, new Item(8780, 4)), - MAHOGANY_PET_FEEDER(44836, 15235, 67, 880, new Item(8782, 4), new Item(8784, 1)) - //PET_HOUSE - , - OAK_PET_HOUSE(44828, 15227, 37, 240, new Item(8778, 4)), - TEAK_PET_HOUSE(44829, 15228, 52, 380, new Item(8780, 4)), - MAHOGANY_PET_HOUSE(44830, 15229, 67, 580, new Item(8782, 4)), - CONSECRATED_PET_HOUSE(44831, 15230, 92, 1580, new Item(8782, 4), new Item(8788, 1)), - DESECRATED_PET_HOUSE(44832, 15231, 92, 1580, new Item(8782, 4), new Item(8788, 1)), - NATURAL_PET_HOUSE(44833, 15232, 92, 1580, new Item(8782, 4), new Item(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(8431, 1), new Item(8433, 1), new Item(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(8435, 3), new Item(8423, 1), new Item(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(1783, 10), new Item(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(556, 1000), new Item(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(554, 1000), new Item(557, 1000), new Item(8417, 1), new Item(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(8778, 3)), - ORNAMENTAL_GLOBE(13650, 8342, 50, 270, new Item(8780, 3)), - LUNAR_GLOBE(13651, 8343, 59, 570, new Item(8780, 3), new Item(8784, 1)), - CELESTIAL_GLOBE(13652, 8344, 68, 570, new Item(8780, 3), new Item(8784, 1)), - ARMILLARY_SPHERE(13653, 8345, 77, 960, new Item(8782, 2), new Item(8784, 2), new Item(2353, 4)), - SMALL_ORREY(13654, 8346, 86, 1320, new Item(8782, 3), new Item(8784, 3)), - LARGE_ORREY(13655, 8347, 95, 1420, new Item(8782, 3), new Item(8784, 5)), - OAK_LECTERN(13642, 8334, 40, 60, new Item(8778, 1)), - EAGLE_LECTERN(13643, 8335, 47, 120, new Item(8778, 2)), - DEMON_LECTERN(13644, 8336, 47, 120, new Item(8778, 2)), - TEAK_EAGLE_LECTERN(13645, 8337, 57, 180, new Item(8780, 2)), - TEAK_DEMON_LECTERN(13646, 8338, 57, 180, new Item(8780, 2)), - MAHOGANY_EAGLE_LECTERN(13647, 8339, 67, 580, new Item(8782, 2), new Item(8784, 1)), - MAHOGANY_DEMON_LECTERN(13648, 8340, 67, 580, new Item(8782, 2), new Item(8784, 1)), - CRYSTAL_BALL(13659, 8351, 42, 280, new Item(8780, 3), new Item(567, 1)), - ELEMENTAL_SPHERE(13660, 8352, 54, 580, new Item(8780, 3), new Item(567, 1), new Item(8784, 1)), - CRYSTAL_OF_POWER(13661, 8353, 66, 890, new Item(8782, 2), new Item(567, 1), new Item(8784, 2)), - ALCHEMICAL_CHART(13662, 8354, 43, 30, new Item(8790, 2)), - ASTRONOMICAL_CHART(13663, 8355, 63, 45, new Item(8790, 3)), - INFERNAL_CHART(13664, 8356, 83, 60, new Item(8790, 4)), - TELESCOPE1(13656, 8348, 44, 121, new Item(8778, 2), new Item(1775, 1)), - TELESCOPE2(13657, 8349, 64, 181, new Item(8780, 2), new Item(1775, 1)), - TELESCOPE3(13658, 8350, 84, 580, new Item(8782, 2), new Item(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(8778, 2)), - TEAK_TREASURE_CHEST(18806, 9840, 66, 180, new Item(8780, 2)), - MAHOGANY_TREASURE_CHEST(18808, 9841, 84, 280, new Item(8782, 2)), - OAK_ARMOUR_CASE(18778, 9826, 46, 180, new Item(8778, 3)), - TEAK_ARMOUR_CASE(18780, 9827, 64, 270, new Item(8780, 3)), - MGANY_ARMOUR_CASE(18782, 9828, 82, 420, new Item(8782, 3)), - OAK_MAGIC_WARDROBE(18784, 9829, 42, 240, new Item(8778, 4)), - C_OAK_MAGIC_WARDROBE(18786, 9830, 51, 360, new Item(8778, 6)), - TEAK_MAGIC_WARDROBE(18788, 9831, 60, 360, new Item(8780, 4)), - C_TEAK_MAGIC_WARDROBE(18790, 9832, 69, 540, new Item(8780, 6)), - MGANY_MAGIC_WARDROBE(18792, 9833, 78, 560, new Item(8782, 4)), - GILDED_MAGIC_WARDROBE(18794, 9834, 87, 860, new Item(8782, 4), new Item(8784, 1)), - MARBLE_MAGIC_WARDROBE(18796, 9835, 96, 500, new Item(8786, 1)), - OAK_CAPE_RACK(18766, 9817, 54, 240, new Item(8778, 4)), - TEAK_CAPE_RACK(18767, 9818, 63, 360, new Item(8780, 4)), - MGANY_CAPE_RACK(18768, 9819, 72, 560, new Item(8782, 4)), - GILDED_CAPE_RACK(18769, 9820, 81, 860, new Item(8782, 4), new Item(8784, 1)), - MARBLE_CAPE_RACK(18770, 9821, 90, 500, new Item(8786, 1)), - MAGIC_CAPE_RACK(18771, 9822, 99, 1000, new Item(8788, 1)), - OAK_TOY_BOX(18798, 9836, 50, 120, new Item(8778, 2)), - TEAK_TOY_BOX(18800, 9837, 68, 180, new Item(8780, 2)), - MAHOGANY_TOY_BOX(18802, 9838, 86, 280, new Item(8782, 2)), - OAK_COSTUME_BOX(18772, 9823, 44, 120, new Item(8778, 2)), - TEAK_COSTUME_BOX(18774, 9824, 62, 180, new Item(8780, 2)), - MAHOGANY_COSTUME_BOX(18776, 9825, 80, 280, new Item(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(8778, 4)), - TEAK_ALTAR(13182, 8063, 50, 360, new Item(8780, 4)), - CLOTH_ALTAR(13185, 8064, 56, 390, new Item(8780, 4), new Item(8790, 2)), - MAHOGANY_ALTAR(13188, 8065, 60, 590, new Item(8782, 4), new Item(8790, 2)), - LIMESTONE_ALTAR(13191, 8066, 64, 910, new Item(8782, 6), new Item(8790, 2), new Item(3420, 2)), - MARBLE_ALTAR(13194, 8067, 70, 1030, new Item(8786, 2), new Item(8790, 2)), - GILDED_ALTAR(13197, 8068, 75, 2230, new Item(8786, 2), new Item(8790, 2), new Item(8784, 4)), - SMALL_STATUE(13271, 8082, 49, 40, new Item(3420, 2)), - MEDIUM_STATUE(13272, 8083, 69, 500, new Item(8786, 1)), - LARGE_STATUE(13282, 8084, 89, 1500, new Item(8786, 3)), - WINDCHIMES(13214, 8079, 49, 323, new Item(8778, 4), new Item(2353, 4)), - BELLS(13215, 8080, 58, 480, new Item(8780, 4), new Item(2353, 6)), - ORGAN(13216, 8081, 69, 680, new Item(8782, 4), new Item(2353, 6)), - SARADOMIN_SYMBOL(13172, 8055, 48, 120, new Item(8778, 2)), - ZAMORAK_SYMBOL(13173, 8056, 48, 120, new Item(8778, 2)), - GUTHIX_SYMBOL(13174, 8057, 48, 120, new Item(8778, 2)), - SARADOMIN_ICON(13175, 8058, 59, 960, new Item(8780, 4), new Item(8784, 2)), - ZAMORAK_ICON(13176, 8059, 59, 960, new Item(8780, 4), new Item(8784, 2)), - GUTHIX_ICON(13177, 8060, 59, 960, new Item(8780, 4), new Item(8784, 2)), - ICON_OF_BOB(13178, 8061, 71, 1160, new Item(8782, 4), new Item(8784, 2)), - STEEL_TORCHES(13202, 8070, 45, 80, new Item(2353, 2)), - WOODEN_TORCHES(13200, 8069, 49, 58, new Item(960, 2)), - STEEL_CANDLESTICKS(13204, 8071, 53, 124, new Item(2353, 6), new Item(36, 6)), - GOLD_CANDLESTICKS(13206, 8072, 57, 46, new Item(2357, 6), new Item(36, 6)), - INCENSE_BURNERS(13208, 8073, 61, 280, new Item(8778, 4), new Item(2353, 2)), - MAHOGANY_BURNERS(13210, 8074, 65, 600, new Item(8782, 4), new Item(2353, 2)), - MARBLE_BURNERS(13212, 8075, 69, 1600, new Item(8786, 2), new Item(2353, 2)), - SHUTTERED_WINDOW(new int[] { 13253, 13226, 13235, 13244, 13217, 13262 }, 8076, 49, 228, new Item(960, 8)), - DECORATIVE_WINDOW(new int[] { 13254, 13227, 13236, 13245, 13218, 13263 }, 8077, 69, 200, new Item(1775, 8)), - STAINED_GLASS(new int[] { 13255, 13228, 13237, 13246, 13219, 13264 }, 8078, 89, 400, new Item(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(8778, 5), new Item(8786, 1)), - TEAK_THRONE(13666, 8358, 67, 1450, new Item(8780, 5), new Item(8786, 2)), - MAHOGANY_THRONE(13667, 8359, 74, 2200, new Item(8782, 5), new Item(8786, 3)), - GILDED_THRONE(13668, 8360, 81, 1700, new Item(8782, 5), new Item(8786, 2), new Item(8784, 3)), - SKELETON_THRONE(13669, 8361, 88, 7003, new Item(8788, 5), new Item(8786, 4), new Item(526, 5), new Item(964, 2)), - CRYSTAL_THRONE(13670, 8362, 95, 15000, new Item(8788, 15)), - DEMONIC_THRONE(13671, 8363, 99, 25000, new Item(8788, 25)), - OAK_LEVER(13672, 8364, 68, 300, new Item(8778, 5)), - TEAK_LEVER(13673, 8365, 78, 450, new Item(8780, 5)), - MAHOGANY_LEVER(13674, 8366, 88, 700, new Item(8782, 5)), - FLOOR_DECORATION(new int[] { 13689, 13686, 13687, 13688, 13684, 13685 }, 8370, 61, 700, new Item(8782, 5)), - STEEL_CAGE(new int[] { 13689, 13686, 13687, 13688, 13684, 13685 }, 8371, 68, 1100, new Item(8782, 5), new Item(2353, 20)), - FLOOR_TRAP(new int[] { 13689, 13686, 13687, 13688, 13684, 13685 }, 8372, 74, 770, new Item(8782, 5), new Item(8792, 10)), - MAGIC_CIRCLE(new int[] { 13689, 13686, 13687, 13688, 13684, 13685 }, 8373, 82, 2700, new Item(8782, 5), new Item(8788, 2)), - MAGIC_CAGE(new int[] { 13689, 13686, 13687, 13688, 13684, 13685 }, 8374, 89, 4700, new Item(8782, 5), new Item(8788, 4)), - OAK_TRAPDOOR(13675, 8367, 68, 300, new Item(8778, 5)), - TEAK_TRAPDOOR(13676, 8368, 78, 450, new Item(8780, 5)), - MAHOGANY_TRAPDOOR(13677, 8369, 88, 700, new Item(8782, 5)), - CARVED_TEAK_BENCH(13694, 8112, 44, 360, new Item(8780, 4)), - MAHOGANY_BENCH(13695, 8113, 52, 560, new Item(8782, 4)), - GILDED_BENCH(13696, 8114, 61, 1760, new Item(8782, 4), new Item(8784, 4)), - OAK_DECO(13798, 8102, 16, 120.0, new Item(8778, 2)), - TEAK_DECO(13814, 8103, 36, 180.0, new Item(8780, 2)), - GILDED_DECO(13782, 8104, 56, 1020.0, new Item(8782, 3), new Item(8784, 2)), - ROUND_SHIELD(13734, 8105, 66, 120, new Item(8778, 2)), - SQUARE_SHIELD(13766, 8106, 76, 360, new Item(8780, 4)), - KITE_SHIELD(13750, 8107, 86, 420, new Item(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(2353, 20), new Item(995, 50000)), - SPIKES_SIDE(13335, 8302, 65, 623, new Item(2353, 20), new Item(995, 50000)), - SPIKES_CORNER(13336, 8302, 65, 623, new Item(2353, 20), new Item(995, 50000)), - SPIKES_FL(13338, 8302, 65, 623, new Item(2353, 20), new Item(995, 50000)), - TENTACLE_MID(13331, 8303, 71, 326, new Item(1929, 20), new Item(995, 100000)), - TENTACLE_SIDE(13332, 8303, 71, 326, new Item(1929, 20), new Item(995, 100000)), - TENTACLE_CORNER(13333, 8303, 71, 326, new Item(1929, 20), new Item(995, 100000)), - TENTACLE_FL(13338, 8303, 71, 326, new Item(1929, 20), new Item(995, 100000)), - FP_FLOOR_MID(13371, 8304, 77, 357, new Item(590, 20), new Item(995, 125000)), - FP_FLOOR_SIDE(13371, 8304, 77, 357, new Item(590, 20), new Item(995, 125000)), - FP_FLOOR_CORNER(13371, 8304, 77, 357, new Item(590, 20), new Item(995, 125000)), - FLAME_PIT(13337, 8304, 77, 357, new Item(590, 20), new Item(995, 125000)), - ROCNAR_FLOOR_MID(13371, 8305, 83, 387, new Item(995, 150000)), - ROCNAR_FLOOR_SIDE(13371, 8305, 83, 387, new Item(995, 150000)), - ROCNAR_FLOOR_CORNER(13371, 8305, 83, 387, new Item(995, 150000)), - ROCNAR(13373, 8305, 83, 387, new Item(995, 150000)), - ROCNAR_FL(13338, 8305, 83, 387, new Item(995, 150000)), - OAK_CAGE(13313, 8297, 65, 640, new Item(8778, 10), new Item(2353, 2)), - OAK_CAGE_DOOR(13314, 8297, 65, 640, new Item(8778, 10), new Item(2353, 2)), - OAK_STEEL_CAGE(13316, 8298, 70, 800, new Item(8778, 10), new Item(2353, 10)), - OAK_STEEL_CAGE_DOOR(13317, 8298, 70, 800, new Item(8778, 10), new Item(2353, 10)), - STEEL_CAGE_OU(13319, 8299, 75, 400, new Item(2353, 20)), - STEEL_CAGE_DOOR(13320, 8299, 75, 400, new Item(2353, 20)), - SPIKED_CAGE(13322, 8300, 80, 500, new Item(2353, 25)), - SPIKED_CAGE_DOOR(13323, 8300, 80, 500, new Item(2353, 25)), - BONE_CAGE(13325, 8301, 85, 603, new Item(8778, 10), new Item(526, 10)), - BONE_CAGE_DOOR(13326, 8301, 85, 603, new Item(8778, 10), new Item(526, 10)), - SKELETON_GUARD(13366, 8131, 70, 223, new Item(995, 50000)), - GUARD_DOG(13367, 8132, 74, 273, new Item(995, 75000)), - HOBGOBLIN(13368, 8133, 78, 316, new Item(995, 100000)), - BABY_RED_DRAGON(13372, 8134, 82, 387, new Item(995, 150000)), - HUGE_SPIDER(13370, 8135, 86, 447, new Item(995, 200000)), - TROLL(13369, 8136, 90, 1000, new Item(995, 1000000)), - HELLHOUND(2715, 8137, 94, 2236, new Item(995, 5000000)), - OAK_LADDER(13328, 8306, 68, 300, new Item(8778, 5)), - TEAK_LADDER(13329, 8307, 78, 450, new Item(8780, 5)), - MAHOGANY_LADDER(13330, 8308, 88, 700, new Item(8782, 5)), - DECORATIVE_BLOOD(13312, 8125, 72, 4, new Item(1763, 4)), - DECORATIVE_PIPE(13311, 8126, 83, 120, new Item(2353, 6)), - HANGING_SKELETON(13310, 8127, 94, 3, new Item(964, 2), new Item(526, 6)), - CANDLE(13342, 8128, 72, 243, new Item(8778, 4), new Item(33, 4)), - TORCH(13341, 8129, 84, 244, new Item(8778, 4), new Item(594, 4)), - SKULL_TORCH(13343, 8130, 94, 246, new Item(8778, 4), new Item(594, 4), new Item(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(8778, 10)), - OAK_DOOR_RIGHT(13345, 8122, 74, 600, new Item(8778, 10)), - STEEL_DOOR_LEFT(13346, 8123, 84, 800, new Item(8778, 10), new Item(2353, 10)), - STEEL_DOOR_RIGHT(13347, 8123, 84, 800, new Item(8778, 10), new Item(2353, 10)), - MARBLE_DOOR_LEFT(13348, 8124, 94, 2000, new Item(8786, 4)), - MARBLE_DOOR_RIGHT(13349, 8124, 94, 2000, new Item(8786, 4)), - SPIKE_TRAP(13356, 8143, 72, 223, new Item(995, 50000)), - MAN_TRAP(13357, 8144, 76, 273, new Item(995, 75000)), - TANGLE_TRAP(13358, 8145, 80, 316, new Item(995, 100000)), - MARBLE_TRAP(13359, 8146, 84, 387, new Item(995, 150000)), - TELEPORT_TRAP(13360, 8147, 88, 447, new Item(995, 200000)), - - /* objID, int, lvl, exp, materials */ - PIT_DOG(39260, 18791, 70, 200, new Item(995, 40000)), - PIT_OGRE(39261, 18792, 73, 234, new Item(995, 55000)), - PIT_ROCK_PROTECTOR(39262, 18793, 79, 300, new Item(995, 90000)), - PIT_SCABARITE(39263, 18794, 84, 387, new Item(995, 150000)), - PIT_BLACK_DEMON(39264, 18795, 89, 547, new Item(995, 300000)), - PIT_IRON_DRAGON(39265, 18796, 97, 2738, new Item(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(995, 500000)), - KALPHITE_SOLDIER(13374, 8139, 80, 866, new Item(995, 750000)), - TOK_XIL(13377, 8140, 85, 2236, new Item(995, 5000000)), - DAGANNOTH(13376, 8141, 90, 2738, new Item(995, 7500000)), - STEEL_DRAGON(13375, 8142, 95, 3162, new Item(995, 1000000)), - WOODEN_CRATE(13283, 8148, 75, 143, new Item(960, 5)), - OAK_T_CHEST(13285, 8149, 79, 340, new Item(8778, 5), new Item(2353, 2)), - TEAK_T_CHEST(13287, 8150, 83, 530, new Item(8780, 5), new Item(2353, 4)), - MGANY_T_CHEST(13289, 8151, 87, 1000, new Item(8782, 5), new Item(8784, 1)), - MAGIC_CHEST(13291, 8152, 91, 1000, new Item(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. */ @@ -1082,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. */ @@ -1102,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; @@ -1136,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; @@ -1176,6 +853,7 @@ public enum Decoration { this.experience = experience; this.tools = tools; this.items = items; + this.refundItems = refundItems; } /** @@ -1202,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 @@ -1216,7 +894,7 @@ public enum Decoration { } return null; } - + public static Decoration forName(String name) { for (Decoration d : Decoration.values()) { if (d.name().equals(name)) { @@ -1238,7 +916,7 @@ public enum Decoration { } return 0; } - + /** * Gets the objectId. * @param style The current housing style. @@ -1250,7 +928,7 @@ public enum Decoration { } return objectId; } - + /** * Gets the objectId. * @return The objectId. @@ -1271,7 +949,7 @@ public enum Decoration { * Gets the experience. * @return The experience. */ - public double getExperience() { + public int getExperience() { return experience; } @@ -1283,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. @@ -1307,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; } - - - -} \ No newline at end of file +} diff --git a/Server/src/main/content/global/skill/construction/EstateAgentDialogue.kt b/Server/src/main/content/global/skill/construction/EstateAgentDialogue.kt index b25f9aab3..efba1ad00 100644 --- a/Server/src/main/content/global/skill/construction/EstateAgentDialogue.kt +++ b/Server/src/main/content/global/skill/construction/EstateAgentDialogue.kt @@ -131,7 +131,7 @@ class EstateAgentDialogue : core.game.dialogue.DialoguePlugin { 7 -> { if (player.inventory.contains(995, 1000)) { - player.inventory.remove(Item(995, 1000)) + player.inventory.remove(Item(Items.COINS_995, 1000)) player.houseManager.createNewHouseAt(HouseLocation.RIMMINGTON) npc( "Thank you. Go through the Rimmington house portal", diff --git a/Server/src/main/content/global/skill/construction/Hotspot.kt b/Server/src/main/content/global/skill/construction/Hotspot.kt index 33c1758ed..dd245e782 100644 --- a/Server/src/main/content/global/skill/construction/Hotspot.kt +++ b/Server/src/main/content/global/skill/construction/Hotspot.kt @@ -1,7 +1,5 @@ package content.global.skill.construction -import content.global.skill.construction.BuildHotspot - /** * Represents a hotspot. * @author Emperor diff --git a/Server/src/main/content/global/skill/construction/HouseLocation.java b/Server/src/main/content/global/skill/construction/HouseLocation.java index aa66b31d5..ac55b0001 100644 --- a/Server/src/main/content/global/skill/construction/HouseLocation.java +++ b/Server/src/main/content/global/skill/construction/HouseLocation.java @@ -79,7 +79,7 @@ public enum HouseLocation { } if (player.getSkills().hasLevel(Skills.CONSTRUCTION, hl.levelRequirement)) { if (player.getInventory().contains(995, hl.cost)) { - player.getInventory().remove(new Item(995, hl.cost)); + player.getInventory().remove(new Item(Items.COINS_995, hl.cost)); player.getHouseManager().setLocation(hl); player.sendMessage("You have changed your house location to " + hl.name() + "!"); } else { diff --git a/Server/src/main/content/global/skill/construction/HouseManager.java b/Server/src/main/content/global/skill/construction/HouseManager.java index 4b1f7b632..65ee6c3d9 100644 --- a/Server/src/main/content/global/skill/construction/HouseManager.java +++ b/Server/src/main/content/global/skill/construction/HouseManager.java @@ -1,14 +1,8 @@ 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; import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.audio.Audio; import core.game.node.entity.skill.Skills; import core.game.node.scenery.Scenery; import core.game.system.task.Pulse; @@ -17,16 +11,17 @@ import core.game.world.map.build.DynamicRegion; 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 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; import static core.api.regionspec.RegionSpecificationKt.using; @@ -61,7 +56,7 @@ public final class HouseManager { /** * The house zone. */ - private final HouseZone zone = new HouseZone(this); + private HouseZone zone = new HouseZone(this); /** * The player's house rooms. @@ -103,39 +98,12 @@ 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())]; + if (data.get("locked") != null) { + locked = true; + } Object servRaw = data.get("servant"); if(servRaw != null){ servant = Servant.parse((JSONObject) servRaw); @@ -159,43 +127,40 @@ 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); - } - } - } - /** - * Enter's the player's house. + * Prepares for entering the player's house. * @param player * @param buildingMode */ - public void enter(final Player player, boolean buildingMode) { + public void preEnter(final Player player, boolean buildingMode) { if (this.buildingMode != buildingMode || !isLoaded()) { this.buildingMode = buildingMode; 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()); + } + + /** + * Enters the player's house and plays the ding sound. + * @param player + * @param buildingMode + */ + public void enter(final Player player, boolean buildingMode) { + preEnter(player, buildingMode); player.getProperties().setTeleportLocation(getEnterLocation()); openLoadInterface(player); + postEnter(player, buildingMode); + } + + /** + * Performs post-house-enter administration. + * @param player + * @param buildingMode + */ + public void postEnter(final Player player, boolean buildingMode) { checkForAndSpawnServant(player); updateVarbits(player, buildingMode); unlockMusicTrack(player); @@ -203,7 +168,7 @@ public final class HouseManager { private void openLoadInterface(Player player) { player.getInterfaceManager().openComponent(399); - player.getAudioManager().send(new Audio(984)); + playAudio(player, Sounds.POH_TELEPORT_984); submitCloseLoadInterfacePulse(player); } @@ -225,7 +190,7 @@ public final class HouseManager { public boolean pulse() { spawnServant(); if (servant.isGreet()){ - player.getDialogueInterpreter().sendDialogues(servant.getType().getId(), servant.getType().getId() == 4243 ? FacialExpression.HALF_GUILTY : null, "Welcome."); + player.getDialogueInterpreter().sendDialogues(servant.getType().getId(), servant.getType().getId() == 4243 ? FacialExpression.HAPPY : null, "Welcome."); } return true; } @@ -233,8 +198,8 @@ public final class HouseManager { } private void updateVarbits(Player player, boolean build) { - player.varpManager.get(261).setVarbit(0, build ? 1 : 0); - player.varpManager.get(262).setVarbit(0, getRoomAmount()); + setVarp(player, 261, build ? 1 : 0); + setVarp(player, 262, getRoomAmount()); } private void unlockMusicTrack(Player player) { @@ -242,7 +207,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) { @@ -252,7 +217,7 @@ public final class HouseManager { } if (house.isInHouse(player)) { player.animate(Animation.RESET); - player.setLocation(house.location.getExitLocation()); + player.getProperties().setTeleportLocation(house.location.getExitLocation()); } } @@ -263,15 +228,16 @@ public final class HouseManager { */ public void toggleBuildingMode(Player player, boolean enable) { if (!isInHouse(player)) { - player.getPacketDispatch().sendMessage("Building mode really only helps if you're in a house."); + //player.getPacketDispatch().sendMessage("Building mode really only helps if you're in a house."); + setLocked(enable); + player.sendMessage("Your house is now "+(player.getHouseManager().isLocked() ? "locked." : "unlocked." )); return; } if (buildingMode != enable) { if (enable) { expelGuests(player); } - buildingMode = enable; - reload(player, enable); + enter(player, enable); player.getPacketDispatch().sendMessage("Building mode is now " + (buildingMode ? "on." : "off.")); } } @@ -280,6 +246,7 @@ public final class HouseManager { * Reloads the house. * @param player The player. * @param buildingMode If building mode should be enabled. + * NOTE: I think we should avoid this method, it might be causing some issues. It's actually really suspicious... */ public void reload(Player player, boolean buildingMode) { int diffX = player.getLocation().getLocalX(); @@ -323,7 +290,7 @@ public final class HouseManager { */ public Location getEnterLocation() { if (houseRegion == null) { - SystemLogger.logErr(this.getClass(), "House wasn't constructed yet!"); + log(this.getClass(), Log.ERR, "House wasn't constructed yet!"); return null; } for (int x = 0; x < 8; x++) { @@ -396,10 +363,12 @@ public final class HouseManager { houseRegion = getPreparedRegion(); configureRoofs(); prepareHouseChunks(style, houseRegion, buildingMode, rooms); + houseRegion.flagActive(); if (hasDungeon()) { dungeonRegion = getPreparedRegion(); prepareDungeonChunks(style, dungeonRegion, houseRegion, buildingMode, rooms[3]); + dungeonRegion.flagActive(); } ZoneBuilder.configure(zone); @@ -411,6 +380,7 @@ public final class HouseManager { DynamicRegion region = new DynamicRegion(-1, borders.getSouthWestX() >> 6, borders.getSouthWestY() >> 6); region.setBorders(borders); region.setUpdateAllPlanes(true); + region.setBuild(true); RegionManager.addRegion(region.getId(), region); return region; } @@ -428,8 +398,7 @@ public final class HouseManager { @Override public BuildRegionChunk getChunk(int x, int y, int plane, @NotNull DynamicRegion dyn) { - BuildRegionChunk chunk = rooms[plane][x][y].getChunk().copy(dyn.getPlanes()[plane]); - return chunk; + return rooms[plane][x][y].getChunk().copy(dyn.getPlanes()[plane]); } @Override @@ -449,15 +418,10 @@ public final class HouseManager { using(target), fillWith(defaultChunk) .from(from) - .onPlanes(0) - .onCondition((destX, destY, plane) -> rooms[plane][destX][destY] == null), - fillWith((RegionChunk) null) - .from(from) - .onPlanes(1,2) - .onCondition((destX, destY, plane) -> rooms[plane][destX][destY] == null), + .onPlanes(0), fillWith(defaultSkyChunk) .from(from) - .onPlanes(3), + .onPlanes(1,2,3), loadRooms .from(from) .onPlanes(0,1,2) @@ -549,6 +513,7 @@ public final class HouseManager { break; } case SOUTH: { + chunkX = 7 - chunkX; chunkY = 7 - chunkY; break; } @@ -743,8 +708,11 @@ public final class HouseManager { * Checks if the house region was constructed and active. * @return {@code True} if an active region for the house exists. */ + //public boolean isLoaded() { + // return (houseRegion != null) || (dungeonRegion != null); + //} public boolean isLoaded() { - return (houseRegion != null) || (dungeonRegion != null); + return (houseRegion != null && houseRegion.isActive()) || (dungeonRegion != null && dungeonRegion.isActive()); } /** @@ -889,4 +857,4 @@ public final class HouseManager { public HouseZone getZone() { return zone; } -} \ No newline at end of file +} diff --git a/Server/src/main/content/global/skill/construction/HouseZone.java b/Server/src/main/content/global/skill/construction/HouseZone.java index a095b7d7b..33f875362 100644 --- a/Server/src/main/content/global/skill/construction/HouseZone.java +++ b/Server/src/main/content/global/skill/construction/HouseZone.java @@ -3,9 +3,15 @@ package content.global.skill.construction; import core.game.node.entity.Entity; import core.game.node.entity.player.Player; +import core.game.world.map.build.DynamicRegion; import core.game.world.map.zone.MapZone; +import core.game.world.map.RegionManager; +import core.game.world.map.Region; +import core.game.system.task.Pulse; import core.game.world.map.zone.ZoneRestriction; +import static core.api.ContentAPIKt.*; + /** * Handles the player owned house zone. * @@ -32,26 +38,42 @@ 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() { + unregisterOldRegions(); + registerRegion(house.getHouseRegion().getId()); + if (house.getDungeonRegion() != null) { + registerRegion(house.getDungeonRegion().getId()); + } + } + + private void unregisterOldRegions() { if (previousRegion != -1) { unregisterRegion(previousRegion); } if (previousDungeon != -1) { unregisterRegion(previousDungeon); } - registerRegion(house.getHouseRegion().getId()); - if (house.getDungeonRegion() != null) { - registerRegion(house.getHouseRegion().getId()); - } } @Override public boolean enter(Entity e) { + if (e instanceof Player) { + Player pl = (Player) e; + if (house == pl.getHouseManager()) { + previousRegion = house.getHouseRegion().getId(); + if (house.getDungeonRegion() != null) + previousDungeon = house.getDungeonRegion().getId(); + } + registerLogoutListener(pl, "houselogout", (p) -> { + p.setLocation(house.getLocation().getExitLocation()); + return kotlin.Unit.INSTANCE; + }); + } return super.enter(e); } @@ -60,19 +82,45 @@ 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 (logout) { - if (e instanceof Player) { - Player p = (Player) e; - HouseManager.leave(p); - return true; + 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()) { + // Snowscape: tear down the house when the last person leaves, allowing guests to stay in the house without the owner. + if (!house.getHouseRegion().isViewed() && (!house.hasDungeon() || !house.getDungeonRegion().isViewed())) { + house.expelGuests(p); + int toRemove = previousRegion; + int dungRemove = previousDungeon; + submitWorldPulse(new Pulse(2) { + public boolean pulse() { + Region r = RegionManager.forId(toRemove); + Region dr = dungRemove != -1 ? RegionManager.forId(dungRemove) : null; + RegionManager.removeRegion(toRemove); + unregisterRegion(toRemove); + r.setActive(false); + if (dungRemove != -1) { + RegionManager.removeRegion(dungRemove); + unregisterRegion(dungRemove); + dr.setActive(false); + } + return true; + } + }); } + // 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; } -} \ No newline at end of file +} diff --git a/Server/src/main/content/global/skill/construction/PortalOptionPlugin.java b/Server/src/main/content/global/skill/construction/PortalOptionPlugin.java index 23d951dac..23d9621a2 100644 --- a/Server/src/main/content/global/skill/construction/PortalOptionPlugin.java +++ b/Server/src/main/content/global/skill/construction/PortalOptionPlugin.java @@ -8,6 +8,7 @@ import core.game.dialogue.DialoguePlugin; import core.game.interaction.OptionHandler; import core.game.node.Node; import core.game.node.entity.player.Player; +import core.game.node.entity.skill.Skills; import core.game.node.scenery.Scenery; import core.plugin.Initializable; import core.plugin.Plugin; @@ -33,6 +34,7 @@ public final class PortalOptionPlugin extends OptionHandler { SceneryDefinition.forId(13405).getHandlers().put("option:lock", this); SceneryDefinition.forId(13405).getHandlers().put("option:enter", this); ClassScanner.definePlugin(new PortalDialogue()); + ClassScanner.definePlugin(new PortalExitDialogue()); return this; } @@ -40,7 +42,8 @@ public final class PortalOptionPlugin extends OptionHandler { public boolean handle(Player player, Node node, String option) { Scenery object = node.asScenery(); if (object.getId() == 13405 && option.equals("enter")) { - HouseManager.leave(player); + player.getDialogueInterpreter().open("houseExitDialogue"); + //HouseManager.leave(player); return true; } if (option.equals("lock")) { @@ -111,11 +114,14 @@ public final class PortalOptionPlugin extends OptionHandler { break; } //If the Player has a house, but does not have their house moved to the portal they are interacting with + /* Snowscape: allow entering house from anywhere if (player.getHouseManager().getLocation().getPortalId() != player.getAttribute("con:portal", -1)) { player.getPacketDispatch().sendMessage("Your house is in " + player.getHouseManager().getLocation().getName() + "."); player.sendMessage("Speak with an estate agent to change your house location."); break; } + */ + player.removeAttribute("lasthouseentered"); player.getHouseManager().enter(player, buttonId == 2); break; case 3: @@ -134,10 +140,12 @@ public final class PortalOptionPlugin extends OptionHandler { player.getPacketDispatch().sendMessage("You aren't a friend of yourself!"); return Unit.INSTANCE; } + /* Snowscape: allow entering house if host is not home. if (!p.getHouseManager().isLoaded()) { player.getPacketDispatch().sendMessage("This player is not at home right now."); return Unit.INSTANCE; } + */ if (p.getHouseManager().isBuildingMode()) { player.getPacketDispatch().sendMessage("This player is in building mode."); return Unit.INSTANCE; @@ -146,6 +154,7 @@ public final class PortalOptionPlugin extends OptionHandler { player.getPacketDispatch().sendMessage("The other player has locked their house."); return Unit.INSTANCE; } + player.setAttribute("/save:lasthouseentered", (String) value); p.setAttribute("poh_owner", (String) value); p.getHouseManager().enter(player, false); return Unit.INSTANCE; @@ -162,4 +171,114 @@ public final class PortalOptionPlugin extends OptionHandler { return new int[] { DialogueInterpreter.getDialogueKey("con:portal") }; } } + + + + /** + * Snowscape: select destination via dialogue when leaving the house + * + */ + @PluginManifest(type= PluginType.DIALOGUE) + private class PortalExitDialogue extends DialoguePlugin { + + /** + * Constructs a new {@code PortalExitDialogue} {@code Object} + */ + public PortalExitDialogue() { + + } + + /** + * Constructs a new {@code PortalExitDialogue} {@code Object} + * @param player The player. + */ + public PortalExitDialogue(Player player) { + super(player); + } + + @Override + public DialoguePlugin newInstance(Player player) { + return new PortalExitDialogue(player); + } + + @Override + public boolean open(Object... args) { + options("Leave house (home location)", "Rimmington", "Taverly", "Pollnivneach", "Next Page"); + stage = 1; + return true; + } + + @Override + public boolean handle(int interfaceId, int buttonId) { + switch (stage) { + case 1: + switch (buttonId) { + case 1: + HouseManager.leave(player); + end(); + break; + case 2: + attemptHouseExit(HouseLocation.RIMMINGTON); + end(); + break; + case 3: + attemptHouseExit(HouseLocation.TAVERLY); + end(); + break; + case 4: + attemptHouseExit(HouseLocation.POLLNIVNEACH); + end(); + break; + case 5: + options("Rellekka", "Brimhaven","Yanille", "Cancel"); + stage = 2; + break; + } + break; + case 2: + switch (buttonId) { + case 1: + attemptHouseExit(HouseLocation.RELLEKKA); + end(); + break; + case 2: + attemptHouseExit(HouseLocation.BRIMHAVEN); + end(); + break; + case 3: + attemptHouseExit(HouseLocation.YANILLE); + end(); + break; + case 4: + end(); + break; + } + } + return true; + } + + @Override + public int[] getIds() { + return new int[] { DialogueInterpreter.getDialogueKey("houseExitDialogue") }; + } + + public void attemptHouseExit(HouseLocation targetLocation){ + int level = 0; + String message = null; + if (player.getHouseManager().isInHouse(player)) { + level = getStatLevel(player, Skills.CONSTRUCTION); + message = "You need "; + } else { + level = getStatLevel(Repository.getPlayerByName(player.getAttribute("lasthouseentered", null)), Skills.CONSTRUCTION); + message = "The house owner needs "; + } + + if (targetLocation.getLevelRequirement() <= level){ + //player.animate(Animation.RESET); + player.getProperties().setTeleportLocation(targetLocation.getExitLocation()); + } else { + player.sendMessage(message + targetLocation.getLevelRequirement() + " Construction to for you to teleport to " + targetLocation.getName() + "."); + } + } + } } \ No newline at end of file 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/ServantType.java b/Server/src/main/content/global/skill/construction/ServantType.java index 6abba4fff..b505c6b0b 100644 --- a/Server/src/main/content/global/skill/construction/ServantType.java +++ b/Server/src/main/content/global/skill/construction/ServantType.java @@ -2,6 +2,7 @@ package content.global.skill.construction; import core.game.node.item.Item; +import org.rs09.consts.Items; /** * Represents a type of servant @@ -11,10 +12,10 @@ import core.game.node.item.Item; public enum ServantType { NONE(-1, -1, -1, -1, -1), RICK(4235, 500, 6, 20, 60), - MAID(4237, 1000, 10, 25, 50, new Item(2003)), - COOK(4239, 3000, 16, 30, 17, new Item(2301), new Item(712)), - BUTLER(4241, 5000, 20, 40, 12, new Item(1897), new Item(712)), - DEMON_BUTLER(4243, 10000, 26, 50, 7, new Item(2011)) + MAID(4237, 1000, 10, 25, 50, new Item(Items.STEW_2003)), + COOK(4239, 3000, 16, 30, 17, new Item(Items.PINEAPPLE_PIZZA_2301), new Item(Items.CUP_OF_TEA_712)), + BUTLER(4241, 5000, 20, 40, 12, new Item(Items.CHOCOLATE_CAKE_1897), new Item(Items.CUP_OF_TEA_712)), + DEMON_BUTLER(4243, 10000, 26, 50, 7, new Item(Items.CURRY_2011)) ; /** diff --git a/Server/src/main/content/global/skill/construction/decoration/BeerBarrelPlugin.java b/Server/src/main/content/global/skill/construction/decoration/BeerBarrelPlugin.java index d1f8f7bc9..dce9b7cf9 100644 --- a/Server/src/main/content/global/skill/construction/decoration/BeerBarrelPlugin.java +++ b/Server/src/main/content/global/skill/construction/decoration/BeerBarrelPlugin.java @@ -8,6 +8,7 @@ import core.game.node.scenery.Scenery; import core.game.world.update.flag.context.Animation; import core.plugin.Initializable; import core.plugin.Plugin; +import org.rs09.consts.Items; /** * Handles the Construction beer barrels. @@ -43,7 +44,7 @@ public class BeerBarrelPlugin extends UseWithHandler { Player player = event.getPlayer(); final Scenery object = (Scenery) event.getUsedWith(); - if (player.getInventory().remove(new Item(1919))) { + if (player.getInventory().remove(new Item(Items.BEER_GLASS_1919))) { player.animate(Animation.create(833)); player.sendMessage("You fill up your glass."); player.getInventory().add(new Item(getReward(object.getId()), 1)); 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 764bc11ae..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,12 +38,12 @@ class ConstructionDoorPlugin : OptionHandler() { } override fun handle(player: Player, node: Node, option: String): Boolean { - val `object` = node as Scenery - val second = core.game.global.action.DoorActionHandler.getSecondDoor(`object`, player) when (option) { "pick-lock", "force" -> return false //TODO } - core.game.global.action.DoorActionHandler.open(`object`, second, getReplaceId(`object`), getReplaceId(second), true, 500, false) + 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/FireplacePlugin.java b/Server/src/main/content/global/skill/construction/decoration/FireplacePlugin.java index b1f7f7128..d6dad175b 100644 --- a/Server/src/main/content/global/skill/construction/decoration/FireplacePlugin.java +++ b/Server/src/main/content/global/skill/construction/decoration/FireplacePlugin.java @@ -14,6 +14,7 @@ import core.game.system.task.Pulse; import core.game.world.GameWorld; import core.game.world.update.flag.context.Animation; import core.plugin.Plugin; +import org.rs09.consts.Items; /** * Handles the various fireplaces that you may light in Construction. @@ -51,7 +52,7 @@ public final class FireplacePlugin extends OptionHandler { if (!obj.isActive()) { return true; } - player.getInventory().remove(new Item(1511)); + player.getInventory().remove(new Item(Items.LOGS_1511)); player.getSkills().addExperience(Skills.FIREMAKING, 80); SceneryBuilder.replace(new Scenery(obj.getId(), obj.getLocation()), new Scenery(obj.getId() + 1, obj.getLocation(), obj.getRotation()), 1000); player.sendMessage("You light the fireplace."); diff --git a/Server/src/main/content/global/skill/construction/decoration/bedroom/WardrobeHandler.kt b/Server/src/main/content/global/skill/construction/decoration/bedroom/WardrobeHandler.kt index a4d73852b..5cc89e118 100644 --- a/Server/src/main/content/global/skill/construction/decoration/bedroom/WardrobeHandler.kt +++ b/Server/src/main/content/global/skill/construction/decoration/bedroom/WardrobeHandler.kt @@ -25,7 +25,7 @@ class WardrobeHandler : OptionHandler() { } else { player.interfaceManager.open(Component(594)) } - return true; + return true } } \ No newline at end of file diff --git a/Server/src/main/content/global/skill/construction/decoration/chapel/BoneOfferPlugin.java b/Server/src/main/content/global/skill/construction/decoration/chapel/BoneOfferPlugin.java index d241c0e1a..e681c861d 100644 --- a/Server/src/main/content/global/skill/construction/decoration/chapel/BoneOfferPlugin.java +++ b/Server/src/main/content/global/skill/construction/decoration/chapel/BoneOfferPlugin.java @@ -18,6 +18,7 @@ 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; /** * Handles the offering of bones on an altar. @@ -35,12 +36,12 @@ public class BoneOfferPlugin extends UseWithHandler { * The offer Animation. */ private final Animation ANIM = new Animation(896); - + /** * Constructor. */ public BoneOfferPlugin() { - super(526, 2859, 528, 3183, 3179, 530, 532, 3125, 4812, 3123, 534, 6812, 536, 4830, 4832, 6729, 4834, 14693); + super(526, 528, 530, 532, 534, 536, 2530, 2859, 3123, 3125, 3127, 3179, 3180, 3181, 3182, 3183, 3185, 3186, 3187, 4812, 4830, 4832, 4834, 6729, 6812); } @Override @@ -98,7 +99,7 @@ public class BoneOfferPlugin extends UseWithHandler { if (counter == 1 || counter % 5 == 0) { if (player.getInventory().remove(new Item(b.getItemId()))) { player.animate(ANIM); - player.getAudioManager().send(new Audio(958)); + playAudio(player, Sounds.POH_OFFER_BONES_958); player.getPacketDispatch().sendPositionedGraphics(GFX, gfxLoc); player.sendMessage(getMessage(isLit(left), isLit(right))); player.getSkills().addExperience(Skills.PRAYER, b.getExperience() * getMod(altar, isLit(left), isLit(right))); diff --git a/Server/src/main/content/global/skill/construction/decoration/chapel/BurnerListener.kt b/Server/src/main/content/global/skill/construction/decoration/chapel/BurnerListener.kt index 4a32054ce..a6c67fa0a 100644 --- a/Server/src/main/content/global/skill/construction/decoration/chapel/BurnerListener.kt +++ b/Server/src/main/content/global/skill/construction/decoration/chapel/BurnerListener.kt @@ -7,6 +7,7 @@ import core.game.world.update.flag.context.Animation import core.tools.RandomFunction import core.game.interaction.InteractionListener import core.game.interaction.IntType +import org.rs09.consts.Items /** * Handles the lighting of the torches of the Chapel. @@ -21,14 +22,14 @@ class BurnerListener : InteractionListener { if (player.ironmanManager.checkRestriction() && !player.houseManager.isInHouse(player)) { return@on true } - if (!player.inventory.containsItem(Item(590)) || !player.inventory.containsItem(Item(251))) { + if (!player.inventory.containsItem(Item(Items.TINDERBOX_590)) || !player.inventory.containsItem(Item(Items.CLEAN_MARRENTILL_251))) { player.dialogueInterpreter.sendDialogue( "You'll need a tinderbox and a clean marrentill herb in order to", "light the burner." ) return@on true } - if (player.inventory.remove(Item(251))) { + if (player.inventory.remove(Item(Items.CLEAN_MARRENTILL_251))) { player.lock(1) player.animate(Animation.create(3687)) player.sendMessage("You burn some marrentill in the incense burner.") diff --git a/Server/src/main/content/global/skill/construction/decoration/chapel/ShieldBlessingPlugin.kt b/Server/src/main/content/global/skill/construction/decoration/chapel/ShieldBlessingPlugin.kt index 6e6f6a539..9a8e34c76 100644 --- a/Server/src/main/content/global/skill/construction/decoration/chapel/ShieldBlessingPlugin.kt +++ b/Server/src/main/content/global/skill/construction/decoration/chapel/ShieldBlessingPlugin.kt @@ -1,14 +1,15 @@ package content.global.skill.construction.decoration.chapel +import core.api.playAudio import core.game.interaction.NodeUsageEvent import core.game.interaction.UseWithHandler -import core.game.node.entity.player.link.audio.Audio import core.game.node.entity.skill.Skills import core.game.node.item.Item import core.game.world.update.flag.context.Animation import core.plugin.Initializable import core.plugin.Plugin import org.rs09.consts.Items +import org.rs09.consts.Sounds const val HOLY_ELIXER = 13754 const val SPIRIT_SHIELD = 13734 @@ -38,7 +39,7 @@ class ShieldBlessingPlugin : UseWithHandler(HOLY_ELIXER, SPIRIT_SHIELD) { } player.animator.animate(Animation(896)) - player.audioManager.send(Audio(958)) + playAudio(player, Sounds.POH_OFFER_BONES_958) if(player.inventory.remove(Item(HOLY_ELIXER)) && player.inventory.remove(Item(SPIRIT_SHIELD))) player.inventory.add(Item(Items.BLESSED_SPIRIT_SHIELD_13736)) diff --git a/Server/src/main/content/global/skill/construction/decoration/costume/SnowscapeFancyDressBox.kt b/Server/src/main/content/global/skill/construction/decoration/costume/SnowscapeFancyDressBox.kt new file mode 100644 index 000000000..8306a1e41 --- /dev/null +++ b/Server/src/main/content/global/skill/construction/decoration/costume/SnowscapeFancyDressBox.kt @@ -0,0 +1,30 @@ +package content.global.skill.construction.decoration.costume + +import core.game.node.scenery.Scenery +import core.game.node.scenery.SceneryBuilder +import core.game.interaction.InteractionListener +import core.game.interaction.IntType +import core.api.* + +/** + * + * + */ +class SnowscapeCostumeRoomBank : InteractionListener { + + val IDs = intArrayOf(18772,18774,18776) + + override fun defineListeners() { + on(IDs, IntType.SCENERY, "open"){ player, node -> + if (!player.houseManager.isInHouse(player)) { + sendMessage(player, "You can only do that in your own house.") + return@on true + } + + setAttribute(player, "inCostumeRoomBank", true) + openBankAccount(player) + + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/construction/decoration/kitchen/BeerBarrelPlugin.java b/Server/src/main/content/global/skill/construction/decoration/kitchen/BeerBarrelPlugin.java index 6a8c19044..eb6b3fe20 100644 --- a/Server/src/main/content/global/skill/construction/decoration/kitchen/BeerBarrelPlugin.java +++ b/Server/src/main/content/global/skill/construction/decoration/kitchen/BeerBarrelPlugin.java @@ -9,6 +9,7 @@ import core.game.node.scenery.Scenery; import core.game.world.update.flag.context.Animation; import core.plugin.Initializable; import core.plugin.Plugin; +import org.rs09.consts.Items; /** * Handles the Construction beer barrels. @@ -44,7 +45,7 @@ public class BeerBarrelPlugin extends UseWithHandler { Player player = event.getPlayer(); final Scenery object = (Scenery) event.getUsedWith(); - if (player.getInventory().remove(new Item(1919))) { + if (player.getInventory().remove(new Item(Items.BEER_GLASS_1919))) { player.animate(Animation.create(3661 + (object.getId() - 13569))); player.sendMessage("You fill up your glass with " + object.getName().toLowerCase().replace("barrel", "").trim() + "."); player.getInventory().add(new Item(getReward(object.getId()), 1)); diff --git a/Server/src/main/content/global/skill/construction/decoration/kitchen/LarderPlugin.java b/Server/src/main/content/global/skill/construction/decoration/kitchen/LarderPlugin.java index f30f0cebc..9c3289cf2 100644 --- a/Server/src/main/content/global/skill/construction/decoration/kitchen/LarderPlugin.java +++ b/Server/src/main/content/global/skill/construction/decoration/kitchen/LarderPlugin.java @@ -10,6 +10,7 @@ import core.game.node.entity.player.Player; import core.game.node.item.Item; import core.plugin.Plugin; import core.plugin.ClassScanner; +import org.rs09.consts.Items; /** * Handles the interactions for the three Larders. @@ -95,19 +96,19 @@ public final class LarderPlugin extends OptionHandler { case 1: switch (buttonId) { case 1: - player.getInventory().add(new Item(7738, 1)); + player.getInventory().add(new Item(Items.TEA_LEAVES_7738, 1)); end(); break; case 2: - player.getInventory().add(new Item(1927, 1)); + player.getInventory().add(new Item(Items.BUCKET_OF_MILK_1927, 1)); end(); break; case 3: - player.getInventory().add(new Item(1944, 1)); + player.getInventory().add(new Item(Items.EGG_1944, 1)); end(); break; case 4: - player.getInventory().add(new Item(1933, 1)); + player.getInventory().add(new Item(Items.POT_OF_FLOUR_1933, 1)); end(); break; case 5: @@ -120,19 +121,19 @@ public final class LarderPlugin extends OptionHandler { case 2: switch (buttonId) { case 1: - player.getInventory().add(new Item(1942, 1)); + player.getInventory().add(new Item(Items.POTATO_1942, 1)); end(); break; case 2: - player.getInventory().add(new Item(1550, 1)); + player.getInventory().add(new Item(Items.GARLIC_1550, 1)); end(); break; case 3: - player.getInventory().add(new Item(1957, 1)); + player.getInventory().add(new Item(Items.ONION_1957, 1)); end(); break; case 4: - player.getInventory().add(new Item(1985, 1)); + player.getInventory().add(new Item(Items.CHEESE_1985, 1)); end(); break; } diff --git a/Server/src/main/content/global/skill/construction/decoration/kitchen/ShelfPlugin.java b/Server/src/main/content/global/skill/construction/decoration/kitchen/ShelfPlugin.java index 660749037..9fed7ebce 100644 --- a/Server/src/main/content/global/skill/construction/decoration/kitchen/ShelfPlugin.java +++ b/Server/src/main/content/global/skill/construction/decoration/kitchen/ShelfPlugin.java @@ -10,6 +10,7 @@ import core.game.node.item.Item; import core.plugin.Initializable; import core.plugin.Plugin; import core.plugin.ClassScanner; +import org.rs09.consts.Items; /** * Handles the shelves in the kitchen room. @@ -107,23 +108,23 @@ public final class ShelfPlugin extends OptionHandler { switch (buttonId) { case 1: end(); - player.getInventory().add(new Item(7688, 1)); + player.getInventory().add(new Item(Items.KETTLE_7688, 1)); break; case 2: end(); - player.getInventory().add(new Item(7702, 1)); + player.getInventory().add(new Item(Items.TEAPOT_7702, 1)); break; case 3: end(); - player.getInventory().add(new Item(7728, 1)); + player.getInventory().add(new Item(Items.EMPTY_CUP_7728, 1)); break; case 4: end(); - player.getInventory().add(new Item(1919, 1)); + player.getInventory().add(new Item(Items.BEER_GLASS_1919, 1)); break; case 5: end(); - player.getInventory().add(new Item(1887, 1)); + player.getInventory().add(new Item(Items.CAKE_TIN_1887, 1)); break; } break; @@ -131,23 +132,23 @@ public final class ShelfPlugin extends OptionHandler { switch (buttonId) { case 1: end(); - player.getInventory().add(new Item(7688, 1)); + player.getInventory().add(new Item(Items.KETTLE_7688, 1)); break; case 2: end(); - player.getInventory().add(new Item(7702, 1)); + player.getInventory().add(new Item(Items.TEAPOT_7702, 1)); break; case 3: end(); - player.getInventory().add(new Item(7728, 1)); + player.getInventory().add(new Item(Items.EMPTY_CUP_7728, 1)); break; case 4: end(); - player.getInventory().add(new Item(1919, 1)); + player.getInventory().add(new Item(Items.BEER_GLASS_1919, 1)); break; case 5: end(); - player.getInventory().add(new Item(1923, 1)); + player.getInventory().add(new Item(Items.BOWL_1923, 1)); break; } break; @@ -155,19 +156,19 @@ public final class ShelfPlugin extends OptionHandler { switch (buttonId) { case 1: end(); - player.getInventory().add(new Item(7688, 1)); + player.getInventory().add(new Item(Items.KETTLE_7688, 1)); break; case 2: end(); - player.getInventory().add(new Item(7702, 1)); + player.getInventory().add(new Item(Items.TEAPOT_7702, 1)); break; case 3: end(); - player.getInventory().add(new Item(4244, 1)); + player.getInventory().add(new Item(Items.PORCELAIN_CUP_4244, 1)); break; case 4: end(); - player.getInventory().add(new Item(1919, 1)); + player.getInventory().add(new Item(Items.BEER_GLASS_1919, 1)); break; case 5: interpreter.sendOptions("Select an Option", "Bowl", "Cake tin"); @@ -179,30 +180,30 @@ public final class ShelfPlugin extends OptionHandler { switch (buttonId) { case 1: end(); - player.getInventory().add(new Item(1923, 1)); + player.getInventory().add(new Item(Items.BOWL_1923, 1)); break; case 2: end(); - player.getInventory().add(new Item(1887, 1)); + player.getInventory().add(new Item(Items.CAKE_TIN_1887, 1)); break; } case 5:// teak shelves switch (buttonId) { case 1: end(); - player.getInventory().add(new Item(7688, 1)); + player.getInventory().add(new Item(Items.KETTLE_7688, 1)); break; case 2: end(); - player.getInventory().add(new Item(7702, 1)); + player.getInventory().add(new Item(Items.TEAPOT_7702, 1)); break; case 3: end(); - player.getInventory().add(new Item(7735, 1)); + player.getInventory().add(new Item(Items.PORCELAIN_CUP_7735, 1)); break; case 4: end(); - player.getInventory().add(new Item(1919, 1)); + player.getInventory().add(new Item(Items.BEER_GLASS_1919, 1)); break; case 5: interpreter.sendOptions("Select an Option", "Bowl", "Pie dish", "Empty pot"); @@ -214,15 +215,15 @@ public final class ShelfPlugin extends OptionHandler { switch (buttonId) { case 1: end(); - player.getInventory().add(new Item(1923, 1)); + player.getInventory().add(new Item(Items.BOWL_1923, 1)); break; case 2: end(); - player.getInventory().add(new Item(2313, 1)); + player.getInventory().add(new Item(Items.PIE_DISH_2313, 1)); break; case 3: end(); - player.getInventory().add(new Item(1931, 1)); + player.getInventory().add(new Item(Items.EMPTY_POT_1931, 1)); break; } break; diff --git a/Server/src/main/content/global/skill/construction/decoration/portalchamber/PortalChamberDialogue.kt b/Server/src/main/content/global/skill/construction/decoration/portalchamber/PortalChamberDialogue.kt index 90673ed20..eeaf6e455 100644 --- a/Server/src/main/content/global/skill/construction/decoration/portalchamber/PortalChamberDialogue.kt +++ b/Server/src/main/content/global/skill/construction/decoration/portalchamber/PortalChamberDialogue.kt @@ -10,7 +10,7 @@ import core.plugin.Initializable * @author Ceikry */ @Initializable -class PortalChamberDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { +class PortalChamberDialogue(player: Player? = null) : DialoguePlugin(player) { var portal = "none" override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ @@ -41,7 +41,7 @@ class PortalChamberDialogue(player: Player? = null) : core.game.dialogue.Dialogu return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return PortalChamberDialogue(player) } 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 95d5fa1e2..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"); @@ -215,7 +223,7 @@ public class PortalChamberPlugin extends OptionHandler { PortalChamberPlugin.direct(player, "LUMBRIDGE"); return true; } - }, + }, new PageAction("Falador Portal") { @Override public boolean run(Player player) { 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 fb5ad0e1d..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 @@ -1,14 +1,19 @@ package content.global.skill.construction.decoration.questhall +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.entity.player.link.audio.Audio; +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 core.game.interaction.InteractionListener -import core.game.interaction.IntType +import org.rs09.consts.Items +import org.rs09.consts.Sounds import java.util.concurrent.Executors import java.util.concurrent.TimeUnit @@ -24,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 @@ -53,7 +65,7 @@ class MountedGlory : InteractionListener { 1 -> { player.lock(5) player.visualize(Animation(714), Graphics(308, 100, 50)) - player.getAudioManager().send(Audio(200), true); + playGlobalAudio(player.location, Sounds.TELEPORT_ALL_200) } 4 -> player.animator.reset().also { teleport(player, TELEPORTS[int]) } } @@ -62,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/questhall/MountedGloryPlugin.java b/Server/src/main/content/global/skill/construction/decoration/questhall/MountedGloryPlugin.java index 4323ce498..90d96eb80 100644 --- a/Server/src/main/content/global/skill/construction/decoration/questhall/MountedGloryPlugin.java +++ b/Server/src/main/content/global/skill/construction/decoration/questhall/MountedGloryPlugin.java @@ -7,7 +7,6 @@ import core.game.dialogue.DialogueInterpreter; import core.game.interaction.OptionHandler; import core.game.node.Node; import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.audio.Audio; import core.game.system.task.Pulse; import core.game.world.GameWorld; import core.game.world.map.Location; @@ -15,6 +14,9 @@ import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Graphics; import core.plugin.Initializable; import core.plugin.Plugin; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.playGlobalAudio; /** * Handles the POH Mounted Glory. @@ -79,7 +81,7 @@ public class MountedGloryPlugin extends OptionHandler { } player.lock(); player.visualize(ANIMATION, GRAPHICS); - player.getAudioManager().send(new Audio(200), true); + playGlobalAudio(player.getLocation(), Sounds.TELEPORT_ALL_200); player.getImpactHandler().setDisabledTicks(4); GameWorld.getPulser().submit(new Pulse(4, player) { @Override 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 59443cc14..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 @@ -1,15 +1,16 @@ package content.global.skill.construction.decoration.study +import content.global.handlers.item.TeleTabsListener import core.api.* import core.cache.def.impl.SceneryDefinition import core.game.component.Component import core.game.interaction.OptionHandler -import content.global.handlers.item.TeleTabsOptionPlugin 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 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 @@ -18,6 +19,7 @@ import core.plugin.Initializable import core.plugin.Plugin import core.game.world.GameWorld import core.game.interaction.InterfaceListener +import org.rs09.consts.Items /** * Handles the lectern @@ -47,21 +49,21 @@ class LecternPlugin : OptionHandler() { * The required decoration (lectern) */ private val requiredDecorations: Array, vararg requiredItems: Item) { - ARDOUGNE(2, 51, 61.0, Item(TeleTabsOptionPlugin.TeleTabs.ADDOUGNE_TELEPORT.item), arrayOf(Decoration.TEAK_EAGLE_LECTERN, Decoration.MAHOGANY_EAGLE_LECTERN), SOFT_CLAY, Item(563, 2), Item(555, 2)), - BONES_TO_BANANNAS(3, 15, 25.0, Item(8014), arrayOf(Decoration.DEMON_LECTERN, Decoration.TEAK_DEMON_LECTERN, Decoration.MAHOGANY_DEMON_LECTERN), SOFT_CLAY, Item(561, 1), Item(557, 2), Item(555, 2)), - BONES_TO_PEACHES(4, 60, 35.5, Item(8015), arrayOf(Decoration.MAHOGANY_DEMON_LECTERN), SOFT_CLAY, Item(561, 2), Item(557, 4), Item(555, 4)), - CAMELOT(5, 45, 55.5, Item(TeleTabsOptionPlugin.TeleTabs.CAMELOT_TELEPORT.item), arrayOf(Decoration.TEAK_EAGLE_LECTERN, Decoration.MAHOGANY_EAGLE_LECTERN), SOFT_CLAY, Item(563), Item(556, 5)), - ENCHANT_DIAMOND(6, 57, 67.0, Item(8019), arrayOf(Decoration.TEAK_DEMON_LECTERN, Decoration.MAHOGANY_DEMON_LECTERN), SOFT_CLAY, Item(564), Item(557, 10)), - ENCHANT_DRAGONSTONE(7, 68, 78.0, Item(8020), arrayOf(Decoration.MAHOGANY_DEMON_LECTERN), SOFT_CLAY, Item(564), Item(557, 15), Item(555, 15)), - ENCHANT_EMERALD(8, 27, 37.0, Item(8017), arrayOf(Decoration.DEMON_LECTERN, Decoration.TEAK_DEMON_LECTERN, Decoration.MAHOGANY_DEMON_LECTERN), SOFT_CLAY, Item(564), Item(556, 3)), - ENCHANT_ONYX(9, 87, 97.0, Item(8021), arrayOf(Decoration.MAHOGANY_DEMON_LECTERN), SOFT_CLAY, Item(564), Item(557, 20), Item(554, 20)), - ENCHANT_RUBY(10, 49, 59.0, Item(8018), arrayOf(Decoration.TEAK_DEMON_LECTERN, Decoration.MAHOGANY_DEMON_LECTERN), SOFT_CLAY, Item(564), Item(554, 5)), - ENCHANT_SAPPHIRE(11, 7, 17.5, Item(8016), arrayOf(Decoration.OAK_LECTERN, Decoration.EAGLE_LECTERN, Decoration.TEAK_EAGLE_LECTERN, Decoration.MAHOGANY_EAGLE_LECTERN, Decoration.DEMON_LECTERN, Decoration.TEAK_DEMON_LECTERN, Decoration.MAHOGANY_DEMON_LECTERN), SOFT_CLAY, Item(564), Item(555)), - FALADOR(12, 37, 48.0, Item(TeleTabsOptionPlugin.TeleTabs.FALADOR_TELEPORT.item), arrayOf(Decoration.EAGLE_LECTERN, Decoration.TEAK_EAGLE_LECTERN, Decoration.MAHOGANY_EAGLE_LECTERN), SOFT_CLAY, Item(563), Item(555), Item(556, 3)), - LUMBRIDGE(13, 31, 41.0, Item(TeleTabsOptionPlugin.TeleTabs.LUMBRIDGE_TELEPORT.item), arrayOf(Decoration.EAGLE_LECTERN, Decoration.TEAK_EAGLE_LECTERN, Decoration.MAHOGANY_EAGLE_LECTERN), SOFT_CLAY, Item(563), Item(557), Item(556, 3)), - HOUSE(14, 40, 30.0, Item(8013), arrayOf(Decoration.MAHOGANY_EAGLE_LECTERN), SOFT_CLAY, Item(563), Item(557), Item(556)), - VARROCK(15, 25, 35.0, Item(TeleTabsOptionPlugin.TeleTabs.VARROCK_TELEPORT.item), arrayOf(Decoration.OAK_LECTERN, Decoration.EAGLE_LECTERN, Decoration.TEAK_EAGLE_LECTERN, Decoration.MAHOGANY_EAGLE_LECTERN, Decoration.DEMON_LECTERN, Decoration.TEAK_DEMON_LECTERN, Decoration.MAHOGANY_DEMON_LECTERN), SOFT_CLAY, Item(563), Item(554), Item(556, 3)), - WATCHTOWER(16, 58, 68.0, Item(TeleTabsOptionPlugin.TeleTabs.WATCH_TOWER_TELEPORT.item), arrayOf(Decoration.MAHOGANY_EAGLE_LECTERN), SOFT_CLAY, Item(563, 2), Item(557, 2)); + ARDOUGNE(2, 51, 61.0, Item(TeleTabsListener.TeleTabs.ADDOUGNE_TELEPORT.item), arrayOf(Decoration.TEAK_EAGLE_LECTERN, Decoration.MAHOGANY_EAGLE_LECTERN), SOFT_CLAY, Item(Items.LAW_RUNE_563, 2), Item(Items.WATER_RUNE_555, 2)), + BONES_TO_BANANNAS(3, 15, 25.0, Item(Items.BONES_TO_BANANAS_8014), arrayOf(Decoration.DEMON_LECTERN, Decoration.TEAK_DEMON_LECTERN, Decoration.MAHOGANY_DEMON_LECTERN), SOFT_CLAY, Item(Items.NATURE_RUNE_561, 1), Item(Items.EARTH_RUNE_557, 2), Item(Items.WATER_RUNE_555, 2)), + BONES_TO_PEACHES(4, 60, 35.5, Item(Items.BONES_TO_PEACHES_8015), arrayOf(Decoration.MAHOGANY_DEMON_LECTERN), SOFT_CLAY, Item(Items.NATURE_RUNE_561, 2), Item(Items.EARTH_RUNE_557, 4), Item(Items.WATER_RUNE_555, 4)), + CAMELOT(5, 45, 55.5, Item(TeleTabsListener.TeleTabs.CAMELOT_TELEPORT.item), arrayOf(Decoration.TEAK_EAGLE_LECTERN, Decoration.MAHOGANY_EAGLE_LECTERN), SOFT_CLAY, Item(Items.LAW_RUNE_563), Item(Items.AIR_RUNE_556, 5)), + ENCHANT_DIAMOND(6, 57, 67.0, Item(Items.ENCHANT_DIAMOND_8019), arrayOf(Decoration.TEAK_DEMON_LECTERN, Decoration.MAHOGANY_DEMON_LECTERN), SOFT_CLAY, Item(Items.COSMIC_RUNE_564), Item(Items.EARTH_RUNE_557, 10)), + ENCHANT_DRAGONSTONE(7, 68, 78.0, Item(Items.ENCHANT_DRAGONSTN_8020), arrayOf(Decoration.MAHOGANY_DEMON_LECTERN), SOFT_CLAY, Item(Items.COSMIC_RUNE_564), Item(Items.EARTH_RUNE_557, 15), Item(Items.WATER_RUNE_555, 15)), + ENCHANT_EMERALD(8, 27, 37.0, Item(Items.ENCHANT_EMERALD_8017), arrayOf(Decoration.DEMON_LECTERN, Decoration.TEAK_DEMON_LECTERN, Decoration.MAHOGANY_DEMON_LECTERN), SOFT_CLAY, Item(Items.COSMIC_RUNE_564), Item(Items.AIR_RUNE_556, 3)), + ENCHANT_ONYX(9, 87, 97.0, Item(Items.ENCHANT_ONYX_8021), arrayOf(Decoration.MAHOGANY_DEMON_LECTERN), SOFT_CLAY, Item(Items.COSMIC_RUNE_564), Item(Items.EARTH_RUNE_557, 20), Item(Items.FIRE_RUNE_554, 20)), + ENCHANT_RUBY(10, 49, 59.0, Item(Items.ENCHANT_RUBY_8018), arrayOf(Decoration.TEAK_DEMON_LECTERN, Decoration.MAHOGANY_DEMON_LECTERN), SOFT_CLAY, Item(Items.COSMIC_RUNE_564), Item(Items.FIRE_RUNE_554, 5)), + ENCHANT_SAPPHIRE(11, 7, 17.5, Item(Items.ENCHANT_SAPPHIRE_8016), arrayOf(Decoration.OAK_LECTERN, Decoration.EAGLE_LECTERN, Decoration.TEAK_EAGLE_LECTERN, Decoration.MAHOGANY_EAGLE_LECTERN, Decoration.DEMON_LECTERN, Decoration.TEAK_DEMON_LECTERN, Decoration.MAHOGANY_DEMON_LECTERN), SOFT_CLAY, Item(Items.COSMIC_RUNE_564), Item(Items.WATER_RUNE_555)), + FALADOR(12, 37, 48.0, Item(TeleTabsListener.TeleTabs.FALADOR_TELEPORT.item), arrayOf(Decoration.EAGLE_LECTERN, Decoration.TEAK_EAGLE_LECTERN, Decoration.MAHOGANY_EAGLE_LECTERN), SOFT_CLAY, Item(Items.LAW_RUNE_563), Item(Items.WATER_RUNE_555), Item(Items.AIR_RUNE_556, 3)), + LUMBRIDGE(13, 31, 41.0, Item(TeleTabsListener.TeleTabs.LUMBRIDGE_TELEPORT.item), arrayOf(Decoration.EAGLE_LECTERN, Decoration.TEAK_EAGLE_LECTERN, Decoration.MAHOGANY_EAGLE_LECTERN), SOFT_CLAY, Item(Items.LAW_RUNE_563), Item(Items.EARTH_RUNE_557), Item(Items.AIR_RUNE_556, 3)), + HOUSE(14, 40, 30.0, Item(Items.TELEPORT_TO_HOUSE_8013), arrayOf(Decoration.MAHOGANY_EAGLE_LECTERN), SOFT_CLAY, Item(Items.LAW_RUNE_563), Item(Items.EARTH_RUNE_557), Item(Items.AIR_RUNE_556)), + VARROCK(15, 25, 35.0, Item(TeleTabsListener.TeleTabs.VARROCK_TELEPORT.item), arrayOf(Decoration.OAK_LECTERN, Decoration.EAGLE_LECTERN, Decoration.TEAK_EAGLE_LECTERN, Decoration.MAHOGANY_EAGLE_LECTERN, Decoration.DEMON_LECTERN, Decoration.TEAK_DEMON_LECTERN, Decoration.MAHOGANY_DEMON_LECTERN), SOFT_CLAY, Item(Items.LAW_RUNE_563), Item(Items.FIRE_RUNE_554), Item(Items.AIR_RUNE_556, 3)), + WATCHTOWER(16, 58, 68.0, Item(TeleTabsListener.TeleTabs.WATCH_TOWER_TELEPORT.item), arrayOf(Decoration.MAHOGANY_EAGLE_LECTERN), SOFT_CLAY, Item(Items.LAW_RUNE_563, 2), Item(Items.EARTH_RUNE_557, 2)); /** * The required items @@ -83,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) { @@ -92,7 +99,7 @@ class LecternPlugin : OptionHandler() { for (item in requiredItems) { val staff = MagicStaff.forId(item.id) if (staff != null && player.equipment.containsAtLeastOneItem(staff.staves)) { - continue; + continue } if (!player.inventory.containsItem(item)) { //TODO staffs @@ -162,8 +169,8 @@ class LecternPlugin : OptionHandler() { val id = player.getAttribute("ttb:objectid", 0) val deco = Decoration.forObjectId(id) val values = decorationVarps[deco] ?: Pair(0, 0) - player.varpManager.get(261).setVarbit(0, values.first).send(player) - player.varpManager.get(262).setVarbit(0, values.second).send(player) + setVarp(player, 261, values.first) + setVarp(player, 262, values.second) return@onOpen true } on(400) { player, _, _, buttonID, _, _ -> @@ -171,7 +178,7 @@ class LecternPlugin : OptionHandler() { if (ttb != null && ttb.canMake(player)) { player.interfaceManager.close() var requiredItemsCountingStaves = ttb.requiredItems.filter({ item -> - val staff = MagicStaff.forId(item.id); + val staff = MagicStaff.forId(item.id) !(staff != null && player.equipment.containsAtLeastOneItem(staff.staves)) }).toTypedArray() player.pulseManager.run(object : Pulse(1) { @@ -215,6 +222,6 @@ class LecternPlugin : OptionHandler() { /** * Soft clay */ - private val SOFT_CLAY = Item(1761, 1) + private val SOFT_CLAY = Item(Items.SOFT_CLAY_1761, 1) } } 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 665e6d490..b30f862d1 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 @@ -12,6 +12,7 @@ import core.game.node.item.Item import core.plugin.Initializable import core.plugin.Plugin import kotlin.math.ceil +import org.rs09.consts.Items @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){ @@ -57,8 +58,8 @@ class ArmourStand : UseWithHandler(494, 468, 496, 470, 498, 472, 500, 502, 474, return true } @Initializable - class RepairDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + class RepairDialogue(player: Player? = null) : DialoguePlugin(player){ + override fun newInstance(player: Player?): DialoguePlugin { return RepairDialogue(player) } var item: Item? = null @@ -88,7 +89,7 @@ class ArmourStand : UseWithHandler(494, 468, 496, 470, 498, 472, 500, 502, 474, } fun exchangeItems(item: Item, cost: Int, product: Item) { - val coins = Item(995, cost) + 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) diff --git a/Server/src/main/content/global/skill/construction/decoration/workshop/ClockmakersBenchPlugin.java b/Server/src/main/content/global/skill/construction/decoration/workshop/ClockmakersBenchPlugin.java index 3567a72fc..410f1a4a0 100644 --- a/Server/src/main/content/global/skill/construction/decoration/workshop/ClockmakersBenchPlugin.java +++ b/Server/src/main/content/global/skill/construction/decoration/workshop/ClockmakersBenchPlugin.java @@ -16,6 +16,7 @@ import core.game.node.scenery.Scenery; import core.plugin.Initializable; import core.plugin.Plugin; import core.plugin.ClassScanner; +import org.rs09.consts.Items; /** * Handles the clockmakers bench in the workshop @@ -35,13 +36,13 @@ public class ClockmakersBenchPlugin extends OptionHandler { private enum Craftable { TOY_HORSEY(2520, 10, BuildingUtils.PLANK), - CLOCKWORK(8792, 8, new Item(2353)), - TOY_SOLDIER(7759, 13, BuildingUtils.PLANK, new Item(8792)), - TOY_DOLL(7763, 18, BuildingUtils.PLANK, new Item(8792)), - TOY_MOUSE(7767, 33, BuildingUtils.PLANK, new Item(8792)), - TOY_CAT(7771, 85, BuildingUtils.PLANK, new Item(8792)), - WATCH(2575, 28, new Item(8792), new Item(2353)), - SEXTANT(2574, 23, new Item(2353)); + CLOCKWORK(8792, 8, new Item(Items.STEEL_BAR_2353)), + TOY_SOLDIER(7759, 13, BuildingUtils.PLANK, new Item(Items.CLOCKWORK_8792)), + TOY_DOLL(7763, 18, BuildingUtils.PLANK, new Item(Items.CLOCKWORK_8792)), + TOY_MOUSE(7767, 33, BuildingUtils.PLANK, new Item(Items.CLOCKWORK_8792)), + TOY_CAT(7771, 85, BuildingUtils.PLANK, new Item(Items.CLOCKWORK_8792)), + WATCH(2575, 28, new Item(Items.CLOCKWORK_8792), new Item(Items.STEEL_BAR_2353)), + SEXTANT(2574, 23, new Item(Items.STEEL_BAR_2353)); /** * The itemId to give after making this diff --git a/Server/src/main/content/global/skill/construction/decoration/workshop/SnowscapeWorkbench.kt b/Server/src/main/content/global/skill/construction/decoration/workshop/SnowscapeWorkbench.kt new file mode 100644 index 000000000..86974da72 --- /dev/null +++ b/Server/src/main/content/global/skill/construction/decoration/workshop/SnowscapeWorkbench.kt @@ -0,0 +1,104 @@ +package content.global.skill.construction.decoration.workshop + + + +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.skill.Skills +import core.game.node.scenery.Scenery +import core.game.node.item.Item +import org.rs09.consts.Items +import content.global.skill.construction.BuildingUtils + + +/** + * Simple replacement intil flatpacks are in. Using the workbench will consume planks in exchange for construction exp. + * + * + */ + +class SnowscapeWorkbench : InteractionListener { + + val workbenchIds = intArrayOf(13704,13705,13706,13707,13708) + val plankIds = intArrayOf(Items.PLANK_960, Items.OAK_PLANK_8778, Items.TEAK_PLANK_8780, Items.MAHOGANY_PLANK_8782) + + override fun defineListeners() { + defineInteraction( + IntType.SCENERY, + workbenchIds, + "work-at", + persistent = true, allowedDistance = 1, + handler = ::handlePlankConstruction + ) + } + + private fun handlePlankConstruction(player: Player, node: Node, state: Int) : Boolean { + if (!inInventory(player, Items.SAW_8794) || !inInventory(player, Items.HAMMER_2347)) { + sendMessage(player,"You need a hammer and saw to do that.") + player.scripts.reset() + return false + } + + for (plankId in plankIds) { + if (amountInInventory(player, plankId) > 0) { + if (node.id < getAllowedWorkbench(plankId)) { + sendMessage(player,"You need a better workbench to use ${Item(plankId).getName()}s.") + player.scripts.reset() + return false + } + if (getDynLevel(player, Skills.CONSTRUCTION) < getLevelRequirement(plankId)) { + sendMessage(player,"You need level ${getLevelRequirement(plankId)} construction to use ${Item(plankId).getName()}s.") + player.scripts.reset() + return false + } + + if (removeItem(player, Item(plankId, 1))) { + rewardXP(player, Skills.CONSTRUCTION, getExperience(plankId)) + animate(player, BuildingUtils.BUILD_MID_ANIM) //This animation has a sound effect built in + + return delayScript(player, 1) + } + } + } + + + sendMessage(player,"You have no planks in your inventory.") + player.scripts.reset() + return false + } + + // The following functions really should have been an enum class or something, but I don't know how to do that + + fun getAllowedWorkbench(plankId: Int) : Int { + return when (plankId) { + Items.PLANK_960 -> 13704 + Items.OAK_PLANK_8778 -> 13705 + Items.TEAK_PLANK_8780 -> 13706 + Items.MAHOGANY_PLANK_8782 -> 13707 + else -> 0 + } + } + + fun getExperience(plankId: Int) : Double { + return when (plankId) { + Items.PLANK_960 -> 30.0 + Items.OAK_PLANK_8778 -> 60.0 + Items.TEAK_PLANK_8780 -> 90.0 + Items.MAHOGANY_PLANK_8782 -> 140.0 + else -> 0.0 + } + } + + fun getLevelRequirement(plankId: Int) : Int { + return when (plankId) { + Items.PLANK_960 -> 1 + Items.OAK_PLANK_8778 -> 15 + Items.TEAK_PLANK_8780 -> 35 + Items.MAHOGANY_PLANK_8782 -> 40 + else -> 0 + } + } +} \ No newline at end of file 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 6a28cadc7..ddb967ebe 100644 --- a/Server/src/main/content/global/skill/construction/npc/HouseServantDialogue.java +++ b/Server/src/main/content/global/skill/construction/npc/HouseServantDialogue.java @@ -18,6 +18,7 @@ import core.game.world.GameWorld; import core.game.world.map.Location; import core.game.world.map.path.Pathfinder; import content.global.handlers.iface.SawmillPlankInterface.Plank; +import org.rs09.consts.Items; /** * Handles the Servant's dialogues. @@ -146,7 +147,7 @@ public class HouseServantDialogue extends DialoguePlugin { stage++; break; case 3: - if (type != null && player.getInventory().getAmount(995) >= type.getCost() && player.getInventory().remove(new Item(995, type.getCost()))) { + if (type != null && player.getInventory().getAmount(995) >= type.getCost() && player.getInventory().remove(new Item(Items.COINS_995, type.getCost()))) { manager.setServant(new Servant(type)); interpreter.sendDialogue("The servant heads to your house."); stage = 100; @@ -218,11 +219,11 @@ public class HouseServantDialogue extends DialoguePlugin { interpreter.sendDialogues(servant, servant.getId() == 4243 ? FacialExpression.HALF_GUILTY : null, "You have no need to pay me yet, I haven't performed", "any of my services for you."); break; } - if (!player.getInventory().containsItem(new Item(995, type.getCost()))) { + if (!player.getInventory().containsItem(new Item(Items.COINS_995, type.getCost()))) { interpreter.sendDialogues(servant, servant.getId() == 4243 ? FacialExpression.HALF_GUILTY : null, "Thanks for the kind gesture, but you don't have enough", "money to pay me. I require " + type.getCost() + " coins every eight uses", "of my services."); break; } - if (player.getInventory().remove(new Item(995, type.getCost()))) { + if (player.getInventory().remove(new Item(Items.COINS_995, type.getCost()))) { interpreter.sendDialogues(servant, servant.getId() == 4243 ? FacialExpression.HALF_GUILTY : null, "Thank you very much."); servant.setUses(0); } @@ -311,18 +312,18 @@ public class HouseServantDialogue extends DialoguePlugin { case 60: switch(buttonId) { case 1: //planks - bankFetch(player, new Item(960)); + bankFetch(player, new Item(Items.PLANK_960)); stage = 100; break; case 2: //oak - bankFetch(player, new Item(8778)); + bankFetch(player, new Item(Items.OAK_PLANK_8778)); stage = 100; break; case 3: //teak - bankFetch(player, new Item(8780)); + bankFetch(player, new Item(Items.TEAK_PLANK_8780)); break; case 4: //mahog - bankFetch(player, new Item(8782)); + bankFetch(player, new Item(Items.MAHOGANY_PLANK_8782)); break; case 5: options("Soft clay", "Limestone bricks", "Steel bars", "Cloth", "More options"); @@ -333,16 +334,16 @@ public class HouseServantDialogue extends DialoguePlugin { case 61: switch(buttonId) { case 1: //clay - bankFetch(player, new Item(1761)); + bankFetch(player, new Item(Items.SOFT_CLAY_1761)); break; case 2: //lime - bankFetch(player, new Item(3420)); + bankFetch(player, new Item(Items.LIMESTONE_BRICK_3420)); break; case 3: //steel bars - bankFetch(player, new Item(2353)); + bankFetch(player, new Item(Items.STEEL_BAR_2353)); break; case 4: //cloth - bankFetch(player, new Item(8790)); + bankFetch(player, new Item(Items.BOLT_OF_CLOTH_8790)); break; case 5: options("Gold leaves", "Marble blocks", "Magic stones"); @@ -353,13 +354,13 @@ public class HouseServantDialogue extends DialoguePlugin { case 62: switch(buttonId) { case 1: //leaves - bankFetch(player, new Item(4692)); + bankFetch(player, new Item(Items.GOLD_LEAF_4692)); break; case 2: //marble - bankFetch(player, new Item(8786)); + bankFetch(player, new Item(Items.MARBLE_BLOCK_8786)); break; case 3: //magic stones - bankFetch(player, new Item(4703)); + bankFetch(player, new Item(Items.MAGIC_STONE_8788)); break; } break; @@ -478,7 +479,7 @@ public class HouseServantDialogue extends DialoguePlugin { return; } end(); - if (player.getInventory().remove(new Item(item.getId(), amt)) && player.getInventory().remove(new Item(995, amt * plank.getPrice()))) { + if (player.getInventory().remove(new Item(item.getId(), amt)) && player.getInventory().remove(new Item(Items.COINS_995, amt * plank.getPrice()))) { manager.getServant().setItem(new Item(plank.getPlank().getId(), amt)); servant.setInvisible(true); servant.getLocks().lockMovement(100); @@ -565,4 +566,4 @@ public class HouseServantDialogue extends DialoguePlugin { } }, PulseType.STANDARD); } -} \ No newline at end of file +} diff --git a/Server/src/main/content/global/skill/construction/npc/StonemasonPlugin.java b/Server/src/main/content/global/skill/construction/npc/StonemasonPlugin.java index 1810c4cb1..b83bb5965 100644 --- a/Server/src/main/content/global/skill/construction/npc/StonemasonPlugin.java +++ b/Server/src/main/content/global/skill/construction/npc/StonemasonPlugin.java @@ -48,7 +48,7 @@ public class StonemasonPlugin extends OptionHandler { * Constructs a new {@Code SupplyStore} {@Code Object} *//* public SupplyStore() { - super("Keldagrim Stonemason", new Item[] { new Item(3420, 1000), new Item(8786, 20), new Item(8784, 20), new Item(8788, 10) }, false); + super("Keldagrim Stonemason", new Item[] { new Item(Items.LIMESTONE_BRICK_3420, 1000), new Item(Items.MARBLE_BLOCK_8786, 20), new Item(Items.GOLD_LEAF_8784, 20), new Item(Items.MAGIC_STONE_8788, 10) }, false); } @Override diff --git a/Server/src/main/content/global/skill/cooking/CookableItems.java b/Server/src/main/content/global/skill/cooking/CookableItems.java index 4ae79ffb7..cea0f356d 100644 --- a/Server/src/main/content/global/skill/cooking/CookableItems.java +++ b/Server/src/main/content/global/skill/cooking/CookableItems.java @@ -6,148 +6,167 @@ import core.game.node.item.Item; import java.util.HashMap; public enum CookableItems { - /** meats */ - CHICKEN(2140,2138,2144,1,30,34,128,512), - UGTHANKI(2140,2138,2144,1,40,100,0,512), - RABBIT(3228,3226,7222,1,30,34,128,512), - CRAB(7521, 7518, 7520,21, 100, 62,85,438), - - /** fish */ - CRAYFISH(13433, 13435, 13437,1, 30, 34,128,512), - SHRIMP(315, 317, 7954,1, 30, 34,128,512), - KARAMBWANJI(3151, 3150, 592,1, 10, 28,128,512), - SARDINE(325, 327, 369,1, 40, 38,118,492), - ANCHOVIES(319, 321, 323,1, 30, 34,128,512), - HERRING(347, 345, 357,5, 50, 41,108,472), - MACKEREL(355, 353, 357,10, 60, 45,98,452), - TROUT(333, 335, 343,15, 70, 50,88,432), - COD(339, 341, 343,18, 75, 52,74,420), - PIKE(351, 349, 343,20, 80, 53,78,412), - SALMON(329, 331, 343,25, 90, 58,68,392), - SLIMY_EEL(3381, 3379, 3383,28, 95, 58,72,406), - TUNA(361, 359, 367,30, 100, 65,58,372), - RAINBOW_FISH(10136, 10138, 10140,35, 110, 60,48,387), - CAVE_EEL(5003, 5001, 5002,38, 115, 74,42,386), - LOBSTER(379, 377, 381,40, 120, 74,38,332), - BASS(365, 363, 367,43, 130, 79,28,312), - SWORDFISH(373, 371, 375,45, 140, 86,18,292), - LAVA_EEL(2149, 2148, 3383,53, 30, 53,12,282), - MONKFISH(7946, 7944, 7948,62, 150, 92,11,275), - SHARK(385, 383, 387,80, 210, 100,0,202), - SEA_TURTLE(397, 395, 399,82, 212, 100,0,202), - MANTA_RAY(391, 389, 393,91, 216, 100,0,202), - KARAMBWAN(3144, 3142, 3146,30, 190, 100,0,255), - - /** snails */ - THIN_SNAIL(3369, 3363, 3375,12, 70, 47,109,502), - LEAN_SNAIL(3371, 3365, 3375,17, 80, 50,100,486), - FAT_SNAIL(3373, 3367, 3375,22, 95, 56,95,456), - - //Bread - BREAD(2309, 2307, 2311,1,40,37,118,492), - - //Cake - CAKE(1891, 1889, 1903, 40, 180, 74,38,332), - - //Beef(s) (Rat, Bear, Cow, Yak) - BEEF(2142,2132,2146,1,30,34,128,512), - RAT_MEAT(2142,2134,2146,1,30,34,128,512), - BEAR_MEAT(2142,2136,2146,1,30,34,128,512), - YAK_MEAT(2142,10816,2146,1,30,34,128,512), - - //Skewered foods - SKEWER_ROAST_BEAST(9988, 9986, 9990,21, 99, 82,108,496), - SKEWER_CHOMPY(2878, 2876, 7226,30, 140, 99,85,476), - SKEWER_JUBBLY(7568, 7566, 7570,41, 160, 140,75,456), - SKEWER_ROAST_BIRD(9980, 9984, 9982,11, 62, 99,115,502), - SKEWER_ROAST_RABBIT(7223, 7224, 7222,16, 72, 99,113,499), - - //Pies - PIE_REDBERRY(2325, 2321, 2329, 10, 78, 50,78,412), - PIE_MEAT(2327, 2319, 2329, 20, 110, 53,78,412), - PIE_MUD(7170, 7168, 2329, 29,128,63,78,412), - PIE_APPLE(2323,2317,2329,30,130,63,78,412), - GARDEN_PIE(7178,7176,2329,34,138, 68,78,412), - FISH_PIE(7188,7186,2329,47,164,74,78,412), - ADMIRAL_PIE(7198,7196,2329,70,210,94,78,412), - WILD_PIE(7208,7206,2329,85,240,140,78,412), - SUMMER_PIE(7218,7216,2329,95,260,160,78,412), - - //Pizzas - PIZZA_PLAIN(2289, 2287,2305, 35, 143, 68,48,352), - - //Bowl foods - BOWL_STEW(2003, 2001, 2005,25, 117, 58,68,392), - BOWL_NETTLE(4239, 4237, 4239,20, 52, 1,68,392), - BOWL_EGG(7078, 7076, 7090,13, 50, 48,68,392), - BOWL_ONION(7084, 1871, 7092,43, 60, 77,68,392), - BOWL_MUSHROOM(7082, 7080, 7094,46, 60, 90,68,392), - - /* Vegetables */ - BAKED_POTATO(6701, 1942, 6699, 7, 15, 41, 108, 472), - SWEETCORN(5988, 5986, 5990, 28, 104, 54, 90, 424), - - //Miscellaneous - RAW_OOMLIE(Items.RAW_OOMLIE_2337, 0, Items.BURNT_OOMLIE_2426, 50, 0, 999,0,0), // always burns - OOMLIE_WRAP(Items.COOKED_OOMLIE_WRAP_2343, Items.WRAPPED_OOMLIE_2341, Items.BURNT_OOMLIE_WRAP_2345, 50, 110, 999,0,0), - SEAWEED(Items.SEAWEED_401,0,Items.SODA_ASH_1781,0,0,999,0,0), - /** - * Sinew gets overridden by BEEF in this enum, due to values being looked up by the items RAW id. - * This gets corrected in {@link SinewCookingPulse} - */ - SINEW(Items.SINEW_9436,Items.RAW_BEEF_2132,Items.SINEW_9436,0,3,0,0,0); - - public final static HashMapcookingMap = new HashMap<>(); - public final static HashMapintentionalBurnMap = new HashMap<>(); - public final int raw,cooked,level,burnLevel,burnt; - public final double experience; - double low,high; - CookableItems(int cooked, int raw, int burnt, int level, double experience, int burnLevel, double low, double high){ - this.raw = raw; - this.cooked = cooked; - this.burnt = burnt; - this.level = level; - this.experience = experience; - this.burnLevel = burnLevel; - this.low = low; - this.high = high; - } - - static{ - CookableItems[] cookableItems = values(); - int cookableItemsLength = cookableItems.length; - for(int x = 0; x < cookableItemsLength; x++){ - CookableItems item = cookableItems[x]; - cookingMap.putIfAbsent(item.raw,item); - intentionalBurnMap.putIfAbsent(item.cooked,item); - } - } - - public static CookableItems forId(int id){ - return cookingMap.get(id); - } - - public static Item getCooked(int id){ - return new Item(cookingMap.get(id).cooked); - } - - public static Item getBurnt(int id){ - return new Item(cookingMap.get(id).burnt); - } - - public static Item getRaw(int id){ - return new Item(cookingMap.get(id).raw); - } - - public static boolean intentionalBurn(int id){ - return (intentionalBurnMap.get(id) != null); - } - - public static Item getIntentionalBurn(int id){ - return new Item(intentionalBurnMap.get(id).burnt); - } - - public static int getBurnLevel(int id){ - return cookingMap.get(id).burnLevel; - } + // meats + CHICKEN(2140, 2138, 2144, 1, 30, 128, 512, 128, 512), + UGTHANKI(2140, 2138, 2144, 1, 40, 40, 252, 30, 253), + RABBIT(3228, 3226, 7222, 1, 30, 128, 512, 128, 512), + CRAB(7521, 7518, 7520, 21, 100, 57, 377, 57, 377), + CHOMPY(2878, 2876, 2880, 30, 100, 200, 255, 200, 255), + JUBBLY(7568, 7566, 7570, 41, 160, 195, 250, 195, 250), // Can burn at 99 cooking. Ballpark low/high. + + // fish + CRAYFISH(13433, 13435, 13437, 1, 30, 128, 512, 128, 512), + SHRIMP(315, 317, 7954, 1, 30, 128, 512, 128, 512), + KARAMBWANJI(3151, 3150, 592, 1, 10, 200, 400, 200, 400), + SARDINE(325, 327, 369, 1, 40, 118, 492, 118, 492), + ANCHOVIES(319, 321, 323, 1, 30, 128, 512, 128, 512), + HERRING(347, 345, 357, 5, 50, 108, 472, 108, 472), + MACKEREL(355, 353, 357, 10, 60, 98, 452, 98, 452), + TROUT(333, 335, 343, 15, 70, 88, 432, 88, 432), + COD(339, 341, 343, 18, 75, 83, 422, 88, 432), + PIKE(351, 349, 343, 20, 80, 78, 412, 78, 412), + SALMON(329, 331, 343, 25, 90, 68, 392, 68, 392), + SLIMY_EEL(3381, 3379, 3383, 28, 95, 63, 382, 63, 382), + TUNA(361, 359, 367, 30, 100, 58, 372, 58, 372), + RAINBOW_FISH(10136, 10138, 10140, 35, 110, 56, 370, 56, 370), + CAVE_EEL(5003, 5001, 5002, 38, 115, 38, 332, 38, 332), + LOBSTER(379, 377, 381, 40, 120, 38, 332, 38, 332), + BASS(365, 363, 367, 43, 130, 33, 312, 33, 312), + SWORDFISH(373, 371, 375, 45, 140, 18, 292, 30, 310), + LAVA_EEL(2149, 2148, 3383, 53, 30, 256, 256, 256, 256), // never burn + MONKFISH(7946, 7944, 7948, 62, 150, 11, 275, 13, 280), + SHARK(385, 383, 387, 80, 210, 1, 202, 1, 232), + SEA_TURTLE(397, 395, 399, 82, 212, 1, 202, 1, 222), + MANTA_RAY(391, 389, 393, 91, 216, 1, 202, 1, 222), + KARAMBWAN(3144, 3142, 3146, 30, 190, 70, 255, 70, 255), + + // snails + THIN_SNAIL(3369, 3363, 3375, 12, 70, 93, 444, 93, 444), + LEAN_SNAIL(3371, 3365, 3375, 17, 80, 85, 428, 93, 444), + FAT_SNAIL(3373, 3367, 3375, 22, 95, 73, 402, 73, 402), + + // bread + BREAD(2309, 2307, 2311, 1, 40, 118, 492, 118, 492), + PITTA_BREAD(1865, 1863, 1867, 58, 40,118, 492, 118, 492), + + // cake + CAKE(1891, 1889, 1903, 40, 180, 0, 0, 38, 332), + + // beef(s) (rat, bear, cow, yak) + BEEF(2142, 2132, 2146, 1, 30, 128, 512, 128, 512), + RAT_MEAT(2142, 2134, 2146, 1, 30, 128, 512, 128, 512), + BEAR_MEAT(2142, 2136, 2146, 1, 30, 128, 512, 128, 512), + YAK_MEAT(2142, 10816, 2146, 1, 30, 128, 512, 128, 512), + + // skewered foods + SKEWER_CHOMPY(2878, 7230, 2880, 30, 100, 200, 255, 200, 255), + SKEWER_ROAST_RABBIT(7223, 7224, 7222, 16, 72, 160, 255, 160, 255), + SKEWER_ROAST_BIRD(9980, 9984, 9982, 11, 62, 155, 255, 155, 255), + SKEWER_ROAST_BEAST(9988, 9992, 9990, 21, 82.5, 180, 255, 180, 255), + + // pies + REDBERRY_PIE(2325, 2321, 2329, 10, 78, 0, 0, 98, 452), + MEAT_PIE(2327, 2319, 2329, 20, 110, 0, 0, 78, 412), + MUD_PIE(7170, 7168, 2329, 29, 128, 0, 0, 58, 372), + APPLE_PIE(2323, 2317, 2329, 30, 130, 0, 0, 58, 372), + GARDEN_PIE(7178, 7176, 2329, 34, 138, 0, 0, 48, 352), + FISH_PIE(7188, 7186, 2329, 47, 164, 0, 0, 38, 332), + ADMIRAL_PIE(7198, 7196, 2329, 70, 210, 0, 0, 15, 270), + WILD_PIE(7208, 7206, 2329, 85, 240, 0, 0, 1, 222), + SUMMER_PIE(7218, 7216, 2329, 95, 260, 0, 0, 1, 212), + + // pizzas + PIZZA_PLAIN(2289, 2287, 2305, 35, 143, 0, 0, 48, 352), + + // bowl foods + BOWL_STEW(2003, 2001, 2005, 25, 117, 68, 392, 68, 392), + BOWL_CURRY(2011, 2009, 2013, 60, 280, 38, 332, 38, 332), + BOWL_NETTLE(4239, 4237, 4239, 20, 52, 78, 412, 78, 412), + BOWL_EGG(7078, 7076, 7090, 13, 50, 0, 0, 90, 438), + BOWL_ONION(7084, 1871, 7092, 43, 60, 36, 322, 36, 322), + BOWL_MUSHROOM(7082, 7080, 7094, 46, 60, 16, 282, 16, 282), + + // vegetables + BAKED_POTATO(6701, 1942, 6699, 7, 15, 0, 0, 108, 472), + SWEETCORN(5988, 5986, 5990, 28, 104, 78, 412, 78, 412), + + // miscellaneous + RAW_OOMLIE(Items.RAW_OOMLIE_2337, 0, Items.BURNT_OOMLIE_2426, 50, 0, 0, 0, 0, 0), // always burns + OOMLIE_WRAP(Items.COOKED_OOMLIE_WRAP_2343, Items.WRAPPED_OOMLIE_2341, Items.BURNT_OOMLIE_WRAP_2345, 50, 30, 106, 450, 112, 476), + SEAWEED(Items.SEAWEED_401, 0, Items.SODA_ASH_1781, 0, 0, 0, 0, 0, 0), + + /** + * Sinew gets overridden by BEEF in this enum, due to values being looked up by the items RAW id. + * This gets corrected in {@link SinewCookingPulse} + */ + SINEW(Items.SINEW_9436, Items.RAW_BEEF_2132, Items.SINEW_9436, 0, 3, 0, 0, 0, 0); + + public final static HashMap cookingMap = new HashMap<>(); + public final static HashMap intentionalBurnMap = new HashMap<>(); + public final static HashMap gauntletValues = new HashMap<>(); + public final static HashMap lumbridgeRangeValues = new HashMap<>(); + public final int cooked, raw, burnt, level, low, high, lowRange, highRange; + public final double experience; + + CookableItems(int cooked, int raw, int burnt, int level, double experience, int low, int high, int lowRange, int highRange) { + this.cooked = cooked; + this.raw = raw; + this.burnt = burnt; + this.level = level; + this.experience = experience; + this.low = low; + this.high = high; + this.lowRange = lowRange; + this.highRange = highRange; + } + + static { + for (CookableItems item : values()) { + cookingMap.putIfAbsent(item.raw, item); + intentionalBurnMap.putIfAbsent(item.cooked, item); + } + + gauntletValues.put(Items.RAW_LOBSTER_377, new int[]{55, 368}); + gauntletValues.put(Items.RAW_SWORDFISH_371, new int[]{30, 310}); + gauntletValues.put(Items.RAW_MONKFISH_7944, new int[]{24, 290}); + gauntletValues.put(Items.RAW_SHARK_383, new int[]{15, 270}); + + lumbridgeRangeValues.put(Items.BREAD_DOUGH_2307, new int[]{128, 512}); + lumbridgeRangeValues.put(Items.RAW_BEEF_2132, new int[]{138, 532}); + lumbridgeRangeValues.put(Items.RAW_RAT_MEAT_2134, new int[]{138, 532}); + lumbridgeRangeValues.put(Items.RAW_BEAR_MEAT_2136, new int[]{138, 532}); + lumbridgeRangeValues.put(Items.RAW_YAK_MEAT_10816, new int[]{138, 532}); + lumbridgeRangeValues.put(Items.RAW_CHICKEN_2138, new int[]{138, 532}); + lumbridgeRangeValues.put(Items.RAW_SHRIMPS_317, new int[]{138, 532}); + lumbridgeRangeValues.put(Items.RAW_ANCHOVIES_321, new int[]{138, 532}); + lumbridgeRangeValues.put(Items.RAW_SARDINE_327, new int[]{128, 512}); + lumbridgeRangeValues.put(Items.RAW_HERRING_345, new int[]{118, 492}); + lumbridgeRangeValues.put(Items.RAW_MACKEREL_353, new int[]{108, 472}); + lumbridgeRangeValues.put(Items.UNCOOKED_BERRY_PIE_2321, new int[]{108, 462}); + lumbridgeRangeValues.put(Items.THIN_SNAIL_3363, new int[]{103, 464}); + lumbridgeRangeValues.put(Items.RAW_TROUT_335, new int[]{98, 452}); + lumbridgeRangeValues.put(Items.LEAN_SNAIL_3365, new int[]{95, 448}); + lumbridgeRangeValues.put(Items.RAW_COD_341, new int[]{93, 442}); + lumbridgeRangeValues.put(Items.RAW_PIKE_349, new int[]{88, 432}); + lumbridgeRangeValues.put(Items.UNCOOKED_MEAT_PIE_2319, new int[]{88, 432}); + lumbridgeRangeValues.put(Items.FAT_SNAIL_3367, new int[]{83, 422}); + lumbridgeRangeValues.put(Items.UNCOOKED_STEW_2001, new int[]{78, 412}); + lumbridgeRangeValues.put(Items.RAW_SALMON_331, new int[]{78, 402}); + } + + public static CookableItems forId(int id) { + return cookingMap.get(id); + } + + public static Item getBurnt(int id) { + return new Item(cookingMap.get(id).burnt); + } + + public static boolean intentionalBurn(int id) { + return (intentionalBurnMap.get(id) != null); + } + + public static Item getIntentionalBurn(int id) { + return new Item(intentionalBurnMap.get(id).burnt); + } } diff --git a/Server/src/main/content/global/skill/cooking/CookingDialogue.kt b/Server/src/main/content/global/skill/cooking/CookingDialogue.kt index f3ea44026..a560c8026 100644 --- a/Server/src/main/content/global/skill/cooking/CookingDialogue.kt +++ b/Server/src/main/content/global/skill/cooking/CookingDialogue.kt @@ -3,7 +3,6 @@ package content.global.skill.cooking import core.api.* import core.cache.def.impl.ItemDefinition import core.game.node.scenery.Scenery -import content.global.skill.cooking.CookableItems import content.global.skill.cooking.CookingRewrite.Companion.cook import core.net.packet.PacketRepository import core.net.packet.context.ChildPositionContext @@ -15,6 +14,7 @@ import core.tools.START_DIALOGUE /** * @author Ceikry * @author bushtail - fixing it up + * @auther Woah - for more fixing up */ class CookingDialogue(vararg val args: Any) : DialogueFile(){ @@ -47,7 +47,7 @@ class CookingDialogue(vararg val args: Any) : DialogueFile(){ "Dry the meat into sinew", "Cook the meat" ) - stage = 100 + stage = if (amountInInventory(player!!, initial) > 1) 100 else 101 return } } @@ -78,7 +78,7 @@ class CookingDialogue(vararg val args: Any) : DialogueFile(){ when (buttonID) { 1 -> { product = Items.SINEW_9436 - display(Items.COOKED_MEAT_2142) + display() } 2 -> { product = CookableItems.forId(initial).cooked @@ -86,6 +86,19 @@ class CookingDialogue(vararg val args: Any) : DialogueFile(){ } } } + + 101 -> { + when (buttonID) { + 1 -> { + end() + cook(player!!, `object`, initial, Items.SINEW_9436, 1) + } + 2 -> { + end() + cook(player!!, `object`, initial, CookableItems.forId(initial).cooked, 1) + } + } + } } } @@ -99,12 +112,23 @@ class CookingDialogue(vararg val args: Any) : DialogueFile(){ return -1 } - fun display(vararg args : Int) { + fun display() { + player!!.packetDispatch.sendItemZoomOnInterface(initial, 160, 307, 2) + player!!.packetDispatch.sendString("



${ItemDefinition.forId(initial).name}", 307, 6) + + // Re-format this interface because it is not formatted properly for the chat-box + // Swords + PacketRepository.send(RepositionChild::class.java, ChildPositionContext(player, 307, 0, 12, 15)) + PacketRepository.send(RepositionChild::class.java, ChildPositionContext(player, 307, 1, 431, 15)) + // "How many would you like to cook?" + PacketRepository.send(RepositionChild::class.java, ChildPositionContext(player, 307, 7, 0, 12)) + // Right click context menu boxes + PacketRepository.send(RepositionChild::class.java, ChildPositionContext(player, 307, 3, 58, 27)) + PacketRepository.send(RepositionChild::class.java, ChildPositionContext(player, 307, 4, 58, 27)) + PacketRepository.send(RepositionChild::class.java, ChildPositionContext(player, 307, 5, 58, 27)) + PacketRepository.send(RepositionChild::class.java, ChildPositionContext(player, 307, 6, 58, 27)) + player!!.interfaceManager.openChatbox(307) - PacketRepository.send(RepositionChild::class.java, ChildPositionContext(player, 307, 3, 60, 90)) - PacketRepository.send(RepositionChild::class.java, ChildPositionContext(player, 307, 2, 208, 20)) - player!!.packetDispatch.sendItemZoomOnInterface(if(args.size == 1) args[0] else product, 160, 307, 2) - player!!.packetDispatch.sendString(ItemDefinition.forId(product).name, 307, 3) stage = 1 } } \ No newline at end of file diff --git a/Server/src/main/content/global/skill/cooking/CookingDialoguePlugin.java b/Server/src/main/content/global/skill/cooking/CookingDialoguePlugin.java deleted file mode 100644 index 25f49ad6f..000000000 --- a/Server/src/main/content/global/skill/cooking/CookingDialoguePlugin.java +++ /dev/null @@ -1,199 +0,0 @@ -/* -package core.game.node.entity.skill.cooking; - -import core.game.dialogue.DialoguePlugin; -import org.crandor.game.content.global.consumable.Consumables; -import org.crandor.game.content.global.consumable.Food; -import core.game.node.entity.skill.cooking.CookableItems; -import core.game.node.entity.skill.cooking.CookingPulse; -import org.crandor.game.node.entity.player.Player; -import org.crandor.game.node.entity.player.link.RunScript; -import org.crandor.game.node.item.Item; -import org.crandor.game.node.object.GameObject; -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; - -*/ -/** - * Represents the dialogue used to handle the amount to make of a cookable item. - * @author 'Vexia - * @version 1.0 - *//* - -@InitializablePlugin -public final class CookingDialoguePlugin extends DialoguePlugin { - - */ -/** - * Represents the cooking dialogue id. - *//* - - public static final int DIALOGUE_ID = 43989; - - */ -/** - * Represents the sinew item. - *//* - - private static final Item SINEW = new Item(9436); - - */ -/** - * Represents the meat item. - *//* - - private static final Item MEAT = new Item(2142); - - */ -/** - * Represents the food we're cooking. - *//* - - private int food; - - */ -/** - * Represents the obejct we're cooking on. - *//* - - private GameObject object; - - */ -/** - * Constructs a new {@code CookingDialoguePlugin} {@code Object}. - *//* - - public CookingDialoguePlugin() { - */ -/** - * empty. - *//* - - } - - */ -/** - * Constructs a new {@code CookingDialoguePlugin} {@code Object}. - * @param player the player. - *//* - - public CookingDialoguePlugin(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new CookingDialoguePlugin(player); - } - - @Override - public boolean open(Object... args) { - food = ((int) args[0]); - object = ((GameObject) args[1]); - if (args.length == 3) { - interpreter.sendOptions("Select an Option", "Dry the meat into sinew.", "Cook the meat."); - stage = 100; - return true; - } - if (player.getInventory().getAmount(CookableItems.forId(food).raw) == 1) { - end(); - cook(player,object,food,1); - return true; - } - display(); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - end(); - int amount = getAmount(buttonId); - if (amount == -1) { - player.setAttribute("runscript", new RunScript() { - @Override - public boolean handle() { - int amount = (int) value; - cook(player, object, food, amount); - return false; - } - }); - player.getDialogueInterpreter().sendInput(false, "Enter the amount:"); - } else if (amount == 28) { - player.setAttribute("runscript", new RunScript() { - @Override - public boolean handle() { - cook(player, object, food, amount); - return false; - } - }); - } - cook(player, object, food, amount); - break; - */ -/*case 100: - switch (buttonId) { - case 1: - food = Consumables.forFood(SINEW); - display(); - break; - case 2: - food = Consumables.forFood(MEAT); - display(); - break; - } - break;*//* - - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { DIALOGUE_ID }; - } - - */ -/** - * Method used to display the content food. - *//* - - public void display() { - player.getInterfaceManager().openChatbox(307); - PacketRepository.send(RepositionChild.class, new ChildPositionContext(player, 307, 3, 60, 79)); - player.getPacketDispatch().sendItemZoomOnInterface(CookableItems.getRaw(food).getId(), 160, 307, 2); - player.getPacketDispatch().sendString(CookableItems.getRaw(food).getName(), 307, 3); - stage = 0; - } - - public void cook(Player player, GameObject object, int food, int amount){ - player.getPulseManager().run(new CookingPulse(player,object,food,amount)); - } - - */ -/** - * Method used to get the amount to make based off the button id. - * @param buttonId the button id. - * @return the amount to make. - *//* - - private final int getAmount(final int buttonId) { - switch (buttonId) { - case 5: - return 1; - case 4: - return 5; - case 3: - return -1; - case 2: - return 28; - } - return -1; - } - -} -*/ diff --git a/Server/src/main/content/global/skill/cooking/CookingPlugin.java b/Server/src/main/content/global/skill/cooking/CookingPlugin.java deleted file mode 100644 index 526362223..000000000 --- a/Server/src/main/content/global/skill/cooking/CookingPlugin.java +++ /dev/null @@ -1,263 +0,0 @@ -/* -package core.game.node.entity.skill.cooking; - -import org.crandor.cache.def.impl.ItemDefinition; -import core.game.dialogue.DialoguePlugin; -import org.crandor.game.content.global.consumable.Consumables; -import org.crandor.game.content.global.consumable.Food; -import core.game.node.entity.skill.cooking.CookableItems; -import core.game.node.entity.skill.cooking.CookingPulse; -import org.crandor.game.interaction.NodeUsageEvent; -import org.crandor.game.interaction.UseWithHandler; -import org.crandor.game.node.Node; -import org.crandor.game.node.entity.player.Player; -import org.crandor.game.node.entity.player.link.RunScript; -import org.crandor.game.node.item.Item; -import org.crandor.game.node.object.GameObject; -import org.crandor.game.world.map.Direction; -import org.crandor.game.world.map.Location; -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; - -*/ -/** - * Handles the interaction of a raw food (or if intentionally burning cooked food) - * with a range/fire. - * @author ceik - *//* - -@InitializablePlugin -public class CookingPlugin extends UseWithHandler { - - public static int DIALOGUE_ID = 43989; - - */ -/** - * Represents the objects allowed to cook {@code Food} on. - *//* - - public static final int[] OBJECTS = new int[] { 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, 9439, 9440, 9441, 10824, 17640, 17641, 17642, 17643, 18039, 21795, 24285, 24329, 27251, 33498, 35449, 36815, 36816, 37426, 40110 }; - - */ -/** - * Constructs a new {@code CookingPlugin} {@code Object}. - *//* - - public CookingPlugin() { - super(2140, 1861, 3228, 7521, 3151, 325, 319, 347, 355, 333, 339, 351, 329, 3381, 361, 10136, 5003, 379, 365, 373, 2149, 7946, 385, 397, 391, 3369, 3371, 3373, 1893, 1895, 1897, 1899, 1901, 1963, 2102, 2120, 2108, 5972, 5504, 1982, 1965, 1957, 5988, 1781, 6701, 6703, 6705, 7056, 7058, 7060, 1933, 1783, 1927, 1929, 6032, 6034, 2011, 227, 1921, 1937, 7934, 7086, 4239, 7068, 7086, 2003, 7078, 7072, 4239, 7062, 7064, 7084, 1871, 7082, 2309, 1891, 1967, 1971, 2142, 9436, 2142, 2142, 2325, 2333, 2327, 2331, 7170, 2323, 2335, 7178, 7180, 7188, 7190, 7198, 7200, 7208, 7210, 7218, 7220, 2289, 2291, 2293, 2295, 2297, 2299, 2301, 2303, 315, 9988, 2878, 7568, 9980, 7223, 5982, 598, 4293, 10816, 2138, 2134, 3142, 2136, 1859, 3226, 7518, 3150, 327, 321, 345, 353, 335, 341, 349, 331, 3379, 359, 10138, 5001, 377, 363, 371, 2148, 7944, 383, 395, 389, 3363, 3365, 3367, 5986, 401, 1942, 4237, 2001, 7076, 1871, 7080, 2307, 1889, 2132, 2132, 2132, 2132, 2321, 2319, 7168, 2317, 7176, 7186, 7196, 7206, 7216, 2287, 317, 9986, 2876, 7566, 9984, 7224); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - new CookingDialoguePlugin().init(); - for (int object : OBJECTS) { - addHandler(object, OBJECT_TYPE, this); - } - return this; - } - - @Override - public boolean handle(NodeUsageEvent event) { - final GameObject object = (GameObject) event.getUsedWith(); - final int food = event.getUsedItem().getId(); - //handles intentional burning (deprecates BurnMeatPlugin) - if(CookableItems.intentionalBurn(food)){ - event.getPlayer().getPulseManager().run(new CookingPulse(event.getPlayer(),object,event.getUsedItem().getId(),true)); - return true; - } - event.getPlayer().getDialogueInterpreter().open(DIALOGUE_ID, food, event.getUsedWith()); - return true; - } - - @Override - public Location getDestination(final Player player, final Node node) { - if (node.getName().toLowerCase().equals("fire")) { - return player.getLocation().getY() > node.getLocation().getY() ? node.getLocation().transform(0, 1, 0) : player.getLocation().getX() < node.getLocation().getX() ? node.getLocation().transform(-1, 0, 0) : player.getLocation().getX() > node.getLocation().getX() ? node.getLocation().transform(1, 0, 0) : node.getLocation().transform(0, -1, 0); - } else { - Direction direction = node.getDirection(); - if (direction == Direction.NORTH) { - return node.getLocation().transform(1, 1, 0); - } else if (direction == Direction.SOUTH) { - return node.getLocation().transform(-1, 0, 0); - } - return null; - } - } - - public final class CookingDialoguePlugin extends DialoguePlugin { - - */ -/** - * Represents the sinew item. - *//* - - private final Item SINEW = new Item(9436); - - */ -/** - * Represents the meat item. - *//* - - private final Item MEAT = new Item(2142); - - */ -/** - * Represents the food we're cooking. - *//* - - private int food; - - private int product; - - private boolean nonFood = false; - - */ -/** - * Represents the obejct we're cooking on. - *//* - - private GameObject object; - - */ -/** - * Constructs a new {@code CookingDialoguePlugin} {@code Object}. - *//* - - public CookingDialoguePlugin() { - */ -/** - * empty. - *//* - - } - - */ -/** - * Constructs a new {@code CookingDialoguePlugin} {@code Object}. - * @param player the player. - *//* - - public CookingDialoguePlugin(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new CookingDialoguePlugin(player); - } - - @Override - public boolean open(Object... args) { - food = ((int) args[0]); - if (args.length == 3){ - nonFood = true; - stage = 0; - product = (int)args[1]; - display(); - return true; - } - if (player.getInventory().getAmount(CookableItems.forId(food).raw) == 1) { - end(); - cook(player,object,food,1); - return true; - } - display(); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - end(); - int amount = getAmount(buttonId); - if (amount == -1) { - player.setAttribute("runscript", new RunScript() { - @Override - public boolean handle() { - int amount = (int) value; - if(nonFood){ - cookNonFood(player,food,product,amount); - return false; - } - cook(player, object, food, amount); - return false; - } - }); - player.getDialogueInterpreter().sendInput(false, "Enter the amount:"); - } else if (amount == 28) { - player.setAttribute("runscript", new RunScript() { - @Override - public boolean handle() { - if(nonFood){ - cookNonFood(player,food,product,amount); - } - cook(player, object, food, amount); - return false; - } - }); - } - if(nonFood){ - cookNonFood(player,food,product,amount); - break; - } - cook(player, object, food, amount); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { DIALOGUE_ID }; - } - - */ -/** - * Method used to display the content food. - *//* - - public void display() { - player.getInterfaceManager().openChatbox(307); - PacketRepository.send(RepositionChild.class, new ChildPositionContext(player, 307, 3, 60, 79)); - player.getPacketDispatch().sendItemZoomOnInterface(nonFood ? product : CookableItems.getCooked(food).getId(), 160, 307, 2); - player.getPacketDispatch().sendString(nonFood ? ItemDefinition.forId(product).getName() : CookableItems.getCooked(food).getName(), 307, 3); - stage = 0; - } - - public void cookNonFood(Player player, int food, int product, int amount){ - player.getPulseManager().run(new CookingPulse(player,food,amount,product,false)); - } - - public void cook(Player player, GameObject object, int food, int amount){ - player.getPulseManager().run(new CookingPulse(player,object,food,amount)); - } - - */ -/** - * Method used to get the amount to make based off the button id. - * @param buttonId the button id. - * @return the amount to make. - *//* - - private final int getAmount(final int buttonId) { - switch (buttonId) { - case 5: - return 1; - case 4: - return 5; - case 3: - return -1; - case 2: - return 28; - } - return -1; - } - - } -} -*/ diff --git a/Server/src/main/content/global/skill/cooking/CookingPulse.java b/Server/src/main/content/global/skill/cooking/CookingPulse.java deleted file mode 100644 index f31c02aec..000000000 --- a/Server/src/main/content/global/skill/cooking/CookingPulse.java +++ /dev/null @@ -1,244 +0,0 @@ -/* -package core.game.node.entity.skill.cooking; - -import org.crandor.game.content.global.SkillcapePerks; -import plugin.tutorial.TutorialSession; -import plugin.tutorial.TutorialStage; -import core.game.node.entity.skill.SkillPulse; -import core.game.node.entity.skill.Skills; -import org.crandor.game.node.entity.impl.Animator; -import org.crandor.game.node.entity.player.Player; -import org.crandor.game.node.entity.player.link.audio.Audio; -import org.crandor.game.node.entity.player.link.diary.DiaryType; -import org.crandor.game.node.item.Item; -import org.crandor.game.node.object.GameObject; -import org.crandor.game.system.task.Pulse; -import org.crandor.game.world.update.flag.context.Animation; - -import static org.crandor.tools.RandomFunction.RANDOM; - -*/ -/** - * Pulse for cooking - * @author ceik - *//* - -public final class CookingPulse extends Pulse { - private static Player player; - - private static GameObject node; - - private static boolean intentionalBurn = false; - - //range animation - private static final Animation RANGE_ANIMATION = new Animation(883, Animator.Priority.HIGH); - - //fire animation - private static final Animation FIRE_ANIMATION = new Animation(897, Animator.Priority.HIGH); - - //Cooking sound - public static final Audio SOUND = new Audio(2393, 1, 1); - - //food's ID - private final int food; - - //amount to cook - private int amount; - - //whether or not we are cooking food - private boolean isFood = true; - - */ -/** - * Constructs a new {@code CookingPulse} {@code Object}. - * @param player the player. - * @param node the object node (fire/range) - * @param food the food to cook. - * @param amount the amount to cook - *//* - - public CookingPulse(Player player, GameObject node, final int food, final int amount) { - this.player = player; - this.node = node; - this.food = food; - this.amount = amount; - } - - */ -/** - * Another constructor for the Cooking Pulse, this time flags it as an intentional burn - * @param player the player cooking - * @param node the object being used to cook (range/fire) - * @param food the item being cooked - * @param intentionalBurn whether or not it's an intentional burn - *//* - - public CookingPulse(Player player, GameObject node, final int food, final boolean intentionalBurn) { - this.player = player; - this.food = food; - this.amount = 1; - this.intentionalBurn = intentionalBurn; - } - - */ -/** - * handler for cooking non-food items - * @param player the player cooking - * @param item the item being cooked - * @param amount the amount the player has - * @param isFood whether we are cooking food - *//* - - public CookingPulse(Player player, final int item, int amount, int product, final boolean isFood){ - this.player = player; - this.food = item; - this.amount = amount; - this.isFood = isFood; - } - - @Override - public void start() { - if (intentionalBurn || checkRequirements()) { - super.start(); - } - } - - @Override - public boolean pulse() { - if(intentionallyBurn(food) || !checkRequirements()){ - return true; - } - return reward(); - } - - public boolean checkRequirements() { //check if the player has the requirements - if (!node.isActive()) { - return true; - } - //handle cook's assistant range - if(node.getId() == 114 && !player.getQuestRepository().isComplete("Cook's Assistant")){ - player.getPacketDispatch().sendMessage("You need to have completed the Cook's Assistant quest in order to use that range."); - return false; - } - if (player.getSkills().getLevel(Skills.COOKING) < CookableItems.forId(food).level) { - player.getDialogueInterpreter().sendDialogue("You need a cooking level of " + CookableItems.forId(food).level + " to cook this."); - return false; - } - int inventoryAmount = player.getInventory().getAmount(CookableItems.forId(food).raw); - if (amount > inventoryAmount) { - amount = inventoryAmount; - } - if (amount < 1) { - return false; - } - return true; - } - - public void animate() { - player.animate(getAnimation(node)); - } - - public boolean reward() { - if (getDelay() == 1) { - setDelay(node.getName().toLowerCase().equals("range") ? 5 : 4); - return false; - } - //handle tutorial stuff - if(!TutorialSession.getExtension(player).finished()){ - updateTutorial(player); - amount --; - return true; - } - - if (cook(player,node,isBurned(player,node,food),food)) { - amount--; - } else { - return true; - } - return amount < 1; - } - - public boolean cook(final Player player, final GameObject object, final boolean burned, final int food) { - Item item = CookableItems.getRaw(food); - if(intentionalBurn){ //stops the cook function from being ran if we burning on purpose - return false; - } - //lumbridge diary - if (CookableItems.forId(food) != null && player.getInventory().remove(item)) { - if (!burned) { - player.getInventory().add(CookableItems.getCooked(food)); - } else { - player.getInventory().add(CookableItems.getBurnt(food)); - } - player.getSkills().addExperience(Skills.COOKING, burned ? 0 : CookableItems.forId(food).experience, true); - player.getPacketDispatch().sendMessage(getMessage(item, burned)); - player.getAudioManager().send(SOUND); - return true; - } - return false; - } - - public boolean intentionallyBurn(int food){ - if(intentionalBurn && player.getInventory().remove(new Item(food))){ //only runs if it's flagged as intentional burning - player.getInventory().add(CookableItems.getIntentionalBurn(food)); - player.getPacketDispatch().sendMessage("You deliberately burn the perfectly good piece of meat."); - return true; - } else { - return false; - } - } - - public String getMessage(Item food, boolean burned){ - if(!burned) { - return "You manage to cook some " + food.getName().replace("Raw ", ""); - } else { - return "You accidentally burn some " + food.getName().replace("Raw ",""); - } - } - - public boolean isBurned(final Player player, final GameObject object, int food) { - double burn_stop = (double)CookableItems.getBurnLevel(food); - if (SkillcapePerks.hasSkillcapePerk(player, SkillcapePerks.COOKING)) { - return false; - } - if (player.getSkills().getLevel(Skills.COOKING) > burn_stop) { - return false; - } - double burn_chance = 60.0 + (object.getName().equals("fire") ? 1.00 : 0) - (object.getId() == 114 ? 1.00 : 0); - double cook_level = (double) player.getSkills().getLevel(Skills.COOKING); - double lev_needed = (double) CookableItems.forId(food).level; - double multi_a = (burn_stop - lev_needed); - double burn_dec = (burn_chance / multi_a); - double multi_b = (cook_level - lev_needed); - burn_chance -= (multi_b * burn_dec); - double randNum = RANDOM.nextDouble() * 100.0; - return !(burn_chance <= randNum); - } - - public boolean updateTutorial(Player player){ - if (TutorialSession.getExtension(player).getStage() == 14) { - TutorialStage.load(player, 15, false); - return cook(player, node,true, food); - } else if (TutorialSession.getExtension(player).getStage() == 15) { - TutorialStage.load(player, 16, false); - return cook(player, node, false, food); - } - if (TutorialSession.getExtension(player).getStage() == 20) { - TutorialStage.load(player, 21, false); - return cook(player, node, false, food); - } - return cook(player, node, isBurned(player,node,food),food); - } - - */ -/** - * Gets the animation to use based on the interacting object. - * @param object the object. - * @return the animation. - *//* - - private final Animation getAnimation(final GameObject object) { - return !object.getName().toLowerCase().equals("fire") ? RANGE_ANIMATION : FIRE_ANIMATION; - } -} -*/ 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 5da124cb8..7bc993a8b 100644 --- a/Server/src/main/content/global/skill/cooking/CookingRewrite.kt +++ b/Server/src/main/content/global/skill/cooking/CookingRewrite.kt @@ -1,5 +1,6 @@ package content.global.skill.cooking +import core.api.amountInInventory import core.game.interaction.IntType import core.game.interaction.InteractionListener import core.game.node.entity.player.Player @@ -15,6 +16,7 @@ import org.rs09.consts.Items.UNCOOKED_CAKE_1889 /** * @author Ceikry * @author bushtail - added bear meat for sinew making + * @author Woah - added single food cooking */ class CookingRewrite : InteractionListener { @@ -47,14 +49,24 @@ class CookingRewrite : InteractionListener { } //cook a standard item - player.dialogueInterpreter.open(CookingDialogue(item.id,obj)) + if (amountInInventory(player, item.id) > 1) { + player.dialogueInterpreter.open(CookingDialogue(item.id,obj)) + } else { + // Don't display dialogue if player only has *1* of the item + val product = if (CookableItems.intentionalBurn(item.id)) { // checks intentional burning + CookableItems.getIntentionalBurn(item.id).id + } else { + CookableItems.forId(item.id).cooked + } + cook(player, obj, item.id, product, 1) + } return@onUseWith true } } 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, 3039, 3769, 3775, 4265, 4266, 5249, 5499, 5631, 5632, 5981, 9682, 10433, 11404, 11405, 11406, 12102, 12796, 13337, 13881, 14169, 14919, 15156, 20000, 20001, 21620, 21792, 22713, 22714, 23046, 24283, 24284, 25155, 25156, 25465, 25730, 27297, 29139, 30017, 32099, 33500, 34495, 34546, 36973, 37597, 37629, 37726, 114, 4172, 5275, 8750, 16893, 22154, 34410, 34565, 114, 9085, 9086, 9087, 12269, 15398, 25440, 25441, 2724, 2725, 2726, 4618, 4650, 5165, 6093, 6094, 6095, 6096, 8712, 9374, 9439, 9440, 9441, 10824, 17640, 17641, 17642, 17643, 18039, 18170, 21795, 24285, 24329, 27251, 33498, 35449, 36815, 36816, 37426, 40110, 10377) @JvmStatic fun cook(player: Player, `object`: Scenery?, initial: Int, product: Int, amount: Int) { diff --git a/Server/src/main/content/global/skill/cooking/IntentionalBurnPulse.java b/Server/src/main/content/global/skill/cooking/IntentionalBurnPulse.java index ffb0c23da..051f5a14c 100644 --- a/Server/src/main/content/global/skill/cooking/IntentionalBurnPulse.java +++ b/Server/src/main/content/global/skill/cooking/IntentionalBurnPulse.java @@ -3,6 +3,9 @@ package content.global.skill.cooking; import core.game.node.entity.player.Player; import core.game.node.item.Item; import core.game.node.scenery.Scenery; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.playAudio; public class IntentionalBurnPulse extends StandardCookingPulse { int initial,product,amount; @@ -19,7 +22,7 @@ public class IntentionalBurnPulse extends StandardCookingPulse { @Override public boolean checkRequirements() { - return true; + return object.isActive(); } @Override @@ -47,7 +50,7 @@ public class IntentionalBurnPulse extends StandardCookingPulse { if (player.getInventory().remove(initialItem)) { player.getInventory().add(productItem); player.getPacketDispatch().sendMessage(getMessage(initialItem, productItem, burned)); - player.getAudioManager().send(SOUND); + playAudio(player, Sounds.FRY_2577); return true; } return false; diff --git a/Server/src/main/content/global/skill/cooking/SkeweredFoodPlugin.java b/Server/src/main/content/global/skill/cooking/SkeweredFoodPlugin.java index d7be08283..8f02bfcb9 100644 --- a/Server/src/main/content/global/skill/cooking/SkeweredFoodPlugin.java +++ b/Server/src/main/content/global/skill/cooking/SkeweredFoodPlugin.java @@ -7,6 +7,7 @@ import core.game.node.entity.player.Player; import core.game.node.item.Item; import core.plugin.Initializable; import core.plugin.Plugin; +import org.rs09.consts.Items; /** * Represents the plugin used to make skwered items. @@ -56,7 +57,10 @@ public class SkeweredFoodPlugin extends UseWithHandler { * @date 22/12/2013 */ public enum SkeweredSet { - RABBIT(new Item(3226), new Item(7224)), CHOMPY(new Item(2876), new Item(7230)), BIRD(new Item(9978), new Item(9984)), BEAST(new Item(9986), new Item(9992)); + CHOMPY(new Item(Items.RAW_CHOMPY_2876), new Item(Items.SKEWERED_CHOMPY_7230)), + RABBIT(new Item(Items.RAW_RABBIT_3226), new Item(Items.SKEWERED_RABBIT_7224)), + BIRD(new Item(Items.RAW_BIRD_MEAT_9978), new Item(Items.SKEWERED_BIRD_MEAT_9984)), + BEAST(new Item(Items.RAW_BEAST_MEAT_9986), new Item(Items.SKEWERED_BEAST_9992)); /** * Represents the raw item. diff --git a/Server/src/main/content/global/skill/cooking/StandardCookingPulse.java b/Server/src/main/content/global/skill/cooking/StandardCookingPulse.java index 3ef44db7c..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; @@ -13,6 +15,10 @@ import core.game.system.task.Pulse; import core.game.world.update.flag.context.Animation; import core.tools.RandomFunction; import org.rs09.consts.Items; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.*; +import content.data.Quests; public class StandardCookingPulse extends Pulse { //range animation @@ -24,6 +30,8 @@ public class StandardCookingPulse extends Pulse { //Cooking sound public static final Audio SOUND = new Audio(2577, 1, 1); + private static final int LUMBRIDGE_RANGE = 114; + private final int initial; private final int product; private int amount; @@ -33,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 @@ -46,7 +59,7 @@ public class StandardCookingPulse extends Pulse { properties = CookableItems.forId(initial); if (checkRequirements()) { super.start(); - cook(player, object, CookableItems.cookingMap.get(initial) != null && isBurned(player, object, initial), initial, product); + cook(player, object, properties != null && burned, initial, product); amount--; } } @@ -67,7 +80,7 @@ public class StandardCookingPulse extends Pulse { this.experience = 0; if (properties != null) { // Handle Cook's Assistant range - if (object.getId() == 114 && !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; } @@ -90,8 +103,8 @@ public class StandardCookingPulse extends Pulse { public boolean reward() { if (getDelay() == 1) { - int delay = object.getName().toLowerCase().equals("range") ? 5 : 4; - if(SkillcapePerks.isActive(SkillcapePerks.HASTY_COOKING, player)) { + int delay = object.getName().toLowerCase().contains("range") ? 5 : 4; + if (SkillcapePerks.isActive(SkillcapePerks.HASTY_COOKING, player)) { delay -= 1; } setDelay(delay); @@ -108,25 +121,28 @@ public class StandardCookingPulse extends Pulse { public boolean isBurned(final Player player, final Scenery object, int food) { boolean hasGauntlets = player.getEquipment().containsItem(new Item(Items.COOKING_GAUNTLETS_775)); - double burn_stop = (double) CookableItems.getBurnLevel(food); int effectiveCookingLevel = player.getSkills().getLevel(Skills.COOKING); - if(SkillcapePerks.isActive(SkillcapePerks.HASTY_COOKING, player)) { + if (SkillcapePerks.isActive(SkillcapePerks.HASTY_COOKING, player)) { effectiveCookingLevel -= 5; } - int gauntlets_boost = 0; CookableItems item = CookableItems.forId(food); - if (hasGauntlets && (food == Items.RAW_SWORDFISH_371 || food == Items.RAW_LOBSTER_377 || food == Items.RAW_MONKFISH_7944 || food == Items.RAW_SHARK_383)) { - burn_stop -= 6; - gauntlets_boost += 6; + int low; + int high; + if (hasGauntlets && CookableItems.gauntletValues.containsKey(food)) { + int[] successValues = CookableItems.gauntletValues.get(food); + low = successValues[0]; + high = successValues[1]; + } else if (object.getId() == LUMBRIDGE_RANGE) { + int[] successValues = CookableItems.lumbridgeRangeValues.getOrDefault(food, new int[]{item.lowRange, item.highRange}); + low = successValues[0]; + high = successValues[1]; + } else { + boolean isFire = object.getName().toLowerCase().contains("fire"); + low = isFire ? item.low : item.lowRange; + high = isFire ? item.high : item.highRange; } - if (effectiveCookingLevel >= burn_stop) { - return false; - } - int cook_level = effectiveCookingLevel + gauntlets_boost; double host_ratio = RandomFunction.randomDouble(100.0); - double low = item.low + (object.getName().contains("fire") ? 0 : (0.1 * item.low)); - double high = item.high + (object.getName().contains("fire") ? 0 : (0.1 * item.high)); - double client_ratio = RandomFunction.getSkillSuccessChance(low,high,cook_level); + double client_ratio = RandomFunction.getSkillSuccessChance(low, high, effectiveCookingLevel); return host_ratio > client_ratio; } @@ -137,11 +153,10 @@ public class StandardCookingPulse extends Pulse { //handle special cooking results (spits, cake, etc) that don't justify separate plugin switch (initial) { - case Items.RAW_BEAST_MEAT_9986: - case Items.RAW_CHOMPY_2876: - case Items.RAW_JUBBLY_7566: + case Items.SKEWERED_CHOMPY_7230: + case Items.SKEWERED_RABBIT_7224: case Items.SKEWERED_BIRD_MEAT_9984: - case Items.RAW_RABBIT_3226: // Iron spits + case Items.SKEWERED_BEAST_9992: case Items.IRON_SPIT_7225: if (RandomFunction.random(15) == 5) { player.getPacketDispatch().sendMessage("Your iron spit seems to have broken in the process."); @@ -162,12 +177,17 @@ 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)); } player.getPacketDispatch().sendMessage(getMessage(initialItem, productItem, burned)); - player.getAudioManager().send(SOUND); + playAudio(player, Sounds.FRY_2577); return true; } return false; @@ -183,11 +203,19 @@ public class StandardCookingPulse extends Pulse { if (CookableItems.intentionalBurn(food.getId())) { return "You deliberately burn the perfectly good piece of meat."; } - if (!burned) { - return "You manage to cook some " + food.getName().replace("Raw ", ""); - } else { - return "You accidentally burn some " + food.getName().replace("Raw ", ""); + + if (!burned && food.getName().startsWith("Raw")) { + return "You manage to cook some " + food.getName().replace("Raw ", "") + "."; + } else if (burned && food.getName().startsWith("Raw")) { + return "You accidentally burn some " + food.getName().replace("Raw ", "") + "."; } + + if (!burned && food.getName().startsWith(("Uncooked"))) { + return "You manage to cook some " + food.getName().replace("Uncooked ", "") + "."; + } else if (burned && food.getName().startsWith(("Uncooked"))) { + return "You accidentally burn some " + food.getName().replace("Uncooked ", "") + "."; + } + return null; } private Animation getAnimation(final Scenery object) { diff --git a/Server/src/main/content/global/skill/cooking/dairy/DairyChurnPulse.java b/Server/src/main/content/global/skill/cooking/dairy/DairyChurnPulse.java index 9b774e607..f93e436cb 100644 --- a/Server/src/main/content/global/skill/cooking/dairy/DairyChurnPulse.java +++ b/Server/src/main/content/global/skill/cooking/dairy/DairyChurnPulse.java @@ -1,5 +1,8 @@ package content.global.skill.cooking.dairy; +import content.region.fremennik.rellekka.handlers.RellekkaUtils; +import content.region.fremennik.rellekka.handlers.RellekkaZone; +import core.game.event.ResourceProducedEvent; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.diary.DiaryType; import core.game.node.entity.skill.SkillPulse; @@ -11,6 +14,9 @@ import core.game.world.update.flag.context.Animation; import core.tools.StringUtils; import org.rs09.consts.Items; +import static core.api.ContentAPIKt.location; +import static core.api.ContentAPIKt.sendMessage; + /** * Represents the skill pulse used to make a dairy product. * @author 'Vexia @@ -106,14 +112,9 @@ public final class DairyChurnPulse extends SkillPulse { } } player.getPacketDispatch().sendMessage("You make " + (StringUtils.isPlusN(dairy.getProduct().getName().toLowerCase()) ? "an" : "a") + " " + dairy.getProduct().getName().toLowerCase() + "."); + player.dispatch(new ResourceProducedEvent(dairy.getProduct().getId(), amount, node, BUCKET_OF_MILK.getId())); player.getSkills().addExperience(Skills.COOKING, dairy.getExperience(), true); - - // Seers village diary - if (player.getLocation().withinDistance(new Location(2730, 3578, 0)) - && !player.getAchievementDiaryManager().getDiary(DiaryType.SEERS_VILLAGE).isComplete(0,8)) { - player.getAchievementDiaryManager().getDiary(DiaryType.SEERS_VILLAGE).updateTask(player,0,8,true); - } - break; + break; } } 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 3d41926c4..3e2eff513 100644 --- a/Server/src/main/content/global/skill/cooking/recipe/Recipe.java +++ b/Server/src/main/content/global/skill/cooking/recipe/Recipe.java @@ -23,7 +23,7 @@ import core.game.node.item.Item; public abstract class Recipe { /** - * Represents the array of active recipes in Keldagrim.. + * Represents the array of active recipes in 2009Scape. */ // TODO: // - Making this an enum would drastically save on file/line count, since the recipes seem to mostly be plain-old-data classes @@ -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/BattleStaves.java b/Server/src/main/content/global/skill/crafting/BattleStaves.java deleted file mode 100644 index ceacd25b5..000000000 --- a/Server/src/main/content/global/skill/crafting/BattleStaves.java +++ /dev/null @@ -1,122 +0,0 @@ -package content.global.skill.crafting; - -/** - * Represents a battlestave. - * @author 'Vexia - */ -public enum BattleStaves { - WATER(571, 1395, 54, 100, 215, 149), - EARTH(575, 1399, 58, 112.5, 29415, 151), - FIRE(569, 1393, 62, 125, 2153, 152), - AIR(573, 1397, 66, 137.5, 2152, 150); - - /** - * The obelisk used. - */ - private final int obelisk; - - /** - * The product made. - */ - private final int product; - - /** - * The level needed to make the staff. - */ - private final int level; - - /** - * The exp gained. - */ - private final double exp; - - /** - * The object id. - */ - private final int objectId; - - /** - * The graphic id. - */ - private final int graphicId; - - /** - * Constructs a new {@code BattleStaves} {@Code Object} - * @param obelisk the obelisk. - * @param product the product. - * @param level the lever. - * @param exp the experience. - * @param objectId the id. - * @param graphicId the id. - */ - BattleStaves(int obelisk, int product, int level, double exp, final int objectId, final int graphicId) { - this.obelisk = obelisk; - this.product = product; - this.level = level; - this.exp = exp; - this.objectId = objectId; - this.graphicId = graphicId; - } - - /** - * Gets a battle stave by the id. - * @param id the id. - * @return the object. - */ - public static BattleStaves forId(int id) { - for (BattleStaves staff : BattleStaves.values()) { - if (staff.getObelisk() == id) { - return staff; - } - } - return null; - } - - /** - * Gets the obelisk. - * @return The obelisk. - */ - public int getObelisk() { - return obelisk; - } - - /** - * Gets the product. - * @return The product. - */ - public int getProduct() { - return product; - } - - /** - * Gets the level. - * @return The level. - */ - public int getLevel() { - return level; - } - - /** - * Gets the objectId. - * @return The objectId. - */ - public int getObjectId() { - return objectId; - } - - /** - * Gets the graphicId. - * @return The graphicId. - */ - public int getGraphicId() { - return graphicId; - } - - /** - * Gets the exp. - * @return The exp. - */ - public double getExp() { - return exp; - } -} diff --git a/Server/src/main/content/global/skill/crafting/BattlestaffListener.kt b/Server/src/main/content/global/skill/crafting/BattlestaffListener.kt new file mode 100644 index 000000000..bb31a0689 --- /dev/null +++ b/Server/src/main/content/global/skill/crafting/BattlestaffListener.kt @@ -0,0 +1,89 @@ +package content.global.skill.crafting + +import core.api.* +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.player.link.diary.DiaryType +import core.game.node.entity.skill.Skills +import org.rs09.consts.Items +import kotlin.math.min + +class BattlestaffListener : InteractionListener { + + private val battlestaff = intArrayOf(Items.BATTLESTAFF_1391, Items.DRAMEN_STAFF_772) + val orbs = BattlestaffProduct.values().map { it.requiredOrbItemId }.toIntArray() + + override fun defineListeners() { + onUseWith(IntType.ITEM, orbs, *battlestaff) { player, used, with -> + val product = BattlestaffProduct.productMap[used.id] ?: return@onUseWith true + + if (!hasLevelDyn(player, Skills.CRAFTING, product.minimumLevel)) { + sendMessage(player, "You need a Crafting level of ${product.minimumLevel} to make this.") + return@onUseWith true + } + + // Avoids sending dialogue if only one can be created + if (amountInInventory(player, used.id) == 1 || amountInInventory(player, with.id) == 1) { + + if (removeItem(player, product.requiredOrbItemId) && removeItem(player, with.id)) { + addItem(player, product.producedItemId, product.amountProduced) + rewardXP(player, Skills.CRAFTING, product.experience) + } + + if (product.producedItemId == Items.AIR_BATTLESTAFF_1397) { + player.achievementDiaryManager.finishTask(player, DiaryType.VARROCK, 2, 6) + } + + return@onUseWith true + } + + sendSkillDialogue(player) { + withItems(product.producedItemId) + create { _, amount -> + + runTask(player, 2, amount) { + if (amount < 1) return@runTask + + if (removeItem(player, product.requiredOrbItemId) && removeItem(player, with.id)) { + addItem(player, product.producedItemId, product.amountProduced) + rewardXP(player, Skills.CRAFTING, product.experience) + } + + if (product.producedItemId == Items.AIR_BATTLESTAFF_1397) { + player.achievementDiaryManager.finishTask(player, DiaryType.VARROCK, 2, 6) + } else return@runTask + } + } + + calculateMaxAmount { _ -> + min(amountInInventory(player, with.id), amountInInventory(player, used.id)) + } + } + + return@onUseWith true + } + } + + enum class BattlestaffProduct( + val requiredOrbItemId: Int, + val producedItemId: Int, + val amountProduced: Int, + val minimumLevel: Int, + val experience: Double, + ) { + WATER_BATTLESTAFF(Items.WATER_ORB_571, Items.WATER_BATTLESTAFF_1395, 1, 54, 100.0), + EARTH_BATTLESTAFF(Items.EARTH_ORB_575, Items.EARTH_BATTLESTAFF_1399, 1, 58, 112.5), + FIRE_BATTLESTAFF(Items.FIRE_ORB_569, Items.FIRE_BATTLESTAFF_1393, 1, 62, 125.0), + AIR_BATTLESTAFF(Items.AIR_ORB_573, Items.AIR_BATTLESTAFF_1397, 1, 66, 137.5); + + companion object { + val productMap = HashMap() + + init { + for (product in BattlestaffProduct.values()) { + productMap[product.requiredOrbItemId] = product + } + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/crafting/BattlestaveMakePlugin.java b/Server/src/main/content/global/skill/crafting/BattlestaveMakePlugin.java deleted file mode 100644 index 6081eed76..000000000 --- a/Server/src/main/content/global/skill/crafting/BattlestaveMakePlugin.java +++ /dev/null @@ -1,66 +0,0 @@ -package content.global.skill.crafting; - -import core.game.node.entity.player.link.diary.DiaryType; -import core.game.node.entity.skill.Skills; -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 battle stave making plugin used for zaff. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class BattlestaveMakePlugin extends UseWithHandler { - - /** - * Represents the original staff item. - */ - private static final Item STAFF = new Item(1391, 1); - - /** - * Constructs a new {@code BattlestaveMakePlugin} {@code Object}. - */ - public BattlestaveMakePlugin() { - super(STAFF.getId(), 573, 571, 575, 569); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - addHandler(573, ITEM_TYPE, this); - addHandler(571, ITEM_TYPE, this); - addHandler(575, ITEM_TYPE, this); - addHandler(569, ITEM_TYPE, this); - return this; - } - - @Override - public boolean handle(NodeUsageEvent event) { - final Player player = event.getPlayer(); - if (event.getUsedItem().getId() != STAFF.getId()) { - return false; - } - int id = event.getUsedItem().getId() == STAFF.getId() ? event.getUsedWith().getId() : event.getUsedItem().getId(); - final BattleStaves staff = BattleStaves.forId(id); - if (staff == null) - return true; - if (player.getSkills().getLevel(Skills.CRAFTING) < staff.getLevel()) { - player.getPacketDispatch().sendMessage("You need a crafting level of " + staff.getLevel() + " to make this."); - return true; - } - player.getInventory().remove(STAFF); - player.getInventory().remove(new Item(staff.getObelisk(), 1)); - player.getInventory().add(new Item(staff.getProduct(), 1)); - player.getSkills().addExperience(Skills.CRAFTING, staff.getExp(), true); - // Craft an air battlestaff - if (staff == BattleStaves.AIR) { - player.getAchievementDiaryManager().finishTask(player, DiaryType.VARROCK, 2, 6); - } - return true; - } - -} diff --git a/Server/src/main/content/global/skill/crafting/GlassProduct.java b/Server/src/main/content/global/skill/crafting/GlassProduct.java deleted file mode 100644 index 125fb246a..000000000 --- a/Server/src/main/content/global/skill/crafting/GlassProduct.java +++ /dev/null @@ -1,105 +0,0 @@ -package content.global.skill.crafting; - -/** - * Represents the glass product. - * @author 'Vexia - */ -public enum GlassProduct { - - VIAL(38, 229, 1, 33, 35), ORB(39, 567, 1, 46, 52.5), BEER(40, 1919, 1, 1, 17.5), CANDLE(41, 4527, 1, 4, 19), OIL_LAMP(42, 4525, 1, 12, 25), FISHBOWL(44, 6667, 1, 42, 42.5), LANTERN_LENS(43, 4542, 1, 49, 55), LIGHT_ORB(45, 10973, 1, 87, 70); - - /** - * Represents the button pressed. - */ - private final int buttonId; - - /** - * Represents the product. - */ - private final int product; - - /** - * Represents the amount. - */ - private final int amount; - - /** - * Represents the level needed. - */ - private final int level; - - /** - * Represents the experience gained. - */ - private final double experience; - - /** - * Constructs a new {@code GlassDefinitions.java} {@Code Object} - * @param buttonId the button id. - * @param result the product. - * @param amount the amount. - * @param level the level. - * @param experience the exp. - */ - private GlassProduct(int buttonId, int result, int amount, int level, double experience) { - this.buttonId = buttonId; - this.product = result; - this.amount = amount; - this.level = level; - this.experience = experience; - } - - /** - * Gets the buttonId. - * @return The buttonId. - */ - public int getButtonId() { - return buttonId; - } - - /** - * Gets the product. - * @return The product. - */ - public int getProduct() { - return product; - } - - /** - * Gets the amount. - * @return The amount. - */ - public int getAmount() { - return amount; - } - - /** - * Gets the level. - * @return The level. - */ - public int getLevel() { - return level; - } - - /** - * Gets the experience. - * @return The experience. - */ - public double getExperience() { - return experience; - } - - /** - * Gets the glass product by the id. - * @param id the id. - * @return the product. - */ - public static GlassProduct forId(int id) { - for (GlassProduct def : GlassProduct.values()) { - if (def.getButtonId() == id) { - return def; - } - } - return null; - } -} diff --git a/Server/src/main/content/global/skill/crafting/LeatherCraftDialogue.java b/Server/src/main/content/global/skill/crafting/LeatherCraftDialogue.java index 2a6086ac8..65fa4ae71 100644 --- a/Server/src/main/content/global/skill/crafting/LeatherCraftDialogue.java +++ b/Server/src/main/content/global/skill/crafting/LeatherCraftDialogue.java @@ -1,6 +1,7 @@ package content.global.skill.crafting; import static core.api.ContentAPIKt.*; +import core.api.InputType; import core.cache.def.impl.ItemDefinition; import core.game.component.Component; import core.game.dialogue.DialoguePlugin; @@ -60,7 +61,7 @@ public final class LeatherCraftDialogue extends DialoguePlugin { if (type.equals("hard")) { player.getInterfaceManager().openChatbox(309); player.getPacketDispatch().sendItemZoomOnInterface(1131, 150, 309, 2); - player.getPacketDispatch().sendString("



" + "Heardleather body", 309, 6); + player.getPacketDispatch().sendString("



Hardleather body", 309, 6); } else { /** dragon */ leather = (int) args[1]; @@ -195,7 +196,7 @@ public final class LeatherCraftDialogue extends DialoguePlugin { case 5: case 9: case 13: - amt = 10; + amt = 30; break; case 4: case 8: @@ -204,7 +205,7 @@ public final class LeatherCraftDialogue extends DialoguePlugin { if (hidee == null) { return false; } - sendInputDialogue(player, true, "Enter the amount:", (value) -> { + sendInputDialogue(player, InputType.AMOUNT, "Enter the amount:", (value) -> { player.getPulseManager().run(new DragonCraftPulse(player, null, hidee, (int) value)); return Unit.INSTANCE; }); diff --git a/Server/src/main/content/global/skill/crafting/MoltenGlassMakePlugin.java b/Server/src/main/content/global/skill/crafting/MoltenGlassMakePlugin.java deleted file mode 100644 index 37f25628c..000000000 --- a/Server/src/main/content/global/skill/crafting/MoltenGlassMakePlugin.java +++ /dev/null @@ -1,121 +0,0 @@ -package content.global.skill.crafting; - -import core.plugin.Initializable; -import core.game.node.entity.skill.SkillPulse; -import core.game.node.entity.skill.Skills; -import core.game.interaction.NodeUsageEvent; -import core.game.interaction.UseWithHandler; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; -import core.game.world.update.flag.context.Animation; -import core.plugin.Plugin; - -/** - * Represents the plugin used to make molten glass. - * @author Vexia - * @author Splinter - * @version 1.0 - */ -@Initializable -public final class MoltenGlassMakePlugin extends UseWithHandler { - - /** - * Constructs a new {@code MoltenGlassMakePlugin} {@code Object}. - */ - public MoltenGlassMakePlugin() { - super(1783, 1781); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - addHandler(4304, OBJECT_TYPE, this); - addHandler(6189, OBJECT_TYPE, this); - addHandler(9390, OBJECT_TYPE, this); - addHandler(11010, OBJECT_TYPE, this); - addHandler(11666, OBJECT_TYPE, this); - addHandler(12100, OBJECT_TYPE, this); - addHandler(12809, OBJECT_TYPE, this); - addHandler(18497, OBJECT_TYPE, this); - addHandler(26814, OBJECT_TYPE, this); - addHandler(30021, OBJECT_TYPE, this); - addHandler(30510, OBJECT_TYPE, this); - addHandler(36956, OBJECT_TYPE, this); - addHandler(37651, OBJECT_TYPE, this); - return this; - } - - @Override - public boolean handle(NodeUsageEvent event) { - event.getPlayer().getPulseManager().run(new MoltenGlassPulse(event.getPlayer(), event.getUsedItem())); - return true; - } - - /** - * Represents the pulse for molten glass making via a furnace - * @author Splinter - */ - public static class MoltenGlassPulse extends SkillPulse { - - /** - * Represents the bucket of sand item. - */ - private static final Item BUCKET_OF_SAND = new Item(1783); - - /** - * Represents the soda ash item. - */ - private static final Item SODA_ASH = new Item(1781); - - /** - * Represents the molten glass item. - */ - private static final Item MOLTEN_GLASS = new Item(1775); - - /** - * Represents the bucket item. - */ - private static final Item BUCKET = new Item(1925); - - /** - * Constructs a new {@code MoltenGlassPulse} {@code Object}. - * @param player the player. - * @param node the node. - */ - public MoltenGlassPulse(Player player, Item node) { - super(player, node); - } - - @Override - public boolean checkRequirements() { - if (player.getInventory().contains(1783, 1) && player.getInventory().contains(1781, 1)) { - return true; - } - player.sendMessage("You need at least one heap of soda ash and one bucket of sand to do this."); - return false; - } - - @Override - public void animate() { - player.animate(Animation.create(899)); - } - - @Override - public boolean reward() { - if (getDelay() == 1) { - super.setDelay(3); - return false; - } - if (player.getInventory().remove(BUCKET_OF_SAND, SODA_ASH)) { - player.lock(3); - player.getInventory().add(MOLTEN_GLASS, BUCKET); - player.getSkills().addExperience(Skills.CRAFTING, 20, true); - player.sendMessage("You heat the sand and soda ash in the furnace to make glass."); - } else { - return true; - } - return false; - } - - } - -} diff --git a/Server/src/main/content/global/skill/crafting/StuddedArmourPlugin.java b/Server/src/main/content/global/skill/crafting/StuddedArmourPlugin.java index b0a0d189e..1abe86feb 100644 --- a/Server/src/main/content/global/skill/crafting/StuddedArmourPlugin.java +++ b/Server/src/main/content/global/skill/crafting/StuddedArmourPlugin.java @@ -71,7 +71,7 @@ public final class StuddedArmourPlugin extends UseWithHandler { * @version 1.0 */ public enum StuddedArmour { - CHAPS(new Item(1095), new Item(1097), 18, 27), BODY(new Item(1129), new Item(1133), 14, 25); + CHAPS(new Item(1095), new Item(1097), 44, 42), BODY(new Item(1129), new Item(1133), 41, 40); /** * Represents the unstudded item. 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/gem/GemCutPulse.java b/Server/src/main/content/global/skill/crafting/gem/GemCutPulse.java index 123687c50..f094fa838 100644 --- a/Server/src/main/content/global/skill/crafting/gem/GemCutPulse.java +++ b/Server/src/main/content/global/skill/crafting/gem/GemCutPulse.java @@ -4,6 +4,9 @@ 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 org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.playAudio; /** * Represents the pulse used to cut a gem. @@ -61,7 +64,7 @@ public final class GemCutPulse extends SkillPulse { @Override public void animate() { if (ticks % 5 == 0 || ticks < 1) { - player.getAudioManager().send(2586); + playAudio(player, Sounds.CHISEL_2586); player.animate(gem.getAnimation()); } } diff --git a/Server/src/main/content/global/skill/crafting/glass/GlassCraftingListener.kt b/Server/src/main/content/global/skill/crafting/glass/GlassCraftingListener.kt new file mode 100644 index 000000000..84f0c199f --- /dev/null +++ b/Server/src/main/content/global/skill/crafting/glass/GlassCraftingListener.kt @@ -0,0 +1,69 @@ +package content.global.skill.crafting.glass + +import core.api.* +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.interaction.InterfaceListener +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import org.rs09.consts.Components +import org.rs09.consts.Items + +class GlassCraftingListener : InteractionListener, InterfaceListener { + + companion object { + private const val OP_MAKE_ONE = 155 + private const val OP_MAKE_FIVE = 196 + private const val OP_MAKE_ALL = 124 + private const val OP_MAKE_X = 199 + + private const val GLASS_BLOWING_PIPE = Items.GLASSBLOWING_PIPE_1785 + private const val MOLTEN_GLASS = Items.MOLTEN_GLASS_1775 + private const val GLASS_BLOWING_INTERFACE = Components.CRAFTING_GLASS_542 + } + + override fun defineListeners() { + onUseWith(IntType.ITEM, GLASS_BLOWING_PIPE, MOLTEN_GLASS) { player, _, _ -> + openInterface(player, GLASS_BLOWING_INTERFACE) + return@onUseWith true + } + } + + override fun defineInterfaceListeners() { + on(GLASS_BLOWING_INTERFACE) { player, _, opcode, buttonID, _, _ -> + val product = GlassProduct.forButtonID(buttonID) ?: return@on true + + if (!inInventory(player, GLASS_BLOWING_PIPE)) { + sendMessage(player, "You need a glassblowing pipe to do this.") + return@on true + } + + if (!inInventory(player, MOLTEN_GLASS)) { + sendMessage(player, "You need molten glass to do this.") + return@on true + } + + if (!hasLevelDyn(player, Skills.CRAFTING, product.minimumLevel)) { + sendMessage(player, "You need a Crafting level of ${product.minimumLevel} to make this.") + return@on true + } + + when (opcode) { + OP_MAKE_ONE -> make(player, product, 1) + OP_MAKE_FIVE -> make(player, product, 5) + OP_MAKE_ALL -> make(player, product, amountInInventory(player, MOLTEN_GLASS)) + OP_MAKE_X -> sendInputDialogue(player, InputType.AMOUNT, "Enter the amount:") { value -> + make(player, product, Integer.parseInt(value.toString())) + } + else -> return@on true + } + + return@on true + } + } + + private fun make(player: Player, product: GlassProduct, amount: Int) { + closeInterface(player) + submitIndividualPulse(player, GlassCraftingPulse(player, product, amount)) + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/crafting/glass/GlassCraftingPulse.kt b/Server/src/main/content/global/skill/crafting/glass/GlassCraftingPulse.kt new file mode 100644 index 000000000..f287ff956 --- /dev/null +++ b/Server/src/main/content/global/skill/crafting/glass/GlassCraftingPulse.kt @@ -0,0 +1,44 @@ +package content.global.skill.crafting.glass + +import core.api.* +import core.game.event.ResourceProducedEvent +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.game.system.task.Pulse +import org.rs09.consts.Items +import org.rs09.consts.Sounds + +class GlassCraftingPulse( + private val player: Player, + private val product: GlassProduct, + private var amount: Int +) : Pulse() { + + override fun pulse(): Boolean { + if (amount < 1) return true + + if (!inInventory(player, Items.GLASSBLOWING_PIPE_1785) || !inInventory(player, Items.MOLTEN_GLASS_1775)) { + return true + } + + //Animation is updated on 2 June 2009 - https://runescape.wiki/w/Update:Patch_Notes_(2_June_2009) + animate(player, 884) + playAudio(player, Sounds.GLASSBLOWING_2724) + if (product.producedItemId in intArrayOf(Items.UNPOWERED_ORB_567, Items.OIL_LAMP_4525)) { + sendMessage(player, "You make an ${product.name.lowercase().replace("_", " ")}.") + + } else sendMessage(player, "You make a ${product.name.lowercase().replace("_", " ")}.") + + if (removeItem(player, Items.MOLTEN_GLASS_1775)) { + addItem(player, product.producedItemId, product.amount) + rewardXP(player, Skills.CRAFTING, product.experience) + player.dispatch(ResourceProducedEvent(product.producedItemId, product.amount, player)) + + } else return true + + amount-- + delay = 3 + + return false + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/crafting/glass/GlassMakeListener.kt b/Server/src/main/content/global/skill/crafting/glass/GlassMakeListener.kt new file mode 100644 index 000000000..9dc4d98ae --- /dev/null +++ b/Server/src/main/content/global/skill/crafting/glass/GlassMakeListener.kt @@ -0,0 +1,40 @@ +package content.global.skill.crafting.glass + +import core.api.* +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import org.rs09.consts.Items +import kotlin.math.min + +class GlassMakeListener : InteractionListener { + + companion object{ + private const val SODA_ASH = Items.SODA_ASH_1781 + private const val BUCKET_OF_SAND = Items.BUCKET_OF_SAND_1783 + private const val MOLTEN_GLASS = Items.MOLTEN_GLASS_1775 + private val INPUTS = intArrayOf(SODA_ASH, BUCKET_OF_SAND) + private val FURNACES = intArrayOf(4304, 6189, 9390, 11010, 11666, 12100, 12809, 18497, 26814, 30021, 30510, 36956, 37651) + } + + override fun defineListeners() { + onUseWith(IntType.SCENERY, INPUTS, *FURNACES){ player, _, _ -> + + if (!inInventory(player, SODA_ASH, 1) || !inInventory(player, BUCKET_OF_SAND, 1)) { + sendMessage(player, "You need at least one heap of soda ash and one bucket of sand to do this.") + return@onUseWith true + } + + sendSkillDialogue(player) { + withItems(MOLTEN_GLASS) + create { id, amount -> + submitIndividualPulse(player, GlassMakePulse(player, id, amount)) + } + calculateMaxAmount { _ -> + min(amountInInventory(player, SODA_ASH), amountInInventory(player, BUCKET_OF_SAND)) + } + } + + return@onUseWith true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/crafting/glass/GlassMakePulse.kt b/Server/src/main/content/global/skill/crafting/glass/GlassMakePulse.kt new file mode 100644 index 000000000..a641e2082 --- /dev/null +++ b/Server/src/main/content/global/skill/crafting/glass/GlassMakePulse.kt @@ -0,0 +1,40 @@ +package content.global.skill.crafting.glass + +import core.api.* +import core.game.event.ResourceProducedEvent +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.game.system.task.Pulse +import org.rs09.consts.Items + +class GlassMakePulse( + private val player: Player, + private val product: Int, + private var amount: Int +) : Pulse() { + + override fun pulse(): Boolean { + if (amount < 1) return true + + if (!inInventory(player, Items.SODA_ASH_1781) || !inInventory(player, Items.BUCKET_OF_SAND_1783)) { + return true + } + + animate(player, 3243) + //Audio unknown (2725?) + 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.BUCKET_1925) + addItem(player, Items.MOLTEN_GLASS_1775) + rewardXP(player, Skills.CRAFTING, 20.0) + player.dispatch(ResourceProducedEvent(product, amount, player)) + + } else return true + + amount-- + delay = 2 + + return false + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/crafting/glass/GlassProduct.kt b/Server/src/main/content/global/skill/crafting/glass/GlassProduct.kt new file mode 100644 index 000000000..4e0ced589 --- /dev/null +++ b/Server/src/main/content/global/skill/crafting/glass/GlassProduct.kt @@ -0,0 +1,35 @@ +package content.global.skill.crafting.glass + +enum class GlassProduct ( + val buttonId: Int, + val producedItemId: Int, + val amount: Int, + val minimumLevel: Int, + val experience: Double +) { + VIAL(38, 229, 1, 33, 35.0), + ORB(39, 567, 1, 46, 52.5), + BEER_GLASS(40, 1919, 1, 1, 17.5), + CANDLE_LANTERN(41, 4527, 1, 4, 19.0), + OIL_LAMP(42, 4525, 1, 12, 25.0), + LANTERN_LENS(43, 4542, 1, 49, 55.0), + FISHBOWL(44, 6667, 1, 42, 42.5), + LIGHT_ORB(45, 10973, 1, 87, 70.0); + + companion object { + private val BUTTON_MAP = HashMap() + private val PRODUCT_MAP = HashMap() + + init { + for (product in GlassProduct.values()) { + BUTTON_MAP[product.buttonId] = product + } + + for (product in GlassProduct.values()) { + PRODUCT_MAP[product.producedItemId] = product + } + } + + fun forButtonID(buttonId: Int): GlassProduct? = BUTTON_MAP[buttonId] + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/crafting/jewellery/JewelleryCrafting.java b/Server/src/main/content/global/skill/crafting/jewellery/JewelleryCrafting.java index 37304e77d..89358a5fc 100644 --- a/Server/src/main/content/global/skill/crafting/jewellery/JewelleryCrafting.java +++ b/Server/src/main/content/global/skill/crafting/jewellery/JewelleryCrafting.java @@ -5,6 +5,7 @@ import core.game.component.Component; 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.*; import java.util.HashMap; @@ -185,7 +186,9 @@ public class JewelleryCrafting { for (int i = 0; i < data.items.length; i++) { if (player.getInventory().contains(data.getItems()[i], 1)) { length++; - } + } else if (inEquipmentOrInventory(player, 10881, 1) && player.goldSatchel.contains(data.getItems()[i], 1)){ + length++; + } } if (!player.getInventory().contains(mouldFor(data.name()), 1)) { length--; @@ -235,6 +238,9 @@ public class JewelleryCrafting { amt = player.getInventory().getAmount(new Item(GOLD_BAR)); } else { int first = player.getInventory().getAmount(new Item(data.getItems()[0])); + if (inEquipmentOrInventory(player, 10881, 1)) { + first += player.goldSatchel.getAmount(new Item(data.getItems()[0])); + } int second = player.getInventory().getAmount(new Item(data.getItems()[1])); if (first == second) { amt = first; @@ -250,7 +256,9 @@ public class JewelleryCrafting { for (int i = 0; i < data.items.length; i++) { if (player.getInventory().contains(data.getItems()[i], amount)) { length++; - } + } else if (data.getItems()[i] != GOLD_BAR && inEquipmentOrInventory(player, 10881, 1) && player.goldSatchel.contains(data.getItems()[i], 1)){ + length++; + } } if (length != data.getItems().length) { player.getPacketDispatch().sendMessage("You don't have the required items to make this item."); diff --git a/Server/src/main/content/global/skill/crafting/jewellery/JewelleryPulse.java b/Server/src/main/content/global/skill/crafting/jewellery/JewelleryPulse.java index 2959e1402..be2f7b58c 100644 --- a/Server/src/main/content/global/skill/crafting/jewellery/JewelleryPulse.java +++ b/Server/src/main/content/global/skill/crafting/jewellery/JewelleryPulse.java @@ -5,6 +5,9 @@ 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 org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.*; /** * Represents the pulse used to craft jewllery. @@ -55,6 +58,7 @@ public final class JewelleryPulse extends SkillPulse { public void animate() { if (ticks % 5 == 0) { player.animate(ANIMATION); + playAudio(player, Sounds.FURNACE_2725); } } @@ -63,11 +67,19 @@ public final class JewelleryPulse extends SkillPulse { if (++ticks % 5 != 0) { return false; } - if (player.getInventory().remove(getItems())) { + Item items[] = getItems(); + for (int i = 0; i < items.length; i++) { + if (player.getInventory().remove(items[i]) || (items[i].getId() != 2357 && inEquipmentOrInventory(player, 10881, 1) && player.goldSatchel.remove(items[i]))) { + continue; + } else { + return true; + } + } + //if (player.getInventory().remove(getItems())) { final Item item = new Item(type.getSendItem()); player.getInventory().add(item); player.getSkills().addExperience(Skills.CRAFTING, type.getExperience(), true); - } + //} amount--; return amount < 1; } 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 d42a1a08b..357b69ca8 100644 --- a/Server/src/main/content/global/skill/crafting/lightsources/LightSourceExtinguisher.kt +++ b/Server/src/main/content/global/skill/crafting/lightsources/LightSourceExtinguisher.kt @@ -1,5 +1,6 @@ package content.global.skill.crafting.lightsources +import core.api.log import core.cache.def.impl.ItemDefinition import core.game.container.Container import core.game.interaction.OptionHandler @@ -9,6 +10,7 @@ import core.game.node.item.Item import core.tools.SystemLogger import core.plugin.Initializable import core.plugin.Plugin +import core.tools.Log /** @@ -28,7 +30,7 @@ class LightSourceExtinguisher : OptionHandler(){ val lightSource = LightSources.forId(node.id) - lightSource ?: return false.also { SystemLogger.logWarn(this::class.java, "UNHANDLED EXTINGUISH OPTION: ID = ${node.id}") } + lightSource ?: return false.also { log(this::class.java, Log.WARN, "UNHANDLED EXTINGUISH OPTION: ID = ${node.id}") } 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 d1e1bb009..81a1a98cc 100644 --- a/Server/src/main/content/global/skill/crafting/lightsources/LightSourceLighter.kt +++ b/Server/src/main/content/global/skill/crafting/lightsources/LightSourceLighter.kt @@ -1,6 +1,7 @@ package content.global.skill.crafting.lightsources 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.player.Player @@ -65,6 +66,12 @@ 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 + } + if(!light(event.player,used,lightSource)){ event.player.sendMessage("You need a Firemaking level of at least ${lightSource.levelRequired} to light this.") } @@ -85,6 +92,7 @@ class LightSourceLighter : UseWithHandler(590,36,38){ if(playerLevel < requiredLevel) return false player.inventory.replace(item,Item(lightSource.litID)) + player.dispatch(LitLightSourceEvent(lightSource.litID)) return true } } diff --git a/Server/src/main/content/global/skill/crafting/limestone/ChiselLimestonePulse.java b/Server/src/main/content/global/skill/crafting/limestone/ChiselLimestonePulse.java index 62333f0d6..259850dfa 100644 --- a/Server/src/main/content/global/skill/crafting/limestone/ChiselLimestonePulse.java +++ b/Server/src/main/content/global/skill/crafting/limestone/ChiselLimestonePulse.java @@ -5,6 +5,9 @@ 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 org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.playAudio; /** * @@ -48,7 +51,7 @@ public class ChiselLimestonePulse extends SkillPulse { @Override public void animate() { if (ticks % 5 == 0 || ticks < 1) { - player.getAudioManager().send(2586); + playAudio(player, Sounds.CHISEL_2586); player.animate(Animation.create(4470)); } } 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 81328adad..8bc77007c 100644 --- a/Server/src/main/content/global/skill/crafting/silver/SilverCraftingPulse.kt +++ b/Server/src/main/content/global/skill/crafting/silver/SilverCraftingPulse.kt @@ -8,6 +8,7 @@ import core.game.node.scenery.Scenery import core.game.system.task.Pulse import org.rs09.consts.Animations import org.rs09.consts.Items +import org.rs09.consts.Sounds /** * Handles tick-based silver crafting logic. @@ -28,6 +29,7 @@ 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) diff --git a/Server/src/main/content/global/skill/crafting/spinning/SpinningPulse.java b/Server/src/main/content/global/skill/crafting/spinning/SpinningPulse.java index 541f68761..ae652f9a8 100644 --- a/Server/src/main/content/global/skill/crafting/spinning/SpinningPulse.java +++ b/Server/src/main/content/global/skill/crafting/spinning/SpinningPulse.java @@ -9,6 +9,9 @@ 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 org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.playAudio; /** * Represents the pulse used to spin an item. @@ -67,6 +70,7 @@ public final class SpinningPulse extends SkillPulse { public void animate() { if (ticks % 5 == 0) { player.animate(ANIMATION); + playAudio(player, Sounds.SPINNING_2590); } } diff --git a/Server/src/main/content/global/skill/farming/BasketsAndSacks.kt b/Server/src/main/content/global/skill/farming/BasketsAndSacks.kt index 904915b32..fea2f83d8 100644 --- a/Server/src/main/content/global/skill/farming/BasketsAndSacks.kt +++ b/Server/src/main/content/global/skill/farming/BasketsAndSacks.kt @@ -23,8 +23,7 @@ enum class BasketsAndSacks(val produceID: Int, val baseContainer: Int, val capac init { values().map { it.produceID to it }.toMap(map) for(b in values()){ - b.containers.add(b.baseContainer) - for(i in 1 until b.capacity){ + for(i in 0 until b.capacity){ map[b.baseContainer + (i * 2)] = b b.containers.add(b.baseContainer + (i * 2)) } diff --git a/Server/src/main/content/global/skill/farming/CompostBin.kt b/Server/src/main/content/global/skill/farming/CompostBin.kt index 7345bde62..c7e7f5cc2 100644 --- a/Server/src/main/content/global/skill/farming/CompostBin.kt +++ b/Server/src/main/content/global/skill/farming/CompostBin.kt @@ -2,13 +2,13 @@ package content.global.skill.farming import core.api.* import core.game.node.entity.player.Player -import core.game.node.entity.player.link.audio.Audio import core.game.node.entity.skill.Skills import core.game.node.item.Item import core.tools.RandomFunction import org.json.simple.JSONArray import org.json.simple.JSONObject import org.rs09.consts.Items +import org.rs09.consts.Sounds import java.util.concurrent.TimeUnit class CompostBin(val player: Player, val bin: CompostBins) { @@ -19,20 +19,38 @@ 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 } fun close() { isClosed = true - player.audioManager.send(2428) + sendMessage(player, "You close the compost bin.") + // TODO: Add animation - https://youtu.be/B50dwm8fdcQ?t=225 https://youtu.be/BHYgNDLx0s4?t=488 + playAudio(player, Sounds.COMPOST_CLOSE_2428) + sendMessage(player, "The contents have begun to rot.") finishedTime = System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(RandomFunction.random(35,50).toLong()) updateBit() } fun open(){ isClosed = false - player.audioManager.send(2429) + // TODO: Add animation + playAudio(player, Sounds.COMPOST_OPEN_2429) + sendMessage(player, "You open the compost bin.") updateBit() } @@ -47,13 +65,17 @@ class CompostBin(val player: Player, val bin: CompostBins) { isSuperCompost = true isClosed = false } - player.audioManager.send(Audio (2443, 1, 1)) + playAudio(player, Sounds.FARMING_SCOOP_2443) updateBit() if(isSuperCompost) rewardXP(player, Skills.FARMING, 8.5) else rewardXP(player, Skills.FARMING, 4.5) return Item(item) } + fun isDefaultState() : Boolean { + return (isFinished == false && finishedTime == 0L && items.size == 0) + } + fun isReady(): Boolean { return System.currentTimeMillis() > finishedTime && finishedTime != 0L } @@ -112,25 +134,26 @@ class CompostBin(val player: Player, val bin: CompostBins) { } else item.amount for(i in 0 until amount) { - player.audioManager.send(2441) + playAudio(player, Sounds.FARMING_PUTIN_2441) addItem(item.id) } } fun updateBit(){ - player.varpManager.get(bin.varpIndex).clearBitRange(bin.varpOffest,bin.varpOffest + 7) if(items.isNotEmpty()) { - if (isFinished) { - player.varpManager.get(bin.varpIndex).setVarbit(bin.varpOffest + 1, if(items.size == 15) 15 else 14) - if (isTomatoes) player.varpManager.get(bin.varpIndex).setVarbit(bin.varpOffest + 7, 1) - else if (isSuperCompost) player.varpManager.get(bin.varpIndex).setVarbit(bin.varpOffest + 5, 1) + if (isClosed) { + setVarbit(player, bin.varbit, 0x40) + } else if (isFinished) { + var finalValue = if (items.size == 15) 15 else 14 + if (isTomatoes) finalValue += 0x80 + else if (isSuperCompost) finalValue += 0x20 + setVarbit(player, bin.varbit, finalValue) } else { - player.varpManager.get(bin.varpIndex).setVarbit(bin.varpOffest, items.size) - if (isTomatoes) player.varpManager.get(bin.varpIndex).setVarbit(bin.varpOffest + 7, 1) + var finalValue = items.size + if (isTomatoes) finalValue += 0x80 + setVarbit(player, bin.varbit, finalValue) } - if (isClosed) player.varpManager.get(bin.varpIndex).setVarbit(bin.varpOffest + 6, 1) - } - player.varpManager.get(bin.varpIndex).send(player) + } else setVarbit(player, bin.varbit, 0) } fun save(root: JSONObject){ @@ -176,4 +199,4 @@ class CompostBin(val player: Player, val bin: CompostBins) { updateBit() } } -} \ No newline at end of file +} diff --git a/Server/src/main/content/global/skill/farming/CompostBinOptionHandler.kt b/Server/src/main/content/global/skill/farming/CompostBinOptionHandler.kt index 88ff95986..a8a0b182f 100644 --- a/Server/src/main/content/global/skill/farming/CompostBinOptionHandler.kt +++ b/Server/src/main/content/global/skill/farming/CompostBinOptionHandler.kt @@ -1,5 +1,6 @@ package content.global.skill.farming +import core.api.* import core.cache.def.impl.SceneryDefinition import core.game.interaction.OptionHandler import core.game.node.Node @@ -10,7 +11,7 @@ import core.plugin.Plugin @Initializable class CompostBinOptionHandler : OptionHandler() { override fun newInstance(arg: Any?): Plugin { - for(i in 7836..7839) + for (i in 7836..7839) SceneryDefinition.forId(i).childrenIds.forEach { val def = SceneryDefinition.forId(it) def.handlers["option:open"] = this @@ -26,15 +27,15 @@ class CompostBinOptionHandler : OptionHandler() { val cBin = CompostBins.forObject(node.asScenery()) ?: return false val bin = cBin.getBinForPlayer(player) - when(option){ - "close" -> if(!bin.isFull()) player.sendMessage("This shouldn't be happening. Report this.") else bin.close() - "open" -> if(!bin.isFinished) player.sendMessage("I should probably wait until it is done to open it.") else bin.open() + when (option) { + "close" -> if (!bin.isFull()) sendMessage(player, "This shouldn't be happening. Report this.") else bin.close() + "open" -> if (!bin.isFinished) sendMessage(player, "I should probably wait until it is done to open it.") else bin.open() "take-tomato" -> { if (!bin.isTomatoes || !bin.isFinished) { - player.sendMessage("This shouldn't be happening. Report this.") + sendMessage(player, "This shouldn't be happening. Report this.") } else { - if(player.inventory.isFull){ - player.sendMessage("You don't have enough inventory space to do this.") + if (player.inventory.isFull) { + sendMessage(player, "You don't have enough inventory space to do this.") } else { val reward = bin.takeItem() if (reward != null) diff --git a/Server/src/main/content/global/skill/farming/CompostBins.kt b/Server/src/main/content/global/skill/farming/CompostBins.kt index 60e0393e9..3780625f4 100644 --- a/Server/src/main/content/global/skill/farming/CompostBins.kt +++ b/Server/src/main/content/global/skill/farming/CompostBins.kt @@ -1,19 +1,20 @@ package content.global.skill.farming +import content.global.skill.farming.timers.Compost +import core.api.getOrStartTimer import core.cache.def.impl.SceneryDefinition -import core.cache.def.impl.VarbitDefinition -import core.game.node.scenery.Scenery import core.game.node.entity.player.Player +import core.game.node.scenery.Scenery -enum class CompostBins(val varpIndex: Int, val varpOffest: Int) { - FALADOR_COMPOST(511,0), - CATHERBY_COMPOST(511,8), - PORT_PHAS_COMPOST(511,16), - ARDOUGNE_COMPOST(511,24); +enum class CompostBins(val varbit: Int) { + FALADOR_COMPOST(740), + CATHERBY_COMPOST(741), + PORT_PHAS_COMPOST(742), + ARDOUGNE_COMPOST(743); companion object { @JvmField - val bins = values().map { (it.varpIndex shl it.varpOffest) to it }.toMap() + val bins = values().map { it.varbit to it }.toMap() @JvmStatic fun forObject(obj: Scenery): CompostBins?{ @@ -23,17 +24,12 @@ enum class CompostBins(val varpIndex: Int, val varpOffest: Int) { @JvmStatic fun forObjectID(id: Int): CompostBins?{ val objDef = SceneryDefinition.forId(id) - val def = VarbitDefinition.forObjectID(objDef.varbitID) - return bins[def.varpId shl def.startBit] + return bins[objDef.varbitID] } } fun getBinForPlayer(player: Player) : CompostBin { - var state: FarmingState? = player.states.get("farming") as FarmingState? - return if(state == null){ - state = player.registerState("farming") as FarmingState - state.getBin(this).also { state.init() } - } else - state.getBin(this) + val bins = getOrStartTimer (player) + return bins.getBin (this) } -} \ No newline at end of file +} diff --git a/Server/src/main/content/global/skill/farming/CompostType.kt b/Server/src/main/content/global/skill/farming/CompostType.kt index 5d19c40c2..01e34dbad 100644 --- a/Server/src/main/content/global/skill/farming/CompostType.kt +++ b/Server/src/main/content/global/skill/farming/CompostType.kt @@ -2,6 +2,6 @@ package content.global.skill.farming enum class CompostType { NONE, - NORMAL, - SUPER + COMPOST, + SUPERCOMPOST } \ No newline at end of file diff --git a/Server/src/main/content/global/skill/farming/CropHarvester.kt b/Server/src/main/content/global/skill/farming/CropHarvester.kt index 3c9dff538..b70347b67 100644 --- a/Server/src/main/content/global/skill/farming/CropHarvester.kt +++ b/Server/src/main/content/global/skill/farming/CropHarvester.kt @@ -6,7 +6,6 @@ import core.game.interaction.OptionHandler import core.game.node.Node import content.global.skill.summoning.familiar.GiantEntNPC import core.game.node.entity.player.Player -import core.game.node.entity.player.link.audio.Audio; import core.game.node.entity.skill.Skills import core.game.node.item.Item import core.game.system.task.Pulse @@ -14,17 +13,16 @@ import core.game.world.update.flag.context.Animation import core.plugin.Initializable import core.plugin.Plugin import org.rs09.consts.Items +import org.rs09.consts.Sounds -val livesBased = arrayOf(PatchType.HERB, PatchType.CACTUS, PatchType.BELLADONNA, PatchType.HOPS, PatchType.ALLOTMENT,PatchType.EVIL_TURNIP) - -val spadeAnim = Animation(830) +val livesBased = arrayOf(PatchType.HERB_PATCH, PatchType.CACTUS_PATCH, PatchType.BELLADONNA_PATCH, PatchType.HOPS_PATCH, PatchType.ALLOTMENT, PatchType.EVIL_TURNIP_PATCH) @Initializable class CropHarvester : OptionHandler() { override fun newInstance(arg: Any?): Plugin { - SceneryDefinition.setOptionHandler("harvest",this) - SceneryDefinition.setOptionHandler("pick",this) + SceneryDefinition.setOptionHandler("harvest", this) + SceneryDefinition.setOptionHandler("pick", this) return this } @@ -36,62 +34,94 @@ class CropHarvester : OptionHandler() { val fPatch = FarmingPatch.forObject(node.asScenery()) fPatch ?: return null val patch = fPatch.getPatchFor(player) + val patchName = patch.patch.type.displayName() val plantable = patch.plantable plantable ?: return null + var firstHarvest = true return object : Pulse(0) { override fun pulse(): Boolean { - var reward = Item(crop, 1) + var reward = Item(crop) val familiar = player.familiarManager.familiar - if(familiar != null && familiar is GiantEntNPC) { + if (familiar != null && familiar is GiantEntNPC) { familiar.modifyFarmingReward(fPatch, reward) } - if(!player.inventory.hasSpaceFor(reward)){ - player.sendMessage("You don't have enough inventory space for that.") + if (!hasSpaceFor(player, reward)) { + sendMessage(player, "You have run out of inventory space.") return true } - var requiredItem = when(fPatch.type){ - PatchType.HERB -> Items.SECATEURS_5329 + var requiredItem = when (fPatch.type) { + PatchType.TREE_PATCH -> Items.SECATEURS_5329 else -> Items.SPADE_952 } - if(requiredItem == Items.SECATEURS_5329){ - if(player.inventory.contains(Items.MAGIC_SECATEURS_7409,1)){ + if (requiredItem == Items.SECATEURS_5329) { + if (inInventory(player, Items.MAGIC_SECATEURS_7409)) { requiredItem = Items.MAGIC_SECATEURS_7409 } } - val anim = when(requiredItem){ - Items.SPADE_952 -> Animation(830) - Items.SECATEURS_5329 -> Animation(7227) - Items.MAGIC_SECATEURS_7409 -> Animation(7228) + val anim = when (requiredItem) { + 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) } - val sound = when(requiredItem){ - Items.SPADE_952 -> Audio(1470, 1, 1) - Items.SECATEURS_5329 -> Audio(2437, 1, 1) - Items.MAGIC_SECATEURS_7409 -> Audio(2437, 1, 1) - else -> Audio(0) + val sound = when (requiredItem) { + Items.SPADE_952 -> Sounds.DIGSPADE_1470 + Items.SECATEURS_5329 -> Sounds.FARMING_PICK_2437 + Items.MAGIC_SECATEURS_7409 -> Sounds.FARMING_PICK_2437 + else -> 0 } - if(!player.inventory.containsItem(Item(requiredItem))){ - player.sendMessage("You lack the needed tool to harvest these crops.") + if (!inInventory(player, requiredItem)) { + sendMessage(player, "You lack the needed tool to harvest these crops.") return true } - player.animator.animate(anim) - player.audioManager.send(sound) + val necklace = getItemFromEquipment(player, EquipmentSlot.NECK) + var noted = false + if (necklace != null && (necklace.name.lowercase().contains("amulet of farming") || necklace.name.lowercase().contains("amulet of nature"))) { + noted = true + } else if (inEquipment(player,10878,1)) { + noted = true + } + val sendHarvestMessages = if (fPatch.type == PatchType.FLOWER_PATCH) false else true + if (sendHarvestMessages && firstHarvest) { + sendMessage(player, "You begin to harvest the $patchName.") + if (noted) { + sendMessage(player, "The leprechaun exchanges your produce for banknotes.") + } + 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 - player.inventory.add(reward) - player.skills.addExperience(Skills.FARMING,plantable.harvestXP) - if(patch.patch.type in livesBased){ + if (noted) { + addItem(player, note(reward).id) + } else { + addItem(player, reward.id) + } + 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){ + if (patch.harvestAmt <= 0 && crop == plantable.harvestItem) { patch.clear() } } + if (sendHarvestMessages && (patch.cropLives <= 0 || patch.harvestAmt <= 0)) { + sendMessage(player, "The $patchName is now empty.") + } return patch.cropLives <= 0 || patch.harvestAmt <= 0 } } @@ -107,13 +137,18 @@ class CropHarvester : OptionHandler() { val plantable = patch.plantable plantable ?: return false - if(patch.isWeedy()){ - player.sendMessage("Something seems to have gone wrong here. Report this.") + if (patch.isWeedy() || patch.isEmptyAndWeeded()) { + sendMessage(player, "Something seems to have gone wrong here. Report this.") + return true + } + + if (!hasSpaceFor(player, Item(plantable.harvestItem))) { + sendMessage(player, "You don't have enough inventory space to do that.") return true } val pulse = harvestPulse(player, node, plantable.harvestItem) ?: return false - player.pulseManager.run(pulse) + submitIndividualPulse(player, pulse) return true } diff --git a/Server/src/main/content/global/skill/farming/DigUpPatchDialogue.kt b/Server/src/main/content/global/skill/farming/DigUpPatchDialogue.kt index e3bf56d79..a0d70bb07 100644 --- a/Server/src/main/content/global/skill/farming/DigUpPatchDialogue.kt +++ b/Server/src/main/content/global/skill/farming/DigUpPatchDialogue.kt @@ -1,54 +1,75 @@ package content.global.skill.farming +import core.api.* import core.game.dialogue.DialoguePlugin +import core.game.interaction.QueueStrength import core.game.node.entity.player.Player -import core.game.node.entity.player.link.audio.Audio -import core.game.node.item.GroundItemManager -import core.game.node.item.Item -import core.game.system.task.Pulse -import core.game.world.update.flag.context.Animation import core.plugin.Initializable +import org.rs09.consts.Sounds @Initializable -class DigUpPatchDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { +class DigUpPatchDialogue(player: Player? = null) : DialoguePlugin(player) { var patch: Patch? = null - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return DigUpPatchDialogue(player) } override fun open(vararg args: Any?): Boolean { patch = args[0] as Patch - if(patch?.isWeedy() == true){ - player.dialogueInterpreter.sendDialogue("Use a rake to get rid of weeds.") + if (patch?.isWeedy() == true || patch?.isEmptyAndWeeded() == true) { + sendDialogue(player, "There aren't any crops in this patch to dig up.") stage = 1000 return true } - player.dialogueInterpreter.sendOptions("Dig up this patch?","Yes","No") + if (patch?.patch?.type == PatchType.TREE_PATCH) { + val isTreeStump = patch?.getCurrentState() == patch?.plantable!!.value + patch?.plantable!!.stages + 2 + if (patch!!.isGrown() && !isTreeStump) { + sendMessage(player, "You need to chop this tree down first.") // this message is not authentic + stage = 1000 + return true + } + } + sendDialogueOptions(player, "Are you sure you want to dig up this patch?", "Yes, I want to clear it for new crops.", "No, I want to leave it as it is.") stage = 0 return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { - when(stage){ - 0 -> when(buttonId){ + when (stage) { + 0 -> when (buttonId) { 1 -> { end() - player.animator.animate(Animation(830)) - player.audioManager.send(Audio(1470, 1, 1)) - player.pulseManager.run(object : Pulse(){ - override fun pulse(): Boolean { - if(patch?.patch?.type == PatchType.TREE){ - if(patch?.getCurrentState() == (patch?.plantable?.value ?: 0) + (patch?.plantable?.stages ?: 0) + 2 && patch?.isWeedy() != true){ - if(!player.inventory.add(Item(patch?.plantable?.harvestItem ?: 0))){ - GroundItemManager.create(Item(patch?.plantable?.harvestItem ?: 0),player) + val anim = getAnimation(830) + sendMessage(player, "You start digging the farming patch...") + queueScript(player, 0, QueueStrength.WEAK) { stage: Int -> + when (stage) { + 0 -> { + animate(player, anim) + playAudio(player, Sounds.DIGSPADE_1470) + return@queueScript delayScript(player,anim.duration + 2) + } + 1 -> { + animate(player, anim) + playAudio(player, Sounds.DIGSPADE_1470) + return@queueScript delayScript(player, anim.duration + 1) + } + 2 -> { + if (patch?.patch?.type == PatchType.TREE_PATCH) { + if (patch?.getCurrentState() == (patch?.plantable?.value ?: 0) + (patch?.plantable?.stages ?: 0) + 2 && patch?.isWeedy() != true && patch?.isEmptyAndWeeded() != true) { + addItemOrDrop(player, patch?.plantable?.harvestItem ?: 0) } } + if (patch?.plantable == Plantable.SCARECROW) { + addItemOrDrop(player, patch?.plantable?.harvestItem ?: 0) + } + patch?.clear() + sendMessage(player, "You have successfully cleared this patch for new crops.") + return@queueScript stopExecuting(player) } - patch?.clear() - return true + else -> return@queueScript stopExecuting(player) } - }) + } } else -> end() } diff --git a/Server/src/main/content/global/skill/farming/FarmerPayOptionDialogue.kt b/Server/src/main/content/global/skill/farming/FarmerPayOptionDialogue.kt index eeb8903f5..7b29a5568 100644 --- a/Server/src/main/content/global/skill/farming/FarmerPayOptionDialogue.kt +++ b/Server/src/main/content/global/skill/farming/FarmerPayOptionDialogue.kt @@ -1,68 +1,113 @@ 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){ + 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++ - } - } - - 1 -> options("Sure!","No, thanks.").also { stage++ } - 2 -> { - if(player!!.inventory.containsItem(item)){ - player("Here you go.").also { stage = 10 } + 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 + 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 (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 (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 { - 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 (!(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 (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/FarmingPatch.kt b/Server/src/main/content/global/skill/farming/FarmingPatch.kt index e6ab30f5e..ed033d253 100644 --- a/Server/src/main/content/global/skill/farming/FarmingPatch.kt +++ b/Server/src/main/content/global/skill/farming/FarmingPatch.kt @@ -1,77 +1,105 @@ package content.global.skill.farming +import core.api.* import core.cache.def.impl.SceneryDefinition -import core.cache.def.impl.VarbitDefinition import core.game.node.scenery.Scenery import core.game.node.entity.player.Player +import content.global.skill.farming.timers.CropGrowth -enum class FarmingPatch(val varpIndex: Int, val varpOffset: Int, val type: PatchType) { +enum class FarmingPatch(val varbit: Int, val type: PatchType) { //Allotments - S_FALADOR_ALLOTMENT_NW(504,0,PatchType.ALLOTMENT), - S_FALADOR_ALLOTMENT_SE(504,8,PatchType.ALLOTMENT), - CATHERBY_ALLOTMENT_N(504,16,PatchType.ALLOTMENT), - CATHERBY_ALLOTMENT_S(504,24,PatchType.ALLOTMENT), - ARDOUGNE_ALLOTMENT_S(505,8,PatchType.ALLOTMENT), - ARDOUGNE_ALLOTMENT_N(505,0,PatchType.ALLOTMENT), - PORT_PHAS_ALLOTMENT_NW(505,16,PatchType.ALLOTMENT), - PORT_PHAS_ALLOTMENT_SE(505,24,PatchType.ALLOTMENT), - HARMONY_ISLAND_ALLOTMENT(982,8,PatchType.ALLOTMENT), + S_FALADOR_ALLOTMENT_NW(708,PatchType.ALLOTMENT), + S_FALADOR_ALLOTMENT_SE(709,PatchType.ALLOTMENT), + CATHERBY_ALLOTMENT_N(710,PatchType.ALLOTMENT), + CATHERBY_ALLOTMENT_S(711,PatchType.ALLOTMENT), + ARDOUGNE_ALLOTMENT_S(713,PatchType.ALLOTMENT), + ARDOUGNE_ALLOTMENT_N(712,PatchType.ALLOTMENT), + PORT_PHAS_ALLOTMENT_NW(714,PatchType.ALLOTMENT), + PORT_PHAS_ALLOTMENT_SE(715,PatchType.ALLOTMENT), + HARMONY_ISLAND_ALLOTMENT(3402,PatchType.ALLOTMENT), //Herb - CATHERBY_HERB_CE(515,8,PatchType.HERB), - S_FALADOR_HERB_NE(515,0,PatchType.HERB), - ARDOUGNE_HERB_CE(515,16,PatchType.HERB), - PORT_PHAS_HERB_NE(515,24,PatchType.HERB), - TROLL_STRONGHOLD_HERB(830,0,PatchType.HERB), + CATHERBY_HERB_CE(781,PatchType.HERB_PATCH), + S_FALADOR_HERB_NE(780,PatchType.HERB_PATCH), + ARDOUGNE_HERB_CE(782,PatchType.HERB_PATCH), + PORT_PHAS_HERB_NE(783,PatchType.HERB_PATCH), + TROLL_STRONGHOLD_HERB(2788,PatchType.HERB_PATCH), //Flower - S_FALADOR_FLOWER_C(508,0,PatchType.FLOWER), - CATHERBY_FLOWER_C(508,8,PatchType.FLOWER), - ARDOUGNE_FLOWER_C(508,16,PatchType.FLOWER), - PORT_PHAS_FLOWER_C(508,24,PatchType.FLOWER), - WILDERNESS_FLOWER(1183,12,PatchType.FLOWER), + S_FALADOR_FLOWER_C(728,PatchType.FLOWER_PATCH), + CATHERBY_FLOWER_C(729,PatchType.FLOWER_PATCH), + ARDOUGNE_FLOWER_C(730,PatchType.FLOWER_PATCH), + PORT_PHAS_FLOWER_C(731,PatchType.FLOWER_PATCH), + WILDERNESS_FLOWER(5067,PatchType.FLOWER_PATCH), //Tree - N_FALADOR_TREE(502,8,PatchType.TREE), - TAVERLY_TREE(502,0,PatchType.TREE), - GNOME_STRONGHOLD_TREE(830,11,PatchType.TREE), - LUMBRIDGE_TREE(502,24,PatchType.TREE), - VARROCK_TREE(502,16,PatchType.TREE), + N_FALADOR_TREE(701,PatchType.TREE_PATCH), + TAVERLY_TREE(700,PatchType.TREE_PATCH), + GNOME_STRONGHOLD_TREE(2953,PatchType.TREE_PATCH), + LUMBRIDGE_TREE(703,PatchType.TREE_PATCH), + VARROCK_TREE(702,PatchType.TREE_PATCH), //Fruit Tree - GNOME_STRONGHOLD_FRUIT_TREE(503,0,PatchType.FRUIT_TREE), - CATHERBY_FRUIT_TREE(503,24,PatchType.FRUIT_TREE), - TREE_GNOME_VILLAGE_FRUIT_TREE(503,8,PatchType.FRUIT_TREE), - BRIMHAVEN_FRUIT_TREE(503,16,PatchType.FRUIT_TREE), - LLETYA_FRUIT_TREE(830,23,PatchType.FRUIT_TREE), + GNOME_STRONGHOLD_FRUIT_TREE(704,PatchType.FRUIT_TREE_PATCH), + CATHERBY_FRUIT_TREE(707,PatchType.FRUIT_TREE_PATCH), + TREE_GNOME_VILLAGE_FRUIT_TREE(705,PatchType.FRUIT_TREE_PATCH), + BRIMHAVEN_FRUIT_TREE(706,PatchType.FRUIT_TREE_PATCH), + LLETYA_FRUIT_TREE(4317,PatchType.FRUIT_TREE_PATCH), //Hops - ENTRANA_HOPS(506,8,PatchType.HOPS), - LUMBRIDGE_HOPS(506,16,PatchType.HOPS), - MCGRUBOR_HOPS(506,24,PatchType.HOPS), - YANILLE_HOPS(506,0,PatchType.HOPS), + ENTRANA_HOPS(717,PatchType.HOPS_PATCH), + LUMBRIDGE_HOPS(718,PatchType.HOPS_PATCH), + MCGRUBOR_HOPS(719,PatchType.HOPS_PATCH), + YANILLE_HOPS(716,PatchType.HOPS_PATCH), //Bushes - CHAMPIONS_GUILD_BUSH(509,0,PatchType.BUSH), - RIMMINGTON_BUSH(509,8,PatchType.BUSH), - ARDOUGNE_BUSH(509,24,PatchType.BUSH), - ETCETERIA_BUSH(509,16,PatchType.BUSH), + CHAMPIONS_GUILD_BUSH(732,PatchType.BUSH_PATCH), + RIMMINGTON_BUSH(733,PatchType.BUSH_PATCH), + ARDOUGNE_BUSH(735,PatchType.BUSH_PATCH), + ETCETERIA_BUSH(734,PatchType.BUSH_PATCH), //Spirit Tree - ETCETERIA_SPIRIT_TREE(507,8,PatchType.SPIRIT_TREE), - PORT_SARIM_SPIRIT_TREE(507,0,PatchType.SPIRIT_TREE), - KARAMJA_SPIRIT_TREE(507,16,PatchType.SPIRIT_TREE), + ETCETERIA_SPIRIT_TREE(722,PatchType.SPIRIT_TREE_PATCH), + PORT_SARIM_SPIRIT_TREE(720,PatchType.SPIRIT_TREE_PATCH), + KARAMJA_SPIRIT_TREE(724,PatchType.SPIRIT_TREE_PATCH), //Other - DRAYNOR_BELLADONNA(512, 16, PatchType.BELLADONNA), - CANIFIS_MUSHROOM(512, 8, PatchType.MUSHROOM), - ALKHARID_CACTUS(512, 0, PatchType.CACTUS), - EVIL_TURNIP(1171, 7, PatchType.EVIL_TURNIP); + DRAYNOR_BELLADONNA(748, PatchType.BELLADONNA_PATCH), + CANIFIS_MUSHROOM(746, PatchType.MUSHROOM_PATCH), + ALKHARID_CACTUS(744, PatchType.CACTUS_PATCH), + EVIL_TURNIP(4291, PatchType.EVIL_TURNIP_PATCH); companion object { @JvmField - val patches = FarmingPatch.values().map { (it.varpIndex shl it.varpOffset) to it }.toMap() + val patches = FarmingPatch.values().map { it.varbit to it }.toMap() + val patchNodes = ArrayList() + val nodeMap = HashMap() + + init { + patchNodes.addAll(8550..8557) //allotment wrappers + patchNodes.addAll(7847..7853) //flower patch wrappers + patchNodes.addAll(8150..8156) //herb patch wrappers + patchNodes.addAll(8388..8391) // Tree patches + patchNodes.add(19147) //Tree patch + patchNodes.addAll(7962..7965) //fruit trees + patchNodes.addAll(8173..8176) //hops + patchNodes.addAll(7577..7580) //bush + patchNodes.add(23760) //evil turnip + patchNodes.add(7572) //belladonna + patchNodes.add(8337) //mushroom + patchNodes.add(27197) //jade vine + patchNodes.add(7771) //cactus + patchNodes.add(7807) //calquat + patchNodes.addAll(8382..8383)//spirit trees + patchNodes.add(8338) //spirit tree + patchNodes.add(18816) //death plateau wrapper + + for (patch in patchNodes) { + val def = SceneryDefinition.forId(patch) + nodeMap[def.varbitID] = def + } + } @JvmStatic fun forObject(obj: Scenery): FarmingPatch?{ @@ -81,17 +109,16 @@ enum class FarmingPatch(val varpIndex: Int, val varpOffset: Int, val type: Patch @JvmStatic fun forObjectID(id: Int): FarmingPatch?{ val objDef = SceneryDefinition.forId(id) - val def = VarbitDefinition.forObjectID(objDef.varbitID) - return patches[def.varpId shl def.startBit] + return patches[objDef.varbitID] + } + + fun getSceneryDefByVarbit (id: Int) : SceneryDefinition? { + return nodeMap[id] } } - fun getPatchFor(player: Player): Patch{ - var state: FarmingState? = player.states.get("farming") as FarmingState? - return if(state == null){ - state = player.registerState("farming") as FarmingState - state.getPatch(this).also { state.init() } - } else - state.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/FarmingPatchZone.kt b/Server/src/main/content/global/skill/farming/FarmingPatchZone.kt index 05fd37f31..09a190c29 100644 --- a/Server/src/main/content/global/skill/farming/FarmingPatchZone.kt +++ b/Server/src/main/content/global/skill/farming/FarmingPatchZone.kt @@ -34,33 +34,37 @@ class FarmingPatchZone : MapArea, TickListener { override fun tick() { playersInZone.toList().forEach { (player, ticks) -> - if(ticks == 500){ - val npc = NPC(NPCs.GITHAN_7122) - npc.location = player.location - npc.init() - npc.moveStep() - npc.face(player) - openDialogue(player, SpiritDialogue(true), npc) - } else if (ticks == 1000){ - val npc = NPC(NPCs.GITHAN_7122) - npc.location = player.location - npc.init() - npc.moveStep() - npc.face(player) - openDialogue(player, SpiritDialogue(false), npc) - playersInZone.remove(player) + if(!player.isArtificial) { + if (ticks == 500) { + spawnGithan(player, true) + } else if (ticks == 1000) { + spawnGithan(player, false) + } + playersInZone[player] = ticks + 1 } - - playersInZone[player] = ticks + 1 } } - internal class SpiritDialogue(val is5: Boolean) : DialogueFile(){ + /** + * Spawns a Githan at a player if they've been standing in a farming patch zone for long enough. + * @param player The player to spawn the Githan at. + * @param firstDialogue Whether the Githan should play the first dialogue or not (and playing the second dialogue instead). + */ + private fun spawnGithan(player: Player, firstDialogue: Boolean) { + val npc = NPC(NPCs.GITHAN_7122) + npc.location = player.location + npc.init() + npc.moveStep() + npc.face(player) + openDialogue(player, SpiritDialogue(firstDialogue), npc) + } + + internal class SpiritDialogue(private val firstDialogue: Boolean) : DialogueFile(){ override fun handle(componentID: Int, buttonID: Int) { when(stage){ 0 -> { - if(is5) npcl(core.game.dialogue.FacialExpression.NEUTRAL, "In case you didn't know, you don't have to stand by your Farming patch. Your crops will grow even if you're not around.").also { stage++ } - else npcl(core.game.dialogue.FacialExpression.NEUTRAL, " Did you know that if your Farming patch has fully grown, it will never catch disease or die? It will be perfectly safe until you choose to harvest it.").also { stage++ } + if(firstDialogue) npcl(FacialExpression.NEUTRAL, "In case you didn't know, you don't have to stand by your Farming patch. Your crops will grow even if you're not around.").also { stage++ } + else npcl(FacialExpression.NEUTRAL, "Did you know that if your Farming patch has fully grown, it will never catch disease or die? It will be perfectly safe until you choose to harvest it.").also { stage++ } } 1 -> end() } 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 03cf0c2a2..000000000 --- a/Server/src/main/content/global/skill/farming/FarmingState.kt +++ /dev/null @@ -1,172 +0,0 @@ -package content.global.skill.farming - -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 - -@PlayerState("farming") -class FarmingState(player: Player? = null) : State(player) { - private val patchMap = HashMap() - private val binMap = HashMap() - - - fun getPatch(patch: FarmingPatch): Patch { - return patchMap[patch] ?: (Patch(player!!,patch).also { patchMap[patch] = it }) - } - - fun getBin(bin: CompostBins) : CompostBin{ - return binMap[bin] ?: (CompostBin(player!!,bin).also { binMap[bin] = it }) - } - - fun getPatches(): MutableCollection{ - return patchMap.values - } - - fun getBins(): MutableCollection{ - return binMap.values - } - - override fun save(root: JSONObject) { - val patches = JSONArray() - for((key,patch) in patchMap){ - val p = JSONObject() - p.put("patch-ordinal",key.ordinal) - p.put("patch-plantable-ordinal",patch.plantable?.ordinal ?: -1) - p.put("patch-watered",patch.isWatered) - p.put("patch-diseased",patch.isDiseased) - p.put("patch-dead",patch.isDead) - p.put("patch-stage",patch.currentGrowthStage) - p.put("patch-state",patch.getCurrentState()) - p.put("patch-nextGrowth",patch.nextGrowth) - p.put("patch-harvestAmt",patch.harvestAmt) - p.put("patch-checkHealth",patch.isCheckHealth) - p.put("patch-compost",patch.compost.ordinal) - p.put("patch-paidprot",patch.protectionPaid) - p.put("patch-croplives", patch.cropLives) - patches.add(p) - } - val bins = JSONArray() - for((key,bin) in binMap){ - val b = JSONObject() - b.put("bin-ordinal",key.ordinal) - bin.save(b) - bins.add(b) - } - root.put("farming-patches",patches) - root.put("farming-bins",bins) - } - - override fun parse(_data: JSONObject) { - player ?: return - if(_data.containsKey("farming-bins")){ - (_data["farming-bins"] as JSONArray).forEach { - val bin = it as JSONObject - val binOrdinal = bin["bin-ordinal"].toString().toInt() - val cBin = CompostBins.values()[binOrdinal] - val b = cBin.getBinForPlayer(player) - b.parse(bin["binData"] as JSONObject) - SystemLogger.logErr(this::class.java, "bin size " + binMap.size) - } - } - if(_data.containsKey("farming-patches")){ - val data = _data["farming-patches"] as JSONArray - for(d in data){ - val p = d as JSONObject - val patchOrdinal = p["patch-ordinal"].toString().toInt() - val patchPlantableOrdinal = p["patch-plantable-ordinal"].toString().toInt() - val patchWatered = p["patch-watered"] as Boolean - val patchDiseased = p["patch-diseased"] as Boolean - val patchDead = p["patch-dead"] as Boolean - val patchStage = p["patch-stage"].toString().toInt() - val nextGrowth = p["patch-nextGrowth"].toString().toLong() - val harvestAmt = (p["patch-harvestAmt"] ?: 0).toString().toInt() - val checkHealth = p["patch-checkHealth"] as Boolean - val savedState = p["patch-state"].toString().toInt() - val compostOrdinal = p["patch-compost"].toString().toInt() - val protectionPaid = p["patch-paidprot"] as Boolean - val cropLives = if(p["patch-croplives"] != null) p["patch-croplives"].toString().toInt() else 3 - val fPatch = FarmingPatch.values()[patchOrdinal] - val plantable = if(patchPlantableOrdinal != -1) Plantable.values()[patchPlantableOrdinal] else null - val patch = Patch(player,fPatch,plantable,patchStage,patchDiseased,patchDead,patchWatered,nextGrowth,harvestAmt,checkHealth) - - patch.cropLives = cropLives - patch.compost = CompostType.values()[compostOrdinal] - patch.protectionPaid = protectionPaid - patch.setCurrentState(savedState) - - if((savedState - (patch?.plantable?.value ?: 0)) > patch.currentGrowthStage){ - patch.setCurrentState(savedState) - } else { - patch.setCurrentState((patch.plantable?.value ?: 0) + patch.currentGrowthStage) - } - - val type = patch.patch.type - val shouldPlayCatchup = !patch.isGrown() || (type == PatchType.BUSH && patch.getFruitOrBerryCount() < 4) || (type == PatchType.FRUIT_TREE && patch.getFruitOrBerryCount() < 6) - if(shouldPlayCatchup && patch.plantable != null && !patchDead){ - var stagesToSimulate = if (!patch.isGrown()) patch.plantable!!.stages - patch.currentGrowthStage else 0 - if (type == PatchType.BUSH) - stagesToSimulate += Math.min(4, 4 - patch.getFruitOrBerryCount()) - if (type == PatchType.FRUIT_TREE) - stagesToSimulate += Math.min(6, 6 - patch.getFruitOrBerryCount()) - - val nowTime = System.currentTimeMillis() - var simulatedTime = patch.nextGrowth - - while (simulatedTime < nowTime && stagesToSimulate-- > 0 && !patch.isDead) { - val timeToIncrement = TimeUnit.MINUTES.toMillis(patch.getStageGrowthMinutes().toLong()) - patch.update() - simulatedTime += timeToIncrement - } - } - - if(patchMap[fPatch] == null) { - patchMap[fPatch] = patch - } - } - } - } - - override fun newInstance(player: Player?): State { - return FarmingState(player) - } - - override fun createPulse() { - pulse = object : Pulse(3){ - override fun pulse(): Boolean { - - GlobalScope.launch { - var removeList = ArrayList() - for((_,patch) in patchMap){ - - if(patch.getCurrentState() in 1..3 && patch.nextGrowth == 0L){ - patch.nextGrowth = System.currentTimeMillis() + 60000 - continue - } - - if(patch.nextGrowth < System.currentTimeMillis() && !patch.isDead){ - patch.update() - patch.nextGrowth = System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(patch.getStageGrowthMinutes().toLong()) - } - - } - - for((_,bin) in binMap){ - if(bin.isReady() && !bin.isFinished){ - bin.finish() - } - } - } - - return false - } - } - } -} diff --git a/Server/src/main/content/global/skill/farming/FruitAndBerryPicker.kt b/Server/src/main/content/global/skill/farming/FruitAndBerryPicker.kt index 10ae27787..f07689362 100644 --- a/Server/src/main/content/global/skill/farming/FruitAndBerryPicker.kt +++ b/Server/src/main/content/global/skill/farming/FruitAndBerryPicker.kt @@ -12,6 +12,7 @@ import core.game.system.task.Pulse import core.game.world.update.flag.context.Animation import core.plugin.Initializable import core.plugin.Plugin +import org.rs09.consts.Sounds import java.util.concurrent.TimeUnit @Initializable @@ -44,35 +45,47 @@ class FruitAndBerryPicker : OptionHandler() { val animation = Animation(2281) - if(patch.getFruitOrBerryCount() <= 0){ - player.sendMessage("This shouldn't be happening. Please report this.") + if (patch.getFruitOrBerryCount() <= 0) { + sendMessage(player, "This shouldn't be happening. Please report this.") return true } - if(!player.inventory.hasSpaceFor(Item(plantable.harvestItem))){ - player.sendMessage("You do not have enough inventory space for this.") + if (!hasSpaceFor(player, Item(plantable.harvestItem))) { + sendMessage(player, "You don't have enough inventory space to do that.") return true } - if(System.currentTimeMillis() - patch.nextGrowth > TimeUnit.MINUTES.toMillis(45)){ + if (System.currentTimeMillis() - patch.nextGrowth > TimeUnit.MINUTES.toMillis(45)) { patch.nextGrowth = System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(45) } - player.pulseManager.run(object : Pulse(animation.duration){ + submitIndividualPulse(player, object : Pulse(animation.duration) { override fun pulse(): Boolean { val reward = Item(plantable.harvestItem, 1) + 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) { + if (familiar != null && familiar is GiantEntNPC) { familiar.modifyFarmingReward(fPatch, reward) } - player.animator.animate(animation) - player.audioManager.send(2437) - addItemOrDrop(player,reward.id,reward.amount) - player.skills.addExperience(Skills.FARMING,plantable.harvestXP) + animate(player, animation) + playAudio(player, Sounds.FARMING_PICK_2437) + addItemOrDrop(player, reward.id, reward.amount) + rewardXP(player, Skills.FARMING, plantable.harvestXP) patch.setCurrentState(patch.getCurrentState() - 1) + if (patch.patch.type == PatchType.CACTUS_PATCH) { + sendMessage(player, "You carefully pick a spine from the cactus.") + } else { + val determiner = if (patch.patch.type == PatchType.BUSH_PATCH) "some" else "a" + sendMessage(player, "You pick $determiner ${reward.name.lowercase()}.") + } + return patch.getFruitOrBerryCount() == 0 } }) diff --git a/Server/src/main/content/global/skill/farming/HealthChecker.kt b/Server/src/main/content/global/skill/farming/HealthChecker.kt index 990f6b167..5c819f1a3 100644 --- a/Server/src/main/content/global/skill/farming/HealthChecker.kt +++ b/Server/src/main/content/global/skill/farming/HealthChecker.kt @@ -1,19 +1,20 @@ package content.global.skill.farming +import core.api.* import core.cache.def.impl.SceneryDefinition import core.game.interaction.OptionHandler import core.game.node.Node import core.game.node.entity.player.Player import core.game.node.entity.skill.Skills -import core.tools.SystemLogger import core.plugin.Initializable import core.plugin.Plugin +import core.tools.Log import java.util.concurrent.TimeUnit @Initializable -class HealthChecker : OptionHandler(){ +class HealthChecker : OptionHandler() { override fun newInstance(arg: Any?): Plugin { - SceneryDefinition.setOptionHandler("check-health",this) + SceneryDefinition.setOptionHandler("check-health", this) return this } @@ -25,24 +26,36 @@ class HealthChecker : OptionHandler(){ val patch = fPatch.getPatchFor(player) val type = patch.patch.type - if(type != PatchType.BUSH && type != PatchType.FRUIT_TREE && type != PatchType.TREE && type != PatchType.CACTUS){ - player.sendMessage("This shouldn't be happening. Please report this.") + if (type != PatchType.BUSH_PATCH && type != PatchType.FRUIT_TREE_PATCH && type != PatchType.TREE_PATCH && type != PatchType.CACTUS_PATCH) { + sendMessage(player, "This shouldn't be happening. Please report this.") return true } - if(!patch.isCheckHealth) return true + if (!patch.isCheckHealth) return true - player.skills.addExperience(Skills.FARMING,patch.plantable?.checkHealthXP ?: 0.0) + rewardXP(player, Skills.FARMING, patch.plantable?.checkHealthXP ?: 0.0) patch.isCheckHealth = false - when(type){ - PatchType.TREE -> patch.setCurrentState(patch.getCurrentState() + 1) - PatchType.FRUIT_TREE -> patch.setCurrentState(patch.getCurrentState() - 14) - PatchType.BUSH -> patch.setCurrentState(patch.plantable!!.value + patch.plantable!!.stages + 4) - PatchType.CACTUS -> patch.setCurrentState(patch.plantable!!.value + patch.plantable!!.stages + 3) - else -> SystemLogger.logErr(this::class.java,"Unreachable patch type from when(type) switch in HealthChecker.kt line 36") + when (type) { + 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) + 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.") + } + else -> log(this::class.java, Log.ERR, "Unreachable patch type from when(type) switch in HealthChecker.kt") } - if(type == PatchType.FRUIT_TREE){ + if (type == PatchType.FRUIT_TREE_PATCH) { patch.nextGrowth = TimeUnit.MINUTES.toMillis(45) } diff --git a/Server/src/main/content/global/skill/farming/InspectionHandler.kt b/Server/src/main/content/global/skill/farming/InspectionHandler.kt index 80bd06fe1..00a30a5d2 100644 --- a/Server/src/main/content/global/skill/farming/InspectionHandler.kt +++ b/Server/src/main/content/global/skill/farming/InspectionHandler.kt @@ -1,16 +1,18 @@ package content.global.skill.farming +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.plugin.Initializable import core.plugin.Plugin +import core.tools.prependArticle @Initializable class InspectionHandler : OptionHandler() { override fun newInstance(arg: Any?): Plugin { - SceneryDefinition.setOptionHandler("inspect",this) + SceneryDefinition.setOptionHandler("inspect", this) return this } @@ -18,29 +20,36 @@ class InspectionHandler : OptionHandler() { node ?: return false player ?: return false val patch = FarmingPatch.forObject(node.asScenery()) - if(patch == null){ - player.sendMessage("This is an improperly handled inspect option. Report this please.") - } else { - val p = patch.getPatchFor(player) - val status1 = if(p.getCurrentState() <= 2) "This patch needs weeding." else if(p.getCurrentState() == 3) "This patch is weed-free." else { - if(p.isDiseased && !p.isDead) "This patch has become diseased." - else if(p.isDead) "The crops in this patch are dead." - else if(p.plantable == Plantable.SCARECROW) "There is a scarecrow in this patch." - else "This patch has something growing in it." - } - val status2 = if(patch.type == PatchType.ALLOTMENT || patch.type == PatchType.FLOWER || patch.type == PatchType.HOPS){ - if(p.isWatered) { - "This patch has been watered." - } else if(p.getCurrentState() > 3 && !p.isGrown() && !p.isDead && !p.isDiseased) { - "This patch could use some water." - } else "" - } else "" - val status3 = if(p.compost == CompostType.NONE) "This patch has not been treated." else "This patch has been treated with ${p.compost.name.toLowerCase()} compost." - player.sendMessage("$status1 $status2") - player.sendMessage(status3) - val varpValue = player.varpManager.get(patch.varpIndex).getBitRangeValue(patch.varpOffset,patch.varpOffset + 7) + if (patch == null) { + sendMessage(player, "This is an improperly handled inspect option. Report this please.") + return true } + + val p = patch.getPatchFor(player) + val patchName = p.patch.type.displayName() + + val statusPatchType = if (patch == FarmingPatch.TROLL_STRONGHOLD_HERB) "This is a very special herb patch." + else "This is ${prependArticle(patchName)}." + + val statusCompost = if (p.compost == CompostType.NONE) "The soil has not been treated." + else "The soil has been treated with ${p.compost.name.lowercase()}." + + val statusStage = if (p.plantable == Plantable.SCARECROW) "" + else if (p.isWeedy()) "The patch needs weeding." + else if (p.isEmptyAndWeeded()) "The patch is empty and weeded." + else if (p.isDiseased && !p.isDead) "The patch is diseased and needs attending to before it dies." + else if (p.isDead) "The patch has become infected by disease and has died." + else if (p.isGrown()) "The patch is fully grown." + else "The patch has something growing in it." + + val statusGardener = if (patch == FarmingPatch.TROLL_STRONGHOLD_HERB) "My Arm will look after this patch for you." + else if (p.protectionPaid) "A nearby gardener is looking after this patch for you." + else "" + + sendMessage(player, "$statusPatchType $statusCompost $statusStage".trim()) + if (statusGardener != "") sendMessage(player, statusGardener) + return true } -} \ No newline at end of file +} diff --git a/Server/src/main/content/global/skill/farming/Patch.kt b/Server/src/main/content/global/skill/farming/Patch.kt index c57cf4d03..c3b70eb6d 100644 --- a/Server/src/main/content/global/skill/farming/Patch.kt +++ b/Server/src/main/content/global/skill/farming/Patch.kt @@ -1,9 +1,10 @@ package content.global.skill.farming +import core.api.* import core.game.node.entity.player.Player +import core.tools.Log import core.tools.RandomFunction import org.rs09.consts.Items -import core.tools.SystemLogger import java.util.concurrent.TimeUnit import kotlin.math.ceil import kotlin.math.min @@ -19,8 +20,8 @@ class Patch(val player: Player, val patch: FarmingPatch, var plantable: Plantabl fun setNewHarvestAmount() { val compostMod = when(compost) { CompostType.NONE -> 0 - CompostType.NORMAL -> 1 - CompostType.SUPER -> 2 + CompostType.COMPOST -> 1 + CompostType.SUPERCOMPOST -> 2 } harvestAmt = when (plantable) { Plantable.LIMPWURT_SEED, Plantable.WOAD_SEED -> 3 @@ -28,14 +29,14 @@ class Patch(val player: Player, val patch: FarmingPatch, var plantable: Plantabl Plantable.WILLOW_SAPLING -> 0 else -> 1 } - if(plantable != null && plantable?.applicablePatch != PatchType.FLOWER) { + if(plantable != null && plantable?.applicablePatch != PatchType.FLOWER_PATCH) { harvestAmt += compostMod } cropLives = 3 + compostMod } fun rollLivesDecrement(farmingLevel: Int, magicSecateurs: Boolean){ - if(patch.type == PatchType.HERB){ + if(patch.type == PatchType.HERB_PATCH){ //authentic formula thanks to released data. var herbSaveLow = when(plantable){ Plantable.GUAM_SEED -> min(24 + farmingLevel, 80) @@ -68,10 +69,10 @@ class Patch(val player: Player, val patch: FarmingPatch, var plantable: Plantabl //inauthentic formulae based on reported averages due to lack of formula var chance = when(patch.type){ PatchType.ALLOTMENT -> 8 //average of 8 per life times 3 lives = average 24 - PatchType.HOPS -> 6 //average of 6 per life times 3 lives = 18 - PatchType.BELLADONNA -> 2 //average of 2 per life times 3 lives = 6 - PatchType.EVIL_TURNIP -> 2 //average 2 per, same as BELLADONNA - PatchType.CACTUS -> 3 //average of 3 per life times 3 lives = 9 + 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 } @@ -84,81 +85,127 @@ class Patch(val player: Player, val patch: FarmingPatch, var plantable: Plantabl } fun isWeedy(): Boolean { - return getCurrentState() in 0..3 + return getCurrentState() in 0..2 + } + + fun isEmptyAndWeeded(): Boolean { + return getCurrentState() == 3 } fun getCurrentState(): Int{ - return player.varpManager.get(patch.varpIndex).getVarbitValue(patch.varpOffset) ?: 0 + return getVarbit(player, patch.varbit) } fun setCurrentState(state: Int){ - player.varpManager.get(patch.varpIndex).setVarbit(patch.varpOffset,state) + setVarbit(player, patch.varbit, state) updateBit() } + fun setVisualState (state: Int) { + val finalState = ensureStateSanity(state) + setVarbit(player, patch.varbit, finalState) + } + + fun ensureStateSanity (state: Int) : Int { + val patchDef = FarmingPatch.getSceneryDefByVarbit(patch.varbit) ?: return state + val currentStateDef = patchDef.getChildObjectAtIndex(state) + if (currentStateDef.name == patchDef.getChildObjectAtIndex(3).name) { //if we're weedy + if (state and 0x40 != 0) { //if this invalid state was caused by water/death + //remove water/death + isDead = false + isWatered = false + log(this::class.java, Log.DEBUG, "Patch for ${player.username} at varbit ${patch.varbit} with plantable ${plantable?.name ?: "none"} was set to watered/dead at stage $currentGrowthStage, which isn't valid.") + return (state and (0x40.inv())) + } + else if (state and 0x80 != 0) { //if this invalid state was caused by disease + //remove disease + isDiseased = false + 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.") + } + } + return state + } + fun isFertilized(): Boolean { 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) } - private fun updateBit(){ + fun updateBit(){ if(isCheckHealth){ when(patch.type){ - PatchType.FRUIT_TREE -> player.varpManager.get(patch.varpIndex).setVarbit(patch.varpOffset,plantable!!.value + plantable!!.stages + 20) - PatchType.BUSH -> player.varpManager.get(patch.varpIndex).setVarbit(patch.varpOffset,250 + (plantable!!.ordinal - Plantable.REDBERRY_SEED.ordinal)) - PatchType.CACTUS -> player.varpManager.get(patch.varpIndex).setVarbit(patch.varpOffset, 31) - else -> SystemLogger.logWarn(this::class.java, "Invalid setting of isCheckHealth for patch type: " + patch.type.name) + 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) + PatchType.TREE_PATCH -> setVarbit(player, patch.varbit, plantable!!.value + plantable!!.stages) + else -> log(this::class.java, Log.WARN, "Invalid setting of isCheckHealth for patch type: " + patch.type.name + "at" + patch.name) } } else { when(patch.type){ - PatchType.ALLOTMENT,PatchType.FLOWER,PatchType.HOPS -> { - player.varpManager.get(patch.varpIndex).setVarbit(patch.varpOffset + 6, if (isWatered || isDead) 1 else 0) - player.varpManager.get(patch.varpIndex).setVarbit(patch.varpOffset + 7, if (isDiseased) 1 else 0) + PatchType.ALLOTMENT,PatchType.FLOWER_PATCH,PatchType.HOPS_PATCH -> { + var state = getUnmodifiedValue() + if (isWatered || isDead) state = state or 0x40 + if (isDiseased) state = state or 0x80 + + if (state != getVarbit(player, patch.varbit)) + setVisualState(state) } - PatchType.BUSH -> { - if(isDead) player.varpManager.get(patch.varpIndex).setVarbit(patch.varpOffset,getBushDeathValue()) - else if(isDiseased && !isDead) player.varpManager.get(patch.varpIndex).setVarbit(patch.varpOffset,getBushDiseaseValue()) - //else player.varpManager.get(patch.varpIndex).setVarbit(patch.varpOffset, (plantable?.value ?: 0) + currentGrowthStage) + PatchType.BUSH_PATCH -> { + if(isDead) setVisualState(getBushDeathValue()) + else if(isDiseased && !isDead) setVisualState(getBushDiseaseValue()) } - PatchType.TREE -> { - if(isDead) player.varpManager.get(patch.varpIndex).setVarbit(patch.varpOffset + 7,1) - else if(isDiseased && !isDead) player.varpManager.get(patch.varpIndex).setVarbit(patch.varpOffset + 6, 1) + PatchType.TREE_PATCH -> { + var state = getVarbit(player, patch.varbit) + + if (isDead) state = state or 0x80 + else if (isDiseased) state = state or 0x40 + + if (state != getVarbit(player, patch.varbit)) + setVisualState(state) } - PatchType.FRUIT_TREE -> { - if(isDead) player.varpManager.get(patch.varpIndex).setVarbit(patch.varpOffset,getFruitTreeDeathValue()) - else if(isDiseased && !isDead) player.varpManager.get(patch.varpIndex).setVarbit(patch.varpOffset, getFruitTreeDiseaseValue()) + PatchType.FRUIT_TREE_PATCH -> { + if(isDead) setVisualState(getFruitTreeDeathValue()) + else if(isDiseased && !isDead) setVisualState(getFruitTreeDiseaseValue()) } - PatchType.BELLADONNA -> { - if(isDead) player.varpManager.get(patch.varpIndex).setVarbit(patch.varpOffset, getBelladonnaDeathValue()) - else if(isDiseased && !isDead) player.varpManager.get(patch.varpIndex).setVarbit(patch.varpOffset, getBelladonnaDiseaseValue()) - else player.varpManager.get(patch.varpIndex).setVarbit(patch.varpOffset, (plantable?.value ?: 0) + currentGrowthStage) + PatchType.BELLADONNA_PATCH -> { + if(isDead) setVisualState(getBelladonnaDeathValue()) + else if(isDiseased && !isDead) setVisualState(getBelladonnaDiseaseValue()) + else setVisualState((plantable?.value ?: 0) + currentGrowthStage) } - PatchType.CACTUS -> { - if(isDead) player.varpManager.get(patch.varpIndex).setVarbit(patch.varpOffset, getCactusDeathValue()) - else if(isDiseased && !isDead) player.varpManager.get(patch.varpIndex).setVarbit(patch.varpOffset, getCactusDiseaseValue()) + PatchType.CACTUS_PATCH -> { + if(isDead) setVisualState(getCactusDeathValue()) + else if(isDiseased && !isDead) setVisualState(getCactusDiseaseValue()) } - PatchType.HERB -> { - if(isDead) player.varpManager.get(patch.varpIndex).setVarbit(patch.varpOffset, getHerbDeathValue()) - else if(isDiseased && !isDead) player.varpManager.get(patch.varpIndex).setVarbit(patch.varpOffset, getHerbDiseaseValue()) - else player.varpManager.get(patch.varpIndex).setVarbit(patch.varpOffset, (plantable?.value ?: 0) + currentGrowthStage) + PatchType.HERB_PATCH -> { + if(isDead) setVisualState(getHerbDeathValue()) + else if(isDiseased && !isDead) setVisualState(getHerbDiseaseValue()) + else setVisualState((plantable?.value ?: 0) + currentGrowthStage) } else -> {} } } - player.varpManager.get(patch.varpIndex).send(player) } fun cureDisease() { + setVarbit(player, patch.varbit, (plantable?.value ?: 0) + currentGrowthStage) isDiseased = false - when(patch.type){ - PatchType.BUSH,PatchType.FRUIT_TREE -> player.varpManager.get(patch.varpIndex).setVarbit(patch.varpOffset,(plantable?.value ?: 0) + currentGrowthStage) - PatchType.TREE -> player.varpManager.get(patch.varpIndex).clearBitRange(patch.varpOffset + 6, patch.varpOffset + 7) - PatchType.CACTUS -> player.varpManager.get(patch.varpIndex).setVarbit(patch.varpOffset, (plantable?.value ?: 0) + currentGrowthStage) - else -> {} - } updateBit() } @@ -167,6 +214,10 @@ class Patch(val player: Player, val patch: FarmingPatch, var plantable: Plantabl updateBit() } + private fun getUnmodifiedValue(): Int { + return (plantable?.value ?: 0) + currentGrowthStage + } + private fun getBushDiseaseValue(): Int{ if(plantable == Plantable.POISON_IVY_SEED){ return (plantable?.value ?: 0) + currentGrowthStage + 12 @@ -224,7 +275,7 @@ class Patch(val player: Player, val patch: FarmingPatch, var plantable: Plantabl } private fun grow(){ - if(isWeedy() && getCurrentState() > 0) { + if((isWeedy() || isEmptyAndWeeded()) && getCurrentState() > 0) { nextGrowth = System.currentTimeMillis() + 60000 setCurrentState(getCurrentState() - 1) currentGrowthStage-- @@ -236,33 +287,33 @@ 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.NORMAL -> 8 - CompostType.SUPER -> 13 + 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 ){ - //bush, tree, fruit tree, herb and cactus can not disease on stage 1(0) of growth. - if(!((patch.type == PatchType.BUSH || patch.type == PatchType.TREE || patch.type == PatchType.FRUIT_TREE || patch.type == PatchType.CACTUS || patch.type == PatchType.HERB) && 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 && 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.type == PatchType.TREE || patch.type == PatchType.BUSH || patch.type == PatchType.CACTUS) && plantable != null && plantable?.stages == currentGrowthStage + 1){ + 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){ isCheckHealth = true } - if((patch.type == PatchType.FRUIT_TREE || patch.type == PatchType.BUSH || patch.type == PatchType.CACTUS) && plantable?.stages == currentGrowthStage){ - if((patch.type == PatchType.BUSH && getFruitOrBerryCount() < 4) || (patch.type == PatchType.FRUIT_TREE && getFruitOrBerryCount() < 6) || (patch.type == PatchType.CACTUS && getFruitOrBerryCount() < 3)){ + if((patch.type == PatchType.FRUIT_TREE_PATCH || patch.type == PatchType.BUSH_PATCH || patch.type == PatchType.CACTUS_PATCH) && plantable?.stages == currentGrowthStage){ + if((patch.type == PatchType.BUSH_PATCH && getFruitOrBerryCount() < 4) || (patch.type == PatchType.FRUIT_TREE_PATCH && getFruitOrBerryCount() < 6) || (patch.type == PatchType.CACTUS_PATCH && getFruitOrBerryCount() < 3)){ setCurrentState(getCurrentState() + 1) } } - if(patch.type == PatchType.TREE) { + if(patch.type == PatchType.TREE_PATCH) { // Willow branches if(harvestAmt < 6) { - harvestAmt++; + harvestAmt++ } } @@ -276,7 +327,7 @@ class Patch(val player: Player, val patch: FarmingPatch, var plantable: Plantabl } fun regrowIfTreeStump() { - if(patch.type == PatchType.TREE && plantable != null) { + if(patch.type == PatchType.TREE_PATCH && plantable != null) { // plantable.value + plantable.stages is the check-health stage, so +1 is the choppable tree, and +2 is the stump if(getCurrentState() == plantable!!.value + plantable!!.stages + 2) { setCurrentState(getCurrentState() - 1) @@ -300,11 +351,24 @@ class Patch(val player: Player, val patch: FarmingPatch, var plantable: Plantabl } fun clear(){ + if (isGrown()) { + var seedAmount = 1 + var seedMessage = "a seed" + if (patch.type == PatchType.ALLOTMENT || plantable!! == Plantable.JUTE_SEED) { + seedAmount = 3 + seedMessage = "some seeds" + } else if (patch.type == PatchType.HOPS_PATCH) { + seedAmount = 4 + seedMessage = "some seeds" + } + addItemOrDrop(player, plantable!!.itemID, seedAmount) + sendMessage(player, "You find $seedMessage in the patch as you clear it.") + } isCheckHealth = false isDiseased = false isDead = false plantable = null - player.varpManager.get(patch.varpIndex).clearBitRange(patch.varpOffset,patch.varpOffset + 7) + setVarbit(player, patch.varbit, 0) nextGrowth = 0L currentGrowthStage = 3 setCurrentState(3) @@ -319,7 +383,7 @@ class Patch(val player: Player, val patch: FarmingPatch, var plantable: Plantabl fun getStageGrowthMinutes() : Int { var minutes = patch.type.stageGrowthTime - if(patch.type == PatchType.FRUIT_TREE && isGrown()) { + if(patch.type == PatchType.FRUIT_TREE_PATCH && isGrown()) { // Fruit trees take 160 minutes per stage to grow, but // restocking their fruit should take 40 minutes per fruit minutes = 40 @@ -336,7 +400,7 @@ 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 && (fpatch.plantable == plantable?.protectionFlower || fpatch.plantable == Plantable.forItemID(Items.WHITE_LILY_SEED_14589)) diff --git a/Server/src/main/content/global/skill/farming/PatchRaker.kt b/Server/src/main/content/global/skill/farming/PatchRaker.kt index ee4641925..10af95489 100644 --- a/Server/src/main/content/global/skill/farming/PatchRaker.kt +++ b/Server/src/main/content/global/skill/farming/PatchRaker.kt @@ -1,31 +1,45 @@ package content.global.skill.farming +import core.api.* import core.game.node.entity.player.Player import core.game.node.entity.skill.Skills -import core.game.node.item.Item import core.game.system.task.Pulse -import core.game.world.update.flag.context.Animation import org.rs09.consts.Items +import org.rs09.consts.Sounds object PatchRaker { - val RAKE_ANIM = Animation(2273) + val RAKE_ANIM = getAnimation(2273) @JvmStatic - fun rake(player: Player, patch: FarmingPatch){ - player.pulseManager.run(object : Pulse(){ + fun rake(player: Player, patch: FarmingPatch) { + val p = patch.getPatchFor(player) + val patchName = p.patch.type.displayName() + var firstRake = true + if (!p.isWeedy()) { + sendMessage(player, "This $patchName doesn't need weeding right now.") + return + } + submitIndividualPulse(player, object : Pulse() { override fun pulse(): Boolean { var patchStage = patch.getPatchFor(player).getCurrentState() - if(patchStage <= 2){ - player.animator.animate(RAKE_ANIM) - player.audioManager.send(2442) + if (firstRake || patchStage < 2) { + // don't play the animation when on patchStage 2 as it has already + // played three times at this point and the patch will be weed-free + // after the third play + animate(player, RAKE_ANIM) + playAudio(player, Sounds.FARMING_RAKING_2442) + firstRake = false } - if(delay < 5) { + if (delay < 5) { delay = 5 } else { patch.getPatchFor(player).currentGrowthStage++ patch.getPatchFor(player).setCurrentState(++patchStage) - player.inventory.add(Item(Items.WEEDS_6055)) - player.skills.addExperience(Skills.FARMING,4.0) + addItem(player, Items.WEEDS_6055) + rewardXP(player, Skills.FARMING, 4.0) + } + if (patchStage >= 3) { + resetAnimator(player) } return patchStage >= 3 } diff --git a/Server/src/main/content/global/skill/farming/PatchType.kt b/Server/src/main/content/global/skill/farming/PatchType.kt index 20a76ee97..0cbe40129 100644 --- a/Server/src/main/content/global/skill/farming/PatchType.kt +++ b/Server/src/main/content/global/skill/farming/PatchType.kt @@ -2,15 +2,20 @@ package content.global.skill.farming enum class PatchType(val stageGrowthTime: Int) { ALLOTMENT(10), - HOPS(10), - TREE(40), - FRUIT_TREE(160), - BUSH(20), - FLOWER(5), - HERB(20), - SPIRIT_TREE(293), - MUSHROOM(30), - BELLADONNA(80), - CACTUS(60), - EVIL_TURNIP(5) + HOPS_PATCH(10), + TREE_PATCH(40), + FRUIT_TREE_PATCH(160), + BUSH_PATCH(20), + FLOWER_PATCH(5), + HERB_PATCH(20), + SPIRIT_TREE_PATCH(295), + MUSHROOM_PATCH(30), + BELLADONNA_PATCH(80), + CACTUS_PATCH(60), + EVIL_TURNIP_PATCH(5); + + /** + * Returns the display name of this PatchType. + */ + fun displayName(): String = name.lowercase().replace("_", " ") } diff --git a/Server/src/main/content/global/skill/farming/Plantable.kt b/Server/src/main/content/global/skill/farming/Plantable.kt index 8c006e9ea..83c016c64 100644 --- a/Server/src/main/content/global/skill/farming/Plantable.kt +++ b/Server/src/main/content/global/skill/farming/Plantable.kt @@ -3,88 +3,88 @@ 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) { - //Flowers - MARIGOLD_SEED(5096,8,4,8.5,47.0,0.0,2,PatchType.FLOWER,Items.MARIGOLDS_6010), - ROSEMARY_SEED(5097,13,4,12.0,66.5,0.0,11,PatchType.FLOWER, Items.ROSEMARY_6014), - NASTURTIUM_SEED(5098,18,4,19.5,111.0,0.0,24,PatchType.FLOWER,Items.NASTURTIUMS_6012), - WOAD_SEED(5099,23,4,20.5,115.5,0.0,25,PatchType.FLOWER,Items.WOAD_LEAF_1793), - LIMPWURT_SEED(5100,28,4,21.5,120.0,0.0,26,PatchType.FLOWER,Items.LIMPWURT_ROOT_225), - WHITE_LILY_SEED(14589,37,4,42.0,250.0,0.0,52,PatchType.FLOWER,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,Items.SCARECROW_6059), + // Flower (technically) + SCARECROW(Items.SCARECROW_6059,"scarecrow",33,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,Items.BARLEY_6006,Item(Items.COMPOST_6032,3)), - HAMMERSTONE_SEED(5307,4,4,9.0,10.0,0.0,4,PatchType.HOPS,Items.HAMMERSTONE_HOPS_5994,Item(Items.MARIGOLDS_6010)), - ASGARNIAN_SEED(5308,11,5,10.9,12.0,0.0,8,PatchType.HOPS,Items.ASGARNIAN_HOPS_5996,Item(Items.ONIONS10_5458)), - JUTE_SEED(5306,56,5,13.0,14.5,0.0,13,PatchType.HOPS,Items.JUTE_FIBRE_5931,Item(Items.BARLEY_MALT_6008,6)), - YANILLIAN_SEED(5309,19,6,14.5,16.0,0.0,16,PatchType.HOPS,Items.YANILLIAN_HOPS_5998,Item(Items.TOMATOES5_5968)), - KRANDORIAN_SEED(5310,28,7,17.5,19.5,0.0,21,PatchType.HOPS,Items.KRANDORIAN_HOPS_6000,Item(Items.CABBAGES10_5478,3)), - WILDBLOOD_SEED(5311,38,8,23.0,26.0,0.0,28,PatchType.HOPS,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,Items.OAK_ROOTS_6043,Item(Items.TOMATOES5_5968)), - WILLOW_SAPLING(5371,15,6,25.0,0.0,1456.5,30,PatchType.TREE,Items.WILLOW_ROOTS_6045,Item(Items.APPLES5_5386)), - MAPLE_SAPLING(5372,24,8,45.0,0.0,3403.4,45,PatchType.TREE,Items.MAPLE_ROOTS_6047,Item(Items.ORANGES5_5396)), - YEW_SAPLING(5373,35,10,81.0,0.0,7069.9,60,PatchType.TREE,Items.YEW_ROOTS_6049,Item(Items.CACTUS_SPINE_6016,10)), - MAGIC_SAPLING(5374,48,12,145.5,0.0,13768.3,75,PatchType.TREE,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,Items.COOKING_APPLE_1955,Item(Items.SWEETCORN_5986,9)), - BANANA_SAPLING(5497,35,6,28.0,10.5,1750.5,33,PatchType.FRUIT_TREE,Items.BANANA_1963,Item(Items.APPLES5_5386,4)), - ORANGE_SAPLING(5498,72,6,35.5,13.5,2470.2,39,PatchType.FRUIT_TREE,Items.ORANGE_2108,Item(Items.STRAWBERRIES5_5406,3)), - CURRY_SAPLING(5499,99,6,40.0,15.0,2906.9,42,PatchType.FRUIT_TREE,Items.CURRY_LEAF_5970,Item(Items.BANANAS5_5416,5)), - PINEAPPLE_SAPLING(5500,136,6,57.0,21.5,4605.7,51,PatchType.FRUIT_TREE,Items.PINEAPPLE_2114,Item(Items.WATERMELON_5982,10)), - PAPAYA_SAPLING(5501,163,6,72.0,27.0,6146.4,57,PatchType.FRUIT_TREE,Items.PAPAYA_FRUIT_5972,Item(Items.PINEAPPLE_2114,10)), - PALM_SAPLING(5502,200,6,110.5,41.5,10150.1,68,PatchType.FRUIT_TREE,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,Items.REDBERRIES_1951,Item(Items.CABBAGES10_5478,4)), - CADAVABERRY_SEED(5102,15,6,18.0,7.0,102.5,22,PatchType.BUSH,Items.CADAVA_BERRIES_753,Item(Items.TOMATOES5_5968,3)), - DWELLBERRY_SEED(5103,26,27,31.5,12.0,177.5,36,PatchType.BUSH,Items.DWELLBERRIES_2126,Item(Items.STRAWBERRIES5_5406,3)), - JANGERBERRY_SEED(5104,38,8,50.5,19.0,284.5,48,PatchType.BUSH,Items.JANGERBERRIES_247,Item(Items.WATERMELON_5982,6)), - WHITEBERRY_SEED(5105,51,8,78.0,29.0,437.5,59,PatchType.BUSH,Items.WHITE_BERRIES_239,null), - POISON_IVY_SEED(5106,197,8,120.0,45.0,675.0,70,PatchType.BUSH,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,27,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,null), + 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,Items.GRIMY_GUAM_199), - MARRENTILL_SEED(5292,11,4,13.5,15.0,0.0,14,PatchType.HERB,Items.GRIMY_MARRENTILL_201), - TARROMIN_SEED(5293,18,4,16.0,18.0,0.0,19,PatchType.HERB,Items.GRIMY_TARROMIN_203), - HARRALANDER_SEED(5294,25,4,21.5,24.0,0.0,26,PatchType.HERB,Items.GRIMY_HARRALANDER_205), - RANARR_SEED(5295,32,4,27.0,30.5,0.0,32,PatchType.HERB,Items.GRIMY_RANARR_207), - AVANTOE_SEED(5298,39,4,54.5,61.5,0.0,50,PatchType.HERB,Items.GRIMY_AVANTOE_211), - TOADFLAX_SEED(5296,46,4,34.0,38.5,0.0,38,PatchType.HERB,Items.GRIMY_TOADFLAX_3049), - IRIT_SEED(5297,53,4,43.0,48.5,0.0,44,PatchType.HERB,Items.GRIMY_IRIT_209), - KWUARM_SEED(5299,68,4,69.0,78.0,0.0,56,PatchType.HERB,Items.GRIMY_KWUARM_213), - SNAPDRAGON_SEED(5300,75,4,87.5,98.5,0.0,62,PatchType.HERB,Items.GRIMY_SNAPDRAGON_3051), - CADANTINE_SEED(5301,82,4,106.5,120.0,0.0,67,PatchType.HERB,Items.GRIMY_CADANTINE_215), - LANTADYME_SEED(5302,89,4,134.5,151.5,0.0,73,PatchType.HERB,Items.GRIMY_LANTADYME_2485), - DWARF_WEED_SEED(5303,96,4,170.5,192.0,0.0,79,PatchType.HERB,Items.GRIMY_DWARF_WEED_217), - TORSTOL_SEED(5304,103,4,199.5,224.5,0.0,85,PatchType.HERB,Items.GRIMY_TORSTOL_219), - GOUT_TUBER(6311,192,4,105.0,45.0,0.0,29,PatchType.HERB,Items.GOUTWEED_3261), - SPIRIT_WEED_SEED(12176, 204, 4, 32.0, 36.0, 0.0, 36, PatchType.HERB, 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, Items.CAVE_NIGHTSHADE_2398), - MUSHROOM_SPORE(Items.MUSHROOM_SPORE_5282, 6, 7, 61.5, 57.7, 0.0, 53, PatchType.MUSHROOM, Items.MUSHROOM_6004), - CACTUS_SEED(Items.CACTUS_SEED_5280, 8, 7, 66.5, 25.0, 374.0, 55, PatchType.CACTUS, Items.CACTUS_SPINE_6016), - EVIL_TURNIP_SEED(Items.EVIL_TURNIP_SEED_12148, 4, 1, 41.0, 46.0, 0.0, 42, PatchType.EVIL_TURNIP, 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", 6, 7, 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) ; - 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/SeedOnPlantPot.kt b/Server/src/main/content/global/skill/farming/SeedOnPlantPot.kt index 42d921dd9..39c83483e 100644 --- a/Server/src/main/content/global/skill/farming/SeedOnPlantPot.kt +++ b/Server/src/main/content/global/skill/farming/SeedOnPlantPot.kt @@ -1,14 +1,13 @@ package content.global.skill.farming -import core.api.addItem -import core.api.inInventory -import core.api.removeItem -import core.api.sendDialogue +import core.api.* import core.game.node.Node -import core.game.node.entity.player.Player import org.rs09.consts.Items import core.game.interaction.IntType +import core.game.node.entity.player.Player +import content.global.skill.farming.timers.* import core.game.interaction.InteractionListener +import core.tools.prependArticle class SeedlingListener : InteractionListener { override fun defineListeners() { @@ -20,7 +19,7 @@ class SeedlingListener : InteractionListener { val seed = used.asItem() ?: return false val pot = with.asItem() ?: return false - if(!inInventory(player, Items.GARDENING_TROWEL_5325)){ + if (!inInventory(player, Items.GARDENING_TROWEL_5325)) { sendDialogue(player, "You need a gardening trowel on you to do this.") return false } @@ -29,6 +28,8 @@ class SeedlingListener : InteractionListener { if (seedling == -1) return false if (!removeItem(player, seed.id) || !removeItem(player, pot)) return true addItem(player, seedling) + sendMessage(player, "You sow ${prependArticle(seed.name.lowercase())} in the plantpot.") + sendMessage(player, "It needs watering before it will grow.") return true } @@ -43,25 +44,17 @@ class SeedlingListener : InteractionListener { addItem(player, wateredSeedling) addItem(player, nextCan) - var state = player.states["seedling"] as SeedlingState? - - if (state != null) { - state.addSeedling(wateredSeedling) - return true - } - - state = player.registerState("seedling") as SeedlingState? - state?.addSeedling(wateredSeedling) - state?.init() - + var seedlings = getOrStartTimer (player) + seedlings.addSeedling(wateredSeedling) return true } - private fun Int.getNext(): Int{ + private fun Int.getNext(): Int { val index = WATERING_CANS.indexOf(this) if (index == -1) return Items.WATERING_CAN_5331 return if (index != WATERING_CANS.size -1) WATERING_CANS[index + 1] else Items.WATERING_CAN_5331 } + fun getSeedling(id: Int) : Int { return when (id) { Items.ACORN_5312 -> Items.OAK_SEEDLING_5358 @@ -114,4 +107,4 @@ class SeedlingListener : InteractionListener { ) private val WATERING_CANS = intArrayOf(Items.WATERING_CAN8_5340,Items.WATERING_CAN7_5339,Items.WATERING_CAN6_5338,Items.WATERING_CAN5_5337,Items.WATERING_CAN4_5336,Items.WATERING_CAN3_5335,Items.WATERING_CAN2_5334,Items.WATERING_CAN1_5333) -} \ No newline at end of file +} diff --git a/Server/src/main/content/global/skill/farming/SeedlingState.kt b/Server/src/main/content/global/skill/farming/SeedlingState.kt deleted file mode 100644 index 8fb2877f0..000000000 --- a/Server/src/main/content/global/skill/farming/SeedlingState.kt +++ /dev/null @@ -1,83 +0,0 @@ -package content.global.skill.farming - -import core.game.node.entity.player.Player -import core.game.node.item.Item -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 java.util.concurrent.TimeUnit - -@PlayerState("seedling") -class SeedlingState(player: Player? = null) : State(player) { - val seedlings = ArrayList() - - fun addSeedling(seedling: Int){ - seedlings.add(Seedling(seedling, System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(5),seedling + if(seedling > 5400) 8 else 6)) - } - - override fun save(root: JSONObject) { - val seedArray = JSONArray() - for(s in seedlings){ - val seed = JSONObject() - seed.put("id",s.id) - seed.put("ttl",s.TTL) - seed.put("sapling",s.sapling) - seedArray.add(seed) - } - root.put("seedlings",seedArray) - } - - override fun parse(_data: JSONObject) { - if(_data.containsKey("seedlings")){ - (_data["seedlings"] as JSONArray).forEach { - val s = it as JSONObject - val id = s["id"].toString().toInt() - val ttl = s["ttl"].toString().toLong() - val sapling = s["sapling"].toString().toInt() - seedlings.add(Seedling(id,ttl,sapling)) - } - } - } - - override fun newInstance(player: Player?): State { - return SeedlingState(player) - } - - override fun createPulse() { - if(seedlings.isEmpty()) return - player ?: return - - pulse = object : Pulse(5){ - override fun pulse(): Boolean { - val removeList = ArrayList() - GlobalScope.launch { - for (seed in seedlings) { - if (System.currentTimeMillis() > seed.TTL) { - val inInventory = player.inventory.get(Item(seed.id)) - if (inInventory != null) { - player.inventory.replace(Item(seed.sapling), inInventory.slot) - removeList.add(seed) - } else { - val inBank = player.bank.get(Item(seed.id)) - if(inBank == null) removeList.add(seed) - else { - player.bank.remove(Item(inBank.id,1)) - player.bank.add(Item(seed.sapling)) - removeList.add(seed) - } - } - } - } - seedlings.removeAll(removeList) - } - return false - } - } - - } - -} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/farming/ToolLeprechaunDialogue.kt b/Server/src/main/content/global/skill/farming/ToolLeprechaunDialogue.kt new file mode 100644 index 000000000..50ec9662f --- /dev/null +++ b/Server/src/main/content/global/skill/farming/ToolLeprechaunDialogue.kt @@ -0,0 +1,187 @@ +package content.global.skill.farming + +import content.minigame.vinesweeper.Vinesweeper +import core.api.openInterface +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.Components +import org.rs09.consts.NPCs + +/** + * Tool Leprechaun dialogue. + * @author ovenbread + * + * This is really hard to find anything beyond the first dialogue. + * https://www.youtube.com/watch?v=2wgWB9U5Ju8 0L25 May 19, 2010 + * https://www.youtube.com/watch?v=gqend8EibPs: 0:02 Nov 28, 2010 + * https://www.youtube.com/watch?v=6cnsQsVGzXI: 4:05 Feb 14, 2012 - The Leprechaun in Troll Stronghold! + * + * Note: + * Ultracompost was introduced in 2017(OSRS) 2018(RS3), so it is not included. + * Leprechaun Composting was only added in 2016, so it is also not included. + */ +@Initializable +class ToolLeprechaunDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage) { + START_DIALOGUE -> npc(FacialExpression.OLD_HAPPY, "Ah, 'tis a foine day to be sure! Can I help ye with tool", "storage, or a trip to Winkin's Farm, or what?").also { stage++ } + + 1 -> showTopics( + Topic(FacialExpression.NEUTRAL, "Yes, please.", 10), + Topic("What tools can you store?", 2, true), + Topic(FacialExpression.NEUTRAL, "No thanks, I'll keep hold of my stuff.", 20), + Topic("Can you take me to Winkin's Farm?", 30), + // "Other topics." -> Lost City quest where is Shamus. Evil Tree Reward collection in late 2009. + ) + 2 -> playerl(FacialExpression.THINKING, "What can you store?").also { stage++ } + 3 -> npcl(FacialExpression.OLD_HAPPY, "We'll hold onto yer rake, yer seed dibber, yer spade, yer secateurs, yer waterin' can and yer trowel - but mind it's not one of them fancy trowels only archaeologists use!").also { stage++ } + 4 -> npcl(FacialExpression.OLD_HAPPY, "We'll take a few buckets off yer hands too, and even yer compost and supercompost! There's room in our shed for plenty of compost, so bring it on.").also { stage++ } + 5 -> npcl(FacialExpression.OLD_HAPPY, "Also if ye hands us yer farming produce, we might be able to change it into banknotes.").also { stage++ } + 6 -> npcl(FacialExpression.OLD_HAPPY, "So... do ye want to be using the store?").also { stage++ } + 7 -> showTopics( + Topic(FacialExpression.NEUTRAL, "Yes, please.", 10), + Topic("What do you do with the tools you're storing?", 11, true), + Topic(FacialExpression.NEUTRAL, "No thanks, I'll keep hold of my stuff.", 20), + Topic("Can you take me to Winkin's Farm?", 30), + ) + 10 -> { + end() + openInterface(player, Components.FARMING_TOOLS_125) + } + 11 -> playerl(FacialExpression.THINKING, "What do you do with the tools you're storing? They can't possibly all fit in your pockets!").also { stage++ } + 12 -> npcl(FacialExpression.OLD_HAPPY, "We leprechauns have a shed where we keep 'em. It's a magic shed, so ye can get yer items back from any of us leprechauns whenever ye want. Saves ye havin' to carry loads of stuff around the country!").also { stage++ } + 13 -> npcl(FacialExpression.OLD_HAPPY, "So... do ye want to be using the store?").also { stage = 1 } + + 20 -> npcl(FacialExpression.OLD_NORMAL, "Ye must be dafter than ye look if ye likes luggin' yer tools everywhere ye goes!").also { + stage = END_DIALOGUE + } + 30 -> { + end() + Vinesweeper.Companion.VinesweeperTeleport.teleport(npc!!, player!!) + } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return ToolLeprechaunDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.TOOL_LEPRECHAUN_3021) + } +} + +/** + * Note: + * This Leprechaun Larry is a special case where he does NOT transport you to Winkin's farm, but has a store. + */ +@Initializable +class ToolLeprechaunOnVacationDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage) { + START_DIALOGUE -> npc(FacialExpression.OLD_HAPPY, "Aye, top o' th' mornin' to ya!", "Are ye wantin' help with th' tool store?").also { + stage = 2 + } + 2 -> showTopics( + Topic(FacialExpression.NEUTRAL, "Yes please.", 10), + Topic(FacialExpression.THINKING, "Why are you sunbathing up a mountain?", 3), + Topic(FacialExpression.NEUTRAL, "No thanks, I'll keep hold of my stuff.", 20), + Topic("Would you like to trade?", 12), + ) + 3 -> npcl(FacialExpression.OLD_HAPPY, "We tool leprechauns work hard, that we do. An'nary a penny do we get in return. So ye cannae begrudge me mah holiday an' a wee drink or twelve!").also { stage++ } + 4 -> playerl(FacialExpression.THINKING, "Yes, very nice, but why are you sunbathing up a mountain? Surely a beach would be more appropriate?").also { stage++ } + 5 -> npcl(FacialExpression.OLD_HAPPY, "Ahh, but I likes th' ruggedy mountain, ye see. Also, I ha' a terrible allergy to sand.").also { stage++ } + 6 -> playerl(FacialExpression.NEUTRAL, "Fair enough, I suppose.").also { stage++ } + 7 -> npcl(FacialExpression.OLD_HAPPY, "So were ye wantin' help with th' tool store?").also { stage++ } + 8 -> showTopics( + Topic(FacialExpression.NEUTRAL, "Yes, please.", 10), + Topic(FacialExpression.NEUTRAL, "No thanks, I'll keep hold of my stuff.", 20), + ) + 10 -> { + end() + openInterface(player, Components.FARMING_TOOLS_125) + } + 12 -> npcl(FacialExpression.OLD_HAPPY, "Sure, have a look.").also { stage++ } + 13 -> end().also{ + npc.openShop(player) + } + 20 -> npcl(FacialExpression.OLD_NORMAL, "Ye must be dafter than ye look if ye likes luggin' yer tools everywhere ye goes!").also { + stage = END_DIALOGUE + } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return ToolLeprechaunOnVacationDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.TOOL_LEPRECHAUN_4965) + } +} + +/** + * Note: + * Goth chatheads are unfortunately updated and have frozen FacialExpressions. Disabled talk-to for now. + */ +// @Initializable +class ToolLeprechaunGothDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage) { + START_DIALOGUE -> npc(FacialExpression.HAPPY, "Ah, 'tis a foine day to be sure! Can I help ye with tool", "storage, or a trip to Winkin's Farm, or what?").also { stage++ } + + 1 -> showTopics( + Topic(FacialExpression.NEUTRAL, "Yes, please.", 10), + Topic("What tools can you store?", 2, true), + Topic(FacialExpression.NEUTRAL, "No thanks, I'll keep hold of my stuff.", 20), + Topic("Can you take me to Winkin's Farm?", 30), + // "Other topics." -> Lost City quest where is Shamus. Evil Tree Reward collection in late 2009. + ) + 2 -> playerl(FacialExpression.THINKING, "What can you store?").also { stage++ } + 3 -> npcl(FacialExpression.HAPPY, "We'll hold onto yer rake, yer seed dibber, yer spade, yer secateurs, yer waterin' can and yer trowel - but mind it's not one of them fancy trowels only archaeologists use!").also { stage++ } + 4 -> npcl(FacialExpression.HAPPY, "We'll take a few buckets off yer hands too, and even yer compost and supercompost! There's room in our shed for plenty of compost, so bring it on.").also { stage++ } + 5 -> npcl(FacialExpression.HAPPY, "Also if ye hands us yer farming produce, we might be able to change it into banknotes.").also { stage++ } + 6 -> npcl(FacialExpression.HAPPY, "So... do ye want to be using the store?").also { stage++ } + 7 -> showTopics( + Topic(FacialExpression.NEUTRAL, "Yes, please.", 10), + Topic("What do you do with the tools you're storing?", 11, true), + Topic(FacialExpression.NEUTRAL, "No thanks, I'll keep hold of my stuff.", 20), + Topic("Can you take me to Winkin's Farm?", 30), + ) + 10 -> { + end() + openInterface(player, Components.FARMING_TOOLS_125) + } + 11 -> playerl(FacialExpression.THINKING, "What do you do with the tools you're storing? They can't possibly all fit in your pockets!").also { stage++ } + 12 -> npcl(FacialExpression.HAPPY, "We leprechauns have a shed where we keep 'em. It's a magic shed, so ye can get yer items back from any of us leprechauns whenever ye want. Saves ye havin' to carry loads of stuff around the country!").also { stage++ } + 13 -> npcl(FacialExpression.HAPPY, "So... do ye want to be using the store?").also { stage = 1 } + + 20 -> npcl(FacialExpression.NEUTRAL, "Ye must be dafter than ye look if ye likes luggin' yer tools everywhere ye goes!").also { + stage = END_DIALOGUE + } + 30 -> { + end() + Vinesweeper.Companion.VinesweeperTeleport.teleport(npc!!, player!!) + } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return ToolLeprechaunDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.GOTH_LEPRECHAUN_8000) + } +} \ 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 ad68f4222..75fbe8f07 100644 --- a/Server/src/main/content/global/skill/farming/ToolLeprechaunInterface.kt +++ b/Server/src/main/content/global/skill/farming/ToolLeprechaunInterface.kt @@ -1,227 +1,231 @@ package content.global.skill.farming import core.api.* -import core.game.component.Component import core.game.node.entity.player.Player import core.game.node.item.Item import org.rs09.consts.Components import org.rs09.consts.Items import core.game.interaction.InterfaceListener -private const val varp = 615 class ToolLeprechaunInterface : InterfaceListener { - private val FARMING_TOOLS = Components.FARMING_TOOLS_125 private val TOOLS_SIDE = Components.FARMING_TOOLS_SIDE_126 override fun defineInterfaceListeners() { - onOpen(FARMING_TOOLS){player, component -> - player.varpManager?.flagSave(varp) - player.interfaceManager.openSingleTab(Component(TOOLS_SIDE)) + onOpen(FARMING_TOOLS) { player, component -> + openSingleTab(player, TOOLS_SIDE) return@onOpen true } - onClose(FARMING_TOOLS){player, _ -> - player.interfaceManager.closeSingleTab() + onClose(FARMING_TOOLS) { player, _ -> + closeTabInterface(player) + return@onClose true } - on(FARMING_TOOLS){player, _, opcode, buttonID, _, _ -> - when(buttonID){ - 33 -> doWithdrawal(player, Items.RAKE_5341,::setHasRake,::hasRake) - 34 -> doWithdrawal(player,Items.SEED_DIBBER_5343,::setHasDibber,::hasDibber) - 35 -> doWithdrawal(player,Items.SPADE_952,::setHasSpade,::hasSpade) + on(FARMING_TOOLS) { player, _, opcode, buttonID, _, _ -> + when (buttonID) { + 33 -> doWithdrawal(player, Items.RAKE_5341, ::setHasRake, ::hasRake) + 34 -> doWithdrawal(player, Items.SEED_DIBBER_5343, ::setHasDibber, ::hasDibber) + 35 -> doWithdrawal(player, Items.SPADE_952, ::setHasSpade, ::hasSpade) 36 -> { - val sec = if(hasMagicSecateurs(player)) Items.MAGIC_SECATEURS_7409 else Items.SECATEURS_5329 - doWithdrawal(player,sec,::setHasSecateurs,::hasSecateurs) + val sec = if (hasMagicSecateurs(player)) Items.MAGIC_SECATEURS_7409 else Items.SECATEURS_5329 + doWithdrawal(player, sec, ::setHasSecateurs, ::hasSecateurs) } 37 -> { - if(!hasWateringCan(player)){ - player.dialogueInterpreter.sendDialogue("You don't have any of those stored.") + if (!hasWateringCan(player)) { + sendMessage(player, "You haven't got a watering can stored in here!") } else { - player.inventory.add(Item(getWateringCan(player))) - setNoWateringCan(player) + if (freeSlots(player) == 0) { + sendMessage(player, "You don't have enough space for that.") + } + if (addItem(player, getWateringCan(player))) setNoWateringCan(player) } } - 38 -> doWithdrawal(player,Items.GARDENING_TROWEL_5325,::setHasGardeningTrowel,::hasGardeningTrowel) - 39 -> doStackedWithdrawal(player,Items.BUCKET_1925,getAmount(opcode),::updateBuckets,::getNumBuckets) - 40 -> doStackedWithdrawal(player,Items.COMPOST_6032,getAmount(opcode),::updateCompost,::getNumCompost) - 41 -> doStackedWithdrawal(player,Items.SUPERCOMPOST_6034,getAmount(opcode),::updateSuperCompost,::getNumSuperCompost) + 38 -> doWithdrawal(player, Items.GARDENING_TROWEL_5325, ::setHasGardeningTrowel, ::hasGardeningTrowel) + 39 -> doStackedWithdrawal(player, Items.BUCKET_1925, getAmount(opcode), ::updateBuckets, ::getNumBuckets) + 40 -> doStackedWithdrawal(player, Items.COMPOST_6032, getAmount(opcode), ::updateCompost, ::getNumCompost) + 41 -> doStackedWithdrawal(player, Items.SUPERCOMPOST_6034, getAmount(opcode), ::updateSuperCompost, ::getNumSuperCompost) } - player.varpManager.get(varp).send(player) return@on true } - on(TOOLS_SIDE){player, _, opcode, buttonID, _, _ -> - when(buttonID){ - 18 -> doDeposit(player,Items.RAKE_5341,::setHasRake,::hasRake) - 19 -> doDeposit(player,Items.SEED_DIBBER_5343,::setHasDibber,::hasDibber) - 20 -> doDeposit(player,Items.SPADE_952,::setHasSpade,::hasSpade) + on(TOOLS_SIDE) { player, _, opcode, buttonID, _, _ -> + when (buttonID) { + 18 -> doDeposit(player, Items.RAKE_5341, ::setHasRake, ::hasRake) + 19 -> doDeposit(player, Items.SEED_DIBBER_5343, ::setHasDibber, ::hasDibber) + 20 -> doDeposit(player, Items.SPADE_952, ::setHasSpade, ::hasSpade) 21 -> { - if(!player.inventory.contains(Items.SECATEURS_5329,1) && !player.inventory.contains(Items.MAGIC_SECATEURS_7409,1)){ - player.dialogueInterpreter.sendDialogue("You don't have any of those to store.") - } else if (!hasSecateurs(player)){ - if(player.inventory.contains(Items.MAGIC_SECATEURS_7409,1)){ - player.inventory.remove(Item(Items.MAGIC_SECATEURS_7409)) + if (!inInventory(player, Items.SECATEURS_5329) && !inInventory(player, Items.MAGIC_SECATEURS_7409)) { + sendMessage(player, "You haven't got any secateurs to store.") + } else if (!hasSecateurs(player)) { + if (inInventory(player, Items.MAGIC_SECATEURS_7409)) { + removeItem(player, Items.MAGIC_SECATEURS_7409) setHasSecateurs(player,true) setHasMagicSecateurs(player,true) } else { - player.inventory.remove(Item(Items.SECATEURS_5329)) + removeItem(player, Items.SECATEURS_5329) setHasSecateurs(player,true) setHasMagicSecateurs(player,false) } } else { - player.dialogueInterpreter.sendDialogue("You already have one of those stored.") + sendMessage(player, "You cannot store more than one pair of secateurs in here.") } } 22 -> { val can = getHighestCan(player) - if(can == null){ - player.dialogueInterpreter.sendDialogue("You don't have any of those to store.") - } else if(!hasWateringCan(player)){ - player.inventory.remove(can) + if (can == null) { + sendMessage(player, "You haven't got a watering can to store.") + } else if (!hasWateringCan(player)) { + removeItem(player, can) setWateringCan(player,can) } else { - player.dialogueInterpreter.sendDialogue("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) - 24 -> doStackedDeposit(player,Items.BUCKET_1925,getAmount(opcode),::updateBuckets,::getNumBuckets) - 25 -> doStackedDeposit(player,Items.COMPOST_6032,getAmount(opcode),::updateCompost,::getNumCompost) - 26 -> doStackedDeposit(player,Items.SUPERCOMPOST_6034,getAmount(opcode),::updateSuperCompost,::getNumSuperCompost) + 23 -> doDeposit(player, Items.GARDENING_TROWEL_5325, ::setHasGardeningTrowel, ::hasGardeningTrowel) + 24 -> doStackedDeposit(player, Items.BUCKET_1925, getAmount(opcode), ::updateBuckets, ::getNumBuckets) + 25 -> doStackedDeposit(player, Items.COMPOST_6032, getAmount(opcode), ::updateCompost, ::getNumCompost) + 26 -> doStackedDeposit(player, Items.SUPERCOMPOST_6034, getAmount(opcode), ::updateSuperCompost, ::getNumSuperCompost) } - player.varpManager.get(varp).send(player) return@on true } } - private fun doWithdrawal(player: Player?, item: Int, withdrawMethod: (Player?,Boolean) -> Unit, checkMethod: (Player?) -> Boolean){ + private fun doWithdrawal(player: Player?, item: Int, withdrawMethod: (Player?, Boolean) -> Unit, checkMethod: (Player?) -> Boolean) { player ?: return - if(!checkMethod.invoke(player)){ - player.dialogueInterpreter.sendDialogue("You don't have any of those stored.") + if (!checkMethod.invoke(player)) { + val determiner = if (getItemName(item).lowercase().endsWith("s")) "any" else "a" + sendMessage(player, "You haven't got $determiner ${getItemName(item).lowercase()} stored in here!") } else { - if(!player.inventory.hasSpaceFor(Item(item))){ - player.dialogueInterpreter.sendDialogue("You don't have enough space for that.") + if (!hasSpaceFor(player, Item(item))) { + sendMessage(player, "You don't have enough space for that.") return } - withdrawMethod.invoke(player,false) - player.inventory.add(Item(item)) + withdrawMethod.invoke(player, false) + addItem(player, item) } } - private fun doDeposit(player: Player?, item: Int, depositMethod: (Player?,Boolean) -> Unit, checkMethod: (Player?) -> Boolean){ + private fun doDeposit(player: Player?, item: Int, depositMethod: (Player?, Boolean) -> Unit, checkMethod: (Player?) -> Boolean) { player ?: return - if(!player.inventory.contains(item,1)){ - player.dialogueInterpreter.sendDialogue("You don't have any of those to store.") + if (!inInventory(player, item)) { + val determiner = if (getItemName(item).lowercase().endsWith("s")) "any" else "a" + sendMessage(player, "You haven't got $determiner ${getItemName(item).lowercase()} to store.") + return } - if(!checkMethod.invoke(player)){ - depositMethod.invoke(player,true) - player.inventory.remove(Item(item)) + if (!checkMethod.invoke(player)) { + depositMethod.invoke(player, true) + removeItem(player, item) } else { - player.dialogueInterpreter.sendDialogue("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.") } } - private fun doStackedDeposit(player: Player?, item: Int, amount: Int, updateQuantityMethod: (Player?, Int) -> Unit, quantityCheckMethod: (Player?) -> Int){ + private fun doStackedDeposit(player: Player?, item: Int, amount: Int, updateQuantityMethod: (Player?, Int) -> Unit, quantityCheckMethod: (Player?) -> Int) { player ?: return - val hasAmount = player.inventory.getAmount(item) + val hasAmount = amountInInventory(player, item) var finalAmount = amount - val spaceLeft = (if(item == Items.BUCKET_1925) 31 else 255) - quantityCheckMethod.invoke(player) + val spaceLeft = (if (item == Items.BUCKET_1925) 255 else 255) - quantityCheckMethod.invoke(player) - if(hasAmount == 0){ - player.dialogueInterpreter.sendDialogue("You don't have any of those to store.") + if (hasAmount == 0) { + val itemName = if (item == Items.BUCKET_1925) "buckets" else getItemName(item).lowercase() + sendMessage(player, "You haven't got any $itemName to store.") return } - if(amount == -2){ - sendInputDialogue(player, true, "Enter the amount:"){value -> + if (amount == -2) { + sendInputDialogue(player, true, "Enter the amount:") { value -> var amt = value as Int - if(amt > hasAmount){ + if (amt > hasAmount) { amt = hasAmount } - if(amt > spaceLeft){ + if (amt > spaceLeft) { amt = spaceLeft } - if(amt == 0){ - player.dialogueInterpreter.sendDialogue("You don't have any of those to store.") - return@sendInputDialogue - } - player.inventory.remove(Item(item,amt)) - updateQuantityMethod.invoke(player,amt) - player.varpManager.get(varp).send(player) + if (removeItem(player, Item(item, amt))) updateQuantityMethod.invoke(player, amt) } return } - if(amount == -1){ + + if (amount == -1) { finalAmount = hasAmount - if(finalAmount > spaceLeft){ + if (finalAmount > spaceLeft) { finalAmount = spaceLeft } } - if(finalAmount > hasAmount){ + if (finalAmount > hasAmount) { finalAmount = hasAmount } - if(finalAmount > spaceLeft) finalAmount = spaceLeft - if(!player.inventory.contains(item,finalAmount)){ - player.dialogueInterpreter.sendDialogue("You don't have any of those to store.") + if (finalAmount > spaceLeft) { + if (item == Items.BUCKET_1925) { + sendMessage(player, "You can't store that many buckets in here.") + } else { + sendMessage(player, "You can't store that much ${getItemName(item).lowercase()} in here.") + } return } - if(finalAmount > spaceLeft){ - player.dialogueInterpreter.sendDialogue("You can't store any more of those.") - return - } - - player.inventory.remove(Item(item,finalAmount)) + removeItem(player, Item(item, finalAmount)) updateQuantityMethod.invoke(player,finalAmount) } - private fun doStackedWithdrawal(player: Player?,item: Int, amount: Int,updateQuantityMethod: (Player?,Int) -> Unit, quantityCheckMethod: (Player?) -> Int){ + private fun doStackedWithdrawal(player: Player?, item: Int, amount: Int, updateQuantityMethod: (Player?, Int) -> Unit, quantityCheckMethod: (Player?) -> Int) { player ?: return - val hasAmount = quantityCheckMethod.invoke(player) + val hasAmount = if (item == Items.BUCKET_1925) 31 else quantityCheckMethod.invoke(player) var finalAmount = amount - if(hasAmount == 0){ - player.dialogueInterpreter.sendDialogue("You don't have any of those stored.") - } else { - if(amount == -2){ - sendInputDialogue(player, true, "Enter the amount:"){value -> - var amt = value as Int - if(amt > hasAmount){ - amt = hasAmount - } - if(amt > player.inventory.freeSlots()){ - amt = player.inventory.freeSlots() - } - if(amt <= 0){ - player.dialogueInterpreter.sendDialogue("You don't have enough inventory space for that.") - } else { - player.inventory.add(Item(item, amt)) - updateQuantityMethod.invoke(player, -amt) - player.varpManager.get(varp).send(player) - } - } - return - } - if(amount == -1){ - finalAmount = player.inventory.freeSlots() - } - if(finalAmount > hasAmount){ - finalAmount = hasAmount - } - if(!player.inventory.hasSpaceFor(Item(item,finalAmount)) || finalAmount == 0){ - player.dialogueInterpreter.sendDialogue("You don't have enough inventory space for that.") - return - } - player.inventory.add(Item(item,finalAmount)) - updateQuantityMethod.invoke(player,-finalAmount) + + if (hasAmount == 0) { + val itemName = if (item == Items.BUCKET_1925) "buckets" else getItemName(item).lowercase() + sendMessage(player, "You haven't got any $itemName stored in here!") + return } + + if (amount == -2) { + sendInputDialogue(player, InputType.AMOUNT, "Enter the amount:") { value -> + var amt = value as Int + if (amt > hasAmount) { + amt = hasAmount + } + if (amt > freeSlots(player)) { + amt = freeSlots(player) + } + if (amt <= 0) { + sendMessage(player, "You don't have enough inventory space for that.") + } else { + addItem(player, item, amt) + updateQuantityMethod.invoke(player, -amt) + } + } + return + } + if (amount == -1) { + finalAmount = freeSlots(player) + } + if (finalAmount > hasAmount) { + finalAmount = hasAmount + } + if (!hasSpaceFor(player, Item(item, finalAmount)) || finalAmount == 0) { + sendMessage(player, "You don't have enough inventory space for that.") + return + } + addItem(player, item, finalAmount) + updateQuantityMethod.invoke(player, -finalAmount) } - fun getAmount(opcode: Int): Int{ - return when(opcode) { + fun getAmount(opcode: Int): Int { + return when (opcode) { 155 -> 1 196 -> 5 124 -> -1 @@ -230,50 +234,50 @@ class ToolLeprechaunInterface : InterfaceListener { } } - private fun hasRake(player: Player?): Boolean{ - return checkBit(player,0) + private fun hasRake(player: Player?): Boolean { + return getVarbit(player!!, 1435) == 1 } - private fun setHasRake(player: Player?,hasRake: Boolean){ - player?.varpManager?.get(varp)?.setVarbit(0,if(hasRake) 1 else 0) + private fun setHasRake(player: Player?, hasRake: Boolean) { + setVarbit(player!!, 1435, if (hasRake) 1 else 0, true) } - private fun hasDibber(player: Player?): Boolean{ - return checkBit(player,1) + private fun hasDibber(player: Player?): Boolean { + return getVarbit(player!!, 1436) == 1 } - private fun setHasDibber(player: Player?,hasDibber: Boolean){ - player?.varpManager?.get(varp)?.setVarbit(1,if(hasDibber) 1 else 0) + private fun setHasDibber(player: Player?, hasDibber: Boolean) { + setVarbit(player!!, 1436, if (hasDibber) 1 else 0, true) } - private fun hasSpade(player: Player?): Boolean{ - return checkBit(player,2) + private fun hasSpade(player: Player?): Boolean { + return getVarbit(player!!, 1437) == 1 } - private fun setHasSpade(player: Player?,hasSpade: Boolean){ - player?.varpManager?.get(varp)?.setVarbit(2,if(hasSpade) 1 else 0) + private fun setHasSpade(player: Player?, hasSpade: Boolean) { + setVarbit(player!!, 1437, if (hasSpade) 1 else 0, true) } - private fun hasSecateurs(player: Player?): Boolean{ - return checkBit(player,3) + private fun hasSecateurs(player: Player?): Boolean { + return getVarbit(player!!, 1438) == 1 } - private fun setHasSecateurs(player: Player?,hasSecateurs: Boolean){ - player?.varpManager?.get(varp)?.setVarbit(3,if(hasSecateurs) 1 else 0) + private fun setHasSecateurs(player: Player?, hasSecateurs: Boolean) { + setVarbit(player!!, 1438, if (hasSecateurs) 1 else 0, true) } - private fun hasWateringCan(player: Player?): Boolean{ - return checkBitRange(player,4,7) > 0 + private fun hasWateringCan(player: Player?): Boolean { + return getVarbit(player!!, 1439) > 0 } - private fun getWateringCan(player: Player?): Int{ - var can = checkBitRange(player,4,7) //Watering cans are stored in the Varp as a number between 1 and 9. Watering Can(0) is 1 and Watering Can(8) is 9 - if(can == 1) can = 0 + private fun getWateringCan(player: Player?): Int { + var can = getVarbit(player!!, 1439) //Watering cans are stored in the Varp as a number between 1 and 9. Watering Can(0) is 1 and Watering Can(8) is 9 + if (can == 1) can = 0 return Items.WATERING_CAN_5331 + can } - private fun setWateringCan(player: Player?, item: Item){ - val can = when(item.id){ + private fun setWateringCan(player: Player?, item: Item) { + val can = when (item.id) { Items.WATERING_CAN_5331 -> 1 Items.WATERING_CAN1_5333 -> 2 Items.WATERING_CAN2_5334 -> 3 @@ -285,71 +289,63 @@ class ToolLeprechaunInterface : InterfaceListener { Items.WATERING_CAN8_5340 -> 9 else -> 0 } - player?.varpManager?.get(varp)?.setVarbit(4,can) + setVarbit(player!!, 1439, can, true) } - private fun setNoWateringCan(player: Player?){ - player?.varpManager?.get(varp)?.setVarbit(4,0) + private fun setNoWateringCan(player: Player?) { + setVarbit(player!!, 1439, 0, true) } private fun hasGardeningTrowel(player: Player?): Boolean { - return checkBit(player,8) + return getVarbit(player!!, 1440) == 1 } - private fun setHasGardeningTrowel(player: Player?,hasTrowel: Boolean){ - player?.varpManager?.get(varp)?.setVarbit(8,if(hasTrowel) 1 else 0) + private fun setHasGardeningTrowel(player: Player?, hasTrowel: Boolean) { + setVarbit(player!!, 1440, if (hasTrowel) 1 else 0, true) } private fun getNumBuckets(player: Player?): Int { - return checkBitRange(player,9,13) + return getVarbit(player!!, 1441) } - private fun updateBuckets(player: Player?,amount: Int){ - player?.varpManager?.get(varp)?.setVarbit(9,getNumBuckets(player) + amount) + private fun updateBuckets(player: Player?, amount: Int) { + setVarbit(player!!, 1441, getNumBuckets(player) + amount, true) } private fun getNumCompost(player: Player?): Int { - return checkBitRange(player,14,21) + return getVarbit(player!!, 1442) } - private fun updateCompost(player: Player?,amount: Int){ - player?.varpManager?.get(varp)?.setVarbit(14,getNumCompost(player) + amount) + private fun updateCompost(player: Player?, amount: Int) { + setVarbit(player!!, 1442, getNumCompost(player) + amount, true) } private fun getNumSuperCompost(player: Player?): Int { - return checkBitRange(player,22,29) + return getVarbit(player!!, 1443) } - private fun updateSuperCompost(player: Player?,amount: Int){ - player?.varpManager?.get(varp)?.setVarbit(22,getNumSuperCompost(player) + amount) + private fun updateSuperCompost(player: Player?, amount: Int) { + setVarbit(player!!, 1443, getNumSuperCompost(player) + amount, true) } private fun hasMagicSecateurs(player: Player?): Boolean { - return checkBit(player,30) + return getVarbit(player!!, 1848) == 1 } - private fun setHasMagicSecateurs(player: Player?,hasMagic: Boolean){ - player?.varpManager?.get(varp)?.setVarbit(30,if(hasMagic) 1 else 0) + private fun setHasMagicSecateurs(player: Player?, hasMagic: Boolean) { + setVarbit(player!!, 1848, if (hasMagic) 1 else 0, true) } - private fun checkBitRange(player: Player?,start: Int, end: Int): Int{ - return (player?.varpManager?.get(varp)?.getBitRangeValue(start,end) ?: 0) ushr start - } - - private fun checkBit(player: Player?,offset: Int): Boolean{ - return player?.varpManager?.get(varp)?.getVarbitValue(offset) == 1 - } - - private fun getHighestCan(player: Player?): Item?{ + private fun getHighestCan(player: Player?): Item? { player ?: return null var highestCan = Item(0) - for(item in player.inventory.toArray()){ - if(item == null) continue - if(item.name.contains("Watering")){ - if(item.id > highestCan.id) highestCan = item + for (item in player.inventory.toArray()) { + if (item == null) continue + if (item.name.contains("Watering")) { + if (item.id > highestCan.id) highestCan = item } } - return if(highestCan.id == 0) null else highestCan + return if (highestCan.id == 0) null else highestCan } -} \ No newline at end of file +} diff --git a/Server/src/main/content/global/skill/farming/UseWithBinHandler.kt b/Server/src/main/content/global/skill/farming/UseWithBinHandler.kt index f3d5f7bbf..8d557e9b6 100644 --- a/Server/src/main/content/global/skill/farming/UseWithBinHandler.kt +++ b/Server/src/main/content/global/skill/farming/UseWithBinHandler.kt @@ -1,7 +1,6 @@ package content.global.skill.farming -import core.api.toIntArray -import core.game.node.item.Item +import core.api.* import core.game.system.task.Pulse import core.game.world.update.flag.context.Animation import org.rs09.consts.Items @@ -25,56 +24,58 @@ class UseWithBinHandler : InteractionListener { val used = usedNode.id - when(used){ - Items.COMPOST_POTION1_6476,Items.COMPOST_POTION2_6474, Items.COMPOST_POTION3_6472,Items.COMPOST_POTION4_6470 -> { - if(!bin.isSuperCompost && bin.isFinished && !bin.isClosed){ - player.animator.animate(compostPotionAnimation) - player.pulseManager.run(object : Pulse(compostPotionAnimation.duration){ + when (used) { + Items.COMPOST_POTION1_6476, Items.COMPOST_POTION2_6474, Items.COMPOST_POTION3_6472, Items.COMPOST_POTION4_6470 -> { + if (!bin.isSuperCompost && bin.isFinished && !bin.isClosed) { + animate(player, compostPotionAnimation) + submitIndividualPulse(player, object : Pulse(compostPotionAnimation.duration) { override fun pulse(): Boolean { - if(player.inventory.remove(usedNode.asItem())) { + if (removeItem(player, usedNode.asItem())) { bin.convert() - player.inventory.add(Item(used.getNext())) + addItem(player, used.getNext()) } return true } }) } else { - player.dialogueInterpreter.sendDialogue("You can only do this with an open bin of","finished regular compost.") + sendDialogue(player, "You can only do this with an open bin of finished regular compost.") } } Items.BUCKET_1925 -> { - if(bin.isFinished && !bin.isClosed){ - player.pulseManager.run(object : Pulse(scoopAnimation.duration){ + if (bin.isFinished && !bin.isClosed) { + submitIndividualPulse(player, object : Pulse(scoopAnimation.duration) { override fun pulse(): Boolean { - if(!player.inventory.containsItem(usedNode.asItem())) return true - player.animator.animate(scoopAnimation) + if (!player.inventory.containsItem(usedNode.asItem())) return true + animate(player, scoopAnimation) val item = bin.takeItem() - if(item != null && player.inventory.remove(usedNode.asItem())){ + if (item != null && removeItem(player, usedNode.asItem())) { player.inventory.add(item) } return item == null || !player.inventory.containsItem(usedNode.asItem()) } }) } else { - player.dialogueInterpreter.sendDialogue("You can only scoop an opened bin of finished compost.") + sendDialogue(player, "You can only scoop an opened bin of finished compost.") } } else -> - if(bin.isFull()){ - player.sendMessage("This compost bin is already full.") + if (bin.isFull()) { + sendMessage(player, "This compost bin is already full.") return@onUseWith true - } else { - player.pulseManager.run(object : Pulse(fillAnim.duration){ + } else if (!bin.isFinished) { + submitIndividualPulse(player, object : Pulse(fillAnim.duration) { override fun pulse(): Boolean { - player.animator.animate(fillAnim) - if(player.inventory.remove(usedNode.asItem())){ + animate(player, fillAnim) + if (removeItem(player, usedNode.asItem())) { bin.addItem(usedNode.asItem()) } return bin.isFull() || player.inventory.getAmount(usedNode.asItem()) == 0 } }) + } else { + sendDialogue(player, "The compost bin must be empty of compost before you can put new items in it.") } } return@onUseWith true @@ -82,8 +83,8 @@ class UseWithBinHandler : InteractionListener { } fun loadNodes() { - for(p in Plantable.values()){ - if(p.harvestItem != Items.SCARECROW_6059) { + for (p in Plantable.values()) { + if (p.harvestItem != Items.SCARECROW_6059) { allowedNodes.add(p.harvestItem) } } @@ -96,8 +97,8 @@ class UseWithBinHandler : InteractionListener { allowedNodes.add(Items.BUCKET_1925) } - private fun Int.getNext(): Int{ - if(this != 6476) return this + 2 + private fun Int.getNext(): Int { + if (this != 6476) return this + 2 else return Items.VIAL_229 } -} \ No newline at end of file +} diff --git a/Server/src/main/content/global/skill/farming/UseWithPatchHandler.kt b/Server/src/main/content/global/skill/farming/UseWithPatchHandler.kt index bb2352078..6dac10dce 100644 --- a/Server/src/main/content/global/skill/farming/UseWithPatchHandler.kt +++ b/Server/src/main/content/global/skill/farming/UseWithPatchHandler.kt @@ -5,82 +5,123 @@ 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 +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 val SEED_DIBBER = Items.SEED_DIBBER_5343 val SPADE = Items.SPADE_952 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 secateursAnim = Animation(7227) + 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() - val patches = ArrayList() override fun defineListeners() { loadNodes() - onUseWith(IntType.SCENERY, allowedNodes.toIntArray(), *patches.toIntArray()) { player, used, with -> + onUseWith(IntType.SCENERY, allowedNodes.toIntArray(), *FarmingPatch.patchNodes.toIntArray()) { player, used, with -> val patch = FarmingPatch.forObject(with.asScenery()) ?: return@onUseWith true val usedItem = used.asItem() + if (patch == FarmingPatch.TROLL_STRONGHOLD_HERB) { + if (!hasRequirement(player, Quests.MY_ARMS_BIG_ADVENTURE)) + return@onUseWith true + } + player.faceLocation(with.location) - when(usedItem.id){ + when (usedItem.id) { RAKE -> PatchRaker.rake(player,patch) - SEED_DIBBER -> player.sendMessage("I should plant a seed, not the seed dibber.") - SPADE -> player.dialogueInterpreter.open(67984003,patch.getPatchFor(player)) //DigUpPatchDialogue.kt - SECATEURS -> { + SEED_DIBBER -> sendMessage(player, "I should plant a seed, not the seed dibber.") + SPADE -> { + val anim = spadeDigAnim val p = patch.getPatchFor(player) - if(patch.type == PatchType.TREE) { - if(p.isDiseased && !p.isDead) { - player.pulseManager.run(object: Pulse(){ + if (p.isDead) { + sendMessage(player, "You start digging the farming patch...") + queueScript(player, 0, QueueStrength.WEAK) { stage: Int -> + when (stage) { + 0 -> { + animate(player, anim) + playAudio(player, Sounds.DIGSPADE_1470) + return@queueScript delayScript(player,anim.duration + 2) + } + 1 -> { + animate(player, anim) + playAudio(player, Sounds.DIGSPADE_1470) + return@queueScript delayScript(player, anim.duration + 1) + } + 2 -> { + p.clear() + sendMessage(player, "You have successfully cleared this patch for new crops.") + return@queueScript stopExecuting(player) + } + else -> return@queueScript stopExecuting(player) + } + } + } else { + openDialogue(player, 67984003, patch.getPatchFor(player)) // DigUpPatchDialogue.kt + } + } + SECATEURS, MAGIC_SECATEURS -> { + val p = patch.getPatchFor(player) + if (patch.type == PatchType.TREE_PATCH) { + if (p.isDiseased && !p.isDead) { + submitIndividualPulse(player, object: Pulse() { override fun pulse(): Boolean { - player.animator.animate(secateursAnim) + if (usedItem.id == SECATEURS) animate(player, secateursTreeAnim) else animate(player, magicSecateursTreeAnim) p.cureDisease() return true } }) - } else if(p.plantable == Plantable.WILLOW_SAPLING && p.harvestAmt > 0) { + } else if (p.plantable == Plantable.WILLOW_SAPLING && p.harvestAmt > 0) { val pulse = CropHarvester.harvestPulse(player, with, Items.WILLOW_BRANCH_5933) ?: return@onUseWith false - player.pulseManager.run(pulse) + submitIndividualPulse(player, pulse) } } } TROWEL, Items.PLANT_POT_5350 -> { - if(!player.inventory.containsAtLeastOneItem(TROWEL)) { - player.sendMessage("You need a trowel to fill plant pots with dirt.") + if (!inInventory(player, TROWEL)) { + sendMessage(player, "You need a trowel to fill plant pots with dirt.") return@onUseWith true } val p = patch.getPatchFor(player) - if(!p.isWeedy()){ - player.sendMessage("This patch has something growing in it.") + if (!p.isWeedy() && !p.isEmptyAndWeeded()) { + sendMessage(player, "This patch has something growing in it.") return@onUseWith true - } else if (p.currentGrowthStage != 3){ - player.sendMessage("I should clear this of weeds before trying to take some dirt.") + } else if (p.currentGrowthStage != 3) { + sendMessage(player, "I should clear this of weeds before trying to take some dirt.") return@onUseWith true } - val potAmount = player.inventory.getAmount(Items.PLANT_POT_5350) + val potAmount = amountInInventory(player, Items.PLANT_POT_5350) - if(potAmount == 0){ - player.sendMessage("You have no plant pots to fill.") + if (potAmount == 0) { + sendMessage(player, "You have no plant pots to fill.") return@onUseWith true } - val anim = Animation(2272) + val anim = trowelDigAnim - player.pulseManager.run(object : Pulse(anim.duration){ + submitIndividualPulse(player, object : Pulse(anim.duration) { override fun pulse(): Boolean { - if(player.inventory.remove(Item(Items.PLANT_POT_5350))){ - player.animator.animate(anim) - player.inventory.add(Item(Items.PLANT_POT_5354)) + if (removeItem(player, Items.PLANT_POT_5350)) { + animate(player, anim) + addItem(player, Items.PLANT_POT_5354) } else return true return false } @@ -89,168 +130,222 @@ class UseWithPatchHandler : InteractionListener { Items.PLANT_CURE_6036 -> { val p = patch.getPatchFor(player) - if(p.isDiseased && !p.isDead){ - player.pulseManager.run(object: Pulse(){ - override fun pulse(): Boolean { - player.animator.animate(plantCureAnim) - player.audioManager.send(2438) - if(player.inventory.remove(usedItem)){ - player.inventory.add(Item(Items.VIAL_229)) - p.cureDisease() + val patchName = p.patch.type.displayName() + if (p.isDiseased && !p.isDead) { + sendMessage(player, "You treat the $patchName with the plant cure.") + queueScript(player, 0, QueueStrength.WEAK) { stage: Int -> + when (stage) { + 0 -> { + animate(player, plantCureAnim) + playAudio(player, Sounds.FARMING_PLANTCURE_2438) + return@queueScript delayScript(player, plantCureAnim.duration / 2) } - return true + 1 -> { + if (removeItem(player, usedItem)) { + addItem(player, Items.VIAL_229) + p.cureDisease() + sendMessage(player, "It is restored to health.") + } + return@queueScript stopExecuting(player) + } + else -> return@queueScript stopExecuting(player) } - }) + } } else { - player.sendMessage("I have no reason to do this right now.") + sendMessage(player, "I have no reason to do this right now.") } } - 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 -> { + 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 && (t == PatchType.ALLOTMENT || t == PatchType.FLOWER || t == PatchType.HOPS) && !p.isGrown()){ - player.pulseManager.run(object : Pulse(){ + if (t == PatchType.ALLOTMENT || t == PatchType.FLOWER_PATCH || t == PatchType.HOPS_PATCH) { + submitIndividualPulse(player, object : Pulse() { override fun pulse(): Boolean { - if(p.isWeedy()){ - player.sendMessage("You should grow something first.") + if (p.isWeedy() || p.isEmptyAndWeeded()) { + sendMessage(player, "You should grow something first.") return true } - player.animator.animate(wateringCanAnim) - player.audioManager.send(2446) - if(player.inventory.remove(usedItem)){ - player.inventory.add(Item(usedItem.id.getNext())) + 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 + } + animate(player, wateringCanAnim) + playAudio(player, Sounds.FARMING_WATERING_2446) + if (removeItem(player, usedItem)) { + addItem(player, usedItem.id.getNext()) p.water() } return true } }) + } else { + sendMessage(player, "This patch doesn't need watering.") } } Items.SUPERCOMPOST_6034, Items.COMPOST_6032 -> { val p = patch.getPatchFor(player) - if(p.compost == CompostType.NONE) { - player.animator.animate(pourBucketAnim) - player.audioManager.send(2427) - player.pulseManager.run(object : Pulse(){ - override fun pulse(): Boolean { - if(player.inventory.remove(usedItem,false)){ - p.compost = if(usedItem.id == Items.SUPERCOMPOST_6034) CompostType.SUPER else CompostType.NORMAL - if(p.compost == CompostType.SUPER) rewardXP(player, Skills.FARMING, 26.0) else rewardXP(player, Skills.FARMING, 18.5) - if(p.plantable != null && p.plantable?.applicablePatch != PatchType.FLOWER) { - p.harvestAmt += if(p.compost == CompostType.NORMAL) 1 else if(p.compost == CompostType.SUPER) 2 else 0 - } - p.cropLives += if(p.compost == CompostType.SUPER) 2 else 1 - player.inventory.add(Item(Items.BUCKET_1925)) + val patchName = p.patch.type.displayName() + + if (!p.isEmptyAndWeeded()) { + sendMessage(player, "This patch needs to be empty and weeded to do that.") + } else if (p.compost == CompostType.NONE) { + animate(player, pourBucketAnim) + playAudio(player, Sounds.FARMING_COMPOST_2427) + runTask(player) { + if (player.inventory.remove(usedItem,false)) { + sendMessage(player, "You treat the $patchName with ${usedItem.name.lowercase()}.") + p.compost = if (usedItem.id == Items.SUPERCOMPOST_6034) CompostType.SUPERCOMPOST else CompostType.COMPOST + if (p.compost == CompostType.SUPERCOMPOST) rewardXP(player, Skills.FARMING, 26.0) else rewardXP(player, Skills.FARMING, 18.5) + if (p.plantable != null && p.plantable?.applicablePatch != PatchType.FLOWER_PATCH) { + p.harvestAmt += if (p.compost == CompostType.COMPOST) 1 else if (p.compost == CompostType.SUPERCOMPOST) 2 else 0 } - return true + p.cropLives += if (p.compost == CompostType.SUPERCOMPOST) 2 else 1 + addItem(player, Items.BUCKET_1925) } - }) + return@runTask + } } else { - player.sendMessage("This patch has already been treated with compost.") + sendMessage(player, "This $patchName has already been treated with ${p.compost.name.lowercase()}.") } } else -> { val plantable = Plantable.forItemID(usedItem.id) ?: return@onUseWith false - if(plantable.applicablePatch != patch.type){ - player.sendMessage("You can't plant that seed in this patch.") + if (plantable.applicablePatch != patch.type) { + 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 $plantableNamePlural in $patchType.") return@onUseWith true } - if(plantable.requiredLevel > player.skills.getLevel(Skills.FARMING)){ - player.sendMessage("You need a Farming level of ${plantable.requiredLevel} to plant this.") + if (!hasLevelDyn(player, Skills.FARMING, plantable.requiredLevel)) { + sendMessage(player, "You need a Farming level of ${plantable.requiredLevel} to plant this.") return@onUseWith true } val p = patch.getPatchFor(player) - if(p.getCurrentState() < 3 && p.isWeedy()){ - player.sendMessage("You must weed your patch before you can plant a seed in it.") + if (p.getCurrentState() < 3 && p.isWeedy() && plantable != Plantable.SCARECROW) { + sendMessage(player, "This patch needs weeding first.") return@onUseWith true - } else if(p.getCurrentState() > 3){ - player.sendMessage("There is already something growing in this patch.") + } else if (p.getCurrentState() > 3) { + sendMessage(player, "There is already something growing in this patch.") return@onUseWith true } - val plantItem = - if(patch.type == PatchType.ALLOTMENT) Item(plantable.itemID,3) else if(patch.type == PatchType.HOPS){ - if(plantable == Plantable.JUTE_SEED) Item(plantable.itemID,3) else Item(plantable.itemID,4) - } else { - Item(plantable.itemID,1) - } + 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.ALLOTMENT){ - if(!player.inventory.containsItem(plantItem)){ - player.sendMessage("You need 3 seeds to plant an allotment patch.") - 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 } - if(patch.type != PatchType.FRUIT_TREE && patch.type != PatchType.TREE){ - if(!player.inventory.contains(Items.SEED_DIBBER_5343,1)){ - player.sendMessage("You need a seed dibber to plant that.") - return@onUseWith true - } - } else { - if(!player.inventory.contains(Items.SPADE_952,1)){ - player.sendMessage("You need a spade to plant that.") - return@onUseWith true - } + + val requiredItem = when (patch.type) { + PatchType.TREE_PATCH, PatchType.FRUIT_TREE_PATCH -> Items.SPADE_952 + PatchType.FLOWER_PATCH -> if (plantable == Plantable.SCARECROW) null else Items.SEED_DIBBER_5343 + else -> Items.SEED_DIBBER_5343 } - player.lock() - if(player.inventory.remove(plantItem)) { - player.animator.animate(Animation(2291)) - player.audioManager.send(2432) - player.pulseManager.run(object : Pulse(3) { - 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) + if (requiredItem != null && !inInventory(player, requiredItem)) { + sendMessage(player, "You need ${prependArticle(requiredItem.asItem().name.lowercase())} to plant that.") + 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) } - p.plant(plantable) - player.skills.addExperience(Skills.FARMING, plantable.plantingXP) - p.setNewHarvestAmount() - if(p.patch.type == PatchType.TREE || p.patch.type == PatchType.FRUIT_TREE){ - player.inventory.add(Item(Items.PLANT_POT_5350)) - } - player.unlock() - return true + animate(player, anim) + playAudio(player, sound) + val delay = if (patch.type == PatchType.TREE_PATCH || patch.type == PatchType.FRUIT_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) { + addItem(player, Items.PLANT_POT_5350) + } + + val itemAmount = + if (p.patch.type == PatchType.TREE_PATCH || p.patch.type == PatchType.FRUIT_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 } } - fun loadNodes(){ - patches.addAll(8550..8557) //allotment wrappers - patches.addAll(7847..7853) //flower patch wrappers - patches.addAll(8150..8156) //herb patch wrappers - patches.addAll(8388..8391) // Tree patches - patches.add(19147) //Tree patch - patches.addAll(7962..7965) //fruit trees - patches.addAll(8173..8176) //hops - patches.addAll(7577..7580) //bush - patches.add(23760) //evil turnip - patches.add(7572) //belladonna - patches.add(8337) //mushroom - patches.add(27197) //jade vine - patches.add(7771) //cactus - patches.add(7807) //calquat - patches.addAll(8382..8383)//spirit trees - patches.add(8338) //spirit tree - patches.add(18816) //death plateau wrapper - for(p in Plantable.values()){ + fun loadNodes() { + for (p in Plantable.values()) { allowedNodes.add(p.itemID) } - allowedNodes.addAll(arrayListOf(RAKE,SEED_DIBBER,SPADE,SECATEURS,TROWEL,Items.SUPERCOMPOST_6034,Items.COMPOST_6032,Items.PLANT_CURE_6036,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, Items.PLANT_POT_5350)) + allowedNodes.addAll( + arrayListOf( + RAKE, + SEED_DIBBER, + SPADE, + SECATEURS, + MAGIC_SECATEURS, + TROWEL, + Items.SUPERCOMPOST_6034, + Items.COMPOST_6032, + Items.PLANT_CURE_6036, + 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, + Items.PLANT_POT_5350 + ) + ) } private fun Int.getNext(): Int { - if(this == Items.WATERING_CAN1_5333) return Items.WATERING_CAN_5331 + if (this == Items.WATERING_CAN1_5333) return Items.WATERING_CAN_5331 else return this - 1 } } diff --git a/Server/src/main/content/global/skill/farming/timers/Compost.kt b/Server/src/main/content/global/skill/farming/timers/Compost.kt new file mode 100644 index 000000000..cc69490a0 --- /dev/null +++ b/Server/src/main/content/global/skill/farming/timers/Compost.kt @@ -0,0 +1,65 @@ +package content.global.skill.farming.timers + +import core.api.* +import core.game.system.timer.* +import org.json.simple.* +import core.game.node.entity.Entity +import core.game.node.entity.player.Player +import content.global.skill.farming.* + +class Compost : PersistTimer (500, "farming:compost", isSoft = true) { + private val binMap = HashMap() + lateinit var player: Player + + override fun onRegister (entity: Entity) { + player = (entity as? Player)!! + } + + override fun getInitialRunDelay() : Int { + return 1 //run once immediately after log in to complete any pending-but-enough-time-has-passed bins. + } + + override fun run (entity: Entity) : Boolean { + val removeList = ArrayList () + for((cBin,bin) in binMap){ + if(bin.isReady() && !bin.isFinished){ + bin.finish() + } + else if (bin.isDefaultState()) + removeList.add(cBin) + } + removeList.forEach { binMap.remove(it) } + removeList.clear() + + return binMap.isNotEmpty() + } + + fun getBin (bin: CompostBins) : CompostBin{ + return binMap[bin] ?: (CompostBin (player, bin).also { binMap[bin] = it }) + } + + fun getBins(): MutableCollection{ + return binMap.values + } + + override fun save (root: JSONObject, entity: Entity) { + val bins = JSONArray() + for((key,bin) in binMap){ + val b = JSONObject() + b.put("bin-ordinal",key.ordinal) + bin.save(b) + bins.add(b) + } + root.put("bins", bins) + } + + override fun parse (root: JSONObject, entity: Entity) { + (root["bins"] as JSONArray).forEach { + val bin = it as JSONObject + val binOrdinal = bin["bin-ordinal"].toString().toInt() + val cBin = CompostBins.values()[binOrdinal] + val b = CompostBin ((entity as? Player)!!, cBin).also { binMap[cBin] = it } + b.parse(bin["binData"] as JSONObject) + } + } +} diff --git a/Server/src/main/content/global/skill/farming/timers/CropGrowth.kt b/Server/src/main/content/global/skill/farming/timers/CropGrowth.kt new file mode 100644 index 000000000..074e5ae43 --- /dev/null +++ b/Server/src/main/content/global/skill/farming/timers/CropGrowth.kt @@ -0,0 +1,153 @@ +package content.global.skill.farming.timers + +import core.tools.* +import core.game.system.timer.* +import org.json.simple.* +import core.game.node.entity.Entity +import core.game.node.entity.player.Player +import content.global.skill.farming.* +import java.util.concurrent.TimeUnit +import java.time.* + +class CropGrowth : PersistTimer (500, "farming:crops", isSoft = true) { + private val patchMap = HashMap() + lateinit var player: Player + + override fun onRegister (entity: Entity) { + player = (entity as? Player)!! + runOfflineCatchupLogic() + } + + //Sync the 5 minute run cycles with :05 on realtime clocks - authentic + override fun getInitialRunDelay() : Int { + val now = LocalTime.now() + val minsUntil5MinSync = 5 - (now.getMinute() % 5) + val ticks = secondsToTicks (minsUntil5MinSync * 60) + player.debug("[CropGrowth] Scheduled first growth cycle for $ticks ticks from now.") + return ticks + } + + override fun run (entity: Entity) : Boolean { + var removeList = ArrayList() + for((fp,patch) in patchMap){ + if(patch.getCurrentState() in 1..3 && patch.nextGrowth == 0L){ + patch.nextGrowth = System.currentTimeMillis() + 60000 + continue + } + + //Go ahead and grow anything within 4 minutes of the 5-minute-synced growth cycles, bringing out-of-sync patches into sync. + //This seems to be authentic as well, with the RS wiki sometimes stating 20-minute patches can grow in as little as 7 minutes depending on timing of planting + //It also makes sense, as otherwise if you e.g. planted something at 10:34 that takes 5 minutes to grow, it would take 6 minutes in reality instead of 5. + //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){ + patch.nextGrowth = System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(patch.getStageGrowthMinutes().toLong()) + patch.update() + } + + if (patch.getCurrentState() == 0) + removeList.add(fp) + } + removeList.forEach { patchMap.remove(it) } + removeList.clear() + return patchMap.isNotEmpty() + } + + 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.isDead) { + 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()) + } + + val nowTime = System.currentTimeMillis() + var simulatedTime = patch.nextGrowth + + while (simulatedTime < nowTime && stagesToSimulate-- > 0 && !patch.isDead) { + val timeToIncrement = TimeUnit.MINUTES.toMillis(patch.getStageGrowthMinutes().toLong()) + patch.update() + simulatedTime += timeToIncrement + } + } + } + } + + fun getPatch(patch: FarmingPatch, addPatch: Boolean ): Patch { + return patchMap[patch] ?: (Patch(player,patch).also { if (addPatch) patchMap[patch] = it }) + } + + fun getPatches(): MutableCollection{ + return patchMap.values + } + + override fun save (root: JSONObject, entity: Entity) { + val patches = JSONArray() + for((key,patch) in patchMap){ + val p = JSONObject() + p.put("patch-ordinal",key.ordinal) + p.put("patch-plantable-ordinal",patch.plantable?.ordinal ?: -1) + p.put("patch-watered",patch.isWatered) + p.put("patch-diseased",patch.isDiseased) + p.put("patch-dead",patch.isDead) + p.put("patch-stage",patch.currentGrowthStage) + p.put("patch-state",patch.getCurrentState()) + p.put("patch-nextGrowth",patch.nextGrowth) + p.put("patch-harvestAmt",patch.harvestAmt) + p.put("patch-checkHealth",patch.isCheckHealth) + p.put("patch-compost",patch.compost.ordinal) + p.put("patch-paidprot",patch.protectionPaid) + p.put("patch-croplives", patch.cropLives) + patches.add(p) + } + root["patches"] = patches + } + + override fun parse (root: JSONObject, entity: Entity) { + val data = root["patches"] as JSONArray + for(d in data){ + val p = d as JSONObject + val patchOrdinal = p["patch-ordinal"].toString().toInt() + val patchPlantableOrdinal = p["patch-plantable-ordinal"].toString().toInt() + val patchWatered = p["patch-watered"] as Boolean + val patchDiseased = p["patch-diseased"] as Boolean + val patchDead = p["patch-dead"] as Boolean + val patchStage = p["patch-stage"].toString().toInt() + val nextGrowth = p["patch-nextGrowth"].toString().toLong() + val harvestAmt = (p["patch-harvestAmt"] ?: 0).toString().toInt() + val checkHealth = p["patch-checkHealth"] as Boolean + val savedState = p["patch-state"].toString().toInt() + val compostOrdinal = p["patch-compost"].toString().toInt() + val protectionPaid = p["patch-paidprot"] as Boolean + val cropLives = if(p["patch-croplives"] != null) p["patch-croplives"].toString().toInt() else 3 + val fPatch = FarmingPatch.values()[patchOrdinal] + val plantable = if(patchPlantableOrdinal != -1) Plantable.values()[patchPlantableOrdinal] else null + val patch = Patch((entity as? Player)!!,fPatch,plantable,patchStage,patchDiseased,patchDead,patchWatered,nextGrowth,harvestAmt,checkHealth) + + patch.cropLives = cropLives + patch.compost = CompostType.values()[compostOrdinal] + patch.protectionPaid = protectionPaid + patch.setCurrentState(savedState) + + if((savedState - (patch?.plantable?.value ?: 0)) > patch.currentGrowthStage){ + patch.setCurrentState(savedState) + } else { + patch.setCurrentState((patch.plantable?.value ?: 0) + patch.currentGrowthStage) + } + + if(patchMap[fPatch] == null) { + patchMap[fPatch] = patch + } + } + } +} diff --git a/Server/src/main/content/global/skill/farming/timers/SeedlingGrowth.kt b/Server/src/main/content/global/skill/farming/timers/SeedlingGrowth.kt new file mode 100644 index 000000000..843c6871c --- /dev/null +++ b/Server/src/main/content/global/skill/farming/timers/SeedlingGrowth.kt @@ -0,0 +1,74 @@ +package content.global.skill.farming.timers + +import core.game.node.entity.Entity +import core.game.node.item.Item +import core.game.system.timer.* +import core.game.node.entity.player.Player +import content.global.skill.farming.* +import java.util.concurrent.TimeUnit +import org.json.simple.* +import java.time.* + +class SeedlingGrowth : PersistTimer (1, "farming:seedling", isSoft = true) { + val seedlings = ArrayList() + lateinit var player: Player + + fun addSeedling(seedling: Int){ + seedlings.add( + Seedling( + seedling, + System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(5), + seedling + if(seedling > 5400) 8 else 6 + ) + ) + } + + override fun onRegister (entity: Entity) { + player = (entity as? Player)!! + } + + override fun run (entity: Entity) : Boolean { + val removeList = ArrayList() + for (seed in seedlings) { + if (System.currentTimeMillis() > seed.TTL) { + val inInventory = player.inventory.get(Item(seed.id)) + if (inInventory != null) { + player.inventory.replace(Item(seed.sapling), inInventory.slot) + removeList.add(seed) + } else { + val inBank = player.bank.get(Item(seed.id)) + if(inBank == null) removeList.add(seed) + else { + player.bank.remove(Item(inBank.id,1)) + player.bank.add(Item(seed.sapling)) + removeList.add(seed) + } + } + } + } + seedlings.removeAll(removeList) + return seedlings.isNotEmpty() + } + + override fun save(root: JSONObject, entity: Entity) { + val seedArray = JSONArray() + for(s in seedlings){ + val seed = JSONObject() + seed.put("id",s.id) + seed.put("ttl",s.TTL) + seed.put("sapling",s.sapling) + seedArray.add(seed) + } + root.put("seedlings",seedArray) + } + + override fun parse(root: JSONObject, entity: Entity) { + (root["seedlings"] as JSONArray).forEach { + val s = it as JSONObject + val id = s["id"].toString().toInt() + val ttl = s["ttl"].toString().toLong() + val sapling = s["sapling"].toString().toInt() + seedlings.add(Seedling(id,ttl,sapling)) + } + } +} diff --git a/Server/src/main/content/global/skill/firemaking/FireMakingPulse.java b/Server/src/main/content/global/skill/firemaking/FireMakingPulse.java index 6da6f4327..7d9f0b35d 100644 --- a/Server/src/main/content/global/skill/firemaking/FireMakingPulse.java +++ b/Server/src/main/content/global/skill/firemaking/FireMakingPulse.java @@ -1,5 +1,6 @@ package content.global.skill.firemaking; +import core.api.Container; import core.game.event.LitFireEvent; import core.game.node.entity.skill.SkillPulse; import core.game.node.entity.skill.Skills; @@ -12,8 +13,13 @@ import core.game.node.scenery.SceneryBuilder; import core.game.world.GameWorld; import core.game.world.map.RegionManager; import core.game.world.update.flag.context.Animation; -import core.game.world.update.flag.player.FaceLocationFlag; import core.tools.RandomFunction; +import org.rs09.consts.Items; + +import static core.api.ContentAPIKt.inInventory; +import static core.api.ContentAPIKt.replaceSlot; + +import static core.api.ContentAPIKt.removeItem; /** * Represents the pulse used to light a log. @@ -29,7 +35,8 @@ public final class FireMakingPulse extends SkillPulse { /** * Represents the tinderbox item. */ - private static final Item TINDERBOX = new Item(590); + private static final Item TINDERBOX = new Item(Items.TINDERBOX_590); + /** * Represents the log being burned. @@ -83,9 +90,15 @@ public final class FireMakingPulse extends SkillPulse { } if (player.getAttribute("remove-log", false)) { player.removeAttribute("remove-log"); - if (player.getInventory().remove(node)) { + if (inInventory(player, node.getId(), 1)) { + //replaceSlot(player, node.getSlot(), new Item(node.getId(), (node.getAmount() - 1)), node, Container.INVENTORY); + // Snowscape, remove any item instead of specific slot, and update the grounditem with our new location. + removeItem(player, new Item(node.getId(), 1), Container.INVENTORY); + this.groundItem = new GroundItem(node, player.getLocation(), player); GroundItemManager.create(groundItem); - } + } else { + return false; + } } return true; } @@ -96,24 +109,28 @@ public final class FireMakingPulse extends SkillPulse { @Override public boolean reward() { + /* Snowscape: this section allows making a new fire without an animation if you're fast enough, but since we auto-repeat we don't need that if (getLastFire() >= GameWorld.getTicks()) { createFire(); return true; } - if (ticks == 0) { + */ + if (ticks % 5 == 0) { player.animate(ANIMATION); } - if (++ticks % 3 != 0) { + if (++ticks % 5 != 0) { return false; } - if (ticks % 12 == 0) { - player.animate(ANIMATION); - } + if (!success()) { return false; } createFire(); - return true; + // Snowscape: return with the remove-log attribute, so the next pulse attempts to remove another log and start over + player.setAttribute("remove-log", true); + return false; + + } /** @@ -129,7 +146,7 @@ public final class FireMakingPulse extends SkillPulse { SceneryBuilder.add(object, fire.getLife(), getAsh(player, fire, object)); GroundItemManager.destroy(groundItem); player.moveStep(); - player.faceLocation(FaceLocationFlag.getFaceLocation(player, object)); + player.faceLocation(object.getFaceLocation(player.getLocation())); player.getSkills().addExperience(Skills.FIREMAKING,fire.getXp()); int playerRegion = player.getViewport().getRegion().getId(); @@ -140,7 +157,7 @@ public final class FireMakingPulse extends SkillPulse { @Override public void message(int type) { - String name = node.getId() == 3125 ? "bones" : "logs"; + String name = node.getId() == Items.JOGRE_BONES_3125 ? "bones" : "logs"; switch (type) { case 0: player.getPacketDispatch().sendMessage("You attempt to light the " + name + ".."); @@ -172,7 +189,7 @@ public final class FireMakingPulse extends SkillPulse { * @return {@code GroundItem} the itemm. */ public static GroundItem getAsh(final Player player, Log fire, final Scenery object) { - final GroundItem ash = new GroundItem(new Item(592), object.getLocation(), player); + final GroundItem ash = new GroundItem(new Item(Items.ASHES_592), object.getLocation(), player); ash.setDecayTime(fire.getLife() + 200); return ash; } @@ -191,4 +208,4 @@ public final class FireMakingPulse extends SkillPulse { } return false; } -} \ No newline at end of file +} diff --git a/Server/src/main/content/global/skill/fishing/Fish.java b/Server/src/main/content/global/skill/fishing/Fish.java deleted file mode 100644 index 06bc90d74..000000000 --- a/Server/src/main/content/global/skill/fishing/Fish.java +++ /dev/null @@ -1,126 +0,0 @@ -package content.global.skill.fishing; - -import core.game.node.item.Item; - -import java.util.HashMap; - -/** - * Represents a type of fish to catch. - * @author ceikry - */ -public enum Fish { - CRAYFISH(new Item(13435),1, 10, 0.15, 0.5), - SHRIMP(new Item(317), 1, 10, 0.191, 0.5), - SARDINE(new Item(327), 5, 20, 0.148, 0.374), - KARAMBWANJI(new Item(3150), 5, 5, 0.4, 0.98), - HERRING(new Item(345), 10, 30, 0.129, 0.504), - ANCHOVIE(new Item(321), 15, 40, 0.098, 0.5), - MACKEREL(new Item(353), 16, 20, 0.055, 0.258), - TROUT(new Item(335), 20, 50, 0.246, 0.468), - COD(new Item(341), 23, 45, 0.063, 0.219), - PIKE(new Item(349), 25, 60, 0.14, 0.379), - SLIMY_EEL(new Item(3379), 28, 65, 0.117, 0.216), - SALMON(new Item(331), 30, 70, 0.156, 0.378), - FROG_SPAWN(new Item(5004), 33, 75, 0.164, 0.379), - TUNA(new Item(359), 35, 80, 0.109, 0.205), - RAINBOW_FISH(new Item(10138), 38, 80, 0.113, 0.254), - CAVE_EEL(new Item(5001), 38, 80, 0.145, 0.316), - LOBSTER(new Item(377), 40, 90, 0.16, 0.375), - BASS(new Item(363), 46, 100, 0.078, 0.16), - SWORDFISH(new Item(371), 50, 100, 0.105, 0.191), - LAVA_EEL(new Item(2148), 53, 30, 0.227, 0.379), - MONKFISH(new Item(7944), 62, 120, 0.293, 0.356), - KARAMBWAN(new Item(3142), 65, 105, 0.414, 0.629), - SHARK(new Item(383), 76, 110, 0.121, 0.16), - SEA_TURTLE(new Item(395), 79, 38, 0.0, 0.0), - MANTA_RAY(new Item(389), 81, 46, 0.0, 0.0), - SEAWEED(new Item(401), 16, 1, 0.63, 0.219), - CASKET(new Item(405), 16, 10, 0.63, 0.219), - OYSTER(new Item(407), 16, 10, 0.63, 0.219); - - /** - * Constructs a new {@code Fish} {@code Object}. - * @param item the Item - * @param level the level. - * @param experience the experience. - */ - Fish(final Item item, final int level, final double experience, final double lowChance, final double highChance, final int... npcs) { - this.item = item; - this.level = level; - this.experience = experience; - this.npcs = npcs; - // The chances are given in the table based on when the fish can first be caught. - // Linearly extrapolate the success chance at level 1. - // y = (x - x0) * ((y1 - y0) / (x1 - x0)) + y0 - this.lowChance = (1 - (double)level)*((highChance - lowChance) / (99.0 - (double)level)) + lowChance; - this.highChance = highChance; - } - - public static HashMap fishMap = new HashMap<>(); - static{ - for(Fish fish : Fish.values()){ - fishMap.putIfAbsent(fish.item.getId(),fish); - } - } - private final Item item; - - /** - * Represents the required level to catch the {@link Fish}. - */ - private final int level; - - /** - * Represents the experience gained from this fish. - */ - private final double experience; - - private final double lowChance; - private final double highChance; - - /** - * The npc ids that give this fish. - */ - private final int[] npcs; - - /** - * Gets the fish. - * @param item the item. - * @return the fash. - */ - public static Fish forItem(Item item) { - return fishMap.get(item.getId()); - } - - /** - * @return the item. - */ - public Item getItem() { - return item; - } - - /** - * @return the level. - */ - public int getLevel() { - return level; - } - - /** - * @return the experience. - */ - public double getExperience() { - return experience; - } - - /** - * Gets the npcs. - * @return the npcs - */ - public int[] getNpcs() { - return npcs; - } - - public double getSuccessChance(int level) { - return ((double)level - 1.0)*((highChance - lowChance) / (99.0 - 1.0)) + lowChance; - } -} diff --git a/Server/src/main/content/global/skill/fishing/Fish.kt b/Server/src/main/content/global/skill/fishing/Fish.kt new file mode 100644 index 000000000..8ccc67f24 --- /dev/null +++ b/Server/src/main/content/global/skill/fishing/Fish.kt @@ -0,0 +1,67 @@ +package content.global.skill.fishing + +import core.api.asItem +import core.game.node.item.Item +import org.rs09.consts.Items + +enum class Fish(val id: Int, val level: Int, val experience: Double, val lowChance: Double, val highChance: Double) { + CRAYFISH(Items.RAW_CRAYFISH_13435, 1, 10.0, 0.15, 0.5), + SHRIMP(Items.RAW_SHRIMPS_317, 1, 10.0, 0.191, 0.5), + SARDINE(Items.RAW_SARDINE_327, 5, 20.0, 0.148, 0.374), + KARAMBWANJI(Items.RAW_KARAMBWANJI_3150, 5, 5.0, 0.4, 0.98), + HERRING(Items.RAW_HERRING_345, 10, 30.0, 0.129, 0.504), + ANCHOVIE(Items.RAW_ANCHOVIES_321, 15, 40.0, 0.098, 0.5), + MACKEREL(Items.RAW_MACKEREL_353, 16, 20.0, 0.055, 0.258), + TROUT(Items.RAW_TROUT_335, 20, 50.0, 0.246, 0.468), + COD(Items.RAW_COD_341, 23, 45.0, 0.063, 0.219), + PIKE(Items.RAW_PIKE_349, 25, 60.0, 0.14, 0.379), + SLIMY_EEL(Items.SLIMY_EEL_3379, 28, 65.0, 0.117, 0.216), + SALMON(Items.RAW_SALMON_331, 30, 70.0, 0.156, 0.378), + FROG_SPAWN(Items.FROG_SPAWN_5004, 33, 75.0, 0.164, 0.379), + TUNA(Items.RAW_TUNA_359, 35, 80.0, 0.109, 0.205), + RAINBOW_FISH(Items.RAW_RAINBOW_FISH_10138, 38, 80.0, 0.113, 0.254), + CAVE_EEL(Items.RAW_CAVE_EEL_5001, 38, 80.0, 0.145, 0.316), + 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, 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), + SEA_TURTLE(Items.RAW_SEA_TURTLE_395, 79, 38.0, 0.0, 0.0), + MANTA_RAY(Items.RAW_MANTA_RAY_389, 81, 46.0, 0.0, 0.0), + SEAWEED(Items.SEAWEED_401, 16, 1.0, 0.63, 0.219), + CASKET(Items.CASKET_405, 16, 10.0, 0.63, 0.219), + OYSTER(Items.OYSTER_407, 16, 10.0, 0.63, 0.219); + + companion object { + val fishMap: HashMap = HashMap() + val bigFishMap: HashMap = HashMap() + init { + for(fish in values()) { + fishMap[fish.id] = fish + } + bigFishMap[Fish.BASS] = Items.BIG_BASS_7989 + bigFishMap[Fish.SWORDFISH] = Items.BIG_SWORDFISH_7991 + bigFishMap[Fish.SHARK] = Items.BIG_SHARK_7993 + } + + @JvmStatic + fun forItem(item: Item) : Fish? { + return fishMap[item.id] + } + + @JvmStatic + fun getBigFish(fish: Fish) : Int? { + return bigFishMap[fish] + } + } + + fun getSuccessChance(level: Int): Double { + return (level.toDouble() - 1.0) * ((highChance - lowChance) / (99.0 - 1.0)) + lowChance + } + + fun getItem() : Item { + return this.id.asItem() + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/fishing/FishSpots.java b/Server/src/main/content/global/skill/fishing/FishSpots.java deleted file mode 100644 index 65053463a..000000000 --- a/Server/src/main/content/global/skill/fishing/FishSpots.java +++ /dev/null @@ -1,56 +0,0 @@ -package content.global.skill.fishing; - -import core.game.world.map.Location; - -import java.util.HashMap; - -/** - * Fishing spot locations - * @author Ceikry - */ -public enum FishSpots { - CATHERBY(Location.create(2844, 3429, 0), Location.create(2839, 3431, 0), new Location(2836, 3431, 0), new Location(2837, 3431, 0), new Location(2838, 3431, 0), new Location(2839, 3431, 0), new Location(2840, 3431, 0), new Location(2844, 3429, 0), new Location(2845, 3429, 0), new Location(2846, 3429, 0), new Location(2853, 3423, 0), new Location(2854, 3423, 0), new Location(2855, 3423, 0), new Location(2859, 3426, 0), new Location(2860, 3426, 0)), - ENTRANA(new Location(2842, 3359, 0), new Location(2843, 3359, 0), new Location(2844, 3359, 0), new Location(2847, 3361, 0), new Location(2848, 3361, 0), new Location(2849, 3361, 0), new Location(2850, 3361, 0)), - ENTRANA_PLATFORM(new Location(2879, 3334, 0), new Location(2879, 3335, 0), new Location(2879, 3338, 0), new Location(2879, 3339, 0), new Location(2877, 3342, 0), new Location(2876, 3342, 0), new Location(2875, 3342, 0), new Location(2875, 3331, 0), new Location(2876, 3331, 0), new Location(2877, 3331, 0)), - SEERS(new Location(2714, 3533, 0), new Location(2714, 3532, 0), new Location(2726, 3524, 0), new Location(2727, 3524, 0), new Location(2728, 3524, 0)), - DRAYNOR(new Location(3085, 3230, 0), new Location(3086, 3227, 0)), - BARBARIAN(new Location(3110, 3432, 0), new Location(3110, 3433, 0), new Location(3110, 3434, 0), Location.create(3104, 3425, 0), Location.create(3104, 3424, 0)), - LUMBRIDGE(new Location(3239, 3243, 0), new Location(3239, 3241, 0), new Location(3239, 3242, 0), Location.create(3238, 3251, 0), Location.create(3238, 3252, 0), Location.create(3238, 3253, 0)), - LUMBRIDGE_SWAMP(Location.create(3242, 3143, 0), Location.create(3242, 3148, 0), Location.create(3241, 3148, 0), Location.create(3240, 3147, 0), Location.create(3240, 3146, 0), Location.create(3244, 3150, 0), Location.create(3245, 3152, 0), Location.create(3246, 3155, 0), Location.create(3246, 3156, 0), new Location(3239, 3241, 0)), - LUMBRIDGE_MARSH(Location.create(3169, 3266, 0), Location.create(3168, 3266, 0), Location.create(3174, 3274, 0), Location.create(3173, 3274, 0)), - ALKHARID(new Location(3267, 3148, 0), new Location(3268, 3147, 0), new Location(3276, 3140, 0), new Location(3275, 3140, 0)), - FISHING_GUILD(new Location(2612, 3411, 0), new Location(2602, 3423, 0), new Location(2602, 3411, 0), new Location(2602, 3412, 0), new Location(2602, 3414, 0), new Location(2602, 3415, 0), new Location(2603, 3417, 0), new Location(2604, 3417, 0), new Location(2605, 3416, 0), new Location(2606, 3416, 0), new Location(2607, 3416, 0), new Location(2608, 3416, 0), new Location(2609, 3416, 0), new Location(2610, 3416, 0), new Location(2611, 3416, 0), new Location(2612, 3415, 0), new Location(2612, 3414, 0), new Location(2607, 3410, 0), new Location(2606, 3410, 0), new Location(2605, 3410, 0), new Location(2599, 3419, 0), new Location(2600, 3419, 0), new Location(2602, 3419, 0), new Location(2603, 3419, 0), new Location(2604, 3419, 0), new Location(2605, 3420, 0), new Location(2605, 3421, 0), new Location(2604, 3422, 0), new Location(2603, 3422, 0), new Location(2602, 3422, 0), new Location(2601, 3422, 0), new Location(2605, 3424, 0), new Location(2605, 3425, 0), new Location(2602, 3426, 0), new Location(2603, 3426, 0), new Location(2604, 3426, 0)), - GNOME(new Location(2388, 3424, 0), new Location(2390, 3422, 0), new Location(2391, 3421, 0), new Location(2382, 3413, 0), new Location(2382, 3414, 0), new Location(2382, 3415, 0)), CASTLEWAR(new Location(2465, 3156, 0), new Location(2464, 3155, 0), new Location(2468, 3157, 0), new Location(2469, 3157, 0), new Location(2471, 3158, 0), new Location(2472, 3156, 0), new Location(2462, 3145, 0), new Location(2461, 3150, 0), new Location(2461, 3151, 0)), SHILO(new Location(2460, 3144, 0), new Location(2461, 3150, 0), new Location(2461, 3151, 0), new Location(2853, 2977, 0), new Location(2854, 2977, 0), new Location(2855, 2977, 0), new Location(2856, 2977, 0), new Location(2858, 2976, 0), new Location(2859, 2976, 0), new Location(2860, 2976, 0), new Location(2863, 2975, 0), new Location(2863, 2975, 0), new Location(2864, 2975, 0), new Location(2865, 2972, 0), new Location(2860, 2972, 0), new Location(2855, 2974, 0), new Location(2852, 2973, 0)), KARAMJA(new Location(2923, 3178, 0), new Location(2923, 3179, 0), new Location(2923, 3180, 0), new Location(2924, 3181, 0), new Location(2925, 3181, 0), new Location(2926, 3180, 0), new Location(2926, 3179, 0), new Location(2926, 3178, 0), new Location(2926, 3177, 0), new Location(2926, 3176, 0)), DONATOR_ISLAND(Location.create(159, 41, 0), Location.create(160, 41, 0), Location.create(161, 41, 0), Location.create(162, 41, 0), Location.create(162, 30, 0)), - PISCATORIS(Location.create(2341, 3702, 0), Location.create(2342, 3702, 0), Location.create(2343, 3702, 0), Location.create(2344, 3702, 0), Location.create(2345, 3702, 0), Location.create(2346, 3702, 0), Location.create(2347, 3702, 0), Location.create(2349, 3702, 0), Location.create(2352, 3703, 0)), - TUTORIAL_ISLAND(Location.create(3101,3092, 0)), - WILDERNESS_ARENA(Location.create(3187, 3927, 0),Location.create(3185, 3926, 0),Location.create(3183, 3926, 0), Location.create(3181, 3926, 0)); - - FishSpots(final Location... locations) { - this.locations = locations; - } - - private final Location[] locations; - - public Location[] getLocations() { - return locations; - } - - public static HashMap locationMap = new HashMap<>(); - - static{ - FishSpots[] spots = values(); - int spotsLength = spots.length; - for(int x = 0; x < spotsLength; x++){ - Location[] locations = spots[x].locations; - int locationsLength = locations.length; - for(int y = 0; y < locationsLength; y++){ - locationMap.putIfAbsent(locations[y],spots[x]); - } - } - } - - //statically gets the fishing spot (loops suck btw) - public static FishSpots forLocation(Location loc) { - return locationMap.get(loc); - } -} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/fishing/FishSpots.kt b/Server/src/main/content/global/skill/fishing/FishSpots.kt new file mode 100644 index 000000000..937c555c6 --- /dev/null +++ b/Server/src/main/content/global/skill/fishing/FishSpots.kt @@ -0,0 +1,224 @@ +package content.global.skill.fishing + +import core.game.world.map.Location + +enum class FishSpots(vararg val locations: Location) { + CATHERBY( + Location(2844, 3429, 0), + Location(2839, 3431, 0), + Location(2836, 3431, 0), + Location(2837, 3431, 0), + Location(2838, 3431, 0), + Location(2839, 3431, 0), + Location(2840, 3431, 0), + Location(2844, 3429, 0), + Location(2845, 3429, 0), + Location(2846, 3429, 0), + Location(2853, 3423, 0), + Location(2854, 3423, 0), + Location(2855, 3423, 0), + Location(2859, 3426, 0), + Location(2860, 3426, 0) + ), + ENTRANA( + Location(2842, 3359, 0), + Location(2843, 3359, 0), + Location(2844, 3359, 0), + Location(2847, 3361, 0), + Location(2848, 3361, 0), + Location(2849, 3361, 0), + Location(2850, 3361, 0) + ), + ENTRANA_PLATFORM( + Location(2879, 3334, 0), + Location(2879, 3335, 0), + Location(2879, 3338, 0), + Location(2879, 3339, 0), + Location(2877, 3342, 0), + Location(2876, 3342, 0), + Location(2875, 3342, 0), + Location(2875, 3331, 0), + Location(2876, 3331, 0), + Location(2877, 3331, 0) + ), + SEERS( + Location(2714, 3533, 0), + Location(2714, 3532, 0), + Location(2726, 3524, 0), + Location(2727, 3524, 0), + Location(2728, 3524, 0) + ), + DRAYNOR( + Location(3085, 3230, 0), + Location(3086, 3227, 0) + ), + BARBARIAN( + Location(3110, 3432, 0), + Location(3110, 3433, 0), + Location(3110, 3434, 0), + Location(3104, 3425, 0), + Location(3104, 3424, 0) + ), + LUMBRIDGE( + Location(3239, 3243, 0), + Location(3239, 3241, 0), + Location(3239, 3242, 0), + Location(3238, 3251, 0), + Location(3238, 3252, 0), + Location(3238, 3253, 0) + ), + LUMBRIDGE_SWAMP( + Location(3242, 3143, 0), + Location(3242, 3148, 0), + Location(3241, 3148, 0), + Location(3240, 3147, 0), + Location(3240, 3146, 0), + Location(3244, 3150, 0), + Location(3245, 3152, 0), + Location(3246, 3155, 0), + Location(3246, 3156, 0), + Location(3239, 3241, 0) + ), + LUMBRIDGE_MARSH( + Location(3169, 3266, 0), + Location(3168, 3266, 0), + Location(3174, 3274, 0), + Location(3173, 3274, 0) + ), + ALKHARID( + Location(3267, 3148, 0), + Location(3268, 3147, 0), + Location(3276, 3140, 0), + Location(3275, 3140, 0) + ), + FISHING_GUILD( + Location(2612, 3411, 0), + Location(2602, 3423, 0), + Location(2602, 3411, 0), + Location(2602, 3412, 0), + Location(2602, 3414, 0), + Location(2602, 3415, 0), + Location(2603, 3417, 0), + Location(2604, 3417, 0), + Location(2605, 3416, 0), + Location(2606, 3416, 0), + Location(2607, 3416, 0), + Location(2608, 3416, 0), + Location(2609, 3416, 0), + Location(2610, 3416, 0), + Location(2611, 3416, 0), + Location(2612, 3415, 0), + Location(2612, 3414, 0), + Location(2607, 3410, 0), + Location(2606, 3410, 0), + Location(2605, 3410, 0), + Location(2599, 3419, 0), + Location(2600, 3419, 0), + Location(2602, 3419, 0), + Location(2603, 3419, 0), + Location(2604, 3419, 0), + Location(2605, 3420, 0), + Location(2605, 3421, 0), + Location(2604, 3422, 0), + Location(2603, 3422, 0), + Location(2602, 3422, 0), + Location(2601, 3422, 0), + Location(2605, 3424, 0), + Location(2605, 3425, 0), + Location(2602, 3426, 0), + Location(2603, 3426, 0), + Location(2604, 3426, 0) + ), + GNOME( + Location(2388, 3424, 0), + Location(2390, 3422, 0), + Location(2391, 3421, 0), + Location(2382, 3413, 0), + Location(2382, 3414, 0), + Location(2382, 3415, 0) + ), + CASTLEWAR( + Location(2465, 3156, 0), + Location(2464, 3155, 0), + Location(2468, 3157, 0), + Location(2469, 3157, 0), + Location(2471, 3158, 0), + Location(2472, 3156, 0), + Location(2462, 3145, 0), + Location(2461, 3150, 0), + Location(2461, 3151, 0) + ), + SHILO( + Location(2460, 3144, 0), + Location(2461, 3150, 0), + Location(2461, 3151, 0), + Location(2853, 2977, 0), + Location(2854, 2977, 0), + Location(2855, 2977, 0), + Location(2856, 2977, 0), + Location(2858, 2976, 0), + Location(2859, 2976, 0), + Location(2860, 2976, 0), + Location(2863, 2975, 0), + Location(2863, 2975, 0), + Location(2864, 2975, 0), + Location(2865, 2972, 0), + Location(2860, 2972, 0), + Location(2855, 2974, 0), + Location(2852, 2973, 0) + ), + KARAMJA( + Location(2923, 3178, 0), + Location(2923, 3179, 0), + Location(2923, 3180, 0), + Location(2924, 3181, 0), + Location(2925, 3181, 0), + Location(2926, 3180, 0), + Location(2926, 3179, 0), + Location(2926, 3178, 0), + Location(2926, 3177, 0), + Location(2926, 3176, 0) + ), + PISCATORIS( + Location(2341, 3702, 0), + Location(2342, 3702, 0), + Location(2343, 3702, 0), + Location(2344, 3702, 0), + Location(2345, 3702, 0), + Location(2346, 3702, 0), + Location(2347, 3702, 0), + Location(2349, 3702, 0), + Location(2352, 3703, 0) + ), + TUTORIAL_ISLAND( + Location(3101, 3092, 0) + ), + WILDERNESS_AREA( + Location(3187, 3927, 0), + Location(3185, 3926, 0), + Location(3183, 3926, 0), + Location(3181, 3926, 0) + ); + + companion object { + private val locs: ArrayList = ArrayList() + private val locMap: HashMap = HashMap() + init { + for(value in values()) { + for(loc in value.locations) { + locMap[loc] = value + locs.add(loc) + } + } + } + + @JvmStatic + fun forLocation(loc: Location): FishSpots? { + return if (locMap[loc] == null) { + TUTORIAL_ISLAND + } else { + locMap[loc] + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/fishing/FishingNPC.java b/Server/src/main/content/global/skill/fishing/FishingNPC.java deleted file mode 100644 index e622667d0..000000000 --- a/Server/src/main/content/global/skill/fishing/FishingNPC.java +++ /dev/null @@ -1,103 +0,0 @@ -package content.global.skill.fishing; - -import core.game.node.entity.npc.AbstractNPC; -import core.game.world.GameWorld; -import core.game.world.map.Location; -import core.game.world.map.RegionManager; -import core.plugin.Initializable; -import core.tools.RandomFunction; - -/** - * Represents the fishing npc. - * @author Vexia - * - */ -@Initializable -public final class FishingNPC extends AbstractNPC { - - /** - * Represents the fishing spot. - */ - private FishSpots spot; - - /** - * Represents the delay of switching. - */ - private int switchDelay; - - /** - * Constructs a new {@code FishingNPC} {@code Object}. - */ - public FishingNPC() { - super(0, null); - spot = null; - } - - /** - * Constructs a new {@code FishingNPC} {@code Object}. - * @param id the id. - * @param location the location. - */ - public FishingNPC(int id, Location location) { - super(id, location, false); - switchDelay = getRandomDelay(); - } - - @Override - public AbstractNPC construct(int id, Location location, Object... objects) { - return new FishingNPC(id, location); - } - - @Override - public void handleTickActions() { - if (spot == null) { - return; - } - if (switchDelay < GameWorld.getTicks()) { - moveSpot(); - } - } - - @Override - public void init() { - super.init(); - spot = FishSpots.forLocation(getLocation()); - } - - /** - * Method used to move a spot. - */ - public void moveSpot() { - if (spot == null) { - if (isInvisible()) { - setInvisible(false); - } else { - setInvisible(true); - } - switchDelay = GameWorld.getTicks() + RandomFunction.random(200, 390); - return; - } - if (spot == FishSpots.TUTORIAL_ISLAND) { - return; - } - Location rand = spot.getLocations()[RandomFunction.random(spot.getLocations().length)]; - if (RegionManager.getLocalNpcs(rand, 0).size() == 0) { - getProperties().setTeleportLocation(rand); - } - switchDelay = GameWorld.getTicks() + getRandomDelay(); - } - - /** - * Gets the random delay. - * @return the delay. - */ - private int getRandomDelay() { - return RandomFunction.random(200, 390); - } - - @Override - public int[] getIds() { - return new int[] { 316, 319, 320, 323, 325, 326, 327, 330, 332, 404, 952, 1174, 1175, 1331, 2067, 2068, 2724, 4908, 5748, 5749, 7045, 313, 322, 334, 406, 1191, 1333, 1405, 1406, 3574, 3575, 3848, 3849, 5471, 7044, 233, 234, 235, 236, 309, 310, 311, 314, 315, 317, 318, 328, 329, 331, 403, 800, 927, 1189, 1190, 1236, 1237, 1238, 3019, 7636, 312, 321, 324, 333, 405, 1332, 1399, 3804, 5470, 7046 }; - } - -} diff --git a/Server/src/main/content/global/skill/fishing/FishingNPC.kt b/Server/src/main/content/global/skill/fishing/FishingNPC.kt new file mode 100644 index 000000000..2af0ba8e6 --- /dev/null +++ b/Server/src/main/content/global/skill/fishing/FishingNPC.kt @@ -0,0 +1,55 @@ +package content.global.skill.fishing + +import core.api.* +import core.game.node.entity.npc.NPC +import core.game.node.entity.npc.NPCBehavior +import core.tools.RandomFunction + +private val fishingSpots = FishingSpot.getAllIds() +class FishingNPC : NPCBehavior(*fishingSpots) { + + override fun onCreation(self: NPC) { + setAttribute(self, "fishing:spot", FishSpots.forLocation(self.location)) + setAttribute(self, "fishing:switchdelay", 0) + } + + override fun tick(self: NPC): Boolean { + if(getSpot(self) == null) { + return false + } + if(getAttribute(self, "fishing:switchdelay", 0) < getWorldTicks()) { + moveSpot(self) + return false + } + return false + } + + private fun moveSpot(self: NPC) { + when (val spot = getSpot(self)) { + null -> { + self.isInvisible = !self.isInvisible + setAttribute(self, "fishing:switchdelay", getWorldTicks() + getRandomDelay()) + return + } + FishSpots.TUTORIAL_ISLAND -> { + return + } + else -> { + val randLoc = spot.locations[RandomFunction.random(spot.locations.size)] + if(findLocalNPCs(randLoc, 0).isEmpty()) { + teleport(self, randLoc) + } + setAttribute(self, "fishing:switchdelay", getWorldTicks() + getRandomDelay()) + } + } + } + + private fun getRandomDelay(): Int { + return RandomFunction.random(200, 390) + } + + private fun getSpot(npc: NPC): FishSpots? { + return getAttribute(npc, "fishing:spot", null) + } + +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/fishing/FishingOption.java b/Server/src/main/content/global/skill/fishing/FishingOption.java deleted file mode 100644 index b1652e64b..000000000 --- a/Server/src/main/content/global/skill/fishing/FishingOption.java +++ /dev/null @@ -1,213 +0,0 @@ -package content.global.skill.fishing; - -import core.game.container.Container; -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.RandomFunction; -import org.rs09.consts.Items; - -import java.util.Arrays; -import java.util.HashMap; - -/** - * Represents a fishing option. - * @author Emperor - */ -public enum FishingOption { - - CRAYFISH_CAGE(new Item(13431), 1, Animation.create(619), null, "cage", Fish.CRAYFISH), - SMALL_NET(new Item(303), 1, Animation.create(621), null, "net", Fish.SHRIMP, Fish.ANCHOVIE), - BAIT(new Item(307), 5, Animation.create(622), new Item[]{new Item(313)}, "bait", Fish.SARDINE, Fish.HERRING), - LURE(new Item(309), 20, new Animation(622), new Item[]{new Item(Items.FEATHER_314), new Item(Items.STRIPY_FEATHER_10087)}, "lure", Fish.TROUT, Fish.SALMON, Fish.RAINBOW_FISH), - L_BAIT(new Item(307), 25, Animation.create(622), new Item[]{new Item(313)}, "bait", Fish.PIKE), - CAGE(new Item(301), 40, Animation.create(619), null, "cage", Fish.LOBSTER), - HARPOON(new Item(311), 35, Animation.create(618), null, "harpoon", Fish.TUNA, Fish.SWORDFISH), - BARB_HARPOON(new Item(10129), 35, Animation.create(618), null, "harpoon", Fish.TUNA, Fish.SWORDFISH), - BIG_NET(new Item(305), 16, Animation.create(620), null, "net", Fish.MACKEREL, Fish.COD, Fish.BASS, Fish.SEAWEED), - N_HARPOON(new Item(311), 76, Animation.create(618), null, "harpoon", Fish.SHARK), - H_NET(new Item(303), 62, Animation.create(621), null, "net", Fish.MONKFISH), - MORTMYRE_ROD(new Item(307), 5, Animation.create(622), new Item[]{new Item(313)},"bait", Fish.SLIMY_EEL), - LUMBDSWAMP_ROD(new Item(307), 5, Animation.create(622), new Item[]{new Item(313)}, "bait", Fish.SLIMY_EEL, Fish.FROG_SPAWN), - KBWANJI_NET(new Item(Items.SMALL_FISHING_NET_303), 5, Animation.create(621), null, "net", Fish.KARAMBWANJI), - KARAMBWAN_VES(new Item(Items.KARAMBWAN_VESSEL_3157), 65, Animation.create(1193), new Item[]{new Item(Items.RAW_KARAMBWANJI_3150)}, "fish", Fish.KARAMBWAN), - OILY_FISHING_ROD(new Item(Items.OILY_FISHING_ROD_1585), 53, Animation.create(622), new Item[]{new Item(313)}, "bait", Fish.LAVA_EEL); - - public static final HashMap nameMap = new HashMap<>(); - static{ - for(FishingOption option : FishingOption.values()){ - nameMap.putIfAbsent(option.name,option); - } - } - - public static FishingOption forName(String name){ - return nameMap.get(name); - } - /** - * The tool required. - */ - private final Item tool; - - /** - * The fishing level required. - */ - private final int level; - - /** - * The fishing animation. - */ - private final Animation animation; - - /** - * The bait. - */ - private final Item[] bait; - - /** - * The option name. - */ - private final String name; - - /** - * The fish to catch. - */ - private final Fish[] fish; - - /** - * Constructs a new {@code FishingOption} {@code Object}. - * @param tool The tool needed. - * @param level The fishing level required. - * @param animation The animation. - * @param fish The fish to catch. - */ - FishingOption(Item tool, int level, Animation animation, Item[] bait, String name, Fish... fish) { - this.tool = tool; - this.level = level; - this.animation = animation; - this.bait = bait; - this.name = name; - Arrays.sort(fish, (x, y) -> y.getLevel() - x.getLevel()); - this.fish = fish; - } - - /** - * Method used to get a random {@link Fish}. - * @return the {@link Fish}. - */ - public Fish rollFish(final Player player) { - if (this == BIG_NET) { - switch (RandomFunction.randomize(100)) { - case 0: - return Fish.OYSTER; - case 50: - return Fish.CASKET; - case 90: - return Fish.SEAWEED; - } - } - int visibleLevel = player.getSkills().getLevel(Skills.FISHING); - int invisibleLevel = visibleLevel + player.getFamiliarManager().getBoost(Skills.FISHING); - for(Fish f : fish) { - if(f.getLevel() > player.getSkills().getLevel(Skills.FISHING)) { - continue; - } - if(this == LURE && (player.getInventory().contains(Items.STRIPY_FEATHER_10087, 1) != (f == Fish.RAINBOW_FISH))) { - continue; - } - double chance = f.getSuccessChance(invisibleLevel); - //System.out.printf("rollFish: %s %s %s %s %s\n", player.getName(), f.getItem().getName(), f.getLowChance(), f.getHighChance(), chance); - if(RandomFunction.random(0.0, 1.0) < chance) { - return f; - } - } - return null; - } - - /** - * Gets the start message. - * @return The start message. - */ - public String getStartMessage() { - if (name.equals("net")) { - return "You cast out your net..."; - } - return "You attempt to catch a fish."; - } - - /** - * Gets the tool. - * @return The tool. - */ - public Item getTool() { - return tool; - } - - /** - * Gets the level. - * @return The level. - */ - public int getLevel() { - return level; - } - - /** - * Gets the animation. - * @return The animation. - */ - public Animation getAnimation() { - return animation; - } - - public String getBaitName() { - if(bait != null && bait.length > 0) { - return bait[0].getName(); - } else { - return "null"; - } - - } - - public boolean hasBait(Container inventory) { - if(bait == null) { - return true; - } else { - boolean any_bait = false; - for(Item b : bait) { - any_bait = any_bait || inventory.containsItem(b); - } - return any_bait; - } - } - - public boolean removeBait(Container inventory) { - if(bait == null) { - return true; - } else { - // Remove more specific bait (later in the list) first. - for(int i = bait.length; i > 0; i--) { - if(inventory.remove(bait[i-1])) { - return true; - } - } - return false; - } - } - - /** - * Gets the name. - * @return The name. - */ - public String getName() { - return name; - } - - /** - * Gets the fish. - * @return The fish. - */ - public Fish[] getFish() { - return fish; - } - -} diff --git a/Server/src/main/content/global/skill/fishing/FishingOption.kt b/Server/src/main/content/global/skill/fishing/FishingOption.kt new file mode 100644 index 000000000..5b304893c --- /dev/null +++ b/Server/src/main/content/global/skill/fishing/FishingOption.kt @@ -0,0 +1,104 @@ +package content.global.skill.fishing + +import core.api.* +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.game.world.update.flag.context.Animation +import core.tools.RandomFunction +import org.rs09.consts.Items + +enum class FishingOption(val tool: Int, val level: Int, val animation: Animation, val bait: IntArray?, val option: String, vararg val fish: Fish) { + CRAYFISH_CAGE(Items.CRAYFISH_CAGE_13431, 1, Animation(10009), null, "cage", Fish.CRAYFISH), + SMALL_NET(Items.SMALL_FISHING_NET_303, 1, Animation(621), null, "net", Fish.SHRIMP, Fish.ANCHOVIE), + BAIT(Items.FISHING_ROD_307, 5, Animation(622), intArrayOf(Items.FISHING_BAIT_313), "bait", Fish.SARDINE, Fish.HERRING), + LURE(Items.FLY_FISHING_ROD_309, 20, Animation(622), intArrayOf(Items.FEATHER_314, Items.STRIPY_FEATHER_10087, Items.RED_FEATHER_10088, Items.BLUE_FEATHER_10089, Items.YELLOW_FEATHER_10090, Items.ORANGE_FEATHER_10091), "lure", Fish.TROUT, Fish.SALMON, Fish.RAINBOW_FISH), + PIKE_BAIT(Items.FISHING_ROD_307, 25, Animation(622), intArrayOf(Items.FISHING_BAIT_313), "bait", Fish.PIKE), + LOBSTER_CAGE(Items.LOBSTER_POT_301, 40, Animation(619), null, "cage", Fish.LOBSTER), + HARPOON(Items.HARPOON_311, 35, Animation(618), null, "harpoon", Fish.TUNA, Fish.SWORDFISH), + BARB_HARPOON(Items.BARB_TAIL_HARPOON_10129, 35, Animation(618), null, "harpoon", Fish.TUNA, Fish.SWORDFISH), + BIG_NET(Items.BIG_FISHING_NET_305, 16, Animation(620), null, "net", Fish.MACKEREL, Fish.COD, Fish.BASS, Fish.SEAWEED), + 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.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); + + companion object { + private val nameMap: HashMap = HashMap() + + init { + for(value in values()) { + nameMap[value.option] = value + } + } + + fun forName(opName: String): FishingOption? { + return nameMap[opName] + } + } + + fun rollFish(player: Player): Fish? { + if(this == BIG_NET) { + when(RandomFunction.randomize(100)) { + 0 -> return Fish.OYSTER + 50 -> return Fish.CASKET + 90 -> return Fish.SEAWEED + } + } + val vlvl = getDynLevel(player, Skills.FISHING) + val ilvl = vlvl + player.familiarManager.getBoost(Skills.FISHING) + for(f in fish) { + if(f.level > vlvl) { + continue + } + if(this == LURE && inInventory(player, Items.STRIPY_FEATHER_10087) != (f == Fish.RAINBOW_FISH)) { + continue + } + val chance = f.getSuccessChance(ilvl) + if(RandomFunction.random(0.0, 1.0) < chance) { + return f + } + } + return null + } + + fun getBaitName(): String { + if(bait != null && bait.isNotEmpty()) { + return getItemName(bait[0]) + } + return "none" + } + + fun hasBait(player: Player): Boolean { + return if(bait == null) true else { + var anyBait = false + for(b in bait) { + anyBait = anyBait || inInventory(player, b) + } + anyBait + } + } + + fun removeBait(player: Player): Boolean { + return if (bait == null) { + true + } else { + for (i in bait.size downTo 1) { + if (removeItem(player, bait[i - 1], Container.INVENTORY)) { + return true + } + } + false + } + } + + fun getStartMessage(): String { + return if(option == "net") + "You cast out your net..." + else + "You attempt to catch a fish." + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/fishing/FishingSpot.java b/Server/src/main/content/global/skill/fishing/FishingSpot.java deleted file mode 100644 index 03fb8e437..000000000 --- a/Server/src/main/content/global/skill/fishing/FishingSpot.java +++ /dev/null @@ -1,82 +0,0 @@ -package content.global.skill.fishing; - -import java.util.HashMap; - -/** - * Represents a fishing spot. - * @author ceikry - */ -public enum FishingSpot { - - NET_BAIT(new int[] { 952, 316, 319, 320, 323, 325, 326, 327, 330, 332, 404, 1331, 2724, 4908, 7045 }, FishingOption.SMALL_NET, FishingOption.BAIT), - LURE_BAIT(new int[] { 309, 310, 311, 314, 315, 317, 318, 328, 329, 331, 403, 927, 1189, 1190, 3019 }, FishingOption.LURE, FishingOption.L_BAIT), - CAGE_HARPOON(new int[] { 312, 321, 324, 333, 405, 1332, 1399, 3804, 5470, 7046}, FishingOption.CAGE, FishingOption.HARPOON), - NET_HARPOON(new int[] { 313, 322, 334, 406, 1191, 1333, 1405, 1406, 3574, 3575, 5471, 7044 }, FishingOption.BIG_NET, FishingOption.N_HARPOON), - HARPOON_NET(new int[] { 3848, 3849 }, FishingOption.HARPOON, FishingOption.H_NET, FishingOption.BARB_HARPOON), - SPOT_MORTMYRE(new int[] {5748, 5749}, FishingOption.MORTMYRE_ROD), - SPOT_LUMDSAWMP(new int[] {2067, 2068}, FishingOption.LUMBDSWAMP_ROD), - SPOT_KBWANJI(new int[] {1174}, FishingOption.KBWANJI_NET), - SPOT_KARAMBWAN(new int[] {1177}, FishingOption.KARAMBWAN_VES), - BAIT_EELS(new int[] {800}, FishingOption.OILY_FISHING_ROD); - - public static HashMap spotMap = new HashMap<>(); - - static{ - FishingSpot[] spots = values(); - for (FishingSpot spot : spots) { - int[] spotIds = spot.ids; - for (int spotId : spotIds) { - spotMap.putIfAbsent(spotId, spot); - } - } - } - - public static FishingSpot forId(int npcId) { - return spotMap.get(npcId); - } - - - /** - * The NPC ids. - */ - private final int[] ids; - - /** - * The fishing options. - */ - private FishingOption options[]; - - /** - * Constructs a new {@code FishingSpot} {@code Object}. - * @param ids The NPC ids. - */ - FishingSpot(int[] ids, FishingOption... options) { - this.ids = ids; - this.options = options; - } - - public FishingOption getOptionByName(String name){ - for(FishingOption o : this.options){ - if(o.getName().equals(name)){return o;} - } - System.out.println("Unhandled fishing spot option, spot id: " + this.ids[0] + " desired fishing option: " + name); - return FishingOption.SMALL_NET; //safe, default return. - } - - /** - * Gets the ids. - * @return The ids. - */ - public int[] getIds() { - return ids; - } - - /** - * Gets the options. - * @return The options. - */ - public FishingOption[] getOptions() { - return options; - } - -} diff --git a/Server/src/main/content/global/skill/fishing/FishingSpot.kt b/Server/src/main/content/global/skill/fishing/FishingSpot.kt new file mode 100644 index 000000000..e60dcb98d --- /dev/null +++ b/Server/src/main/content/global/skill/fishing/FishingSpot.kt @@ -0,0 +1,165 @@ +package content.global.skill.fishing + +import org.rs09.consts.NPCs + +enum class FishingSpot(val ids: IntArray, vararg val options: FishingOption) { + NET_BAIT( + intArrayOf( + NPCs.FISHING_SPOT_952, + NPCs.FISHING_SPOT_316, + NPCs.FISHING_SPOT_319, + NPCs.FISHING_SPOT_320, + NPCs.FISHING_SPOT_323, + NPCs.FISHING_SPOT_325, + NPCs.FISHING_SPOT_326, + NPCs.FISHING_SPOT_327, + NPCs.FISHING_SPOT_330, + NPCs.FISHING_SPOT_332, + NPCs.FISHING_SPOT_404, + NPCs.FISHING_SPOT_1331, + NPCs.FISHING_SPOT_2724, + NPCs.FISHING_SPOT_4908, + NPCs.FISHING_SPOT_7045 + ), + FishingOption.SMALL_NET, + FishingOption.BAIT + ), + CAGE( + intArrayOf( + NPCs.FISHING_SPOT_6267, + NPCs.FISHING_SPOT_6996, + NPCs.FISHING_SPOT_7862, + NPCs.FISHING_SPOT_7863, + NPCs.FISHING_SPOT_7864 + ), + FishingOption.CRAYFISH_CAGE + ), + LURE_BAIT( + intArrayOf( + NPCs.FISHING_SPOT_309, + NPCs.FISHING_SPOT_310, + NPCs.FISHING_SPOT_311, + NPCs.FISHING_SPOT_314, + NPCs.FISHING_SPOT_315, + NPCs.FISHING_SPOT_317, + NPCs.FISHING_SPOT_318, + NPCs.FISHING_SPOT_328, + NPCs.FISHING_SPOT_329, + NPCs.FISHING_SPOT_331, + NPCs.FISHING_SPOT_403, + NPCs.FISHING_SPOT_927, + NPCs.FISHING_SPOT_1189, + NPCs.FISHING_SPOT_1190, + NPCs.FISHING_SPOT_3019 + ), + FishingOption.LURE, + FishingOption.PIKE_BAIT + ), + CAGE_HARPOON( + intArrayOf( + NPCs.FISHING_SPOT_312, + NPCs.FISHING_SPOT_321, + NPCs.FISHING_SPOT_324, + NPCs.FISHING_SPOT_333, + NPCs.FISHING_SPOT_405, + NPCs.FISHING_SPOT_1332, + NPCs.FISHING_SPOT_1399, + NPCs.FISHING_SPOT_3804, + NPCs.FISHING_SPOT_5470, + NPCs.FISHING_SPOT_7046 + ), + FishingOption.LOBSTER_CAGE, + FishingOption.HARPOON + ), + NET_HARPOON( + intArrayOf( + NPCs.FISHING_SPOT_313, + NPCs.FISHING_SPOT_322, + NPCs.FISHING_SPOT_334, + NPCs.FISHING_SPOT_406, + NPCs.FISHING_SPOT_1191, + NPCs.FISHING_SPOT_1333, + NPCs.FISHING_SPOT_1405, + NPCs.FISHING_SPOT_1406, + NPCs.FISHING_SPOT_3574, + NPCs.FISHING_SPOT_3575, + NPCs.FISHING_SPOT_5471, + NPCs.FISHING_SPOT_7044 + ), + FishingOption.BIG_NET, + FishingOption.SHARK_HARPOON + ), + HARPOON_NET( + intArrayOf( + NPCs.FISHING_SPOT_3848, + NPCs.FISHING_SPOT_3849 + ), + FishingOption.HARPOON, + FishingOption.MONKFISH_NET + ), + SPOT_MORTMYRE( + intArrayOf( + NPCs.FISHING_SPOT_5748, + NPCs.FISHING_SPOT_5749 + ), + FishingOption.MORTMYRE_ROD + ), + SPOT_LUMDSWAMP( + intArrayOf( + NPCs.FISHING_SPOT_2067, + NPCs.FISHING_SPOT_2068 + ), + FishingOption.LUMBDSWAMP_NET, + FishingOption.LUMBDSWAMP_ROD + ), + SPOT_KBWANJI( + intArrayOf( + NPCs.FISHING_SPOT_1174 + ), + FishingOption.KBWANJI_NET + ), + SPOT_KARAMBWAN( + intArrayOf( + NPCs.FISHING_SPOT_1177 + ), + FishingOption.KARAMBWAN_VES + ), + BAIT_EELS( + intArrayOf( + NPCs.FISHING_SPOT_800 + ), + FishingOption.OILY_FISHING_ROD + ); + + companion object { + private val spotMap: HashMap = HashMap() + private val array: ArrayList = ArrayList() + + init { + val spots = values() + for(spot in spots) { + for(id in spot.ids) { + spotMap[id] = spot + array.add(id) + } + } + } + + fun forId(npcID: Int) : FishingSpot? { + return spotMap[npcID] + } + + fun getAllIds() : IntArray { + return array.toIntArray() + } + } + + fun getOptionByName(op: String) : FishingOption? { + for(o in options) { + if(o.option == op.lowercase()) { + return o + } + } + return null + } +} \ 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/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 index 001bd1a82..15fda63c1 100644 --- a/Server/src/main/content/global/skill/fletching/Fletching.java +++ b/Server/src/main/content/global/skill/fletching/Fletching.java @@ -97,7 +97,7 @@ public class Fletching { } public enum Limb { WOODEN_STOCK(9440,9420,9454,9, 12, new Animation(4436)), - OAK_STOCK(9442,9422,9176,24, 32, new Animation(4437)), + 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)), @@ -132,6 +132,7 @@ public class Fletching { YEW_LONGBOW((byte) 1,66,855,70, 75, new Animation(6688)), MAGIC_SHORTBOW((byte) 1,72,861,80, 83.3, new Animation(6683)), MAGIC_LONGBOW((byte) 1,70,859,85, 91.5, new Animation(6689)), + OGRE_COMP_BOW((byte) 1,4825,4827,30, 45, new Animation(-1)), //crossbows BRONZE_CBOW((byte) 2,9454,9174,9, 6, new Animation(6671)), @@ -190,13 +191,13 @@ public class Fletching { } 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), + BRONZE_ARROW(39, 882, 1, 1.3), + IRON_ARROW(40, 884, 15, 2.5), + STEEL_ARROW(41, 886, 30, 5), + MITHRIL_ARROW(42, 888, 45, 7.5), + ADAMANT_ARROW(43, 890, 60, 10), + RUNE_ARROW(44, 892, 75, 12.5), + DRAGON_ARROW(11237, 11212, 90, 15), BROAD_ARROW(13278, 4160, 52, 15); public int unfinished,finished,level; @@ -265,13 +266,13 @@ public class Fletching { } } 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), + STANDARD(1511, FletchingItems.ARROW_SHAFT, FletchingItems.SHORT_BOW, FletchingItems.LONG_BOW, FletchingItems.WOODEN_STOCK), + ACHEY(2862, FletchingItems.OGRE_ARROW_SHAFT, FletchingItems.OGRE_COMP_BOW), + OAK(1521, FletchingItems.OAK_ARROW_SHAFT, FletchingItems.OAK_SHORTBOW, FletchingItems.OAK_LONGBOW, FletchingItems.OAK_STOCK), + WILLOW(1519, FletchingItems.WILLOW_ARROW_SHAFT, FletchingItems.WILLOW_SHORTBOW, FletchingItems.WILLOW_LONGBOW, FletchingItems.WILLOW_STOCK), + MAPLE(1517, FletchingItems.MAPLE_ARROW_SHAFT, FletchingItems.MAPLE_SHORTOW, FletchingItems.MAPLE_LONGBOW, FletchingItems.MAPLE_STOCK), + YEW(1515, FletchingItems.YEW_ARROW_SHAFT, FletchingItems.YEW_SHORTBOW, FletchingItems.YEW_LONGBOW, FletchingItems.YEW_STOCK), + MAGIC(1513, FletchingItems.MAGIC_ARROW_SHAFT, FletchingItems.MAGIC_SHORTBOW, FletchingItems.MAGIC_LONGBOW), TEAK(6333, FletchingItems.TEAK_STOCK), MAHOGANY(6332, FletchingItems.MAHOGANY_STOCK); @@ -304,28 +305,34 @@ public class Fletching { //Achey logs OGRE_ARROW_SHAFT(2864, 6.4, 5, 4), + OGRE_COMP_BOW(4825, 45, 30, 1), //Oak logs + OAK_ARROW_SHAFT(52, 10, 15, 30), OAK_SHORTBOW(54, 16.5, 20, 1), OAK_LONGBOW(56,25,25,1), OAK_STOCK(9442, 16, 24, 1), //Willow logs + WILLOW_ARROW_SHAFT(52, 15, 30, 45), WILLOW_SHORTBOW(60, 33.3, 35, 1), WILLOW_LONGBOW(58, 41.5, 40, 1), WILLOW_STOCK(9444, 22, 39, 1), //Maple logs + MAPLE_ARROW_SHAFT(52, 20, 45, 60), MAPLE_SHORTOW(64, 50, 50, 1), MAPLE_LONGBOW(62, 58.3, 55, 1), MAPLE_STOCK(9448, 32, 54, 1), //Yew logs + YEW_ARROW_SHAFT(52, 25, 60, 75), YEW_SHORTBOW(68, 67.5, 65, 1), YEW_LONGBOW(66, 75, 70, 1), YEW_STOCK(9452, 50, 69, 1), //Magic logs + MAGIC_ARROW_SHAFT(52, 30, 75, 90), MAGIC_SHORTBOW(72, 83.3, 80,1), MAGIC_LONGBOW(70, 91.5, 85, 1), diff --git a/Server/src/main/content/global/skill/fletching/FletchingListeners.kt b/Server/src/main/content/global/skill/fletching/FletchingListeners.kt index 461df2d2c..464a0b6ff 100644 --- a/Server/src/main/content/global/skill/fletching/FletchingListeners.kt +++ b/Server/src/main/content/global/skill/fletching/FletchingListeners.kt @@ -1,11 +1,13 @@ package content.global.skill.fletching -import core.game.node.entity.skill.Skills -import content.global.skill.fletching.Fletching +import content.data.Quests import content.global.skill.fletching.items.arrow.ArrowHeadPulse import content.global.skill.fletching.items.arrow.HeadlessArrowPulse +import content.global.skill.fletching.items.arrow.HeadlessOgreArrowPulse import content.global.skill.fletching.items.bow.StringPulse import content.global.skill.fletching.items.crossbow.LimbPulse +import core.api.* +import core.game.node.entity.skill.Skills import core.game.node.item.Item import core.net.packet.PacketRepository import core.net.packet.context.ChildPositionContext @@ -21,6 +23,7 @@ import org.rs09.consts.Items.YELLOW_FEATHER_10090 import core.game.dialogue.SkillDialogueHandler import core.game.interaction.InteractionListener import core.game.interaction.IntType +import core.game.node.entity.player.Player class FletchingListeners : InteractionListener { @@ -32,7 +35,9 @@ class FletchingListeners : InteractionListener { val MITH_GRAPPLE = Items.MITH_GRAPPLE_9418 val ROPE_GRAPPLE = Items.MITH_GRAPPLE_9419 val ARROW_SHAFT = Items.ARROW_SHAFT_52 + val OGRE_ARROW_SHAFT = Items.OGRE_ARROW_SHAFT_2864 val FLETCHED_SHAFT = Items.HEADLESS_ARROW_53 + val FLIGHTED_OGRE_ARROW = Items.FLIGHTED_OGRE_ARROW_2865 val UNFINISHED_ARROWS = Fletching.ArrowHeads.values().map(Fletching.ArrowHeads::unfinished).toIntArray() val FEATHERS = intArrayOf(FEATHER_314,STRIPY_FEATHER_10087,RED_FEATHER_10088,BLUE_FEATHER_10089,YELLOW_FEATHER_10090,ORANGE_FEATHER_10091) val UNSTRUNG_BOWS = Fletching.String.values().map(Fletching.String::unfinished).toIntArray() @@ -42,6 +47,14 @@ class FletchingListeners : InteractionListener { onUseWith(IntType.ITEM,STRINGS,*UNSTRUNG_BOWS){ player, string, bow -> val enum = Fletching.stringMap[bow.id] ?: return@onUseWith false + + if (bow.id == Items.UNSTRUNG_COMP_BOW_4825) { + // You shouldn't be able to string a bow + if (getQuestStage(player, Quests.ZOGRE_FLESH_EATERS) < 8) { + player.packetDispatch.sendMessage("You must have started Zogre Flesh Eaters and asked Grish to string this.") + return@onUseWith true + } + } if(enum.string != string.id){ player.sendMessage("That's not the right kind of string for this.") return@onUseWith true @@ -63,7 +76,7 @@ class FletchingListeners : InteractionListener { onUseWith(IntType.ITEM,ARROW_SHAFT,*FEATHERS){ player, shaft, feather -> val handler: SkillDialogueHandler = - object : SkillDialogueHandler(player, SkillDialogue.ONE_OPTION, Item(FLETCHED_SHAFT)) { + 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)) } @@ -73,14 +86,28 @@ class FletchingListeners : InteractionListener { } } handler.open() - PacketRepository.send(RepositionChild::class.java, ChildPositionContext(player, Components.SKILL_MULTI1_309, 2, 210, 10)) + return@onUseWith true + } + + onUseWith(IntType.ITEM,OGRE_ARROW_SHAFT,*FEATHERS){ player, shaft, feather -> + val handler: SkillDialogueHandler = + object : SkillDialogueHandler(player, SkillDialogue.MAKE_SET_ONE_OPTION, Item(FLIGHTED_OGRE_ARROW)) { + override fun create(amount: Int, index: Int) { + player.pulseManager.run(HeadlessOgreArrowPulse(player, shaft.asItem(), Item(feather.id, 4), amount)) + } + + override fun getAll(index: Int): Int { + return player.inventory.getAmount(FLIGHTED_OGRE_ARROW) + } + } + handler.open() return@onUseWith true } onUseWith(IntType.ITEM,FLETCHED_SHAFT,*UNFINISHED_ARROWS){ player, shaft, unfinished -> val head = Fletching.arrowHeadMap[unfinished.id] ?: return@onUseWith false val handler: SkillDialogueHandler = - object : SkillDialogueHandler(player, SkillDialogue.ONE_OPTION, head.getFinished()) { + 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)) } @@ -90,7 +117,6 @@ class FletchingListeners : InteractionListener { } } handler.open() - PacketRepository.send(RepositionChild::class.java, ChildPositionContext(player, Components.SKILL_MULTI1_309, 2, 210, 10)) return@onUseWith true } @@ -136,6 +162,36 @@ class FletchingListeners : InteractionListener { return@onUseWith true } + /** + * (Long) Kebbit bolts don't need feathers and go 6 at a time so use their own interaction + */ + fun makeKebbitBolt(player : Player, ingredient : Item) : Boolean{ + val longBolts = when(ingredient.id){ + Items.KEBBIT_SPIKE_10105 -> false + Items.LONG_KEBBIT_SPIKE_10107 -> true + else -> return false + } + val level = if(longBolts) 42 else 26 + if (getDynLevel(player, Skills.FLETCHING) < level){ + sendMessage(player, "You need a fletching level of $level to create ${if (longBolts) "long " else ""}kebbit bolts.") + return true + } + val finalProduct = if(longBolts) Items.LONG_KEBBIT_BOLTS_10159 else Items.KEBBIT_BOLTS_10158 + val xp = if(longBolts) 47.7 else 28.6 // source https://runescape.wiki/w/Fletching?oldid=1069981#Bolts_2 + if(removeItem(player, ingredient.id)){ + addItem(player, finalProduct, 6) + player.skills.addExperience(Skills.FLETCHING, xp) + animate(player, 885) + } + return true + } + onUseWith(IntType.ITEM, Items.CHISEL_1755, Items.KEBBIT_SPIKE_10105) { player, used, with -> + return@onUseWith makeKebbitBolt(player, with as Item) + } + + onUseWith(IntType.ITEM, Items.CHISEL_1755, Items.LONG_KEBBIT_SPIKE_10107) { player, used, with -> + return@onUseWith makeKebbitBolt(player, with as Item) + } } } \ 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 index 94d898bef..3ec122866 100644 --- a/Server/src/main/content/global/skill/fletching/FletchingPlugin.java +++ b/Server/src/main/content/global/skill/fletching/FletchingPlugin.java @@ -49,7 +49,7 @@ public class FletchingPlugin extends UseWithHandler { //handle darts if(Fletching.isDart(event.getUsedItem().getId())){ final Fletching.Darts dart = Fletching.dartMap.get(event.getUsedItem().getId()); - SkillDialogueHandler handler = new SkillDialogueHandler(player, SkillDialogue.ONE_OPTION, dart.getFinished()) { + 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)); @@ -60,7 +60,6 @@ public class FletchingPlugin extends UseWithHandler { } }; handler.open(); - PacketRepository.send(RepositionChild.class, new ChildPositionContext(player, 309, 2, 230, 10)); return true; } @@ -78,7 +77,7 @@ public class FletchingPlugin extends UseWithHandler { final boolean hasFeather = (featherId == 314 || (featherId >= 10087 && featherId <= 10091)); if (hasFeather) { - SkillDialogueHandler handler = new SkillDialogueHandler(player, SkillDialogue.ONE_OPTION, bolt.getFinished()) { + 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)); @@ -89,14 +88,13 @@ public class FletchingPlugin extends UseWithHandler { } }; handler.open(); - PacketRepository.send(RepositionChild.class, new ChildPositionContext(player, 309, 2, 210, 10)); return true; } return false; } //handle logs - if(Fletching.isLog(event.getUsedItem().getId())) { + 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; diff --git a/Server/src/main/content/global/skill/fletching/FletchingPulse.java b/Server/src/main/content/global/skill/fletching/FletchingPulse.java index 9c7d0d638..54219541e 100644 --- a/Server/src/main/content/global/skill/fletching/FletchingPulse.java +++ b/Server/src/main/content/global/skill/fletching/FletchingPulse.java @@ -9,6 +9,7 @@ import core.game.node.entity.player.Player; import core.game.node.item.Item; import core.game.world.update.flag.context.Animation; import core.tools.StringUtils; +import content.data.Quests; /** * fletching skill pulse @@ -36,6 +37,11 @@ public final class FletchingPulse extends SkillPulse { */ private int amount = 0; + /** + * Represents the amount to arrows fletched (for ogre arrow shafts which is a random number from 2-6). + */ + private int finalAmount = 0; + /** * Constructs a new {@code FletchingPulse.java} {@code Object}. * @param player @@ -57,11 +63,22 @@ public final class FletchingPulse extends SkillPulse { amount = player.getInventory().getAmount(node); } if (fletch == Fletching.FletchingItems.OGRE_ARROW_SHAFT) { - if (player.getQuestRepository().getQuest("Big Chompy Bird Hunting").getStage(player) == 0) { + if (player.getQuestRepository().getQuest(Quests.BIG_CHOMPY_BIRD_HUNTING).getStage(player) == 0) { player.getPacketDispatch().sendMessage("You must have started Big Chompy Bird Hunting to make those."); return false; } } + if (fletch == Fletching.FletchingItems.OGRE_COMP_BOW) { + // Technically, this isn't supposed to show up till you've asked Grish. + if (player.getQuestRepository().getQuest(Quests.ZOGRE_FLESH_EATERS).getStage(player) < 8) { + player.getPacketDispatch().sendMessage("You must have started Zogre Flesh Eaters and asked Grish to make this."); + return false; + } + if (!player.getInventory().contains(2859, 1)) { + player.getPacketDispatch().sendMessage("You need to have Wolf Bones in order to make this."); + return false; + } + } return true; } @@ -82,9 +99,18 @@ public final class FletchingPulse extends SkillPulse { 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)); + // The amount of shafts given is random; between two and six will be made. + finalAmount = RandomFunction.random(2,6); + item.setAmount(finalAmount); } - player.getInventory().add(item); + if ( fletch == Fletching.FletchingItems.OGRE_COMP_BOW ) { + if (!player.getInventory().contains(2859, 1)) { + return false; + } else { + player.getInventory().remove(new Item(2859)); + } + } + player.getInventory().add(item); player.getSkills().addExperience(Skills.FLETCHING, fletch.experience, true); String message = getMessage(); player.getPacketDispatch().sendMessage(message); @@ -110,6 +136,18 @@ public final class FletchingPulse extends SkillPulse { switch (fletch) { case ARROW_SHAFT: return "You carefully cut the wood into 15 arrow shafts."; + case OAK_ARROW_SHAFT: + return "You carefully cut the wood into 30 arrow shafts."; + case WILLOW_ARROW_SHAFT: + return "You carefully cut the wood into 45 arrow shafts."; + case MAPLE_ARROW_SHAFT: + return "You carefully cut the wood into 60 arrow shafts."; + case YEW_ARROW_SHAFT: + return "You carefully cut the wood into 75 arrow shafts."; + case MAGIC_ARROW_SHAFT: + return "You carefully cut the wood into 90 arrow shafts."; + case OGRE_ARROW_SHAFT: + return "You carefully cut the wood into " + finalAmount + " arrow shafts."; default: return "You carefully cut the wood into " + (StringUtils.isPlusN(fletch.getItem().getName()) ? "an" : "a") + " " + fletch.getItem().getName().replace("(u)", "").trim() + "."; } diff --git a/Server/src/main/content/global/skill/fletching/GemBoltListener.kt b/Server/src/main/content/global/skill/fletching/GemBoltListener.kt new file mode 100644 index 000000000..94c09c65b --- /dev/null +++ b/Server/src/main/content/global/skill/fletching/GemBoltListener.kt @@ -0,0 +1,69 @@ +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/GemBoltPlugin.java b/Server/src/main/content/global/skill/fletching/GemBoltPlugin.java deleted file mode 100644 index 068e69de2..000000000 --- a/Server/src/main/content/global/skill/fletching/GemBoltPlugin.java +++ /dev/null @@ -1,103 +0,0 @@ -package content.global.skill.fletching; - -import content.global.skill.fletching.items.gem.GemBoltCutPulse; -import content.global.skill.fletching.items.gem.GemBoltPulse; -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; -import core.game.dialogue.SkillDialogueHandler; -import core.game.dialogue.SkillDialogueHandler.SkillDialogue; -import org.rs09.consts.Items; - -/** - * Represents the gem bolt creating plugin. - * @author Ceikry - * @version 2.0 - */ -@Initializable -public final class GemBoltPlugin extends UseWithHandler { - Fletching.GemBolts bolt; - - /** - * Constructs a new {@code GemBoltPlugin} {@code Object}. - */ - public GemBoltPlugin() { - super(Items.OPAL_BOLT_TIPS_45, - Items.PEARL_BOLT_TIPS_46, - Items.JADE_BOLT_TIPS_9187, - Items.TOPAZ_BOLT_TIPS_9188, - Items.SAPPHIRE_BOLT_TIPS_9189, - Items.EMERALD_BOLT_TIPS_9190, - Items.RUBY_BOLT_TIPS_9191, - Items.DIAMOND_BOLT_TIPS_9192, - Items.DRAGON_BOLT_TIPS_9193, - Items.ONYX_BOLT_TIPS_9194, - 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); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - addHandler(Items.CHISEL_1755, ITEM_TYPE, this); - for(Fletching.GemBolts gem : Fletching.GemBolts.values()){ - addHandler(gem.base, ITEM_TYPE,this); - } - return this; - } - - @Override - public boolean handle(final NodeUsageEvent event) { - final Player player = event.getPlayer(); - bolt = Fletching.tipMap.get(event.getUsedItem().getId()); - if(bolt == null){ - bolt = Fletching.tipMap.get(event.getUsedWith().getId()); - } - if(bolt != null){ - SkillDialogueHandler handler = new SkillDialogueHandler(player, SkillDialogue.ONE_OPTION, new Item(bolt.product)) { - @Override - public void create(final int amount, int index) { - player.getPulseManager().run(new GemBoltPulse(player, event.getUsedItem(), bolt, amount)); - } - @Override - public int getAll(int index) { - return player.getInventory().getAmount(event.getUsedItem()); - } - }; - handler.open(); - PacketRepository.send(RepositionChild.class, new ChildPositionContext(player, 309, 2, 210, 10)); - } else { - final Fletching.GemBolts gem = Fletching.gemMap.get(event.getUsedItem().getId() == Items.CHISEL_1755 ? event.getBaseItem().getId() : event.getUsedItem().getId()); - if(gem == null){ - return false; - } - new SkillDialogueHandler(player, SkillDialogue.ONE_OPTION, new Item(gem.gem)) { - @Override - public void create(final int amount, final int index) { - player.getPulseManager().run(new GemBoltCutPulse(player, event.getUsedItem(), gem, amount)); - } - - @Override - public int getAll(int index) { - return player.getInventory().getAmount(gem.gem); - } - - }.open(); - } - return true; - } -} 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 index 927f16d10..5ea27c2cc 100644 --- a/Server/src/main/content/global/skill/fletching/items/arrow/ArrowHeadPulse.java +++ b/Server/src/main/content/global/skill/fletching/items/arrow/ArrowHeadPulse.java @@ -7,6 +7,8 @@ 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 @@ -53,6 +55,10 @@ public class ArrowHeadPulse extends SkillPulse { 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; } 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 index 3ac1ab593..3125b5669 100644 --- a/Server/src/main/content/global/skill/fletching/items/arrow/HeadlessArrowPulse.java +++ b/Server/src/main/content/global/skill/fletching/items/arrow/HeadlessArrowPulse.java @@ -6,6 +6,8 @@ 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 @@ -75,6 +77,10 @@ public final class HeadlessArrowPulse extends SkillPulse { } else { useSets = false; } + if (!hasSpaceFor(player, HEADLESS_ARROW.asItem())) { + sendDialogue(player, "You do not have enough inventory space."); + return false; + } return true; } diff --git a/Server/src/main/content/global/skill/fletching/items/arrow/HeadlessOgreArrowPulse.java b/Server/src/main/content/global/skill/fletching/items/arrow/HeadlessOgreArrowPulse.java new file mode 100644 index 000000000..fd7af6731 --- /dev/null +++ b/Server/src/main/content/global/skill/fletching/items/arrow/HeadlessOgreArrowPulse.java @@ -0,0 +1,131 @@ +package content.global.skill.fletching.items.arrow; + +import core.game.node.entity.player.Player; +import core.game.node.entity.skill.SkillPulse; +import core.game.node.entity.skill.Skills; +import core.game.node.item.Item; +import org.rs09.consts.Items; + +import static core.api.ContentAPIKt.hasSpaceFor; +import static core.api.ContentAPIKt.sendDialogue; + +/** + * Represents the arrow pulse for creating unfinished ogre arrows. + * @author 'Vexia + */ +public final class HeadlessOgreArrowPulse extends SkillPulse { + + /** + * Represents the headless ogre arrow item. + */ + private final Item HEADLESS_ARROW = new Item(Items.FLIGHTED_OGRE_ARROW_2865); + + /** + * Represents the ogre arrow shaft item. + */ + private final Item ARROW_SHAFT = new Item(Items.OGRE_ARROW_SHAFT_2864); + + /** + * Represents the feather items. + */ + private static final Item[] FEATHER = new Item[] { + new Item(Items.FEATHER_314, 4), + }; + + /** + * The feather being used. + */ + private Item feather; + + /** + * Represents the amount to make. + */ + private int sets; + + /** + * Constructs a new {@code ArrowPulse.java} {@code Object}. + * @param player the player. + * @param node the node. + */ + public HeadlessOgreArrowPulse(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 (!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 >= 24 && shaftAmount >= 6) { + feather.setAmount(24); + ARROW_SHAFT.setAmount(6); + player.getPacketDispatch().sendMessage("You attach 24 feathers to 6 ogre arrow shafts."); + } else { + int amount = Math.min(featherAmount / 4, shaftAmount); + feather.setAmount(amount*4); + ARROW_SHAFT.setAmount(amount); + player.getPacketDispatch().sendMessage(amount == 1 + ? "You attach a feathers to a shaft." : "You attach " + amount * 4 + " feathers to " + amount + " ogre arrow shafts."); + } + if (player.getInventory().remove(feather, ARROW_SHAFT)) { + HEADLESS_ARROW.setAmount(ARROW_SHAFT.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/BoltPulse.java b/Server/src/main/content/global/skill/fletching/items/bolts/BoltPulse.java index 26c974603..b345c4276 100644 --- a/Server/src/main/content/global/skill/fletching/items/bolts/BoltPulse.java +++ b/Server/src/main/content/global/skill/fletching/items/bolts/BoltPulse.java @@ -76,6 +76,11 @@ public final class BoltPulse extends SkillPulse { 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; } 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 index fdae3a1bb..0d8645e44 100644 --- a/Server/src/main/content/global/skill/fletching/items/bow/StringPulse.java +++ b/Server/src/main/content/global/skill/fletching/items/bow/StringPulse.java @@ -1,5 +1,7 @@ package content.global.skill.fletching.items.bow; +import core.game.node.entity.player.info.LogType; +import core.game.node.entity.player.info.PlayerMonitor; import core.game.node.entity.player.link.diary.DiaryType; import core.game.world.map.zone.ZoneBorders; import core.game.node.entity.skill.SkillPulse; @@ -8,6 +10,8 @@ import content.global.skill.fletching.Fletching; import core.game.node.entity.player.Player; import core.game.node.item.Item; +import static core.api.ContentAPIKt.amountInInventory; + /** * Represents the skill pulse of stringing. * @@ -25,6 +29,9 @@ public class StringPulse extends SkillPulse { */ private int amount; + private int initialAmount; + private int processedAmount; + /** * Constructs a new {@code StringbowPlugin.java} {@code Object}. * @@ -35,6 +42,8 @@ public class StringPulse extends SkillPulse { super(player, node); this.bow = bow; this.amount = amount; + this.initialAmount = amountInInventory(player, node.getId()); + this.processedAmount = 0; } @Override @@ -46,6 +55,9 @@ public class StringPulse extends SkillPulse { 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; @@ -65,6 +77,10 @@ public class StringPulse extends SkillPulse { 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."); + processedAmount++; + if (processedAmount > initialAmount) { + PlayerMonitor.log(player, LogType.DUPE_ALERT, "fletched item (" + player.getName() + ", " + bow.unfinished + "): initialAmount " + initialAmount + ", processedAmount " + processedAmount); + } if (bow == Fletching.String.MAGIC_SHORTBOW && (new ZoneBorders(2721, 3489, 2724, 3493, 0).insideBorder(player) 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 index ec2e31e0e..d65fac185 100644 --- a/Server/src/main/content/global/skill/fletching/items/darts/DartPulse.java +++ b/Server/src/main/content/global/skill/fletching/items/darts/DartPulse.java @@ -6,6 +6,9 @@ import content.global.skill.fletching.Fletching; import core.game.node.entity.player.Player; import core.game.node.item.Item; +import static core.api.ContentAPIKt.*; +import content.data.Quests; + /** * Represents the dart pulse. * @author ceikry @@ -44,10 +47,14 @@ public final class DartPulse extends SkillPulse { player.getDialogueInterpreter().sendDialogue("You need a fletching level of " + dart.level + " to do this."); return false; } - if (!player.getQuestRepository().isComplete("The Tourist Trap")){ + if (!player.getQuestRepository().isComplete(Quests.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; } 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 index 266d18dad..4cd856e8a 100644 --- a/Server/src/main/content/global/skill/fletching/items/gem/GemBoltPulse.java +++ b/Server/src/main/content/global/skill/fletching/items/gem/GemBoltPulse.java @@ -48,6 +48,10 @@ public final class GemBoltPulse extends SkillPulse { 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; } diff --git a/Server/src/main/content/global/skill/gather/GatheringSkillOptionListeners.kt b/Server/src/main/content/global/skill/gather/GatheringSkillOptionListeners.kt index e29aa194e..cc7112fb3 100644 --- a/Server/src/main/content/global/skill/gather/GatheringSkillOptionListeners.kt +++ b/Server/src/main/content/global/skill/gather/GatheringSkillOptionListeners.kt @@ -1,42 +1,18 @@ package content.global.skill.gather +import content.global.skill.fishing.FishingSpot +import content.global.skill.gather.fishing.FishingPulse +import core.game.interaction.InteractionListener import core.game.node.Node import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player -import content.global.skill.fishing.FishingSpot -import content.global.skill.gather.fishing.FishingPulse -import content.global.skill.gather.mining.MiningSkillPulse -import content.global.skill.gather.woodcutting.WoodcuttingSkillPulse -import org.rs09.consts.NPCs -import content.region.misc.miscellania.dialogue.KjallakOnChopDialogue -import core.game.interaction.InteractionListener -import core.game.interaction.IntType class GatheringSkillOptionListeners : InteractionListener { val ETCETERIA_REGION = 10300 override fun defineListeners() { - on(IntType.SCENERY,"chop-down","chop down","cut down","chop"){ player, node -> - if(player.location.regionId == ETCETERIA_REGION){ - player.dialogueInterpreter.open(KjallakOnChopDialogue(), NPC(NPCs.CARPENTER_KJALLAK_3916)) - return@on true - } - player.pulseManager.run(WoodcuttingSkillPulse(player, node.asScenery())) - return@on true - } - on(IntType.SCENERY,"mine"){ player, node -> - player.pulseManager.run(MiningSkillPulse(player, node.asScenery())) - return@on true - } - - on(IntType.NPC,"net"){ player, node -> return@on fish(player,node,"net")} - on(IntType.NPC,"lure"){ player, node -> return@on fish(player,node,"lure")} - on(IntType.NPC,"bait"){ player, node -> return@on fish(player,node,"bait")} - on(IntType.NPC,"harpoon"){ player, node -> return@on fish(player,node,"harpoon")} - on(IntType.NPC,"cage"){ player, node -> return@on fish(player,node,"cage")} - on(IntType.NPC,"fish"){ player, node -> return@on fish(player,node,"fish") } } fun fish(player: Player, node: Node, opt: String): Boolean{ diff --git a/Server/src/main/content/global/skill/gather/PickaxeRepairPlugin.kt b/Server/src/main/content/global/skill/gather/PickaxeRepairPlugin.kt index 8ac6c82c9..83e3db86a 100644 --- a/Server/src/main/content/global/skill/gather/PickaxeRepairPlugin.kt +++ b/Server/src/main/content/global/skill/gather/PickaxeRepairPlugin.kt @@ -1,10 +1,12 @@ package content.global.skill.gather +import core.api.playAudio import core.game.interaction.NodeUsageEvent import core.game.interaction.UseWithHandler import core.game.node.item.Item import core.plugin.Initializable import core.plugin.Plugin +import org.rs09.consts.Sounds /** * Repairs pickaxes after being broken @@ -35,7 +37,7 @@ class PickaxeRepairPlugin : UseWithHandler(480,482,484,486,488,490){ event.player.inventory.remove(used.asItem()) event.player.inventory.add(Item(product)) event.player.sendMessage("You carefully reattach the head to the handle.") - event.player.audioManager.send(10) + playAudio(event.player, Sounds.EYEGLO_COIN_10) return true } diff --git a/Server/src/main/content/global/skill/gather/SkillingResource.java b/Server/src/main/content/global/skill/gather/SkillingResource.java index e2c2a982c..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. @@ -645,7 +650,6 @@ public enum SkillingResource { static { for (SkillingResource resource : SkillingResource.values()) { if (RESOURCES.containsKey(resource.id)) { - SystemLogger.logErr(SkillingResource.class, "Already contained resource " + resource.getId() + " - " + resource.name()); } RESOURCES.put(resource.id, resource); } diff --git a/Server/src/main/content/global/skill/gather/fishing/FishingListener.kt b/Server/src/main/content/global/skill/gather/fishing/FishingListener.kt new file mode 100644 index 000000000..1cad8a28d --- /dev/null +++ b/Server/src/main/content/global/skill/gather/fishing/FishingListener.kt @@ -0,0 +1,166 @@ +package content.global.skill.gather.fishing + +import content.global.handlers.item.equipment.fistofguthixgloves.FOGGlovesManager +import content.global.skill.fishing.Fish +import content.global.skill.fishing.FishingOption +import content.global.skill.fishing.FishingSpot +import content.global.skill.skillcapeperks.SkillcapePerks +import content.global.skill.skillcapeperks.SkillcapePerks.Companion.isActive +import content.global.skill.summoning.familiar.Forager +import core.api.* +import core.game.event.ResourceProducedEvent +import core.game.interaction.Clocks +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.Node +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.game.node.item.Item +import core.game.system.command.sets.STATS_BASE +import core.game.system.command.sets.STATS_FISH +import core.game.world.GameWorld +import core.game.world.map.path.Pathfinder +import core.tools.RandomFunction +import core.tools.colorize +import org.rs09.consts.Items + +class FishingListener : InteractionListener{ + override fun defineListeners() { + val SPOT_IDS = FishingSpot.values().flatMap { it.ids.toList() }.toIntArray() + defineInteraction( + IntType.NPC, + SPOT_IDS, + "net", "lure", "bait", "harpoon", "cage", "fish", + persistent = true, + allowedDistance = 1, + handler = ::handleFishing + ) + } + + private fun handleFishing(player: Player, node: Node, state: Int) : Boolean { + val npc = node as? NPC ?: return clearScripts(player) + val spot = FishingSpot.forId(npc.id) ?: return clearScripts(player) + val op = spot.getOptionByName(getUsedOption(player)) ?: return clearScripts(player) + + var forager: Forager? = null + + if (player.familiarManager.hasFamiliar() && player.familiarManager.familiar is Forager) { + forager = player.familiarManager.familiar as Forager + } + + if (!finishedMoving(player)) { + return restartScript(player) + } + + if (state == 0) { + if (!checkRequirements(player, op, node)) return clearScripts(player) + forager?.let { + val dest = player.location.transform(player.direction) + Pathfinder.find(it, dest).walk(it) + } + when (op.option) { + "cage" -> if (spot.name == "CAGE_HARPOON") { + sendMessage(player, "You attempt to catch a lobster.") + } else sendMessage(player, "You attempt to catch a crayfish.") + "harpoon" -> sendMessage(player, "You start harpooning fish.") + "net" -> sendMessage(player, "You cast out your net...") + in arrayOf("bait", "lure") -> { + sendMessage(player, "You cast out your line...") + sendMessage(player, "You attempt to catch a fish.") + } + else -> { // Probably not authentic, but covers unknown cases. + sendMessage(player, "You attempt to catch some fish...") + } + } + } + + if (clockReady(player, Clocks.SKILLING)) { + anim(player, op) + forager?.handlePassiveAction() + + val fish = op?.rollFish(player) ?: return delayClock(player, Clocks.SKILLING, 5) + if (!hasSpaceFor(player, Item(fish.id)) || !op.removeBait(player)) return restartScript(player) + player.dispatch(ResourceProducedEvent(fish.id, fish.getItem().amount, node)) + val item = fish.getItem() + val bigFishId = Fish.getBigFish(fish) + val bigFishChance = if (GameWorld.settings?.isDevMode == true) 10 else 5000 + if (bigFishId != null && RandomFunction.roll(bigFishChance)) { + sendMessage(player, "You catch an enormous" + getItemName(fish.id).lowercase().replace("raw", "") + "!") + addItemOrDrop(player, bigFishId, 1) + } else { + var msg = when (fish) { + in arrayOf(Fish.ANCHOVIE, Fish.SHRIMP, Fish.SEAWEED) -> "You catch some " + in arrayOf(Fish.OYSTER) -> "You catch an " + else -> "You catch a " + } + msg += getItemName(fish.id).lowercase().replace("raw ", "").replace("big ", "") + msg += if (fish == Fish.SHARK) "!" else "." + sendMessage(player, msg) + addItemOrDrop(player, item.id, item.amount) + } + + if (isActive(SkillcapePerks.GREAT_AIM, player) && RandomFunction.roll(20)) { + addItemOrDrop(player, item.id, item.amount) + sendMessage(player, colorize("%RYour expert aim catches you a second fish.")) + player.incrementAttribute("/save:$STATS_BASE:$STATS_FISH") + } + + player.incrementAttribute("/save:$STATS_BASE:$STATS_FISH") + var xp = fish.experience + if ((item.id == Items.RAW_SWORDFISH_371 && inEquipment(player, Items.SWORDFISH_GLOVES_12860)) + || (item.id == Items.RAW_SHARK_383 && inEquipment(player, Items.SHARK_GLOVES_12861))) { + xp += 100 + FOGGlovesManager.updateCharges(player) + } + rewardXP(player, Skills.FISHING, xp) + delayClock(player, Clocks.SKILLING, 5) + if (!checkRequirements(player, op, node)) return clearScripts(player) + } + return keepRunning(player) + } + + private fun anim(player: Player, option: FishingOption) { + if (animationFinished(player)) + animate(player, option.animation) + } + + private fun checkRequirements(player: Player, option: FishingOption, node: Node) : Boolean { + if (!inInventory(player, option.tool) && !hasBarbTail(player, option)) { + // The fly fishing rod & net dialogue is confirmed from videos. Others are assumptions based upon this. + var msg = "You need a " + msg += if (getItemName(option.tool).contains("net", true)) "net to " else "${getItemName(option.tool).lowercase()} to " + msg += if (option.option in arrayOf("lure", "bait")) "${option.option} these fish." else "catch these fish." + sendDialogue(player, msg) + return false + } + if (!option.hasBait(player)) { + var msg = "You don't have any " + option.getBaitName().lowercase() + msg += if (option.getBaitName() == getItemName(Items.FISHING_BAIT_313)) " left." else "s left." + sendDialogue(player, msg) + return false + } + if (!hasLevelDyn(player, Skills.FISHING, option.level)) { + sendDialogue(player, "You need a Fishing level of at least ${option.level} to ${option.option} these fish.") + return false + } + if (freeSlots(player) == 0) { + if (option.fish.contains(Fish.LOBSTER)) { + sendDialogue(player, "You can't carry any more lobsters.") + } else { + sendDialogue(player, "You can't carry any more fish.") + } + return false + } + return node.isActive && node.location.withinDistance(player.location, 1) + } + + + private fun hasBarbTail(player: Player, option: FishingOption): Boolean { + val bh = FishingOption.BARB_HARPOON.tool + if (option == FishingOption.HARPOON || option == FishingOption.SHARK_HARPOON) { + if (inInventory(player, bh) || inEquipment(player, bh)) return true + } + return false + } +} 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 15f0391fa..ea254e311 100644 --- a/Server/src/main/content/global/skill/gather/fishing/FishingPulse.kt +++ b/Server/src/main/content/global/skill/gather/fishing/FishingPulse.kt @@ -1,27 +1,29 @@ package content.global.skill.gather.fishing -import core.game.event.ResourceProducedEvent import content.data.skill.SkillingPets -import core.game.node.entity.npc.NPC -import core.game.node.entity.player.Player -import core.game.node.entity.player.link.skillertasks.SkillTasks -import core.game.node.entity.skill.SkillPulse -import core.game.node.entity.skill.Skills import content.global.skill.fishing.Fish import content.global.skill.fishing.FishingOption import content.global.skill.skillcapeperks.SkillcapePerks import content.global.skill.skillcapeperks.SkillcapePerks.Companion.isActive import content.global.skill.summoning.familiar.Forager +import core.api.* +import core.game.event.ResourceProducedEvent +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.skillertasks.SkillTasks +import core.game.node.entity.skill.SkillPulse +import core.game.node.entity.skill.Skills import core.game.node.item.Item +import core.game.system.command.sets.STATS_BASE +import core.game.system.command.sets.STATS_FISH import core.game.system.task.Pulse +import core.game.world.GameWorld.Pulser import core.game.world.map.Location import core.game.world.map.path.Pathfinder import core.game.world.update.flag.context.Animation import core.tools.RandomFunction -import core.game.system.command.sets.STATS_BASE -import core.game.system.command.sets.STATS_FISH -import core.game.world.GameWorld.Pulser import core.tools.colorize +import org.rs09.consts.Items /** * Handles a fishing pulse. @@ -52,26 +54,34 @@ class FishingPulse(player: Player?, npc: NPC, private val option: FishingOption? if (option == null) { return false } - player.debug(player.inventory.containsItem(option.tool).toString()) - if (!player.inventory.containsItem(option.tool) && !hasBarbTail()) { - //System.out.println(isBareHanded(player)); - player.dialogueInterpreter.sendDialogue("You need a " + option.tool.name.toLowerCase() + " to catch these fish.") + player.debug(inInventory(player, option.tool).toString()) + if (!inInventory(player, option.tool) && !hasBarbTail()) { + // The fly fishing rod & net dialogue is confirmed from videos. Others are assumptions based upon this. + var msg = "You need a " + msg += if (getItemName(option.tool).contains("net", true)) "net to " else "${getItemName(option.tool).lowercase()} to " + msg += if (option.option in arrayOf("lure", "bait")) "${option.option} these fish." else "catch these fish." + sendDialogue(player, msg) stop() return false } - if (!option.hasBait(player.inventory)) { - player.dialogueInterpreter.sendDialogue("You don't have any " + option.getBaitName().toLowerCase() + "s left.") + if (!option.hasBait(player)) { + var msg = "You don't have any " + option.getBaitName().lowercase() + msg += if (option.getBaitName() == getItemName(Items.FISHING_BAIT_313)) " left." else "s left." + sendDialogue(player, msg) stop() return false } - if (player.skills.getLevel(Skills.FISHING) < option!!.level) { - val f = option!!.fish[option!!.fish.size - 1] - player.dialogueInterpreter.sendDialogue("You need a fishing level of " + f.level + " to catch " + (if (f == Fish.SHRIMP || f == Fish.ANCHOVIE) "" else "a") + " " + f.item.name.toLowerCase() + ".".trim { it <= ' ' }) + if (!hasLevelDyn(player, Skills.FISHING, option.level)) { + sendDialogue(player, "You need a Fishing level of at least ${option.level} to ${option.option} these fish.") stop() return false } - if (player.inventory.freeSlots() == 0) { - player.dialogueInterpreter.sendDialogue("You don't have enough space in your inventory.") + if (freeSlots(player) == 0) { + if (option.fish.contains(Fish.LOBSTER)) { + sendDialogue(player, "You can't carry any more lobsters.") + } else { + sendDialogue(player, "You can't carry any more fish.") + } stop() return false } @@ -109,18 +119,18 @@ class FishingPulse(player: Player?, npc: NPC, private val option: FishingOption? forager.handlePassiveAction() } if (success()) { - if (player.inventory.hasSpaceFor(fish!!.item) && option!!.removeBait(player.inventory)) { + if (player.inventory.hasSpaceFor(Item(fish!!.id)) && option!!.removeBait(player)) { if (player.skillTasks.hasTask()) { updateSkillTask() } - player.dispatch(ResourceProducedEvent(fish!!.item.id, fish!!.item.amount, node!!)) + player.dispatch(ResourceProducedEvent(fish!!.id, 1, node!!)) SkillingPets.checkPetDrop(player, SkillingPets.HERON) - val item = fish!!.item + val item = fish!! if (isActive(SkillcapePerks.GREAT_AIM, player) && RandomFunction.random(100) <= 5) { - player.inventory.add(item) + addItem(player, item.id) player.sendMessage(colorize("%RYour expert aim catches you a second fish.")) } - player.inventory.add(item) + addItem(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) @@ -169,20 +179,17 @@ class FishingPulse(player: Player?, npc: NPC, private val option: FishingOption? player.skillTasks.decreaseTask(player, SkillTasks.FTUNA1) player.skillTasks.decreaseTask(player, SkillTasks.FTUNA2) } + else -> {} } } private fun isBareHanded(p: Player): Boolean { - if (option == FishingOption.HARPOON || option == FishingOption.N_HARPOON) { - if (checkFish(p) > 0 && !(player.inventory.containsItem( - option.tool - ) || player.equipment.containsItem(option.tool) || hasBarbTail()) + if (option == FishingOption.HARPOON || option == FishingOption.SHARK_HARPOON) { + if (checkFish(p) > 0 && !(inInventory(player, option.tool) || inEquipment(player, option.tool) || hasBarbTail()) ) { return true } - if (checkFish(p) > 2 && !(player.inventory.containsItem( - option.tool - ) || player.equipment.containsItem(option.tool) || hasBarbTail()) + if (checkFish(p) > 2 && !(inInventory(player, option.tool) || inEquipment(player, option.tool) || hasBarbTail()) ) { return true } @@ -228,11 +235,9 @@ class FishingPulse(player: Player?, npc: NPC, private val option: FishingOption? * @return `True` if so. */ private fun hasBarbTail(): Boolean { - if (option == FishingOption.HARPOON || option == FishingOption.N_HARPOON) { - if (player.inventory.containsItem(FishingOption.BARB_HARPOON.tool) || player.equipment.containsItem( - FishingOption.BARB_HARPOON.tool - ) - ) { + val bh = FishingOption.BARB_HARPOON.tool + if (option == FishingOption.HARPOON || option == FishingOption.SHARK_HARPOON) { + if (inInventory(player, bh) || inEquipment(player, bh)) { return true } } @@ -241,15 +246,23 @@ class FishingPulse(player: Player?, npc: NPC, private val option: FishingOption? override fun message(type: Int) { when (type) { - 0 -> player.packetDispatch.sendMessage(option!!.startMessage) + 0 -> sendMessage(player, option!!.getStartMessage()) 2 -> { - player.packetDispatch.sendMessage( - if (fish == Fish.ANCHOVIE || fish == Fish.SHRIMP) "You catch some " + fish!!.item.name.toLowerCase() - .replace("raw", "") - .trim { it <= ' ' } + "." else "You catch a " + fish!!.item.name.toLowerCase() - .replace("raw", "").trim { it <= ' ' } + ".") + var msg = when (fish) { + in arrayOf(Fish.ANCHOVIE, Fish.SHRIMP, Fish.SEAWEED) -> "You catch some " + in arrayOf(Fish.OYSTER) -> "You catch an " + else -> "You catch a " + } + msg += getItemName(fish!!.id).lowercase().replace("raw ", "").replace("big ", "") + msg += if (fish == Fish.SHARK) "!" else "." + sendMessage(player, msg) + if (player.inventory.freeSlots() == 0) { - player.dialogueInterpreter.sendDialogue("You don't have enough space in your inventory.") + if (fish == Fish.LOBSTER) { + sendDialogue(player, "You can't carry any more lobsters.") + } else { + sendDialogue(player, "You can't carry any more fish.") + } stop() } } 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 new file mode 100644 index 000000000..1c46081a4 --- /dev/null +++ b/Server/src/main/content/global/skill/gather/mining/MiningListener.kt @@ -0,0 +1,232 @@ +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 +import core.api.* +import core.cache.def.impl.ItemDefinition +import core.game.event.ResourceProducedEvent +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.Node +import core.game.node.entity.npc.drop.DropFrequency +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.ChanceItem +import core.game.node.scenery.Scenery +import core.game.node.scenery.SceneryBuilder +import core.game.system.command.sets.STATS_BASE +import core.game.system.command.sets.STATS_ROCKS +import core.game.world.map.zone.ZoneBorders +import core.tools.RandomFunction +import core.tools.prependArticle +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 + ) + } + 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)) + + 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 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 + } + anim(player, tool) + sendMessage(player, "You swing your pickaxe at the rock...") + return delayScript(player, getDelay(resource)) + } + + anim(player, tool) + if (!checkReward(player, resource, tool)) + return delayScript(player, getDelay(resource)) + + // Reward logic + var reward = resource!!.reward + var rewardAmount : Int + if (reward > 0) { + reward = calculateReward(player, resource, isEssence, isGems, reward) // calculate rewards + 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 + rewardXP(player, Skills.MINING, experience) + + // If player is wearing Bracelet of Clay, soften + if(reward == Items.CLAY_434){ + val bracelet = getItemFromEquipment(player, EquipmentSlot.HANDS) + if(bracelet != null && bracelet.id == Items.BRACELET_OF_CLAY_11074){ + var charges = player.getAttribute("jewellery-charges:bracelet-of-clay", 28) + charges-- + reward = Items.SOFT_CLAY_1761 + sendMessage(player, "Your bracelet of clay softens the clay for you.") + if(charges <= 0) { + if(removeItem(player, bracelet, Container.EQUIPMENT)) { + sendMessage(player, "Your bracelet of clay crumbles to dust.") + charges = 28 + } + } + player.setAttribute("/save:jewellery-charges:bracelet-of-clay", charges) + } + } + + //If the player is mining gold in the witchaven dungeon, reward family crest perfect gold ore + val familyCrestGoldOreArea = ZoneBorders(2733, 9695, 2741, 9683) + if (reward == Items.GOLD_ORE_444 && inBorders(player, familyCrestGoldOreArea)) { + reward = Items.PERFECT_GOLD_ORE_446 + } + val rewardName = getItemName(reward).lowercase() + + // Send the message for the resource reward + if (isGems) { + sendMessage(player, "You get ${prependArticle(rewardName)}.") + } else { + sendMessage(player, "You get some ${rewardName.lowercase()}.") + } + + // Give the mining reward, increment 'rocks mined' attribute + 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) { + var chance = 282 + var altered = false + val ring = getItemFromEquipment(player, EquipmentSlot.RING) + if (ring != null && ring.name.lowercase().contains("ring of wealth") || inEquipment(player, Items.RING_OF_THE_STAR_SPRITE_14652)) { + chance = (chance / 1.5).toInt() + altered = true + } + val necklace = getItemFromEquipment(player, EquipmentSlot.NECK) + if (necklace != null && necklace.id in 1705..1713) { + chance = (chance / 1.5).toInt() + altered = true + } + if (RandomFunction.roll(chance)) { + val gem = GEM_REWARDS.random() + sendMessage(player,"You find a ${gem.name}!") + if (freeSlots(player) == 0) { + sendMessage(player,"You do not have enough space in your inventory, so you drop the gem on the floor.") + } + addItemOrDrop(player, gem.id) + } + } + + // Transform ore to depleted version + if (!isEssence && resource!!.respawnRate != 0 && RandomFunction.roll(getDynLevel(player, Skills.MINING)*5/resource!!.respawnDuration + 1)) { + SceneryBuilder.replace(node as Scenery, Scenery(resource!!.emptyId, node.getLocation(), node.type, node.rotation), resource!!.respawnDuration) + node.setActive(false) + return true + } + } + return true + } + + private fun calculateRewardAmount(player: Player, isMiningEssence: Boolean, reward: Int): Int { + var amount = 1 + + // If player is wearing Varrock armour from diary, roll chance at extra ore + if (!isMiningEssence && player.achievementDiaryManager.getDiary(DiaryType.VARROCK).level != -1) { + when (reward) { + Items.CLAY_434, Items.COPPER_ORE_436, Items.TIN_ORE_438, Items.LIMESTONE_3211, Items.BLURITE_ORE_668, Items.IRON_ORE_440, Items.ELEMENTAL_ORE_2892, Items.SILVER_ORE_442, Items.COAL_453 -> if (player.achievementDiaryManager.armour >= 0 && RandomFunction.random(100) < 4) { + amount += 1 + sendMessage(player,"The Varrock armour allows you to mine an additional ore.") + } + Items.GOLD_ORE_444, Items.GRANITE_500G_6979, Items.GRANITE_2KG_6981, Items.GRANITE_5KG_6983, Items.MITHRIL_ORE_447 -> if (player.achievementDiaryManager.armour >= 1 && RandomFunction.random(100) < 3) { + amount += 1 + sendMessage(player, "The Varrock armour allows you to mine an additional ore.") + } + Items.ADAMANTITE_ORE_449 -> if (player.achievementDiaryManager.armour >= 2 && RandomFunction.random(100) < 2) { + amount += 1 + sendMessage(player, "The Varrock armour allows you to mine an additional ore.") + } + } + } + + // If player has mining boost from Shooting Star, roll chance at extra ore + if (hasTimerActive(player)) { + if (RandomFunction.getRandom(5) == 3) { + sendMessage(player, "...you manage to mine a second ore thanks to the Star Sprite.") + amount += 1 + } + } + return amount + } + + private fun calculateReward(player: Player, resource: MiningNode, isMiningEssence: Boolean, isMiningGems: Boolean, reward: Int): Int { + // If the player is mining sandstone or granite, then get size of sandstone/granite and xp reward for that size + var reward = reward + if (resource == MiningNode.SANDSTONE || resource == MiningNode.GRANITE) { + val value = RandomFunction.randomize(if (resource == MiningNode.GRANITE) 3 else 4) + reward += value shl 1 + rewardXP(player, Skills.MINING, value * 10.toDouble()) + } else if (isMiningEssence && getDynLevel(player, Skills.MINING) >= 30) { + reward = 7936 + } else if (isMiningGems) { + reward = RandomFunction.rollWeightedChanceTable(MiningNode.gemRockGems).id + } + return reward + } + + private fun checkReward(player: Player, resource: MiningNode?, tool: SkillingTool): Boolean { + val level = 1 + getDynLevel(player, Skills.MINING) + getFamiliarBoost(player, Skills.MINING) + val hostRatio = Math.random() * (100.0 * resource!!.rate) + var toolRatio = tool.ratio + if(SkillcapePerks.isActive(SkillcapePerks.PRECISION_MINER,player)){ + toolRatio += 0.075 + } + val clientRatio = Math.random() * ((level - resource.level) * (1.0 + toolRatio)) + return hostRatio < clientRatio + } + + fun getDelay(resource: MiningNode) : Int { + return if (resource.id == 2491) 3 else 4 + } + + fun anim(player: Player, tool: SkillingTool) { + if (animationFinished(player)) + animate(player, tool.animation) + } + + fun checkRequirements(player: Player, resource: MiningNode, node: Node): Boolean { + if (getDynLevel(player, Skills.MINING) < resource.level) { + sendMessage(player, "You need a mining level of ${resource.level} to mine this rock.") + return false + } + if (SkillingTool.getPickaxe(player) == null) { + sendMessage(player, "You do not have a pickaxe to use.") + return false + } + if (freeSlots(player) == 0) { + if(resource.identifier == 13.toByte()) { + sendDialogue(player,"Your inventory is too full to hold any more gems.") + return false + } + sendDialogue(player,"Your inventory is too full to hold any more ${ItemDefinition.forId(resource!!.reward).name.lowercase()}.") + return false + } + return node.isActive + } +} 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 301382938..d403ae49b 100644 --- a/Server/src/main/content/global/skill/gather/mining/MiningNode.java +++ b/Server/src/main/content/global/skill/gather/mining/MiningNode.java @@ -275,7 +275,7 @@ public enum MiningNode{ COAL_49(42035,452, (byte) 8), //Gold - GOLD_ORE_0( 2099, 452, (byte) 20), + GOLD_ORE_0( 2099, 452, (byte) 9), GOLD_ORE_1( 2098, 450, (byte) 9), GOLD_ORE_2( 2611, 21298, (byte) 9), GOLD_ORE_3( 2610, 21297, (byte) 9), @@ -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); @@ -522,21 +525,21 @@ public enum MiningNode{ experience = 80.0; rate = 0.70; reward = 447; - level = 55; + level = 50; break; case 11: respawnRate = 400 | 800 << 16; experience = 95.0; rate = 0.85; reward = 449; - level = 70; + level = 60; break; case 12: respawnRate = 1250 | 2500 << 16; experience = 125.0; rate = 0.95; reward = 451; - level = 85; + level = 70; break; case 13: respawnRate = 166 | 175 << 16; @@ -573,13 +576,13 @@ public enum MiningNode{ reward = 12630; level = 46; break; - case 20: - respawnRate = 50 | 100 << 16; - experience = 17.5; - rate = 0.6; - reward = 446; - 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 a485930b3..58464672e 100644 --- a/Server/src/main/content/global/skill/gather/mining/MiningSkillPulse.kt +++ b/Server/src/main/content/global/skill/gather/mining/MiningSkillPulse.kt @@ -11,6 +11,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 @@ -141,7 +142,7 @@ class MiningSkillPulse(private val player: Player, private val node: Node) : Pul if(reward == Items.CLAY_434){ val bracelet = getItemFromEquipment(player, EquipmentSlot.HANDS) if(bracelet != null && bracelet.id == Items.BRACELET_OF_CLAY_11074){ - var charges = player.getAttribute("jewellery-charges:bracelet-of-clay", 28); + var charges = player.getAttribute("jewellery-charges:bracelet-of-clay", 28) charges-- reward = Items.SOFT_CLAY_1761 sendMessage(player, "Your bracelet of clay softens the clay for you.") @@ -164,10 +165,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) { @@ -178,7 +178,7 @@ class MiningSkillPulse(private val player: Player, private val node: Node) : Pul chance = (chance / 1.5).toInt() altered = true } - val necklace = getItemFromEquipment(player, EquipmentSlot.AMULET) + val necklace = getItemFromEquipment(player, EquipmentSlot.NECK) if (necklace != null && necklace.id in 1705..1713) { chance = (chance / 1.5).toInt() altered = true @@ -225,7 +225,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 new file mode 100644 index 000000000..4ca385088 --- /dev/null +++ b/Server/src/main/content/global/skill/gather/woodcutting/WoodcuttingListener.kt @@ -0,0 +1,276 @@ +package content.global.skill.gather.woodcutting + +import content.data.skill.SkillingPets +import content.data.skill.SkillingTool +import content.data.tables.BirdNest +import content.global.skill.farming.FarmingPatch.Companion.forObject +import content.global.skill.firemaking.Log +import content.global.skill.skillcapeperks.SkillcapePerks +import content.global.skill.skillcapeperks.SkillcapePerks.Companion.isActive +import content.region.misc.miscellania.dialogue.KjallakOnChopDialogue +import core.api.* +import core.cache.def.impl.ItemDefinition +import core.game.container.impl.EquipmentContainer +import core.game.event.ResourceProducedEvent +import core.game.interaction.Clocks +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.impl.Projectile +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.entity.skill.Skills +import core.game.node.item.Item +import core.game.node.scenery.Scenery +import core.game.node.scenery.SceneryBuilder +import core.game.system.command.sets.STATS_BASE +import core.game.system.command.sets.STATS_LOGS +import core.game.world.map.RegionManager +import core.tools.RandomFunction +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import org.rs09.consts.Sounds +import org.rs09.consts.Sounds.TREE_FALLING_2734 +import java.util.* +import kotlin.streams.toList + +class WoodcuttingListener : InteractionListener { + private val woodcuttingSounds = intArrayOf( + Sounds.WOODCUTTING_HIT_3038, + Sounds.WOODCUTTING_HIT_3039, + Sounds.WOODCUTTING_HIT_3040, + Sounds.WOODCUTTING_HIT_3041, + Sounds.WOODCUTTING_HIT_3042 + ) + + override fun defineListeners() { + defineInteraction( + IntType.SCENERY, + ids = WoodcuttingNode.values().map { it.id }.toIntArray(), + "chop-down", "chop", "chop down", "cut down", + persistent = true, + allowedDistance = 1, + handler = ::handleWoodcutting + ) + } + + private fun handleWoodcutting(player: Player, node: Node, state: Int) : Boolean { + val resource = WoodcuttingNode.forId(node.id) + val tool = SkillingTool.getHatchet(player) + + if (!finishedMoving(player)) + return restartScript(player) + + if (state == 0) { + if (!checkWoodcuttingRequirements(player, resource, node)) { + return clearScripts(player) + } + sendMessage(player, "You swing your axe at the tree...") + } + + if (clockReady(player, Clocks.SKILLING)) { + animateWoodcutting(player) + if (!checkReward(player, resource, tool)) + return delayClock(player, Clocks.SKILLING, 3) + + val reward = resource.getReward() + val rewardAmount: Int + + if (tool.id == Items.INFERNO_ADZE_13661 && reward != Items.BARK_3239 && RandomFunction.roll(4)) { + sendMessage(player, "You chop some logs. The heat of the inferno adze incinerates them.") + Projectile.create( + player, null, + 1776, + 35, 30, + 20, 25 + ).transform( + player, + player.location.transform(2, 0, 0), + true, + 25, 25 + ).send() + + //add woodcutting experience + player.getSkills().addExperience(Skills.WOODCUTTING, resource.getExperience()) + + //nullcheck the fire, and only if it exists award the firemaking XP + val fire = Log.forId(reward) + if (fire != null) { + player.getSkills().addExperience(Skills.FIREMAKING, fire.getXp()) + } + + delayClock(player, Clocks.SKILLING, 3) + return rollDepletion(player, node.asScenery(), resource) + } + + 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) { + 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()) + ".") + } + + //give the reward + player.inventory.add(Item(reward, rewardAmount)) + player.dispatch(ResourceProducedEvent(reward, rewardAmount, node, -1)) + var cutLogs = player.getAttribute("$STATS_BASE:$STATS_LOGS", 0) + player.setAttribute("/save:$STATS_BASE:$STATS_LOGS", ++cutLogs) + + //calculate bonus bird nest for mining + val chance = 282 + if (RandomFunction.random(chance) == chance / 2) { + if (isActive(SkillcapePerks.NEST_HUNTER, player)) { + if (!player.inventory.add(BirdNest.getRandomNest(false).nest)) { + BirdNest.drop(player) + } + } else { + BirdNest.drop(player) + } + } + } + + delayClock(player, Clocks.SKILLING, 3) + rollDepletion(player, node.asScenery(), resource) + if (!checkWoodcuttingRequirements(player, resource, node)) { + return clearScripts(player) + } + } + return keepRunning(player) + } + + private fun rollDepletion(player: Player, node: Scenery, resource: WoodcuttingNode): Boolean { + //transform to depleted version + //OSRS and RS3 Wikis both agree: All trees present in 2009 are a 1/8 fell chance, aside from normal trees/dead trees which are 100% + //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) || listOf(2, 3, 4, 6).contains(resource.identifier.toInt())){ + if (resource.isFarming()) { + val fPatch = forObject(node.asScenery()) + if (fPatch != null) { + val patch = fPatch.getPatchFor(player) + patch.setCurrentState(patch.getCurrentState() + 1) + node.isActive = false + playAudio(player, TREE_FALLING_2734) + queueScript(player, 2, QueueStrength.SOFT) { + node.isActive = true + return@queueScript stopExecuting(player) + } + } + return true + } + if (resource.getEmptyId() > -1) { + SceneryBuilder.replace(node, node.transform(resource.getEmptyId()), resource.getRespawnDuration()) + } else { + SceneryBuilder.replace(node, node.transform(0), resource.getRespawnDuration()) + } + node.setActive(false) + playAudio(player, TREE_FALLING_2734) + return true + } + } + return false + } + + private fun checkReward(player: Player, resource: WoodcuttingNode, tool: SkillingTool): Boolean { + val skill = Skills.WOODCUTTING + val level: Int = player.getSkills().getLevel(skill) + player.getFamiliarManager().getBoost(skill) + val hostRatio = RandomFunction.randomDouble(100.0) + val lowMod: Double = if (tool == SkillingTool.BLACK_AXE) resource.tierModLow / 2 else resource.tierModLow + val low: Double = resource.baseLow + tool.ordinal * lowMod + val highMod: Double = if (tool == SkillingTool.BLACK_AXE) resource.tierModHigh / 2 else resource.tierModHigh + val high: Double = resource.baseHigh + tool.ordinal * highMod + val clientRatio = RandomFunction.getSkillSuccessChance(low, high, level) + return hostRatio < clientRatio + } + + fun animateWoodcutting(player: Player) { + if (!player.animator.isAnimating) { + player.animate(SkillingTool.getHatchet(player).animation) + val playersAroundMe: List = RegionManager.getLocalPlayers(player, 2) + .stream() + .filter { p: Player -> p.username != player.username } + .toList() + val soundIndex = RandomFunction.random(0, woodcuttingSounds.size) + + for (p in playersAroundMe) { + playAudio(p, woodcuttingSounds[soundIndex]) + } + } + } + + fun checkWoodcuttingRequirements(player: Player, resource: WoodcuttingNode, node: Node): Boolean { + var regionId = player.location.regionId + if (regionId == 10300 || regionId == 10044) { //miscellania then etceteria, respectively. + var npc = if (regionId == 10300) NPCs.CARPENTER_KJALLAK_3916 else NPCs.LUMBERJACK_LEIF_1395 + openDialogue(player, KjallakOnChopDialogue(), NPC(npc, player.location)) + return false + } + if (player.getSkills().getLevel(Skills.WOODCUTTING) < resource.getLevel()) { + player.getPacketDispatch().sendMessage("You need a woodcutting level of " + resource.getLevel() + " to chop this tree.") + return false + } + if (SkillingTool.getHatchet(player) == null) { + player.packetDispatch.sendMessage("You do not have an axe to use.") + 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 + } + return node.isActive + } + + + private fun calculateRewardAmount(player: Player, reward: Int): Int { + var amount = 1 + + // 3239: Hollow tree (bark) 10% chance of obtaining + if (reward == 3239 && RandomFunction.random(100) >= 10) { + amount = 0 + } + + // Seers village medium reward - extra normal log while in seer's village + if (reward == 1511 && player.getAchievementDiaryManager().getDiary(DiaryType.SEERS_VILLAGE).isComplete(1) && player.getViewport().getRegion().getId() == 10806) { + amount = 2 + } + return amount + } + + private fun calculateExperience(player: Player, resource: WoodcuttingNode, amount: Int): Double { + var amount = amount + var experience: Double = resource.getExperience() + val reward = resource.reward + if (player.getLocation().getRegionId() == 10300) { + return 1.0 + } + + // Bark + if (reward == Items.BARK_3239) { + // If we receive the item, give the experience points + if (amount >= 1) { + experience = 82.5 + } else { + return 0.0 + } + } + + // Seers village medium reward - extra 10% xp from maples while wearing headband + if (reward == 1517 && player.getAchievementDiaryManager().getDiary(DiaryType.SEERS_VILLAGE).isComplete(1) && player.getEquipment().get(EquipmentContainer.SLOT_HAT) != null && player.getEquipment().get(EquipmentContainer.SLOT_HAT).getId() == 14631) { + experience *= 1.10 + } + return experience * amount + } +} diff --git a/Server/src/main/content/global/skill/gather/woodcutting/WoodcuttingNode.java b/Server/src/main/content/global/skill/gather/woodcutting/WoodcuttingNode.java index 9415b4b2c..ea769da8a 100644 --- a/Server/src/main/content/global/skill/gather/woodcutting/WoodcuttingNode.java +++ b/Server/src/main/content/global/skill/gather/woodcutting/WoodcuttingNode.java @@ -141,10 +141,10 @@ public enum WoodcuttingNode { double experience,rate; public byte identifier; boolean farming; - double baseLow = 64; - double baseHigh = 200; - double tierModLow = 32; - double tierModHigh = 100; + public double baseLow = 2; + public double baseHigh = 6; + public double tierModLow = 1; + public double tierModHigh = 3; WoodcuttingNode(int full, int empty,byte identifier){ this.full = full; this.empty = empty; @@ -161,6 +161,10 @@ public enum WoodcuttingNode { rate = 0.05; experience = 25.0; level = 1; + baseLow = 64; + baseHigh = 200; + tierModLow = 32; + tierModHigh = 100; break; case 5: reward = 1511; @@ -169,6 +173,10 @@ public enum WoodcuttingNode { experience = 100; level = 1; this.rewardAmount = 2; + baseLow = 64; + baseHigh = 200; + tierModLow = 32; + tierModHigh = 100; break; case 6: reward = 2862; @@ -176,6 +184,10 @@ public enum WoodcuttingNode { rate = 0.05; experience = 25.0; level = 1; + baseLow = 64; + baseHigh = 200; + tierModLow = 32; + tierModHigh = 100; break; case 7: reward = 1521; @@ -253,7 +265,7 @@ public enum WoodcuttingNode { reward = 10810; respawnRate = 75 | 130 << 16; rate = 0.73; - experience = 140.2; + experience = 40.0; level = 54; rewardAmount = 35; baseLow = 6; @@ -308,6 +320,10 @@ public enum WoodcuttingNode { experience = 25.0; level = 36; rewardAmount = Integer.MAX_VALUE; + baseLow = 255; + baseHigh = 255; + tierModLow = 0; + tierModHigh = 0; break; case 30: reward = 3692; @@ -332,6 +348,10 @@ public enum WoodcuttingNode { experience = 37.5; level = 15; rewardAmount = 10; + baseLow = 32; + baseHigh = 100; + tierModLow = 16; + tierModHigh = 50; break; case 20: reward = 1519; @@ -340,6 +360,10 @@ public enum WoodcuttingNode { experience = 67.8; level = 30; rewardAmount = 20; + baseLow = 16; + baseHigh = 50; + tierModLow = 8; + tierModHigh = 25; break; case 21: reward = 1517; @@ -348,6 +372,10 @@ public enum WoodcuttingNode { experience = 100.0; level = 45; rewardAmount = 30; + baseLow = 8; + baseHigh = 25; + tierModLow = 4; + tierModHigh = 12.5; break; case 22: reward = 1515; @@ -356,6 +384,10 @@ public enum WoodcuttingNode { experience = 175.0; level = 60; rewardAmount = 40; + baseLow = 4; + baseHigh = 12.5; + tierModLow = 2; + tierModHigh = 6.25; break; case 23: reward = 1513; @@ -364,6 +396,10 @@ public enum WoodcuttingNode { experience = 250.0; level = 75; rewardAmount = 50; + baseLow = 2; + baseHigh = 6; + tierModLow = 1; + tierModHigh = 3; break; } } 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 43e220666..bdb54f1fe 100644 --- a/Server/src/main/content/global/skill/gather/woodcutting/WoodcuttingSkillPulse.java +++ b/Server/src/main/content/global/skill/gather/woodcutting/WoodcuttingSkillPulse.java @@ -29,6 +29,7 @@ import org.rs09.consts.Sounds; import java.util.List; import java.util.stream.Collectors; +import static core.api.ContentAPIKt.playAudio; import static core.game.system.command.sets.StatAttributeKeysKt.STATS_BASE; import static core.game.system.command.sets.StatAttributeKeysKt.STATS_LOGS; @@ -123,11 +124,9 @@ public class WoodcuttingSkillPulse extends Pulse { int soundIndex = RandomFunction.random(0, woodcuttingSounds.length); - player.getAudioManager().send( - new Audio(woodcuttingSounds[soundIndex]), - playersAroundMe, - player.getLocation() - ); + for (Player p : playersAroundMe) { + playAudio(p, woodcuttingSounds[soundIndex]); + } } } @@ -165,9 +164,10 @@ public class WoodcuttingSkillPulse extends Pulse { player.getSkills().addExperience(Skills.WOODCUTTING, experience, true); - //send the message for the resource reward + //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() + "."); } @@ -204,7 +204,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; @@ -216,7 +216,7 @@ public class WoodcuttingSkillPulse extends Pulse { } node.setActive(false); - player.getAudioManager().send(Sounds.TREE_FALLING_2734); + playAudio(player, Sounds.TREE_FALLING_2734); return true; } } diff --git a/Server/src/main/content/global/skill/herblore/BarbarianMixListener.kt b/Server/src/main/content/global/skill/herblore/BarbarianMixListener.kt new file mode 100644 index 000000000..20077f2d3 --- /dev/null +++ b/Server/src/main/content/global/skill/herblore/BarbarianMixListener.kt @@ -0,0 +1,59 @@ +package content.global.skill.herblore + +import content.global.skill.herblore.BarbarianPotion +import core.game.node.entity.skill.Skills +import core.game.node.entity.player.Player +import core.game.node.item.Item +import core.game.node.Node +import core.game.interaction.InteractionListener +import core.game.interaction.IntType +import core.api.hasLevelStat +import core.api.sendMessage +import core.api.removeItem +import core.api.addItem +import core.api.rewardXP + +/** + * Represents the barbarian mixing listener. + * @author 'Vexia + * @author treevar + * @version 2.0 + */ + +class BarbarianMixListener : InteractionListener { + override fun defineListeners(){ + for (potion in BarbarianPotion.values()) { + if (potion.isBoth()) { + onUseWith(IntType.ITEM, potion.getItem(), 11324) { player, used, with -> //Roe + handle(player, used, with); + } + } + onUseWith(IntType.ITEM, potion.getItem(), 11326) { player, used, with -> //Caviar + handle(player, used, with); + } + } + } + + fun handle(player: Player, inputPotion: Node, egg: Node): Boolean { + val potion: BarbarianPotion? = BarbarianPotion.forId(inputPotion.getId()) + if(potion == null){ + return false + } + + if (!hasLevelStat(player, Skills.HERBLORE, potion.getLevel())) { + sendMessage(player, "You need a herblore level of " + potion.getLevel().toString() + " to make this mix.") + return true + } + + if(!removeItem(player, potion.getItem())) { //Remove input potion + return false + } + if(!removeItem(player, egg.getId())) { //Remove egg used + addItem(player, potion.getItem()) //Add potion back to inventory if we can't remove the egg + return false + } + addItem(player, potion.getProduct()) //Add output potion + rewardXP(player, Skills.HERBLORE, potion.getExp()) //Add exp + return true + } +} diff --git a/Server/src/main/content/global/skill/herblore/BarbarianMixPlugin.java b/Server/src/main/content/global/skill/herblore/BarbarianMixPlugin.java deleted file mode 100644 index f82eda97c..000000000 --- a/Server/src/main/content/global/skill/herblore/BarbarianMixPlugin.java +++ /dev/null @@ -1,62 +0,0 @@ -package content.global.skill.herblore; - -import core.game.node.entity.skill.Skills; -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 barbarian mixing plugin. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class BarbarianMixPlugin extends UseWithHandler { - - /** - * Constructs a new {@code BarbarianMixPlugin} {@Code Object}. - */ - public BarbarianMixPlugin() { - super(123, 177, 4846, 117, 129, 3012, 135, 3036, 9743, 141, 147, 183, 143, 3020, 10002, 159, 3028, 165, 2456, 171, 3044, 191); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - addHandler(11324, ITEM_TYPE, this); - addHandler(11326, ITEM_TYPE, this); - return this; - } - - @Override - public boolean handle(NodeUsageEvent event) { - final Player player = event.getPlayer(); - if (player == null) { - return true; - } - final BarbarianPotion potion = BarbarianPotion.forId(((Item) event.getUsedWith()).getId()); - if (potion == null) { - return true; - } - if (!potion.isBoth() && event.getUsedItem().getId() == 11324 || ((Item) event.getUsedWith()).getId() == 11324) { - return true; - } - if (player.getSkills().getLevel(Skills.HERBLORE) < potion.getLevel()) { - player.getPacketDispatch().sendMessage("You need a herblore level of " + potion.getLevel() + " to make this mix."); - return true; - } - if (potion.getItem() == event.getUsedItem().getId() || potion.getItem() == ((Item) event.getUsedWith()).getId()) { - player.getInventory().remove(new Item(potion.getItem(), 1)); - player.getInventory().add(new Item(potion.getProduct(), 1)); - player.getSkills().addExperience(Skills.HERBLORE, potion.getExp(), true); - if (potion.isBoth()) { - player.getInventory().remove(new Item(11324, 1)); - } - player.getInventory().remove(new Item(11326, 1)); - } - return true; - } - -} diff --git a/Server/src/main/content/global/skill/herblore/BarbarianPotion.java b/Server/src/main/content/global/skill/herblore/BarbarianPotion.java deleted file mode 100644 index 623df9590..000000000 --- a/Server/src/main/content/global/skill/herblore/BarbarianPotion.java +++ /dev/null @@ -1,103 +0,0 @@ -package content.global.skill.herblore; - -/** - * Represents the barbarian potion. - * @author 'Vexia - */ -public enum BarbarianPotion { - ATTACK_POTION(123, 4, 11429, 8, true), ANTI_POISION_POTION(177, 6, 11433, 12, true), RELIC(4846, 9, 11437, 14, true), STRENGTH_POTION(117, 14, 11443, 17, true), RESTORE_POTION(129, 24, 11449, 21, true), ENERGY_POTION(3012, 29, 11453, 23, false), DEFENCE_POTION(135, 33, 11457, 25, false), AGILITY_POTION(3036, 37, 11461, 27, false), COMBAT_POTION(9743, 40, 11445, 28, false), PRAYER_POTION(141, 42, 11465, 29, false), SUPER_ATTACK_POTION(147, 47, 11469, 33, false), SUPER_ANTIPOISION_POTION(183, 51, 11473, 35, false), FISHING_POTION(143, 53, 11477, 38, false), SUPER_ENERGY_POTION(3020, 59, 11481, 42, false), HUNTER_POTION(10002, 11517, 58, 40, false), SUPER_STRENGTH_POTION(159, 59, 11485, 42, false), SUPER_RESTORE(3028, 67, 11493, 48, false), SUPER_DEFENCE_POTION(165, 71, 11497, 50, false), ANTIFIRE_POTION(2456, 11505, 75, 53, false), RANGING_POTION(171, 80, 11509, 54, false), MAGIC_POTION(3044, 83, 11513, 57, false), ZAMORAK_BREW(191, 85, 11521, 58, false); - - /** - * Constructs a new {@code BarbarianPotion} {@Code Object}. - * @param item the item. - * @param product the product. - * @param exp the exp. - * @param both if both can be added(roe, cavier). - */ - BarbarianPotion(int item, int level, int product, double exp, boolean both) { - this.item = item; - this.level = level; - this.product = product; - this.exp = exp; - this.both = both; - } - - /** - * The item id. - */ - private int item; - - /** - * The product item id. - */ - private int product; - - /** - * The level required. - */ - private int level; - - /** - * the exp gained. - */ - private double exp; - - /** - * Represents if both Roe & Cavier can be added. - */ - private boolean both; - - /** - * Gets the item. - * @return The item. - */ - public int getItem() { - return item; - } - - /** - * Gets the product. - * @return The product. - */ - public int getProduct() { - return product; - } - - /** - * Gets the level. - * @return The level. - */ - public int getLevel() { - return level; - } - - /** - * Gets the exp. - * @return The exp. - */ - public double getExp() { - return exp; - } - - /** - * Gets the both. - * @return The both. - */ - public boolean isBoth() { - return both; - } - - /** - * Gets the value from the id. - * @param id the id. - * @return the value. - */ - public static BarbarianPotion forId(int id) { - for (BarbarianPotion pot : BarbarianPotion.values()) { - if (pot.getItem() == id) { - return pot; - } - } - return null; - } -} diff --git a/Server/src/main/content/global/skill/herblore/BarbarianPotion.kt b/Server/src/main/content/global/skill/herblore/BarbarianPotion.kt new file mode 100644 index 000000000..5876c4018 --- /dev/null +++ b/Server/src/main/content/global/skill/herblore/BarbarianPotion.kt @@ -0,0 +1,108 @@ +package content.global.skill.herblore; + +/** + * Represents the barbarian potion. + * @author 'Vexia + * @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); + + /** + * Constructs a new {@code BarbarianPotion} {@Code Object}. + * @param item the input potion id. + * @param level the level requirement to make. + * @param product the product potion id. + * @param exp the exp rewarded. + * @param both if both can be added(roe, cavier). + */ + constructor(item: Int, level: Int, product: Int, exp: Double, both: Boolean) { + this.item = item; + this.level = level; + this.product = product; + this.exp = exp; + this.both = both; + } + + /** + * The item id. + */ + private val item: Int; + + /** + * The product item id. + */ + private val product: Int; + + /** + * The level required. + */ + private val level: Int; + + /** + * the exp gained. + */ + private val exp: Double; + + /** + * Represents if both Roe & Cavier can be added. + */ + private val both: Boolean; + + /** + * Gets the item. + * @return The item. + */ + fun getItem(): Int { + return item; + } + + /** + * Gets the product. + * @return The product. + */ + fun getProduct(): Int { + return product; + } + + /** + * Gets the level. + * @return The level. + */ + fun getLevel(): Int { + return level; + } + + /** + * Gets the exp. + * @return The exp. + */ + fun getExp(): Double { + return exp; + } + + /** + * Gets whether roe and caviar can be used. + * @return true if both, false if only caviar. + */ + fun isBoth(): Boolean { + return both; + } + + companion object { + /** + * Gets the barbarian potion from the input potion id + * @param id the id of the input potion. + * @return the barbarian potion that takes the id as input. + */ + @JvmStatic + fun forId(id: Int): BarbarianPotion? { + for (pot in BarbarianPotion.values()) { + if (pot.getItem() == id) { + return pot; + } + } + return null; + } + } +} diff --git a/Server/src/main/content/global/skill/herblore/FinishedPotion.java b/Server/src/main/content/global/skill/herblore/FinishedPotion.java index e78d80432..38a71f84d 100644 --- a/Server/src/main/content/global/skill/herblore/FinishedPotion.java +++ b/Server/src/main/content/global/skill/herblore/FinishedPotion.java @@ -8,34 +8,34 @@ import org.rs09.consts.Items; * @author 'Vexia */ public enum FinishedPotion { - ATTACK_POTION(UnfinishedPotion.GUAM, new Item(221), 3, 25, new Item(121)), - ANTIPOISON_POTION(UnfinishedPotion.MARRENTILL, new Item(235), 5, 37.5, new Item(175)), - STRENGTH_POTION(UnfinishedPotion.TARROMIN, new Item(225), 12, 50, new Item(115)), - RESTORE_POTION(UnfinishedPotion.HARRALANDER, new Item(223), 22, 62.5, new Item(127)), - ENERGY_POTION(UnfinishedPotion.HARRALANDER, new Item(1975), 26, 67.5, new Item(3010)), - DEFENCE_POTION(UnfinishedPotion.RANARR, new Item(239), 30, 45, new Item(133)), - AGILITY_POTION(UnfinishedPotion.TOADFLAX, new Item(2152), 34, 80, new Item(3034)), - COMBAT_POTION(UnfinishedPotion.HARRALANDER, new Item(9736), 36, 84, new Item(9741)), - PRAYER_POTION(UnfinishedPotion.RANARR, new Item(231), 38, 87.5, new Item(139)), - SUMMONING_POTION(UnfinishedPotion.SPIRIT_WEED, new Item(12109), 40, 92, new Item(12142)), - SUPER_ATTACK(UnfinishedPotion.IRIT, new Item(221), 45, 100, new Item(145)), - SUPER_ANTIPOISON(UnfinishedPotion.IRIT, new Item(235), 48, 106.3, new Item(181)), - FISHING_POTION(UnfinishedPotion.AVANTOE, new Item(231), 50, 112.5, new Item(151)), - SUPER_ENERGY(UnfinishedPotion.AVANTOE, new Item(2970), 52, 117.5, new Item(3018)), - HUNTING_POTION(UnfinishedPotion.AVANTOE, new Item(Items.KEBBIT_TEETH_DUST_10111), 53, 120, new Item(10000)), - SUPER_STRENGTH(UnfinishedPotion.KWUARM, new Item(225), 55, 125, new Item(157)), + ATTACK_POTION(UnfinishedPotion.GUAM, new Item(221), 3, 25, new Item(2428)), + ANTIPOISON_POTION(UnfinishedPotion.MARRENTILL, new Item(235), 5, 37.5, new Item(2446)), + STRENGTH_POTION(UnfinishedPotion.TARROMIN, new Item(225), 12, 50, new Item(113)), + RESTORE_POTION(UnfinishedPotion.HARRALANDER, new Item(223), 22, 62.5, new Item(2430)), + ENERGY_POTION(UnfinishedPotion.HARRALANDER, new Item(1975), 26, 67.5, new Item(3008)), + DEFENCE_POTION(UnfinishedPotion.RANARR, new Item(239), 30, 45, new Item(2432)), + AGILITY_POTION(UnfinishedPotion.TOADFLAX, new Item(2152), 34, 80, new Item(3032)), + COMBAT_POTION(UnfinishedPotion.HARRALANDER, new Item(9736), 36, 84, new Item(9739)), + PRAYER_POTION(UnfinishedPotion.RANARR, new Item(231), 38, 87.5, new Item(2434)), + SUMMONING_POTION(UnfinishedPotion.SPIRIT_WEED, new Item(12109), 40, 92, new Item(12140)), + SUPER_ATTACK(UnfinishedPotion.IRIT, new Item(221), 45, 100, new Item(2436)), + SUPER_ANTIPOISON(UnfinishedPotion.IRIT, new Item(235), 48, 106.3, new Item(2448)), + FISHING_POTION(UnfinishedPotion.AVANTOE, new Item(231), 50, 112.5, new Item(2438)), + SUPER_ENERGY(UnfinishedPotion.AVANTOE, new Item(2970), 52, 117.5, new Item(3016)), + HUNTING_POTION(UnfinishedPotion.AVANTOE, new Item(Items.KEBBIT_TEETH_DUST_10111), 53, 120, new Item(9998)), + SUPER_STRENGTH(UnfinishedPotion.KWUARM, new Item(225), 55, 125, new Item(2440)), WEAPON_POISON(UnfinishedPotion.KWUARM, new Item(241), 60, 137.5, new Item(187)), - SUPER_RESTORE(UnfinishedPotion.SNAPDRAGON, new Item(223), 63, 142.5, new Item(3026)), - SUPER_DEFENCE(UnfinishedPotion.CADANTINE, new Item(239), 66, 160, new Item(163)), - ANTIFIRE(UnfinishedPotion.LANTADYME, new Item(241), 69, 157.5, new Item(2454)), - SUPER_RANGING_POTION(UnfinishedPotion.DWARF_WEED, new Item(245), 72, 162.5, new Item(169)), - SUPER_MAGIC(UnfinishedPotion.LANTADYME, new Item(3138), 76, 172.5, new Item(3042)), - ZAMORAK_BREW(UnfinishedPotion.TORSTOL, new Item(247), 78, 175, new Item(189)), - SARADOMIN_BREW(UnfinishedPotion.TOADFLAX, GrindingItem.BIRDS_NEST.getProduct(), 81, 180, new Item(6687)), + SUPER_RESTORE(UnfinishedPotion.SNAPDRAGON, new Item(223), 63, 142.5, new Item(3024)), + SUPER_DEFENCE(UnfinishedPotion.CADANTINE, new Item(239), 66, 160, new Item(2442)), + ANTIFIRE(UnfinishedPotion.LANTADYME, new Item(241), 69, 157.5, new Item(2452)), + SUPER_RANGING_POTION(UnfinishedPotion.DWARF_WEED, new Item(245), 72, 162.5, new Item(2444)), + SUPER_MAGIC(UnfinishedPotion.LANTADYME, new Item(3138), 76, 172.5, new Item(3040)), + ZAMORAK_BREW(UnfinishedPotion.TORSTOL, new Item(247), 78, 175, new Item(2450)), + SARADOMIN_BREW(UnfinishedPotion.TOADFLAX, GrindingItem.BIRDS_NEST.getProduct(), 81, 180, new Item(6685)), STRONG_WEAPON_POISON(UnfinishedPotion.STRONG_WEAPON_POISON, new Item(223), 73, 165, new Item(5937)), SUPER_STRONG_WEAPON_POISON(UnfinishedPotion.SUPER_STRONG_WEAPON_POISON, new Item(6018), 82, 190, new Item(5940)), - STRONG_ANTIPOISON(UnfinishedPotion.STRONG_ANTIPOISON, new Item(6049), 68, 155, new Item(5945)), - SUPER_STRONG_ANTIPOISON(UnfinishedPotion.SUPER_STRONG_ANTIPOISON, new Item(6051), 79, 177.5, new Item(5954)), + STRONG_ANTIPOISON(UnfinishedPotion.STRONG_ANTIPOISON, new Item(6049), 68, 155, new Item(5943)), + SUPER_STRONG_ANTIPOISON(UnfinishedPotion.SUPER_STRONG_ANTIPOISON, new Item(6051), 79, 177.5, new Item(5952)), BLAMISH_OIL(UnfinishedPotion.HARRALANDER, new Item(1581), 25, 80, new Item(1582)); /** diff --git a/Server/src/main/content/global/skill/herblore/GrindItemPlugin.kt b/Server/src/main/content/global/skill/herblore/GrindItemPlugin.kt index 3d7184c06..ec9cd70c4 100644 --- a/Server/src/main/content/global/skill/herblore/GrindItemPlugin.kt +++ b/Server/src/main/content/global/skill/herblore/GrindItemPlugin.kt @@ -1,9 +1,12 @@ package content.global.skill.herblore +import core.api.addItem +import core.api.amountInInventory +import core.api.removeItem +import core.game.dialogue.SkillDialogueHandler import core.game.interaction.NodeUsageEvent import core.game.interaction.UseWithHandler import core.game.node.entity.skill.SkillPulse -import content.global.skill.herblore.GrindingItem import core.game.node.item.Item import core.game.world.update.flag.context.Animation import core.net.packet.PacketRepository @@ -11,7 +14,9 @@ import core.net.packet.context.ChildPositionContext import core.net.packet.out.RepositionChild import core.plugin.Initializable import core.plugin.Plugin -import core.game.dialogue.SkillDialogueHandler +import org.rs09.consts.Items +import kotlin.math.ceil +import kotlin.math.roundToInt /** * plugin used to handle the grinding of an item. @@ -30,14 +35,19 @@ class GrindItemPlugin : UseWithHandler(233) { override fun handle(event: NodeUsageEvent): Boolean { val grind = GrindingItem.forItem(if (event.usedItem.id == 233) event.baseItem else event.usedItem) - val handler = object : SkillDialogueHandler(event.player,SkillDialogue.ONE_OPTION,grind.product){ + val handler = object : SkillDialogueHandler(event.player,SkillDialogue.ONE_OPTION,grind.product) { override fun create(amount: Int, index: Int) { - player.pulseManager.run(object : SkillPulse(player,event.usedItem){ + player.pulseManager.run(object : SkillPulse(player,event.usedItem) { var amt = 0 init { amt = amount - if(amt > player.inventory.getAmount(node)){ - amt = player.inventory.getAmount(node) + if(amt > amountInInventory(player, node.id)) { + amt = amountInInventory(player, node.id) + } + if (node.id == FISHING_BAIT) { + if(amt > (amountInInventory(player, node.id) / 10)) { + amt = ceil(amountInInventory(player, node.id).toDouble() / 10).roundToInt() + } } super.setDelay(2) } @@ -50,8 +60,20 @@ class GrindItemPlugin : UseWithHandler(233) { } override fun reward(): Boolean { - if(player.inventory.remove(node)){ - player.inventory.add(GrindingItem.forItem(node).product) + if (node.id == Items.FISHING_BAIT_313) { + var quantity = 0 + quantity = if (amountInInventory(player, FISHING_BAIT) >= 10) { + 10 + } else { + amountInInventory(player, FISHING_BAIT) + } + if (removeItem(player, Item(node.id, quantity))) { + addItem(player, GrindingItem.forItem(node).product.id, quantity) + } + } else { + if (removeItem(player, Item(node.id, 1))) { + addItem(player, GrindingItem.forItem(node).product.id) + } } amt-- return amt <= 0 @@ -61,7 +83,7 @@ class GrindItemPlugin : UseWithHandler(233) { } override fun getAll(index: Int): Int { - return player.inventory.getAmount(event.usedItem) + return amountInInventory(player, event.usedItem.id) } } handler.open() @@ -74,5 +96,6 @@ class GrindItemPlugin : UseWithHandler(233) { * Represents the animation to use. */ private val ANIMATION = Animation(364) + private const val FISHING_BAIT = Items.FISHING_BAIT_313 } } diff --git a/Server/src/main/content/global/skill/herblore/GrindingItem.java b/Server/src/main/content/global/skill/herblore/GrindingItem.java index 7f8db39df..83b1f3efe 100644 --- a/Server/src/main/content/global/skill/herblore/GrindingItem.java +++ b/Server/src/main/content/global/skill/herblore/GrindingItem.java @@ -8,21 +8,21 @@ import core.game.node.item.Item; * @author Vexia */ public enum GrindingItem { - UNICORN_HORN(new Item[] { new Item(237) },new Item(235)), - KEBBIT_TEETH(new Item[] { new Item(10109) }, new Item(10111)), - BIRDS_NEST(new Item[] { new Item(5070), new Item(5071), new Item(5072), new Item(5073), new Item(5074), new Item(5075) }, new Item(6693)), - GOAT_HORN(new Item[] { new Item(9735) }, new Item(9736)), - MUD_RUNE(new Item[] { new Item(4698) }, new Item(9594)), - ASHES(new Item[] { new Item(592) }, new Item(8865)), - POISON_KARAMBWAN(new Item[] { new Item(3146) }, new Item(3152)), - FISHING_BAIT(new Item[] { new Item(313) }, new Item(12129)), - SEAWEED(new Item[] { new Item(401) }, new Item(6683)), - BAT_BONES(new Item[] { new Item(530) }, new Item(2391)), - CHARCOAL(new Item[] { new Item(973) }, new Item(704)), - ASTRAL_RUNE_SHARDS(new Item[] { new Item(11156) }, new Item(11155)), - GARLIC(new Item[] { new Item(1550) }, new Item(4668)), - DRAGON_SCALE(new Item[] { new Item(243) }, new Item(241)), - ANCHOVIES(new Item[] { new Item(319) }, new Item(11266)), + UNICORN_HORN(new Item[] { new Item(Items.UNICORN_HORN_237) }, new Item(Items.UNICORN_HORN_DUST_235)), + KEBBIT_TEETH(new Item[] { new Item(Items.KEBBIT_TEETH_10109) }, new Item(Items.KEBBIT_TEETH_DUST_10111)), + BIRDS_NEST(new Item[] { new Item(Items.BIRDS_NEST_5075) }, new Item(Items.CRUSHED_NEST_6693)), + GOAT_HORN(new Item[] { new Item(Items.DESERT_GOAT_HORN_9735) }, new Item(Items.GOAT_HORN_DUST_9736)), + MUD_RUNE(new Item[] { new Item(Items.MUD_RUNE_4698) }, new Item(Items.GROUND_MUD_RUNES_9594)), + ASHES(new Item[] { new Item(Items.ASHES_592) }, new Item(Items.GROUND_ASHES_8865)), + POISON_KARAMBWAN(new Item[] { new Item(Items.POISON_KARAMBWAN_3146) }, new Item(Items.KARAMBWAN_PASTE_3152)), + FISHING_BAIT(new Item[] { new Item(Items.FISHING_BAIT_313) }, new Item(Items.GROUND_FISHING_BAIT_12129)), + SEAWEED(new Item[] { new Item(Items.SEAWEED_401) }, new Item(Items.GROUND_SEAWEED_6683)), + BAT_BONES(new Item[] { new Item(Items.BAT_BONES_530) }, new Item(Items.GROUND_BAT_BONES_2391)), + CHARCOAL(new Item[] { new Item(Items.CHARCOAL_973) }, new Item(Items.GROUND_CHARCOAL_704)), + ASTRAL_RUNE_SHARDS(new Item[] { new Item(Items.ASTRAL_RUNE_SHARDS_11156) }, new Item(Items.GROUND_ASTRAL_RUNE_11155)), + GARLIC(new Item[] { new Item(Items.GARLIC_1550) }, new Item(Items.GARLIC_POWDER_4668)), + DRAGON_SCALE(new Item[] { new Item(Items.BLUE_DRAGON_SCALE_243) }, new Item(Items.DRAGON_SCALE_DUST_241)), + ANCHOVIES(new Item[] { new Item(Items.ANCHOVIES_319) }, new Item(Items.ANCHOVY_PASTE_11266)), CHOCOLATE_BAR(new Item[] {new Item(Items.CHOCOLATE_BAR_1973)}, new Item(Items.CHOCOLATE_DUST_1975)), GUAM_LEAF(new Item[] { new Item(Items.CLEAN_GUAM_249) }, new Item(Items.GROUND_GUAM_6681)); diff --git a/Server/src/main/content/global/skill/herblore/HerbCleanListener.kt b/Server/src/main/content/global/skill/herblore/HerbCleanListener.kt new file mode 100644 index 000000000..9136cd3ed --- /dev/null +++ b/Server/src/main/content/global/skill/herblore/HerbCleanListener.kt @@ -0,0 +1,57 @@ +package content.global.skill.herblore + +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 java.util.* +import content.data.Quests + +import core.game.system.task.Pulse + + +/** + * Dirty herb cleaning listener + * @author Woah + */ +class HerbCleanListener : InteractionListener { + override fun defineListeners() { + on(IntType.ITEM, "clean") { player, node -> + lock(player, 1) + 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) { + sendMessage(player, "You cannot clean this herb. You need a Herblore level of " + herb.level + " to attempt this.") + return@on true + } + + val exp = herb.experience + + player.pulseManager.run(object : Pulse() { + var counter = 0 + override fun pulse(): Boolean { + if (amountInInventory(player, node.asItem().id) == 0 ) + return true + if(removeItem(player, node.asItem().id)) { + addItem(player, herb.product.id) + rewardXP(player, Skills.HERBLORE, exp) + playAudio(player, 5153) + //sendMessage(player, "You clean the dirt from the " + herb.product.name.lowercase(Locale.getDefault()).replace("clean", "").trim { it <= ' ' } + " leaf.") + } + return false + } + }) + return@on true + + /* Vanilla actions, replaced by pulse above + replaceSlot(player, node.asItem().slot, herb.product, node.asItem()) + rewardXP(player, Skills.HERBLORE, exp) + playAudio(player, 5153) + sendMessage(player, "You clean the dirt from the " + herb.product.name.lowercase(Locale.getDefault()).replace("clean", "").trim { it <= ' ' } + " leaf.") + return@on true + */ + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/herblore/HerbCleanPlugin.java b/Server/src/main/content/global/skill/herblore/HerbCleanPlugin.java deleted file mode 100644 index 5a7ff46d2..000000000 --- a/Server/src/main/content/global/skill/herblore/HerbCleanPlugin.java +++ /dev/null @@ -1,57 +0,0 @@ -package content.global.skill.herblore; - -import core.api.Container; -import static core.api.ContentAPIKt.*; -import core.cache.def.impl.ItemDefinition; -import core.game.interaction.OptionHandler; -import core.game.node.Node; -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 core.plugin.Plugin; -import core.tools.RandomFunction; - -/** - * Represents the cleaning of a dirty herb. - * @author Vexia - * @version 1.0 - */ -@Initializable -public final class HerbCleanPlugin extends OptionHandler { - private static final int[] SFX_IDS = new int[] { 5153, 5155, 5157 }; - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ItemDefinition.setOptionHandler("clean", this); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - if (!player.getQuestRepository().isComplete("Druidic Ritual")) { - player.getPacketDispatch().sendMessage("You must complete the Druidic Ritual quest before you can use Herblore."); - return true; - } - final Herbs herb = Herbs.forItem((Item) node); - if (player.getSkills().getLevel(Skills.HERBLORE) < herb.getLevel()) { - player.getPacketDispatch().sendMessage("You need level " + herb.getLevel() + " Herblore to clean the " + herb.getProduct().getName().replace("Clean", "Grimy") + "."); - return true; - } - double exp = herb.getExperience(); - if (removeItem(player, node.asItem(), Container.INVENTORY)){ - player.getSkills().addExperience(Skills.HERBLORE, exp, true); - addItem(player, herb.getProduct().getId(), 1, Container.INVENTORY); - player.getAudioManager().send(SFX_IDS[RandomFunction.random(SFX_IDS.length)], 1); - player.getPacketDispatch().sendMessage("You clean the dirt from the " + herb.getProduct().getName().toLowerCase().replace("clean", "").trim() + " leaf."); - } - player.lock(1); - return true; - } - - @Override - public boolean isWalk() { - return false; - } - -} diff --git a/Server/src/main/content/global/skill/herblore/HerbTarPulse.java b/Server/src/main/content/global/skill/herblore/HerbTarPulse.java index 45793be82..cbc66e0f8 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. @@ -25,7 +26,7 @@ public final class HerbTarPulse extends SkillPulse { /** * Represents the swamp tar item. */ - private static final Item SWAMP_TAR = new Item(1939, 15); + private static final Item SWAMP_TAR = new Item(1939, 45); /** * Represents the tar to make. @@ -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; } @@ -65,7 +66,7 @@ public final class HerbTarPulse extends SkillPulse { return false; } if (!player.getInventory().containsItem(SWAMP_TAR)) { - player.getPacketDispatch().sendMessage("You need at least 15 swamp tar in order to do this."); + player.getPacketDispatch().sendMessage("You need at least 45 swamp tar in order to do this."); return false; } return true; @@ -83,7 +84,7 @@ public final class HerbTarPulse extends SkillPulse { return false; } if (player.getInventory().containsItem(SWAMP_TAR) && player.getInventory().containsItem(tar.getIngredient()) && player.getInventory().remove(SWAMP_TAR) && player.getInventory().remove(tar.getIngredient())) { - final Item item = new Item(tar.getTar().getId(), 15); + final Item item = new Item(tar.getTar().getId(), 45); player.getInventory().add(item); player.getSkills().addExperience(Skills.HERBLORE, tar.getExperience(), true); player.getPacketDispatch().sendMessage("You add the " + tar.getIngredient().getName().toLowerCase().replace("clean", "").trim() + " to the swamp tar."); diff --git a/Server/src/main/content/global/skill/herblore/HerblorePulse.java b/Server/src/main/content/global/skill/herblore/HerblorePulse.java index 800efe53e..0f859ad23 100644 --- a/Server/src/main/content/global/skill/herblore/HerblorePulse.java +++ b/Server/src/main/content/global/skill/herblore/HerblorePulse.java @@ -6,6 +6,14 @@ import core.game.node.entity.player.Player; import core.game.node.item.Item; import core.game.world.update.flag.context.Animation; import core.tools.StringUtils; +import content.global.skill.skillcapeperks.*; +import content.data.consumables.Consumables; +import core.tools.RandomFunction; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.playAudio; +import content.data.Quests; + /** * Represents the skill pulse used to handle the creating of potions. @@ -62,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; } @@ -117,7 +125,7 @@ public final class HerblorePulse extends SkillPulse { final Item item = potion.getProduct(); player.getInventory().add(item); player.getPacketDispatch().sendMessage("You put the" + StringUtils.formatDisplayName(potion.getIngredient().getName().toLowerCase().replace("clean", "")) + " leaf into the vial of water."); - player.getAudioManager().send(2608); + playAudio(player, Sounds.GRIND_2608); if (cycles++ == 3) { player.animate(ANIMATION); cycles = 0; @@ -130,11 +138,19 @@ public final class HerblorePulse extends SkillPulse { */ public void handleFinished() { if ((player.getInventory().containsItem(potion.getBase()) && player.getInventory().containsItem(potion.getIngredient())) && player.getInventory().remove(potion.getBase(), potion.getIngredient())) { - final Item item = potion.getProduct(); + Item item = potion.getProduct(); + if (SkillcapePerks.isActive (SkillcapePerks.BREWMASTER, player)) { + if (RandomFunction.random(100) < 15) { + Consumables consum = Consumables.getConsumableById (item.getId()); + if (consum != null) + item = new Item (consum.getConsumable().getIds()[0], item.getAmount()); + player.sendMessage ("Due to your expertise, you manage to make an extra dose."); + } + } player.getInventory().add(item); player.getSkills().addExperience(Skills.HERBLORE, potion.getExperience(), true); player.getPacketDispatch().sendMessage("You mix the " + potion.getIngredient().getName().toLowerCase() + " into your potion."); - player.getAudioManager().send(2608); + playAudio(player, Sounds.GRIND_2608); player.animate(ANIMATION); } } diff --git a/Server/src/main/content/global/skill/herblore/Herbs.java b/Server/src/main/content/global/skill/herblore/Herbs.java index 5ee4959eb..1973162f1 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), 0, 8, new Item(1534)); /** * Represents the herb item. diff --git a/Server/src/main/content/global/skill/herblore/PotionDecantingPlugin.java b/Server/src/main/content/global/skill/herblore/PotionDecantingPlugin.java deleted file mode 100644 index 884d4917b..000000000 --- a/Server/src/main/content/global/skill/herblore/PotionDecantingPlugin.java +++ /dev/null @@ -1,332 +0,0 @@ -package content.global.skill.herblore; - -import core.cache.def.impl.ItemDefinition; -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; - -import java.util.HashMap; -import java.util.Map; - -/** - * Represents the plugin used to handle the decanting of potions. - * @author 'Vexia - */ -@Initializable -public final class PotionDecantingPlugin extends UseWithHandler { - - /** - * Represents the empty item vial. - */ - private static final Item EMPTY_VIAL = new Item(229, 1); - - /** - * Represents the array of potions to utilitize. - */ - public static final int[] POTIONS = new int[] { - /* empty vial */229, - /* Strength potion(4) */113, - /* Strength potion(3) */115, - /* Strength potion(2) */117, - /* Strength potion(1) */119, - /* Attack potion(3) */121, - /* Attack potion(2) */123, - /* Attack potion(1) */125, - /* Restore potion(3) */127, - /* Restore potion(2) */129, - /* Restore potion(1) */131, - /* Defence potion(3) */133, - /* Defence potion(2) */135, - /* Defence potion(1) */137, - /* Prayer potion(3) */139, - /* Prayer potion(2) */141, - /* Prayer potion(1) */143, - /* Super attack(3) */145, - /* Super attack(2) */147, - /* Super attack(1) */149, - /* Fishing potion(3) */151, - /* Fishing potion(2) */153, - /* Fishing potion(1) */155, - /* Super strength(3) */157, - /* Super strength(2) */159, - /* Super strength(1) */161, - /* Super defence(3) */163, - /* Super defence(2) */165, - /* Super defence(1) */167, - /* Ranging potion(3) */169, - /* Ranging potion(2) */171, - /* Ranging potion(1) */173, - /* Antipoison(3) */175, - /* Antipoison(2) */177, - /* Antipoison(1) */179, - /* Super antipoison(3) */181, - /* Super antipoison(2) */183, - /* Super antipoison(1) */185, - /* Bravery potion */739, - /* Cadava potion */756, - /* Magic ogre potion */2395, - /* Attack potion(4) */2428, - /* Restore potion(4) */2430, - /* Defence potion(4) */2432, - /* Prayer potion(4) */2434, - /* Super attack(4) */2436, - /* Fishing potion(4) */2438, - /* Super strength(4) */2440, - /* Super defence(4) */2442, - /* Ranging potion(4) */2444, - /* Antipoison(4) */2446, - /* Super antipoison(4) */2448, - /* Antifire potion(4) */2452, - /* Antifire potion(3) */2454, - /* Antifire potion(2) */2456, - /* Antifire potion(1) */2458, - /* Energy potion(4) */3008, - /* Energy potion(3) */3010, - /* Energy potion(2) */3012, - /* Energy potion(1) */3014, - /* Super energy(4) */3016, - /* Super energy(3) */3018, - /* Super energy(2) */3020, - /* Super energy(1) */3022, - /* Super restore(4) */3024, - /* Super restore(3) */3026, - /* Super restore(2) */3028, - /* Super restore(1) */3030, - /* Agility potion(4) */3032, - /* Agility potion(3) */3034, - /* Agility potion(2) */3036, - /* Agility potion(1) */3038, - /* Magic potion(4) */3040, - /* Magic potion(3) */3042, - /* Magic potion(2) */3044, - /* Magic potion(1) */3046, - /* Troll potion */3265, - /* Explosive potion */4045, - /* Super kebab */4608, - /* Strange potion */4836, - /* Antipoison+(4) */5943, - /* Antipoison+(3) */5945, - /* Antipoison+(2) */5947, - /* Antipoison+(1) */5949, - /* Antipoison++(4) */5952, - /* Antipoison++(3) */5954, - /* Antipoison++(2) */5956, - /* Antipoison++(1) */5958, - /* Supercompost */6034, - /* Compost potion(4) */6470, - /* Compost potion(3) */6472, - /* Compost potion(2) */6474, - /* Compost potion(1) */6476, - /* Combat potion(4) */9739, - /* Combat potion(3) */9741, - /* Combat potion(2) */9743, - /* Combat potion(1) */9745, - /* Hunter potion(4) */9998, - /* Hunter potion(3) */10000, - /* Hunter potion(2) */10002, - /* Hunter potion(1) */10004, - /* Antipoison mix(2) */11433, - /* Antipoison mix(1) */11435, - /* Superattack mix(2) */11469, - /* Superattack mix(1) */11471, - /* Antifire mix(2) */11505, - /* Antifire mix(1) */11507, - /* Goblin potion (4) */11809, - /* Goblin potion (3) */11810, - /* Goblin potion (2) */11811, - /* Goblin potion (1) */11812, - /* Summoning potion(4) */12140, - /* Summoning potion(3) */12142, - /* Summoning potion(2) */12144, - /* Summoning potion(1) */12146, - /* Saradomin brews */ 6685, 6687, 6689, 6691, - /* Zamorak brews*/ 2450, 189, 191, 193, - /* Extended antifire potions*/ 14753, 14755, 14757, 14759, - /* Super ranging */ 14776, 14777, 14778, 14779, - /* Super magic */ 14780, 14781, 14782, 14783, - /* Overloads */ 14784, 14785, 14786, 14787, - /*Super Combat Pots */ 14871, 14869, 14867, 14865}; - - /** - * Constructs a new {@code PotionDecantingPlugin} {@code Object}. - */ - public PotionDecantingPlugin() { - super(POTIONS); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - for (int i : POTIONS) { - addHandler(i, ITEM_TYPE, this); - } - return this; - } - - @Override - public boolean handle(NodeUsageEvent event) { - final Player player = event.getPlayer(); - final Item item = event.getUsedItem(); - final Item other = event.getBaseItem(); - final String itemName = formatName(item); - final String otherName = formatName(other); - if (!itemName.equals(otherName) && (item.getId() != 229 && other.getId() != 229)) { - return false; - } - final int itemDose = getPotionDose(item); - final int otherDose = getPotionDose(other); - if (flagged(itemDose, otherDose)) { - return false; - } - - if (item.getId() == item.getNoteChange() || other.getId() == item.getNoteChange()) { - return false; - } - - final int[] newDoses = getDoses(itemDose, otherDose); - if (itemDose == 4 && otherDose == 0 || otherDose == 4 && itemDose == 0) { - player.getInventory().replace(getItem(item.getName().contains("Vial") ? otherName : itemName, newDoses[1]), item.getSlot()); - player.getInventory().replace(getItem(item.getName().contains("Vial") ? otherName : itemName, newDoses[1]), other.getSlot()); - player.getPacketDispatch().sendMessage("You decant the potion into two equal parts."); - } else { - player.getInventory().replace(getItem(item.getName().contains("Vial") ? otherName : itemName, newDoses[0]), other.getSlot()); - player.getInventory().replace(getItem(item.getName().contains("Vial") ? otherName : itemName, newDoses[1]), item.getSlot()); - player.getPacketDispatch().sendMessage("You have combined the liquid into " + (newDoses[0] == 0 ? newDoses[1] : newDoses[0]) + " doses."); - } - return true; - } - - /** - * Decants all the potions in a players inventory. - * @param player The player - */ - public static void decant(Player player) { - Map potions = new HashMap<>(); - - int totalVials = 0; - int newVials = 0; - int vialsToGive; - - for (Item item : player.getInventory().toArray()) { - if (item != null) { - if (getPotionDose(item) > 0 && isPot(item.getId())) { - String itemName = formatName(item); - item.setAmount(item.getAmount()); - totalVials += item.getAmount(); - if (player.getInventory().remove(item)) { - if (potions.get(itemName) != null) { - potions.put(itemName, potions.get(itemName) + (item.getAmount() * getPotionDose(item))); - } else { - potions.put(itemName, item.getAmount() * getPotionDose(item)); - } - } - } - } - } - - for (Map.Entry entry : potions.entrySet()) { - int amount = entry.getValue() / 4; - int excess = entry.getValue() % 4; - newVials += amount + (excess > 0 ? 1 : 0); - Item fourDose = getItem(entry.getKey(), 4); - player.getInventory().add(new Item(fourDose.getId() + 1, amount), player); - if (excess > 0) { - player.getInventory().add(new Item(getItem(entry.getKey(), excess).getId() + 1), player); - } - } - - vialsToGive = totalVials - newVials; - - if (vialsToGive > 0) { - player.getInventory().add(new Item(EMPTY_VIAL.getId() + 1, vialsToGive), player); - } - } - - /** - * Method used to format a name of a potion. - * @param item the item. - * @return the name. - */ - public static String formatName(final Item item) { - return item.getName().replace("(1)", "").replace("(2)", "").replace("(3)", "").replace("(4)", "").trim(); - } - - /** - * Method used to get the potion does between two potions. - * @param item the item. - * @return the doses. - */ - public static int getPotionDose(final Item item) { - return item.getName().contains("(1)") ? 1 : item.getName().contains("(2)") ? 2 : item.getName().contains("(3)") ? 3 : item.getName().contains("(4)") ? 4 : 0; - } - - /** - * Method used to check if a new potion ca be created based on the doses. - * @param itemDose the item dose. - * @param otherDose the other dose. - * @return True if flagged. - */ - public boolean flagged(final int itemDose, final int otherDose) { - return (itemDose == 4 && otherDose != 0 || itemDose != 4 && otherDose == 0 || itemDose == 4 && otherDose == 3 || itemDose == 0 && otherDose != 4) || (otherDose == 4 && itemDose != 0 || otherDose != 4 && itemDose == 0 || otherDose == 4 && itemDose == 3 || otherDose == 0 && itemDose != 4); - } - - /** - * Method used to return the doses of the new items. - * @param itemDose the item dose. - * @param otherDose the other dose. - * @return the new doses. - */ - public int[] getDoses(final int itemDose, final int otherDose) { - return itemDose == 1 && otherDose == 1 ? new int[] { 2, 0 } : itemDose == 3 && otherDose == 3 ? new int[] { 4, 2 } : itemDose == 2 && otherDose == 2 ? new int[] { 4, 0 } : itemDose == 2 && otherDose == 3 ? new int[] { 4, 1 } : itemDose == 1 && otherDose == 2 ? new int[] { 0, 3 } : itemDose == 4 && otherDose == 0 ? new int[] { 2, 2 } : itemDose == 1 && otherDose == 3 ? new int[] { 0, 4 } : otherDose == 2 && itemDose == 3 ? new int[] { 1, 4 } : otherDose == 1 && itemDose == 2 ? new int[] { 0, 3 } : otherDose == 4 && itemDose == 0 ? new int[] { 2, 2 } : otherDose == 1 && itemDose == 3 ? new int[] { 0, 4 } : itemDose == 0 && otherDose == 4 ? new int[] { 2, 2 } : otherDose == 4 && itemDose == 0 ? new int[] { 2, 2 } : new int[] { 0, 0 }; - } - - /** - * Method used to get the new item. - * @param name the name. - * @param dose the dose of the new item. - * @return the new item. - */ - public static Item getItem(String name, int dose) { - ItemDefinition def = null; - name += (oldSchool(name) ? " " : "")+"(" + dose + ")"; - if (dose == 0) { - return EMPTY_VIAL; - } - for (int id : POTIONS) { - def = ItemDefinition.forId(id); - if (def.getName().equals(name)) { - return new Item(id); - } - } - return null; - } - - /** - * Checks if an item is a pot. - * @param itemId The item id. - * @return true if so. - */ - public static boolean isPot(int itemId) { - for (int i : POTIONS) { - if (itemId == i || itemId == (i + 1)) { - return true; - } - } - return false; - } - - /** - * If an item is oldschool... add an extra space. - * @param name - * @return - * @author Jagex - */ - private static boolean oldSchool(String name){ - if(name.contains("xtended") || name.contains("uper rangin") || name.contains("uper magic") || name.contains("verload")){ - return true; - } - return false; - } -} 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/BoxTrapNode.java b/Server/src/main/content/global/skill/hunter/BoxTrapNode.java index 6cebfaf6a..129702015 100644 --- a/Server/src/main/content/global/skill/hunter/BoxTrapNode.java +++ b/Server/src/main/content/global/skill/hunter/BoxTrapNode.java @@ -8,7 +8,7 @@ import core.game.node.item.Item; * Handles the box trap node. * @author Vexia */ -public final class BoxTrapNode extends TrapNode { +public class BoxTrapNode extends TrapNode { /** * The summoning level required. diff --git a/Server/src/main/content/global/skill/hunter/HunterPlugin.java b/Server/src/main/content/global/skill/hunter/HunterPlugin.java index 83cb8993b..b434c2287 100644 --- a/Server/src/main/content/global/skill/hunter/HunterPlugin.java +++ b/Server/src/main/content/global/skill/hunter/HunterPlugin.java @@ -303,7 +303,13 @@ public final class HunterPlugin extends OptionHandler { @Override public boolean handle(Player player, Node node, String option) { - BNetTypes.forNpc((NPC) node).handle(player, (NPC) node); + BNetTypes type = BNetTypes.forNpc((NPC) node); + if (type == null) { + player.sendMessage("There seems to be something wrong with this catch option."); + player.sendMessage("Please submit a detailed bug report on gitlab."); + return true; + } + type.handle(player, (NPC) node); return true; } diff --git a/Server/src/main/content/global/skill/hunter/ImpBoxPlugin.java b/Server/src/main/content/global/skill/hunter/ImpBoxPlugin.java index 800e342eb..a1cf25062 100644 --- a/Server/src/main/content/global/skill/hunter/ImpBoxPlugin.java +++ b/Server/src/main/content/global/skill/hunter/ImpBoxPlugin.java @@ -46,10 +46,16 @@ public class ImpBoxPlugin extends OptionHandler { public boolean handle(Player player, Node node, String option) { switch (option) { case "bank": + int boxSlot = node.asItem().getSlot(); + player.getInventory().replace(new Item(10025), boxSlot); + player.sendMessage("You free the imp in exchange for it taking some items to your bank."); + player.getBank().openDepositBox(); + /* Snowscape Custom: the normal imp interface is broken, so we fixed/buffed it by making it act as a one-time-use deposit box instead. Component component = new Component(478); component.setPlugin(new ImpInterfaceHandler((Item) node)); player.getInterfaceManager().open(component); PacketRepository.send(ContainerPacket.class, new ContainerContext(player, 478, 61, 91, player.getInventory(), true)); + */ break; case "talk-to": player.getDialogueInterpreter().open("imp-box"); diff --git a/Server/src/main/content/global/skill/hunter/ImpetuousImpulses.java b/Server/src/main/content/global/skill/hunter/ImpetuousImpulses.java deleted file mode 100644 index 2ce0c9526..000000000 --- a/Server/src/main/content/global/skill/hunter/ImpetuousImpulses.java +++ /dev/null @@ -1,152 +0,0 @@ -package content.global.skill.hunter; - -import content.global.skill.hunter.bnet.BNetTypes; -import content.global.skill.hunter.bnet.ImplingNode; -import core.api.StartupListener; -import core.game.world.GameWorld; -import core.game.world.map.Location; -import core.tools.RandomFunction; - -import java.util.Random; - -/** - * Handles the impetuous impulse minigame. - * @author Vexia - * @author Emperor - */ -public final class ImpetuousImpulses implements StartupListener { - - /** - * The random instance used. - */ - private static final Random RANDOM = new Random(); - - /** - * The puro puro locations. - */ - private static final Location[] PURO_LOCATIONS = new Location[] { new Location(2575, 4314, 0), new Location(2572, 4302, 0), new Location(2575, 4297, 0), new Location(2589, 4297, 0), new Location(2603, 4297, 0), new Location(2612, 4301, 0), new Location(2619, 4296, 0), new Location(2616, 4297, 0), new Location(2620, 4308, 0), new Location(2617, 4313, 0), new Location(2620, 4317, 0), new Location(2611, 4312, 0), new Location(2607, 4305, 0), new Location(2593, 4303, 0), new Location(2578, 4318, 0), new Location(2578, 4330, 0), new Location(2581, 4338, 0), new Location(2586, 4342, 0), Location.create(2585, 4344, 0), Location.create(2573, 4335, 0), Location.create(2570, 4325, 0), Location.create(2567, 4313, 0), Location.create(2566, 4299, 0), Location.create(2573, 4292, 0), Location.create(2603, 4342, 0), Location.create(2616, 4318, 0), Location.create(2616, 4337, 0), Location.create(2584, 4297, 0), Location.create(2596, 4310, 0), Location.create(2596, 4310, 0), Location.create(2588, 4303, 0) }; - - /** - * The possible locations. - */ - public static final Location[] LOCATIONS = new Location[] { - /* - * Camelot - */ - Location.create(2718 + RANDOM.nextInt(50), 3441 + RANDOM.nextInt(20), 0), - /* - * Duel arena entrance - */ - Location.create(3300 + RANDOM.nextInt(10), 3250 + RANDOM.nextInt(10), 0), - /* - * Castle wars entrance - */ - Location.create(2478 + RANDOM.nextInt(20), 3068 + RANDOM.nextInt(20), 0), - /* - * Lumbridge - */ - Location.create(3175 + RANDOM.nextInt(10), 3261 + RANDOM.nextInt(10), 0), - /* - * Draynor - */ - Location.create(3092 + RANDOM.nextInt(5), 3227 + RANDOM.nextInt(5), 0), - /* - * Falador (east) - */ - Location.create(3071 + RANDOM.nextInt(10), 3355 + RANDOM.nextInt(10), 0), - /* - * Varrock (west) - */ - Location.create(3131 + RANDOM.nextInt(5), 3401 + RANDOM.nextInt(5), 0), - /* - * Ardougne (south) - */ - Location.create(2630 + RANDOM.nextInt(10), 3217 + RANDOM.nextInt(10), 0), - /* - * Yanille (north) - */ - Location.create(2594 + RANDOM.nextInt(20), 3121 + RANDOM.nextInt(20), 0), - /* - * Red salamander hunting place (ZMI altar) - */ - Location.create(2436 + RANDOM.nextInt(15), 3206 + RANDOM.nextInt(15), 0), - /* - * Shilo hunting area (graahk/larupia/..) - */ - Location.create(2763 + RANDOM.nextInt(20), 3003 + RANDOM.nextInt(20), 0), - /* - * Rimmington - */ - Location.create(2947 + RANDOM.nextInt(20), 3219 + RANDOM.nextInt(20), 0), - /* - * Karamja volcano - */ - Location.create(2856 + RANDOM.nextInt(15), 3150 + RANDOM.nextInt(15), 0), - /* - * Brimhaven dungeon entrance - */ - Location.create(2733 + RANDOM.nextInt(10), 3146 + RANDOM.nextInt(10), 0), - /* - * Sophanem - */ - Location.create(3299 + RANDOM.nextInt(5), 2784 + RANDOM.nextInt(5), 0), - /* - * Tree gnome stronghold - */ - Location.create(2438 + RANDOM.nextInt(20), 3420 + RANDOM.nextInt(20), 0), - /* - * Zanaris (center, just north of grain field) - */ - Location.create(2416 + RANDOM.nextInt(3), 4456 + RANDOM.nextInt(3), 0), }; - - /** - * Spawns the imps. - */ - private void spawnImps() { - int amount = BNetTypes.getImplings().size() + 2; - for (ImplingNode impling : BNetTypes.getImplings()) { - if (impling.getLevel() == 83) { - int rand = RandomFunction.random(1, 2); - for (int i = 0; i < rand; i++) { - createImp(impling, true); - } - int randAmt = RandomFunction.random(2, 5); - for (int i = 0; i < randAmt; i++) { - createImp(impling, false); - } - continue; - } - for (int i = 0; i < amount; i++) { - if (RANDOM.nextInt(10) < 4) { - createImp(impling, false); - continue; - } - createImp(impling, true); - } - amount--; - } - } - - /** - * Creates the impling. - * @param impling the impling. - * @param puro the puro. - */ - private void createImp(ImplingNode impling, boolean puro) { - ImplingNPC npc = null; - if (puro) { - npc = new ImplingNPC(impling.getNpcs()[1], PURO_LOCATIONS[RANDOM.nextInt(PURO_LOCATIONS.length)], impling); - npc.setAttribute("puroPuro", true); - } else { - npc = new ImplingNPC(impling.getNpcs()[0], LOCATIONS[RANDOM.nextInt(LOCATIONS.length)], impling); - npc.setAttribute("nextTeleport", GameWorld.getTicks() + 600); - } - npc.init(); - } - - @Override - public void startup() { - spawnImps(); - } - -} diff --git a/Server/src/main/content/global/skill/hunter/ImplingNPC.java b/Server/src/main/content/global/skill/hunter/ImplingNPC.java deleted file mode 100644 index f9f7fff71..000000000 --- a/Server/src/main/content/global/skill/hunter/ImplingNPC.java +++ /dev/null @@ -1,120 +0,0 @@ -package content.global.skill.hunter; - -import content.global.skill.hunter.bnet.ImplingNode; -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.AbstractNPC; -import core.game.node.entity.player.Player; -import core.game.system.task.Pulse; -import core.game.world.map.Location; -import core.game.world.map.path.Pathfinder; -import core.game.world.update.flag.context.Graphics; -import core.plugin.Initializable; -import core.tools.RandomFunction; -import core.game.system.config.NPCConfigParser; -import core.game.world.GameWorld; - -/** - * Handles an impling npc. - * @author Vexia - */ -@Initializable -public final class ImplingNPC extends AbstractNPC { - - /** - * The impling node. - */ - private final ImplingNode impling; - - /** - * Constructs a new {@code ImplingNPC} {@code Object}. - */ - public ImplingNPC() { - super(0, null); - this.impling = null; - this.setWalks(true); - this.setWalkRadius(20); - } - - /** - * Constructs a new {@code ImplingNPC} {@code Object}. - * @param id the id. - * @param location the location. - */ - public ImplingNPC(int id, Location location, ImplingNode impling) { - super(id, location); - this.impling = impling; - if (impling != null) { - this.getDefinition().getHandlers().put(NPCConfigParser.RESPAWN_DELAY, impling.getRespawnTime()); - } - } - - @Override - public AbstractNPC construct(int id, Location location, Object... objects) { - return new ImplingNPC(id, location, null); - } - - @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); - } - } - } - if (RandomFunction.random(100) < 4) { - sendChat("Tee hee!"); - } - int nextTeleport = getAttribute("nextTeleport", -1); - if (nextTeleport > -1 && GameWorld.getTicks() > nextTeleport) { - setAttribute("nextTeleport", GameWorld.getTicks() + 600); - graphics(new Graphics(590)); - GameWorld.getPulser().submit(new Pulse(1) { - @Override - public boolean pulse() { - teleport(ImpetuousImpulses.LOCATIONS[RandomFunction.random(ImpetuousImpulses.LOCATIONS.length)]); - return true; - } - }); - return; - } - } - - @Override - public boolean isAttackable(Entity entity, CombatStyle style, boolean message) { - if (style != CombatStyle.MAGIC) { - return false; - } - if (entity.getProperties().getSpell().getSpellId() == 12 || entity.getProperties().getSpell().getSpellId() == 30 || entity.getProperties().getSpell().getSpellId() == 56) { - return true; - } - return super.isAttackable(entity, style, message); - } - - @Override - public void handleDrops(Player p, Entity killer) { - getProperties().setTeleportLocation(getProperties().getSpawnLocation()); - } - - @Override - public void checkImpact(BattleState state) { - } - - @Override - public int[] getIds() { - return new int[] { 1028, 6055, 1029, 6056, 1030, 6057, 1031, 6058, 1032, 6059, 1033, 6060, 1034, 6061, 1035, 6062, 6053, 6063}; - } - - /** - * Gets the impling. - * @return The impling. - */ - public ImplingNode getImpling() { - return impling; - } - -} diff --git a/Server/src/main/content/global/skill/hunter/TrapCreatePulse.java b/Server/src/main/content/global/skill/hunter/TrapCreatePulse.java index 642525df5..dddc84e4d 100644 --- a/Server/src/main/content/global/skill/hunter/TrapCreatePulse.java +++ b/Server/src/main/content/global/skill/hunter/TrapCreatePulse.java @@ -11,6 +11,9 @@ import core.game.node.scenery.Scenery; import core.game.node.scenery.SceneryBuilder; import core.game.world.map.Location; import core.game.world.map.RegionManager; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.*; /** * Handles the trap creating pulse. @@ -51,6 +54,26 @@ public final class TrapCreatePulse extends SkillPulse { this.trap = trap; this.startLocation = node instanceof GroundItem ? node.getLocation() : player.getLocation(); this.instance = HunterManager.getInstance(player); + if (checkRequirements()){ + switch(trap) { + case BIRD_SNARE: + playAudio(player, Sounds.HUNTING_SETNOOSE_2646, 40); + break; + case BOX_TRAP: + playAudio(player, Sounds.HUNTING_LAYBOXTRAP_2636, 20); + break; + case NET_TRAP: + lock(player, 3); + playAudio(player, Sounds.HUNTING_SET_TWITCHNET_2644); + break; + case RABBIT_SNARE: + playAudio(player, Sounds.HUNTING_SETSNARE_2647); + break; + case DEAD_FALL: + lock(player, 6); + playAudio(player, Sounds.HUNTING_SETDEADFALL_2645, 130); + } + } } @Override diff --git a/Server/src/main/content/global/skill/hunter/TrapDismantlePulse.java b/Server/src/main/content/global/skill/hunter/TrapDismantlePulse.java index 8a468ca84..0492f937b 100644 --- a/Server/src/main/content/global/skill/hunter/TrapDismantlePulse.java +++ b/Server/src/main/content/global/skill/hunter/TrapDismantlePulse.java @@ -5,6 +5,9 @@ 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.scenery.Scenery; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.*; /** * Handles the dismantling of a trap. @@ -39,6 +42,27 @@ public final class TrapDismantlePulse extends SkillPulse { this.trap = wrapper.getType(); this.wrapper = wrapper; this.instance = HunterManager.getInstance(player); + if (checkRequirements()){ + switch(trap) { + case BIRD_SNARE: + lock(player,5); + playAudio(player, Sounds.HUNTING_DISMANTLE_2632, 50); + break; + case BOX_TRAP: + lock(player,4); + playAudio(player, Sounds.HUNTING_DISMANTLE_2632, 50); + break; + case NET_TRAP: + lock(player, 5); + playAudio(player, Sounds.HUNTING_DISMANTLE_2632, 20); + break; + case RABBIT_SNARE: + case DEAD_FALL: + lock(player, 4); + playAudio(player, Sounds.HUNTING_DISMANTLE_2632, 80); + break; + } + } } @Override diff --git a/Server/src/main/content/global/skill/hunter/TrapSetting.java b/Server/src/main/content/global/skill/hunter/TrapSetting.java index f20daa17e..73a69dbd8 100644 --- a/Server/src/main/content/global/skill/hunter/TrapSetting.java +++ b/Server/src/main/content/global/skill/hunter/TrapSetting.java @@ -16,6 +16,11 @@ import core.game.world.GameWorld; import core.game.world.map.Location; import core.game.world.update.flag.context.Animation; import core.tools.RandomFunction; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.playAudio; + +import core.game.node.entity.npc.drop.NPCDropTables; /** * A setting for a trap type. @@ -158,7 +163,13 @@ public class TrapSetting { return; } addTool(player, wrapper, type); - player.getInventory().add(wrapper.getItems().toArray(new Item[] {})); + // Snowscape modification: custom bonecrusher functionality extended to include hunter bones + //player.getInventory().add(wrapper.getItems().toArray(new Item[] {})); + for (Item item: wrapper.getItems().toArray(new Item[] {})) { + if (!NPCDropTables.handleBoneCrusher(player, item)) { + player.getInventory().add(item); + } + } } else { if (isObjectTrap() && !ground) { player.getInventory().add(wrapper.getItems().toArray(new Item[] {})); @@ -303,9 +314,25 @@ public class TrapSetting { case 3: handleCatch(counter, wrapper, node, npc, success); if (success) { - wrapper.setTicks(GameWorld.getTicks() + 100); + wrapper.setTicks(GameWorld.getTicks() + 1000); wrapper.setReward(node); wrapper.setObject(getFinalId(wrapper, node)); + switch(wrapper.getType()) { + case BIRD_SNARE: + playAudio(player, Sounds.HUNTING_NOOSE_2637, 0, 1, wrapper.getObject().getLocation(), 10); + playAudio(player, Sounds.HUNTING_BIRDCAUGHT_2625, 20, 1, wrapper.getObject().getLocation(), 10); + break; + case BOX_TRAP: + playAudio(player, Sounds.HUNTING_BOXTRAP_2627, 0, 1, wrapper.getObject().getLocation(), 10); + break; + case NET_TRAP: + playAudio(player, Sounds.HUNTING_TWITCHNET_2652, 0, 1, wrapper.getObject().getLocation(), 10); + playAudio(player, Sounds.SALAMANDER_HIT_739, 20, 1, wrapper.getObject().getLocation(), 10); + break; + case DEAD_FALL: + playAudio(player, Sounds.HUNTING_DEADFALL_2631, 0, 1, wrapper.getObject().getLocation(), 10); + break; + } return true; } npc.moveStep(); diff --git a/Server/src/main/content/global/skill/hunter/TrapWrapper.java b/Server/src/main/content/global/skill/hunter/TrapWrapper.java index 4cbe32987..b596d864c 100644 --- a/Server/src/main/content/global/skill/hunter/TrapWrapper.java +++ b/Server/src/main/content/global/skill/hunter/TrapWrapper.java @@ -102,7 +102,7 @@ public final class TrapWrapper { this.type = type; this.object = object; this.originalId = object.getId(); - this.ticks = GameWorld.getTicks() + (100); + this.ticks = GameWorld.getTicks() + (1000); this.instance = HunterManager.getInstance(player); this.object.getAttributes().setAttribute("trap-uid", instance.getUid()); } diff --git a/Server/src/main/content/global/skill/hunter/Traps.java b/Server/src/main/content/global/skill/hunter/Traps.java index 0555254a6..e5e30ca34 100644 --- a/Server/src/main/content/global/skill/hunter/Traps.java +++ b/Server/src/main/content/global/skill/hunter/Traps.java @@ -5,6 +5,7 @@ import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.game.node.item.Item; import core.game.node.scenery.Scenery; +import core.tools.Log; import core.tools.SystemLogger; import core.game.world.map.Location; import core.game.world.update.flag.context.Animation; @@ -12,18 +13,20 @@ import core.game.world.update.flag.context.Animation; import java.util.ArrayList; import java.util.List; +import static core.api.ContentAPIKt.log; + /** * Represents a trap type. * @author Vexia */ public enum Traps { BIRD_SNARE(new TrapSetting(10006, new int[] { 19175 }, new int[] {}, "lay", 19174, Animation.create(5208), Animation.create(5207), 1), - new TrapNode(new int[] { 5073 }, 1, 34, new int[] { 19179, 19180 }, new Item[] { new Item(10088, 8), new Item(9978), new Item(526) }), - new TrapNode(new int[] { 5075 }, 5, 48, new int[] { 19183, 19184 }, new Item[] { new Item(10090, 8), new Item(9978), new Item(526) }), - new TrapNode(new int[] { 5076 }, 9, 61, new int[] { 19185, 19186 }, new Item[] { new Item(10091, 8), new Item(9978), new Item(526) }), - new TrapNode(new int[] { 5074 }, 11, 64.7, new int[] { 19181, 19182 }, new Item[] { new Item(10089, 8), new Item(9978), new Item(526) }), - new TrapNode(new int[] { 5072 }, 19, 95.2, new int[] { 19177, 19178 }, new Item[] { new Item(10087, 8), new Item(9978), new Item(526) }), - new TrapNode(new int[] { 7031 }, 39, 167, new int[] { 28931, 28930 }, new Item[] { new Item(11525, 8), new Item(9978), new Item(526) }) { + new TrapNode(new int[] { 5073 }, 1, 34, new int[] { 19179, 19180 }, new Item[] { new Item(10088, 60), new Item(9978), new Item(526) }), + new TrapNode(new int[] { 5075 }, 5, 48, new int[] { 19183, 19184 }, new Item[] { new Item(10090, 60), new Item(9978), new Item(526) }), + new TrapNode(new int[] { 5076 }, 9, 61, new int[] { 19185, 19186 }, new Item[] { new Item(10091, 60), new Item(9978), new Item(526) }), + new TrapNode(new int[] { 5074 }, 11, 64.7, new int[] { 19181, 19182 }, new Item[] { new Item(10089, 60), new Item(9978), new Item(526) }), + new TrapNode(new int[] { 5072 }, 19, 95.2, new int[] { 19177, 19178 }, new Item[] { new Item(10087, 60), new Item(9978), new Item(526) }), + new TrapNode(new int[] { 7031 }, 39, 167, new int[] { 28931, 28930 }, new Item[] { new Item(11525, 60), new Item(9978), new Item(526) }) { @Override public boolean canCatch(TrapWrapper wrapper, final NPC npc) { return false; @@ -39,7 +42,14 @@ public enum Traps { new BoxTrapNode(new int[] { 5080 }, 63, 265, new Item[] { new Item(10034, 1) }, 1), new BoxTrapNode(new int[] { 7012, 7014 }, 66, 400, new Item[] { new Item(12535) }, 1), new BoxTrapNode(new int[] { 8654 }, 73, 315, new Item[] { new Item(14861) }, 1), - new BoxTrapNode(new int[] { 7010, 7011 }, 77, 726, new Item[] { new Item(12539, 1) }, 1)), + new BoxTrapNode(new int[] { 7010, 7011 }, 77, 0, new Item[] { new Item(12539, 1) }, 1) { + @Override + public boolean canCatch(TrapWrapper wrapper, final NPC npc) { + //old xp: 726 + wrapper.getPlayer().sendMessage("Note: Giving 0 xp for grenwalls until this area and its requirements are implemented."); + return super.canCatch(wrapper, npc); + } + }), RABBIT_SNARE(new TrapSetting(10031, new int[] { 19333 }, new int[] {}, "lay", -1, Animation.create(5208), Animation.create(9726), 27)), IMP_BOX(new MagicBoxSetting(), new TrapNode(new int[] { 708, 709, 1531 }, 71, 450, new int[] { -1, 19226 }, new Item[] { new Item(10027) })), @@ -101,7 +111,7 @@ public enum Traps { return; } if (instance.getWrapper(object) == null) { - SystemLogger.logErr(this.getClass(), "NO WRAPPER (HUNTER DISMANTLE)"); + log(this.getClass(), Log.ERR, "NO WRAPPER (HUNTER DISMANTLE)"); return; } player.faceLocation(object.getLocation()); diff --git a/Server/src/main/content/global/skill/hunter/bnet/BNetPulse.java b/Server/src/main/content/global/skill/hunter/bnet/BNetPulse.java index 13edf7b3b..38371c49f 100644 --- a/Server/src/main/content/global/skill/hunter/bnet/BNetPulse.java +++ b/Server/src/main/content/global/skill/hunter/bnet/BNetPulse.java @@ -11,9 +11,12 @@ import core.game.world.GameWorld; import core.game.world.update.flag.context.Animation; import core.tools.RandomFunction; import core.tools.StringUtils; +import org.rs09.consts.Sounds; import java.util.Random; +import static core.api.ContentAPIKt.playAudio; + /** * Handles the butterfly net catch pulse. * @author Vexia @@ -73,18 +76,16 @@ public final class BNetPulse extends SkillPulse { player.sendMessage("You need a Hunter level of at least " + type.getLevel() + " in order to do that."); return false; } - if (!type.isBareHand(player)) { - if (type.hasWeapon(player)) { - player.getPacketDispatch().sendMessage("Your hands need to be free."); - return false; - } else if (!type.hasNet(player)) { - player.sendMessage("You need to be wielding a butterfly net to catch " + (type instanceof ImplingNode ? "implings" : "butterflies") + "."); - return false; - } else if (!type.hasJar(player)) { - player.getPacketDispatch().sendMessage("You need to have a" + (StringUtils.isPlusN(type.getJar().getName()) ? "n" : "") + " " + type.getJar().getName().toLowerCase() + "."); - return false; - } - } + if (type.hasWeapon(player)) { + player.getPacketDispatch().sendMessage("Your hands need to be free."); + return false; + } else if (!type.hasNet(player)) { + player.sendMessage("You need to be wielding a butterfly net to catch " + (type instanceof ImplingNode ? "implings" : "butterflies") + "."); + return false; + } else if (!type.hasJar(player)) { + player.getPacketDispatch().sendMessage("You need to have a" + (StringUtils.isPlusN(type.getJar().getName()) ? "n" : "") + " " + type.getJar().getName().toLowerCase() + "."); + return false; + } if (node.isInvisible() || DeathTask.isDead(node)) { return false; } @@ -95,6 +96,7 @@ public final class BNetPulse extends SkillPulse { public void animate() { if (ticks < 1) { player.animate(ANIMATION); + playAudio(player, Sounds.HUNTING_BUTTERFLYNET_2623); } } diff --git a/Server/src/main/content/global/skill/hunter/bnet/BNetTypes.java b/Server/src/main/content/global/skill/hunter/bnet/BNetTypes.java index 77747192f..0b1e65fb2 100644 --- a/Server/src/main/content/global/skill/hunter/bnet/BNetTypes.java +++ b/Server/src/main/content/global/skill/hunter/bnet/BNetTypes.java @@ -22,14 +22,14 @@ public enum BNetTypes { BLACK_WARLOCK(new BNetNode(new int[] { 5082 }, new int[] { 45, 95, 90 }, new double[] { 54, 650, 125 }, new Graphics[] { new Graphics(910), new Graphics(914) }, new ChanceItem(10014))), // Implings ( Clearly these morons typed way too fast - wasn't sure if these names were anywhere else in the code) BABY_IMPLING(new ImplingNode(new int[] { 1028, 6055 }, 17, 20, 18, new Item(11238), new ChanceItem(1755, 1, 1, DropFrequency.COMMON), new ChanceItem(1734, 1, 1, DropFrequency.COMMON), new ChanceItem(1733, 1, 1, DropFrequency.COMMON), new ChanceItem(946, 1, 1, DropFrequency.COMMON), new ChanceItem(1985, 1, 1, DropFrequency.COMMON), new ChanceItem(2347, 1, 1, DropFrequency.COMMON), new ChanceItem(1759, 1, 1, DropFrequency.COMMON), new ChanceItem(1927, 1, 1, DropFrequency.UNCOMMON), new ChanceItem(319, 1, 1, DropFrequency.UNCOMMON), new ChanceItem(2007, 1, 1, DropFrequency.UNCOMMON), new ChanceItem(2007, 1, 1, DropFrequency.UNCOMMON), new ChanceItem(1779, 1, 1, DropFrequency.UNCOMMON), new ChanceItem(7170, 1, 1, DropFrequency.UNCOMMON), new ChanceItem(401, 1, 1, DropFrequency.UNCOMMON), new ChanceItem(1438, 1, 1, DropFrequency.UNCOMMON), new ChanceItem(2355, 1, 1, DropFrequency.RARE), new ChanceItem(1607, 1, 1, DropFrequency.RARE), new ChanceItem(1743, 1, 1, DropFrequency.RARE), new ChanceItem(379, 1, 1, DropFrequency.RARE), new ChanceItem(1761, 1, 1, DropFrequency.RARE))), - YOUNG_IMPLING(new ImplingNode(new int[] { 1029, 6056 }, 22, 22, 25, new Item(11240), new ChanceItem(1539, 5, 5, DropFrequency.COMMON), new ChanceItem(1901, 1, 1, DropFrequency.COMMON), new ChanceItem(7936, 1, 1, DropFrequency.COMMON), new ChanceItem(361, 1, 1, DropFrequency.COMMON), new ChanceItem(1523, 1, 1, DropFrequency.COMMON), new ChanceItem(453, 1, 1, DropFrequency.UNCOMMON), new ChanceItem(1777, 1, 1, DropFrequency.UNCOMMON), new ChanceItem(2293, 1, 1, DropFrequency.UNCOMMON), new ChanceItem(1353, 1, 1, DropFrequency.UNCOMMON), new ChanceItem(2359, 1, 1, DropFrequency.RARE))), + YOUNG_IMPLING(new ImplingNode(new int[] { 1029, 6056 }, 22, 22, 20, new Item(11240), new ChanceItem(1539, 5, 5, DropFrequency.COMMON), new ChanceItem(1901, 1, 1, DropFrequency.COMMON), new ChanceItem(7936, 1, 1, DropFrequency.COMMON), new ChanceItem(361, 1, 1, DropFrequency.COMMON), new ChanceItem(1523, 1, 1, DropFrequency.COMMON), new ChanceItem(453, 1, 1, DropFrequency.UNCOMMON), new ChanceItem(1777, 1, 1, DropFrequency.UNCOMMON), new ChanceItem(2293, 1, 1, DropFrequency.UNCOMMON), new ChanceItem(1353, 1, 1, DropFrequency.UNCOMMON), new ChanceItem(2359, 1, 1, DropFrequency.RARE))), GOURMET_IMPLING(new ImplingNode(new int[] { 1030, 6057 }, 28, 24, 22, new Item(11242), new ChanceItem(365, 1, 1, DropFrequency.COMMON), new ChanceItem(361, 1, 1, DropFrequency.COMMON), new ChanceItem(2011, 1, 1, DropFrequency.COMMON), new ChanceItem(1897, 1, 1, DropFrequency.COMMON), new ChanceItem(2327, 1, 1, DropFrequency.COMMON), new ChanceItem(5004, 1, 1, DropFrequency.COMMON), new ChanceItem(2007, 1, 1, DropFrequency.COMMON), new ChanceItem(5970, 1, 1, DropFrequency.COMMON), new ChanceItem(365, 4, 4, DropFrequency.UNCOMMON), new ChanceItem(3145, 2, 2, DropFrequency.RARE), new ChanceItem(7178, 1, 5, DropFrequency.UNCOMMON), new ChanceItem(5755, 1, 1, DropFrequency.RARE), new ChanceItem(386, 3, 3, DropFrequency.UNCOMMON), new ChanceItem(5406, 1, 1, DropFrequency.RARE), new ChanceItem(10136, 1, 1, DropFrequency.RARE), new ChanceItem(1883, 1, 1, DropFrequency.UNCOMMON))), EARTH_IMPLING(new ImplingNode(new int[] { 1031, 6058 }, 36, 27, 25, new Item(11244), new ChanceItem(6033, 6, 6, DropFrequency.COMMON), new ChanceItem(1440, 1, 1, DropFrequency.COMMON), new ChanceItem(5535, 1, 1, DropFrequency.COMMON), new ChanceItem(557, 32, 32, DropFrequency.COMMON), new ChanceItem(1442, 1, 1, DropFrequency.COMMON), new ChanceItem(1784, 4, 4, DropFrequency.UNCOMMON), new ChanceItem(447, 1, 1, DropFrequency.UNCOMMON), new ChanceItem(447, 1, 1, DropFrequency.UNCOMMON), new ChanceItem(1606, 2, 2, DropFrequency.RARE))), ESSENCE_IMPLING(new ImplingNode(new int[] { 1032, 6059 }, 42, 29, 27, new Item(11246), new ChanceItem(7937, 20, 20, DropFrequency.COMMON), new ChanceItem(555, 30, 30, DropFrequency.COMMON), new ChanceItem(556, 30, 30, DropFrequency.COMMON), new ChanceItem(558, 25, 25, DropFrequency.COMMON), new ChanceItem(559, 28, 28, DropFrequency.COMMON), new ChanceItem(562, 4, 4, DropFrequency.COMMON), new ChanceItem(1448, 1, 1, DropFrequency.COMMON), new ChanceItem(564, 4, 4, DropFrequency.UNCOMMON), new ChanceItem(565, 7, 7, DropFrequency.RARE), new ChanceItem(563, 13, 13, DropFrequency.RARE), new ChanceItem(566, 11, 11, DropFrequency.RARE))), ECLECTIC_IMPLING(new ImplingNode(new int[] { 1033, 6060 }, 50, 32, 30, new Item(11248), new ChanceItem(1273, 1, 1, DropFrequency.COMMON), new ChanceItem(1199, 1, 1, DropFrequency.UNCOMMON), new ChanceItem(2493, 1, 1, DropFrequency.RARE), new ChanceItem(10083, 1, 1, DropFrequency.RARE), new ChanceItem(1213, 1, 1, DropFrequency.RARE), new ChanceItem(1391, 1, 1, DropFrequency.VERY_RARE), new ChanceItem(5970, 1, 1, DropFrequency.COMMON), new ChanceItem(231, 1, 1, DropFrequency.COMMON), new ChanceItem(556, 30, 57, DropFrequency.COMMON), new ChanceItem(8779, 4, 4, DropFrequency.COMMON), new ChanceItem(4527, 1, 1, DropFrequency.UNCOMMON), new ChanceItem(444, 1, 1, DropFrequency.UNCOMMON), new ChanceItem(2358, 5, 5, DropFrequency.UNCOMMON), new ChanceItem(7937, 20, 35, DropFrequency.UNCOMMON), new ChanceItem(237, 1, 1, DropFrequency.UNCOMMON), new ChanceItem(450, 10, 10, DropFrequency.RARE), new ChanceItem(5760, 2, 2, DropFrequency.RARE), new ChanceItem(7208, 1, 1, DropFrequency.RARE), new ChanceItem(5321, 3, 3, DropFrequency.RARE), new ChanceItem(1601, 1, 1, DropFrequency.VERY_RARE))), NATURE_IMPLING(new ImplingNode(new int[] { 1034, 6061 }, 58, 36, 34, new Item(11250), new ChanceItem(5303, 1, 1, DropFrequency.VERY_RARE), new ChanceItem(270, 2, 2, DropFrequency.VERY_RARE), new ChanceItem(5295, 1, 1, DropFrequency.RARE), new ChanceItem(5304, 1, 1, DropFrequency.RARE), new ChanceItem(5298, 5, 5, DropFrequency.UNCOMMON), new ChanceItem(5299, 1, 1, DropFrequency.UNCOMMON), new ChanceItem(5297, 1, 1, DropFrequency.UNCOMMON), new ChanceItem(5974, 1, 1, DropFrequency.UNCOMMON), new ChanceItem(3000, 1, 1, DropFrequency.UNCOMMON), new ChanceItem(5285, 1, 1, DropFrequency.UNCOMMON), new ChanceItem(5286, 1, 1, DropFrequency.UNCOMMON), new ChanceItem(5100, 1, 1, DropFrequency.COMMON), new ChanceItem(5104, 1, 1, DropFrequency.COMMON), new ChanceItem(5281, 1, 1, DropFrequency.COMMON), new ChanceItem(5294, 1, 1, DropFrequency.COMMON), new ChanceItem(6016, 1, 1, DropFrequency.COMMON), new ChanceItem(1513, 1, 1, DropFrequency.COMMON), new ChanceItem(254, 4, 4, DropFrequency.COMMON), new ChanceItem(5313, 1, 1, DropFrequency.UNCOMMON))), - MAGPIE_IMPLING(new ImplingNode(new int[] { 1035, 6062 }, 65, 46, 44, new Item(11252), 500, new ChanceItem(1682, 3, 3, DropFrequency.COMMON), new ChanceItem(1732, 3, 3, DropFrequency.COMMON), new ChanceItem(2569, 3, 3, DropFrequency.COMMON), new ChanceItem(3391, 1, 1, DropFrequency.COMMON), new ChanceItem(1347, 1, 1, DropFrequency.UNCOMMON), new ChanceItem(2571, 4, 4, DropFrequency.UNCOMMON), new ChanceItem(4097, 1, 1, DropFrequency.UNCOMMON), new ChanceItem(4095, 1, 1, DropFrequency.UNCOMMON), new ChanceItem(1215, 1, 1, DropFrequency.RARE), new ChanceItem(1185, 1, 1, DropFrequency.RARE), new ChanceItem(5541, 1, 1, DropFrequency.COMMON), new ChanceItem(1748, 6, 6, DropFrequency.COMMON), new ChanceItem(2364, 2, 2, DropFrequency.UNCOMMON), new ChanceItem(1602, 4, 4, DropFrequency.RARE), new ChanceItem(5287, 1, 1, DropFrequency.RARE), new ChanceItem(985, 1, 1, DropFrequency.RARE), new ChanceItem(987, 1, 1, DropFrequency.RARE), new ChanceItem(993, 1, 1, DropFrequency.VERY_RARE), new ChanceItem(5300, 1, 1, DropFrequency.VERY_RARE))), - NINJA_IMPLING(new ImplingNode(new int[] { 6053, 6063 }, 74, 52, 50, new Item(11254), 2000, new ChanceItem(4097, 1, 1, DropFrequency.COMMON), new ChanceItem(1113, 1, 1, DropFrequency.UNCOMMON), new ChanceItem(3385, 1, 1, DropFrequency.COMMON), new ChanceItem(1215, 1, 1, DropFrequency.UNCOMMON), new ChanceItem(811, 70, 70, DropFrequency.UNCOMMON), new ChanceItem(1333, 1, 1, DropFrequency.UNCOMMON), new ChanceItem(1347, 1, 1, DropFrequency.UNCOMMON), new ChanceItem(9342, 2, 2, DropFrequency.UNCOMMON), new ChanceItem(9194, 4, 4, DropFrequency.RARE), new ChanceItem(140, 4, 4, DropFrequency.COMMON), new ChanceItem(6155, 3, 3, DropFrequency.UNCOMMON), new ChanceItem(1748, 10, 16, DropFrequency.COMMON))), + MAGPIE_IMPLING(new ImplingNode(new int[] { 1035, 6062 }, 65, 216, 44, new Item(11252), 500, new ChanceItem(1682, 3, 3, DropFrequency.COMMON), new ChanceItem(1732, 3, 3, DropFrequency.COMMON), new ChanceItem(2569, 3, 3, DropFrequency.COMMON), new ChanceItem(3391, 1, 1, DropFrequency.COMMON), new ChanceItem(1347, 1, 1, DropFrequency.UNCOMMON), new ChanceItem(2571, 4, 4, DropFrequency.UNCOMMON), new ChanceItem(4097, 1, 1, DropFrequency.UNCOMMON), new ChanceItem(4095, 1, 1, DropFrequency.UNCOMMON), new ChanceItem(1215, 1, 1, DropFrequency.RARE), new ChanceItem(1185, 1, 1, DropFrequency.RARE), new ChanceItem(5541, 1, 1, DropFrequency.COMMON), new ChanceItem(1748, 6, 6, DropFrequency.COMMON), new ChanceItem(2364, 2, 2, DropFrequency.UNCOMMON), new ChanceItem(1602, 4, 4, DropFrequency.RARE), new ChanceItem(5287, 1, 1, DropFrequency.RARE), new ChanceItem(985, 1, 1, DropFrequency.RARE), new ChanceItem(987, 1, 1, DropFrequency.RARE), new ChanceItem(993, 1, 1, DropFrequency.VERY_RARE), new ChanceItem(5300, 1, 1, DropFrequency.VERY_RARE))), + NINJA_IMPLING(new ImplingNode(new int[] { 6053, 6063 }, 74, 240, 50, new Item(11254), 2000, new ChanceItem(4097, 1, 1, DropFrequency.COMMON), new ChanceItem(1113, 1, 1, DropFrequency.UNCOMMON), new ChanceItem(3385, 1, 1, DropFrequency.COMMON), new ChanceItem(1215, 1, 1, DropFrequency.UNCOMMON), new ChanceItem(811, 70, 70, DropFrequency.UNCOMMON), new ChanceItem(1333, 1, 1, DropFrequency.UNCOMMON), new ChanceItem(1347, 1, 1, DropFrequency.UNCOMMON), new ChanceItem(9342, 2, 2, DropFrequency.UNCOMMON), new ChanceItem(9194, 4, 4, DropFrequency.RARE), new ChanceItem(140, 4, 4, DropFrequency.COMMON), new ChanceItem(6155, 3, 3, DropFrequency.UNCOMMON), new ChanceItem(1748, 10, 16, DropFrequency.COMMON))), // Pirate should be here DRAGON_IMPLING(new ImplingNode(new int[] { 6054, 6064 }, 83, 300, 65, new Item(11256), 3000, new ChanceItem(1705, 2, 3, DropFrequency.RARE), new ChanceItem(4093, 1, 1, DropFrequency.VERY_RARE), new ChanceItem(1684, 2, 3, DropFrequency.VERY_RARE), new ChanceItem(11212, 100, 500, DropFrequency.COMMON), new ChanceItem(9341, 3, 40, DropFrequency.COMMON), new ChanceItem(1305, 1, 1, DropFrequency.COMMON), new ChanceItem(5699, 3, 3, DropFrequency.UNCOMMON), new ChanceItem(11230, 105, 350, DropFrequency.UNCOMMON), new ChanceItem(11232, 105, 350, DropFrequency.COMMON), new ChanceItem(11237, 100, 500, DropFrequency.COMMON), new ChanceItem(9193, 10, 49, DropFrequency.COMMON), new ChanceItem(535, 111, 297, DropFrequency.COMMON), new ChanceItem(5316, 1, 1, DropFrequency.UNCOMMON), new ChanceItem(537, 52, 99, DropFrequency.UNCOMMON), new ChanceItem(1616, 3, 6, DropFrequency.UNCOMMON), new ChanceItem(5300, 6, 6, DropFrequency.RARE), new ChanceItem(7219, 5, 15, DropFrequency.RARE))); diff --git a/Server/src/main/content/global/skill/hunter/falconry/FalconryCatchPulse.java b/Server/src/main/content/global/skill/hunter/falconry/FalconryCatchPulse.java index d49ee8653..b33667e41 100644 --- a/Server/src/main/content/global/skill/hunter/falconry/FalconryCatchPulse.java +++ b/Server/src/main/content/global/skill/hunter/falconry/FalconryCatchPulse.java @@ -13,6 +13,9 @@ import core.game.world.GameWorld; import core.game.world.map.Location; import core.game.world.repository.Repository; import core.tools.RandomFunction; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.playAudio; /** * Represents the skill pulse used to catch a kebbit. @@ -123,6 +126,7 @@ public final class FalconryCatchPulse extends SkillPulse { falcon.setAttribute("falcon:catch", falconCatch); falcon.init(); HintIconManager.registerHintIcon(player, falcon); + playAudio(player, Sounds.HUNTING_FALCON_SWOOP_2634, 10, 1, node.getLocation(), 12); GameWorld.getPulser().submit(new Pulse(100, falcon) { @Override public boolean pulse() { @@ -155,6 +159,7 @@ public final class FalconryCatchPulse extends SkillPulse { projectile.setStartHeight(26); projectile.setEndHeight(1); projectile.send(); + playAudio(player, Sounds.HUNTING_FALCON_FLY_2633); } /** diff --git a/Server/src/main/content/global/skill/hunter/implings/ImplingBehavior.kt b/Server/src/main/content/global/skill/hunter/implings/ImplingBehavior.kt new file mode 100644 index 000000000..0848390b2 --- /dev/null +++ b/Server/src/main/content/global/skill/hunter/implings/ImplingBehavior.kt @@ -0,0 +1,83 @@ +package content.global.skill.hunter.implings + +import core.api.* +import core.tools.* +import core.game.world.map.RegionManager +import core.game.world.map.path.ClipMaskSupplier +import core.game.node.entity.npc.* +import core.game.node.entity.Entity +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.SpellBookManager +import core.game.node.item.Item +import core.game.node.entity.combat.CombatStyle +import content.global.skill.magic.spellconsts.Modern + +/** + * Manages the behavior for implings themselves. +*/ +class ImplingBehavior : NPCBehavior (*Impling.getIds()) { + override fun onCreation (self: NPC) { + self.isWalks = true + self.isNeverWalks = false + } + + override fun tick(self: NPC) : Boolean { + if (RandomFunction.roll(10)) + sendChat(self, "Tee-hee!") + return true + } + + override fun onRespawn(self: NPC) { + if (!isPuroImpling(self)) + log (this::class.java, Log.ERR, "Non-puro impling has respawned!") + sendGraphics(1119, self.properties.teleportLocation) + } + + override fun canBeAttackedBy(self: NPC, attacker: Entity, style: CombatStyle, shouldSendMessage: Boolean) : Boolean { + if (attacker !is Player) return false + + if (style != CombatStyle.MAGIC) { + if (shouldSendMessage) + sendMessage (attacker, "You can't do that.") + return false + } + + val spellBook = attacker.spellBookManager.spellBook + if (spellBook != SpellBookManager.SpellBook.MODERN.interfaceId) { + if (shouldSendMessage) + sendMessage (attacker, "The impling is too fast for that.") + return false + } + + val spellId = attacker.properties.spell.spellId + if (spellId != Modern.BIND && spellId != Modern.SNARE && spellId != Modern.ENTANGLE) { + if (shouldSendMessage) + sendMessage (attacker, "The impling is immune to that magic.") + return false + } + + return true + } + + override fun onDeathFinished (self: NPC, killer: Entity) { + if (!isPuroImpling(self)) + ImplingController.deregister(self) + else if (self.originalId != self.id) { //if this is a spawner that transformed + self.reTransform() //turn back into spawner NPC + self.behavior = forId(self.id) + } + } + + //manually clear any rolled drops so any drops set by JSON are properly ignored. + override fun onDropTableRolled (self: NPC, killer: Entity, drops: ArrayList) { + drops.clear() + } + + override fun getClippingSupplier (self: NPC) : ClipMaskSupplier { + return ImplingClipper + } + + private fun isPuroImpling (self: NPC) : Boolean { + return self.id > 6054 + } +} diff --git a/Server/src/main/content/global/skill/hunter/implings/ImplingController.kt b/Server/src/main/content/global/skill/hunter/implings/ImplingController.kt new file mode 100644 index 000000000..c27316a2f --- /dev/null +++ b/Server/src/main/content/global/skill/hunter/implings/ImplingController.kt @@ -0,0 +1,90 @@ +package content.global.skill.hunter.implings + +import core.api.* +import core.tools.* +import core.game.world.map.Location +import core.game.node.entity.npc.NPC +import core.game.system.command.* +import kotlin.math.* + +/** + * Manages the high-level behavior of implings in the overworld: Spawning them, clearing them, etc. + * NOTE: This does not manage the spawns INSIDE puro-puro. Those are actually handled by JSON. +*/ +class ImplingController : TickListener, Commands { + override fun tick() { + if (--nextCycle > getTicksBeforeNextCycleToDespawn()) + return + if (activeImplings.size > 0) { + clearSomeImplings(min(activeImplings.size, implingsClearedPerTick)) + return + } + generateSpawners() + nextCycle = secondsToTicks(60 * 30) // 30 minutes + } + + override fun defineCommands() { + define ("implings", Privilege.ADMIN, "", "Lists the currently active implings/spawners") { player, _ -> + for (i in 0..310) + setInterfaceText(player, "", 275, i) + setInterfaceText(player, "Implings", 275, 2) + for ((index, impling) in activeImplings.withIndex()) { + var text = "This shouldn't be here -> ${impling.id}" + if (impling.id < 1028) { + val table = ImplingSpawner.forId(impling.id) + if (table != null) + text = table.name + } + else text = impling.name + setInterfaceText(player, "$text -> ${impling.location}", 275, index + 11) + } + openInterface(player, 275) + } + } + + companion object { + val implingsClearedPerTick = 5 + + var nextCycle = 0 + var activeImplings = ArrayList() + + fun clearSomeImplings (amount: Int) { + for (i in 0 until amount) { + val impling = activeImplings.removeAt(0) + poofClear(impling) + } + } + + fun generateSpawners () { + val typeLocations = ImplingSpawnLocations.values() + for (set in typeLocations) { + val locations = set.locations + val type = set.type + locations.forEach { generateSpawnersAt(it, type) } + } + } + + fun generateSpawnersAt(location: Location, type: ImplingSpawnTypes) { + for (i in 0 until type.spawnRolls) { + val spawner = type.table.roll() ?: continue + if (spawner == ImplingSpawner.Nothing) continue + val npc = NPC.create (spawner.npcId, location) + npc.init() + activeImplings.add(npc) + } + } + + fun getTicksBeforeNextCycleToDespawn() : Int { + return ceil (activeImplings.size / implingsClearedPerTick.toDouble()).toInt() + } + + fun deregister (impling: NPC, graceful: Boolean = false) : Boolean { + activeImplings.remove(impling) + if (graceful) + poofClear(impling) + else + impling.clear() + return true + } + } +} diff --git a/Server/src/main/content/global/skill/hunter/implings/ImplingData.kt b/Server/src/main/content/global/skill/hunter/implings/ImplingData.kt new file mode 100644 index 000000000..affe2032c --- /dev/null +++ b/Server/src/main/content/global/skill/hunter/implings/ImplingData.kt @@ -0,0 +1,178 @@ +package content.global.skill.hunter.implings + +import core.api.utils.* +import core.game.world.map.* +import core.game.world.map.path.* +import core.game.world.map.build.* + +enum class Impling (val npcId: Int, val puroId: Int) { + Baby (1028, 6055), + Young (1029, 6056), + Gourmet (1030, 6057), + Earth (1031, 6058), + Essence (1032, 6059), + Eclectic (1033, 6060), + Ninja (6053, 6063), + Nature (1034, 6061), + Magpie (1035, 6062), + Dragon (6054, 6064); + + companion object { + fun getIds() : IntArray { + val list = ArrayList() + for (imp in values()){ + list.add(imp.npcId) + list.add(imp.puroId) + } + return list.toIntArray() + } + } +} + +enum class ImplingSpawner (val npcId: Int, val table: WeightedTable) { + LowTier (npcId = 1024, WeightedTable.create + ( + Pair (Impling.Baby, 20.0), + Pair (Impling.Young, 20.0), + Pair (Impling.Gourmet, 20.0), + Pair (Impling.Earth, 20.0), + Pair (Impling.Essence, 10.0), + Pair (Impling.Eclectic,10.0) + )), + MidTier (npcId = 1025, WeightedTable.create + ( + Pair (Impling.Gourmet, 10.0), + Pair (Impling.Earth, 10.0), + Pair (Impling.Essence, 20.0), + Pair (Impling.Eclectic, 37.0), + Pair (Impling.Nature, 20.0), + Pair (Impling.Magpie, 2.0), + Pair (Impling.Ninja, 1.0) + )), + HighTier (npcId = 1026, WeightedTable.create + ( + Pair (Impling.Nature, 10.0), + Pair (Impling.Magpie, 50.0), + Pair (Impling.Ninja, 30.0), + Pair (Impling.Dragon, 10.0) + )), + LowPuroTier (npcId = 6065, WeightedTable.create + ( + Pair (Impling.Baby, 20.0), + Pair (Impling.Young, 20.0), + Pair (Impling.Gourmet, 20.0), + Pair (Impling.Earth, 20.0), + Pair (Impling.Essence, 10.0), + Pair (Impling.Eclectic,10.0) + )), + MidPuroTier (npcId = 6066, WeightedTable.create + ( + Pair (Impling.Gourmet, 10.0), + Pair (Impling.Earth, 10.0), + Pair (Impling.Essence, 20.0), + Pair (Impling.Eclectic, 37.0), + Pair (Impling.Nature, 20.0), + Pair (Impling.Magpie, 2.0), + Pair (Impling.Ninja, 1.0) + )), + HighPuroTier (npcId = 6067, WeightedTable.create + ( + Pair (Impling.Nature, 150.0), + Pair (Impling.Magpie, 114.0), + Pair (Impling.Ninja, 37.0), + Pair (Impling.Dragon, 10.0), + )), + Nothing (npcId = -1, WeightedTable()); + + companion object { + private val idMap = values().map { it.npcId to it }.toMap() + + fun forId (id: Int) : ImplingSpawner? { + return idMap[id] + } + + fun getIds() : IntArray { + return idMap.keys.toIntArray() + } + } +} + +enum class ImplingSpawnTypes (val table: WeightedTable, val spawnRolls: Int) { + Standard (WeightedTable.create + ( + Pair (ImplingSpawner.LowTier, 14.0), + Pair (ImplingSpawner.MidTier, 7.0), + Pair (ImplingSpawner.HighTier, 4.0), + Pair (ImplingSpawner.Nothing, 75.0) + ), spawnRolls = 3), + LowTierOnly (WeightedTable.create + ( + Pair (ImplingSpawner.LowTier, 100.0) + ), spawnRolls = 1), + MidTierOnly (WeightedTable.create + ( + Pair (ImplingSpawner.MidTier, 100.0) + ), spawnRolls = 1), + HighTierOnly (WeightedTable.create + ( + Pair (ImplingSpawner.HighTier, 100.0) + ), spawnRolls = 1), + HighPuroTierOnly (WeightedTable.create + ( + Pair (ImplingSpawner.HighPuroTier, 100.0) + ), spawnRolls = 1) +} + +enum class ImplingSpawnLocations (val type: ImplingSpawnTypes, vararg val locations: Location) { + StandardSpawns (ImplingSpawnTypes.Standard, + Location.create(2204, 3232, 0), + Location.create(2582, 2974, 0), + Location.create(2522, 3105, 0), + Location.create(2470, 3221, 0), + Location.create(2593, 3251, 0), + Location.create(2735, 3354, 0), + Location.create(2646, 3424, 0), + Location.create(2462, 3429, 0), + Location.create(2386, 3513, 0), + Location.create(2335, 3649, 0), + Location.create(2740, 3536, 0), + Location.create(2654, 3609, 0), + Location.create(2724, 3769, 0), + Location.create(2817, 3513, 0), + Location.create(2844, 3154, 0), + Location.create(2844, 3033, 0), + Location.create(2841, 2926, 0), + Location.create(2907, 3491, 0), + Location.create(3020, 3525, 0), + Location.create(3021, 3424, 0), + Location.create(2981, 3276, 0), + Location.create(3135, 3377, 0), + Location.create(3149, 3233, 0), + Location.create(3170, 3004, 0), + Location.create(3239, 3289, 0), + Location.create(3287, 3271, 0), + Location.create(3418, 3124, 0), + Location.create(3356, 3010, 0), + Location.create(3550, 3529, 0), + Location.create(3449, 3488, 0), + Location.create(3441, 3352, 0) + ), + LowTierOnlySpawns (ImplingSpawnTypes.LowTierOnly, + Location.create(2348, 3610, 0), + Location.create(2277, 3186, 0), + Location.create(2459, 3085, 0), + Location.create(2564, 3393, 0), + Location.create(2780, 3463, 0), + Location.create(2966, 3411, 0), + Location.create(3094, 3237, 0), + Location.create(3281, 3427, 0), + Location.create(3278, 3160, 0) + ) +} + +object ImplingClipper : ClipMaskSupplier { + override fun getClippingFlag (z: Int, x: Int, y: Int) : Int { + var flag = RegionManager.getClippingFlag(z, x, y) + return flag and (RegionFlags.SOLID_TILE.inv()) and (RegionFlags.TILE_OBJECT.inv()) //Allow walking on water and flying over small objects, but keep all other tile flags the same. + } +} diff --git a/Server/src/main/content/global/skill/hunter/implings/ImplingSpawnerBehavior.kt b/Server/src/main/content/global/skill/hunter/implings/ImplingSpawnerBehavior.kt new file mode 100644 index 000000000..ad49144ec --- /dev/null +++ b/Server/src/main/content/global/skill/hunter/implings/ImplingSpawnerBehavior.kt @@ -0,0 +1,52 @@ +package content.global.skill.hunter.implings + +import core.api.* +import core.tools.* +import core.game.node.entity.npc.* +import core.game.world.map.path.ClipMaskSupplier + +/** + * Manages the behavior for impling spawners (the invisible NPCs that turn into random implings). +*/ +class ImplingSpawnerBehavior : NPCBehavior (*ImplingSpawner.getIds()) { + override fun onCreation (self: NPC) { + val isPuro = isPuroSpawner(self) + val delay = if (isPuro) 120 else 180 + setAttribute(self, "transformTime", getWorldTicks() + secondsToTicks(delay)) + self.setRespawnTicks(3) + self.isRespawn = isPuro + self.walkRadius = if (isPuro) 20 else 100 + self.isWalks = true + self.isNeverWalks = false + self.setInvisible(true) + } + + override fun onRespawn (self: NPC) { + if (!isPuroSpawner(self)) + log (this::class.java, Log.ERR, "Non-puro spawner has respawned!") + this.onCreation(self) + } + + override fun tick (self: NPC) : Boolean { + var transformTime = getAttribute(self, "transformTime", 0) + if (transformTime != 0 && transformTime <= getWorldTicks()) { + val table = ImplingSpawner.forId(self.id)?.table ?: return ImplingController.deregister(self) + val impling = table.roll() ?: return ImplingController.deregister(self) + val targetId = if (isPuroSpawner(self)) impling.puroId else impling.npcId + self.transform(targetId) + self.behavior = forId(self.id) + self.setInvisible(false) + removeAttribute(self, "transformTime") + sendGraphics(1119, self.location) + } + return true + } + + override fun getClippingSupplier (self: NPC) : ClipMaskSupplier { + return ImplingClipper + } + + private fun isPuroSpawner(self: NPC) : Boolean { + return self.id > 6000 + } +} 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 6a5d97383..f4959cbae 100644 --- a/Server/src/main/content/global/skill/hunter/pitfall/HunterPitfall.kt +++ b/Server/src/main/content/global/skill/hunter/pitfall/HunterPitfall.kt @@ -1,9 +1,9 @@ -import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeUnit import core.api.* import core.game.node.entity.Entity import core.game.node.entity.combat.CombatStyle -import core.game.node.entity.impl.Animator.Priority; +import core.game.node.entity.impl.Animator.Priority import core.game.node.entity.impl.ForceMovement import core.game.node.entity.npc.AbstractNPC import core.game.node.entity.player.Player @@ -22,6 +22,7 @@ import org.rs09.consts.NPCs import core.game.interaction.InteractionListener import core.game.interaction.IntType import core.game.world.GameWorld +import org.rs09.consts.Sounds /*@Initializable class HunterPitfall : OptionHandler() { @@ -60,28 +61,28 @@ val HUNTER_REQS = hashMapOf( "Sabre-toothed kyatt" to 55, ) //val pitVarpOffsets = hashMapOf( 19264 to 3, 19265 to 6, 19266 to 9, 19267 to 12, 19268 to 15,) -data class Pit(val varpId: Int, val varpOffset: Int, val horizontal: Boolean) +data class Pit(val varbitId: Int, val horizontal: Boolean) val pitVarps = hashMapOf( // Larupia pits (the duplicate 24 is likely authentic) - Location.create(2565,2888) to Pit(917, 27, true), - Location.create(2573,2885) to Pit(918, 0, false), - Location.create(2556,2893) to Pit(917, 18, false), - Location.create(2552,2904) to Pit(917, 24, true), - Location.create(2543,2908) to Pit(917, 21, false), - Location.create(2538,2899) to Pit(917, 24, true), + Location.create(2565,2888) to Pit(2967, true), + Location.create(2573,2885) to Pit(2968, false), + Location.create(2556,2893) to Pit(2964, false), + Location.create(2552,2904) to Pit(2966, true), + Location.create(2543,2908) to Pit(2965, false), + Location.create(2538,2899) to Pit(2966, true), // Kyatt pits - Location.create(2700,3795) to Pit(917, 0, true), - Location.create(2700,3785) to Pit(917, 3, false), - Location.create(2706,3789) to Pit(917, 6, false), - Location.create(2730,3791) to Pit(917, 9, true), - Location.create(2737,3784) to Pit(917, 12, true), - Location.create(2730,3780) to Pit(917, 15, false), + Location.create(2700,3795) to Pit(2958, true), + Location.create(2700,3785) to Pit(2959, false), + Location.create(2706,3789) to Pit(2960, false), + Location.create(2730,3791) to Pit(2961, true), + Location.create(2737,3784) to Pit(2962, true), + Location.create(2730,3780) to Pit(2963, false), // Graahk pits - Location.create(2766,3010) to Pit(918, 3, false), - Location.create(2762,3005) to Pit(918, 6, false), - Location.create(2771,3004) to Pit(918, 9, true), - Location.create(2777,3001) to Pit(918, 12, false), - Location.create(2784,3001) to Pit(918, 15, true), + Location.create(2766,3010) to Pit(2969, false), + Location.create(2762,3005) to Pit(2970, false), + Location.create(2771,3004) to Pit(2971, true), + Location.create(2777,3001) to Pit(2972, false), + Location.create(2784,3001) to Pit(2973, true), ) /*val pitJumpSpots = hashMapOf( Location.create(2766,3010) to hashMapOf( @@ -166,7 +167,7 @@ class PitfallListeners : InteractionListener { return@setDest dst } on(PIT, IntType.SCENERY, "trap") { player, node -> - val pit = node as Scenery; + val pit = node as Scenery // TODO: check hunter level, remove logs if(player.skills.getLevel(Skills.HUNTER) < 31) { player.sendMessage("You need a hunter level of 31 to set a pitfall trap.") @@ -187,6 +188,7 @@ class PitfallListeners : InteractionListener { player.setAttribute("pitfall:timestamp:${pit.location.x}:${pit.location.y}", System.currentTimeMillis()) setPitState(player, pit.location, 1) + playAudio(player, Sounds.HUNTING_PLACEBRANCHES_2639) val collapsePulse = object : Pulse(201, player) { override fun pulse(): Boolean { val oldTime = player.getAttribute("pitfall:timestamp:${pit.location.x}:${pit.location.y}", System.currentTimeMillis()) @@ -202,12 +204,13 @@ class PitfallListeners : InteractionListener { return@on true } on(SPIKED_PIT, IntType.SCENERY, "jump") { player, node -> - val pit = node as Scenery; + val pit = node as Scenery val src = player.getLocation() val dir = pitJumpSpots(pit.getLocation())!![src] if(dir != null) { val dst = src.transform(dir, 3) - ForceMovement.run(player, src, dst, ForceMovement.WALK_ANIMATION, Animation(1603), dir, 16); + ForceMovement.run(player, src, dst, ForceMovement.WALK_ANIMATION, Animation(1603), dir, 16) + playAudio(player, Sounds.HUNTING_JUMP_2635) val pitfall_npc: Entity? = player.getAttribute("pitfall_npc", null) if(pitfall_npc != null && pitfall_npc.getLocation().getDistance(src) < 3.0) { val last_pit_loc: Location? = pitfall_npc.getAttribute("last_pit_loc", null) @@ -222,6 +225,9 @@ class PitfallListeners : InteractionListener { //pitfall_npc.setLocation(pit.getLocation()); //pitfall_npc.walkingQueue.addPath(pit.location.x, pit.location.y); teleport(pitfall_npc, pit.location) + pitfall_npc.removeAttribute("last_pit_loc") + playAudio(player, Sounds.HUNTING_PITFALL_COLLAPSE_2638, 0, 1, pit.location, 10) + playAudio(player, Sounds.PANTHER_DEATH_667, 50, 1, pit.location, 10) pitfall_npc.startDeath(null) player.removeAttribute("pitfall:timestamp:${pit.location.x}:${pit.location.y}") player.incrementAttribute("pitfall:count", -1) @@ -233,6 +239,7 @@ class PitfallListeners : InteractionListener { val npcdst = dst.transform(dir, if(dir == Direction.SOUTH || dir == Direction.WEST) 1 else 0) teleport(pitfall_npc, npcdst) pitfall_npc.animate(Animation(5232, Priority.HIGH)) + playAudio(player, Sounds.HUNTING_BIGCAT_JUMP_2619, 0, 1, pit.location, 10) pitfall_npc.attack(player) pitfall_npc.setAttribute("last_pit_loc", pit.location) } @@ -241,7 +248,8 @@ class PitfallListeners : InteractionListener { return@on true } on(SPIKED_PIT, IntType.SCENERY, "dismantle") { player, node -> - val pit = node as Scenery; + val pit = node as Scenery + playAudio(player, Sounds.HUNTING_TAKEBRANCHES_2649) player.removeAttribute("pitfall:timestamp:${pit.location.x}:${pit.location.y}") player.incrementAttribute("pitfall:count", -1) setPitState(player, pit.location, 0) @@ -249,14 +257,17 @@ class PitfallListeners : InteractionListener { } on(LARUPIA_PIT, IntType.SCENERY, "dismantle") { player, node -> lootCorpse(player, node as Scenery, 180.0, Items.LARUPIA_FUR_10095, Items.TATTY_LARUPIA_FUR_10093) + sendMessage(player, "You've caught a spined larupia!") return@on true } on(GRAAHK_PIT, IntType.SCENERY, "dismantle") { player, node -> lootCorpse(player, node as Scenery, 240.0, Items.GRAAHK_FUR_10099, Items.TATTY_GRAAHK_FUR_10097) + sendMessage(player, "You've caught a horned graahk!") return@on true } on(KYATT_PIT, IntType.SCENERY, "dismantle") { player, node -> lootCorpse(player, node as Scenery, 300.0, Items.KYATT_FUR_10103, Items.TATTY_KYATT_FUR_10101) + sendMessage(player, "You've caught a sabretoothed kyatt!") return@on true } on(BEAST_IDS, IntType.NPC, "tease") { player, node -> @@ -271,6 +282,7 @@ class PitfallListeners : InteractionListener { return@on true } entity.attack(player) + playAudio(player, Sounds.HUNTING_TEASE_FELINE_2651) player.setAttribute("pitfall_npc", entity) return@on true } @@ -278,12 +290,13 @@ class PitfallListeners : InteractionListener { fun lootCorpse(player: Player, pit: Scenery, xp: Double, goodFur: Int, badFur: Int) { if(player.inventory.freeSlots() < 2) { - player.sendMessage("You don't have enough inventory space. You need 2 more free slots."); + player.sendMessage("You don't have enough inventory space. You need 2 more free slots.") return } setPitState(player, pit.location, 0) - player.getSkills().addExperience(Skills.HUNTER, xp, true); + player.getSkills().addExperience(Skills.HUNTER, xp, true) player.inventory.add(Item(Items.BIG_BONES_532)) + playAudio(player, Sounds.HUNTING_TAKEBRANCHES_2649) // TODO: what's the actual probability of tatty vs perfect fur? val chance = RandomFunction.getSkillSuccessChance(50.0, 100.0, player.skills.getLevel(Skills.HUNTER)) if(RandomFunction.random(0.0, 100.0) < chance) { @@ -295,7 +308,7 @@ class PitfallListeners : InteractionListener { fun setPitState(player: Player, loc: Location, state: Int) { val pit = pitVarps[loc]!! - player.varpManager.get(pit.varpId).setVarbit(pit.varpOffset, state).send(player) + setVarbit(player, pit.varbitId, state) } } diff --git a/Server/src/main/content/global/skill/hunter/tracking/CommonKebbitEast.kt b/Server/src/main/content/global/skill/hunter/tracking/CommonKebbitEast.kt index 68f414773..a78e64fe7 100644 --- a/Server/src/main/content/global/skill/hunter/tracking/CommonKebbitEast.kt +++ b/Server/src/main/content/global/skill/hunter/tracking/CommonKebbitEast.kt @@ -14,22 +14,22 @@ class CommonKebbitEast : HunterTracking() { init { initialMap = hashMapOf( 19439 to arrayListOf( - TrailDefinition(0,TrailType.LINKING, false,Location.create(2354, 3595, 0),Location.create(2360, 3602, 0)), - TrailDefinition(3,TrailType.LINKING,false,Location.create(2354, 3595, 0),Location.create(2355, 3601, 0)), - TrailDefinition(6,TrailType.LINKING,false,Location.create(2354, 3594, 0),Location.create(2349, 3604, 0)) + TrailDefinition(2974,TrailType.LINKING, false,Location.create(2354, 3595, 0),Location.create(2360, 3602, 0)), + TrailDefinition(2975,TrailType.LINKING,false,Location.create(2354, 3595, 0),Location.create(2355, 3601, 0)), + TrailDefinition(2976,TrailType.LINKING,false,Location.create(2354, 3594, 0),Location.create(2349, 3604, 0)) ), 19440 to arrayListOf( - TrailDefinition(18,TrailType.LINKING,true,Location.create(2361, 3611, 0),Location.create(2360, 3602, 0)), - TrailDefinition(21,TrailType.LINKING,true,Location.create(2360, 3612, 0),Location.create(2357, 3607, 0)) + TrailDefinition(2980,TrailType.LINKING,true,Location.create(2361, 3611, 0),Location.create(2360, 3602, 0)), + TrailDefinition(2981,TrailType.LINKING,true,Location.create(2360, 3612, 0),Location.create(2357, 3607, 0)) ) ) linkingTrails = arrayListOf( - TrailDefinition(24,TrailType.LINKING,false,Location.create(2357, 3607, 0),Location.create(2354, 3609, 0),Location.create(2355, 3608, 0)), - TrailDefinition(27,TrailType.LINKING,false,Location.create(2354, 3609, 0),Location.create(2349, 3604, 0),Location.create(2351, 3608, 0)), - TrailDefinition(9,TrailType.LINKING,false ,Location.create(2360, 3602, 0),Location.create(2355, 3601, 0),Location.create(2358, 3599, 0)), - TrailDefinition(12,TrailType.LINKING,false,Location.create(2355, 3601, 0),Location.create(2349, 3604, 0),Location.create(2352, 3603, 0)), - TrailDefinition(15,TrailType.LINKING,false,Location.create(2360, 3602, 0),Location.create(2357, 3607, 0),Location.create(2358, 3603, 0)) + TrailDefinition(2982,TrailType.LINKING,false,Location.create(2357, 3607, 0),Location.create(2354, 3609, 0),Location.create(2355, 3608, 0)), + TrailDefinition(2983,TrailType.LINKING,false,Location.create(2354, 3609, 0),Location.create(2349, 3604, 0),Location.create(2351, 3608, 0)), + TrailDefinition(2977,TrailType.LINKING,false ,Location.create(2360, 3602, 0),Location.create(2355, 3601, 0),Location.create(2358, 3599, 0)), + TrailDefinition(2978,TrailType.LINKING,false,Location.create(2355, 3601, 0),Location.create(2349, 3604, 0),Location.create(2352, 3603, 0)), + TrailDefinition(2979,TrailType.LINKING,false,Location.create(2360, 3602, 0),Location.create(2357, 3607, 0),Location.create(2358, 3603, 0)) ) experience = 36.0 varp = 919 @@ -71,4 +71,4 @@ class CommonKebbitEast : HunterTracking() { return this } -} \ No newline at end of file +} diff --git a/Server/src/main/content/global/skill/hunter/tracking/HunterTracking.kt b/Server/src/main/content/global/skill/hunter/tracking/HunterTracking.kt index 6b21f5990..3a56ee557 100644 --- a/Server/src/main/content/global/skill/hunter/tracking/HunterTracking.kt +++ b/Server/src/main/content/global/skill/hunter/tracking/HunterTracking.kt @@ -1,5 +1,6 @@ package content.global.skill.hunter.tracking +import core.api.* import core.game.interaction.OptionHandler import core.game.node.Node import core.game.node.scenery.Scenery @@ -12,8 +13,9 @@ import core.game.world.map.Location import core.game.world.update.flag.context.Animation import core.tools.RandomFunction import org.rs09.consts.Items -import core.tools.SystemLogger import core.game.world.GameWorld +import core.tools.Log +import org.rs09.consts.Sounds import java.util.* /** @@ -50,7 +52,7 @@ abstract class HunterTracking : OptionHandler(){ val trail = player.getAttribute(attribute, ArrayList()) val initialTrail = getInitialTrail(startobj) if(initialTrail == null) { - SystemLogger.logWarn(this::class.java, "UNHANDLED STARTING OBJECT FOR HUNTER TRACKING $startobj") + log(this::class.java, Log.WARN, "UNHANDLED STARTING OBJECT FOR HUNTER TRACKING $startobj") return } trail.add(initialTrail) @@ -68,7 +70,7 @@ abstract class HunterTracking : OptionHandler(){ nextTrail ?: continue var offsetUsed = false for(i in trail){ - if(i.offset == nextTrail.offset){ offsetUsed = true; break } + if(i.varbit == nextTrail.varbit){ offsetUsed = true; break } } if(offsetUsed) continue if(nextTrail.type == TrailType.TUNNEL){ @@ -92,7 +94,7 @@ abstract class HunterTracking : OptionHandler(){ val possibleTrails = ArrayList() for(trail in linkingTrails){ val invTrail = getTrailInverse(trail,false) - if(invTrail.type == TrailType.TUNNEL && previousTrail.endLocation.withinDistance(invTrail.startLocation,5) && !previousTrail.endLocation.equals(invTrail.startLocation) && previousTrail.offset != trail.offset){ + if(invTrail.type == TrailType.TUNNEL && previousTrail.endLocation.withinDistance(invTrail.startLocation,5) && !previousTrail.endLocation.equals(invTrail.startLocation) && previousTrail.varbit != trail.varbit){ possibleTrails.add(trail) } } @@ -100,7 +102,7 @@ abstract class HunterTracking : OptionHandler(){ } val possibleTrails = ArrayList() for(trail in linkingTrails){ - if(trail.startLocation.equals(previousTrail.endLocation) && previousTrail.offset != trail.offset){ + if(trail.startLocation.equals(previousTrail.endLocation) && previousTrail.varbit != trail.varbit){ possibleTrails.add(trail) } } @@ -114,8 +116,8 @@ abstract class HunterTracking : OptionHandler(){ */ fun getTrailInverse(trail: TrailDefinition, swapLocations: Boolean): TrailDefinition{ if(swapLocations) - return TrailDefinition(trail.offset,if(tunnelEntrances.contains(trail.startLocation)) TrailType.TUNNEL else TrailType.LINKING,!trail.inverted,trail.endLocation,trail.startLocation, trail.triggerObjectLocation) - return TrailDefinition(trail.offset, if(tunnelEntrances.contains(trail.startLocation)) TrailType.TUNNEL else TrailType.LINKING, !trail.inverted, trail.startLocation, trail.endLocation) + return TrailDefinition(trail.varbit,if(tunnelEntrances.contains(trail.startLocation)) TrailType.TUNNEL else TrailType.LINKING,!trail.inverted,trail.endLocation,trail.startLocation, trail.triggerObjectLocation) + return TrailDefinition(trail.varbit, if(tunnelEntrances.contains(trail.startLocation)) TrailType.TUNNEL else TrailType.LINKING, !trail.inverted, trail.startLocation, trail.endLocation) } /** @@ -143,8 +145,7 @@ abstract class HunterTracking : OptionHandler(){ fun clearTrail(player: Player){ player.removeAttribute(attribute) player.removeAttribute(indexAttribute) - player.varpManager.get(varp).clear() - player.varpManager.get(varp).send(player) + setVarp(player, varp, 0) } /** @@ -163,6 +164,7 @@ abstract class HunterTracking : OptionHandler(){ fun reward(player: Player, success: Boolean) { player.lock() player.animator.animate(if(success) KEBBIT_ANIM else MISS_ANIM) + playAudio(player, Sounds.HUNTING_NOOSE_2637) GameWorld.Pulser.submit(object : Pulse(KEBBIT_ANIM.duration){ override fun pulse(): Boolean { if(hasTrail(player) && success){ @@ -189,10 +191,9 @@ abstract class HunterTracking : OptionHandler(){ val trailIndex = player.getAttribute(indexAttribute,0) for(index in 0..trailIndex){ val trl = trail[index] - player.varpManager.get(varp).setVarbit(trl.offset + 2,1) - player.varpManager.get(varp).setVarbit(trl.offset,if(trl.inverted) 1 else 0) + var current = getVarp(player, varp) + setVarbit(player, trl.varbit, (if (trl.inverted) 1 else 0) or (1 shl 2)) } - player.varpManager.get(varp).send(player) } /** @@ -262,4 +263,4 @@ abstract class HunterTracking : OptionHandler(){ fun hasNooseWand(player: Player) : Boolean{ return player.equipment.contains(Items.NOOSE_WAND_10150,1) || player.inventory.contains(Items.NOOSE_WAND_10150,1) } -} \ No newline at end of file +} diff --git a/Server/src/main/content/global/skill/hunter/tracking/PolarKebbitHunting.kt b/Server/src/main/content/global/skill/hunter/tracking/PolarKebbitHunting.kt index b933659b7..92e874416 100644 --- a/Server/src/main/content/global/skill/hunter/tracking/PolarKebbitHunting.kt +++ b/Server/src/main/content/global/skill/hunter/tracking/PolarKebbitHunting.kt @@ -27,20 +27,20 @@ class PolarKebbitHunting : HunterTracking() { ) initialMap = hashMapOf( 19640 to arrayListOf( - TrailDefinition(24,TrailType.TUNNEL,false,Location.create(2712, 3831, 1),Location.create(2718, 3832, 1)), - TrailDefinition(21,TrailType.LINKING,true,Location.create(2712, 3831, 1),Location.create(2716, 3827, 1),Location.create(2713,3827,1)), - TrailDefinition(12,TrailType.LINKING,false,Location.create(2712, 3831, 1),Location.create(2708, 3819, 1),Location.create(2708,3825,1)) + TrailDefinition(3061,TrailType.TUNNEL,false,Location.create(2712, 3831, 1),Location.create(2718, 3832, 1)), + TrailDefinition(3060,TrailType.LINKING,true,Location.create(2712, 3831, 1),Location.create(2716, 3827, 1),Location.create(2713,3827,1)), + TrailDefinition(3057,TrailType.LINKING,false,Location.create(2712, 3831, 1),Location.create(2708, 3819, 1),Location.create(2708,3825,1)) ), 19641 to arrayListOf( - TrailDefinition(0,TrailType.LINKING,true,Location.create(2718, 3820, 1),Location.create(2708, 3819, 1),Location.create(2712,3815,1)), - TrailDefinition(6,TrailType.TUNNEL,false, Location.create(2718, 3820, 1),Location.create(2715, 3820, 1)), - TrailDefinition(9,TrailType.TUNNEL,false,Location.create(2718, 3820, 1),Location.create(2721, 3827, 1)) + TrailDefinition(3053,TrailType.LINKING,true,Location.create(2718, 3820, 1),Location.create(2708, 3819, 1),Location.create(2712,3815,1)), + TrailDefinition(3055,TrailType.TUNNEL,false, Location.create(2718, 3820, 1),Location.create(2715, 3820, 1)), + TrailDefinition(3056,TrailType.TUNNEL,false,Location.create(2718, 3820, 1),Location.create(2721, 3827, 1)) ) ) linkingTrails = arrayListOf( - TrailDefinition(15,TrailType.LINKING,true,Location.create(2714,3821,1),Location.create(2716, 3827, 1)), - TrailDefinition(18,TrailType.TUNNEL,true,Location.create(2716, 3827, 1),Location.create(2718,3829,1)), - TrailDefinition(3,TrailType.TUNNEL,false,Location.create(2708, 3819, 1),Location.create(2711, 3819, 1)) + TrailDefinition(3058,TrailType.LINKING,true,Location.create(2714,3821,1),Location.create(2716, 3827, 1)), + TrailDefinition(3059,TrailType.TUNNEL,true,Location.create(2716, 3827, 1),Location.create(2718,3829,1)), + TrailDefinition(3054,TrailType.TUNNEL,false,Location.create(2708, 3819, 1),Location.create(2711, 3819, 1)) ) experience = 30.0 varp = 926 @@ -65,4 +65,4 @@ class PolarKebbitHunting : HunterTracking() { SceneryDefinition.forId(19435).handlers["option:attack"] = this return this } -} \ No newline at end of file +} diff --git a/Server/src/main/content/global/skill/hunter/tracking/TrailDefinition.kt b/Server/src/main/content/global/skill/hunter/tracking/TrailDefinition.kt index 70a223276..a3af8d305 100644 --- a/Server/src/main/content/global/skill/hunter/tracking/TrailDefinition.kt +++ b/Server/src/main/content/global/skill/hunter/tracking/TrailDefinition.kt @@ -2,13 +2,13 @@ package content.global.skill.hunter.tracking import core.game.world.map.Location -class TrailDefinition(val offset: Int, val type: TrailType, var inverted: Boolean, val startLocation: Location, val endLocation: Location, val triggerObjectLocation: Location = endLocation){ +class TrailDefinition(val varbit: Int, val type: TrailType, var inverted: Boolean, val startLocation: Location, val endLocation: Location, val triggerObjectLocation: Location = endLocation){ override fun toString(): String { - return "$startLocation $endLocation [offset: $offset] [${type.name}] [inverted: $inverted]" + return "$startLocation $endLocation [varbit: $varbit] [${type.name}] [inverted: $inverted]" } } enum class TrailType{ LINKING, INITIAL, TUNNEL -} \ 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/SpellListener.kt b/Server/src/main/content/global/skill/magic/SpellListener.kt index 95498b3a1..c9ba1b177 100644 --- a/Server/src/main/content/global/skill/magic/SpellListener.kt +++ b/Server/src/main/content/global/skill/magic/SpellListener.kt @@ -1,5 +1,7 @@ package content.global.skill.magic +import core.api.playAudio +import core.api.playGlobalAudio import core.api.setAttribute import core.cache.def.impl.ItemDefinition import core.game.node.Node @@ -26,12 +28,12 @@ abstract class SpellListener(val bookName: String) : Listener { @JvmField val GROUND_ITEM = -6 } - fun onCast(spellID: Int,type: Int,method: (player: Player, node: Node?) -> Unit){ - SpellListeners.add(spellID,type,bookName,method) + fun onCast(spellID: Int, type: Int, range: Int = 10, method: (player: Player, node: Node?) -> Unit){ + SpellListeners.add(spellID, type, bookName, range, method) } - fun onCast(spellID: Int, type: Int, vararg ids: Int, method: (player: Player, node: Node?) -> Unit){ - SpellListeners.add(spellID,type,ids,bookName,method) + fun onCast(spellID: Int, type: Int, vararg ids: Int, range: Int = 10, method: (player: Player, node: Node?) -> Unit){ + SpellListeners.add(spellID, type, ids, bookName, range, method) } fun requires(player: Player, magicLevel: Int = 0, runes: Array = arrayOf(), specialEquipment: IntArray = intArrayOf()) { @@ -60,7 +62,12 @@ abstract class SpellListener(val bookName: String) : Listener { } fun removeRunes(player: Player,removeAttr: Boolean = true){ + /* Snowscape modification: remove runes from the inventory or rune satchel player.inventory.remove(*player.getAttribute("spell:runes",ArrayList()).toTypedArray()) + */ + player.getAttribute("spell:runes",ArrayList()).toTypedArray().forEach { rune -> + if (!player.inventory.remove(rune)) player.runeSatchel.remove(rune) + } if(removeAttr) { player.removeAttribute("spell:runes") player.removeAttribute("tablet-spell") @@ -72,11 +79,38 @@ abstract class SpellListener(val bookName: String) : Listener { player.skills.addExperience(Skills.MAGIC,amount) } - fun visualizeSpell(player: Player,anim:Animation,gfx: Graphics,soundID: Int = -1){ + /** + * @param player The player to visualize the spell on + * @param anim The animation object. I.e. Animation(Animations.LUNAR_SPELLBOOK_*) + * @param gfx The graphics object. I.e. Graphics(Graphics.LUNAR_SPELLBOOK_*, height in int) + * @param soundID The sound to play, either raw integer or from the Sounds ConstLib. Defaults to -1 (Nothing). + * @param delay The delay that should be applied before the sound plays, defaults to 0. + * @param global Whether the sound should be played globally instead of per-player. Defaults to true. + */ + fun visualizeSpell(player: Player, anim: Animation, gfx: Graphics, soundID: Int = -1, delay: Int = 0, global: Boolean = true){ if(player.getAttribute("tablet-spell",false)) return - player.visualize(anim,gfx) + player.visualize(anim, gfx) if(soundID != -1){ - player.audioManager.send(soundID) + if(global) playGlobalAudio(player.location, soundID, delay) + else playAudio(player, soundID, delay) + } + } + + /** + * @param player The player to visualize the spell on + * @param anim The integer ID of the animation, found in the Animations ConstLib. + * @param gfx The integer ID of the graphics to show, found in the Graphics ConstLib. + * @param height How high the graphics should display above the ground(?). Defaults to 0. + * @param soundID The sound to play, either raw integer or from the Sounds ConstLib. Defaults to -1 (Nothing). + * @param delay The delay that should be applied before the sound plays, defaults to 0. + * @param global Whether the sound should be played globally instead of per-player. Defaults to true. + */ + fun visualizeSpell(player: Player, anim: Int, gfx: Int, height: Int = 0, soundID: Int = -1, delay: Int = 0, global: Boolean = true) { + if(player.getAttribute("tablet-spell",false)) return + player.visualize(Animation(anim), Graphics(gfx, height)) + if(soundID != -1){ + if(global) playGlobalAudio(player.location, soundID, delay) + else playAudio(player, soundID, delay) } } @@ -95,4 +129,4 @@ abstract class SpellListener(val bookName: String) : Listener { fun showMagicTab(player: Player){ player.interfaceManager.setViewedTab(6) } -} \ 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 d90356132..95ff686a0 100644 --- a/Server/src/main/content/global/skill/magic/SpellListeners.kt +++ b/Server/src/main/content/global/skill/magic/SpellListeners.kt @@ -1,40 +1,81 @@ package content.global.skill.magic +import core.game.event.SpellCastEvent +import core.api.* 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 object SpellListeners { val castMap = HashMap Unit>() + val spellRanges = HashMap() - fun add(spellID: Int, type: Int, book: String, method: (Player,Node?) -> Unit){ + fun add(spellID: Int, type: Int, book: String, distance: Int, method: (Player,Node?) -> Unit){ castMap["$book:$spellID:$type"] = method + spellRanges["$book:$spellID:$type"] = distance } - fun add(spellID: Int, type: Int, ids: IntArray, book: String, method: (Player, Node?) -> Unit){ + fun add(spellID: Int, type: Int, ids: IntArray, book: String, distance: Int, method: (Player, Node?) -> Unit){ for(id in ids) { castMap["$book:$spellID:$type:$id"] = method + spellRanges["$book:$spellID:$type:$id"] = distance } } - fun get(spellID: Int, type: Int, book: String): ((Player,Node?) -> Unit)?{ - SystemLogger.logInfo(this::class.java, "Getting $book:$spellID:$type") - return castMap["$book:$spellID:$type"] + fun get(spellID: Int, type: Int, book: String): Pair Unit)?> { + log(this::class.java, Log.FINE, "Getting $book:$spellID:$type") + return Pair (spellRanges["$book:$spellID:$type"] ?: 10, castMap["$book:$spellID:$type"]) } - fun get(spellID: Int, type: Int, id: Int, book: String): ((Player,Node?) -> Unit)?{ - SystemLogger.logInfo(this::class.java, "Getting $book:$spellID:$type:$id") - return castMap["$book:$spellID:$type:$id"] + fun get(spellID: Int, type: Int, id: Int, book: String): Pair Unit)?> { + log(this::class.java, Log.FINE, "Getting $book:$spellID:$type:$id") + return Pair (spellRanges["$book:$spellID:$type:$id"] ?: 10, castMap["$book:$spellID:$type:$id"]) } @JvmStatic fun run(button: Int, type: Int, book: String, player: Player, node: Node? = null){ - val method = get(button,type,node?.id ?: 0,book) ?: get(button,type,book) ?: return - try { - method.invoke(player, node) - } catch (e: IllegalStateException){ - player.removeAttribute("spell:runes") - return + var (range, method) = get (button, type, node?.id ?: 0, book) + if (method == null) { + var next = get (button, type, book) + range = next.first + method = next.second ?: return + } + + 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 { + try { + method?.invoke (player, node) + } catch (e: IllegalStateException) { + player.removeAttribute ("spell:runes") + return true + } + return true + } + + override fun update () : Boolean { + if (player.location.withinMaxnormDistance (node!!.centerLocation, range) && hasLineOfSight (player, node!!)) { + player.faceLocation (node.getFaceLocation(player.location)) + player.walkingQueue.reset() + pulse() + stop() + return true + } + return super.update() + } + }) + } else { + try { + method?.invoke(player, node) + player.dispatch(SpellCastEvent(SpellBookManager.SpellBook.valueOf(book.uppercase()), button, node)) + } catch (e: IllegalStateException){ + player.removeAttribute("spell:runes") + return + } } } -} \ No newline at end of file +} diff --git a/Server/src/main/content/global/skill/magic/SpellTablets.kt b/Server/src/main/content/global/skill/magic/SpellTablets.kt index f6b8f428e..62b550f63 100644 --- a/Server/src/main/content/global/skill/magic/SpellTablets.kt +++ b/Server/src/main/content/global/skill/magic/SpellTablets.kt @@ -1,12 +1,14 @@ package content.global.skill.magic import content.global.skill.magic.spellconsts.Modern +import core.api.playAudio import core.game.node.entity.player.Player import core.game.node.item.Item import core.game.world.update.flag.context.Animation import org.rs09.consts.Items import core.game.interaction.InteractionListener import core.game.interaction.IntType +import org.rs09.consts.Sounds class SpellTablets : InteractionListener { val B2P_TABLET = Items.BONES_TO_PEACHES_8015 @@ -30,7 +32,7 @@ class SpellTablets : InteractionListener { } fun breakTablet(player: Player){ - player.audioManager.send(979) + playAudio(player, Sounds.POH_TABLET_BREAK_979) player.animator.forceAnimation(Animation(4069)) player.lock(5) player.setAttribute("tablet-spell",true) diff --git a/Server/src/main/content/global/skill/magic/SpellUtils.kt b/Server/src/main/content/global/skill/magic/SpellUtils.kt index a80d646a5..2f44a37e0 100644 --- a/Server/src/main/content/global/skill/magic/SpellUtils.kt +++ b/Server/src/main/content/global/skill/magic/SpellUtils.kt @@ -6,6 +6,7 @@ import core.game.node.entity.combat.spell.CombinationRune import core.game.node.entity.combat.spell.MagicStaff import core.game.node.entity.combat.spell.Runes import core.game.node.item.Item +import core.api.* object SpellUtils { fun usingStaff(p: Player, rune: Int): Boolean { @@ -20,15 +21,54 @@ object SpellUtils { return false } + // Snowscape modifications: replaced entire "hasrune" function to only check the inventory once per rune, so the rune satchel has less overhead. + fun hasRune(p:Player,rune:Item):Boolean{ + if(usingStaff(p,rune.id)) return true + val removeItems = p.getAttribute("spell:runes",ArrayList()) + + val baseAmt = kotlin.math.max(p.inventory.getAmount(rune.id),(if (inEquipmentOrInventory(p, 10882)) p.runeSatchel.getAmount(rune.id) else 0)) + + if(baseAmt >= 0){ + removeItems.add(Item(rune.getId(),kotlin.math.min(baseAmt,rune.getAmount()))) + p.setAttribute("spell:runes",removeItems) + } + + var amtRemaining = rune.amount - baseAmt + val possibleComboRunes = CombinationRune.eligibleFor(Runes.forId(rune.id)) + for (r in possibleComboRunes) { + // Check if this combination rune was already added for a different element + for (alreadyListed in removeItems){ + if (alreadyListed.getId() == r.id){ + amtRemaining -= alreadyListed.getAmount() + } + } + + val amt = kotlin.math.max(p.inventory.getAmount(r.id),(if (inEquipmentOrInventory(p, 10882)) p.runeSatchel.getAmount(r.id) else 0)) + if (amt > 0 && amtRemaining > 0) { + + if (amtRemaining <= amt) { + removeItems.add(Item(r.id,amtRemaining)) + amtRemaining = 0 + break + } + removeItems.add(Item(r.id,amt)) + amtRemaining -= amt + } + } + p.setAttribute("spell:runes",removeItems) + return amtRemaining <= 0 + } + +/* Snowscape: Original function left here for easier merging of updates. fun hasRune(p:Player,rune:Item):Boolean{ val removeItems = p.getAttribute("spell:runes",ArrayList()) if(usingStaff(p,rune.id)) return true - if(p.inventory.containsItem(rune)){ + if(p.inventory.containsItem(rune) || (inEquipmentOrInventory(p, 10882) && p.runeSatchel.containsItem(rune))){ removeItems.add(rune) p.setAttribute("spell:runes",removeItems) } - val baseAmt = p.inventory.getAmount(rune.id) + val baseAmt = p.inventory.getAmount(rune.id) + (if (inEquipmentOrInventory(p, 10882)) p.runeSatchel.getAmount(rune.id) else 0) var amtRemaining = rune.amount - baseAmt val possibleComboRunes = CombinationRune.eligibleFor(Runes.forId(rune.id)) for (r in possibleComboRunes) { @@ -49,9 +89,9 @@ object SpellUtils { fun hasRune(p: Player, item: Item, toRemove: MutableList, message: Boolean): Boolean { if (!usingStaff(p, item.id)) { - val hasBaseRune = p.inventory.contains(item.id, item.amount) + val hasBaseRune = p.inventory.contains(item.id, item.amount) || (inEquipmentOrInventory(p, 10882) && p.runeSatchel.contains(item.id, item.amount)) if (!hasBaseRune) { - val baseAmt = p.inventory.getAmount(item.id) + val baseAmt = p.inventory.getAmount(item.id) + (if (inEquipmentOrInventory(p, 10882)) p.runeSatchel.getAmount(item.id) else 0) if (baseAmt > 0) { toRemove.add(Item(item.id, p.inventory.getAmount(item.id))) } @@ -81,7 +121,7 @@ object SpellUtils { } return true } - +*/ fun attackableNPC(npc: NPC): Boolean{ return npc.definition.hasAction("attack") } 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 d71501ad4..ec6c8999c 100644 --- a/Server/src/main/content/global/skill/magic/ancient/AncientTeleportPlugin.java +++ b/Server/src/main/content/global/skill/magic/ancient/AncientTeleportPlugin.java @@ -51,10 +51,13 @@ public final class AncientTeleportPlugin extends MagicSpell { @Override public boolean cast(Entity entity, Node target) { - if (entity.getLocks().isTeleportLocked() || !super.meetsRequirements(entity, true, false)) { + /* Snowscape Custom to allow teleporting in wilderness. The teleport limitations are still checked later by the send() function, so this is not + if (entity.isTeleBlocked() || !super.meetsRequirements(entity, true, false)) { + 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() == 24 ? TeleportType.HOME : TeleportType.ANCIENT)) { + */ + if (entity.getTeleporter().send(location.transform(0, RandomFunction.random(3), 0), getSpellId() == 28 ? TeleportType.HOME : TeleportType.ANCIENT)) { if (!super.meetsRequirements(entity, true, true)) { entity.getTeleporter().getCurrentTeleport().stop(); return false; diff --git a/Server/src/main/content/global/skill/magic/ancient/BloodSpells.java b/Server/src/main/content/global/skill/magic/ancient/BloodSpells.java index b5c3b2bf4..2cd31111c 100644 --- a/Server/src/main/content/global/skill/magic/ancient/BloodSpells.java +++ b/Server/src/main/content/global/skill/magic/ancient/BloodSpells.java @@ -17,6 +17,9 @@ import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Graphics; import core.plugin.Initializable; import core.plugin.Plugin; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.playGlobalAudio; /** * Handles the Blood spells from the Ancient spellbook. @@ -80,10 +83,10 @@ public final class BloodSpells extends CombatSpell { @Override public Plugin newInstance(SpellType arg) throws Throwable { - SpellBook.ANCIENT.register(4, new BloodSpells(SpellType.RUSH, 56, 33.0, -1, -1, new Animation(1978, Priority.HIGH), null, RUSH_PROJECTILE, RUSH_END, Runes.BLOOD_RUNE.getItem(1), Runes.DEATH_RUNE.getItem(2), Runes.CHAOS_RUNE.getItem(2))); - SpellBook.ANCIENT.register(6, new BloodSpells(SpellType.BURST, 68, 39.0, -1, -1, new Animation(1979, Priority.HIGH), null, null, BURST_END, Runes.BLOOD_RUNE.getItem(2), Runes.DEATH_RUNE.getItem(2), Runes.CHAOS_RUNE.getItem(4))); - SpellBook.ANCIENT.register(5, new BloodSpells(SpellType.BLITZ, 80, 45.0, -1, -1, new Animation(1978, Priority.HIGH), null, BLITZ_PROJECTILE, BLITZ_END, Runes.BLOOD_RUNE.getItem(4), Runes.DEATH_RUNE.getItem(2))); - SpellBook.ANCIENT.register(7, new BloodSpells(SpellType.BARRAGE, 92, 51.0, -1, -1, new Animation(1979, Priority.HIGH), null, null, BARRAGE_END, Runes.SOUL_RUNE.getItem(1), Runes.BLOOD_RUNE.getItem(4), Runes.DEATH_RUNE.getItem(4))); + SpellBook.ANCIENT.register(4, new BloodSpells(SpellType.RUSH, 56, 33.0, Sounds.BLOOD_RUSH_CASTING_108, Sounds.BLOOD_RUSH_IMPACT_110, new Animation(1978, Priority.HIGH), null, RUSH_PROJECTILE, RUSH_END, Runes.BLOOD_RUNE.getItem(1), Runes.DEATH_RUNE.getItem(2), Runes.CHAOS_RUNE.getItem(2))); + SpellBook.ANCIENT.register(6, new BloodSpells(SpellType.BURST, 68, 39.0, Sounds.BLOOD_CAST_106, Sounds.BLOOD_BURST_IMPACT_105, new Animation(1979, Priority.HIGH), null, null, BURST_END, Runes.BLOOD_RUNE.getItem(2), Runes.DEATH_RUNE.getItem(2), Runes.CHAOS_RUNE.getItem(4))); + SpellBook.ANCIENT.register(5, new BloodSpells(SpellType.BLITZ, 80, 45.0, Sounds.BLOOD_CAST_106, Sounds.BLOOD_BLITZ_IMPACT_104, new Animation(1978, Priority.HIGH), null, BLITZ_PROJECTILE, BLITZ_END, Runes.BLOOD_RUNE.getItem(4), Runes.DEATH_RUNE.getItem(2))); + SpellBook.ANCIENT.register(7, new BloodSpells(SpellType.BARRAGE, 92, 51.0, Sounds.BLOOD_CAST_106, Sounds.BLOOD_BARRAGE_IMPACT_102, new Animation(1979, Priority.HIGH), null, null, BARRAGE_END, Runes.SOUL_RUNE.getItem(1), Runes.BLOOD_RUNE.getItem(4), Runes.DEATH_RUNE.getItem(4))); return this; } @@ -94,7 +97,7 @@ public final class BloodSpells extends CombatSpell { projectile.transform(entity, (Entity) target, false, 58, 10).send(); } entity.animate(animation); - sendAudio(entity, getAudio()); + playGlobalAudio(entity.getLocation(), audio.id, 20); } @Override 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 1af555d40..e4f287574 100644 --- a/Server/src/main/content/global/skill/magic/ancient/IceSpells.java +++ b/Server/src/main/content/global/skill/magic/ancient/IceSpells.java @@ -11,14 +11,14 @@ 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.entity.player.link.audio.Audio; -import core.game.node.entity.state.EntityState; import core.game.node.item.Item; -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; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.*; /** * Handles the Ice spells from the Ancient spellbook. @@ -92,7 +92,7 @@ public final class IceSpells extends CombatSpell { * @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, 171, impactSound, anim, start, projectile, end, runes); + super(type, SpellBook.ANCIENT, level, baseExperience, Sounds.ICE_CAST_171, impactSound, anim, start, projectile, end, runes); } @Override @@ -102,15 +102,14 @@ public final class IceSpells extends CombatSpell { projectile.transform(entity, (Entity) target, false, 58, 10).send(); } entity.animate(animation); - sendAudio(entity, audio); + playGlobalAudio(entity.getLocation(), audio.id, 20); + } @Override public void visualizeImpact(Entity entity, Entity target, BattleState state) { if (state.isFrozen()) { - if (target == entity.getProperties().getCombatPulse().getVictim()) { - sendAudio(target, new Audio(impactAudio, 1, 20)); - } + playGlobalAudio(target.getLocation(), impactAudio, 20); target.graphics(BARRAGE_ORB); return; } @@ -119,10 +118,10 @@ public final class IceSpells extends CombatSpell { @Override public Plugin newInstance(SpellType arg) throws Throwable { - SpellBook.ANCIENT.register(0, new IceSpells(SpellType.RUSH, 58, 34.0, 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, 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, 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, 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))); + 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; } @@ -133,8 +132,8 @@ public final class IceSpells extends CombatSpell { } int ticks = (1 + (type.ordinal() - SpellType.RUSH.ordinal())) * 8; if (state.getEstimatedHit() > -1) { - if (victim.getAttribute("freeze_immunity", -1) < GameWorld.getTicks()) { - victim.getStateManager().set(EntityState.FROZEN, ticks); + if (!hasTimerActive(victim, "frozen:immunity")) { + registerTimer(victim, spawnTimer("frozen", ticks, true)); } else if (type == SpellType.BARRAGE) { state.setFrozen(true); } diff --git a/Server/src/main/content/global/skill/magic/ancient/MiasmicSpells.java b/Server/src/main/content/global/skill/magic/ancient/MiasmicSpells.java index d2c331366..1faf6ed30 100644 --- a/Server/src/main/content/global/skill/magic/ancient/MiasmicSpells.java +++ b/Server/src/main/content/global/skill/magic/ancient/MiasmicSpells.java @@ -14,13 +14,14 @@ import core.game.node.entity.impl.Projectile; import core.game.node.entity.impl.Animator.Priority; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.SpellBookManager.SpellBook; -import core.game.node.entity.state.EntityState; import core.game.node.item.Item; import core.game.world.GameWorld; import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Graphics; import core.plugin.Plugin; +import static core.api.ContentAPIKt.*; + /** * Handles the Miasmic spells that are a part of the Ancient spellbook. * @author Splinter @@ -89,8 +90,8 @@ public final class MiasmicSpells extends CombatSpell { * @param projectile The projectile. * @param end The end graphics. */ - private MiasmicSpells(SpellType type, int level, double baseExperience, int impactSound, Animation anim, Graphics start, Projectile projectile, Graphics end, Item... runes) { - super(type, SpellBook.ANCIENT, level, baseExperience, 171, impactSound, anim, start, projectile, end, runes); + private MiasmicSpells(SpellType type, int level, double baseExperience, int castAudio, int impactSound, Animation anim, Graphics start, Projectile projectile, Graphics end, Item... runes) { + super(type, SpellBook.ANCIENT, level, baseExperience, castAudio, impactSound, anim, start, projectile, end, runes); } @Override @@ -100,7 +101,7 @@ public final class MiasmicSpells extends CombatSpell { projectile.transform(entity, (Entity) target, false, 58, 10).send(); } entity.animate(animation); - sendAudio(entity, audio); + playGlobalAudio(entity.getLocation(), audio.id, 20); } @Override @@ -110,17 +111,17 @@ public final class MiasmicSpells extends CombatSpell { @Override public Plugin newInstance(SpellType arg) throws Throwable { - SpellBook.ANCIENT.register(16, new MiasmicSpells(SpellType.RUSH, 61, 36.0, 173, new Animation(10513, Priority.HIGH), RUSH_START, null, RUSH_END, Runes.EARTH_RUNE.getItem(1), Runes.SOUL_RUNE.getItem(1), Runes.CHAOS_RUNE.getItem(2))); - SpellBook.ANCIENT.register(17, new MiasmicSpells(SpellType.BURST, 73, 42.0, 170, new Animation(10516, Priority.HIGH), BURST_START, null, BURST_END, Runes.EARTH_RUNE.getItem(3), Runes.SOUL_RUNE.getItem(3), Runes.CHAOS_RUNE.getItem(4))); - SpellBook.ANCIENT.register(18, new MiasmicSpells(SpellType.BLITZ, 85, 48.0, 169, new Animation(10524, Priority.HIGH), BLITZ_START, null, BLITZ_END, Runes.EARTH_RUNE.getItem(3), Runes.SOUL_RUNE.getItem(3), Runes.BLOOD_RUNE.getItem(2))); - SpellBook.ANCIENT.register(19, new MiasmicSpells(SpellType.BARRAGE, 97, 54.0, 168, new Animation(10518, Priority.HIGH), BARRAGE_START, null, BARRAGE_END, Runes.EARTH_RUNE.getItem(4), Runes.SOUL_RUNE.getItem(4), Runes.BLOOD_RUNE.getItem(4))); + SpellBook.ANCIENT.register(16, new MiasmicSpells(SpellType.RUSH, 61, 36.0, 5368, 5365, new Animation(10513, Priority.HIGH), RUSH_START, null, RUSH_END, Runes.EARTH_RUNE.getItem(1), Runes.SOUL_RUNE.getItem(1), Runes.CHAOS_RUNE.getItem(2))); + SpellBook.ANCIENT.register(17, new MiasmicSpells(SpellType.BURST, 73, 42.0, 5366, 5372, new Animation(10516, Priority.HIGH), BURST_START, null, BURST_END, Runes.EARTH_RUNE.getItem(3), Runes.SOUL_RUNE.getItem(3), Runes.CHAOS_RUNE.getItem(4))); + SpellBook.ANCIENT.register(18, new MiasmicSpells(SpellType.BLITZ, 85, 48.0, 5370, 5367, new Animation(10524, Priority.HIGH), BLITZ_START, null, BLITZ_END, Runes.EARTH_RUNE.getItem(3), Runes.SOUL_RUNE.getItem(3), Runes.BLOOD_RUNE.getItem(2))); + SpellBook.ANCIENT.register(19, new MiasmicSpells(SpellType.BARRAGE, 97, 54.0, 5371, 5369, new Animation(10518, Priority.HIGH), BARRAGE_START, null, BARRAGE_END, Runes.EARTH_RUNE.getItem(4), Runes.SOUL_RUNE.getItem(4), Runes.BLOOD_RUNE.getItem(4))); return this; } @Override public void fireEffect(Entity entity, Entity victim, BattleState state) { - if (victim.getAttribute("miasmic_immunity", -1) < GameWorld.getTicks()) { - victim.getStateManager().register(EntityState.MIASMIC, true, (getSpellId() - 15) * 20); + if (!hasTimerActive(victim, "miasmic:immunity")) { + registerTimer(victim, spawnTimer("miasmic", (getSpellId() - 15) * 20)); } } diff --git a/Server/src/main/content/global/skill/magic/ancient/ShadowSpells.java b/Server/src/main/content/global/skill/magic/ancient/ShadowSpells.java index ef5083ce6..22ca33fa3 100644 --- a/Server/src/main/content/global/skill/magic/ancient/ShadowSpells.java +++ b/Server/src/main/content/global/skill/magic/ancient/ShadowSpells.java @@ -2,6 +2,7 @@ package content.global.skill.magic.ancient; import java.util.List; +import core.game.node.entity.player.Player; import core.game.node.entity.skill.Skills; import core.game.node.entity.combat.spell.Runes; import core.game.node.Node; @@ -17,6 +18,9 @@ import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Graphics; import core.plugin.Initializable; import core.plugin.Plugin; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.playGlobalAudio; /** * Handles the Shadow spells from the Ancient spellbook. @@ -81,10 +85,10 @@ public final class ShadowSpells extends CombatSpell { @Override public Plugin newInstance(SpellType arg) throws Throwable { - SpellBook.ANCIENT.register(12, new ShadowSpells(SpellType.RUSH, 52, 31.0, 175, 176, new Animation(1978, Priority.HIGH), null, RUSH_PROJECTILE, RUSH_END, Runes.SOUL_RUNE.getItem(1), Runes.DEATH_RUNE.getItem(2), Runes.CHAOS_RUNE.getItem(2), Runes.AIR_RUNE.getItem(1))); - SpellBook.ANCIENT.register(14, new ShadowSpells(SpellType.BURST, 64, 37.0, 177, 178, new Animation(1979, Priority.HIGH), null, BURST_PROJECTILE, BURST_END, Runes.SOUL_RUNE.getItem(2), Runes.DEATH_RUNE.getItem(2), Runes.CHAOS_RUNE.getItem(4), Runes.AIR_RUNE.getItem(1))); - SpellBook.ANCIENT.register(13, new ShadowSpells(SpellType.BLITZ, 76, 43.0, 181, 182, new Animation(1978, Priority.HIGH), null, null, BLITZ_END, Runes.SOUL_RUNE.getItem(2), Runes.BLOOD_RUNE.getItem(2), Runes.DEATH_RUNE.getItem(2), Runes.AIR_RUNE.getItem(2))); - SpellBook.ANCIENT.register(15, new ShadowSpells(SpellType.BARRAGE, 88, 48.0, 181, 185, new Animation(1979, Priority.HIGH), null, null, BARRAGE_END, Runes.SOUL_RUNE.getItem(3), Runes.BLOOD_RUNE.getItem(2), Runes.DEATH_RUNE.getItem(4), Runes.AIR_RUNE.getItem(4))); + SpellBook.ANCIENT.register(12, new ShadowSpells(SpellType.RUSH, 52, 31.0, Sounds.SHADOW_CAST_178, Sounds.SHADOW_RUSH_IMPACT_179, new Animation(1978, Priority.HIGH), null, RUSH_PROJECTILE, RUSH_END, Runes.SOUL_RUNE.getItem(1), Runes.DEATH_RUNE.getItem(2), Runes.CHAOS_RUNE.getItem(2), Runes.AIR_RUNE.getItem(1))); + SpellBook.ANCIENT.register(14, new ShadowSpells(SpellType.BURST, 64, 37.0, Sounds.SHADOW_CAST_178, Sounds.SHADOW_BURST_IMPACT_177, new Animation(1979, Priority.HIGH), null, BURST_PROJECTILE, BURST_END, Runes.SOUL_RUNE.getItem(2), Runes.DEATH_RUNE.getItem(2), Runes.CHAOS_RUNE.getItem(4), Runes.AIR_RUNE.getItem(1))); + SpellBook.ANCIENT.register(13, new ShadowSpells(SpellType.BLITZ, 76, 43.0, Sounds.SHADOW_CAST_178, Sounds.SHADOW_BLITZ_IMPACT_176, new Animation(1978, Priority.HIGH), null, null, BLITZ_END, Runes.SOUL_RUNE.getItem(2), Runes.BLOOD_RUNE.getItem(2), Runes.DEATH_RUNE.getItem(2), Runes.AIR_RUNE.getItem(2))); + SpellBook.ANCIENT.register(15, new ShadowSpells(SpellType.BARRAGE, 88, 48.0, Sounds.SHADOW_CAST_178, Sounds.SHADOW_BARRAGE_IMPACT_175, new Animation(1979, Priority.HIGH), null, null, BARRAGE_END, Runes.SOUL_RUNE.getItem(3), Runes.BLOOD_RUNE.getItem(2), Runes.DEATH_RUNE.getItem(4), Runes.AIR_RUNE.getItem(4))); return this; } @@ -95,7 +99,7 @@ public final class ShadowSpells extends CombatSpell { projectile.transform(entity, (Entity) target, false, 58, 10).send(); } entity.animate(animation); - sendAudio(entity, audio); + playGlobalAudio(entity.getLocation(), audio.id, 20); } @Override diff --git a/Server/src/main/content/global/skill/magic/ancient/SmokeSpells.java b/Server/src/main/content/global/skill/magic/ancient/SmokeSpells.java index b92c9ea02..23ddf938f 100644 --- a/Server/src/main/content/global/skill/magic/ancient/SmokeSpells.java +++ b/Server/src/main/content/global/skill/magic/ancient/SmokeSpells.java @@ -10,13 +10,16 @@ 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.Player; import core.game.node.entity.player.link.SpellBookManager.SpellBook; -import core.game.node.entity.state.EntityState; 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 Smoke spells from the Ancient spellbook. @@ -91,10 +94,10 @@ public final class SmokeSpells extends CombatSpell { @Override public Plugin newInstance(SpellType arg) throws Throwable { - SpellBook.ANCIENT.register(8, new SmokeSpells(SpellType.RUSH, 50, 30.0, 176, 177, new Animation(1978, Priority.HIGH), null, RUSH_PROJECTILE, RUSH_END, Runes.DEATH_RUNE.getItem(2), Runes.CHAOS_RUNE.getItem(2), Runes.FIRE_RUNE.getItem(1), Runes.AIR_RUNE.getItem(1))); - SpellBook.ANCIENT.register(10, new SmokeSpells(SpellType.BURST, 62, 36.0, 179, 180, new Animation(1979, Priority.HIGH), null, BURST_PROJECTILE, BURST_END, Runes.DEATH_RUNE.getItem(2), Runes.CHAOS_RUNE.getItem(4), Runes.FIRE_RUNE.getItem(2), Runes.AIR_RUNE.getItem(2))); - SpellBook.ANCIENT.register(9, new SmokeSpells(SpellType.BLITZ, 74, 42.0, 183, 184, new Animation(1978, Priority.HIGH), null, BLITZ_PROJECTILE, BLITZ_END, Runes.BLOOD_RUNE.getItem(2), Runes.DEATH_RUNE.getItem(2), Runes.FIRE_RUNE.getItem(2), Runes.AIR_RUNE.getItem(2))); - SpellBook.ANCIENT.register(11, new SmokeSpells(SpellType.BARRAGE, 86, 48.0, 183, 185, new Animation(1979, Priority.HIGH), null, BARRAGE_PROJECTILE, BARRAGE_END, Runes.BLOOD_RUNE.getItem(2), Runes.DEATH_RUNE.getItem(4), Runes.FIRE_RUNE.getItem(4), Runes.AIR_RUNE.getItem(4))); + SpellBook.ANCIENT.register(8, new SmokeSpells(SpellType.RUSH, 50, 30.0, Sounds.SMOKE_CAST_183, Sounds.SMOKE_RUSH_IMPACT_185, new Animation(1978, Priority.HIGH), null, RUSH_PROJECTILE, RUSH_END, Runes.DEATH_RUNE.getItem(2), Runes.CHAOS_RUNE.getItem(2), Runes.FIRE_RUNE.getItem(1), Runes.AIR_RUNE.getItem(1))); + SpellBook.ANCIENT.register(10, new SmokeSpells(SpellType.BURST, 62, 36.0, Sounds.SMOKE_CAST_183, Sounds.SMOKE_BURST_IMPACT_182, new Animation(1979, Priority.HIGH), null, BURST_PROJECTILE, BURST_END, Runes.DEATH_RUNE.getItem(2), Runes.CHAOS_RUNE.getItem(4), Runes.FIRE_RUNE.getItem(2), Runes.AIR_RUNE.getItem(2))); + SpellBook.ANCIENT.register(9, new SmokeSpells(SpellType.BLITZ, 74, 42.0, Sounds.SMOKE_CAST_183, Sounds.SMOKE_BLITZ_IMPACT_181, new Animation(1978, Priority.HIGH), null, BLITZ_PROJECTILE, BLITZ_END, Runes.BLOOD_RUNE.getItem(2), Runes.DEATH_RUNE.getItem(2), Runes.FIRE_RUNE.getItem(2), Runes.AIR_RUNE.getItem(2))); + SpellBook.ANCIENT.register(11, new SmokeSpells(SpellType.BARRAGE, 86, 48.0, Sounds.SMOKE_CAST_183, Sounds.SMOKE_BARRAGE_IMPACT_180, new Animation(1979, Priority.HIGH), null, BARRAGE_PROJECTILE, BARRAGE_END, Runes.BLOOD_RUNE.getItem(2), Runes.DEATH_RUNE.getItem(4), Runes.FIRE_RUNE.getItem(4), Runes.AIR_RUNE.getItem(4))); return this; } @@ -105,13 +108,13 @@ public final class SmokeSpells extends CombatSpell { projectile.transform(entity, (Entity) target, false, 58, 10).send(); } entity.animate(animation); - sendAudio(entity, audio); + playGlobalAudio(entity.getLocation(), audio.id, 20); } @Override public void fireEffect(Entity entity, Entity victim, BattleState state) { if (state.getEstimatedHit() > -1) { - victim.getStateManager().register(EntityState.POISONED, false, type.ordinal() >= SpellType.BLITZ.ordinal() ? 48 : 28, entity); + applyPoison(victim, entity, type.ordinal() >= SpellType.BLITZ.ordinal() ? 4 : 2); } } 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/EnergyTransferSpell.java b/Server/src/main/content/global/skill/magic/lunar/EnergyTransferSpell.java deleted file mode 100644 index 4a179751c..000000000 --- a/Server/src/main/content/global/skill/magic/lunar/EnergyTransferSpell.java +++ /dev/null @@ -1,84 +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.Node; -import core.game.node.entity.Entity; -import core.game.node.entity.combat.ImpactHandler.HitsplatType; -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.world.update.flag.context.Animation; -import core.game.world.update.flag.context.Graphics; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Represents the energy transfer spell. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class EnergyTransferSpell extends MagicSpell { - - /** - * Represents the animation to use. - */ - private static final Animation ANIMATION = new Animation(4411); - - /** - * Repesents the graphic, next spells of this spell. - */ - private static final Graphics GRAPHICS = new Graphics(738, 90); - - /** - * Constructs a new {@code EnergyTransferSpell} {@code Object}. - */ - public EnergyTransferSpell() { - super(SpellBook.LUNAR, 91, 100, null, null, null, new Item[] { new Item(Runes.ASTRAL_RUNE.getId(), 3), new Item(Runes.LAW_RUNE.getId(), 2), new Item(Runes.NATURE_RUNE.getId(), 1) }); - } - - @Override - public Plugin newInstance(SpellType arg) throws Throwable { - SpellBook.LUNAR.register(5, this); - return this; - } - - @Override - public boolean cast(Entity entity, Node target) { - if (!(target instanceof Player)) { - return false; - } - final Player player = ((Player) entity); - if (!super.meetsRequirements(player, true, true)) { - return false; - } - final Player o = ((Player) target); - int hp = (int) Math.floor(player.getSkills().getLifepoints() * 0.10); - int run = hp; - if (run > (100 - o.getSettings().getRunEnergy())) { - run = (int) (100 - o.getSettings().getRunEnergy()); - } - if (run < 0) { - run = 0; - } - o.getSettings().updateRunEnergy(-run); - player.getImpactHandler().manualHit(player, hp, HitsplatType.NORMAL, 2); - int energy = 100; - energy -= o.getSettings().getSpecialEnergy(); - if (energy < 0) { - energy = 0; - } - if (energy > player.getSettings().getSpecialEnergy()) { - energy = player.getSettings().getSpecialEnergy(); - } - o.getSettings().setSpecialEnergy(o.getSettings().getSpecialEnergy() + energy); - player.getSettings().setSpecialEnergy(player.getSettings().getSpecialEnergy() - energy); - player.animate(ANIMATION); - player.getAudioManager().send(new Audio (2885), true); - o.graphics(GRAPHICS); - return true; - } -} diff --git a/Server/src/main/content/global/skill/magic/lunar/HealSpell.java b/Server/src/main/content/global/skill/magic/lunar/HealSpell.java index 8fb86e46b..f79c85728 100644 --- a/Server/src/main/content/global/skill/magic/lunar/HealSpell.java +++ b/Server/src/main/content/global/skill/magic/lunar/HealSpell.java @@ -9,17 +9,19 @@ import core.game.node.entity.Entity; import core.game.node.entity.combat.ImpactHandler.HitsplatType; 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.world.map.RegionManager; import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Graphics; import core.plugin.Plugin; +import org.rs09.consts.Sounds; import java.util.Iterator; import java.util.List; +import static core.api.ContentAPIKt.playGlobalAudio; + /** * Represents the healing spell. * @author 'Vexia @@ -105,8 +107,8 @@ public final class HealSpell extends MagicSpell { player.getImpactHandler().manualHit(player, transfer, null); o.getSkills().heal(transfer); player.animate(ANIMATION); - player.getAudioManager().send(new Audio(2895), true); - o.getAudioManager().send(new Audio(2892), true); + playGlobalAudio(player.getLocation(), Sounds.LUNAR_HEAL_OTHER_2895); + playGlobalAudio(o.getLocation(), Sounds.LUNAR_HEAL_OTHER_INDIVIDUAL_2892); o.graphics(GRAPHICS); } else { List players = RegionManager.getLocalPlayers(player, 1); @@ -130,9 +132,9 @@ public final class HealSpell extends MagicSpell { } player.getImpactHandler().manualHit(player, percentage, HitsplatType.NORMAL); player.animate(ANIMATION_G); - player.getAudioManager().send(new Audio(2894), true); + playGlobalAudio(player.getLocation(), Sounds.LUNAR_HEAL_GROUP_2894); for (Player p : players) { - p.getAudioManager().send(new Audio(2892), true); + playGlobalAudio(p.getLocation(), Sounds.LUNAR_HEAL_OTHER_INDIVIDUAL_2892); p.graphics(GRAPHICS_G); p.getSkills().heal(percentage); } diff --git a/Server/src/main/content/global/skill/magic/lunar/HumidifySpell.java b/Server/src/main/content/global/skill/magic/lunar/HumidifySpell.java deleted file mode 100644 index 89eb05fa9..000000000 --- a/Server/src/main/content/global/skill/magic/lunar/HumidifySpell.java +++ /dev/null @@ -1,90 +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.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.world.update.flag.context.Animation; -import core.game.world.update.flag.context.Graphics; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Represents the humidify spell. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class HumidifySpell extends MagicSpell { - - /** - * Represents the animation of this graphics. - */ - private static final Animation ANIMATION = new Animation(6294); - - /** - * Repesents the graphick, next spells of this spell. - */ - private static final Graphics GRAPHIC = new Graphics(1061); - - /** - * Represents the empty vessels. - */ - private static final int[] EMPTY = { 229, 1831, 1829, 1827, 1825, 1925, 1923, 1935, 5331, 5332, 5333, 5334, 5335, 5337, 5338, 5339, 6667, 7688, 731, 1980, 434 }; - - /** - * Represents the full vessels. - */ - private static final int[] FULL = { 227, 1823, 1823, 1823, 1823, 1929, 1921, 1937, 5340, 5340, 5340, 5340, 5340, 5340, 5340, 5340, 6668, 7690, 732, 4458, 1761 }; - - /** - * Constructs a new {@code CureOtherSpell} {@code Object}. - */ - public HumidifySpell() { - super(SpellBook.LUNAR, 68, 65, ANIMATION, null, null, new Item[] { new Item(Runes.ASTRAL_RUNE.getId(), 1), new Item(Runes.WATER_RUNE.getId(), 3), new Item(Runes.FIRE_RUNE.getId(), 1) }); - } - - @Override - public Plugin newInstance(SpellType arg) throws Throwable { - SpellBook.LUNAR.register(7, this); - return this; - } - - @Override - public boolean cast(Entity entity, Node target) { - Player p = (Player) entity; - boolean good = false; - for (int i = 0; i < EMPTY.length; i++) { - if (p.getInventory().contains(EMPTY[i], 1)) { - good = true; - break; - } - } - if (!good) { - p.sendMessage("You need something which holds water in order to do this spell."); - return false; - } - if (!super.meetsRequirements(p, true, true)) { - return false; - } - p.lock(ANIMATION.getDuration() + 1); - p.animate(ANIMATION); - p.graphics(GRAPHIC); - p.getAudioManager().send(3614); - for (int k = 0; k < 28; k++) { - for (int i = 0; i < 21; i++) { - if (p.getInventory().contains(EMPTY[i], 1)) { - if (p.getInventory().remove(new Item(EMPTY[i]))) { - p.getInventory().add(new Item(FULL[i])); - } - } - } - } - return true; - } - -} diff --git a/Server/src/main/content/global/skill/magic/lunar/HunterKitSpell.java b/Server/src/main/content/global/skill/magic/lunar/HunterKitSpell.java deleted file mode 100644 index 96e4a47b7..000000000 --- a/Server/src/main/content/global/skill/magic/lunar/HunterKitSpell.java +++ /dev/null @@ -1,97 +0,0 @@ -package content.global.skill.magic.lunar; - -import core.cache.def.impl.ItemDefinition; -import core.plugin.Initializable; -import core.game.node.entity.combat.spell.MagicSpell; -import core.game.node.entity.combat.spell.Runes; -import core.game.interaction.OptionHandler; -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.world.update.flag.context.Animation; -import core.game.world.update.flag.context.Graphics; -import core.plugin.Plugin; - -/** - * Represents the hunter kit spell. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class HunterKitSpell extends MagicSpell { - - /** - * Represents the hunter kit. - */ - private static final Item KIT = new Item(11159); - - /** - * Represents the animation of this graphics. - */ - private static final Animation ANIMATION = new Animation(6303); - - /** - * Repesents the graphick, next spells of this spell. - */ - private static final Graphics GRAPHIC = new Graphics(1074); - - /** - * Constructs a new {@code CureOtherSpell} {@code Object}. - */ - public HunterKitSpell() { - super(SpellBook.LUNAR, 71, 70, null, null, null, new Item[] { new Item(Runes.ASTRAL_RUNE.getId(), 2), new Item(Runes.EARTH_RUNE.getId(), 2) }); - } - - @Override - public Plugin newInstance(SpellType arg) throws Throwable { - SpellBook.LUNAR.register(8, this); - ItemDefinition.forId(KIT.getId()).getHandlers().put("option:open", new OptionHandler() { - @Override - public Plugin newInstance(Object arg) throws Throwable { - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - if (player.getInventory().freeSlots() < 7) { - player.getPacketDispatch().sendMessage("You don't have enough inventory space."); - return true; - } - if (player.getInventory().remove((Item) node)) { - player.getInventory().add(new Item[] { new Item(10150), new Item(10010), new Item(10006), new Item(10031), new Item(10029), new Item(596), new Item(10008) }); - } - return true; - } - - @Override - public boolean isWalk() { - return false; - } - - }); - return this; - } - - @Override - public boolean cast(Entity entity, Node target) { - Player p = (Player) entity; - if (p.getInventory().freeSlots() == 0) { - p.getPacketDispatch().sendMessage("Not enough inventory space!"); - return false; - } - if (!meetsRequirements(entity, true, true)) { - return false; - } - if (p.getInventory().add(KIT)) { - p.lock(5); - p.animate(ANIMATION); - p.graphics(GRAPHIC); - p.getAudioManager().send(3615, 1, 1); - } - return true; - } - -} diff --git a/Server/src/main/content/global/skill/magic/lunar/LunarData.kt b/Server/src/main/content/global/skill/magic/lunar/LunarData.kt index 4c2d94584..9a1b28f6c 100644 --- a/Server/src/main/content/global/skill/magic/lunar/LunarData.kt +++ b/Server/src/main/content/global/skill/magic/lunar/LunarData.kt @@ -1,37 +1,40 @@ package content.global.skill.magic.lunar -import core.game.world.update.flag.context.Animation -import core.game.world.update.flag.context.Graphics +import core.api.addItemOrDrop +import core.api.freeSlots +import core.api.removeItem +import core.api.sendMessage +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 -// Animations -val BAKE_PIE_ANIM = Animation(4413) -val STATSPY_ANIM = Animation(6293) -val CURE_PLANT_ANIM = Animation(4409) -val NPC_CONTACT_ANIM = Animation(4413) -val PLANK_MAKE_ANIM = Animation(6298) -val STRING_JEWELLERY_ANIM = Animation(4412) -val SUPERGLASS_MAKE_ANIM = Animation(4413) -val FERTILE_SOIL_ANIM = Animation(724) -val CURE_ME_ANIM = Animation(4411) -val CURE_GROUP_ANIM = Animation(4409) -val CURE_OTHER_ANIM = Animation(4411) +private val HunterKitContents = intArrayOf( + Items.NOOSE_WAND_10150, + Items.BUTTERFLY_NET_10010, + Items.BIRD_SNARE_10006, + Items.RABBIT_SNARE_10031, + Items.TEASING_STICK_10029, + Items.UNLIT_TORCH_596, + Items.BOX_TRAP_10008 +) +class HunterKitInteraction : InteractionListener { + override fun defineListeners() { + on(Items.HUNTER_KIT_11159, IntType.ITEM, "open") { player, _ -> + if(freeSlots(player) < 6) sendMessage(player, "You don't have enough inventory space.").also { return@on false } + if(removeItem(player, Items.HUNTER_KIT_11159)) { + for(item in HunterKitContents) { + addItemOrDrop(player, item) + } + } + return@on true + } + } +} -// Graphics -val BAKE_PIE_GFX = Graphics(746,75) -val STATSPY_GFX = Graphics(1059) -val CURE_PLANT_GFX = Graphics(742,100) -val NPC_CONTACT_GFX = Graphics(730,130) -val PLANK_MAKE_GFX = Graphics(1063, 120) -val STRING_JEWELLERY_GFX = Graphics(728, 100) -val SUPERGLASS_MAKE_GFX = Graphics(729, 120) -val FERTILE_SOIL_GFX = Graphics(141, 96) -val CURE_ME_GFX = Graphics(731, 90) -val CURE_GROUP_GFX = Graphics(751, 130) -val CURE_OTHER_GFX = Graphics(738, 130) - -enum class JewelleryString(val unstrung: Int, val strung: Int) { +enum class StringJewelleryItems(val unstrung: Int, val strung: Int) { GOLD(Items.GOLD_AMULET_1673, Items.GOLD_AMULET_1692), SAPPHIRE(Items.SAPPHIRE_AMULET_1675, Items.SAPPHIRE_AMULET_1694), EMERALD(Items.EMERALD_AMULET_1677, Items.EMERALD_AMULET_1696), @@ -43,8 +46,8 @@ enum class JewelleryString(val unstrung: Int, val strung: Int) { HOLY(Items.UNSTRUNG_SYMBOL_1714, Items.UNBLESSED_SYMBOL_1716), UNHOLY(Items.UNSTRUNG_EMBLEM_1720, Items.UNPOWERED_SYMBOL_1722); companion object { - val productOfString = values().associate { it.unstrung to it.strung } - fun forId(id : Int) : Int { + private val productOfString = values().associate { it.unstrung to it.strung } + fun forId(id: Int): Int { return productOfString[id]!! } @@ -53,3 +56,80 @@ enum class JewelleryString(val unstrung: Int, val strung: Int) { } } } + +enum class HumidifyItems(val empty: Int, val full: Int) { + VIAL(Items.VIAL_229, Items.VIAL_OF_WATER_227), + WATERSKIN0(Items.WATERSKIN0_1831, Items.WATERSKIN4_1823), + WATERSKIN1(Items.WATERSKIN1_1829, Items.WATERSKIN4_1823), + WATERSKIN2(Items.WATERSKIN2_1827, Items.WATERSKIN4_1823), + WATERSKIN3(Items.WATERSKIN3_1825, Items.WATERSKIN4_1823), + BUCKET(Items.BUCKET_1925, Items.BUCKET_OF_WATER_1929), + BOWL(Items.BOWL_1923, Items.BOWL_OF_WATER_1921), + JUG(Items.JUG_1935, Items.JUG_OF_WATER_1937), + WATERING_CAN0(Items.WATERING_CAN_5331, Items.WATERING_CAN8_5340), + WATERING_CAN1(Items.WATERING_CAN1_5333, Items.WATERING_CAN8_5340), + WATERING_CAN2(Items.WATERING_CAN2_5334, Items.WATERING_CAN8_5340), + WATERING_CAN3(Items.WATERING_CAN3_5335, Items.WATERING_CAN8_5340), + WATERING_CAN4(Items.WATERING_CAN4_5336, Items.WATERING_CAN8_5340), + WATERING_CAN5(Items.WATERING_CAN5_5337, Items.WATERING_CAN8_5340), + WATERING_CAN6(Items.WATERING_CAN6_5338, Items.WATERING_CAN8_5340), + WATERING_CAN7(Items.WATERING_CAN7_5339, Items.WATERING_CAN8_5340), + FISHBOWL(Items.FISHBOWL_6667, Items.FISHBOWL_6668), + KETTLE(Items.KETTLE_7688, Items.FULL_KETTLE_7690), + ENCHANTED_VIAL(Items.ENCHANTED_VIAL_731, Items.HOLY_WATER_732), + CUP(Items.EMPTY_CUP_1980, Items.CUP_OF_WATER_4458); + companion object { + private val productOfFill = values().associate { it.empty to it.full } + fun forId(id: Int): Int { + return productOfFill[id]!! + } + + fun emptyContains(id: Int): Boolean { + return productOfFill.contains(id) + } + } +} + +enum class PlankType (val log: Item, val plank: Item, val price: Int) { + WOOD(Item(1511), Item(960), 70), + OAK(Item(1521), Item(8778), 175), + TEAK(Item(6333), Item(8780), 350), + MAHOGANY(Item(6332), Item(8782), 1050); + companion object { + fun getForLog(item: Item): PlankType? { + for (plankType in values()) { + if (plankType.log.id == item.id) { + return plankType + } + } + return null + } + } +} + +val statSpySkills = arrayOf( + intArrayOf(Skills.ATTACK, 1, 2), + intArrayOf(Skills.HITPOINTS, 5, 6), + intArrayOf(Skills.MINING, 9, 10), + intArrayOf(Skills.STRENGTH, 13, 14), + intArrayOf(Skills.AGILITY, 17, 18), + intArrayOf(Skills.SMITHING, 21, 22), + intArrayOf(Skills.DEFENCE, 25, 26), + intArrayOf(Skills.HERBLORE, 29, 30), + intArrayOf(Skills.FISHING, 33, 34), + intArrayOf(Skills.RANGE, 37, 38), + intArrayOf(Skills.THIEVING, 41, 42), + intArrayOf(Skills.COOKING, 45, 46), + intArrayOf(Skills.PRAYER, 49, 50), + intArrayOf(Skills.CRAFTING, 53, 54), + intArrayOf(Skills.FIREMAKING, 57, 58), + intArrayOf(Skills.MAGIC, 61, 62), + intArrayOf(Skills.FLETCHING, 65, 66), + intArrayOf(Skills.WOODCUTTING, 69, 70), + intArrayOf(Skills.RUNECRAFTING, 73, 74), + intArrayOf(Skills.SLAYER, 77, 78), + intArrayOf(Skills.FARMING, 81, 82), + intArrayOf(Skills.CONSTRUCTION, 85, 86), + intArrayOf(Skills.HUNTER, 89, 90), + intArrayOf(Skills.SUMMONING, 93, 94) +) \ No newline at end of file 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 b4cb649f4..d88710205 100644 --- a/Server/src/main/content/global/skill/magic/lunar/LunarListeners.kt +++ b/Server/src/main/content/global/skill/magic/lunar/LunarListeners.kt @@ -7,14 +7,15 @@ import content.global.skill.farming.FarmingPatch import content.global.skill.magic.SpellListener 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 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.skill.Skills -import core.game.node.entity.state.EntityState import core.game.node.item.Item import core.game.node.scenery.Scenery import core.game.system.command.Privilege @@ -23,283 +24,267 @@ import core.game.system.task.Pulse import core.game.world.map.Location import core.game.world.map.RegionManager import core.game.world.repository.Repository +import core.game.world.update.flag.context.Animation import core.tools.RandomFunction -import org.rs09.consts.Components -import org.rs09.consts.Items +import org.rs09.consts.* +import kotlin.math.floor class LunarListeners : SpellListener("lunar"), Commands { override fun defineListeners() { - + // Level 0 onCast(Lunar.HOME_TELEPORT, NONE) { player, _ -> requires(player) - sendTeleport(player,0.0, Location.create(2100, 3914, 0)) - } - - onCast(Lunar.MOONCLAN_TELEPORT, NONE) { player, _ -> - requires(player,69, arrayOf(Item(Items.ASTRAL_RUNE_9075,2), Item(Items.LAW_RUNE_563,1), Item(Items.EARTH_RUNE_557,2))) - player.getAudioManager().send(Audio(200), true) - sendTeleport(player,66.0, Location.create(2111, 3916, 0)) - } - - onCast(Lunar.MOONCLAN_GR_TELEPORT, NONE) { player, _ -> - requires(player,70, arrayOf(Item(Items.ASTRAL_RUNE_9075,2), Item(Items.LAW_RUNE_563,1), Item(Items.EARTH_RUNE_557,4))) - player.getAudioManager().send(Audio(200), true) - sendGroupTeleport(player,67.0,"Moonclan Island",Location.create(2111, 3916, 0)) - } - - onCast(Lunar.OURANIA_TELEPORT, NONE) { player, _ -> - requires(player,71, arrayOf(Item(Items.ASTRAL_RUNE_9075,2), Item(Items.LAW_RUNE_563,1), Item(Items.EARTH_RUNE_557,6))) - player.getAudioManager().send(Audio(200), true) - sendTeleport(player,69.0, Location.create(2469, 3247, 0)) - } - - onCast(Lunar.WATERBIRTH_TELEPORT, NONE){ player, _ -> - requires(player,72, arrayOf(Item(Items.ASTRAL_RUNE_9075,2), Item(Items.LAW_RUNE_563), Item(Items.WATER_RUNE_555))) - player.getAudioManager().send(Audio(200), true) - sendTeleport(player,71.0, Location.create(2527, 3739, 0)) - } - - onCast(Lunar.WATERBIRTH_GR_TELEPORT, NONE) { player, _ -> - requires(player,73, arrayOf(Item(Items.ASTRAL_RUNE_9075,2), Item(Items.LAW_RUNE_563), Item(Items.WATER_RUNE_555,5))) - player.getAudioManager().send(Audio(200), true) - sendGroupTeleport(player,72.0,"Waterbirth Island", Location.create(2527, 3739, 0)) - } - - onCast(Lunar.BARBARIAN_TELEPORT, NONE) { player, _ -> - requires(player,75, arrayOf(Item(Items.ASTRAL_RUNE_9075,2), Item(Items.LAW_RUNE_563,2), Item(Items.FIRE_RUNE_554,3))) - player.getAudioManager().send(Audio(200), true) - sendTeleport(player,76.0, Location.create(2544, 3572, 0)) - } - - onCast(Lunar.BARBARIAN_GR_TELEPORT, NONE) { player, _ -> - requires(player,77, arrayOf(Item(Items.ASTRAL_RUNE_9075,2), Item(Items.LAW_RUNE_563,2), Item(Items.FIRE_RUNE_554,6))) - player.getAudioManager().send(Audio(200), true) - sendGroupTeleport(player,77.0,"Barbarian Outpost", Location.create(2544, 3572, 0)) - } - - onCast(Lunar.KHAZARD_TELEPORT, NONE) { player, _ -> - requires(player,78, arrayOf(Item(Items.ASTRAL_RUNE_9075,2), Item(Items.LAW_RUNE_563,2), Item(Items.WATER_RUNE_555,4))) - player.getAudioManager().send(Audio(200), true) - sendTeleport(player,80.0, Location.create(2656, 3157, 0)) - } - - onCast(Lunar.KHAZARD_GR_TELEPORT, NONE) { player, _ -> - requires(player,79, arrayOf(Item(Items.ASTRAL_RUNE_9075,2), Item(Items.LAW_RUNE_563,2), Item(Items.WATER_RUNE_555,8))) - player.getAudioManager().send(Audio(200), true) - sendGroupTeleport(player,81.0, "Port Khazard", Location.create(2656, 3157, 0)) - } - - onCast(Lunar.FISHING_GUILD_TELEPORT, NONE) { player, _ -> - requires(player,85, arrayOf(Item(Items.ASTRAL_RUNE_9075,3), Item(Items.LAW_RUNE_563,3), Item(Items.WATER_RUNE_555,10))) - player.getAudioManager().send(Audio(200), true) - sendTeleport(player,89.0, Location.create(2611, 3393, 0)) - } - - onCast(Lunar.FISHING_GUILD_GR_TELEPORT, NONE) { player, _ -> - requires(player,86, arrayOf(Item(Items.ASTRAL_RUNE_9075,3), Item(Items.LAW_RUNE_563,3), Item(Items.WATER_RUNE_555,14))) - player.getAudioManager().send(Audio(200), true) - sendGroupTeleport(player,90.0,"Fishing Guild", Location.create(2611, 3393, 0)) - } - - onCast(Lunar.CATHERBY_TELEPORT, NONE) { player, _ -> - requires(player,87, arrayOf(Item(Items.ASTRAL_RUNE_9075,3), Item(Items.LAW_RUNE_563,3), Item(Items.WATER_RUNE_555,10))) - player.getAudioManager().send(Audio(200), true) - sendTeleport(player,92.0, Location.create(2804, 3433, 0)) - } - - onCast(Lunar.CATHERBY_GR_TELEPORT, NONE) { player, _ -> - requires(player,88, arrayOf(Item(Items.ASTRAL_RUNE_9075,3), Item(Items.LAW_RUNE_563,3), Item(Items.WATER_RUNE_555,15))) - player.getAudioManager().send(Audio(200), true) - sendGroupTeleport(player,93.0,"Catherby", Location.create(2804, 3433, 0)) - } - - onCast(Lunar.ICE_PLATEAU_TELEPORT, NONE) { player, _ -> - requires(player,89, arrayOf(Item(Items.ASTRAL_RUNE_9075,3), Item(Items.LAW_RUNE_563,3), Item(Items.WATER_RUNE_555,8))) - player.getAudioManager().send(Audio(200), true) - sendTeleport(player,96.0, Location.create(2972, 3873, 0)) - } - - onCast(Lunar.ICE_PLATEAU_GR_TELEPORT, NONE) { player, _ -> - requires(player,90, arrayOf(Item(Items.ASTRAL_RUNE_9075,3), Item(Items.LAW_RUNE_563,3), Item(Items.WATER_RUNE_555,16))) - player.getAudioManager().send(Audio(200), true) - sendGroupTeleport(player,99.0, "Ice Plateau", Location.create(2972, 3873, 0)) + player.teleporter.send(Location.create(2100, 3914, 0),TeleportManager.TeleportType.HOME) + setDelay(player,true) } + // Level 65 onCast(Lunar.BAKE_PIE, NONE) { player, _ -> requires(player,65, arrayOf(Item(Items.ASTRAL_RUNE_9075), Item(Items.FIRE_RUNE_554,5), Item(Items.WATER_RUNE_555,4))) bakePie(player) } - onCast(Lunar.MONSTER_EXAMINE, NPC) { player, node -> - requires(player,66, arrayOf(Item(Items.ASTRAL_RUNE_9075), Item(Items.MIND_RUNE_558), Item(Items.COSMIC_RUNE_564))) - examineMonster(player,node!!.asNpc()) - } - + // Level 66 onCast(Lunar.CURE_PLANT, OBJECT) { player, node -> requires(player,66, arrayOf(Item(Items.ASTRAL_RUNE_9075), Item(Items.EARTH_RUNE_557,8))) curePlant(player,node!!.asScenery()) } + // Level 66 + onCast(Lunar.MONSTER_EXAMINE, NPC) { player, node -> + requires(player,66, arrayOf(Item(Items.ASTRAL_RUNE_9075), Item(Items.MIND_RUNE_558), Item(Items.COSMIC_RUNE_564))) + monsterExamine(player,node!!.asNpc()) + } + + // Level 67 onCast(Lunar.NPC_CONTACT, NONE) { player, _ -> requires(player,67, arrayOf(Item(Items.ASTRAL_RUNE_9075), Item(Items.COSMIC_RUNE_564), Item(Items.AIR_RUNE_556,2))) - player.interfaceManager.open(Component(429)) - player.setAttribute("contact-caller"){ - removeRunes(player) - addXP(player,63.0) - setDelay(player,false) - visualizeSpell(player, NPC_CONTACT_ANIM, NPC_CONTACT_GFX,3618) - } + npcContact(player) } - onCast(Lunar.PLANK_MAKE, ITEM) { player, node -> - requires(player, 86, arrayOf(Item(Items.ASTRAL_RUNE_9075, 2), Item(Items.NATURE_RUNE_561, 1), Item(Items.EARTH_RUNE_557, 15))) - plankMake(player, node!!.asItem()) + // Level 68 + onCast(Lunar.CURE_OTHER, PLAYER) { player, node -> + requires(player, 68, arrayOf(Item(Items.ASTRAL_RUNE_9075, 1), Item(Items.LAW_RUNE_563), Item(Items.EARTH_RUNE_557, 10))) + node?.let { cureOther(player, node) } } - onCast(Lunar.STRING_JEWELLERY, NONE) { player, _ -> - requires(player, 80, arrayOf(Item(Items.ASTRAL_RUNE_9075, 2), Item(Items.EARTH_RUNE_557, 10), Item(Items.WATER_RUNE_555, 5))) - stringJewellery(player) + // Level 68 + onCast(Lunar.HUMIDIFY, NONE) { player, _ -> + requires(player, 68, arrayOf(Item(Items.ASTRAL_RUNE_9075, 1), Item(Items.WATER_RUNE_555, 3), Item(Items.FIRE_RUNE_554, 1))) + humidify(player) } + // Level 69 + onCast(Lunar.MOONCLAN_TELEPORT, NONE) { player, _ -> + requires(player, 69, arrayOf(Item(Items.ASTRAL_RUNE_9075,2), Item(Items.LAW_RUNE_563,1), Item(Items.EARTH_RUNE_557,2))) + if (!player.isTeleBlocked) playGlobalAudio(player.location, Sounds.TELEPORT_ALL_200) + sendTeleport(player, 66.0, Location.create(2111, 3916, 0)) + } + + // Level 70 + onCast(Lunar.MOONCLAN_GROUP_TELEPORT, NONE) { player, _ -> + requires(player,70, arrayOf(Item(Items.ASTRAL_RUNE_9075,2), Item(Items.LAW_RUNE_563,1), Item(Items.EARTH_RUNE_557,4))) + if (!player.isTeleBlocked) playGlobalAudio(player.location, Sounds.TELEPORT_ALL_200) + sendGroupTeleport(player,67.0,"Moonclan Island",Location.create(2111, 3916, 0)) + } + + // Level 71 + onCast(Lunar.OURANIA_TELEPORT, NONE) { player, _ -> + requires(player, 71, arrayOf(Item(Items.ASTRAL_RUNE_9075, 2), Item(Items.LAW_RUNE_563, 1), Item(Items.EARTH_RUNE_557, 6))) + if (!player.isTeleBlocked) playGlobalAudio(player.location, Sounds.TELEPORT_ALL_200) + //sendTeleport(player, 69.0, Location.create(2469, 3247, 0)) + sendTeleport(player, 69.0, Location.create(3314, 4818, 0)) + } + + // Level 71 + onCast(Lunar.CURE_ME, NONE) { player, _ -> + requires(player, 71, arrayOf(Item(Items.ASTRAL_RUNE_9075, 2), Item(Items.LAW_RUNE_563, 1), Item(Items.COSMIC_RUNE_564, 2))) + cureMe(player) + } + + // Level 71 + onCast(Lunar.HUNTER_KIT, NONE) { player, _ -> + requires(player, 71, arrayOf(Item(Items.ASTRAL_RUNE_9075, 2), Item(Items.EARTH_RUNE_557, 2))) + if(freeSlots(player) == 0) sendMessage(player, "Not enough inventory space!").also { return@onCast } + hunterKit(player) + } + + // Level 72 + onCast(Lunar.WATERBIRTH_TELEPORT, NONE){ player, _ -> + requires(player,72, arrayOf(Item(Items.ASTRAL_RUNE_9075,2), Item(Items.LAW_RUNE_563), Item(Items.WATER_RUNE_555))) + if (!player.isTeleBlocked) playGlobalAudio(player.location, Sounds.TELEPORT_ALL_200) + sendTeleport(player,71.0, Location.create(2527, 3739, 0)) + } + + // Level 73 + onCast(Lunar.WATERBIRTH_GROUP_TELEPORT, NONE) { player, _ -> + requires(player,73, arrayOf(Item(Items.ASTRAL_RUNE_9075,2), Item(Items.LAW_RUNE_563), Item(Items.WATER_RUNE_555,5))) + if (!player.isTeleBlocked) playGlobalAudio(player.location, Sounds.TELEPORT_ALL_200) + sendGroupTeleport(player,72.0,"Waterbirth Island", Location.create(2527, 3739, 0)) + } + + // Level 74 + onCast(Lunar.CURE_GROUP, NONE) { player, _ -> + requires(player, 74, arrayOf(Item(Items.ASTRAL_RUNE_9075, 2), Item(Items.LAW_RUNE_563, 2), Item(Items.COSMIC_RUNE_564, 2))) + cureGroup(player) + } + + // Level 75 + onCast(Lunar.STAT_SPY, PLAYER) { player, node -> + requires(player, 75, arrayOf(Item(Items.COSMIC_RUNE_564, 2), Item(Items.ASTRAL_RUNE_9075, 2), Item(Items.BODY_RUNE_559, 5))) + node?.let { statSpy(player, node) } + } + + // Level 75 + onCast(Lunar.BARBARIAN_TELEPORT, NONE) { player, _ -> + requires(player,75, arrayOf(Item(Items.ASTRAL_RUNE_9075,2), Item(Items.LAW_RUNE_563,2), Item(Items.FIRE_RUNE_554,3))) + if (!player.isTeleBlocked) playGlobalAudio(player.location, Sounds.TELEPORT_ALL_200) + sendTeleport(player,76.0, Location.create(2544, 3572, 0)) + } + + // Level 76 + onCast(Lunar.BARBARIAN_GROUP_TELEPORT, NONE) { player, _ -> + requires(player,77, arrayOf(Item(Items.ASTRAL_RUNE_9075,2), Item(Items.LAW_RUNE_563,2), Item(Items.FIRE_RUNE_554,6))) + if (!player.isTeleBlocked) playGlobalAudio(player.location, Sounds.TELEPORT_ALL_200) + sendGroupTeleport(player,77.0,"Barbarian Outpost", Location.create(2544, 3572, 0)) + } + + // Level 77 onCast(Lunar.SUPERGLASS_MAKE, NONE) { player, _ -> requires(player, 77, arrayOf(Item(Items.ASTRAL_RUNE_9075, 2), Item(Items.FIRE_RUNE_554, 6), Item(Items.AIR_RUNE_556, 10))) superglassMake(player) } + // Level 78 + onCast(Lunar.KHAZARD_TELEPORT, NONE) { player, _ -> + requires(player,78, arrayOf(Item(Items.ASTRAL_RUNE_9075,2), Item(Items.LAW_RUNE_563,2), Item(Items.WATER_RUNE_555,4))) + if (!player.isTeleBlocked) playGlobalAudio(player.location, Sounds.TELEPORT_ALL_200) + sendTeleport(player,80.0, Location.create(2656, 3157, 0)) + } + + // Level 79 + onCast(Lunar.KHAZARD_GROUP_TELEPORT, NONE) { player, _ -> + requires(player,79, arrayOf(Item(Items.ASTRAL_RUNE_9075,2), Item(Items.LAW_RUNE_563,2), Item(Items.WATER_RUNE_555,8))) + if (!player.isTeleBlocked) playGlobalAudio(player.location, Sounds.TELEPORT_ALL_200) + sendGroupTeleport(player,81.0, "Port Khazard", Location.create(2656, 3157, 0)) + } + + // Level 79 + 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, _ -> + requires(player, 80, arrayOf(Item(Items.ASTRAL_RUNE_9075, 2), Item(Items.EARTH_RUNE_557, 10), Item(Items.WATER_RUNE_555, 5))) + stringJewellery(player) + } + + // Level 81 + /** + * Stat Restore Pot Share + */ + + // Level 82 + /** + * Magic Imbue + */ + + // Level 83 onCast(Lunar.FERTILE_SOIL, OBJECT) { player, node -> + requires(player, 83, arrayOf(Item(Items.ASTRAL_RUNE_9075, 3), Item(Items.NATURE_RUNE_561, 2), Item(Items.EARTH_RUNE_557, 15))) node?.let { fertileSoil(player, node.asScenery()) } } - onCast(Lunar.CURE_ME, NONE) { player, _ -> - cureMe(player) + // Level 84 + /** + * Boost Potion Share + */ + + // Level 85 + onCast(Lunar.FISHING_GUILD_TELEPORT, NONE) { player, _ -> + requires(player,85, arrayOf(Item(Items.ASTRAL_RUNE_9075,3), Item(Items.LAW_RUNE_563,3), Item(Items.WATER_RUNE_555,10))) + if (!player.isTeleBlocked) playGlobalAudio(player.location, Sounds.TELEPORT_ALL_200) + sendTeleport(player,89.0, Location.create(2611, 3393, 0)) } - onCast(Lunar.CURE_GROUP, NONE) { player, _ -> - cureGroup(player) + // Level 86 + onCast(Lunar.FISHING_GUILD_GROUP_TELEPORT, NONE) { player, _ -> + requires(player,86, arrayOf(Item(Items.ASTRAL_RUNE_9075,3), Item(Items.LAW_RUNE_563,3), Item(Items.WATER_RUNE_555,14))) + if (!player.isTeleBlocked) playGlobalAudio(player.location, Sounds.TELEPORT_ALL_200) + sendGroupTeleport(player,90.0,"Fishing Guild", Location.create(2611, 3393, 0)) } - onCast(Lunar.CURE_OTHER, PLAYER) { player, node -> - node?.let { cureOther(player, node) } + // Level 86 + onCast(Lunar.PLANK_MAKE, ITEM) { player, node -> + requires(player, 86, arrayOf(Item(Items.ASTRAL_RUNE_9075, 2), Item(Items.NATURE_RUNE_561, 1), Item(Items.EARTH_RUNE_557, 15))) + plankMake(player, node!!.asItem()) } + + // Level 87 + onCast(Lunar.CATHERBY_TELEPORT, NONE) { player, _ -> + requires(player,87, arrayOf(Item(Items.ASTRAL_RUNE_9075,3), Item(Items.LAW_RUNE_563,3), Item(Items.WATER_RUNE_555,10))) + if (!player.isTeleBlocked) playGlobalAudio(player.location, Sounds.TELEPORT_ALL_200) + sendTeleport(player,92.0, Location.create(2804, 3433, 0)) + } + + // Level 88 + onCast(Lunar.CATHERBY_GROUP_TELEPORT, NONE) { player, _ -> + requires(player,88, arrayOf(Item(Items.ASTRAL_RUNE_9075,3), Item(Items.LAW_RUNE_563,3), Item(Items.WATER_RUNE_555,15))) + if (!player.isTeleBlocked) playGlobalAudio(player.location, Sounds.TELEPORT_ALL_200) + sendGroupTeleport(player,93.0,"Catherby", Location.create(2804, 3433, 0)) + } + + // Level 89 + onCast(Lunar.ICE_PLATEAU_TELEPORT, NONE) { player, _ -> + requires(player,89, arrayOf(Item(Items.ASTRAL_RUNE_9075,3), Item(Items.LAW_RUNE_563,3), Item(Items.WATER_RUNE_555,8))) + if (!player.isTeleBlocked) playGlobalAudio(player.location, Sounds.TELEPORT_ALL_200) + sendTeleport(player,96.0, Location.create(2972, 3873, 0)) + } + + // Level 90 + onCast(Lunar.ICE_PLATEAU_GROUP_TELEPORT, NONE) { player, _ -> + requires(player,90, arrayOf(Item(Items.ASTRAL_RUNE_9075,3), Item(Items.LAW_RUNE_563,3), Item(Items.WATER_RUNE_555,16))) + if (!player.isTeleBlocked) playGlobalAudio(player.location, Sounds.TELEPORT_ALL_200) + sendGroupTeleport(player,99.0, "Ice Plateau", Location.create(2972, 3873, 0)) + } + + // Level 91 + onCast(Lunar.ENERGY_TRANSFER, PLAYER) { player, node -> + requires(player, 91, arrayOf(Item(Items.ASTRAL_RUNE_9075, 3), Item(Items.LAW_RUNE_563, 2), Item(Items.NATURE_RUNE_561, 1))) + node?.let { energyTransfer(player, node) } + } + + // Level 92 + /** + * Heal Other + */ + + // Level 93 + /** + * Vengeance Other + */ + + // Level 94 + /** + * Vengeance + */ + + // Level 95 + /** + * Heal Group + */ + + // Level 96 + /** + * Spellbook Swap + */ } - // Lunar spellbook-related debug commands - override fun defineCommands() { - define("poison", privilege = Privilege.ADMIN) { player, strings -> - if(strings.size == 3) { - var dmg = strings[2].toIntOrNull() - val p = Repository.getPlayerByName(strings[1]) - if(p == null) { - sendMessage(player, "Player ${strings[1]} does not exist.") - return@define - } - if(dmg != null) { - p?.let { addState(it, EntityState.POISONED, false, (dmg * 10 + 8), player) } - } else { - sendMessage(player, "Damage must be an integer. Format:") - sendMessage(player, "::poison username damage") - } - } else { - sendMessage(player, "Invalid arguments provided. Format:") - sendMessage(player, "::poison username damage") - } - } - } - private fun plankMake(player: Player, item: Item) { - - val plankType = PlankType.getForLog(item) - if (plankType == null) { - sendMessage(player, "You need to use this spell on logs.") - return - } - if (!removeItem(player, Item(Items.COINS_995, plankType.price))) { - sendMessage(player, "You need ${plankType.price} coins to convert that log into a plank.") - return - } - lock(player, 3) - setDelay(player, false) - visualizeSpell(player, PLANK_MAKE_ANIM, PLANK_MAKE_GFX, 3617) - removeRunes(player) - replaceSlot(player, item.slot, plankType.plank) - addXP(player, 90.0) - showMagicTab(player) - } - - enum class PlankType (val log: Item, val plank: Item, val price: Int) { - WOOD(Item(1511), Item(960), 70), - OAK(Item(1521), Item(8778), 175), - TEAK(Item(6333), Item(8780), 350), - MAHOGANY(Item(6332), Item(8782), 1050); - companion object { - fun getForLog(item: Item): PlankType? { - for (plankType in values()) { - if (plankType.log.id == item.id) { - return plankType - } - } - return null - } - } - } - - fun curePlant(player: Player, obj: Scenery){ - val fPatch = FarmingPatch.forObject(obj) - if(fPatch == null){ - player.sendMessage("You attempt to cast Cure Plant on ${obj.definition.name}!") - player.sendMessage("Nothing interesting happens.") - return - } - val patch = fPatch.getPatchFor(player) - if(!patch.isDiseased && !patch.isWeedy()){ - player.sendMessage("It is growing just fine.") - return - } - if(patch.isWeedy()){ - player.sendMessage("The weeds are healthy enough already.") - return - } - if(patch.isDead){ - player.sendMessage("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.isGrown()){ - player.sendMessage("That's not diseased.") - return - } - - patch.cureDisease() - removeRunes(player) - addXP(player,60.0) - visualizeSpell(player, CURE_PLANT_ANIM, CURE_PLANT_GFX) - setDelay(player,false) - } - - private fun examineMonster(player: Player, npc: NPC){ - if(!npc.location.withinDistance(player.location)){ - player.sendMessage("You must get closer to use this spell.") - return - } - - player.faceLocation(npc.location) - visualizeSpell(player, STATSPY_ANIM, STATSPY_GFX,3620) - removeRunes(player) - addXP(player,66.0) - setDelay(player,false) - - player.interfaceManager.openSingleTab(Component(Components.DREAM_MONSTER_STAT_522)) - player.packetDispatch.sendString("Monster name : " + npc.definition.name,Components.DREAM_MONSTER_STAT_522,0) - player.packetDispatch.sendString("Combat Level : ${npc.definition.combatLevel}",Components.DREAM_MONSTER_STAT_522,1) - player.packetDispatch.sendString("Hitpoints : ${npc.definition.handlers.get(NPCConfigParser.LIFEPOINTS) ?: 0}",Components.DREAM_MONSTER_STAT_522,2) - player.packetDispatch.sendString("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){ - "This creature is immune to poison." - } else "This creature is not immune to poison." - - player.packetDispatch.sendString(poisonStatus,Components.DREAM_MONSTER_STAT_522,4) - } - + // Spell handlers + // Level 65 private fun bakePie(player: Player){ val playerPies = ArrayList() @@ -320,12 +305,13 @@ class LunarListeners : SpellListener("lunar"), Commands { var counter = 0 override fun pulse(): Boolean { if(playerPies.isEmpty()) return true - if(counter == 0) delay = BAKE_PIE_ANIM.definition.durationTicks + 1 - val item = playerPies.get(0) + + if(counter == 0) delay = animationDuration(Animation(Animations.LUNAR_SPELLBOOK_BAKE_PIE_4413)) + 1 + val item = playerPies[0] val pie = CookableItems.forId(item.id) - visualizeSpell(player, BAKE_PIE_ANIM, BAKE_PIE_GFX, 2879) + visualizeSpell(player, Animations.LUNAR_SPELLBOOK_BAKE_PIE_4413, Graphics.LUNAR_SPELLBOOK_BAKE_PIE_746, 75, Sounds.LUNAR_BAKE_PIE_2879) addXP(player,60.0) - player.skills.addExperience(Skills.COOKING,pie.experience) + player.skills.addExperience(Skills.COOKING, pie.experience) setDelay(player,false) player.inventory.remove(item) player.inventory.add(Item(pie.cooked)) @@ -336,71 +322,236 @@ class LunarListeners : SpellListener("lunar"), Commands { }) } - private fun sendTeleport(player: Player, xp: Double, loc: Location){ - if(player.locks.isTeleportLocked){ - player.sendMessage("A magical force prevents you from teleporting.") + // Level 66 + fun curePlant(player: Player, obj: Scenery) { + if (CompostBins.forObject(obj) != null) { + sendMessage(player, "Bins don't often get diseased.") return } - if(player.teleporter.send(loc,TeleportManager.TeleportType.LUNAR)) { - addXP(player, xp) + val fPatch = FarmingPatch.forObject(obj) + if (fPatch == null) { + sendMessage(player, "Umm... this spell won't cure that!") + return + } + val patch = fPatch.getPatchFor(player) + if (patch.isWeedy()) { + sendMessage(player, "The weeds are healthy enough already.") + return + } + if (patch.isEmptyAndWeeded()) { + sendMessage(player, "There's nothing there to cure.") + return + } + if (patch.isGrown()) { + sendMessage(player, "That's not diseased.") + return + } + 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.isDiseased) { + sendMessage(player, "It is growing just fine.") + return + } + + patch.cureDisease() + removeRunes(player) + addXP(player,60.0) + visualizeSpell(player, Animations.LUNAR_SPELLBOOK_CURE_PLANT_4409, Graphics.LUNAR_SPELLBOOK_CURE_PLANT_748, 100, Sounds.LUNAR_CURE_GROUP_2882) + setDelay(player,false) + } + + // Level 66 + private fun monsterExamine(player: Player, npc: NPC){ + if(!npc.location.withinDistance(player.location)){ + sendMessage(player, "You must get closer to use this spell.") + return + } + face(player, npc) + visualizeSpell(player, Animations.LUNAR_SPELLBOOK_STATSPY_6293, Graphics.LUNAR_SPELLBOOK_STAT_SPY_OVER_PLAYER_1060, soundID = Sounds.LUNAR_STAT_SPY_3620) + removeRunes(player) + addXP(player,66.0) + + setDelay(player, false) + openSingleTab(player, Components.DREAM_MONSTER_STAT_522) + var bonuses = npc.properties.bonuses + setInterfaceText(player, "Monster name: ${npc.definition.name} (${npc.definition.combatLevel})", Components.DREAM_MONSTER_STAT_522, 0) + setInterfaceText(player, """ +Att:${npc.definition.handlers[NPCConfigParser.ATTACK_LEVEL] ?: 0} +Str:${npc.definition.handlers[NPCConfigParser.STRENGTH_LEVEL] ?: 0} +Ranged:${npc.definition.handlers[NPCConfigParser.RANGE_LEVEL] ?: 0} +Magic:${npc.definition.handlers[NPCConfigParser.MAGIC_LEVEL] ?: 0}""", Components.DREAM_MONSTER_STAT_522, 1) + setInterfaceText(player, """ +Defence:${npc.definition.handlers[NPCConfigParser.DEFENCE_LEVEL] ?: 0} +HP:${npc.definition.handlers[NPCConfigParser.LIFEPOINTS] ?: 0} +Max-hit:${npc.getSwingHandler(false).calculateHit(npc, player, 1.0)}""", Components.DREAM_MONSTER_STAT_522, 2) + setInterfaceText(player, "${bonuses[0]},${bonuses[1]},${bonuses[2]},${bonuses[3]},${bonuses[4]}, ${bonuses[5]},${bonuses[6]},${bonuses[7]},${bonuses[8]},${bonuses[9]}", Components.DREAM_MONSTER_STAT_522, 3) + + val poisonStatus = if(npc.definition.handlers.getOrDefault(NPCConfigParser.POISON_IMMUNE,false) == true){ + "Immune to poison. " + } else "" + val poisonStatus2 = if(npc.definition.handlers.getOrDefault(NPCConfigParser.POISONOUS,false) == true){ + "Poisonous." + } else "" + setInterfaceText(player, poisonStatus + poisonStatus2, Components.DREAM_MONSTER_STAT_522, 4) + } + + // Level 67 + private fun npcContact(player: Player) { + openInterface(player, 429) + setAttribute(player, "contact-caller") { removeRunes(player) - setDelay(player, true) + addXP(player,63.0) + setDelay(player,false) + visualizeSpell(player, Animations.LUNAR_SPELLBOOK_NPC_CONTACT_4413, Graphics.LUNAR_SPELLBOOK_NPC_CONTACT_728, 130,3618) } } - private fun sendGroupTeleport(player: Player, xp: Double, destName: String, loc: Location){ - RegionManager.getLocalPlayers(player,1).forEach { - if(it == player) return@forEach - if(it.locks.isTeleportLocked) return@forEach - if(!it.isActive) return@forEach - if(!it.settings.isAcceptAid) return@forEach - if(it.ironmanManager.isIronman) return@forEach - it.setAttribute("t-o_location",loc) - it.interfaceManager.open(Component(Components.TELEPORT_OTHER_326)) - it.packetDispatch.sendString(player.username,Components.TELEPORT_OTHER_326,1) - it.packetDispatch.sendString(destName,Components.TELEPORT_OTHER_326,3) + // Level 68 + private fun cureOther(player: Player, target: Node) { + if(!isPlayer(target)) { + sendMessage(player, "You can only cast this spell on other players.") + return } - sendTeleport(player,xp,loc) + val p = target.asPlayer() + if(!p.isActive || p.locks.isInteractionLocked) { + sendMessage(player, "This player is busy.") + return + } + if(!p.settings.isAcceptAid) { + sendMessage(player, "This player is not accepting any aid.") + return + } + if(!isPoisoned(p)) { + sendMessage(player, "This player is not poisoned.") + return + } + player.face(p) + visualizeSpell(player, Animations.LUNAR_SPELLBOOK_CURE_OTHER_4411, Graphics.LUNAR_SPELLBOOK_CURE_OTHER_736, 130, Sounds.LUNAR_CURE_OTHER_2886) + visualizeSpell(p, -1, Graphics.LUNAR_SPELLBOOK_CURE_OTHER_736, 130, Sounds.LUNAR_CURE_OTHER_INDIVIDUAL_2889) + removeRunes(player, true) + curePoison(p) + sendMessage(p, "You have been cured of poison.") + addXP(player, 65.0) } - private fun stringJewellery(player: Player) { - val playerJewellery = ArrayDeque() + // Level 68 + private fun humidify(player: Player) { + val playerEmpties = ArrayDeque() for(item in player.inventory.toArray()) { if(item == null) continue - if(!JewelleryString.unstrungContains(item.id)) continue - playerJewellery.add(item) + if(!HumidifyItems.emptyContains(item.id)) continue + playerEmpties.add(item) } - player.pulseManager.run(object : Pulse() { - var counter = 0 - override fun pulse(): Boolean { - removeAttribute(player, "spell:runes") - requires(player, 80, arrayOf(Item(Items.ASTRAL_RUNE_9075, 2), Item(Items.EARTH_RUNE_557, 10), Item(Items.WATER_RUNE_555, 5))) - if(counter == 0) delay = animationDuration(STRING_JEWELLERY_ANIM) + 1 - val item = playerJewellery[0] - val strung = JewelleryString.forId(item.id) - setDelay(player,false) - if(removeItem(player, item) && addItem(player, strung)) { - removeRunes(player, true) - visualizeSpell(player, STRING_JEWELLERY_ANIM, STRING_JEWELLERY_GFX, 2903) - rewardXP(player, Skills.CRAFTING, 4.0) - addXP(player, 83.0) - playerJewellery.remove(item) - if(playerJewellery.isNotEmpty()) removeRunes(player,false) else removeRunes(player,true) - } - counter++ - return playerJewellery.isEmpty() - } - }) + if(playerEmpties.isEmpty()) { + sendMessage(player, "You have nothing in your inventory that this spell can humidify.") + return + } + + removeRunes(player) + delayEntity(player, Animation(Animations.LUNAR_SPELLBOOK_HUMIDIFY_6294).duration) + visualizeSpell(player, Animations.LUNAR_SPELLBOOK_HUMIDIFY_6294, Graphics.LUNAR_SPELLBOOK_HUMIDIFY_1061, 20, Sounds.LUNAR_HUMIDIFY_3614) + for(item in playerEmpties) { + val filled = HumidifyItems.forId(item.id) + removeItem(player, item.id) && addItem(player, filled) + } + addXP(player, 65.0) + /** + queueScript(player) { + return@queueScript stopExecuting(player) + } + */ } + // Level 71 + private fun cureMe(player: Player) { + if(!isPoisoned(player)) { + sendMessage(player, "You are not poisoned.") + return + } + removeRunes(player, true) + visualizeSpell(player, Animations.LUNAR_SPELLBOOK_CURE_ME_4411, Graphics.LUNAR_SPELLBOOK_CURE_ME_742, 90, Sounds.LUNAR_CURE_2884) + curePoison(player) + addXP(player, 69.0) + playAudio(player, Sounds.LUNAR_CURE_OTHER_INDIVIDUAL_2900) + sendMessage(player, "You have been cured of poison.") + } + + // Level 71 + private fun hunterKit(player: Player) { + removeRunes(player, true) + if(addItem(player, Items.HUNTER_KIT_11159)) { + visualizeSpell(player, Animations.LUNAR_SPELLBOOK_HUNTER_KIT_6303, Graphics.LUNAR_SPELLBOOK_HUNTER_KIT_1074, soundID = Sounds.LUNAR_HUNTER_KIT_3615) + addXP(player, 70.0) + setDelay(player, 2) + } + } + + // Level 74 + private fun cureGroup(player: Player) { + removeRunes(player, true) + visualizeSpell(player, Animations.LUNAR_SPELLBOOK_CURE_GROUP_4409, Graphics.LUNAR_SPELLBOOK_CURE_GROUP_744, 130, Sounds.LUNAR_CURE_GROUP_2882) + curePoison(player) + for(acct in RegionManager.getLocalPlayers(player, 1)) { + if(!acct.isActive || acct.locks.isInteractionLocked) { + continue + } + if(!acct.settings.isAcceptAid) { + continue + } + curePoison(acct) + sendMessage(acct, "You have been cured of poison.") + visualizeSpell(acct, -1, Graphics.LUNAR_SPELLBOOK_CURE_GROUP_744, 130, Sounds.LUNAR_CURE_OTHER_INDIVIDUAL_2889) + } + addXP(player, 74.0) + } + + // Level 75 + private fun statSpy(player: Player, target: Node) { + if(target !is Player) { + sendMessage(player, "You can only cast this spell on players.") + return + } + val stat = Components.DREAM_PLAYER_STATS_523 + val statCloseEvent = CloseEvent { p, _ -> + p.interfaceManager.restoreTabs() + return@CloseEvent true + } + + removeRunes(player, true) + + face(player, target) + animate(player, Animations.LUNAR_SPELLBOOK_STATSPY_6293) + playAudio(player, Sounds.LUNAR_STAT_SPY_3620) + + rewardXP(player, Skills.MAGIC, 76.0) + + Component(stat).setCloseEvent(statCloseEvent) + + sendGraphics(Graphics.LUNAR_SPELLBOOK_STAT_SPY_OVER_PLAYER_1060, player.location) + sendGraphics(Graphics.LUNAR_SPELLBOOK_STAT_SPY_OVER_MONSTER_734, target.location) + + playGlobalAudio(target.location, Sounds.LUNAR_STAT_SPY_IMPACT_3621) + + for(element in statSpySkills) { + setInterfaceText(player, "${ getDynLevel(target, element[0]) }", Components.DREAM_PLAYER_STATS_523, element[1]) + setInterfaceText(player, "${ getStatLevel(target, element[0]) }", Components.DREAM_PLAYER_STATS_523, element[2]) + } + + setInterfaceText(player, target.username, Components.DREAM_PLAYER_STATS_523, 99) + openSingleTab(player, stat) + } + + // Level 77 private fun superglassMake(player: Player) { - val GLASSWEED = hashSetOf(Items.SODA_ASH_1781, Items.SEAWEED_401, Items.SWAMP_WEED_10978) + val GLASS_WEEDS = hashSetOf(Items.SODA_ASH_1781, Items.SEAWEED_401, Items.SWAMP_WEED_10978) val inv = player.inventory.toArray() var playerWeed: Int = amountInInventory(player, Items.SODA_ASH_1781) + amountInInventory(player, Items.SEAWEED_401) + amountInInventory(player, Items.SWAMP_WEED_10978) var playerSand: Int = amountInInventory(player, Items.BUCKET_OF_SAND_1783) - var index: Int = 0 + var index = 0 fun addMolten(): Boolean { if(RandomFunction.randomDouble(1.0) < 0.3) { @@ -417,7 +568,7 @@ class LunarListeners : SpellListener("lunar"), Commands { for (item in inv) { if (item == null) continue if (index == size) break - if (GLASSWEED.contains(item.id)) { + if (GLASS_WEEDS.contains(item.id)) { if (removeItem(player, item) && removeItem(player, Items.BUCKET_OF_SAND_1783) && addMolten()) { index++ } else { @@ -431,12 +582,91 @@ class LunarListeners : SpellListener("lunar"), Commands { if(index == size && size != 0) { removeRunes(player, true) - visualizeSpell(player, SUPERGLASS_MAKE_ANIM, SUPERGLASS_MAKE_GFX, 2896) + visualizeSpell(player, Animations.LUNAR_SPELLBOOK_SUPERGLASS_MAKE_4413, Graphics.LUNAR_SPELLBOOK_SUPERGLASS_MAKE_729, 120, Sounds.LUNAR_HEATGLASS_2896) rewardXP(player, Skills.CRAFTING, 10.0) addXP(player, 78.0) } } + // Level 79 + 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.") + return + } + + animate(player, Animations.LUNAR_SPELLBOOK_DREAM_START_6295) + delayEntity(player, 4) + queueScript(player, 4, QueueStrength.WEAK) { stage: Int -> + when(stage) { + 0 -> { + animate(player, Animations.LUNAR_SPELLBOOK_DREAM_MID_6296) + sendGraphics(Graphics.LUNAR_SPELLBOOK_DREAM_1056, player.location) + playAudio(player, Sounds.LUNAR_SLEEP_3619) + return@queueScript delayScript(player, 5) + } + else -> { + 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){ + heal(player, 1) + 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() + + for(item in player.inventory.toArray()) { + if(item == null) continue + if(!StringJewelleryItems.unstrungContains(item.id)) continue + playerJewellery.add(item) + } + + player.pulseManager.run(object : Pulse() { + var counter = 0 + override fun pulse(): Boolean { + removeAttribute(player, "spell:runes") + if (playerJewellery.isEmpty()) + return true + requires(player, 80, arrayOf(Item(Items.ASTRAL_RUNE_9075, 2), Item(Items.EARTH_RUNE_557, 10), Item(Items.WATER_RUNE_555, 5))) + if(counter == 0) delay = animationDuration(Animation(Animations.LUNAR_SPELLBOOK_STRING_JEWELLERY_4412)) + 1 + val item = playerJewellery[0] + val strung = StringJewelleryItems.forId(item.id) + setDelay(player,false) + if(removeItem(player, item) && addItem(player, strung)) { + removeRunes(player, true) + visualizeSpell(player, Animations.LUNAR_SPELLBOOK_STRING_JEWELLERY_4412, Graphics.LUNAR_SPELLBOOK_STRING_JEWELLERY_730, 100, Sounds.LUNAR_STRING_AMULET_2903) + rewardXP(player, Skills.CRAFTING, 4.0) + addXP(player, 83.0) + playerJewellery.remove(item) + if(playerJewellery.isNotEmpty()) removeRunes(player,false) else removeRunes(player,true) + } + counter++ + return playerJewellery.isEmpty() + } + }) + } + + // Level 81 + /** + * Stat Restore Pot Share + */ + + // Level 82 + /** + * Magic Imbue + */ + + // Level 83 private fun fertileSoil(player: Player, target: Scenery) { if (CompostBins.forObjectID(target.id) != null) { sendMessage(player, "No, that would be silly.") @@ -458,75 +688,190 @@ class LunarListeners : SpellListener("lunar"), Commands { sendMessage(player, "This patch has already been composted.") return } - requires(player, 83, arrayOf(Item(Items.ASTRAL_RUNE_9075, 3), Item(Items.NATURE_RUNE_561, 2), Item(Items.EARTH_RUNE_557, 15))) removeRunes(player, true) - visualizeSpell(player, FERTILE_SOIL_ANIM, FERTILE_SOIL_GFX, 2891) - patch.compost = CompostType.SUPER + animate(player, Animations.LUNAR_SPELLBOOK_FERTILE_SOIL_4413) + sendGraphics(Graphics.LUNAR_SPELLBOOK_FERTILE_SOIL_724, target.location) + playGlobalAudio(target.location, Sounds.LUNAR_FERTILIZE_2891) + patch.compost = CompostType.SUPERCOMPOST sendMessage(player, "You fertilize the soil.") addXP(player, 87.0) } - private fun cureMe(player: Player) { - if(!hasState(player, EntityState.POISONED)) { - sendMessage(player, "You are not poisoned.") + // Level 84 + /** + * Boost Potion Share + */ + + // Level 86 + private fun plankMake(player: Player, item: Item) { + val plankType = PlankType.getForLog(item) + if (plankType == null) { + sendMessage(player, "You need to use this spell on logs.") return } - requires(player, 71, arrayOf(Item(Items.ASTRAL_RUNE_9075, 2), Item(Items.LAW_RUNE_563, 1), Item(Items.COSMIC_RUNE_564, 2))) - removeRunes(player, true) - visualizeSpell(player, CURE_ME_ANIM, CURE_ME_GFX, 2880) - removeState(player, EntityState.POISONED) - addXP(player, 69.0) - playAudio(player, Audio(2900)) - sendMessage(player, "You have been cured of poison.") - } - - private fun cureGroup(player: Player) { - requires(player, 74, arrayOf(Item(Items.ASTRAL_RUNE_9075, 2), Item(Items.LAW_RUNE_563, 2), Item(Items.COSMIC_RUNE_564, 2))) - removeRunes(player, true) - visualizeSpell(player, CURE_GROUP_ANIM, CURE_GROUP_GFX, 2882) - removeState(player, EntityState.POISONED) - for(acct in RegionManager.getLocalPlayers(player, 1)) { - if(!acct.isActive || acct.locks.isInteractionLocked) { - continue + player.pulseManager.run(object : Pulse() { + var counter = 0 + override fun pulse(): Boolean { + removeAttribute(player, "spell:runes") + if (amountInInventory(player, item.id) == 0 ) + return true + requires(player, 86, arrayOf(Item(Items.ASTRAL_RUNE_9075, 2), Item(Items.NATURE_RUNE_561, 1), Item(Items.EARTH_RUNE_557, 15))) + if(removeItem(player, item.id) && addItem(player, plankType.plank.id)) { + removeRunes(player, false) + if (counter % 6 == 0) + visualizeSpell(player, Animations.LUNAR_SPELLBOOK_PLANK_MAKE_6298, Graphics.LUNAR_SPELLBOOK_PLANK_MAKE_1063, 120, Sounds.LUNAR_MAKE_PLANK_3617) + addXP(player, 90.0) + } + counter++ + return false } - if(!acct.settings.isAcceptAid) { - continue - } - removeState(acct, EntityState.POISONED) - sendMessage(acct, "You have been cured of poison.") - playAudio(acct, Audio(2889), true) - visualize(acct, -1, CURE_GROUP_GFX) + }) + /* Vanilla Spell + 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 } - addXP(player, 74.0) + lock(player, 3) + setDelay(player, false) + visualizeSpell(player, Animations.LUNAR_SPELLBOOK_PLANK_MAKE_6298, Graphics.LUNAR_SPELLBOOK_PLANK_MAKE_1063, 120, Sounds.LUNAR_MAKE_PLANK_3617) + removeRunes(player) + replaceSlot(player, item.slot, plankType.plank) + addXP(player, 90.0) + showMagicTab(player) + */ } - private fun cureOther(player: Player, target: Node) { + // Level 91 + private fun energyTransfer(player: Player, target: Node) { if(!isPlayer(target)) { sendMessage(player, "You can only cast this spell on other players.") return } - val p = target.asPlayer() - if(!p.isActive || p.locks.isInteractionLocked) { + val targetPlayer = target.asPlayer() + if(!targetPlayer.isActive || targetPlayer.locks.isInteractionLocked) { sendMessage(player, "This player is busy.") return } - if(!p.settings.isAcceptAid) { + if(!targetPlayer.settings.isAcceptAid) { sendMessage(player, "This player is not accepting any aid.") return } - if(!hasState(p, EntityState.POISONED)) { - sendMessage(player, "This player is not poisoned.") + if(10 >= player.skills.lifepoints) { + sendMessage(player, "You need more hitpoints to cast this spell.") return } - requires(player, 68, arrayOf(Item(Items.ASTRAL_RUNE_9075, 1), Item(Items.LAW_RUNE_563), Item(Items.EARTH_RUNE_557, 10))) - player.face(p) - visualizeSpell(player, CURE_OTHER_ANIM, CURE_OTHER_GFX, 2886) - visualize(p, -1, CURE_OTHER_GFX) - playAudio(p, Audio(2889), true) + player.face(targetPlayer) + visualizeSpell(player, Animations.LUNAR_SPELLBOOK_ENERGY_TRANSFER_4411, Graphics.LUNAR_SPELLBOOK_ENERGY_TRANSFER_738, 90, Sounds.LUNAR_ENERGY_TRANSFER_2885) + visualize(targetPlayer, -1, Graphics.LUNAR_SPELLBOOK_ENERGY_TRANSFER_738) + val hp = floor(player.skills.lifepoints * 0.10) + var run = hp + if(run > (100 - targetPlayer.settings.runEnergy)) { + run = (100 - targetPlayer.settings.runEnergy) + } + if(run < 0) { + run = 0.0 + } + targetPlayer.settings.runEnergy += run + player.settings.runEnergy -= run + impact(player, hp.toInt(), ImpactHandler.HitsplatType.NORMAL) + var energy = 100 + energy -= targetPlayer.settings.specialEnergy + if(energy < 0) { + energy = 0 + } + if(energy > player.settings.specialEnergy) { + energy = player.settings.specialEnergy + } + targetPlayer.settings.specialEnergy += energy + player.settings.specialEnergy -= energy removeRunes(player, true) - removeState(p, EntityState.POISONED) - sendMessage(p, "You have been cured of poison.") - addXP(player, 65.0) + addXP(player, 100.0) + } + + // Level 92 + /** + * Heal Other + */ + + // Level 93 + /** + * Vengeance Other + */ + + // Level 94 + /** + * Vengeance + */ + + // Level 95 + /** + * Heal Group + */ + + // Level 96 + /** + * Spellbook Swap + */ + + // Other/Multi spell use-case + private fun sendTeleport(player: Player, xp: Double, loc: Location){ + if(player.teleporter.send(loc,TeleportManager.TeleportType.LUNAR)) { + addXP(player, xp) + removeRunes(player) + setDelay(player, true) + } + } + + private fun sendGroupTeleport(player: Player, xp: Double, destName: String, loc: Location){ + RegionManager.getLocalPlayers(player, 1).forEach { + if(it == player) return@forEach + if(it.isTeleBlocked) return@forEach + if(!it.isActive) return@forEach + if(!it.settings.isAcceptAid) return@forEach + if(it.ironmanManager.isIronman) return@forEach + setAttribute(it, "t-o_location", loc) + openInterface(it, Components.TELEPORT_OTHER_326) + setInterfaceText(it, player.username, Components.TELEPORT_OTHER_326, 1) + setInterfaceText(it, destName, Components.TELEPORT_OTHER_326, 3) + } + + sendTeleport(player, xp, loc) + } + + override fun defineCommands() { + define("poison", privilege = Privilege.ADMIN) { player, strings -> + if(strings.size == 3) { + val dmg = strings[2].toIntOrNull() + val p = Repository.getPlayerByName(strings[1]) + if(p == null) { + sendMessage(player, "Player ${strings[1]} does not exist.") + return@define + } + if(dmg != null) { + p.let { applyPoison(it, it, dmg) } + } else { + sendMessage(player, "Damage must be an integer. Format:") + sendMessage(player, "::poison username damage") + } + } else { + sendMessage(player, "Invalid arguments provided. Format:") + sendMessage(player, "::poison username damage") + } + } + + define("humidifykit", privilege = Privilege.ADMIN) { player, _ -> + if(freeSlots(player) < 24) { + sendMessage(player, "Not enough free space.") + return@define + } else { + addItem(player, Items.ASTRAL_RUNE_9075, 100) + addItem(player, Items.WATER_RUNE_555, 300) + addItem(player, Items.FIRE_RUNE_554, 100) + for(item in HumidifyItems.values()) { + addItem(player, item.empty) + } + } + } } } diff --git a/Server/src/main/content/global/skill/magic/lunar/MagicImbueSpell.java b/Server/src/main/content/global/skill/magic/lunar/MagicImbueSpell.java index 80574cddc..79512f7c4 100644 --- a/Server/src/main/content/global/skill/magic/lunar/MagicImbueSpell.java +++ b/Server/src/main/content/global/skill/magic/lunar/MagicImbueSpell.java @@ -13,6 +13,9 @@ import core.game.world.GameWorld; import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Graphics; import core.plugin.Plugin; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.playAudio; /** * The magic imbue spell. @@ -61,7 +64,7 @@ public class MagicImbueSpell extends MagicSpell { player.lock(ANIMATION.getDuration() + 1); player.graphics(GRAPHIC); player.animate(ANIMATION); - player.getAudioManager().send(2888, 1, 1); + playAudio(player, Sounds.LUNAR_EMBUE_RUNES_2888); player.getPacketDispatch().sendMessage("You are charged to combine runes!"); return true; } diff --git a/Server/src/main/content/global/skill/magic/lunar/StatBoostSpell.java b/Server/src/main/content/global/skill/magic/lunar/StatBoostSpell.java index 2877d1a1e..61aeee5f7 100644 --- a/Server/src/main/content/global/skill/magic/lunar/StatBoostSpell.java +++ b/Server/src/main/content/global/skill/magic/lunar/StatBoostSpell.java @@ -8,7 +8,6 @@ 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.bots.AIPlayer; import core.game.node.entity.player.link.SpellBookManager.SpellBook; import core.game.node.item.Item; @@ -17,9 +16,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.Sounds; import java.util.List; +import static core.api.ContentAPIKt.playGlobalAudio; + @Initializable public final class StatBoostSpell extends MagicSpell { @@ -40,7 +42,7 @@ public final class StatBoostSpell extends MagicSpell { public boolean cast(Entity entity, Node target) { final Player player = ((Player) entity); Item item = ((Item) target); - final Potion potion = (Potion) Consumables.getConsumableById(item.getId()); + final Potion potion = (Potion) Consumables.getConsumableById(item.getId()).getConsumable(); player.getInterfaceManager().setViewedTab(6); if (potion == null) { player.getPacketDispatch().sendMessage("You can only cast this spell on a potion."); @@ -53,22 +55,16 @@ public final class StatBoostSpell extends MagicSpell { List pl = RegionManager.getLocalPlayers(player, 1); int plSize = pl.size() - 1; int doses = potion.getDose(item); - if (plSize > doses) { - player.getPacketDispatch().sendMessage("You don't have enough doses."); - return false; - } - if (doses > plSize) { - doses = plSize; - } if (pl.size() == 0) { return false; } if (!super.meetsRequirements(player, true, false)) { return false; } - int size = 1; + int size = 0; for (Player players : pl) { Player o = (Player) players; + if (size >= doses) break; if (!o.isActive() || o.getLocks().isInteractionLocked() || o == player) { continue; } @@ -76,21 +72,26 @@ public final class StatBoostSpell extends MagicSpell { continue; } o.graphics(GRAPHICS); - o.getAudioManager().send(new Audio (2902), true); + playGlobalAudio(o.getLocation(), Sounds.LUNAR_STRENGTH_SHARE2_2902); potion.getEffect().activate(o); size++; } - if (size == 1) { + if (size == 0) { player.getPacketDispatch().sendMessage("There is nobody around that has accept aid on to share the potion with you."); return false; } super.meetsRequirements(player, true, true); potion.getEffect().activate(player); - player.getAudioManager().send(new Audio (2901), true); + playGlobalAudio(player.getLocation(), Sounds.LUNAR_STRENGTH_SHARE_2901); player.animate(ANIMATION); player.graphics(GRAPHICS); player.getInventory().remove(item); - player.getInventory().add(new Item(potion.getIds()[size - 1])); + int newIndex = (potion.getIds().length - doses) + size; + if (newIndex > potion.getIds().length - 1) { + player.getInventory().add(new Item(229)); + return true; + } + player.getInventory().add(new Item(potion.getIds()[newIndex])); return true; } } diff --git a/Server/src/main/content/global/skill/magic/lunar/StatRestoreSpell.java b/Server/src/main/content/global/skill/magic/lunar/StatRestoreSpell.java index e702f9942..1f984d2da 100644 --- a/Server/src/main/content/global/skill/magic/lunar/StatRestoreSpell.java +++ b/Server/src/main/content/global/skill/magic/lunar/StatRestoreSpell.java @@ -9,7 +9,6 @@ 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.bots.AIPlayer; import core.game.node.entity.player.link.SpellBookManager.SpellBook; import core.game.node.item.Item; @@ -17,16 +16,18 @@ 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.Plugin; +import org.rs09.consts.Sounds; import java.util.List; +import static core.api.ContentAPIKt.playGlobalAudio; + @Initializable public class StatRestoreSpell extends MagicSpell { private static final Animation ANIMATION = new Animation(4413); private static final Graphics GRAPHICS = new Graphics(733, 130); - - private static final int[] IDS = new int[] { 2434, 139, 141, 143, 2430, 127, 129, 131, 3024, 3026, 3028, 3030 }; + private static final Consumables[] acceptedPotions = new Consumables[] { Consumables.RESTORE, Consumables.SUPER_RESTO, Consumables.PRAYER, Consumables.ENERGY, Consumables.SUPER_ENERGY }; public StatRestoreSpell() { super(SpellBook.LUNAR, 81, 84, null, null, null, new Item[] { new Item(Runes.ASTRAL_RUNE.getId(), 2), new Item(Runes.EARTH_RUNE.getId(), 10), new Item(Runes.WATER_RUNE.getId(), 10) }); @@ -42,34 +43,35 @@ public class StatRestoreSpell extends MagicSpell { public boolean cast(Entity entity, Node target) { final Player player = ((Player) entity); Item item = ((Item) target); - final Potion potion = (Potion) Consumables.getConsumableById(item.getId()); player.getInterfaceManager().setViewedTab(6); + + if (Consumables.getConsumableById(item.getId()) == null) { + player.getPacketDispatch().sendMessage("You can only cast this spell on a potion."); + return false; + } + + final Potion potion = (Potion) Consumables.getConsumableById(item.getId()).getConsumable(); + if (potion == null) { - player.getPacketDispatch().sendMessage("You can only cast this spell on a potion."); + player.getPacketDispatch().sendMessage("You can only cast this spell on a potion."); return false; } - if (!item.getDefinition().isTradeable() || !isRestore(item.getId())) { + if (!item.getDefinition().isTradeable() || !isRestore(potion)) { player.getPacketDispatch().sendMessage("You can't cast this spell on that item."); return false; } - List pl = RegionManager.getLocalPlayers(player, 1); + List pl = RegionManager.getLocalPlayers(player, 2); int plSize = pl.size() - 1; int doses = potion.getDose(item); - if (plSize > doses) { - player.getPacketDispatch().sendMessage("You don't have enough doses."); - return false; - } - if (doses > plSize) { - doses = plSize; - } if (pl.size() == 0) { return false; } if (!super.meetsRequirements(player, true, false)) { return false; } - int size = 1; - for (Player players : pl) { + int size = 0; + for (Player players : pl) { + if (size >= doses) break; Player o = (Player) players; if (!o.isActive() || o.getLocks().isInteractionLocked() || o == player) { continue; @@ -78,30 +80,33 @@ public class StatRestoreSpell extends MagicSpell { continue; } o.graphics(GRAPHICS); - player.getPacketDispatch().sendMessage("You can only cast this spell on a potion."); potion.getEffect().activate(o); size++; } - if (size == 1) { + if (size == 0) { player.getPacketDispatch().sendMessage("There is nobody around that has accept aid on to share the potion with you."); return false; } super.meetsRequirements(player, true, true); potion.getEffect().activate(player); - player.getAudioManager().send(new Audio (2899), true); + playGlobalAudio(player.getLocation(), Sounds.LUNAR_STAT_SHARE_2899); player.animate(ANIMATION); player.graphics(GRAPHICS); player.getInventory().remove(item); - player.getInventory().add(new Item(potion.getIds()[size - 1])); + int newIndex = (potion.getIds().length - doses) + size; + if (newIndex > potion.getIds().length - 1) { + player.getInventory().add(new Item(229)); + return true; + } + player.getInventory().add(new Item(potion.getIds()[newIndex])); return true; } - private boolean isRestore(int id) { - for (int i : IDS) { - if (i == id) { - return true; - } - } + private boolean isRestore(Potion p) { + for (int i = 0; i < acceptedPotions.length; i++) { + if (p == acceptedPotions[i].getConsumable()) + return true; + } return false; } diff --git a/Server/src/main/content/global/skill/magic/lunar/StatSpySpell.java b/Server/src/main/content/global/skill/magic/lunar/StatSpySpell.java deleted file mode 100644 index 009b0fb5a..000000000 --- a/Server/src/main/content/global/skill/magic/lunar/StatSpySpell.java +++ /dev/null @@ -1,97 +0,0 @@ -package content.global.skill.magic.lunar; - -import core.game.component.CloseEvent; -import core.game.component.Component; -import core.plugin.Initializable; -import core.game.node.entity.skill.Skills; -import core.game.node.entity.combat.spell.MagicSpell; -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.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.world.update.flag.context.Animation; -import core.game.world.update.flag.context.Graphics; -import core.plugin.Plugin; - -/** - * The stat spy spell. - * @author 'Vexia - */ -@Initializable -public final class StatSpySpell extends MagicSpell { - - /** - * Represents the animation of this spell. - */ - private final static Animation ANIMATION = new Animation(6293); - - /** - * Repesents the graphics of this spell. - */ - private static final Graphics GRAPHIC = new Graphics(734, 120); - - /** - * Represents the graphics. - */ - private static final Graphics EYE = new Graphics(1059); - - /** - * Represents the component. - */ - private static final Component COMPONENT = new Component(523); - - /** - * Represents the array of skills. - */ - private static int[][] SKILLS = { { Skills.ATTACK, 1, 2 }, { Skills.HITPOINTS, 5, 6 }, { Skills.MINING, 9, 10 }, { Skills.STRENGTH, 13, 14 }, { Skills.AGILITY, 17, 18 }, { Skills.SMITHING, 21, 22 }, { Skills.DEFENCE, 25, 26 }, { Skills.HERBLORE, 29, 30 }, { Skills.FISHING, 33, 34 }, { Skills.RANGE, 37, 38 }, { Skills.THIEVING, 41, 42 }, { Skills.COOKING, 45, 46 }, { Skills.PRAYER, 49, 50 }, { Skills.CRAFTING, 53, 54 }, { Skills.FIREMAKING, 57, 58 }, { Skills.MAGIC, 61, 62 }, { Skills.FLETCHING, 65, 66 }, { Skills.WOODCUTTING, 69, 70 }, { Skills.RUNECRAFTING, 73, 74 }, { Skills.SLAYER, 77, 78 }, { Skills.FARMING, 81, 82 }, { Skills.CONSTRUCTION, 85, 86 }, { Skills.HUNTER, 89, 90 }, { Skills.SUMMONING, 93, 94 } }; - - /** - * Constructs a new {@code CureOtherSpell} {@code Object}. - */ - public StatSpySpell() { - super(SpellBook.LUNAR, 75, 76, ANIMATION, null, null, new Item[] { new Item(Runes.COSMIC_RUNE.getId(), 2), 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(9, this); - return this; - } - - @Override - public boolean cast(Entity entity, Node target) { - final Player player = ((Player) entity); - if (!(target instanceof Player)) { - player.getPacketDispatch().sendMessage("You can only cast this spell on players."); - return false; - } - if (super.meetsRequirements(player, true, true)) { - final Player o = ((Player) target); - player.animate(ANIMATION); - player.face(o); - player.getAudioManager().send(3620); - COMPONENT.setCloseEvent(new CloseEvent() { - @Override - public boolean close(Player player, Component c) { - player.getInterfaceManager().restoreTabs(); - return true; - } - }); - player.graphics(EYE); - o.graphics(GRAPHIC); - player.getAudioManager().send(3621); - for (int[] element : SKILLS) { - player.getPacketDispatch().sendString("" + o.getSkills().getLevel(element[0]) + "", 523, element[1]); - player.getPacketDispatch().sendString("" + o.getSkills().getStaticLevel(element[0]) + "", 523, element[2]); - } - player.getPacketDispatch().sendString((o.getUsername()), 523, 99); - player.getInterfaceManager().openSingleTab(COMPONENT); - return true; - } - return true; - } - -} diff --git a/Server/src/main/content/global/skill/magic/lunar/VengeanceSpell.java b/Server/src/main/content/global/skill/magic/lunar/VengeanceSpell.java index 5883be295..e06821811 100644 --- a/Server/src/main/content/global/skill/magic/lunar/VengeanceSpell.java +++ b/Server/src/main/content/global/skill/magic/lunar/VengeanceSpell.java @@ -13,6 +13,10 @@ 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.playAudio; +import static core.api.ContentAPIKt.playGlobalAudio; /** * Handles the vengeance (other) spell. @@ -81,7 +85,7 @@ public final class VengeanceSpell extends MagicSpell { visualize(entity, p); entity.setAttribute("vengeance_delay", ticks + 50); p.setAttribute("vengeance", true); - p.getAudioManager().send(vengOther ? 2908 : 2907); + playGlobalAudio(p.getLocation(), vengOther ? Sounds.LUNAR_VENGENCE_OTHER_2908 : Sounds.LUNAR_VENGENCE_2907); return true; } diff --git a/Server/src/main/content/global/skill/magic/modern/AirSpell.java b/Server/src/main/content/global/skill/magic/modern/AirSpell.java index 4e11ce4fb..07cfcf182 100644 --- a/Server/src/main/content/global/skill/magic/modern/AirSpell.java +++ b/Server/src/main/content/global/skill/magic/modern/AirSpell.java @@ -13,6 +13,7 @@ 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.Plugin; +import org.rs09.consts.Sounds; /** * Represents the combat spell plugin used to handle air spells. @@ -117,10 +118,10 @@ public final class AirSpell extends CombatSpell { @Override public Plugin newInstance(SpellType type) throws Throwable { - SpellBook.MODERN.register(1, new AirSpell(SpellType.STRIKE, 1, 5.5, 220, STRIKE_START, STRIKE_PROJECTILE, STRIKE_END, Runes.MIND_RUNE.getItem(1), Runes.AIR_RUNE.getItem(1))); - SpellBook.MODERN.register(10, new AirSpell(SpellType.BOLT, 17, 13.5, 218, BOLT_START, BOLT_PROJECTILE, BOLT_END, Runes.CHAOS_RUNE.getItem(1), Runes.AIR_RUNE.getItem(2))); - SpellBook.MODERN.register(24, new AirSpell(SpellType.BLAST, 41, 25.5, 216, BLAST_START, BLAST_PROJECTILE, BLAST_END, Runes.DEATH_RUNE.getItem(1), Runes.AIR_RUNE.getItem(3))); - SpellBook.MODERN.register(45, new AirSpell(SpellType.WAVE, 62, 36.0, 222, WAVE_START, WAVE_PROJECTILE, WAVE_END, Runes.BLOOD_RUNE.getItem(1), Runes.AIR_RUNE.getItem(5))); + SpellBook.MODERN.register(1, new AirSpell(SpellType.STRIKE, 1, 5.5, Sounds.WINDSTRIKE_CAST_AND_FIRE_220, STRIKE_START, STRIKE_PROJECTILE, STRIKE_END, Runes.MIND_RUNE.getItem(1), Runes.AIR_RUNE.getItem(1))); + SpellBook.MODERN.register(10, new AirSpell(SpellType.BOLT, 17, 13.5, Sounds.WINDBOLT_CAST_AND_FIRE_218, BOLT_START, BOLT_PROJECTILE, BOLT_END, Runes.CHAOS_RUNE.getItem(1), Runes.AIR_RUNE.getItem(2))); + SpellBook.MODERN.register(24, new AirSpell(SpellType.BLAST, 41, 25.5, Sounds.WINDBLAST_CAST_AND_FIRE_216, BLAST_START, BLAST_PROJECTILE, BLAST_END, Runes.DEATH_RUNE.getItem(1), Runes.AIR_RUNE.getItem(3))); + SpellBook.MODERN.register(45, new AirSpell(SpellType.WAVE, 62, 36.0, Sounds.WINDWAVE_CAST_AND_FIRE_222, WAVE_START, WAVE_PROJECTILE, WAVE_END, Runes.BLOOD_RUNE.getItem(1), Runes.AIR_RUNE.getItem(5))); return this; } } diff --git a/Server/src/main/content/global/skill/magic/modern/BindSpell.java b/Server/src/main/content/global/skill/magic/modern/BindSpell.java index 249b0fb8f..83352a107 100644 --- a/Server/src/main/content/global/skill/magic/modern/BindSpell.java +++ b/Server/src/main/content/global/skill/magic/modern/BindSpell.java @@ -16,6 +16,7 @@ import core.game.world.GameWorld; import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Graphics; import core.plugin.Plugin; +import org.rs09.consts.Sounds; /** * Represents the binding combat spell. @@ -95,8 +96,8 @@ public final class BindSpell extends CombatSpell { * @param end the end. * @param runes the runes. */ - private BindSpell(SpellType type, int level, double baseExperience, int sound, Graphics start, Projectile projectile, Graphics end, Item... runes) { - super(type, SpellBook.MODERN, level, baseExperience, sound, -1, ANIMATION, start, projectile, end, runes); + private BindSpell(SpellType type, int level, double baseExperience, int sound, int impactAudio, Graphics start, Projectile projectile, Graphics end, Item... runes) { + super(type, SpellBook.MODERN, level, baseExperience, sound, impactAudio, ANIMATION, start, projectile, end, runes); } @Override @@ -137,9 +138,9 @@ public final class BindSpell extends CombatSpell { // TODO: bind/snare/entangle have separate casting and onhit components // to their sound effects, in order for splashes to sound distinct, currently // these just sound like the spells should on success, even if they splash - SpellBook.MODERN.register(12, new BindSpell(SpellType.BIND, 20, 30.0, 101, BIND_START, BIND_PROJECTILE, BIND_END, Runes.NATURE_RUNE.getItem(2), Runes.EARTH_RUNE.getItem(3), Runes.WATER_RUNE.getItem(3))); - SpellBook.MODERN.register(30, new BindSpell(SpellType.SNARE, 50, 60.0, 186, SNARE_START, SNARE_PROJECTILE, SNARE_END, Runes.NATURE_RUNE.getItem(3), Runes.EARTH_RUNE.getItem(4), Runes.WATER_RUNE.getItem(4))); - SpellBook.MODERN.register(56, new BindSpell(SpellType.ENTANGLE, 79, 89.0, 152, ENTANGLE_START, ENTANGLE_PROJECTILE, ENTANGLE_END, Runes.NATURE_RUNE.getItem(4), Runes.EARTH_RUNE.getItem(5), Runes.WATER_RUNE.getItem(5))); + SpellBook.MODERN.register(12, new BindSpell(SpellType.BIND, 20, 30.0, Sounds.BIND_CAST_101, Sounds.BIND_IMPACT_99, BIND_START, BIND_PROJECTILE, BIND_END, Runes.NATURE_RUNE.getItem(2), Runes.EARTH_RUNE.getItem(3), Runes.WATER_RUNE.getItem(3))); + SpellBook.MODERN.register(30, new BindSpell(SpellType.SNARE, 50, 60.0, Sounds.SNARE_CAST_AND_FIRE_3003, Sounds.SNARE_IMPACT_3002, SNARE_START, SNARE_PROJECTILE, SNARE_END, Runes.NATURE_RUNE.getItem(3), Runes.EARTH_RUNE.getItem(4), Runes.WATER_RUNE.getItem(4))); + SpellBook.MODERN.register(56, new BindSpell(SpellType.ENTANGLE, 79, 89.0, Sounds.ENTANGLE_CAST_AND_FIRE_151, Sounds.ENTANGLE_HIT_153, ENTANGLE_START, ENTANGLE_PROJECTILE, ENTANGLE_END, Runes.NATURE_RUNE.getItem(4), Runes.EARTH_RUNE.getItem(5), Runes.WATER_RUNE.getItem(5))); return this; } 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 115d5ebbb..000000000 --- a/Server/src/main/content/global/skill/magic/modern/ChargeOrbSpells.java +++ /dev/null @@ -1,205 +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; - -/** - * 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(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(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(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(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); - System.out.println("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/ChargeSpell.java b/Server/src/main/content/global/skill/magic/modern/ChargeSpell.java index 8ab28094e..4aa2b1c1b 100644 --- a/Server/src/main/content/global/skill/magic/modern/ChargeSpell.java +++ b/Server/src/main/content/global/skill/magic/modern/ChargeSpell.java @@ -13,6 +13,9 @@ 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.*; /** * Represents the charge spell magic spell. @@ -26,7 +29,7 @@ public final class ChargeSpell extends MagicSpell { * Constructs a new {@code ChargeSpell} {@code Object}. */ public ChargeSpell() { - super(SpellBook.MODERN, 80, 180, Animation.create(811), new Graphics(6, 96), new Audio(1651), new Item[] { Runes.FIRE_RUNE.getItem(3), Runes.BLOOD_RUNE.getItem(3), Runes.AIR_RUNE.getItem(3) }); + super(SpellBook.MODERN, 80, 180, Animation.create(811), new Graphics(6, 96), new Audio(Sounds.CHARGE_1651), new Item[] { Runes.FIRE_RUNE.getItem(3), Runes.BLOOD_RUNE.getItem(3), Runes.AIR_RUNE.getItem(3) }); } @Override @@ -47,7 +50,9 @@ public final class ChargeSpell extends MagicSpell { } p.getLocks().lock("charge_cast", 100); visualize(entity, target); - p.registerState("godcharge").init(); + // Remove the previous copy of the state in order to refresh the duration if recast before 7 minutes + removeTimer (p, "magic:spellcharge"); + registerTimer (p, spawnTimer("magic:spellcharge")); p.getPacketDispatch().sendMessage("You feel charged with magic power."); return true; } 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 be6f86bd7..0c9f896b1 100644 --- a/Server/src/main/content/global/skill/magic/modern/CrumbleUndead.java +++ b/Server/src/main/content/global/skill/magic/modern/CrumbleUndead.java @@ -15,6 +15,9 @@ 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.SnowscapeAPIKt.*; /** * Handles the crumble undead spell. @@ -28,13 +31,13 @@ public final class CrumbleUndead extends CombatSpell { * Constructs a new {@code CrumbleUndead} {@code Object}. */ public CrumbleUndead() { - super(SpellType.CRUMBLE_UNDEAD, SpellBook.MODERN, 39, 24.5, 122, 123, new Animation(724, Priority.HIGH), new Graphics(145, 96), Projectile.create((Entity) null, null, 146, 40, 36, 52, 75, 15, 11), new Graphics(147, 96), Runes.EARTH_RUNE.getItem(2), Runes.AIR_RUNE.getItem(2), Runes.CHAOS_RUNE.getItem(1)); + super(SpellType.CRUMBLE_UNDEAD, SpellBook.MODERN, 39, 24.5, Sounds.CRUMBLE_CAST_AND_FIRE_122, Sounds.CRUMBLE_HIT_124, new Animation(724, Priority.HIGH), new Graphics(145, 96), Projectile.create((Entity) null, null, 146, 40, 36, 52, 75, 15, 11), new Graphics(147, 96), Runes.EARTH_RUNE.getItem(2), Runes.AIR_RUNE.getItem(2), Runes.CHAOS_RUNE.getItem(1)); } @Override public boolean cast(Entity entity, Node target) { NPC npc = target instanceof NPC ? (NPC) target : null; - if (npc == null || npc.getTask() == null || !npc.getTask().undead) { + if (npc == null || !isUndead(npc)) { ((Player) entity).getPacketDispatch().sendMessage("This spell only affects the undead."); return false; } diff --git a/Server/src/main/content/global/skill/magic/modern/CurseSpells.java b/Server/src/main/content/global/skill/magic/modern/CurseSpells.java index b01e2fddf..6b76c0488 100644 --- a/Server/src/main/content/global/skill/magic/modern/CurseSpells.java +++ b/Server/src/main/content/global/skill/magic/modern/CurseSpells.java @@ -14,6 +14,7 @@ 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 curse spells. @@ -143,8 +144,8 @@ public final class CurseSpells extends CombatSpell { * @param end the end. * @param runes the runes. */ - private CurseSpells(SpellType type, int level, double baseExperience, int sound, Graphics start, Projectile projectile, Graphics end, Item... runes) { - super(type, SpellBook.MODERN, level, baseExperience, sound, -1, type.ordinal() <= SpellType.CURSE.ordinal() ? LOW_ANIMATION : HIGH_ANIMATION, start, projectile, end, runes); + private CurseSpells(SpellType type, int level, double baseExperience, int sound, int impactAudio, Graphics start, Projectile projectile, Graphics end, Item... runes) { + super(type, SpellBook.MODERN, level, baseExperience, sound, impactAudio, type.ordinal() <= SpellType.CURSE.ordinal() ? LOW_ANIMATION : HIGH_ANIMATION, start, projectile, end, runes); } @Override @@ -188,12 +189,12 @@ public final class CurseSpells extends CombatSpell { @Override public Plugin newInstance(SpellType type) throws Throwable { - SpellBook.MODERN.register(2, new CurseSpells(SpellType.CONFUSE, 3, 13.0, 99, CONFUSE_START, CONFUSE_PROJECTILE, CONFUSE_END, Runes.BODY_RUNE.getItem(1), Runes.EARTH_RUNE.getItem(2), Runes.WATER_RUNE.getItem(3))); - SpellBook.MODERN.register(7, new CurseSpells(SpellType.WEAKEN, 11, 21.0, 100, WEAKEN_START, WEAKEN_PROJECTILE, WEAKEN_END, Runes.BODY_RUNE.getItem(1), Runes.EARTH_RUNE.getItem(2), Runes.WATER_RUNE.getItem(3))); - SpellBook.MODERN.register(11, new CurseSpells(SpellType.CURSE, 19, 29.0, 101, CURSE_START, CURSE_PROJECTILE, CURSE_END, Runes.BODY_RUNE.getItem(1), Runes.EARTH_RUNE.getItem(3), Runes.WATER_RUNE.getItem(2))); - SpellBook.MODERN.register(50, new CurseSpells(SpellType.VULNERABILITY, 66, 76.0, 119, VULNER_START, VULNER_PROJECTILE, VULNER_END, Runes.SOUL_RUNE.getItem(1), Runes.EARTH_RUNE.getItem(5), Runes.WATER_RUNE.getItem(5))); - SpellBook.MODERN.register(53, new CurseSpells(SpellType.ENFEEBLE, 73, 83.0, 120, ENFEEBLE_START, ENFEEBLE_PROJECTILE, ENFEEBLE_END, Runes.SOUL_RUNE.getItem(1), Runes.EARTH_RUNE.getItem(8), Runes.WATER_RUNE.getItem(8))); - SpellBook.MODERN.register(57, new CurseSpells(SpellType.STUN, 80, 90.0, 121, STUN_START, STUN_PROJECTILE, STUN_END, Runes.SOUL_RUNE.getItem(1), Runes.EARTH_RUNE.getItem(12), Runes.WATER_RUNE.getItem(12))); + SpellBook.MODERN.register(2, new CurseSpells(SpellType.CONFUSE, 3, 13.0, Sounds.CONFUSE_CAST_AND_FIRE_119, Sounds.CONFUSE_HIT_121, CONFUSE_START, CONFUSE_PROJECTILE, CONFUSE_END, Runes.BODY_RUNE.getItem(1), Runes.EARTH_RUNE.getItem(2), Runes.WATER_RUNE.getItem(3))); + SpellBook.MODERN.register(7, new CurseSpells(SpellType.WEAKEN, 11, 21.0, Sounds.WEAKEN_CAST_AND_FIRE_3011, Sounds.WEAKEN_HIT_3010, WEAKEN_START, WEAKEN_PROJECTILE, WEAKEN_END, Runes.BODY_RUNE.getItem(1), Runes.EARTH_RUNE.getItem(2), Runes.WATER_RUNE.getItem(3))); + SpellBook.MODERN.register(11, new CurseSpells(SpellType.CURSE, 19, 29.0, Sounds.CURSE_CAST_AND_FIRE_127, Sounds.CURSE_HIT_126, CURSE_START, CURSE_PROJECTILE, CURSE_END, Runes.BODY_RUNE.getItem(1), Runes.EARTH_RUNE.getItem(3), Runes.WATER_RUNE.getItem(2))); + SpellBook.MODERN.register(50, new CurseSpells(SpellType.VULNERABILITY, 66, 76.0, Sounds.VULNERABILITY_CAST_AND_FIRE_3009, Sounds.VULNERABILITY_IMPACT_3008, VULNER_START, VULNER_PROJECTILE, VULNER_END, Runes.SOUL_RUNE.getItem(1), Runes.EARTH_RUNE.getItem(5), Runes.WATER_RUNE.getItem(5))); + SpellBook.MODERN.register(53, new CurseSpells(SpellType.ENFEEBLE, 73, 83.0, Sounds.ENFEEBLE_CAST_AND_FIRE_148, Sounds.ENFEEBLE_HIT_150, ENFEEBLE_START, ENFEEBLE_PROJECTILE, ENFEEBLE_END, Runes.SOUL_RUNE.getItem(1), Runes.EARTH_RUNE.getItem(8), Runes.WATER_RUNE.getItem(8))); + SpellBook.MODERN.register(57, new CurseSpells(SpellType.STUN, 80, 90.0, Sounds.STUN_CAST_AND_FIRE_3004, Sounds.STUN_IMPACT_3005, STUN_START, STUN_PROJECTILE, STUN_END, Runes.SOUL_RUNE.getItem(1), Runes.EARTH_RUNE.getItem(12), Runes.WATER_RUNE.getItem(12))); return this; } diff --git a/Server/src/main/content/global/skill/magic/modern/EarthSpell.java b/Server/src/main/content/global/skill/magic/modern/EarthSpell.java index b5858590a..3da9d5083 100644 --- a/Server/src/main/content/global/skill/magic/modern/EarthSpell.java +++ b/Server/src/main/content/global/skill/magic/modern/EarthSpell.java @@ -13,6 +13,7 @@ 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 earth combat spells. @@ -117,10 +118,10 @@ public final class EarthSpell extends CombatSpell { @Override public Plugin newInstance(SpellType type) throws Throwable { - SpellBook.MODERN.register(6, new EarthSpell(SpellType.STRIKE, 9, 9.5, 132, STRIKE_START, STRIKE_PROJECTILE, STRIKE_END, Runes.MIND_RUNE.getItem(1), Runes.EARTH_RUNE.getItem(2), Runes.AIR_RUNE.getItem(1))); - SpellBook.MODERN.register(17, new EarthSpell(SpellType.BOLT, 29, 19.5, 130, BOLT_START, BOLT_PROJECTILE, BOLT_END, Runes.CHAOS_RUNE.getItem(1), Runes.EARTH_RUNE.getItem(3), Runes.AIR_RUNE.getItem(2))); - SpellBook.MODERN.register(33, new EarthSpell(SpellType.BLAST, 53, 31.5, 128, BLAST_START, BLAST_PROJECTILE, BLAST_END, Runes.DEATH_RUNE.getItem(1), Runes.EARTH_RUNE.getItem(4), Runes.AIR_RUNE.getItem(3))); - SpellBook.MODERN.register(52, new EarthSpell(SpellType.WAVE, 70, 40.0, 134, WAVE_START, WAVE_PROJECTILE, WAVE_END, Runes.BLOOD_RUNE.getItem(1), Runes.EARTH_RUNE.getItem(7), Runes.AIR_RUNE.getItem(5))); + SpellBook.MODERN.register(6, new EarthSpell(SpellType.STRIKE, 9, 9.5, Sounds.EARTHSTRIKE_CAST_AND_FIRE_132, STRIKE_START, STRIKE_PROJECTILE, STRIKE_END, Runes.MIND_RUNE.getItem(1), Runes.EARTH_RUNE.getItem(2), Runes.AIR_RUNE.getItem(1))); + SpellBook.MODERN.register(17, new EarthSpell(SpellType.BOLT, 29, 19.5, Sounds.EARTHBOLT_CAST_AND_FIRE_130, BOLT_START, BOLT_PROJECTILE, BOLT_END, Runes.CHAOS_RUNE.getItem(1), Runes.EARTH_RUNE.getItem(3), Runes.AIR_RUNE.getItem(2))); + SpellBook.MODERN.register(33, new EarthSpell(SpellType.BLAST, 53, 31.5, Sounds.EARTHBLAST_CAST_AND_FIRE_128, BLAST_START, BLAST_PROJECTILE, BLAST_END, Runes.DEATH_RUNE.getItem(1), Runes.EARTH_RUNE.getItem(4), Runes.AIR_RUNE.getItem(3))); + SpellBook.MODERN.register(52, new EarthSpell(SpellType.WAVE, 70, 40.0, Sounds.EARTHWAVE_CAST_AND_FIRE_134, WAVE_START, WAVE_PROJECTILE, WAVE_END, Runes.BLOOD_RUNE.getItem(1), Runes.EARTH_RUNE.getItem(7), Runes.AIR_RUNE.getItem(5))); return this; } diff --git a/Server/src/main/content/global/skill/magic/modern/FireSpell.java b/Server/src/main/content/global/skill/magic/modern/FireSpell.java index 85f5423f3..7feb995bd 100644 --- a/Server/src/main/content/global/skill/magic/modern/FireSpell.java +++ b/Server/src/main/content/global/skill/magic/modern/FireSpell.java @@ -13,6 +13,7 @@ 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; /** * Handles the fire spells. @@ -118,10 +119,10 @@ public final class FireSpell extends CombatSpell { @Override public Plugin newInstance(SpellType type) throws Throwable { - SpellBook.MODERN.register(8, new FireSpell(SpellType.STRIKE, 13, 11.5, 160, STRIKE_START, STRIKE_PROJECTILE, STRIKE_END, Runes.MIND_RUNE.getItem(1), Runes.FIRE_RUNE.getItem(3), Runes.AIR_RUNE.getItem(2))); - SpellBook.MODERN.register(20, new FireSpell(SpellType.BOLT, 35, 22.5, 157, BOLT_START, BOLT_PROJECTILE, BOLT_END, Runes.CHAOS_RUNE.getItem(1), Runes.FIRE_RUNE.getItem(4), Runes.AIR_RUNE.getItem(3))); - SpellBook.MODERN.register(38, new FireSpell(SpellType.BLAST, 59, 34.5, 155, BLAST_START, BLAST_PROJECTILE, BLAST_END, Runes.DEATH_RUNE.getItem(1), Runes.FIRE_RUNE.getItem(5), Runes.AIR_RUNE.getItem(4))); - SpellBook.MODERN.register(55, new FireSpell(SpellType.WAVE, 75, 42.5, 162, WAVE_START, WAVE_PROJECTILE, WAVE_END, Runes.BLOOD_RUNE.getItem(1), Runes.FIRE_RUNE.getItem(7), Runes.AIR_RUNE.getItem(5))); + SpellBook.MODERN.register(8, new FireSpell(SpellType.STRIKE, 13, 11.5, Sounds.FIRESTRIKE_CAST_AND_FIRE_160, STRIKE_START, STRIKE_PROJECTILE, STRIKE_END, Runes.MIND_RUNE.getItem(1), Runes.FIRE_RUNE.getItem(3), Runes.AIR_RUNE.getItem(2))); + SpellBook.MODERN.register(20, new FireSpell(SpellType.BOLT, 35, 22.5, Sounds.FIREBOLT_CAST_AND_FIRE_157, BOLT_START, BOLT_PROJECTILE, BOLT_END, Runes.CHAOS_RUNE.getItem(1), Runes.FIRE_RUNE.getItem(4), Runes.AIR_RUNE.getItem(3))); + SpellBook.MODERN.register(38, new FireSpell(SpellType.BLAST, 59, 34.5, Sounds.FIREBLAST_CAST_AND_FIRE_155, BLAST_START, BLAST_PROJECTILE, BLAST_END, Runes.DEATH_RUNE.getItem(1), Runes.FIRE_RUNE.getItem(5), Runes.AIR_RUNE.getItem(4))); + SpellBook.MODERN.register(55, new FireSpell(SpellType.WAVE, 75, 42.5, Sounds.FIREWAVE_CAST_AND_FIRE_162, WAVE_START, WAVE_PROJECTILE, WAVE_END, Runes.BLOOD_RUNE.getItem(1), Runes.FIRE_RUNE.getItem(7), Runes.AIR_RUNE.getItem(5))); return this; } diff --git a/Server/src/main/content/global/skill/magic/modern/GodSpells.java b/Server/src/main/content/global/skill/magic/modern/GodSpells.java index 045451c14..8b492fdd8 100644 --- a/Server/src/main/content/global/skill/magic/modern/GodSpells.java +++ b/Server/src/main/content/global/skill/magic/modern/GodSpells.java @@ -2,23 +2,27 @@ package content.global.skill.magic.modern; import core.cache.def.impl.ItemDefinition; import core.game.container.impl.EquipmentContainer; -import core.plugin.Initializable; -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.Runes; 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.impl.Projectile; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.SpellBookManager.SpellBook; +import core.game.node.entity.skill.Skills; 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.Items; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.*; /** * Handles the god spells. @@ -32,6 +36,7 @@ public final class GodSpells extends CombatSpell { * The spell names. */ private static final String[] NAMES = new String[] { "Saradomin strike", "Guthix claws", "Flames of Zamorak" }; + private static final int[] GOD_STAVES = new int[] { Items.SARADOMIN_STAFF_2415, Items.GUTHIX_STAFF_2416, Items.ZAMORAK_STAFF_2417 }; /** * The start graphic for Air strike. @@ -101,10 +106,26 @@ public final class GodSpells extends CombatSpell { * @param end the end. * @param runes the runes. */ - private GodSpells(SpellType type, int sound, Graphics start, Projectile projectile, Graphics end, Item... runes) { - super(type, SpellBook.MODERN, 60, 35.0, -1, -1, ANIMATION, start, projectile, end, runes); + private GodSpells(SpellType type, int sound, int impactAudio, Graphics start, Projectile projectile, Graphics end, Item... runes) { + super(type, SpellBook.MODERN, 60, 35.0, sound, impactAudio, ANIMATION, start, projectile, end, runes); } + private int getSpellIndex() { + int index = -1; + switch (runes[1].getAmount()) { + case 2: // Saradomin strike + index = 0; + break; + case 1: // Guthix claws + index = 1; + break; + case 4: // Flames of Zamorak + index = 2; + break; + } + return index; + } + @Override public boolean meetsRequirements(Entity caster, boolean message, boolean remove) { if (caster instanceof NPC) { @@ -112,21 +133,11 @@ public final class GodSpells extends CombatSpell { } if (caster instanceof Player) { int staffId = ((Player) caster).getEquipment().getNew(EquipmentContainer.SLOT_WEAPON).getId(); - int required = -1; - int index = 0; - switch (runes[1].getAmount()) { - case 2: // Saradomin strike - required = 2415; - break; - case 1: // Guthix claws - required = 2416; - index = 1; - break; - case 4: // Flames of Zamorak - required = 2417; - index = 2; - break; - } + int index = getSpellIndex(); + if(index < 0) { + return false; + } + int required = GOD_STAVES[index]; Player p = (Player) caster; if (p.getSavedData().getActivityData().getGodCasts()[index] < 100 && !p.getZoneMonitor().isInZone("mage arena")) { p.sendMessage("You need to cast " + NAMES[index] + " " + (100 - p.getSavedData().getActivityData().getGodCasts()[index]) + " more times inside the Mage Arena."); @@ -143,6 +154,21 @@ public final class GodSpells extends CombatSpell { return super.meetsRequirements(caster, message, remove); } + @Override + public void fireEffect(Entity entity, Entity victim, BattleState state) { + switch(getSpellIndex()) { + case 0: + victim.getSkills().decrementPrayerPoints(1); + break; + case 1: + victim.getSkills().drainLevel(Skills.DEFENCE, 0.05, 0.05); + break; + case 2: + victim.getSkills().drainLevel(Skills.MAGIC, 0.05, 0.05); + break; + } + } + @Override public void visualize(Entity entity, Node target) { super.visualize(entity, target); @@ -153,19 +179,10 @@ public final class GodSpells extends CombatSpell { } } } - @Override public void visualizeImpact(Entity entity, Entity target, BattleState state) { if (entity instanceof Player) { - int index = 0; // Saradomin strike - switch (runes[1].getAmount()) { - case 1: // Guthix claws - index = 1; - break; - case 4: // Flames of Zamorak - index = 2; - break; - } + int index = getSpellIndex(); Player p = (Player) entity; if (p.getSavedData().getActivityData().getGodCasts()[index] < 100) { p.getSavedData().getActivityData().getGodCasts()[index]++; @@ -173,8 +190,22 @@ public final class GodSpells extends CombatSpell { p.sendMessage("You can now cast " + NAMES[index] + " outside the Arena."); } } + if (state.getEstimatedHit() == -1) { + target.graphics(SPLASH_GRAPHIC); + if (projectile == SARA_PROJECTILE) { + playGlobalAudio(target.getLocation(), Sounds.SARADOMIN_STRIKE_FAIL_1656, 20); + } + if (projectile == GUTHIX_PROJECTILE) { + playGlobalAudio(target.getLocation(), Sounds.CLAWS_OF_GUTHIX_FAIL_1652, 20); + } + if (projectile == ZAM_PROJECTILE) { + playGlobalAudio(target.getLocation(), Sounds.FLAMES_OF_ZAMORAK_FAIL_1654, 20); + } + return; + } } - super.visualizeImpact(entity, target, state); + target.graphics(endGraphic); + playGlobalAudio(target.getLocation(), impactAudio); } @Override @@ -184,9 +215,9 @@ public final class GodSpells extends CombatSpell { @Override public Plugin newInstance(SpellType type) throws Throwable { - SpellBook.MODERN.register(41, new GodSpells(SpellType.GOD_STRIKE, -1, SARA_START, SARA_PROJECTILE, SARA_END, Runes.BLOOD_RUNE.getItem(2), Runes.FIRE_RUNE.getItem(2), Runes.AIR_RUNE.getItem(4))); - SpellBook.MODERN.register(42, new GodSpells(SpellType.GOD_STRIKE, -1, GUTHIX_START, GUTHIX_PROJECTILE, GUTHIX_END, Runes.BLOOD_RUNE.getItem(2), Runes.FIRE_RUNE.getItem(1), Runes.AIR_RUNE.getItem(4))); - SpellBook.MODERN.register(43, new GodSpells(SpellType.GOD_STRIKE, -1, ZAM_START, ZAM_PROJECTILE, ZAM_END, Runes.BLOOD_RUNE.getItem(2), Runes.FIRE_RUNE.getItem(4), Runes.AIR_RUNE.getItem(1))); + SpellBook.MODERN.register(41, new GodSpells(SpellType.GOD_STRIKE, -1, Sounds.SARADOMIN_STRIKE_1659, SARA_START, SARA_PROJECTILE, SARA_END, Runes.BLOOD_RUNE.getItem(2), Runes.FIRE_RUNE.getItem(2), Runes.AIR_RUNE.getItem(4))); + SpellBook.MODERN.register(42, new GodSpells(SpellType.GOD_STRIKE, -1, Sounds.CLAWS_OF_GUTHIX_1653, GUTHIX_START, GUTHIX_PROJECTILE, GUTHIX_END, Runes.BLOOD_RUNE.getItem(2), Runes.FIRE_RUNE.getItem(1), Runes.AIR_RUNE.getItem(4))); + SpellBook.MODERN.register(43, new GodSpells(SpellType.GOD_STRIKE, -1, Sounds.FLAMES_OF_ZAMORAK_1655, ZAM_START, ZAM_PROJECTILE, ZAM_END, Runes.BLOOD_RUNE.getItem(2), Runes.FIRE_RUNE.getItem(4), Runes.AIR_RUNE.getItem(1))); return this; } diff --git a/Server/src/main/content/global/skill/magic/modern/GodspellChargedState.kt b/Server/src/main/content/global/skill/magic/modern/GodspellChargedState.kt deleted file mode 100644 index 3e7ef71fc..000000000 --- a/Server/src/main/content/global/skill/magic/modern/GodspellChargedState.kt +++ /dev/null @@ -1,46 +0,0 @@ -package content.global.skill.magic.modern - -import core.game.node.entity.player.Player -import core.game.node.entity.player.link.audio.Audio -import core.game.system.task.Pulse -import org.json.simple.JSONObject -import core.game.node.entity.state.PlayerState -import core.game.node.entity.state.State -import core.game.world.GameWorld; - -@PlayerState("godcharge") -class GodspellChargedState(player: Player? = null) : State(player) { - val DURATION = 700 - var startTick: Int = 0 - - override fun save(root: JSONObject) { - root.put("ticks_elapsed", GameWorld.ticks - startTick) - } - - override fun parse(_data: JSONObject) { - if(_data.containsKey("ticks_elapsed")){ - startTick = GameWorld.ticks - _data["ticks_elapsed"].toString().toInt() - } - } - - override fun newInstance(player: Player?): State { - var ret = GodspellChargedState(player) - ret.startTick = GameWorld.ticks - return ret - } - - override fun createPulse() { - player ?: return - if(GameWorld.ticks - startTick >= DURATION) return - pulse = object : Pulse(DURATION) { - override fun pulse(): Boolean { - player.sendMessage("Your magical charge fades away.") - player.clearState("godcharge") - player.audioManager.send(Audio(1650)) - pulse = null - return true - } - } - } - -} diff --git a/Server/src/main/content/global/skill/magic/modern/HouseTeleportPlugin.java b/Server/src/main/content/global/skill/magic/modern/HouseTeleportPlugin.java deleted file mode 100644 index 37bce8a90..000000000 --- a/Server/src/main/content/global/skill/magic/modern/HouseTeleportPlugin.java +++ /dev/null @@ -1,54 +0,0 @@ -package content.global.skill.magic.modern; - -import core.game.node.Node; -import core.game.node.entity.Entity; -import core.game.node.entity.combat.spell.SpellType; -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.node.entity.player.link.TeleportManager.TeleportType; -import core.game.node.item.Item; -import core.game.world.GameWorld; -import core.game.world.map.Location; -import core.plugin.Plugin; -import core.tools.RandomFunction; - -/** - * - * For house teleportation. - * - * @author Jamix77 - * - */ -public class HouseTeleportPlugin extends MagicSpell { - - public HouseTeleportPlugin(final int level, final double experience, final Item... items) { - super(SpellBook.MODERN, level, experience, null, null, null, items); - } - - @Override - public Plugin newInstance(SpellType arg) throws Throwable { - - return this; - } - - @Override - public boolean cast(Entity entity, Node target) { - if (((Player)entity).getHouseManager().getLocation().getExitLocation() == null) { - ((Player)entity).sendMessage("You must have a house to teleport to before attempting that."); - return false; - } - Location location = ((Player)entity).getHouseManager().getLocation().getExitLocation(); - if (entity.getLocks().isTeleportLocked() || !super.meetsRequirements(entity, true, false)) { - return false; - } - if (entity.getTeleporter().send(location.transform(0, RandomFunction.random(3), 0), TeleportType.NORMAL)) { - entity.setAttribute("teleport:items", super.runes); - entity.setAttribute("magic-delay", GameWorld.getTicks() + 5); - ((Player) entity).getInventory().remove(super.runes); - return true; - } - return false; - } - -} diff --git a/Server/src/main/content/global/skill/magic/modern/IbanBlast.java b/Server/src/main/content/global/skill/magic/modern/IbanBlast.java index 12be74219..5a35d7e1d 100644 --- a/Server/src/main/content/global/skill/magic/modern/IbanBlast.java +++ b/Server/src/main/content/global/skill/magic/modern/IbanBlast.java @@ -15,6 +15,7 @@ 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 iban blast spell. @@ -28,7 +29,7 @@ public final class IbanBlast extends CombatSpell { * Constructs a new {@code IbanBlast} {@code Object}. */ public IbanBlast() { - super(SpellType.IBANS_BLAST, SpellBook.MODERN, 50, 60.5, -1, -1, new Animation(708, Priority.HIGH), new Graphics(87, 96), Projectile.create((Entity) null, null, 88, 40, 36, 52, 75, 15, 11), new Graphics(89, 96), Runes.FIRE_RUNE.getItem(5), Runes.DEATH_RUNE.getItem(1)); + super(SpellType.IBANS_BLAST, SpellBook.MODERN, 50, 60.5, Sounds.FIREWAVE_CAST_AND_FIRE_162, Sounds.FIREWAVE_HIT_163, new Animation(708, Priority.HIGH), new Graphics(87, 96), Projectile.create((Entity) null, null, 88, 40, 36, 52, 75, 15, 11), new Graphics(89, 96), Runes.FIRE_RUNE.getItem(5), Runes.DEATH_RUNE.getItem(1)); } @Override 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..4740bd02d --- /dev/null +++ b/Server/src/main/content/global/skill/magic/modern/ModernData.kt @@ -0,0 +1,72 @@ +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 + ), + CHARGE_TELE_ORB( + 7171, + 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, + 14534 + ); + 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 3a09289f2..8c0eb276f 100644 --- a/Server/src/main/content/global/skill/magic/modern/ModernListeners.kt +++ b/Server/src/main/content/global/skill/magic/modern/ModernListeners.kt @@ -1,66 +1,42 @@ 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.spellconsts.Modern -import core.game.event.ItemAlchemizationEvent -import core.game.event.TeleportEvent -import core.api.getAttribute -import core.api.sendMessage import content.global.skill.prayer.Bones +import content.global.skill.smithing.smelting.Bar +import content.global.skill.smithing.smelting.SmeltingPulse +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 content.region.kandarin.ardougne.quest.plaguecity.PlagueCityListeners 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.EquipmentSlot -import core.api.getItemFromEquipment -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 + +import core.game.system.task.Pulse +import core.game.world.repository.Repository 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 @@ -89,26 +65,39 @@ class ModernListeners : SpellListener("modern"){ onCast(Modern.CAMELOT_TELEPORT, NONE){ player, _-> requires(player,45, arrayOf(Item(Items.AIR_RUNE_556,5),Item(Items.LAW_RUNE_563))) - player.achievementDiaryManager.finishTask(player, DiaryType.SEERS_VILLAGE, 1, 5); + player.achievementDiaryManager.finishTask(player, DiaryType.SEERS_VILLAGE, 1, 5) sendTeleport(player,55.5, Location.create(2758, 3478, 0)) } onCast(Modern.ARDOUGNE_TELEPORT, NONE){ player, _ -> - 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, 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, 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, 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)) } @@ -145,6 +134,12 @@ 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) + onCast(Modern.CHARGE_AIR_ORB, OBJECT, 7171, 3, method = ::chargeOrb) } private fun boneConvert(player: Player,bananas: Boolean){ @@ -181,14 +176,14 @@ class ModernListeners : SpellListener("modern"){ } } visualizeSpell(player,BONE_CONVERT_ANIM, BONE_CONVERT_GFX) - player.audioManager.send(Audio(114)) + playAudio(player, Sounds.BONES_TO_BANANAS_ALL_114) removeRunes(player) addXP(player,if(bananas) 25.0 else 65.0) setDelay(player,false) } 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 } @@ -199,71 +194,123 @@ 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) - player.audioManager.send(117) + playAudio(player, Sounds.SUPERHEAT_ALL_190) showMagicTab(player) player.pulseManager.run(SmeltingPulse(player, item, bar, 1, true)) setDelay(player,false) } - - private fun alchemize(player: Player,item: Item,high:Boolean){ - if(item.name == "Coins") player.sendMessage("You can't alchemize something that's already gold!").also { return } - if(!item.definition.isTradeable) player.sendMessage("You can't cast this spell on something like that.").also { return } + + // Snowscape: Alchemy spells have been modified. Spells now repeat, and low alchemy converts up to 100 items at a time for bulk pricing. + 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 } if(player.zoneMonitor.isInZone("Alchemists' Playground")){ player.sendMessage("You can only alch items from the cupboards!") - return + return false } 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 + return false } + var loop = true + + val ringOfWealth = intArrayOf(14646,14644,14642,14640,14638) + if(high && ringOfWealth.contains(item.id)){ + if (getAttribute(player, "ringofwealth:unlocked", false)) { + player.sendMessage("You have already unlocked the permanent Wealth effect and can no longer ") + player.sendMessage("high alchemize this ring.") + return false + } else { + player.setAttribute("/save:ringofwealth:unlocked", true) + player.sendMessage("Combining the Alchemy spell with the Wealth enchantment causes a strange ") + player.sendMessage("interaction. You are now permanently enchanted with the Wealth effect and ") + player.sendMessage("no longer need to wear the ring to get the benefit of increased rare drops.") + loop = false + } + } + if (player.pulseManager.current !is MovementPulse) { 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)) - } else { - player.animate(Animation(if (high) 713 else 712)) - player.graphics(Graphics(if (high) 113 else 112)) - } + player.setAttribute("alchemyspelltime", getWorldTicks() + 100) - if (coins.amount > 0) - player.inventory.add(coins) - - player.dispatch(ItemAlchemizationEvent(item.id, high)) - player.inventory.remove(Item(item.id, 1)) - removeRunes(player) - addXP(player, if (high) 65.0 else 31.0) - showMagicTab(player) - setDelay(player, 5) + player.pulseManager.run(object : Pulse(){ + var counter = 0 + override fun pulse(): Boolean { + if (amountInInventory(player, item.id) == 0 || getAttribute(player, "alchemyspelltime", 0) <= getWorldTicks()) + return true + + if (counter % (if (high) 5 else 2) == 0) { + if (explorersRing) { + visualize(player, LOW_ALCH_ANIM, EXPLORERS_RING_GFX) + } else { + 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) + player.dispatch(ItemAlchemizationEvent(item.id, high)) + if (!explorersRing) { + removeAttribute(player, "spell:runes") + if (high) + requires(player,55, arrayOf(Item(Items.FIRE_RUNE_554,5),Item(Items.NATURE_RUNE_561,1))) + else + requires(player,21, arrayOf(Item(Items.FIRE_RUNE_554,3),Item(Items.NATURE_RUNE_561))) + removeRunes(player, false) + addXP(player, if (high) 65.0 else 31.0) + } + val amount = if (high) 1 else kotlin.math.min(10, amountInInventory(player, item.id)) + if (player.inventory.remove(Item(item.id, amount)) && coins.amount > 0) { + player.inventory.add(Item(coins.id, coins.amount * amount)) + } + //showMagicTab(player) + //setDelay(player, 2) + } + if (loop && !explorersRing) { + counter++ + return false + } else { + return true + } + } + }) + return true } private fun sendTeleport(player: Player, xp: Double, location: Location){ - if(player.locks.isTeleportLocked){ + if(player.isTeleBlocked){ player.removeAttribute("spell:runes") player.sendMessage("A magical force prevents you from teleporting.") return @@ -281,22 +328,108 @@ class ModernListeners : SpellListener("modern"){ } private fun attemptHouseTeleport(player: Player){ - if(player.locks.isTeleportLocked){ + if(player.isTeleBlocked){ player.removeAttribute("spell:runes") player.sendMessage("A magical force prevents you from teleporting.") return } - val loc = player.houseManager.location.exitLocation - if(loc == null){ - player.sendMessage("You do not have a house whose portal you can teleport to.") - return + + //Snowscape: teleport to the last house you entered, if possible + var houseOwner: Player + val lastHouseOwner = Repository.getPlayerByName(player.getAttribute("lasthouseentered", null)) + if (lastHouseOwner != null && !lastHouseOwner.getHouseManager().isLocked()) { + houseOwner = lastHouseOwner + } else { + val hasHouse = player.houseManager.location.exitLocation != null + if(!hasHouse){ + player.sendMessage("You do not have a house you can teleport to.") + return + } + houseOwner = player } + houseOwner.houseManager.preEnter(player, false) val teleType = TeleportManager.TeleportType.NORMAL - + val loc = houseOwner.houseManager.getEnterLocation() player.teleporter.send(loc, teleType) + houseOwner.houseManager.postEnter(player, false) //this actually runs when the teleport is SUBMITTED rather than EXECUTED, but this is fine removeRunes(player) 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.CRAFTING, 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) + addItem(player, spell.chargedOrb) + 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/modern/SpellCharge.kt b/Server/src/main/content/global/skill/magic/modern/SpellCharge.kt new file mode 100644 index 000000000..fba47af0d --- /dev/null +++ b/Server/src/main/content/global/skill/magic/modern/SpellCharge.kt @@ -0,0 +1,16 @@ +package content.global.skill.magic.modern + +import core.api.* +import core.game.node.entity.Entity +import core.game.node.entity.player.Player +import org.rs09.consts.Sounds +import core.game.system.timer.PersistTimer + +class SpellCharge : PersistTimer (700, "magic:spellcharge") { + override fun run (entity: Entity) : Boolean { + if (entity !is Player) return false + sendMessage(entity, "Your magical charge fades away.") + playAudio(entity, Sounds.CHARGE_GONE_1650) + return false + } +} diff --git a/Server/src/main/content/global/skill/magic/modern/TeleblockSpell.java b/Server/src/main/content/global/skill/magic/modern/TeleblockSpell.java index 3f8c0f288..baac8b57d 100644 --- a/Server/src/main/content/global/skill/magic/modern/TeleblockSpell.java +++ b/Server/src/main/content/global/skill/magic/modern/TeleblockSpell.java @@ -11,13 +11,14 @@ import core.game.node.entity.impl.Projectile; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.SpellBookManager.SpellBook; import core.game.node.entity.player.link.prayer.PrayerType; -import core.game.node.entity.state.EntityState; 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 static core.api.ContentAPIKt.*; + /** * Handles the teleportation block spell in the modern spellbook. * @author Splinter @@ -77,7 +78,7 @@ public final class TeleblockSpell extends CombatSpell { projectile.transform(entity, (Entity) target, false, 58, 10).send(); } entity.animate(animation); - sendAudio(entity, audio); + playGlobalAudio(entity.getLocation(), audio.id, 20); } @Override @@ -90,7 +91,7 @@ public final class TeleblockSpell extends CombatSpell { entity.asPlayer().sendMessage("You and your opponent must both be in the wilderness for you to use this spell."); return false; } - if (((Player) target).isTeleBlocked()) { + if (hasTimerActive(target.asPlayer(), "teleblock")) { entity.asPlayer().sendMessage("That player is already affected by this spell."); return false; } @@ -112,13 +113,13 @@ public final class TeleblockSpell extends CombatSpell { @Override public void fireEffect(Entity entity, Entity victim, BattleState state) { - if(!victim.isTeleBlocked() && victim instanceof Player && state.getStyle().getSwingHandler().isAccurateImpact(entity, victim)){ + if(!hasTimerActive(victim, "teleblock") && victim instanceof Player && state.getStyle().getSwingHandler().isAccurateImpact(entity, victim)){ int ticks = 500; if(((Player) victim).getPrayer().get(PrayerType.PROTECT_FROM_MAGIC)){ ticks /= 2; } - victim.getStateManager().set(EntityState.TELEBLOCK, ticks); - } else if(victim.isTeleBlocked()){ + registerTimer(victim, spawnTimer("teleblock", ticks)); + } else if(hasTimerActive(victim, "teleblock")){ entity.asPlayer().sendMessage("Your target is already blocked from teleporting."); } } diff --git a/Server/src/main/content/global/skill/magic/modern/TeleotherSpells.java b/Server/src/main/content/global/skill/magic/modern/TeleotherSpells.java index f57b76bad..9219295f4 100644 --- a/Server/src/main/content/global/skill/magic/modern/TeleotherSpells.java +++ b/Server/src/main/content/global/skill/magic/modern/TeleotherSpells.java @@ -15,6 +15,7 @@ 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; /** * Handles the teleport other spells. @@ -50,7 +51,7 @@ public final class TeleotherSpells extends MagicSpell { * @param runes The runes required. */ public TeleotherSpells(int level, double experience, String destination, Location location, Item... runes) { - super(SpellBook.MODERN, level, experience, Animation.create(1818), Graphics.create(343), new Audio(199, 0, 0), runes); + super(SpellBook.MODERN, level, experience, Animation.create(1818), Graphics.create(343), new Audio(Sounds.TELE_OTHER_CAST_199, 1, 0), runes); this.destination = destination; this.location = location; } @@ -74,7 +75,7 @@ public final class TeleotherSpells extends MagicSpell { return false; } Player o = (Player) target; - if (!o.isActive() || o.getLocks().isTeleportLocked() || o.getInterfaceManager().isOpened()) { + if (!o.isActive() || o.isTeleBlocked() || o.getInterfaceManager().isOpened()) { p.getPacketDispatch().sendMessage("The other player is currently busy."); return false; } diff --git a/Server/src/main/content/global/skill/magic/modern/WaterSpell.java b/Server/src/main/content/global/skill/magic/modern/WaterSpell.java index 7a1e2ff65..8096c4038 100644 --- a/Server/src/main/content/global/skill/magic/modern/WaterSpell.java +++ b/Server/src/main/content/global/skill/magic/modern/WaterSpell.java @@ -13,6 +13,7 @@ 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.Plugin; +import org.rs09.consts.Sounds; /** * Represents the water spells. @@ -117,10 +118,10 @@ public final class WaterSpell extends CombatSpell { @Override public Plugin newInstance(SpellType type) throws Throwable { - SpellBook.MODERN.register(4, new WaterSpell(SpellType.STRIKE, 5, 7.5, 211, STRIKE_START, STRIKE_PROJECTILE, STRIKE_END, Runes.MIND_RUNE.getItem(1), Runes.WATER_RUNE.getItem(1), Runes.AIR_RUNE.getItem(1))); - SpellBook.MODERN.register(14, new WaterSpell(SpellType.BOLT, 23, 16.5, 209, BOLT_START, BOLT_PROJECTILE, BOLT_END, Runes.CHAOS_RUNE.getItem(1), Runes.WATER_RUNE.getItem(2), Runes.AIR_RUNE.getItem(2))); - SpellBook.MODERN.register(27, new WaterSpell(SpellType.BLAST, 47, 28.5, 207, BLAST_START, BLAST_PROJECTILE, BLAST_END, Runes.DEATH_RUNE.getItem(1), Runes.WATER_RUNE.getItem(3), Runes.AIR_RUNE.getItem(3))); - SpellBook.MODERN.register(48, new WaterSpell(SpellType.WAVE, 65, 37.5, 213, WAVE_START, WAVE_PROJECTILE, WAVE_END, Runes.BLOOD_RUNE.getItem(1), Runes.WATER_RUNE.getItem(7), Runes.AIR_RUNE.getItem(5))); + SpellBook.MODERN.register(4, new WaterSpell(SpellType.STRIKE, 5, 7.5, Sounds.WATERSTRIKE_CAST_AND_FIRE_211, STRIKE_START, STRIKE_PROJECTILE, STRIKE_END, Runes.MIND_RUNE.getItem(1), Runes.WATER_RUNE.getItem(1), Runes.AIR_RUNE.getItem(1))); + SpellBook.MODERN.register(14, new WaterSpell(SpellType.BOLT, 23, 16.5, Sounds.WATERBOLT_CAST_AND_FIRE_209, BOLT_START, BOLT_PROJECTILE, BOLT_END, Runes.CHAOS_RUNE.getItem(1), Runes.WATER_RUNE.getItem(2), Runes.AIR_RUNE.getItem(2))); + SpellBook.MODERN.register(27, new WaterSpell(SpellType.BLAST, 47, 28.5, Sounds.WATERBLAST_CAST_AND_FIRE_207, BLAST_START, BLAST_PROJECTILE, BLAST_END, Runes.DEATH_RUNE.getItem(1), Runes.WATER_RUNE.getItem(3), Runes.AIR_RUNE.getItem(3))); + SpellBook.MODERN.register(48, new WaterSpell(SpellType.WAVE, 65, 37.5, Sounds.WATERWAVE_CAST_AND_FIRE_213, WAVE_START, WAVE_PROJECTILE, WAVE_END, Runes.BLOOD_RUNE.getItem(1), Runes.WATER_RUNE.getItem(7), Runes.AIR_RUNE.getItem(5))); return this; } diff --git a/Server/src/main/content/global/skill/magic/spellconsts/Lunar.kt b/Server/src/main/content/global/skill/magic/spellconsts/Lunar.kt index acd6e54b9..69e0fa5e0 100644 --- a/Server/src/main/content/global/skill/magic/spellconsts/Lunar.kt +++ b/Server/src/main/content/global/skill/magic/spellconsts/Lunar.kt @@ -34,11 +34,11 @@ object Lunar { const val HEAL_GROUP = 30 const val OURANIA_TELEPORT = 31 const val CURE_PLANT = 32 - const val MOONCLAN_GR_TELEPORT = 33 - const val WATERBIRTH_GR_TELEPORT = 34 - const val BARBARIAN_GR_TELEPORT = 35 - const val KHAZARD_GR_TELEPORT = 36 - const val FISHING_GUILD_GR_TELEPORT = 37 - const val CATHERBY_GR_TELEPORT = 38 - const val ICE_PLATEAU_GR_TELEPORT = 39 + const val MOONCLAN_GROUP_TELEPORT = 33 + const val WATERBIRTH_GROUP_TELEPORT = 34 + const val BARBARIAN_GROUP_TELEPORT = 35 + const val KHAZARD_GROUP_TELEPORT = 36 + const val FISHING_GUILD_GROUP_TELEPORT = 37 + const val CATHERBY_GROUP_TELEPORT = 38 + const val ICE_PLATEAU_GROUP_TELEPORT = 39 } \ 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 8ca73ab62..fa3d3f036 100644 --- a/Server/src/main/content/global/skill/magic/spellconsts/Modern.kt +++ b/Server/src/main/content/global/skill/magic/spellconsts/Modern.kt @@ -5,6 +5,7 @@ object Modern { const val CONFUSE = 2 const val BONES_TO_BANANAS = 9 const val WEAKEN = 11 + const val BIND = 12 const val LOW_ALCHEMY = 13 const val VARROCK_TELEPORT = 15 const val LUMBRIDGE_TELEPORT = 18 @@ -13,13 +14,19 @@ object Modern { const val SUPERHEAT = 25 const val CAMELOT_TELEPORT = 26 const val TELEPORT_TO_HOUSE = 23 + 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 const val STUN = 57 -} \ No newline at end of file +} diff --git a/Server/src/main/content/global/skill/prayer/BoneBuryListener.kt b/Server/src/main/content/global/skill/prayer/BoneBuryListener.kt new file mode 100644 index 000000000..5b41927aa --- /dev/null +++ b/Server/src/main/content/global/skill/prayer/BoneBuryListener.kt @@ -0,0 +1,50 @@ +package content.global.skill.prayer + +import core.api.* +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 +import org.rs09.consts.Animations +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 + + 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) + } + return@on 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/BoneBuryingOptionPlugin.java b/Server/src/main/content/global/skill/prayer/BoneBuryingOptionPlugin.java deleted file mode 100644 index 2422af243..000000000 --- a/Server/src/main/content/global/skill/prayer/BoneBuryingOptionPlugin.java +++ /dev/null @@ -1,83 +0,0 @@ -package content.global.skill.prayer; - -import core.cache.def.impl.ItemDefinition; -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.audio.Audio; -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.plugin.Plugin; - -/** - * Represents the bone bury option plugin. - * @author Vexia - */ -@Initializable -public final class BoneBuryingOptionPlugin extends OptionHandler { - - /** - * Represents the animation to use. - */ - private static final Animation ANIMATION = new Animation(827); - - /** - * Represents the sound to use. - */ - private static final Audio SOUND = new Audio(2738, 10, 1); - - @Override - public boolean handle(final Player player, Node node, String option) { - if (player.getAttribute("delay:bury", -1) > GameWorld.getTicks()) { - return true; - } - player.setAttribute("delay:bury", GameWorld.getTicks() + 2); - final Item item = (Item) node; - Bones bone = Bones.forId(item.getId()); - if (bone == null) { - bone = Bones.BONES; - } - if (item.getSlot() < 0) { - return false; - } - boolean remove = true; - if (!remove) { - player.sendMessage("The gods intervene and you keep your bones!"); - } - if (remove) { - if (player.getInventory().replace(null, item.getSlot()) != item) { - return false; - } - } - player.lock(2); - player.animate(ANIMATION); - player.getPacketDispatch().sendMessage("You dig a hole in the ground..."); - player.getAudioManager().send(SOUND); - final Bones bonee = bone; - GameWorld.getPulser().submit(new Pulse(2, player) { - @Override - public boolean pulse() { - player.getPacketDispatch().sendMessage("You bury the bones."); - player.getSkills().addExperience(Skills.PRAYER, bonee.equals(Bones.LAVA_DRAGON_BONES) && player.getLocation().getY() >= 3794 && player.getLocation().getY() <= 3859 ? bonee.getExperience() * 3 : bonee.getExperience(), true); - return true; - } - }); - return true; - } - - @Override - public boolean isWalk() { - return false; - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ItemDefinition.setOptionHandler("bury", this); - return this; - } - -} diff --git a/Server/src/main/content/global/skill/prayer/Bones.kt b/Server/src/main/content/global/skill/prayer/Bones.kt index f6c47f01d..0917df2cb 100644 --- a/Server/src/main/content/global/skill/prayer/Bones.kt +++ b/Server/src/main/content/global/skill/prayer/Bones.kt @@ -1,73 +1,65 @@ package content.global.skill.prayer -import core.game.node.item.Item -import java.util.ArrayList -import java.util.HashMap +import org.rs09.consts.Items /** * Represents the type of bones. * @author Apache Ah64 */ -enum class Bones -/** - * Construct a new `Bones` `Object`. - * @param itemId The item id. - * @param experience The experience given by burying the bone. - */( - /** - * The bone item id. - */ - val itemId: Int, - /** - * The experience given by burying the bone. - */ - val experience: Double) { - BONES(2530, 4.5), - BONES_2(526, 4.5), - WOLF_BONES(2859, 4.5), - BURNT_BONES(528, 4.5), - MONKEY_BONES(3183, 5.0), - MONKEY_BONES2(3179, 5.0), - BAT_BONES(530, 5.3), - BIG_BONES(532, 15.0), - JOGRE_BONES(3125, 15.0), - ZOGRE_BONES(4812, 12.5), - SHAIKAHAN_BONES(3123, 25.0), - BABY_DRAGON_BONES(534, 30.0), - WYVERN_BONES(6812, 50.0), - DRAGON_BONES(536, 72.0), - FAYRG(4830, 84.0), - RAURG_BONES(4832, 96.0), - DAGANNOTH(6729, 125.0), - OURG_BONES(4834, 140.0), - LAVA_DRAGON_BONES(14693, 85.0); +enum class Bones( /** - * Get the bone experience given when you bury the bone. - * @return The experience. + * Construct a new `Bones` `Object`. + * @param itemId The item id. + * @param experience The experience given by burying the bone. */ /** - * Gets the bone meal item. - * @return the item. + * The bone item id. */ - val boneMeal: Item - get() { - return when(this){ - FAYRG -> Item(4852) - RAURG_BONES -> Item(4853) - OURG_BONES -> Item(4854) - else -> Item(4255 + ordinal) - } - } + val itemId: Int, /** - * Gets the config value for the bone type. - * @param hopper the hopper. - * @return the value. + * The experience given by burying the bone. */ - fun getConfigValue(hopper: Boolean): Int { - return (if (this == BONES_2) ordinal else ordinal - 1) or (if (hopper) 4 else 8) shl 16 - } + val experience: Double, + + /** + * The bones' bonemeal item id if applicable. + */ + val bonemealId: Int?, +) { + ASHES(Items.ASHES_592, 4.5, null), //Snowscape: added ashes so the bonecrusher feature gives prayer exp. Ashes cannot be bured normally. + BONES(Items.BONES_526, 4.5, Items.BONEMEAL_4255), + BONES_2(Items.BONES_2530, 4.5, Items.BONEMEAL_4255), + BONES_3(Items.BONES_3187, 4.5, Items.BONEMEAL_4255), + WOLF_BONES(Items.WOLF_BONES_2859, 4.5, Items.BONEMEAL_4257), + BURNT_BONES(Items.BURNT_BONES_528, 4.5, Items.BONEMEAL_4258), + SMALL_NINJA_MONKEY_BONES(Items.MONKEY_BONES_3179, 16.0, Items.BONEMEAL_4256), + MEDIUM_NINJA_MONKEY_BONES(Items.MONKEY_BONES_3180, 18.0, Items.BONEMEAL_4270), + GORILLA_BONES(Items.MONKEY_BONES_3181, 18.0, Items.BONEMEAL_4855), + BEARDED_GORILLA_BONES(Items.MONKEY_BONES_3182, 18.0, Items.BONEMEAL_5615), + KARAMJA_MONKEY_BONES(Items.MONKEY_BONES_3183, 5.0, Items.BONEMEAL_4260), + SMALL_ZOMBIE_MONKEY_BONES(Items.MONKEY_BONES_3185, 5.0, Items.BONEMEAL_6728), + LARGE_ZOMBIE_MONKEY_BONES(Items.MONKEY_BONES_3186, 5.0, Items.BONEMEAL_6810), + BAT_BONES(Items.BAT_BONES_530, 5.3, Items.BONEMEAL_4261), + BIG_BONES(Items.BIG_BONES_532, 15.0, Items.BONEMEAL_4262), + JOGRE_BONES(Items.JOGRE_BONES_3125, 15.0, Items.BONEMEAL_4263), + ZOGRE_BONES(Items.ZOGRE_BONES_4812, 22.5, Items.BONEMEAL_4264), + SHAIKAHAN_BONES(Items.SHAIKAHAN_BONES_3123, 25.0, Items.BONEMEAL_4265), + BABY_DRAGON_BONES(Items.BABYDRAGON_BONES_534, 30.0, Items.BONEMEAL_4266), + WYVERN_BONES(Items.WYVERN_BONES_6812, 50.0, Items.BONEMEAL_4267), //The bonemeal id should be 6810 + DRAGON_BONES(Items.DRAGON_BONES_536, 72.0, Items.BONEMEAL_4268), + FAYRG(Items.FAYRG_BONES_4830, 84.0, Items.BONEMEAL_4852), + RAURG_BONES(Items.RAURG_BONES_4832, 96.0, Items.BONEMEAL_4853), + DAGANNOTH(Items.DAGANNOTH_BONES_6729, 125.0, Items.BONEMEAL_4271), // The bonemeal id should be 6728 + OURG_BONES(Items.OURG_BONES_4834, 140.0, Items.BONEMEAL_4854), + BURNT_JOGRE_BONES(Items.BURNT_JOGRE_BONES_3127, 16.0, Items.BONEMEAL_4259), + BURNT_RAW_PASTY_JOGRE_BONES(Items.PASTY_JOGRE_BONES_3128, 17.0, null), + BURNT_COOKED_PASTY_JOGRE_BONES(Items.PASTY_JOGRE_BONES_3129, 17.0, null), + MARINATED_JOGRE_BONES(Items.MARINATED_J_BONES_3130, 18.0, null), + RAW_PASTY_JOGRE_BONES(Items.PASTY_JOGRE_BONES_3131, 17.0, null), + COOKED_PASTY_JOGRE_BONES(Items.PASTY_JOGRE_BONES_3132, 17.0, null), + MARINATED_JOGRE_BONES_BAD(Items.MARINATED_J_BONES_3133, 18.0, null); companion object { /** @@ -83,23 +75,7 @@ enum class Bones @JvmStatic fun forBoneMeal(itemId: Int): Bones? { for (bone in values()) { - if (bone.boneMeal.id == itemId) { - return bone - } - } - return null - } - - /** - * Gets the config value for the bone. - * @param value the value. - * @param hopper hopper. - * @return `True` if so. - */ - @JvmStatic - fun forConfigValue(value: Int, hopper: Boolean): Bones? { - for (bone in values()) { - if (bone.getConfigValue(hopper) == value) { + if (bone.bonemealId == itemId) { return bone } } 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 56d413546..000000000 --- a/Server/src/main/content/global/skill/prayer/PrayerAltarPlugin.java +++ /dev/null @@ -1,247 +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.SkillRestoration; -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; - -/** - * 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); - player.getAudioManager().send(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 (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 (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 698116728..c31853789 100644 --- a/Server/src/main/content/global/skill/runecrafting/Altar.java +++ b/Server/src/main/content/global/skill/runecrafting/Altar.java @@ -4,6 +4,11 @@ import core.cache.def.impl.ItemDefinition; import core.game.node.entity.player.Player; import core.game.node.scenery.Scenery; +import static core.api.ContentAPIKt.hasRequirement; +import content.data.Quests; + +import core.game.world.map.Location; + /** * Represents an altar an it's relative information(corresponding ruin, etc) * @author 'Vexia @@ -69,13 +74,33 @@ public enum Altar { * @param player the player. */ public void enterRift(Player player) { + if (this == ASTRAL) { + if (!hasRequirement(player, Quests.LUNAR_DIPLOMACY)) + return; + } + if (this == DEATH) { + if (!hasRequirement(player, Quests.MOURNINGS_END_PART_II)) + return; + } + if (this == BLOOD) { + if (!hasRequirement(player, Quests.LEGACY_OF_SEERGAZE)) + return; + } + if (this == SOUL) { + if (player.getQuestRepository().isComplete(Quests.DRAGON_SLAYER)) { + player.getProperties().setTeleportLocation(Location.create(2836, 3285, 0)); + } else { + player.sendMessage("You need to have completed the Dragon Slayer quest in order to do that."); + 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/CombinationRune.java b/Server/src/main/content/global/skill/runecrafting/CombinationRune.java index 62760bfdc..e23a78f8b 100644 --- a/Server/src/main/content/global/skill/runecrafting/CombinationRune.java +++ b/Server/src/main/content/global/skill/runecrafting/CombinationRune.java @@ -7,11 +7,11 @@ import core.game.node.item.Item; * @author 'Vexia */ public enum CombinationRune { - MIST(new Item(4695), 6, 8.0, new Altar[] { Altar.WATER, Altar.AIR }, Rune.AIR, Rune.WATER), - DUST(new Item(4696), 10, 8.3, new Altar[] { Altar.EARTH, Altar.AIR }, Rune.AIR, Rune.EARTH), - MUD(new Item(4698), 13, 9.3, new Altar[] { Altar.EARTH, Altar.WATER }, Rune.WATER, Rune.EARTH), - SMOKE(new Item(4697), 15, 8.5, new Altar[] { Altar.FIRE, Altar.AIR }, Rune.AIR, Rune.FIRE), - STEAM(new Item(4694), 19, 9.3, new Altar[] { Altar.WATER, Altar.FIRE }, Rune.WATER, Rune.FIRE), + MIST(new Item(4695), 6, 10.0, new Altar[] { Altar.WATER, Altar.AIR }, Rune.AIR, Rune.WATER), + DUST(new Item(4696), 10, 10.0, new Altar[] { Altar.EARTH, Altar.AIR }, Rune.AIR, Rune.EARTH), + MUD(new Item(4698), 13, 10.0, new Altar[] { Altar.EARTH, Altar.WATER }, Rune.WATER, Rune.EARTH), + SMOKE(new Item(4697), 15, 10.0, new Altar[] { Altar.FIRE, Altar.AIR }, Rune.AIR, Rune.FIRE), + STEAM(new Item(4694), 19, 10.0, new Altar[] { Altar.WATER, Altar.FIRE }, Rune.WATER, Rune.FIRE), LAVA(new Item(4699), 23, 10.0, new Altar[] { Altar.FIRE, Altar.EARTH }, Rune.EARTH, Rune.FIRE); /** diff --git a/Server/src/main/content/global/skill/runecrafting/EnchantTiaraDialogue.java b/Server/src/main/content/global/skill/runecrafting/EnchantTiaraDialogue.java index 1d96c6b02..6c596bfd4 100644 --- a/Server/src/main/content/global/skill/runecrafting/EnchantTiaraDialogue.java +++ b/Server/src/main/content/global/skill/runecrafting/EnchantTiaraDialogue.java @@ -65,26 +65,28 @@ public final class EnchantTiaraDialogue extends DialoguePlugin { @Override public boolean handle(int interfaceId, int buttonId) { - end(); int amt = 0; + // ButtonId 5=1x, 4=5x, 3=MakeX, 2=All switch (buttonId) { - case 6: + case 5: amt = 1; break; - case 5: + case 4: amt = 5; break; - case 4: - sendInputDialogue(player, true, "Enter the amount:", (value) -> { - player.getPulseManager().run(new EnchantTiaraPulse(player, event.getUsedItem(), Talisman.forItem(event.getUsedItem()).getTiara(), (int) value)); + case 3: + end() +; sendInputDialogue(player, true, "Enter the amount:", (value) -> { + player.getPulseManager().run(new EnchantTiaraPulse(player, event.getUsedItem(), altar ,Talisman.forItem(event.getUsedItem()).getTiara(), (int) value)); return Unit.INSTANCE; }); return true; - case 3: + case 2: amt = player.getInventory().getAmount(event.getUsedItem()); break; } - player.getPulseManager().run(new EnchantTiaraPulse(player, event.getUsedItem(), altar.getTiara(), amt)); + player.getPulseManager().run(new EnchantTiaraPulse(player, event.getUsedItem(), altar ,altar.getTiara(), amt)); + end(); return true; } diff --git a/Server/src/main/content/global/skill/runecrafting/EnchantTiaraPulse.java b/Server/src/main/content/global/skill/runecrafting/EnchantTiaraPulse.java index 3096fb13e..542d776b3 100644 --- a/Server/src/main/content/global/skill/runecrafting/EnchantTiaraPulse.java +++ b/Server/src/main/content/global/skill/runecrafting/EnchantTiaraPulse.java @@ -2,6 +2,7 @@ package content.global.skill.runecrafting; import core.game.node.entity.impl.Animator; import core.game.node.entity.player.link.diary.DiaryType; +import core.game.node.scenery.Scenery; import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Graphics; import core.game.node.entity.skill.SkillPulse; @@ -9,13 +10,21 @@ import core.game.node.entity.skill.Skills; import core.game.node.entity.player.Player; import core.game.node.item.Item; +import java.util.Objects; + +import static java.lang.Math.min; + /** * Represents the enchanting of a tiara pulse. * @author 'Vexia - * @date 02/11/2013 */ public class EnchantTiaraPulse extends SkillPulse { + /** + * Represents the Altar. + */ + private final Altar altar; + /** * Represents the tiara. */ @@ -39,22 +48,25 @@ public class EnchantTiaraPulse extends SkillPulse { * @param player the player. * @param node the node. */ - public EnchantTiaraPulse(Player player, Item node, final Tiara tiara, final int amount) { + public EnchantTiaraPulse(Player player, Item node, final Altar altar, final Tiara tiara, final int amount) { super(player, node); this.tiara = tiara; this.amount = amount; + this.altar = altar; } @Override public void start() { super.start(); - int tiaraAmt = player.getInventory().getAmount(TIARA); - int talsminAmt = player.getInventory().getAmount(node); - if (tiaraAmt > talsminAmt) { - amount = talsminAmt; - } else { - amount = tiaraAmt; + int tiaraAmt = player.getInventory().getAmount(TIARA); // Plain Silver Tiara + int talismanAmt = player.getInventory().getAmount(tiara.getTalisman().getTalisman()); // specific talisman being fused, "node" is all various talismans in inventory + String talismanType = tiara.getTalisman().getTalisman().getName().toLowerCase(); + String altarType = altar.getRuin().name().toLowerCase(); + // Check that the talisman type and the alter type match + if ( talismanType.contains(altarType) ){ + amount = min(talismanAmt, min(tiaraAmt, amount)); } + } @Override diff --git a/Server/src/main/content/global/skill/runecrafting/MysteriousRuin.java b/Server/src/main/content/global/skill/runecrafting/MysteriousRuin.java index 55a42ce9a..41089d272 100644 --- a/Server/src/main/content/global/skill/runecrafting/MysteriousRuin.java +++ b/Server/src/main/content/global/skill/runecrafting/MysteriousRuin.java @@ -1,7 +1,5 @@ package content.global.skill.runecrafting; -import core.game.container.impl.EquipmentContainer; -import core.game.node.entity.player.Player; import core.game.node.scenery.Scenery; import core.game.world.map.Location; @@ -65,24 +63,6 @@ public enum MysteriousRuin { this.tiara = tiara; } - /** - * Enters the ruin. - * @param player the player. - */ - public void enter(Player player) { - if (player.getEquipment().get(EquipmentContainer.SLOT_HAT) == null) { - return; - } - if (getTiara() == null) { - return; - } - if (getTiara().getTiara().getId() != player.getEquipment().get(EquipmentContainer.SLOT_HAT).getId()) { - return; - } - player.getProperties().setTeleportLocation(getEnd()); - player.getPacketDispatch().sendMessage("You feel a powerful force take hold of you..."); - } - /** * Gets the object. * @return The object. diff --git a/Server/src/main/content/global/skill/runecrafting/MysteriousRuinListener.kt b/Server/src/main/content/global/skill/runecrafting/MysteriousRuinListener.kt new file mode 100644 index 000000000..7bf55dd7f --- /dev/null +++ b/Server/src/main/content/global/skill/runecrafting/MysteriousRuinListener.kt @@ -0,0 +1,106 @@ +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 +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.diary.DiaryType +import core.game.node.item.Item +import core.game.requirement.QuestReq +import core.game.requirement.QuestRequirements.* +import core.game.system.task.Pulse +import core.game.world.update.flag.context.Animation + +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." + + override fun defineListeners() { + onUseWith(IntType.SCENERY, allowedUsed, *allowedWith) { player, used, with -> + return@onUseWith handleTalisman(player, used, with) + } + on(allowedWith, IntType.SCENERY, "enter") { player, node -> + return@on handleTiara(player, node) + } + } + + private fun allRuins(): IntArray { + return MysteriousRuin + .values() + .flatMap { ruins -> ruins.`object`.asList() } + .toIntArray() + } + + private fun handleTalisman(player: Player, used: Node, with: Node): Boolean { + val ruin = MysteriousRuin.forObject(with.asScenery()) + if (!checkQuestCompletion(player, ruin)) { + return true + } + + val talisman = Talisman.forItem(used.asItem()) + if (talisman != ruin.talisman && talisman != Talisman.ELEMENTAL) { + sendMessage(player, nothingInteresting) + return false + } + if (talisman == Talisman.ELEMENTAL && (ruin.talisman != Talisman.AIR && ruin.talisman != Talisman.WATER && ruin.talisman != Talisman.FIRE && ruin.talisman != Talisman.EARTH)) { + sendMessage(player, nothingInteresting) + return false + } + + teleportToRuinTalisman(player, used.asItem(), ruin) + return true + } + + private fun handleTiara(player: Player, node: Node): Boolean { + val ruin = MysteriousRuin.forObject(node.asScenery()) + + if (!checkQuestCompletion(player, ruin)) { + return true + } + + val tiara = Tiara.forItem(player.equipment.get(SLOT_HAT)) + if (tiara == null || tiara != ruin.tiara) { + sendMessage(player, nothingInteresting) + return false + } + + submitTeleportPulse(player, ruin, 0) + return true + } + + private fun checkQuestCompletion(player: Player, ruin: MysteriousRuin): Boolean { + return when (ruin) { + 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) + } + } + + private fun teleportToRuinTalisman(player: Player, talisman: Item, ruin: MysteriousRuin) { + lock(player, 4) + animate(player, animation) + sendMessage(player, "You hold the ${talisman.name} towards the mysterious ruins.") + submitTeleportPulse(player, ruin, 3) + } + + private fun submitTeleportPulse(player: Player, ruin: MysteriousRuin, delay: Int) { + sendMessage(player, "You feel a powerful force take hold of you.") + submitWorldPulse(object : Pulse(delay, player) { + override fun pulse(): Boolean { + teleport(player, ruin.end) + if (ruin == MysteriousRuin.EARTH) { + player.achievementDiaryManager.finishTask(player, DiaryType.VARROCK, 0, ENTER_EARTH_ALTAR) + } + return true + } + }) + } + +} diff --git a/Server/src/main/content/global/skill/runecrafting/MysteriousRuinPlugin.java b/Server/src/main/content/global/skill/runecrafting/MysteriousRuinPlugin.java deleted file mode 100644 index df5663d2e..000000000 --- a/Server/src/main/content/global/skill/runecrafting/MysteriousRuinPlugin.java +++ /dev/null @@ -1,85 +0,0 @@ -package content.global.skill.runecrafting; - -import core.game.interaction.NodeUsageEvent; -import core.game.interaction.UseWithHandler; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.info.Rights; -import core.game.node.entity.player.link.diary.DiaryType; -import core.game.node.scenery.Scenery; -import core.game.system.task.Pulse; -import core.game.world.GameWorld; -import core.game.world.update.flag.context.Animation; -import core.plugin.Plugin; - -/** - * Handles the entering into a mysterious ruin. - * @author Vexia - */ -public final class MysteriousRuinPlugin extends UseWithHandler { - - /** - * Represents the animation used. - */ - private static final Animation ANIMATION = new Animation(827); - - /** - * Constructs a new {@code RunecraftingOptionPlugin} {@code Object}. - */ - public MysteriousRuinPlugin() { - super(1438, 1448, 1444, 1440, 1442, 5516, 1446, 1454, 1452, 1462, 1458, 1456, 1450, 1460); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - for (MysteriousRuin ruin : MysteriousRuin.values()) { - for (int i : ruin.getObject()) { - addHandler(i, OBJECT_TYPE, this); - } - } - return this; - } - - @Override - public boolean handle(NodeUsageEvent event) { - final Player player = event.getPlayer(); - if (!player.getQuestRepository().isComplete("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; - } - final Talisman talisman = Talisman.forItem(event.getUsedItem()); - final MysteriousRuin ruin = MysteriousRuin.forObject(((Scenery) event.getUsedWith())); - if (talisman != ruin.getTalisman() && talisman != Talisman.ELEMENTAL) { - return false; - } - if (talisman == Talisman.ELEMENTAL && (ruin.getTalisman() != Talisman.AIR && ruin.getTalisman() != Talisman.WATER && ruin.getTalisman() != Talisman.FIRE && ruin.getTalisman() != Talisman.EARTH)) { - return false; - } - teleport(player, event, ruin); - return true; - } - - /** - * Teleports the player to the ruin location. - * @param player the player. - * @param event the event. - * @param ruin the ruin. - */ - private void teleport(final Player player, final NodeUsageEvent event, final MysteriousRuin ruin) { - player.lock(4); - player.animate(ANIMATION); - player.getPacketDispatch().sendMessage("You hold the " + event.getUsedItem().getName() + " towards the mysterious ruins."); - player.getPacketDispatch().sendMessage("You feel a powerful force take hold of you."); - GameWorld.getPulser().submit(new Pulse(3, player) { - @Override - public boolean pulse() { - player.getProperties().setTeleportLocation(ruin.getEnd()); - // Enter the Earth Altar using an earth tiara or talisman - if (ruin == MysteriousRuin.EARTH) { - player.getAchievementDiaryManager().finishTask(player, DiaryType.VARROCK, 0, 13); - } - return true; - } - }); - } - -} \ 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..6bb4020f0 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, fromBank: Boolean = false) { + 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 && (fromBank && player.getBank().remove(essItem)) || 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/Rune.java b/Server/src/main/content/global/skill/runecrafting/Rune.java index 79762bf96..ef69a1d2f 100644 --- a/Server/src/main/content/global/skill/runecrafting/Rune.java +++ b/Server/src/main/content/global/skill/runecrafting/Rune.java @@ -9,20 +9,20 @@ import core.game.node.item.Item; * @date 01/11/2013 */ public enum Rune { - AIR(Runes.AIR_RUNE.transform(), 1, 5, new int[] { 1, 11, 22, 33, 44, 55, 66, 77, 88, 99 }), - MIND(Runes.MIND_RUNE.transform(), 2, 5.5, new int[] { 1, 14, 28, 42, 56, 70, 84, 98 }), - WATER(Runes.WATER_RUNE.transform(), 5, 6, new int[] { 1, 19, 38, 57, 76, 95 }), - EARTH(Runes.EARTH_RUNE.transform(), 9, 6.5, new int[] { 1, 26, 52, 78 }), - FIRE(Runes.FIRE_RUNE.transform(), 14, 7, new int[] { 1, 35, 70 }), - BODY(Runes.BODY_RUNE.transform(), 20, 7.5, new int[] { 1, 46, 92 }), - COSMIC(Runes.COSMIC_RUNE.transform(), 27, 8, new int[] { 1, 59 }), - CHAOS(Runes.CHAOS_RUNE.transform(), 35, 8.5, new int[] { 1, 74 }), - ASTRAL(Runes.ASTRAL_RUNE.transform(), 40, 8.7, new int[] { 1, 82 }), - NATURE(Runes.NATURE_RUNE.transform(), 44, 9, new int[] { 1, 91 }), - LAW(Runes.LAW_RUNE.transform(), 54, 9.5), - DEATH(Runes.DEATH_RUNE.transform(), 65, 10), - BLOOD(Runes.BLOOD_RUNE.transform(), 77, 10.5), - SOUL(Runes.SOUL_RUNE.transform(), 90, 11); + AIR(Runes.AIR_RUNE.transform(), 1, 5, new int[] { 1, 11, 22, 33, 44, 55, 66, 77, 88, 99, 110 }), + MIND(Runes.MIND_RUNE.transform(), 2, 5.5, new int[] { 1, 14, 28, 42, 56, 70, 84, 98, 112 }), + WATER(Runes.WATER_RUNE.transform(), 1, 5, new int[] { 1, 11, 22, 33, 44, 55, 66, 77, 88, 99, 110 }), + EARTH(Runes.EARTH_RUNE.transform(), 1, 5, new int[] { 1, 11, 22, 33, 44, 55, 66, 77, 88, 99, 110 }), + FIRE(Runes.FIRE_RUNE.transform(), 1, 5, new int[] { 1, 11, 22, 33, 44, 55, 66, 77, 88, 99, 110 }), + BODY(Runes.BODY_RUNE.transform(), 20, 7.5, new int[] { 1, 46, 92, 138 }), + COSMIC(Runes.COSMIC_RUNE.transform(), 27, 8, new int[] { 1, 59, 118 }), + CHAOS(Runes.CHAOS_RUNE.transform(), 35, 8.5, new int[] { 1, 74, 148 }), + ASTRAL(Runes.ASTRAL_RUNE.transform(), 40, 8.7, new int[] { 1, 82, 164 }), + NATURE(Runes.NATURE_RUNE.transform(), 44, 9, new int[] { 1, 91, 182 }), + LAW(Runes.LAW_RUNE.transform(), 54, 9.5, new int[] { 1, 110, }), + DEATH(Runes.DEATH_RUNE.transform(), 65, 10, new int[] { 1, 131 }), + BLOOD(Runes.BLOOD_RUNE.transform(), 77, 10.5, new int[] { 1, 154 }), + SOUL(Runes.SOUL_RUNE.transform(), 90, 11, new int[] { 1, 179 }); /** * Constructs a new {@code Rune} {@code Object}. diff --git a/Server/src/main/content/global/skill/runecrafting/RuneCraftPulse.java b/Server/src/main/content/global/skill/runecrafting/RuneCraftPulse.java index d333dc443..017efe99c 100644 --- a/Server/src/main/content/global/skill/runecrafting/RuneCraftPulse.java +++ b/Server/src/main/content/global/skill/runecrafting/RuneCraftPulse.java @@ -1,5 +1,8 @@ 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; @@ -10,17 +13,24 @@ import core.game.node.item.Item; import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Graphics; import core.tools.RandomFunction; + +import static core.api.ContentAPIKt.*; import static core.game.system.command.sets.StatAttributeKeysKt.STATS_BASE; import static core.game.system.command.sets.StatAttributeKeysKt.STATS_RC; + import core.game.world.GameWorld; +import org.rs09.consts.Items; +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 { @@ -33,6 +43,7 @@ public final class RuneCraftPulse extends SkillPulse { * Represents the pure essence item. */ private static final Item PURE_ESSENCE = new Item(7936); + private static final Item SOFT_CLAY = new Item(1761); /** * Represents the binding necklace item. @@ -92,42 +103,54 @@ public final class RuneCraftPulse extends SkillPulse { @Override public boolean checkRequirements() { - 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 == Altar.ASTRAL) { + if (!hasRequirement(player, Quests.LUNAR_DIPLOMACY)) + return false; + } + if (altar == Altar.DEATH) { + if (!hasRequirement(player, Quests.MOURNINGS_END_PART_II)) + return false; + } + if (altar == Altar.BLOOD) { + if (!hasRequirement(player, Quests.LEGACY_OF_SEERGAZE)) + return false; + } + 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 && amountInInventory(player, SOFT_CLAY.getId()) == 0) { + sendMessage(player, "You need pure essence to craft this rune, or soft clay to craft teleport tablets."); 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 && amountInInventory(player, SOFT_CLAY.getId()) == 0) { + sendMessage(player, "You need rune or pure essence to craft this rune, or soft clay to craft teleport tablets."); 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; } } - player.lock(4); + player.lock(2); return true; } @@ -135,56 +158,92 @@ public final class RuneCraftPulse extends SkillPulse { public void animate() { player.animate(ANIMATION); player.graphics(GRAPHICS); - player.getAudioManager().send(2710); + playAudio(player, Sounds.BIND_RUNES_2710); } @Override public boolean reward() { + setDelay(5); if (!combination) { - craft(); + if (amountInInventory(player, SOFT_CLAY.getId()) > 0) { + craftTablet(); + } else { + craft(); + } } else { combine(); } - return true; + SnowscapePouchListener.Companion.emptyPouches(player); + return false; } - @Override - public void stop() { - super.stop(); - player.animate(ANIMATION); - } - - @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 final void craft() { - final Item item = new Item(getEssence().getId(), getEssenceAmount()); - int amount = player.getInventory().getAmount(item); - if (!altar.isOurania()) { + private void craft() { + final Item item = getEssenceItem(); + //int amount = player.getInventory().getAmount(item); + int amount = 2; + if (altar.isOurania()) { + amount = 1; + 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() * 4); + addItemOrDrop(player, rune.getRune().getId(), getMultiplier(rune)); + } + } + } else { int total = 0; for(int j = 0; j < amount; j++) { // since getMultiplier is stochastic, roll `amount` independent copies - total += getMultiplier(); + total += getMultiplier(rune); } - 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); - player.getSkills().addExperience(Skills.RUNECRAFTING, rune.getExperience() * amount, true); + + // Fist of guthix gloves + double xp = rune.getExperience() * amount; + if ((altar == Altar.AIR && inEquipment(player, Items.AIR_RUNECRAFTING_GLOVES_12863, 1)) + || (altar == Altar.WATER && inEquipment(player, Items.WATER_RUNECRAFTING_GLOVES_12864, 1)) + || (altar == Altar.EARTH && inEquipment(player, Items.EARTH_RUNECRAFTING_GLOVES_12865, 1))) { + xp += xp * FOGGlovesManager.updateCharges(player, amount) / amount; + } + rewardXP(player, Skills.RUNECRAFTING, xp); // Achievement Diary handling // Craft some nature runes @@ -192,7 +251,8 @@ 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) { + //Snowscape: since runecrafting is one essence at a time, you can't craft 196 at once. So just trigger when level 66 is reached. + if (altar == Altar.AIR && getDynLevel(player, Skills.RUNECRAFTING) >= 66) { player.getAchievementDiaryManager().finishTask(player, DiaryType.FALADOR, 2, 2); } // Craft a water rune at the Water Altar @@ -201,65 +261,77 @@ 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); + //int essenceAmt = player.getInventory().getAmount(PURE_ESSENCE); + int essenceAmt = 1; + int multiplier = getMultiplier(altar.getRune()) * 2; 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(multiplier, runeAmt); + if (removeItem(player, new Item(PURE_ESSENCE.getId(), essenceAmt), Container.INVENTORY) && removeItem(player, new Item(rune.getId(), amount), Container.INVENTORY)) { + rewardXP(player, Skills.RUNECRAFTING, combo.getExperience() * 2); 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 (1000 - player.getEquipment().get(EquipmentContainer.SLOT_AMULET).getCharge() >= 400) { + if (player.getEquipment().remove(BINDING_NECKLACE, true)) { + sendMessage(player, "Your binding necklace crumbles into dust."); + playAudio(player, Sounds.DESTROY_OBJECT_2381); + } } + } else { + sendMessage(player, "Some of the runes are destroyed, as you are not wearing a Binding Necklace."); } + // Snowscape: adding the imbue effect after crafting so that a talisman isn't required for every essence + player.setAttribute("spell:imbue", GameWorld.getTicks() + 20); } } } + /** + * Method used to craft tablets. Custom for Snowscape + */ + private final void craftTablet() { + //int amount = player.getInventory().getAmount(new Item(1761)); + int amount = 1; + Item clay = new Item(1761, amount); + Item tablet = null; + if (altar == Altar.AIR) { tablet = new Item(13599, amount); } + if (altar == Altar.MIND) { tablet = new Item(13600, amount); } + if (altar == Altar.WATER) { tablet = new Item(13601, amount); } + if (altar == Altar.EARTH) { tablet = new Item(13602, amount); } + if (altar == Altar.FIRE) { tablet = new Item(13603, amount); } + if (altar == Altar.BODY) { tablet = new Item(13604, amount); } + if (altar == Altar.COSMIC) { tablet = new Item(13605, amount); } + if (altar == Altar.CHAOS) { tablet = new Item(13606, amount); } + if (altar == Altar.ASTRAL) { tablet = new Item(13611, amount); } + if (altar == Altar.NATURE) { tablet = new Item(13607, amount); } + if (altar == Altar.LAW) { tablet = new Item(13608, amount); } + if (altar == Altar.DEATH) { tablet = new Item(13609, amount); } + if (altar == Altar.BLOOD) { tablet = new Item(13610, amount); } + if (altar == Altar.SOUL) { tablet = new Item(8022, amount); } + if (tablet != null && player.getInventory().remove(clay)) { + player.getInventory().add(tablet); + player.getSkills().addExperience(Skills.RUNECRAFTING, rune.getExperience() * amount * 2, true); + player.getPacketDispatch().sendMessage("You bind the temple's power into teleport tablets."); + } + } + /** * Checks if the player has the spell imbue. * @@ -270,39 +342,25 @@ 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())); + return new Item(PURE_ESSENCE.getId(), 1); } - 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())); + return new Item(PURE_ESSENCE.getId(), 1); } - } - - /** - * 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(RUNE_ESSENCE.getId(), 1); } + //return new Item(PURE_ESSENCE.getId(), amountInInventory(player, PURE_ESSENCE.getId())); + return new Item(PURE_ESSENCE.getId(), 1); } /** @@ -310,11 +368,17 @@ public final class RuneCraftPulse extends SkillPulse { * * @return the amount. */ - public int getMultiplier() { - if (altar.isOurania()) { - return 1; - } - int rcLevel = player.getSkills().getLevel(Skills.RUNECRAFTING); + public int getMultiplier(Rune rune) { + //if (altar.isOurania()) { + // return 1; + //} + 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); + } + + public static int getMultiplier(int rcLevel, Rune rune, int runecraftingFormulaRevision, boolean lumbridgeDiary) { int[] multipleLevels = rune.getMultiple(); int i = 0; for (int level : multipleLevels) { @@ -323,17 +387,18 @@ public final class RuneCraftPulse extends SkillPulse { } } - if(multipleLevels.length > i) { + if (multipleLevels.length > i && runecraftingFormulaRevision >= 573) { int a = Math.max(multipleLevels[i-1], rune.getLevel()); int b = multipleLevels[i]; - double chance = ((double)rcLevel - (double)a) / ((double)b - (double)a); - player.debug(String.format("%d %d %d, %f", a, b, rcLevel, chance)); - if(RandomFunction.random(0.0, 1.0) < chance) { - i += 1; + if(b <= 99 || runecraftingFormulaRevision >= 581) { + double chance = ((double)rcLevel - (double)a) / ((double)b - (double)a); + if(RandomFunction.random(0.0, 1.0) < chance) { + i += 1; + } } } - if (player.getAchievementDiaryManager().getDiary(DiaryType.LUMBRIDGE).isComplete(1) + if (lumbridgeDiary && new ArrayList<>(Arrays.asList(Rune.AIR, Rune.WATER, Rune.FIRE, Rune.EARTH)).contains(rune) && RandomFunction.getRandom(10) == 0) { //approximately 10% chance i += 1; @@ -359,5 +424,4 @@ public final class RuneCraftPulse extends SkillPulse { public Altar getAltar() { return altar; } - } diff --git a/Server/src/main/content/global/skill/runecrafting/RunePouch.java b/Server/src/main/content/global/skill/runecrafting/RunePouch.java index 98b5a6caa..b2fbf892a 100644 --- a/Server/src/main/content/global/skill/runecrafting/RunePouch.java +++ b/Server/src/main/content/global/skill/runecrafting/RunePouch.java @@ -1,6 +1,6 @@ package content.global.skill.runecrafting; -import core.game.global.action.DropItemHandler; +import core.game.global.action.DropListener; import core.game.node.entity.skill.Skills; import core.game.node.entity.player.Player; import core.game.node.item.Item; @@ -195,7 +195,7 @@ public enum RunePouch { */ private void drop(Player player, Item item) { onDrop(player, item); - DropItemHandler.drop(player, item); + DropListener.drop(player, item); } /** 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 903503992..954948eaf 100644 --- a/Server/src/main/content/global/skill/runecrafting/RunecraftingPlugin.java +++ b/Server/src/main/content/global/skill/runecrafting/RunecraftingPlugin.java @@ -1,5 +1,6 @@ package content.global.skill.runecrafting; +import content.global.travel.EssenceTeleport; import core.cache.def.impl.ItemDefinition; import core.cache.def.impl.NPCDefinition; import core.cache.def.impl.SceneryDefinition; @@ -13,15 +14,17 @@ import core.game.node.entity.player.info.Rights; 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.Location; import core.net.packet.PacketRepository; import core.net.packet.context.MinimapStateContext; import core.net.packet.out.MinimapState; +import core.plugin.ClassScanner; import core.plugin.Initializable; import core.plugin.Plugin; -import content.global.travel.EssenceTeleport; -import core.game.world.GameWorld; -import core.plugin.ClassScanner; + +import static core.api.ContentAPIKt.hasRequirement; +import content.data.Quests; /** * Handles runecraftign related options. @@ -36,7 +39,6 @@ public class RunecraftingPlugin extends OptionHandler { ClassScanner.definePlugin(new TiaraPlugin()); ClassScanner.definePlugin(new RunePouchPlugin()); ClassScanner.definePlugin(new EnchantTiaraPlugin()); - ClassScanner.definePlugin(new MysteriousRuinPlugin()); ClassScanner.definePlugin(new CombinationRunePlugin()); SceneryDefinition.forId(2492).getHandlers().put("option:use", this); NPCDefinition.forId(553).getHandlers().put("option:teleport", this); @@ -51,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; } @@ -94,21 +96,19 @@ public class RunecraftingPlugin extends OptionHandler { player.sendMessage("You can only craft Astral runes on Lunar Isle."); return true; } - player.getPulseManager().run(new RuneCraftPulse(player, null, Altar.forObject(((Scenery) node)), false, null)); + Altar a = Altar.forObject(((Scenery) node)); + if (a == Altar.ASTRAL) { + if (!hasRequirement(player, Quests.LUNAR_DIPLOMACY)) + return true; + } + player.getPulseManager().run(new RuneCraftPulse(player, null, a, false, null)); break; case "locate": final Talisman talisman = Talisman.forItem(((Item) node)); talisman.locate(player); break; - case "enter": - final MysteriousRuin ruin = MysteriousRuin.forObject(((Scenery) node)); - if (ruin == null) { - return true; - } - ruin.enter(player); - break; case "climb": - int id = ((Scenery) node).getId(); + int id = (node).getId(); switch (id) { case 26849: ClimbActionHandler.climb(player, null, new Location(3271, 4861, 0)); @@ -130,11 +130,6 @@ public class RunecraftingPlugin extends OptionHandler { SceneryDefinition.forId(altar.getObject()).getHandlers().put("option:craft-rune", this); SceneryDefinition.forId(altar.getPortal()).getHandlers().put("option:use", this); } - for (MysteriousRuin ruin : MysteriousRuin.values()) { - for (int i : ruin.getObject()) { - SceneryDefinition.forId(i).getHandlers().put("option:enter", this); - } - } for (Talisman talisman : Talisman.values()) { ItemDefinition.forId(talisman.getTalisman().getId()).getHandlers().put("option:locate", this); } diff --git a/Server/src/main/content/global/skill/runecrafting/SnowscapePouchListener.kt b/Server/src/main/content/global/skill/runecrafting/SnowscapePouchListener.kt new file mode 100755 index 000000000..0c98a6268 --- /dev/null +++ b/Server/src/main/content/global/skill/runecrafting/SnowscapePouchListener.kt @@ -0,0 +1,91 @@ +package content.global.skill.runecrafting + +import core.api.* +import core.game.node.Node +import core.game.node.entity.player.Player +import core.game.node.item.Item +import core.game.interaction.InteractionListener +import core.game.interaction.IntType +import org.rs09.consts.Items +import content.global.handlers.scenery.BankBoothListener +import content.global.handlers.npc.BankerNPC +import content.global.skill.summoning.familiar.BurdenBeast +import content.global.skill.summoning.familiar.Forager + +/** + * Listener for using runecrafting pouches on banks for easier refilling. + * Using any pouch on a bank booth, chest, or banker will refill all the carried pouches as well as the familiar inventory. + * @author + */ +class SnowscapePouchListener : InteractionListener { + + companion object { + + val pouchIds = intArrayOf(5509,5510,5511,5512,5513,5514,5515) + + // Called by the runecraft pulse + fun emptyPouches(player: Player) { + if (amountInInventory(player, Items.PURE_ESSENCE_7936) > 0 || amountInInventory(player, Items.RUNE_ESSENCE_1436) > 0) return + + if (player.getFamiliarManager().hasFamiliar()) { + val familiar = player.getFamiliarManager().getFamiliar() + if (familiar.isBurdenBeast() && (familiar as BurdenBeast).getContainer().containsAtLeastOneItem(Items.PURE_ESSENCE_7936)) { + (familiar as BurdenBeast).transfer(Item(Items.PURE_ESSENCE_7936), 99, true) + } + } + + for (pouchId in pouchIds) { + if (inInventory(player, pouchId, 1)) { + player.pouchManager.withdrawFromPouch(pouchId) + } + } + } + } + + private fun fillPouchFromBank(player: Player) { + val playerBank = player.getBank() + for (pouchId in pouchIds) { + if (inInventory(player, pouchId, 1)) { + player.pouchManager.addToPouch(pouchId, playerBank.getAmount(Items.PURE_ESSENCE_7936), Items.PURE_ESSENCE_7936, true) + } + } + + if (player.getFamiliarManager().hasFamiliar()) { + val familiar = player.getFamiliarManager().getFamiliar() + if (familiar.isBurdenBeast() && (familiar !is Forager)) { + val familiarContainer = (familiar as BurdenBeast).getContainer() + val amount = kotlin.math.min(familiarContainer.getMaximumAdd(Item(Items.PURE_ESSENCE_7936)), playerBank.getAmount(Item(Items.PURE_ESSENCE_7936))) + val item = Item(Items.PURE_ESSENCE_7936, amount) + if (playerBank.remove(item)) familiarContainer.add(item) + if (familiarContainer.freeSlots() == 0) sendMessage(player, "Your familiar is full.") + + } + } + + sendMessage(player, "There is ${player.getBank().getAmount(Items.PURE_ESSENCE_7936)} Pure Essence remaining in your bank.") + } + + + + override fun defineListeners() { + + // BankBoothListener values are public but BankChestListener values are not, so rather than edit the BankChestListener file I have copied the IDs here. 12309 is the Culinomancer's chest. + val bankChests = intArrayOf(3194,4483,10562,14382,16695,16696,21301,27662,27663) + val bankUseWiths = intArrayOf(*BankBoothListener.BANK_BOOTHS, 12309, *bankChests) + + //6362 is the Ourania Altar banker. + val bankers = intArrayOf(*BankerNPC.NPC_IDS, *BankerNPC.SPECIAL_NPC_IDS, 6362) + + onUseWith(IntType.SCENERY, pouchIds, *bankUseWiths) { player, used, with -> + fillPouchFromBank(player) + return@onUseWith true + } + + onUseWith(IntType.NPC, pouchIds, *bankers) { player, used, with -> + fillPouchFromBank(player) + return@onUseWith true + } + } + + +} diff --git a/Server/src/main/content/global/skill/runecrafting/TiaraPlugin.java b/Server/src/main/content/global/skill/runecrafting/TiaraPlugin.java index adbdd260c..b52a7c1e7 100644 --- a/Server/src/main/content/global/skill/runecrafting/TiaraPlugin.java +++ b/Server/src/main/content/global/skill/runecrafting/TiaraPlugin.java @@ -1,5 +1,7 @@ package content.global.skill.runecrafting; +import static core.api.ContentAPIKt.*; + import core.cache.def.impl.VarbitDefinition; import core.cache.def.impl.ItemDefinition; import core.cache.def.impl.SceneryDefinition; @@ -38,27 +40,27 @@ public final class TiaraPlugin implements Plugin { switch (identifier) { case "equip": if (item.getName().contains("cape")) { - player.getConfigManager().set(CONFIG, 6143); + setVarp(player, CONFIG, 6143); break; } MysteriousRuin ruin = MysteriousRuin.forTalisman(Tiara.forItem(item).getTalisman()); - player.getConfigManager().set(CONFIG, 1 << VarbitDefinition.forObjectID(SceneryDefinition.forId(ruin.getObject()[0]).getVarbitID()).getStartBit(), true); + setVarbit(player, SceneryDefinition.forId(ruin.getObject()[0]).getVarbitID(), 1, true); break; case "unequip": final Item other = args.length == 2 ? null : (Item) args[2]; if (other != null) { if (other.getName().toLowerCase().contains("cape")) { - player.getConfigManager().set(CONFIG, 6143); + setVarp(player, CONFIG, 6143); break; } Tiara tiara = Tiara.forItem(other); if (tiara != null) { MysteriousRuin r = MysteriousRuin.forTalisman(tiara.getTalisman()); - player.getConfigManager().set(CONFIG, 1 << VarbitDefinition.forObjectID(SceneryDefinition.forId(r.getObject()[0]).getVarbitID()).getStartBit(), true); + setVarbit(player, SceneryDefinition.forId(r.getObject()[0]).getVarbitID(), 1, true); break; } } - player.getConfigManager().set(CONFIG, 0); + setVarp(player, CONFIG, 0); break; } return true; diff --git a/Server/src/main/content/global/skill/runecrafting/abyss/AbyssPlugin.kt b/Server/src/main/content/global/skill/runecrafting/abyss/AbyssPlugin.kt index 3c49446ee..e44b57da1 100644 --- a/Server/src/main/content/global/skill/runecrafting/abyss/AbyssPlugin.kt +++ b/Server/src/main/content/global/skill/runecrafting/abyss/AbyssPlugin.kt @@ -4,7 +4,6 @@ import core.api.* import core.game.node.Node import core.plugin.ClassScanner.definePlugin import core.tools.colorize -import core.tools.SystemLogger.logInfo import core.game.node.scenery.Scenery import core.game.node.entity.impl.Animator import core.game.node.entity.npc.NPC @@ -16,6 +15,7 @@ import content.global.skill.runecrafting.abyss.AbyssalNPC import content.global.skill.runecrafting.abyss.DarkMageDialogue import content.global.skill.runecrafting.abyss.ZamorakMageDialogue import core.game.system.task.Pulse +import core.game.system.timer.impl.Skulled import core.game.world.map.Location import core.game.world.map.RegionManager import core.game.world.update.flag.context.Animation @@ -28,6 +28,7 @@ import org.rs09.consts.Scenery as Sceneries import core.game.interaction.InteractionListener import core.game.interaction.IntType import core.game.world.GameWorld +import core.tools.Log import kotlin.math.cos import kotlin.math.sin import kotlin.random.Random @@ -214,7 +215,8 @@ class AbyssPlugin : InteractionListener { npc.visualize(Animation(1979), Graphics(4)) npc.sendChat("Veniens! Sallakar! Rinnesset!") player.skills.decrementPrayerPoints(100.0) - player.skullManager.checkSkull(player) + removeTimer(player) + registerTimer(player, spawnTimer(2000)) GameWorld.Pulser.submit(object : Pulse(2, player) { override fun pulse(): Boolean { rotateObstacles(player,teleportLoc) @@ -244,7 +246,7 @@ class AbyssPlugin : InteractionListener { * Used to make sure the player lands by the blocked obstacle. */ fun rotateObstacles(player: Player, abyssLoc: AbyssLoc) { - setVarbit(player,VARP_SCENERY_ABYSS,ABYSS_OBSTACLES,abyssLoc.getSegment(),true) + setVarbit(player, 625, abyssLoc.getSegment(), true) } /** @@ -255,7 +257,7 @@ class AbyssPlugin : InteractionListener { const val BURN_PROGRESS = 16 const val DISTRACT_PROGRESS = 18 fun handleObstacle(obstacle: Node, player: Player, skill: Int, varbitVal: Int?, animation: Animation, messages: Array): Boolean { - logInfo(this::class.java, "handled abyss ${obstacle.name}") + log(this::class.java, Log.FINE, "handled abyss ${obstacle.name}") player.lock() player.animate(animation) GameWorld.Pulser.submit(object : Pulse(1, player) { @@ -263,10 +265,9 @@ class AbyssPlugin : InteractionListener { override fun pulse(): Boolean { when (count++) { 1 -> sendMessage(player, messages[0]) - 3 -> return if (RandomFunction.random(100) < getStatLevel(player,skill) - ) { + 3 -> return if (RandomFunction.random(100) < getStatLevel(player,skill)+1) { sendMessage(player, colorize("%G${messages[1]}")) - if(varbitVal != null) { setVarbit(player,VARP_SCENERY_ABYSS,ABYSS_OBSTACLES,varbitVal) } + if(varbitVal != null) { setVarbit(player, 625, varbitVal) } false } else { sendMessage(player, colorize("%R${messages[2]}")) @@ -274,7 +275,7 @@ class AbyssPlugin : InteractionListener { true } 5 -> { - if(varbitVal != null) { setVarbit(player,VARP_SCENERY_ABYSS,ABYSS_OBSTACLES,varbitVal or 1) } + if(varbitVal != null) { setVarbit(player, 625, varbitVal or 1) } } 7 -> { player.unlock() diff --git a/Server/src/main/content/global/skill/runecrafting/abyss/AbyssalNPC.java b/Server/src/main/content/global/skill/runecrafting/abyss/AbyssalNPC.java index 0f55805dd..f713fb69b 100644 --- a/Server/src/main/content/global/skill/runecrafting/abyss/AbyssalNPC.java +++ b/Server/src/main/content/global/skill/runecrafting/abyss/AbyssalNPC.java @@ -67,20 +67,19 @@ public final class AbyssalNPC extends AbstractNPC { * @return the pouch. */ private Item getPouch(Player player) { - Item pouch = RunePouch.SMALL.getPouch(); - if (player.hasItem(pouch)) { - pouch = RunePouch.MEDIUM.getPouch(); + if (!player.hasItem(RunePouch.SMALL.getPouch())) { + return RunePouch.SMALL.getPouch(); } - if (player.hasItem(RunePouch.MEDIUM.getPouch()) || player.hasItem(RunePouch.MEDIUM.getDecayedPouch())) { - pouch = RunePouch.LARGE.getPouch(); + if (!player.hasItem(RunePouch.MEDIUM.getPouch()) && !player.hasItem(RunePouch.MEDIUM.getDecayedPouch())) { + return RunePouch.MEDIUM.getPouch(); } - if (player.hasItem(RunePouch.LARGE.getPouch()) || player.hasItem(RunePouch.LARGE.getDecayedPouch())) { - pouch = RunePouch.GIANT.getPouch(); + if (!player.hasItem(RunePouch.LARGE.getPouch()) && !player.hasItem(RunePouch.LARGE.getDecayedPouch())) { + return RunePouch.LARGE.getPouch(); } - if (player.hasItem(RunePouch.GIANT.getPouch()) || player.hasItem(RunePouch.GIANT.getDecayedPouch())) { - pouch = null; + if (!player.hasItem(RunePouch.GIANT.getPouch()) && !player.hasItem(RunePouch.GIANT.getDecayedPouch())) { + return RunePouch.GIANT.getPouch(); } - return pouch; + return null; } @Override 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 af161eea5..ab06e4843 100644 --- a/Server/src/main/content/global/skill/runecrafting/abyss/ZamorakMageDialogue.java +++ b/Server/src/main/content/global/skill/runecrafting/abyss/ZamorakMageDialogue.java @@ -6,6 +6,9 @@ import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.game.node.item.Item; +import static core.api.ContentAPIKt.*; +import content.data.Quests; + /** * Handles the zamorak mages dialogue. @@ -49,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; @@ -188,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: @@ -297,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; } @@ -311,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: @@ -326,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: @@ -425,7 +428,7 @@ public final class ZamorakMageDialogue extends DialoguePlugin { stage = 8; break; case 20: - npc("Well, the creates there ARE particularly offensive..."); + npc("Well, the creatures there ARE particularly offensive..."); stage = 8; break; case 30: @@ -460,7 +463,7 @@ public final class ZamorakMageDialogue extends DialoguePlugin { * @param stage the stage. */ public void setStage(int stage) { - player.getConfigManager().set(492, stage, true); + setVarp(player, 492, stage, true); } /** @@ -468,6 +471,6 @@ public final class ZamorakMageDialogue extends DialoguePlugin { * @return the stage. */ public int getStage() { - return player.getConfigManager().get(492); + return getVarp(player, 492); } } diff --git a/Server/src/main/content/global/skill/skillcapeperks/SkillcapePerks.kt b/Server/src/main/content/global/skill/skillcapeperks/SkillcapePerks.kt index b30636136..7dbc1992b 100644 --- a/Server/src/main/content/global/skill/skillcapeperks/SkillcapePerks.kt +++ b/Server/src/main/content/global/skill/skillcapeperks/SkillcapePerks.kt @@ -5,12 +5,19 @@ import core.game.node.entity.player.Player import core.game.node.entity.player.link.SpellBookManager import core.game.node.entity.player.link.TeleportManager import content.global.skill.runecrafting.Altar +import core.game.world.map.Location import core.game.world.map.zone.impl.DarkZone import core.plugin.Initializable import core.game.world.GameWorld import content.global.skill.farming.* -import core.api.getAttribute -import core.api.sendDialogue +import core.ServerStore +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"), @@ -26,32 +33,33 @@ enum class SkillcapePerks(val attribute: String, val effect: ((Player) -> Unit)? DAMAGE_SPONG("cape_perks:damage-sponge"), MARATHON_RUNNER("cape_perks:marathon-runner"), LIBRARIAN_MAGUS("cape_perks:librarian-magus",{player -> - val time = player.getAttribute("cape_perks:librarian-magus-timer",0L) - if(player.getAttribute("cape_perks:librarian-magus-charges",2) > 0 || System.currentTimeMillis() > time){ - if(System.currentTimeMillis() > time) player.setAttribute("/save:cape_perks:librarian-magus-charges",3) + val store = ServerStore.getArchive("daily-librarian-magus") + val used = store.getInt(player.name,0) + if (used >= 3) { + player.dialogueInterpreter.sendDialogue("Your cape is still on cooldown.") + } else { player.dialogueInterpreter.open(509871234) - if(System.currentTimeMillis() > time) - player.setAttribute("/save:cape_perks:librarian-magus-timer",System.currentTimeMillis() + java.util.concurrent.TimeUnit.DAYS.toMillis(1)) + store[player.name] = used + 1 } }), - ABYSS_WARPING("cape_perks:abyss_warp",{ player -> - val time = player.getAttribute("cape_perks:abyssal_warp_timer",0L) - if(player.getAttribute("cape_perks:abyssal_warp",3) > 0 || System.currentTimeMillis() > time){ - if(System.currentTimeMillis() > time) player.setAttribute("/save:cape_perks:abyssal_warp",3) - player.dialogueInterpreter.open(509871233) - if(System.currentTimeMillis() > time) - player.setAttribute("/save:cape_perks:abyssal_warp_timer",System.currentTimeMillis() + java.util.concurrent.TimeUnit.DAYS.toMillis(1)) + ABYSS_WARPING("cape_perks:abyss_warp",{player -> + val store = ServerStore.getArchive("daily-abyss-warp") + val used = store.getInt(player.name,0) + if (used >= 3) { + player.dialogueInterpreter.sendDialogue("Your cape is still on cooldown.") } else { - player.dialogueInterpreter.sendDialogue("Your cape is still on cooldown.","Ready in " + java.util.concurrent.TimeUnit.MILLISECONDS.toMinutes(time - System.currentTimeMillis()) + " minutes.") + player.dialogueInterpreter.open(509871233) } }), SEED_ATTRACTION("cape_perks:seed_attract",{player -> - val time = player.getAttribute("cape_perks:seed_attract_timer",0L) - if(System.currentTimeMillis() > time){ + val store = ServerStore.getArchive("daily-seed-attract") + if (store.getBoolean(player.name)) { + player.dialogueInterpreter.sendDialogue("Your cape is still on cooldown.") + } else { val possibleSeeds = Plantable.values() for(i in 0 until 10){ var seed = possibleSeeds.random() - while(seed == Plantable.SCARECROW || seed.applicablePatch == PatchType.FRUIT_TREE || seed.applicablePatch == PatchType.TREE || seed.applicablePatch == PatchType.SPIRIT_TREE){ + while(seed == Plantable.SCARECROW || seed.applicablePatch == PatchType.FRUIT_TREE_PATCH || seed.applicablePatch == PatchType.TREE_PATCH || seed.applicablePatch == PatchType.SPIRIT_TREE_PATCH){ seed = possibleSeeds.random() } val reward = core.game.node.item.Item(seed.itemID) @@ -60,9 +68,7 @@ enum class SkillcapePerks(val attribute: String, val effect: ((Player) -> Unit)? } } player.dialogueInterpreter.sendDialogue("You pluck off the seeds that were stuck to your cape.") - player.setAttribute("/save:cape_perks:seed_attract_timer",System.currentTimeMillis() + java.util.concurrent.TimeUnit.DAYS.toMillis(1)) - } else { - player.dialogueInterpreter.sendDialogue("Your cape is still on cooldown.","Ready in " + java.util.concurrent.TimeUnit.MILLISECONDS.toMinutes(time - System.currentTimeMillis()) + " minutes.") + store[player.name] = true } }), TRICKS_OF_THE_TRADE("cape_perks:tott",{player -> @@ -74,6 +80,9 @@ enum class SkillcapePerks(val attribute: String, val effect: ((Player) -> Unit)? } }), HASTY_COOKING("cape_perks:hasty-cooking"), + SMOOTH_HANDS("cape_perks:smooth-hands"), + PET_MASTERY("cape_perks:pet-mastery"), + BREWMASTER("cape_perks:brewmaster"), NONE("cape_perks:none") ; @@ -94,8 +103,8 @@ enum class SkillcapePerks(val attribute: String, val effect: ((Player) -> Unit)? Skillcape.RUNECRAFTING -> ABYSS_WARPING Skillcape.HITPOINTS -> DAMAGE_SPONG Skillcape.AGILITY -> MARATHON_RUNNER - Skillcape.HERBLORE -> NONE - Skillcape.THIEVING -> NONE + Skillcape.HERBLORE -> BREWMASTER + Skillcape.THIEVING -> SMOOTH_HANDS Skillcape.CRAFTING -> NONE Skillcape.FLETCHING -> NONE Skillcape.SLAYER -> TRICKS_OF_THE_TRADE @@ -108,7 +117,7 @@ enum class SkillcapePerks(val attribute: String, val effect: ((Player) -> Unit)? Skillcape.WOODCUTTING -> NEST_HUNTER Skillcape.FARMING -> SEED_ATTRACTION Skillcape.HUNTING -> NONE - Skillcape.SUMMONING -> NONE + Skillcape.SUMMONING -> PET_MASTERY else -> NONE } } @@ -184,12 +193,20 @@ enum class SkillcapePerks(val attribute: String, val effect: ((Player) -> Unit)? else -> null } + end() if(spellbook != null){ + if (spellbook == SpellBookManager.SpellBook.ANCIENT) { + if (!hasRequirement(player, Quests.DESERT_TREASURE)) + return true + } + else if (spellbook == SpellBookManager.SpellBook.LUNAR) { + if (!hasRequirement(player, Quests.LUNAR_DIPLOMACY)) + return true + } player.spellBookManager.setSpellBook(spellbook) player.interfaceManager.openTab(Component(spellbook.interfaceId)) player.incrementAttribute("/save:cape_perks:librarian-magus-charges",-1) } - end() return true } @@ -206,45 +223,70 @@ enum class SkillcapePerks(val attribute: String, val effect: ((Player) -> Unit)? } override fun open(vararg args: Any?): Boolean { - options("Air","Mind","Water","Earth","More...") - stage = 0; + 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() - player.teleporter.send(altar.ruin.end,TeleportManager.TeleportType.TELE_OTHER) + 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)) { + 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 9382a408d..aad0fa5e4 100644 --- a/Server/src/main/content/global/skill/slayer/Master.java +++ b/Server/src/main/content/global/skill/slayer/Master.java @@ -10,36 +10,38 @@ 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.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.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), @@ -59,221 +61,191 @@ public enum Master { 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.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.VAMPIRES, 6), - //new Task(Tasks.WALL_BEASTS,7), + // new Task(Tasks.WALL_BEASTS,7), new Task(Tasks.WOLVES, 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.BASILISKS,8), new Task(Tasks.BLUE_DRAGONS,7), + new Task(Tasks.BLOODVELDS,8), 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.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 Task(Tasks.ELVES, 7), + 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.GARGOYLES, 5), new Task(Tasks.GHOULS,7), - new Task(Tasks.GREEN_DRAGON,6), + 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.HOBGOBLINS,7), - new Task(Tasks.ICE_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.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.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.SEA_SNAKES,6), 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.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.WALL_BEASTS,6), + 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.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.BLOODVELDS,8), new Task(Tasks.BRINE_RATS,7), - new Task(Tasks.BRONZE_DRAGONS,11), + 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 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.HELLHOUNDS,9), + new Task(Tasks.IRON_DRAGONS,12, new Integer[]{30, 60}), new Task(Tasks.INFERNAL_MAGES,7), - new Task(Tasks.IRON_DRAGONS,12), new Task(Tasks.JELLIES, 10), 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.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.ROCK_SLUGS, 5), - //new Task(Tasks.SHADOW_WARRIORS,8), - new Task(Tasks.SKELETAL_WYVERN,7), + // 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.STEEL_DRAGONS,9), 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.AVIANSIES, 10), 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_DRAGON, 5), - new Task(Tasks.SCORPIONS, 4), + new Task(Tasks.RED_DRAGONS, 5), + // 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}, + 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.ANKOU,5), - new Task(Tasks.AVIANSIES,8), - new Task(Tasks.BLACK_DEMONS,8), - new Task(Tasks.BLACK_DRAGONS,9), + new Task(Tasks.AVIANSIES, 10), + new Task(Tasks.BLACK_DEMONS,8), + new Task(Tasks.BLACK_DRAGONS,9, new Integer[]{40, 80}), 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.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.FIRE_GIANTS,7), new Task(Tasks.GARGOYLES,8), + new Task(Tasks.GORAKS,9), new Task(Tasks.GREATER_DEMONS,9), new Task(Tasks.HELLHOUNDS, 10), - new Task(Tasks.IRON_DRAGONS,5), + new Task(Tasks.IRON_DRAGONS,5, new Integer[]{40, 80}), new Task(Tasks.KALPHITES,9), - new Task(Tasks.KURASKS,4), - new Task(Tasks.MITHRIL_DRAGON,9), + new Task(Tasks.MITHRIL_DRAGONS,9, new Integer[]{4, 8}), new Task(Tasks.NECHRYAELS,9), - //new Task(Tasks.RED_DRAGONS,8), - new Task(Tasks.SKELETAL_WYVERN,7), + // 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.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.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)); - //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)); - 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 +273,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; } } -} \ No newline at end of file +} diff --git a/Server/src/main/content/global/skill/slayer/MithrilDragonNPC.java b/Server/src/main/content/global/skill/slayer/MithrilDragonNPC.java index 10c2d3588..aa9e96601 100644 --- a/Server/src/main/content/global/skill/slayer/MithrilDragonNPC.java +++ b/Server/src/main/content/global/skill/slayer/MithrilDragonNPC.java @@ -92,6 +92,6 @@ public final class MithrilDragonNPC extends AbstractNPC { @Override public int[] getIds() { - return Tasks.MITHRIL_DRAGON.getNpcs(); + return Tasks.MITHRIL_DRAGONS.getNpcs(); } } 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/NechryaelBehavior.kt b/Server/src/main/content/global/skill/slayer/NechryaelBehavior.kt new file mode 100644 index 000000000..da44565a0 --- /dev/null +++ b/Server/src/main/content/global/skill/slayer/NechryaelBehavior.kt @@ -0,0 +1,104 @@ +package content.global.skill.slayer + +import core.api.animate +import core.api.getAttribute +import core.api.setAttribute +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.npc.NPC +import core.game.node.entity.npc.NPCBehavior +import core.game.node.entity.player.Player +import core.game.world.GameWorld +import core.tools.RandomFunction +import org.rs09.consts.NPCs + +class NechryaelBehavior : NPCBehavior(*Tasks.NECHRYAELS.npcs) { + private val ATTR_SPAWNS = "deathSpawns" + private val ATTR_NEXTSPAWN = "deathSpawnNextTick" + + override fun afterDamageReceived(self: NPC, attacker: Entity, state: BattleState) { + if (attacker !is Player) return + if (!canSpawnDeathspawn(self)) return + if (!RandomFunction.roll(5)) return + spawnDeathSpawn(self, attacker) + } + + fun spawnDeathSpawn(self: NPC, player: Player) { + val npc = NPC.create(NPCs.DEATH_SPAWN_1614, self.location.transform(self.direction, 1)) + setAttribute(npc, "parent", self) + setAttribute(npc, "target", player) + npc.isRespawn = false + npc.init() + addSpawn(self, npc) + setNextSpawn(self) + animate(self, 9491) + } + + fun canSpawnDeathspawn(self: NPC) : Boolean { + if (getSpawns(self).size >= 2) { + setNextSpawn(self) + return false + } + return getNextSpawn(self) <= GameWorld.ticks + } + + fun getNextSpawn(self: NPC) : Int { + return getAttribute(self, ATTR_NEXTSPAWN, 0) + } + + fun setNextSpawn(self: NPC) { + setAttribute(self, ATTR_NEXTSPAWN, GameWorld.ticks + 50) + } + + fun getSpawns(self: NPC) : ArrayList { + return getAttribute(self, ATTR_SPAWNS, ArrayList()) + } + + fun addSpawn(self: NPC, spawn: NPC) { + val list = getSpawns(self) + list.add(spawn) + setAttribute(self, ATTR_SPAWNS, list) + } + + fun removeSpawn(self: NPC, spawn: NPC) { + val list = getSpawns(self) + list.remove(spawn) + setAttribute(self, ATTR_SPAWNS, list) + } + + override fun shouldIgnoreMultiRestrictions(self: NPC, victim: Entity): Boolean { + val list = getSpawns(self) + return victim == self.properties.combatPulse.getVictim() || list.contains(victim.properties.combatPulse.getVictim()) + } +} + +class DeathspawnBehavior : NPCBehavior(NPCs.DEATH_SPAWN_1614) { + override fun onCreation(self: NPC) { + setAttribute(self, "despawn-time", GameWorld.ticks + 100) + val target = getAttribute(self, "target", null) ?: return + self.attack(target) + } + + override fun onRemoval(self: NPC) { + val parent = getAttribute(self, "parent", null) ?: return + (parent.behavior as? NechryaelBehavior)?.let { it.removeSpawn (parent, self) } + } + + override fun tick(self: NPC): Boolean { + val target = getAttribute(self, "target", null) ?: return true + + if (!target.isActive || DeathTask.isDead(target) || getAttribute(self, "despawn-time", 0) <= GameWorld.ticks) + self.clear() + return true + } + + override fun shouldIgnoreMultiRestrictions(self: NPC, victim: Entity): Boolean { + return victim == getAttribute(self, "target", null) + } + + override fun canBeAttackedBy(self: NPC, attacker: Entity, style: CombatStyle, shouldSendMessage: Boolean): Boolean { + return attacker == getAttribute(self, "target", null) + } +} diff --git a/Server/src/main/content/global/skill/slayer/NechryaelNPC.java b/Server/src/main/content/global/skill/slayer/NechryaelNPC.java deleted file mode 100644 index 2b04eaa0b..000000000 --- a/Server/src/main/content/global/skill/slayer/NechryaelNPC.java +++ /dev/null @@ -1,193 +0,0 @@ -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.npc.AbstractNPC; -import core.game.node.entity.player.Player; -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.tools.RandomFunction; - -import java.util.ArrayList; -import java.util.List; - -/** - * Handles the nechryael npc. - * @author Vexia - */ -@Initializable -public final class NechryaelNPC extends AbstractNPC { - - /** - * The death spawn id. - */ - private static final int DEATH_SPAWN = 1614; - - /** - * The death spawn npcs. - */ - private List spawns = new ArrayList<>(10); - - /** - * The next spawn time. - */ - private int nextSpawn; - - /** - * Constructs a new {@code NechryaelNPC} {@code Object}. - * @param id the id. - * @param location the location. - */ - public NechryaelNPC(int id, Location location) { - super(id, location); - } - - /** - * Constructs a new {@code NechryaelNPC} {@code Object}. - */ - public NechryaelNPC() { - super(0, null); - } - - @Override - public void onImpact(Entity entity, final BattleState state) { - super.onImpact(entity, state); - if (entity instanceof Player && RandomFunction.random(5) == 1) { - spawn((Player) entity); - } - } - - /** - * Spawns a death spawn. - * @param player the player. - */ - public void spawn(Player player) { - if (!canSpawn()) { - return; - } - final DeathSpawnNPC spawn = new DeathSpawnNPC(DEATH_SPAWN, getLocation().transform(getDirection(), 1), player, this); - spawn.init(); - setSpawnTime(); - spawns.add(spawn); - animate(Animation.create(9491)); - spawn.getProperties().getCombatPulse().attack(player); - } - - /** - * Sets the next spawn time. - */ - private void setSpawnTime() { - nextSpawn = GameWorld.getTicks() + 50; - } - - /** - * Checks if a death spawn can spawn. - * @return {@code True} if so. - */ - private boolean canSpawn() { - if (spawns.size() >= 2) { - setSpawnTime(); - return false; - } - return nextSpawn < GameWorld.getTicks(); - } - - @Override - public AbstractNPC construct(int id, Location location, Object... objects) { - return new NechryaelNPC(id, location); - } - - @Override - public int[] getIds() { - return Tasks.NECHRYAELS.getNpcs(); - } - - /** - * Handles the death spawn npc. - * @author Vexia - */ - public static final class DeathSpawnNPC extends AbstractNPC { - - /** - * The player binded with the death spawn. - */ - private final Player player; - - /** - * The parent npc. - */ - private final NechryaelNPC parent; - - /** - * The time until its cleared. - */ - private int time; - - /** - * Constructs a new {@code DeathSpawnNPC} {@code Object}. - * @param id the id. - * @param location the location. - */ - public DeathSpawnNPC(int id, Location location, final Player player, NechryaelNPC parent) { - super(id, location); - this.player = player; - this.parent = parent; - this.setRespawn(false); - this.time = GameWorld.getTicks() + 120; - } - - @Override - public void handleTickActions() { - super.handleTickActions(); - if (!inCombat() || !player.inCombat()) { - getProperties().getCombatPulse().attack(player); - } - if (time < GameWorld.getTicks() || !player.isActive() || player.getLocation().getDistance(getLocation()) > 15) { - clear(); - } - } - - @Override - public void clear() { - super.clear(); - parent.spawns.remove(this); - } - - @Override - public AbstractNPC construct(int id, Location location, Object... objects) { - return new DeathSpawnNPC(id, location, null, null); - } - - @Override - public boolean isAttackable(Entity entity, CombatStyle style, boolean message) { - if (entity instanceof Player) { - final Player t = (Player) entity; - if (t != player) { - if(message) { - t.getPacketDispatch().sendMessage("This isn't spawned for you."); - } - return false; - } - } - return super.isAttackable(entity, style, message); - } - - @Override - public boolean isIgnoreMultiBoundaries(Entity victim) { - return victim == player; - } - - @Override - public boolean isPoisonImmune() { - return true; - } - - @Override - public int[] getIds() { - return new int[] { DEATH_SPAWN }; - } - } -} diff --git a/Server/src/main/content/global/skill/slayer/RockSlug.kt b/Server/src/main/content/global/skill/slayer/RockSlug.kt new file mode 100644 index 000000000..58dfb49c2 --- /dev/null +++ b/Server/src/main/content/global/skill/slayer/RockSlug.kt @@ -0,0 +1,50 @@ +package content.global.skill.slayer + +import core.api.* +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.combat.BattleState +import core.game.node.entity.combat.ImpactHandler +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 java.lang.Integer.max + +class RockSlug : NPCBehavior(*Tasks.ROCK_SLUGS.ids), InteractionListener { + override fun defineListeners() { + onUseWith(IntType.NPC, Items.BAG_OF_SALT_4161, *ids, handler = ::handleSaltUsage) + } + + override fun beforeDamageReceived(self: NPC, attacker: Entity, state: BattleState) { + val lifepoints = self.skills.lifepoints + if (state.estimatedHit + max(state.secondaryHit, 0) > lifepoints - 1) { + state.estimatedHit = lifepoints - 1 + state.secondaryHit = -1 + setAttribute(self, "shouldRun", true) + } + } + + override fun tick(self: NPC): Boolean { + if (getAttribute(self, "shouldRun", false)){ + self.properties.combatPulse.stop() + forceWalk(self, self.properties.spawnLocation, "smart") + removeAttribute(self, "shouldRun") + } + return true + } + + private fun handleSaltUsage(player: Player, used: Node, with: Node) : Boolean { + if (with !is NPC) return false + if (!removeItem(player, used.id)) return false + if (with.skills.lifepoints >= 5) + sendMessage(player, "Your bag of salt is ineffective. The Rockslug is not weak enough.") + else { + sendMessage(player, "The Rockslug shrivels up and dies.") + with.impactHandler.manualHit(player, with.skills.lifepoints, ImpactHandler.HitsplatType.NORMAL) + } + return true + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/slayer/RockSlugPlugin.java b/Server/src/main/content/global/skill/slayer/RockSlugPlugin.java deleted file mode 100644 index a5428a7ba..000000000 --- a/Server/src/main/content/global/skill/slayer/RockSlugPlugin.java +++ /dev/null @@ -1,148 +0,0 @@ -package content.global.skill.slayer; - -import core.game.interaction.NodeUsageEvent; -import core.game.interaction.UseWithHandler; -import core.game.node.entity.combat.BattleState; -import core.game.node.entity.combat.ImpactHandler.HitsplatType; -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.item.Item; -import core.game.world.map.Location; -import core.game.world.map.path.Pathfinder; -import core.plugin.Initializable; -import core.plugin.Plugin; -import core.plugin.ClassScanner; - -/** - * Handles the interactions of a rock slug. - * @author Vexia - */ -@Initializable -public final class RockSlugPlugin implements Plugin { - - /** - * The bag of salt item. - */ - private static final Item SALT = new Item(4161, 1); - - /** - * The rockslug npc ids. - */ - private static final int[] IDS = new int[] { 1631, 1632 }; - - @Override - public Plugin newInstance(Object arg) throws Throwable { - ClassScanner.definePlugin(new RockSlugNPC()); - ClassScanner.definePlugin(new SaltBagHandler()); - return this; - } - - @Override - public Object fireEvent(String identifier, Object... args) { - return null; - } - - /** - * The use with handler for the bag of salt on a rock slug. - * @author Vexia - */ - public final class SaltBagHandler extends UseWithHandler { - - /** - * Constructs a new {@code SaltBagHandler} {@code Object}. - */ - public SaltBagHandler() { - super(SALT.getId()); - } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - for (int id : IDS) { - addHandler(id, NPC_TYPE, this); - } - return this; - } - - @Override - public boolean handle(NodeUsageEvent event) { - final Player player = event.getPlayer(); - final NPC npc = (NPC) event.getUsedWith(); - player.getInventory().remove(SALT); - if (npc.getSkills().getLifepoints() < 10) { - npc.getImpactHandler().manualHit(player, npc.getSkills().getLifepoints(), HitsplatType.NORMAL); - player.getPacketDispatch().sendMessage("The rockslug shrivels up and dies."); - } else { - player.sendMessage("Your bag of salt is ineffective. The Rockslug is not weak enough."); - } - return true; - } - - } - - /** - * The rock slug npc. - * @author Vexia - */ - public final class RockSlugNPC extends AbstractNPC { - - /** - * Constructs a new {@code RockSlugNPC} {@code Object}. - */ - public RockSlugNPC() { - super(-1, null); - } - - /** - * Constructs a new {@code RockSlugNPC} {@code Object}. - * @param id the id. - * @param location the location. - */ - public RockSlugNPC(int id, Location location) { - super(id, location, true); - } - - @Override - public AbstractNPC construct(int id, Location location, Object... objects) { - return new RockSlugNPC(id, location); - } - - @Override - public void checkImpact(BattleState state) { - super.checkImpact(state); - int lifepoints = getSkills().getLifepoints(); - boolean run = false; - if (state.getEstimatedHit() > -1) { - lifepoints -= state.getEstimatedHit(); - if (lifepoints < 1) { - run = true; - state.setEstimatedHit(lifepoints - 1); - } - if (state.getEstimatedHit() < 0) { - state.setEstimatedHit(0); - } - } - if (state.getSecondaryHit() > -1) { - lifepoints -= state.getSecondaryHit(); - if (lifepoints < 1) { - run = true; - state.setSecondaryHit(lifepoints - 1); - } - if (state.getSecondaryHit() < 0) { - state.setSecondaryHit(0); - } - } - if (run) { - getProperties().getCombatPulse().stop(); - Pathfinder.find(getLocation(), getProperties().getSpawnLocation()); - } - } - - @Override - public int[] getIds() { - return IDS; - } - - } - -} diff --git a/Server/src/main/content/global/skill/slayer/SkeletalWyvernBehavior.kt b/Server/src/main/content/global/skill/slayer/SkeletalWyvernBehavior.kt new file mode 100644 index 000000000..437e2e6ad --- /dev/null +++ b/Server/src/main/content/global/skill/slayer/SkeletalWyvernBehavior.kt @@ -0,0 +1,37 @@ +package content.global.skill.slayer + +import content.global.handlers.item.equipment.special.DragonfireSwingHandler +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 core.game.node.entity.player.Player +import core.game.world.update.flag.context.Animation +import core.game.world.update.flag.context.Graphics +import org.rs09.consts.Items + +class SkeletalWyvernBehavior : NPCBehavior(*Tasks.SKELETAL_WYVERN.ids) { + /** + * The combat swing handler. + */ + private val COMBAT_HANDLER = MultiSwingHandler(SwitchAttack(CombatStyle.MELEE.swingHandler, Animation(2985)), SwitchAttack(CombatStyle.RANGE.swingHandler, Animation(2989), Graphics(499)), DragonfireSwingHandler.get(false, 54, Animation(2988), Graphics(501), null, null, false)) + + /** + * The combat swing handler for far combat (5+ tile distance) + */ + private val COMBAT_HANDLER_FAR = MultiSwingHandler(SwitchAttack(CombatStyle.RANGE.swingHandler, Animation(2989), Graphics(499))) + + private val SHIELDS = intArrayOf(Items.DRAGONFIRE_SHIELD_11283, Items.DRAGONFIRE_SHIELD_11285, Items.ELEMENTAL_SHIELD_2890, Items.MIND_SHIELD_9731) + + 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) >= 5) + COMBAT_HANDLER_FAR + else + COMBAT_HANDLER + } +} diff --git a/Server/src/main/content/global/skill/slayer/SkeletalWyvernNPC.java b/Server/src/main/content/global/skill/slayer/SkeletalWyvernNPC.java deleted file mode 100644 index 3ca02b5ce..000000000 --- a/Server/src/main/content/global/skill/slayer/SkeletalWyvernNPC.java +++ /dev/null @@ -1,90 +0,0 @@ -package content.global.skill.slayer; - -import core.game.container.impl.EquipmentContainer; -import core.game.node.entity.combat.BattleState; -import core.game.node.entity.combat.CombatStyle; -import core.game.node.entity.combat.equipment.SwitchAttack; -import content.global.handlers.item.equipment.special.DragonfireSwingHandler; -import core.game.node.entity.npc.AbstractNPC; -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.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 core.game.node.entity.combat.CombatSwingHandler; -import core.game.node.entity.combat.MultiSwingHandler; - -/** - * Handles the skeletal wyvern npc. - * @author Vexia - * @author Splinter - * @version 1.0 - */ -@Initializable -public final class SkeletalWyvernNPC extends AbstractNPC { - - /** - * The combat swing handler. - */ - private static final MultiSwingHandler COMBAT_HANDLER = new MultiSwingHandler(new SwitchAttack(CombatStyle.MELEE.getSwingHandler(), new Animation(2985)), new SwitchAttack(CombatStyle.RANGE.getSwingHandler(), new Animation(2989), new Graphics(499)), DragonfireSwingHandler.get(false, 54, new Animation(2988), new Graphics(501), null, null, false)); - - /** - * The combat swing handler for far combat (5+ tile distance) - */ - private static final MultiSwingHandler COMBAT_HANDLER_FAR = new MultiSwingHandler(new SwitchAttack(CombatStyle.RANGE.getSwingHandler(), new Animation(2989), new Graphics(499))); - - /** - * Constructs a new {@code SkeletalWyvernNPC} {@code Object}. - * @param id the id. - * @param location the location. - */ - public SkeletalWyvernNPC(int id, Location location) { - super(id, location); - } - - /** - * Constructs a new {@code SkeletalWyvernNPC} {@code Object}. - */ - public SkeletalWyvernNPC() { - super(0, null); - } - - @Override - public AbstractNPC construct(int id, Location location, Object... objects) { - return new SkeletalWyvernNPC(id, location); - } - - @Override - public void sendImpact(BattleState state) { - if (state.getStyle() == CombatStyle.MAGIC && state.getVictim() != null && state.getVictim().isPlayer()) { - Player p = state.getVictim().asPlayer(); - Item item = p.getEquipment().get(EquipmentContainer.SLOT_SHIELD); - if (item != null && (item.getId() == 2890 || item.getId() == 9731 || item.getId() == 11283) && state.getEstimatedHit() > 10) { - state.setEstimatedHit(RandomFunction.random(10)); - } - } - Player p = state.getVictim().asPlayer(); - Item item = p.getEquipment().get(EquipmentContainer.SLOT_SHIELD); - if(state.getVictim().getLocation().getDistance(state.getAttacker().getLocation()) >= 5 - && state.getVictim().asPlayer().getPrayer().get(PrayerType.PROTECT_FROM_MAGIC) && (item.getId() == 2890 || item.getId() == 9731 || item.getId() == 11283)){ - state.setEstimatedHit(0); - } - } - - @Override - public CombatSwingHandler getSwingHandler(boolean swing) { - if (this.getProperties().getCombatPulse().getVictim() != null && this.getProperties().getCombatPulse().getVictim().getLocation().getDistance(this.getLocation()) >= 5){ - return COMBAT_HANDLER_FAR; - } - return COMBAT_HANDLER; - } - - @Override - public int[] getIds() { - return Tasks.SKELETAL_WYVERN.getNpcs(); - } - -} diff --git a/Server/src/main/content/global/skill/slayer/SlayerEquipmentFlags.kt b/Server/src/main/content/global/skill/slayer/SlayerEquipmentFlags.kt index ca08db7ca..e8c5e01e6 100644 --- a/Server/src/main/content/global/skill/slayer/SlayerEquipmentFlags.kt +++ b/Server/src/main/content/global/skill/slayer/SlayerEquipmentFlags.kt @@ -24,10 +24,10 @@ object SlayerEquipmentFlags { else if(hasItem(player, Items.NOSE_PEG_4168)) flags = 1 else if(hasItem(player, Items.EARMUFFS_4166)) flags = flags or (1 shl 1) else if(hasItem(player, Items.FACE_MASK_4164)) flags = flags or (1 shl 2) - else if((getItemFromEquipment(player, EquipmentSlot.HAT)?.id ?: 0) in blackMasks) flags = flags or (1 shl 3) + else if((getItemFromEquipment(player, EquipmentSlot.HEAD)?.id ?: 0) in blackMasks) flags = flags or (1 shl 3) else if(hasItem(player, Items.SPINY_HELMET_4551)) flags = flags or (1 shl 4) - if((getItemFromEquipment(player, EquipmentSlot.AMULET)?.id ?: 0) == Items.WITCHWOOD_ICON_8923) flags = flags or (1 shl 7) + if((getItemFromEquipment(player, EquipmentSlot.NECK)?.id ?: 0) == Items.WITCHWOOD_ICON_8923) flags = flags or (1 shl 7) if((getItemFromEquipment(player, EquipmentSlot.SHIELD)?.id ?: 0) == Items.MIRROR_SHIELD_4156) flags = flags or (1 shl 8) SlayerManager.getInstance(player).flags.equipmentFlags = flags } @@ -72,13 +72,13 @@ object SlayerEquipmentFlags { val isCape = SlayerManager.getInstance(player).flags.equipmentFlags == 0x3F val hasMask = hasBlackMask(player) - return if(isCape) 1.075 - else if(hasMask) 1.15 + return if(hasMask) 1.1667 + else if(isCape) 1.075 else 1.0 } private fun hasItem(player: Player, id: Int): Boolean{ - return (getItemFromEquipment(player, EquipmentSlot.HAT)?.id ?: 0) == id + return (getItemFromEquipment(player, EquipmentSlot.HEAD)?.id ?: 0) == id } fun isSlayerEq(item: Int): Boolean{ diff --git a/Server/src/main/content/global/skill/slayer/SlayerManager.kt b/Server/src/main/content/global/skill/slayer/SlayerManager.kt index 51c523592..f0ff71ed5 100644 --- a/Server/src/main/content/global/skill/slayer/SlayerManager.kt +++ b/Server/src/main/content/global/skill/slayer/SlayerManager.kt @@ -1,19 +1,19 @@ package content.global.skill.slayer -import core.api.LoginListener -import core.api.PersistPlayer +import content.global.handlers.item.equipment.fistofguthixgloves.FOGGlovesManager +import core.api.* import core.game.event.EventHook import core.game.event.NPCKillEvent -import core.api.getAttribute -import core.api.rewardXP import core.cache.def.impl.NPCDefinition import core.game.node.entity.Entity import core.game.node.entity.player.Player import core.game.node.entity.skill.Skills import org.json.simple.JSONArray import org.json.simple.JSONObject -import core.api.Event import java.util.* +import org.rs09.consts.Items + +import content.global.skill.hunter.HunterNPC /** * Manages the players slayer data. @@ -103,9 +103,18 @@ class SlayerManager(val player: Player? = null) : LoginListener, PersistPlayer, val player = entity as? Player ?: return val slayer = getInstance(player) val flags = slayer.flags + var xp = npc.skills.maximumLifepoints.toDouble() + //Snowscape custom: grant a third of the normal slayer exp on all kills (this stacks with the exp from on-task kills) + if (npc !is HunterNPC) + rewardXP(player, Skills.SLAYER, xp/3) if (slayer.hasTask() && npc.id in slayer.task!!.npcs) { - rewardXP(player, Skills.SLAYER, npc.skills.maximumLifepoints.toDouble()) + + if (slayer.task!!.dragon && inEquipment(player, Items.DRAGON_SLAYER_GLOVES_12862)) { + xp *= 1.15 + FOGGlovesManager.updateCharges(player) + } + rewardXP(player, Skills.SLAYER, xp) slayer.decrementAmount(1) if(slayer.hasTask()) return flags.taskStreak = flags.taskStreak + 1 @@ -170,6 +179,13 @@ class SlayerManager(val player: Player? = null) : LoginListener, PersistPlayer, flags.setTask(task!!) } + var activeTask: Tasks? = null + get() { + if (hasTask()) + return flags.getTask() + return null + } + var master: Master? get() = flags.getMaster() set(master) { @@ -199,7 +215,7 @@ class SlayerManager(val player: Player? = null) : LoginListener, PersistPlayer, fun decrementAmount(amount: Int) { flags.decrementTaskAmount(amount) - player!!.varpManager.get(2502).setVarbit(0, flags.taskFlags shr 4).send(player) + setVarp(player!!, 2502, flags.taskFlags shr 4) } /** @@ -238,4 +254,4 @@ class SlayerManager(val player: Player? = null) : LoginListener, PersistPlayer, return getAttribute(player, "slayer-manager", SlayerManager()) } } -} \ No newline at end of file +} diff --git a/Server/src/main/content/global/skill/slayer/SlayerMasterDialogue.java b/Server/src/main/content/global/skill/slayer/SlayerMasterDialogue.java index a1f9d9ea9..cb6aa6570 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. @@ -98,7 +100,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 +120,7 @@ public final class SlayerMasterDialogue extends DialoguePlugin { @Override public boolean handle(int interfaceId, int buttonId) { - rerolls = ServerStore.getInt(getStoreFile(), player.getUsername().toLowerCase()); + rerolls = ServerStore.getInt(getStoreFile(), player.getUsername().toLowerCase(), 0); if (isDiary) { switch (stage) { case 999: @@ -335,7 +337,7 @@ public final class SlayerMasterDialogue extends DialoguePlugin { case 502: if (!master.hasRequirements(player)) { interpreter.sendDialogues(master.getNpc(), getExpression(master), "Sorry, but you're not strong enough to be taught by", "me."); - stage = 99; + stage = 999; break; } interpreter.sendDialogues(master.getNpc(), getExpression(master), "Oh, okay then; you twisted my arm. You'll have to", "train against specific groups of creatures."); @@ -349,7 +351,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."); @@ -477,7 +479,7 @@ public final class SlayerMasterDialogue extends DialoguePlugin { case 701: if (!master.hasRequirements(player)) { interpreter.sendDialogues(master.getNpc(), getExpression(master), "Sorry, but you're not strong enough to be taught by", "me."); - stage = 99; + stage = 999; break; } if (!SlayerManager.getInstance(player).hasTask()) { @@ -485,7 +487,7 @@ public final class SlayerMasterDialogue extends DialoguePlugin { 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; @@ -499,7 +501,7 @@ public final class SlayerMasterDialogue extends DialoguePlugin { 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; } diff --git a/Server/src/main/content/global/skill/slayer/SlayerPlugin.java b/Server/src/main/content/global/skill/slayer/SlayerPlugin.java index ec6940b15..53e426aa3 100644 --- a/Server/src/main/content/global/skill/slayer/SlayerPlugin.java +++ b/Server/src/main/content/global/skill/slayer/SlayerPlugin.java @@ -11,6 +11,9 @@ import core.game.world.map.Location; import core.plugin.Initializable; import core.plugin.Plugin; +import static core.api.ContentAPIKt.hasRequirement; +import content.data.Quests; + /** * Handles related slayer nodes. * @author Vexia @@ -25,7 +28,6 @@ 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); @@ -50,9 +52,6 @@ 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: player.teleport(new Location(2543, 3327, 0)); break; @@ -61,6 +60,8 @@ public class SlayerPlugin extends OptionHandler { player.teleport(new Location(2729, 3733, 0)); break; case 15767: + if (!hasRequirement(player, Quests.CABIN_FEVER)) + return true; player.teleport(new Location(3748, 9373, 0)); break; case 15811: diff --git a/Server/src/main/content/global/skill/slayer/SlayerRewardPlugin.java b/Server/src/main/content/global/skill/slayer/SlayerRewardPlugin.java index d8231167e..247e87b82 100644 --- a/Server/src/main/content/global/skill/slayer/SlayerRewardPlugin.java +++ b/Server/src/main/content/global/skill/slayer/SlayerRewardPlugin.java @@ -16,6 +16,9 @@ import core.plugin.Initializable; import core.plugin.Plugin; import core.plugin.ClassScanner; +import static core.api.ContentAPIKt.hasRequirement; +import content.data.Quests; + /** * Handles the slayer reward interface plugin. * @author Vexia @@ -307,6 +310,8 @@ public class SlayerRewardPlugin extends ComponentPlugin { @Override public boolean handle(Player player, Node node, String option) { + 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 14b00d7f3..c034fc557 100644 --- a/Server/src/main/content/global/skill/slayer/SlayerUtils.kt +++ b/Server/src/main/content/global/skill/slayer/SlayerUtils.kt @@ -1,18 +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 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) @@ -23,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 @@ -31,20 +29,23 @@ object SlayerUtils { fun canBeAssigned(player: Player, task: Tasks): Boolean { - return player.getSkills().getLevel(Skills.SLAYER) >= task.levelReq && !SlayerManager.getInstance(player).flags.removed.contains(task) + 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) { player.achievementDiaryManager.finishTask(player, DiaryType.VARROCK, 1, 14) } - player.varpManager.get(2502).setVarbit(0, SlayerManager.getInstance(player).flags.taskFlags shr 4).send(player) + setVarp(player, 2502, SlayerManager.getInstance(player).flags.taskFlags shr 4) } @JvmStatic @@ -56,4 +57,24 @@ object SlayerUtils { .spellBook == SpellBook.MODERN.interfaceId ) } -} \ No newline at end of file + + @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 efd737e30..81a195f5a 100644 --- a/Server/src/main/content/global/skill/slayer/Tasks.java +++ b/Server/src/main/content/global/skill/slayer/Tasks.java @@ -5,103 +5,135 @@ import core.cache.def.impl.NPCDefinition; import java.util.Arrays; 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), - 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), - ANKOU(40, new int[] { 4381, 4382, 4383 }, new String[] { "Neither skeleton nor ghost, but a combination of both." }, 1, true), - 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), - 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), - BASILISKS(40, new int[] { 1616, 1617, 4228 }, new String[] { "A mirror shield is much neccesary when hunting these", "mad creatures." }, 40, 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), - BEARS(13, new int[] { 106, 105, 1195, 3645, 3664, 1326, 1327 }, new String[] { "A large animal with a crunching punch." }, 1, 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), - 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), - 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, 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), - 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), - BRINE_RATS(45, new int[] { 3707 }, new String[] { "Small little creatures they are, yet so very", "powerfull." }, 47, 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, 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), - CAVE_BUG(1, new int[] { 1832, 5750, }, new String[] { "It regenerates life points quickly and seems to be a good", "herblore monster." }, 7, false), - CAVE_CRAWLERS(10, new int[] { 1600, 1601, 1602, 1603, }, new String[] { "The poisonous parts of them are presumably removed." }, 10, 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, false), - CAVE_SLIMES(15, new int[] { 1831 }, new String[] { "These are lesser versions of jellies, watch out they can poison you." }, 17, false), - COCKATRICES(25, new int[] { 1620, 1621, 4227, }, new String[] { "A Mirror shield is necessary when fighting these monsters." }, 25, 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), - 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), - CROCODILES(50, new int[] { 1993, 6779 }, new String[] { "Crocodiles can be found near water and marshes in and near the Kharidian Desert." }, 1, 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), - 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), - DARK_BEASTS(90, new int[] { 2783 }, new String[] { "A dark beast can attack using magic or melee." }, 90, 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), - DOG(15, new int[] { 98, 99, 3582, 6374, 1994, 1593, 1594, 3582, 6374 }, new String[] { "Dogs are much like Wolves, they are pack creatures which will hunt in groups." }, 1, 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), - 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), - EARTH_WARRIORS(35, new int[] { 124 }, new String[] { "An Earth warrior is a monster made of earth which fights using melee." }, 1, false), - ELVES(70, new int[] { 1183, 1184, 2359, 2360, 2361, 2362, 7438, 7439, 7440, 7441 }, new String[]{"Elves are agile creatures."}, 1, 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), - 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), - 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), - 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), - 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), - 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), - GORAKS(70, new int[] { 4418, 6218 }, new String[] { "Goraks can be tough monsters to fight. Be prepared." }, 1, 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), - GREEN_DRAGON(52, new int[] { 941, 4677, 4678, 4679, 4680, 5362 }, new String[] { "Green dragons are very powerfull, they have fierce", "fiery breath." }, 1, false), - HARPIE_BUG_SWARMS(45, new int[] { 3153 }, new String[] { "Harpie Bug Swarms are insectoid Slayer monsters." }, 33, false), - HELLHOUNDS(75, new int[] { 49, 3586, 6210, }, new String[] { "Hellhounds are mid to high level demons." }, 1, 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), - 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), - 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), - 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), - ICE_WARRIOR(45, new int[] { 125, 145, 3073 }, new String[] { "Ice warriors, are cold magestic creatures." }, 1, 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), - 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, 40 | 59 << 16), - 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), - 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), - 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), - KURASKS(65, new int[] { 1608, 1609, 4229, 7805, 7797 }, new String[] { "A kurask is a very quick creature." }, 70, 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), - MITHRIL_DRAGON(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, 5 | 9 << 16), - 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), - 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), - MOSS_GIANTS(40, new int[] { 112, 1587, 1588, 1681, 4534, 4688, 4706 }, new String[] { "They are known to carry large sticks." }, 1, 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), - 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), - OTHERWORDLY_BEING(40, new int[] { 126 }, new String[] { "A creature filled with everlasting power." }, 1, 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), - 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), - ROCK_SLUGS(20, new int[] { 1631, 1632 }, new String[] { "A rock slug can leave behind a trail of his presence.." }, 20, false), - SCORPIONS(7, new int[] { 107, 1477, 4402, 4403, 144 }, new String[] { "A scorpion makes a piercing sound, watch out for it's", " long sharp tail." }, 1, 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), - 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), - 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), - SPIRTUAL_MAGES(60, new int[] { 6221, 6231, 6257, 6278 }, new String[] { "They are dangerous, they hit with mage." }, 83, false), - SPIRTUAL_RANGERS(60, new int[] { 6220, 6230, 6256, 6276 }, new String[] { "They are dangerous, they hit with range." }, 63, false), - SPIRTUAL_WARRIORS(60, new int[] { 6219, 6229, 6255, 6277, }, new String[] { "They are dangerous, they hit with melee." }, 68, 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, 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), - 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), - 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), - VAMPIRES(35, new int[] { 1023, 1220, 1223, 1225, 6214 }, new String[] { "Vampies arr equiped with large fangs", "they can do serious damage." }, 1, false), - WATERFIENDS(75, new int[] { 5361 }, new String[] { "A waterfiend takes no damage from fire!" }, 1, 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), - 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), - 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), - JAD(90, new int[] { }, new String[] { "TzTok-Jad is the king of the Fight Caves." }, 1, false, 1 | 1 << 16), - CHAOS_ELEMENTAL(90, new int[] { 3200 }, new String[] { "The Chaos Elemental is located in the deep Wilderness." }, 1, false, 5 | 25 << 16), - GIANT_MOLE(75, new int[] { 3340 }, new String[] { "Fighting the Giant Mole will require a light source." }, 1, 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, 5 | 25 << 16), - COMMANDER_ZILYANA(90, new int[] { 6247 }, new String[] { "Commander Zilyana is one of the four Godwars bosses." }, 1, false, 5 | 25 << 16), - GENERAL_GRARDOOR(90, new int[] { 6260 }, new String[] { "General Grardoor is one of the four Godwars bosses." }, 1, false, 5 | 25 << 16), - KRIL_TSUTSAROTH(90, new int[] { 6203 }, new String[] { "Kril Tsutsaroth is one of the four Godwars bosses." }, 1, false, 5 | 25 << 16), - KREE_ARRA(90, new int[] { 6222 }, new String[] { "Kree'arra is one of the four Godwars bosses." }, 1, 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, 24 | 39 << 16); + 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 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[] { "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 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, 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), + 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{ @@ -111,23 +143,24 @@ public enum Tasks { public final int combatCheck; public final String[] info; public final int[] ids; - public final boolean undead; - public int amtHash; - Tasks(int combatCheck, int[] ids, String[] info, int levelReq, boolean undead){ + public boolean undead = false; + public boolean dragon = false; + public Quests questReq = null; + Tasks(int combatCheck, int[] ids, String[] info, int levelReq, boolean undead, boolean dragon){ this.levelReq = levelReq; this.ids = ids; this.info = info; this.undead = undead; + this.dragon = dragon; this.combatCheck = combatCheck; } - Tasks(int combatCheck, int[] ids, String[] info, int levelReq, boolean undead, int amtHash){ - this.levelReq = levelReq; + Tasks (int combatCheck, int[] ids, String[] info, int levelReq, Quests questReq) { + this.combatCheck = combatCheck; this.ids = ids; this.info = info; - this.undead = undead; - this.amtHash = amtHash; - this.combatCheck = combatCheck; + this.levelReq = levelReq; + this.questReq = questReq; } public int[] getNpcs(){ @@ -138,6 +171,10 @@ public enum Tasks { return info; } + public boolean hasQuestRequirements (Player player) { + return questReq == null || hasRequirement(player, questReq, false); + } + public static Tasks forId(int id){ return taskMap.get(id); } diff --git a/Server/src/main/content/global/skill/slayer/TurothBehavior.kt b/Server/src/main/content/global/skill/slayer/TurothBehavior.kt new file mode 100644 index 000000000..c1cbfac4f --- /dev/null +++ b/Server/src/main/content/global/skill/slayer/TurothBehavior.kt @@ -0,0 +1,16 @@ +package content.global.skill.slayer + +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 + +class TurothBehavior : NPCBehavior(*Tasks.TUROTHS.ids) { + override fun beforeDamageReceived(self: NPC, attacker: Entity, state: BattleState) { + if (attacker is Player) { + if (!SlayerUtils.hasBroadWeaponEquipped(attacker, state)) + state.neutralizeHits() + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/slayer/TurothNPC.java b/Server/src/main/content/global/skill/slayer/TurothNPC.java deleted file mode 100644 index b046ea58e..000000000 --- a/Server/src/main/content/global/skill/slayer/TurothNPC.java +++ /dev/null @@ -1,58 +0,0 @@ -package content.global.skill.slayer; - -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.plugin.Initializable; - -/** - * Handles the turoth npc. - * @author Vexia - */ -@Initializable -public final class TurothNPC extends AbstractNPC { - - /** - * Constructs a new {@code TurothNPC} {@code Object}. - * @param id the id. - * @param location the location. - */ - public TurothNPC(int id, Location location) { - super(id, location); - } - - /** - * Constructs a new {@code TurothNPC} {@code Object}. - */ - public TurothNPC() { - super(0, null); - } - - @Override - public AbstractNPC construct(int id, Location location, Object... objects) { - return new TurothNPC(id, location); - } - - @Override - public void checkImpact(final BattleState state) { - super.checkImpact(state); - boolean effective = false; - if (state.getAttacker() instanceof Player) { - final Player player = (Player) state.getAttacker(); - effective = SlayerUtils.hasBroadWeaponEquipped(player, state); - } - if (!effective) { - state.setEstimatedHit(0); - if (state.getSecondaryHit() > 0) { - state.setSecondaryHit(0); - } - } - } - - @Override - public int[] getIds() { - return new int[] { 1626, 1627, 1628, 1629, 1630 }; - } - -} diff --git a/Server/src/main/content/global/skill/slayer/WaterFiendNPC.java b/Server/src/main/content/global/skill/slayer/WaterFiendNPC.java deleted file mode 100644 index 6f7fbd669..000000000 --- a/Server/src/main/content/global/skill/slayer/WaterFiendNPC.java +++ /dev/null @@ -1,58 +0,0 @@ -package content.global.skill.slayer; - -import core.game.node.entity.Entity; -import core.game.node.entity.combat.CombatStyle; -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.world.map.Location; -import core.game.world.update.flag.context.Animation; -import core.plugin.Initializable; -import core.game.node.entity.combat.CombatSwingHandler; -import core.game.node.entity.combat.MultiSwingHandler; - -/** - * Handles the water fiend npc. - * @author Vexia - */ -@Initializable -public final class WaterFiendNPC extends AbstractNPC { - - /** - * Handles the combat. - */ - private final CombatSwingHandler combatAction = new MultiSwingHandler(true, new SwitchAttack(CombatStyle.MAGIC.getSwingHandler(), new Animation(1581, Priority.HIGH), null, null, Projectile.create((Entity) null, null, 500, 15, 30, 50, 50, 14, 255)), new SwitchAttack(CombatStyle.RANGE.getSwingHandler(), new Animation(1581, Priority.HIGH), null, null, Projectile.create((Entity) null, null, 16, 15, 30, 50, 50, 14, 255))); - - /** - * Constructs a new {@code WaterFiendNPC} {@code Object}. - * @param id the id. - * @param location the location. - */ - public WaterFiendNPC(int id, Location location) { - super(id, location); - } - - /** - * Constructs a new {@code WaterFiendNPC} {@code Object}. - */ - public WaterFiendNPC() { - super(0, null); - } - - @Override - public AbstractNPC construct(int id, Location location, Object... objects) { - return new WaterFiendNPC(id, location); - } - - @Override - public CombatSwingHandler getSwingHandler(boolean swing) { - return combatAction; - } - - @Override - public int[] getIds() { - return Tasks.WATERFIENDS.getNpcs(); - } - -} diff --git a/Server/src/main/content/global/skill/slayer/WaterfiendBehavior.kt b/Server/src/main/content/global/skill/slayer/WaterfiendBehavior.kt new file mode 100644 index 000000000..e814eafcf --- /dev/null +++ b/Server/src/main/content/global/skill/slayer/WaterfiendBehavior.kt @@ -0,0 +1,55 @@ +package content.global.skill.slayer + +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.NPC +import core.game.node.entity.npc.NPCBehavior +import core.game.world.update.flag.context.Animation + +class WaterfiendBehavior : NPCBehavior(*Tasks.WATERFIENDS.ids) { + private val combatHandler = MultiSwingHandler( + true, + SwitchAttack( + CombatStyle.MAGIC.swingHandler, + Animation(1581, Priority.HIGH), + null, + null, + Projectile.create( + null as Entity?, + null, + 500, + 15, + 30, + 50, + 50, + 14, + 255 + ) + ), + SwitchAttack( + CombatStyle.RANGE.swingHandler, + Animation(1581, Priority.HIGH), + null, + null, + Projectile.create( + null as Entity?, + null, + 16, + 15, + 30, + 50, + 50, + 14, + 255 + ) + ) + ) + override fun getSwingHandlerOverride(self: NPC, original: CombatSwingHandler): CombatSwingHandler { + return combatHandler + } +} \ No newline at end of file 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..0c8c36181 100644 --- a/Server/src/main/content/global/skill/smithing/Bars.java +++ b/Server/src/main/content/global/skill/smithing/Bars.java @@ -26,122 +26,122 @@ public enum Bars { /** * Bronze Mace */ - BRONZE_MACE(BarType.BRONZE, SmithingType.TYPE_MACE, 1422, 2), + BRONZE_MACE(BarType.BRONZE, SmithingType.TYPE_MACE, 1422, 1), /** * Bronze Medium helm */ - BRONZE_MED_HELM(BarType.BRONZE, SmithingType.TYPE_MEDIUM_HELM, 1139, 3), + BRONZE_MED_HELM(BarType.BRONZE, SmithingType.TYPE_MEDIUM_HELM, 1139, 1), /** * Bronze Crossbow Bolt */ - BRONZE_CROSSBOW_BOLT(BarType.BRONZE, SmithingType.TYPE_CROSSBOW_BOLT, 9375, 3), + BRONZE_CROSSBOW_BOLT(BarType.BRONZE, SmithingType.TYPE_CROSSBOW_BOLT, 9375, 1), /** * Bronze Sword */ - BRONZE_SWORD(BarType.BRONZE, SmithingType.TYPE_SWORD, 1277, 4), + BRONZE_SWORD(BarType.BRONZE, SmithingType.TYPE_SWORD, 1277, 1), /** * Bronze Dart Tips */ - BRONZE_DART_TIPS(BarType.BRONZE, SmithingType.TYPE_DART_TIP, 819, 4), + BRONZE_DART_TIPS(BarType.BRONZE, SmithingType.TYPE_DART_TIP, 819, 1), /** * Bronze Nails */ - BRONZE_NAILS(BarType.BRONZE, SmithingType.TYPE_NAIL, 4819, 4), + BRONZE_NAILS(BarType.BRONZE, SmithingType.TYPE_NAIL, 4819, 1), /** * Bronze Wire */ - BRONZE_WIRE(BarType.BRONZE, SmithingType.TYPE_WIRE, 1794, 4), + BRONZE_WIRE(BarType.BRONZE, SmithingType.TYPE_WIRE, 1794, 1), /** * Bronze Arrow Tips */ - BRONZE_ARROW_TIPS(BarType.BRONZE, SmithingType.TYPE_ARROW_TIP, 39, 5), + BRONZE_ARROW_TIPS(BarType.BRONZE, SmithingType.TYPE_ARROW_TIP, 39, 1), /** * Bronze Scimitar */ - BRONZE_SCIMITAR(BarType.BRONZE, SmithingType.TYPE_SCIMITAR, 1321, 5), + BRONZE_SCIMITAR(BarType.BRONZE, SmithingType.TYPE_SCIMITAR, 1321, 1), /** * Bronze Crossbow Limbs */ - BRONZE_CROSSBOW_LIMBS(BarType.BRONZE, SmithingType.TYPE_CROSSBOW_LIMB, 9420, 6), + BRONZE_CROSSBOW_LIMBS(BarType.BRONZE, SmithingType.TYPE_CROSSBOW_LIMB, 9420, 1), /** * Bronze longsword */ - BRONZE_LONGSWORD(BarType.BRONZE, SmithingType.TYPE_LONGSWORD, 1291, 6), + BRONZE_LONGSWORD(BarType.BRONZE, SmithingType.TYPE_LONGSWORD, 1291, 1), /** * Bronze ThrowingKnife */ - BRONZE_THROWINGKNFIE(BarType.BRONZE, SmithingType.TYPE_THROWING_KNIFE, 864, 7), + BRONZE_THROWINGKNFIE(BarType.BRONZE, SmithingType.TYPE_THROWING_KNIFE, 864, 1), /** * Bronze Full helmet */ - BRONZE_FULL_HELM(BarType.BRONZE, SmithingType.TYPE_FULL_HELM, 1155, 7), + BRONZE_FULL_HELM(BarType.BRONZE, SmithingType.TYPE_FULL_HELM, 1155, 1), /** * Bronze Square Shield */ - BRONZE_SQUARE_SHIELD(BarType.BRONZE, SmithingType.TYPE_SQUARE_SHIELD, 1173, 8), + BRONZE_SQUARE_SHIELD(BarType.BRONZE, SmithingType.TYPE_SQUARE_SHIELD, 1173, 1), /** * Bronze Warhammer */ - BRONZE_WAR_HAMMER(BarType.BRONZE, SmithingType.TYPE_WARHAMMER, 1337, 9), + BRONZE_WAR_HAMMER(BarType.BRONZE, SmithingType.TYPE_WARHAMMER, 1337, 1), /** * Bronze BattleAxe */ - BRONZE_BATTLEAXE(BarType.BRONZE, SmithingType.TYPE_BATTLE_AXE, 1375, 10), + BRONZE_BATTLEAXE(BarType.BRONZE, SmithingType.TYPE_BATTLE_AXE, 1375, 1), /** * Bronze ChainBody */ - BRONZE_CHAINBODY(BarType.BRONZE, SmithingType.TYPE_CHAINBODY, 1103, 11), + BRONZE_CHAINBODY(BarType.BRONZE, SmithingType.TYPE_CHAINBODY, 1103, 1), /** * Bronze KitShield */ - BRONZE_KITESHIELD(BarType.BRONZE, SmithingType.TYPE_KITE_SHIELD, 1189, 12), + BRONZE_KITESHIELD(BarType.BRONZE, SmithingType.TYPE_KITE_SHIELD, 1189, 1), /** * Bronze Claws */ - BRONZE_CLAWS(BarType.BRONZE, SmithingType.TYPE_CLAWS, 3095, 13), + BRONZE_CLAWS(BarType.BRONZE, SmithingType.TYPE_CLAWS, 3095, 1), /** * Bronze 2h */ - BRONZE_TWO_HANDED(BarType.BRONZE, SmithingType.TYPE_TWO_HAND_SWORD, 1307, 14), + BRONZE_TWO_HANDED(BarType.BRONZE, SmithingType.TYPE_TWO_HAND_SWORD, 1307, 1), /** * Bronze PlateSkirt */ - BRONZE_PLATE_SKIRT(BarType.BRONZE, SmithingType.TYPE_PLATE_SKIRT, 1087, 16), + BRONZE_PLATE_SKIRT(BarType.BRONZE, SmithingType.TYPE_PLATE_SKIRT, 1087, 1), /** * Bronze PlateLegs */ - BRONZE_PLATELEGS(BarType.BRONZE, SmithingType.TYPE_PLATELEG, 1075, 16), + BRONZE_PLATELEGS(BarType.BRONZE, SmithingType.TYPE_PLATELEG, 1075, 1), /** * Bronze PlateBody */ - BRONZE_PLATEBODY(BarType.BRONZE, SmithingType.TYPE_PLATEBODY, 1117, 18), + BRONZE_PLATEBODY(BarType.BRONZE, SmithingType.TYPE_PLATEBODY, 1117, 1), /** * Bronze Pickaxe */ - BRONZE_PICKAXE(BarType.BRONZE, SmithingType.TYPE_PICKAXE, 1265, 5), + BRONZE_PICKAXE(BarType.BRONZE, SmithingType.TYPE_PICKAXE, 1265, 1), /** * Iron Dagger @@ -151,131 +151,131 @@ public enum Bars { /** * Iron Hatchet */ - IRON_AXE(BarType.IRON, SmithingType.TYPE_AXE, 1349, 16), + IRON_AXE(BarType.IRON, SmithingType.TYPE_AXE, 1349, 15), /** * Iron Mace */ - IRON_MACE(BarType.IRON, SmithingType.TYPE_MACE, 1420, 17), + IRON_MACE(BarType.IRON, SmithingType.TYPE_MACE, 1420, 15), /** * Iron Med Helm */ - IRON_MED_HELM(BarType.IRON, SmithingType.TYPE_MEDIUM_HELM, 1137, 18), + IRON_MED_HELM(BarType.IRON, SmithingType.TYPE_MEDIUM_HELM, 1137, 15), /** * Iron Bolt */ - IRON_BOLT(BarType.IRON, SmithingType.TYPE_CROSSBOW_BOLT, 9377, 18), + IRON_BOLT(BarType.IRON, SmithingType.TYPE_CROSSBOW_BOLT, 9377, 15), /** * Iron Sword */ - IRON_SWORD(BarType.IRON, SmithingType.TYPE_SWORD, 1279, 19), + IRON_SWORD(BarType.IRON, SmithingType.TYPE_SWORD, 1279, 15), /** * Iron Dart Tips */ - IRON_DART_TIPS(BarType.IRON, SmithingType.TYPE_DART_TIP, 820, 19), + IRON_DART_TIPS(BarType.IRON, SmithingType.TYPE_DART_TIP, 820, 15), /** * Iron Nails */ - IRON_NAILS(BarType.IRON, SmithingType.TYPE_NAIL, 4820, 19), + IRON_NAILS(BarType.IRON, SmithingType.TYPE_NAIL, 4820, 15), /** * Iron Split */ - IRON_SPIT(BarType.IRON, SmithingType.TYPE_SPIT_IRON, 7225, 16), + IRON_SPIT(BarType.IRON, SmithingType.TYPE_SPIT_IRON, 7225, 15), /** * Iron Arrow Tips */ - IRON_ARROW_TIPS(BarType.IRON, SmithingType.TYPE_ARROW_TIP, 40, 20), + IRON_ARROW_TIPS(BarType.IRON, SmithingType.TYPE_ARROW_TIP, 40, 15), /** * Iron Scimitar */ - IRON_SCIMITAR(BarType.IRON, SmithingType.TYPE_SCIMITAR, 1323, 20), + IRON_SCIMITAR(BarType.IRON, SmithingType.TYPE_SCIMITAR, 1323, 15), /** * Iron Crossbow Limbs */ - IRON_CROSSBOW_LimbS(BarType.IRON, SmithingType.TYPE_CROSSBOW_LIMB, 9423, 23), + IRON_CROSSBOW_LimbS(BarType.IRON, SmithingType.TYPE_CROSSBOW_LIMB, 9423, 15), /** * Iron LongSword */ - IRON_LONGSWORD(BarType.IRON, SmithingType.TYPE_LONGSWORD, 1293, 21), + IRON_LONGSWORD(BarType.IRON, SmithingType.TYPE_LONGSWORD, 1293, 15), /** * Iron Knife */ - IRON_KNIFE(BarType.IRON, SmithingType.TYPE_THROWING_KNIFE, 863, 22), + IRON_KNIFE(BarType.IRON, SmithingType.TYPE_THROWING_KNIFE, 863, 15), /** * Iron Full Helm */ - IRON_FULL_HELM(BarType.IRON, SmithingType.TYPE_FULL_HELM, 1153, 22), + IRON_FULL_HELM(BarType.IRON, SmithingType.TYPE_FULL_HELM, 1153, 15), /** * Iron Square Shield */ - IRON_SQUARE_SHIELD(BarType.IRON, SmithingType.TYPE_SQUARE_SHIELD, 1175, 23), + IRON_SQUARE_SHIELD(BarType.IRON, SmithingType.TYPE_SQUARE_SHIELD, 1175, 15), /** * Oil Lantern Frame */ - OIL_LANTERN_FRAME(BarType.IRON, SmithingType.TYPE_OIL_LANTERN, 4540, 26), + OIL_LANTERN_FRAME(BarType.IRON, SmithingType.TYPE_OIL_LANTERN, 4540, 15), /** * Iron WarHammer */ - IRON_WARHAMMER(BarType.IRON, SmithingType.TYPE_WARHAMMER, 1335, 24), + IRON_WARHAMMER(BarType.IRON, SmithingType.TYPE_WARHAMMER, 1335, 15), /** * Iron Battleaxe */ - IRON_BATTLEAXE(BarType.IRON, SmithingType.TYPE_BATTLE_AXE, 1363, 25), + IRON_BATTLEAXE(BarType.IRON, SmithingType.TYPE_BATTLE_AXE, 1363, 15), /** * Iron ChainBody */ - IRON_CHAINBODY(BarType.IRON, SmithingType.TYPE_CHAINBODY, 1101, 26), + IRON_CHAINBODY(BarType.IRON, SmithingType.TYPE_CHAINBODY, 1101, 15), /** * Iron Kite Shield */ - IRON_KITE_SHIELD(BarType.IRON, SmithingType.TYPE_KITE_SHIELD, 1191, 27), + IRON_KITE_SHIELD(BarType.IRON, SmithingType.TYPE_KITE_SHIELD, 1191, 15), /** * Iron Claws */ - IRON_CLAWS(BarType.IRON, SmithingType.TYPE_CLAWS, 3096, 28), + IRON_CLAWS(BarType.IRON, SmithingType.TYPE_CLAWS, 3096, 15), /** * Iron 2H */ - IRON_TWO_HANDED_SWORD(BarType.IRON, SmithingType.TYPE_TWO_HAND_SWORD, 1309, 29), + IRON_TWO_HANDED_SWORD(BarType.IRON, SmithingType.TYPE_TWO_HAND_SWORD, 1309, 15), /** * Iron PlateSkirt */ - IRON_PLATESKIRT(BarType.IRON, SmithingType.TYPE_PLATE_SKIRT, 1081, 31), + IRON_PLATESKIRT(BarType.IRON, SmithingType.TYPE_PLATE_SKIRT, 1081, 15), /** * Iron PlateLegs */ - IRON_PLATELEGS(BarType.IRON, SmithingType.TYPE_PLATELEG, 1067, 31), + IRON_PLATELEGS(BarType.IRON, SmithingType.TYPE_PLATELEG, 1067, 15), /** * Iron PlateBody */ - IRON_PLATEBODY(BarType.IRON, SmithingType.TYPE_PLATEBODY, 1115, 33), + IRON_PLATEBODY(BarType.IRON, SmithingType.TYPE_PLATEBODY, 1115, 15), /** * Iron PickAxe */ - IRON_PICKAXE(BarType.IRON, SmithingType.TYPE_PICKAXE, 1267, 20), + IRON_PICKAXE(BarType.IRON, SmithingType.TYPE_PICKAXE, 1267, 15), /** * Steel Dagger @@ -285,134 +285,132 @@ public enum Bars { /** * Steel Axe */ - STEEL_AXE(BarType.STEEL, SmithingType.TYPE_AXE, 1353, 31), + STEEL_AXE(BarType.STEEL, SmithingType.TYPE_AXE, 1353, 30), /** * Steel Mace */ - STEEL_MACE(BarType.STEEL, SmithingType.TYPE_MACE, 1424, 32), + STEEL_MACE(BarType.STEEL, SmithingType.TYPE_MACE, 1424, 30), /** * Steel Medium Helm */ - STEEL_MED_HELM(BarType.STEEL, SmithingType.TYPE_MEDIUM_HELM, 1141, 33), + STEEL_MED_HELM(BarType.STEEL, SmithingType.TYPE_MEDIUM_HELM, 1141, 30), /** * Steel CrossBow bolts */ - STEEL_CROSSBOW_BOLT(BarType.STEEL, SmithingType.TYPE_CROSSBOW_BOLT, 9378, 33), + STEEL_CROSSBOW_BOLT(BarType.STEEL, SmithingType.TYPE_CROSSBOW_BOLT, 9378, 30), /** * Steel Sword */ - STEEL_SWORD(BarType.STEEL, SmithingType.TYPE_SWORD, 1281, 34), + STEEL_SWORD(BarType.STEEL, SmithingType.TYPE_SWORD, 1281, 30), /** * Steel Dart Tips */ - STEEL_DART_TIPS(BarType.STEEL, SmithingType.TYPE_DART_TIP, 821, 34), + STEEL_DART_TIPS(BarType.STEEL, SmithingType.TYPE_DART_TIP, 821, 30), /** * Steel Nails */ - STEEL_NAILS(BarType.STEEL, SmithingType.TYPE_NAIL, 1539, 34), + STEEL_NAILS(BarType.STEEL, SmithingType.TYPE_NAIL, 1539, 30), /** * Steel ArrowTips */ - STEEL_ARROW_TIPS(BarType.STEEL, SmithingType.TYPE_ARROW_TIP, 41, 35), + STEEL_ARROW_TIPS(BarType.STEEL, SmithingType.TYPE_ARROW_TIP, 41, 30), /** * Steel Scimitar */ - STEEL_SCIMITAR(BarType.STEEL, SmithingType.TYPE_SCIMITAR, 1325, 35), + STEEL_SCIMITAR(BarType.STEEL, SmithingType.TYPE_SCIMITAR, 1325, 30), /** * Steel Crossbow Limbs */ - STEEL_CROSSBOW_LIMBS(BarType.STEEL, SmithingType.TYPE_CROSSBOW_LIMB, 9425, 36), + STEEL_CROSSBOW_LIMBS(BarType.STEEL, SmithingType.TYPE_CROSSBOW_LIMB, 9425, 30), /** * Steel LongSword */ - STEEL_LONGSWORD(BarType.STEEL, SmithingType.TYPE_LONGSWORD, 1295, 36), + STEEL_LONGSWORD(BarType.STEEL, SmithingType.TYPE_LONGSWORD, 1295, 30), /** * Steel Knife */ - STEEL_THROWING_KNIFE(BarType.STEEL, SmithingType.TYPE_THROWING_KNIFE, 865, 37), + STEEL_THROWING_KNIFE(BarType.STEEL, SmithingType.TYPE_THROWING_KNIFE, 865, 30), /** * Steel Full Helm */ - STEEL_FULL_HELM(BarType.STEEL, SmithingType.TYPE_FULL_HELM, 1157, 37), + STEEL_FULL_HELM(BarType.STEEL, SmithingType.TYPE_FULL_HELM, 1157, 30), /** * Steel Studs */ - STEEL_STUDS(BarType.STEEL, SmithingType.TYPE_STUDS, 2370, 36), + STEEL_STUDS(BarType.STEEL, SmithingType.TYPE_STUDS, 2370, 30), /** * Steel Square Shield */ - STEEL_SQUARE_SHIELD(BarType.STEEL, SmithingType.TYPE_SQUARE_SHIELD, 1177, 38), + STEEL_SQUARE_SHIELD(BarType.STEEL, SmithingType.TYPE_SQUARE_SHIELD, 1177, 30), /** * Steel Lantern */ - STEEL_LANTERN(BarType.STEEL, SmithingType.TYPE_LANTERN, 4527, 49), - - STEEL_BULLSEYE(BarType.STEEL, SmithingType.TYPE_BULLSEYE, 4544, 49), + STEEL_BULLSEYE(BarType.STEEL, SmithingType.TYPE_BULLSEYE, 4544, 30), /** * Steel WarHammer */ - STEEL_WARHAMMER(BarType.STEEL, SmithingType.TYPE_WARHAMMER, 1339, 39), + STEEL_WARHAMMER(BarType.STEEL, SmithingType.TYPE_WARHAMMER, 1339, 30), /** * Steel battle axe */ - STEEL_BATTLE_AXE(BarType.STEEL, SmithingType.TYPE_BATTLE_AXE, 1365, 40), + STEEL_BATTLE_AXE(BarType.STEEL, SmithingType.TYPE_BATTLE_AXE, 1365, 30), /** * Steel ChainBody */ - STEEL_CHAINBODY(BarType.STEEL, SmithingType.TYPE_CHAINBODY, 1105, 41), + STEEL_CHAINBODY(BarType.STEEL, SmithingType.TYPE_CHAINBODY, 1105, 30), /** * Steel Kite Shield */ - STEEL_KITE_SHIELD(BarType.STEEL, SmithingType.TYPE_KITE_SHIELD, 1193, 42), + STEEL_KITE_SHIELD(BarType.STEEL, SmithingType.TYPE_KITE_SHIELD, 1193, 30), /** * Steel Claws */ - STEEL_CLAWS(BarType.STEEL, SmithingType.TYPE_CLAWS, 3097, 43), + STEEL_CLAWS(BarType.STEEL, SmithingType.TYPE_CLAWS, 3097, 30), /** * Steel 2h */ - STEEL_TWO_HANDED_SWORD(BarType.STEEL, SmithingType.TYPE_TWO_HAND_SWORD, 1311, 44), + STEEL_TWO_HANDED_SWORD(BarType.STEEL, SmithingType.TYPE_TWO_HAND_SWORD, 1311, 30), /** * Steel plate skirt */ - STEEL_PLATE_SKIRT(BarType.STEEL, SmithingType.TYPE_PLATE_SKIRT, 1083, 46), + STEEL_PLATE_SKIRT(BarType.STEEL, SmithingType.TYPE_PLATE_SKIRT, 1083, 30), /** * Steel platelegs */ - STEEL_PLATELEGS(BarType.STEEL, SmithingType.TYPE_PLATELEG, 1069, 46), + STEEL_PLATELEGS(BarType.STEEL, SmithingType.TYPE_PLATELEG, 1069, 30), /** * Steel platebody */ - STEEL_PLATEBODY(BarType.STEEL, SmithingType.TYPE_PLATEBODY, 1119, 48), + STEEL_PLATEBODY(BarType.STEEL, SmithingType.TYPE_PLATEBODY, 1119, 30), /** * Steel pickaxe */ - STEEL_PICKAXE(BarType.STEEL, SmithingType.TYPE_PICKAXE, 1269, 35), + STEEL_PICKAXE(BarType.STEEL, SmithingType.TYPE_PICKAXE, 1269, 30), /** * Mithril Dagger @@ -422,377 +420,377 @@ public enum Bars { /** * Mithril Hatchet */ - MITHRIL_HATCHET(BarType.MITHRIL, SmithingType.TYPE_AXE, 1355, 51), + MITHRIL_HATCHET(BarType.MITHRIL, SmithingType.TYPE_AXE, 1355, 50), /** * Mithril Mace */ - MITHRIL_MACE(BarType.MITHRIL, SmithingType.TYPE_MACE, 1428, 52), + MITHRIL_MACE(BarType.MITHRIL, SmithingType.TYPE_MACE, 1428, 50), /** * Mithril Med Helm */ - MITHRIL_MED_HELM(BarType.MITHRIL, SmithingType.TYPE_MEDIUM_HELM, 1143, 53), + MITHRIL_MED_HELM(BarType.MITHRIL, SmithingType.TYPE_MEDIUM_HELM, 1143, 50), /** * Mithril Crossbow Bolt */ - MITHRIL_CROSSBOW_BOLT(BarType.MITHRIL, SmithingType.TYPE_CROSSBOW_BOLT, 9379, 53), + MITHRIL_CROSSBOW_BOLT(BarType.MITHRIL, SmithingType.TYPE_CROSSBOW_BOLT, 9379, 50), /** * Mithril Sword */ - MITHRIL_SWORD(BarType.MITHRIL, SmithingType.TYPE_SWORD, 1285, 54), + MITHRIL_SWORD(BarType.MITHRIL, SmithingType.TYPE_SWORD, 1285, 50), /** * Mithril Dart Tips */ - MITHRIL_DART_TIPS(BarType.MITHRIL, SmithingType.TYPE_DART_TIP, 822, 54), + MITHRIL_DART_TIPS(BarType.MITHRIL, SmithingType.TYPE_DART_TIP, 822, 50), /** * Mithril Nails */ - MITHRIL_NAILS(BarType.MITHRIL, SmithingType.TYPE_NAIL, 4822, 54), + MITHRIL_NAILS(BarType.MITHRIL, SmithingType.TYPE_NAIL, 4822, 50), /** * Mithril Arrow Tips */ - MITHRIL_ARROW_TIPS(BarType.MITHRIL, SmithingType.TYPE_ARROW_TIP, 42, 55), + MITHRIL_ARROW_TIPS(BarType.MITHRIL, SmithingType.TYPE_ARROW_TIP, 42, 50), /** * Mithril Scimitar */ - MITHRIL_SCIMITAR(BarType.MITHRIL, SmithingType.TYPE_SCIMITAR, 1329, 55), + MITHRIL_SCIMITAR(BarType.MITHRIL, SmithingType.TYPE_SCIMITAR, 1329, 50), /** * Mithril Crossbow Limbs */ - MITHRIL_CROSSBOW_LIMBS(BarType.MITHRIL, SmithingType.TYPE_CROSSBOW_LIMB, 9427, 56), + MITHRIL_CROSSBOW_LIMBS(BarType.MITHRIL, SmithingType.TYPE_CROSSBOW_LIMB, 9427, 50), /** * Mithril LongSword */ - MITHRIL_LONGSWORD(BarType.MITHRIL, SmithingType.TYPE_LONGSWORD, 1299, 56), + MITHRIL_LONGSWORD(BarType.MITHRIL, SmithingType.TYPE_LONGSWORD, 1299, 50), /** * Mithril Knife */ - MITHRIL_KNIFE(BarType.MITHRIL, SmithingType.TYPE_THROWING_KNIFE, 866, 57), + MITHRIL_KNIFE(BarType.MITHRIL, SmithingType.TYPE_THROWING_KNIFE, 866, 50), /** * Mithril Full Helm */ - MITHRIL_FULL_HELM(BarType.MITHRIL, SmithingType.TYPE_FULL_HELM, 1159, 57), + MITHRIL_FULL_HELM(BarType.MITHRIL, SmithingType.TYPE_FULL_HELM, 1159, 50), /** * Mithril SquareShield */ - MITHRIL_SQUARE_SHIELD(BarType.MITHRIL, SmithingType.TYPE_SQUARE_SHIELD, 1181, 58), + MITHRIL_SQUARE_SHIELD(BarType.MITHRIL, SmithingType.TYPE_SQUARE_SHIELD, 1181, 50), /** * Mithril Grapple Tips */ - MITHRIL_GRAPPLE_TIPS(BarType.MITHRIL, SmithingType.TYPE_GRAPPLE_TIP, 9416, 59), + MITHRIL_GRAPPLE_TIPS(BarType.MITHRIL, SmithingType.TYPE_GRAPPLE_TIP, 9416, 50), /** * Mithril Warhammer */ - MITHRIL_WARHAMMER(BarType.MITHRIL, SmithingType.TYPE_WARHAMMER, 1343, 59), + MITHRIL_WARHAMMER(BarType.MITHRIL, SmithingType.TYPE_WARHAMMER, 1343, 50), /** * Mithril BattleAxe */ - MITHRIL_BATTLEAXE(BarType.MITHRIL, SmithingType.TYPE_BATTLE_AXE, 1369, 60), + MITHRIL_BATTLEAXE(BarType.MITHRIL, SmithingType.TYPE_BATTLE_AXE, 1369, 50), /** * Mithril ChainBody */ - MITHRIL_CHAINBODY(BarType.MITHRIL, SmithingType.TYPE_CHAINBODY, 1109, 61), + MITHRIL_CHAINBODY(BarType.MITHRIL, SmithingType.TYPE_CHAINBODY, 1109, 50), /** * Mithril KiteShield */ - MITHRIL_KITE_SHIELD(BarType.MITHRIL, SmithingType.TYPE_KITE_SHIELD, 1197, 62), + MITHRIL_KITE_SHIELD(BarType.MITHRIL, SmithingType.TYPE_KITE_SHIELD, 1197, 50), /** * Mithril Claws */ - MITHRIL_CLAWS(BarType.MITHRIL, SmithingType.TYPE_CLAWS, 3099, 63), + MITHRIL_CLAWS(BarType.MITHRIL, SmithingType.TYPE_CLAWS, 3099, 50), /** * Mithril 2H */ - MITHRIL_TWO_HANDED_SWORD(BarType.MITHRIL, SmithingType.TYPE_TWO_HAND_SWORD, 1315, 64), + MITHRIL_TWO_HANDED_SWORD(BarType.MITHRIL, SmithingType.TYPE_TWO_HAND_SWORD, 1315, 50), /** * Mithril PlateSkirt */ - MITHRIL_PLATESKIRT(BarType.MITHRIL, SmithingType.TYPE_PLATE_SKIRT, 1085, 66), + MITHRIL_PLATESKIRT(BarType.MITHRIL, SmithingType.TYPE_PLATE_SKIRT, 1085, 50), /** * Mithril PlateLegs */ - MITHRIL_PLATELEGS(BarType.MITHRIL, SmithingType.TYPE_PLATELEG, 1071, 66), + MITHRIL_PLATELEGS(BarType.MITHRIL, SmithingType.TYPE_PLATELEG, 1071, 50), /** * Mithril PlateBody */ - MITHRIL_PLATEBODY(BarType.MITHRIL, SmithingType.TYPE_PLATEBODY, 1121, 68), + MITHRIL_PLATEBODY(BarType.MITHRIL, SmithingType.TYPE_PLATEBODY, 1121, 50), /** * Mithril PickAxe */ - MITHRIL_PICKAXE(BarType.MITHRIL, SmithingType.TYPE_PICKAXE, 1273, 55), + MITHRIL_PICKAXE(BarType.MITHRIL, SmithingType.TYPE_PICKAXE, 1273, 50), /** * Adamant Dagger */ - ADAMANT_DAGGER(BarType.ADAMANT, SmithingType.TYPE_DAGGER, 1211, 70), + ADAMANT_DAGGER(BarType.ADAMANT, SmithingType.TYPE_DAGGER, 1211, 60), /** * Adamant Hatchet */ - ADAMANT_AXE(BarType.ADAMANT, SmithingType.TYPE_AXE, 1357, 71), + ADAMANT_AXE(BarType.ADAMANT, SmithingType.TYPE_AXE, 1357, 60), /** * Adamant Mace */ - ADAMANT_MACE(BarType.ADAMANT, SmithingType.TYPE_MACE, 1430, 72), + ADAMANT_MACE(BarType.ADAMANT, SmithingType.TYPE_MACE, 1430, 60), /** * Adamant Med Helm */ - ADAMANT_MEDIUM_HELM(BarType.ADAMANT, SmithingType.TYPE_MEDIUM_HELM, 1145, 73), + ADAMANT_MEDIUM_HELM(BarType.ADAMANT, SmithingType.TYPE_MEDIUM_HELM, 1145, 60), /** * Adamant Crossbow Bolt */ - ADAMANT_BOLT(BarType.ADAMANT, SmithingType.TYPE_CROSSBOW_BOLT, 9380, 73), + ADAMANT_BOLT(BarType.ADAMANT, SmithingType.TYPE_CROSSBOW_BOLT, 9380, 60), /** * Adamant Sword */ - ADAMANT_SWORD(BarType.ADAMANT, SmithingType.TYPE_SWORD, 1287, 74), + ADAMANT_SWORD(BarType.ADAMANT, SmithingType.TYPE_SWORD, 1287, 60), /** * Adamant Dart Tips */ - ADAMANT_DART_TIPS(BarType.ADAMANT, SmithingType.TYPE_DART_TIP, 823, 74), + ADAMANT_DART_TIPS(BarType.ADAMANT, SmithingType.TYPE_DART_TIP, 823, 60), /** * Adamant Nails */ - ADAMANT_NAILS(BarType.ADAMANT, SmithingType.TYPE_NAIL, 4823, 74), + ADAMANT_NAILS(BarType.ADAMANT, SmithingType.TYPE_NAIL, 4823, 60), /** * Adamant Arrow Tips */ - ADAMANT_ARROW_TIPS(BarType.ADAMANT, SmithingType.TYPE_ARROW_TIP, 43, 75), + ADAMANT_ARROW_TIPS(BarType.ADAMANT, SmithingType.TYPE_ARROW_TIP, 43, 60), /** * Adamant Scmitar */ - ADAMANT_SCIMITAR(BarType.ADAMANT, SmithingType.TYPE_SCIMITAR, 1331, 75), + ADAMANT_SCIMITAR(BarType.ADAMANT, SmithingType.TYPE_SCIMITAR, 1331, 60), /** * Adamant Crossbow Limbs */ - ADAMANT_LIMBS(BarType.ADAMANT, SmithingType.TYPE_CROSSBOW_LIMB, 9429, 76), + ADAMANT_LIMBS(BarType.ADAMANT, SmithingType.TYPE_CROSSBOW_LIMB, 9429, 60), /** * Adamant LongSword */ - ADAMANT_LONGSWORD(BarType.ADAMANT, SmithingType.TYPE_LONGSWORD, 1301, 76), + ADAMANT_LONGSWORD(BarType.ADAMANT, SmithingType.TYPE_LONGSWORD, 1301, 60), /** * Adamant Knife */ - ADAMANT_KNIFE(BarType.ADAMANT, SmithingType.TYPE_THROWING_KNIFE, 867, 77), + ADAMANT_KNIFE(BarType.ADAMANT, SmithingType.TYPE_THROWING_KNIFE, 867, 60), /** * Adamant Full Helm */ - ADAMANT_FULL_HELM(BarType.ADAMANT, SmithingType.TYPE_FULL_HELM, 1161, 77), + ADAMANT_FULL_HELM(BarType.ADAMANT, SmithingType.TYPE_FULL_HELM, 1161, 60), /** * Adamant Square Shield */ - ADAMANT_SQUARE_SHIELD(BarType.ADAMANT, SmithingType.TYPE_SQUARE_SHIELD, 1183, 78), + ADAMANT_SQUARE_SHIELD(BarType.ADAMANT, SmithingType.TYPE_SQUARE_SHIELD, 1183, 60), /** * Adamant Warhammer */ - ADAMANT_WARHAMMER(BarType.ADAMANT, SmithingType.TYPE_WARHAMMER, 1345, 79), + ADAMANT_WARHAMMER(BarType.ADAMANT, SmithingType.TYPE_WARHAMMER, 1345, 60), /** * Adamant BattleAxe */ - ADAMANT_BATTLEAXE(BarType.ADAMANT, SmithingType.TYPE_BATTLE_AXE, 1371, 80), + ADAMANT_BATTLEAXE(BarType.ADAMANT, SmithingType.TYPE_BATTLE_AXE, 1371, 60), /** * Adamant ChainBody */ - ADAMANT_CHAINBODY(BarType.ADAMANT, SmithingType.TYPE_CHAINBODY, 1111, 81), + ADAMANT_CHAINBODY(BarType.ADAMANT, SmithingType.TYPE_CHAINBODY, 1111, 60), /** * Adamant KiteShield */ - ADAMANT_KITESHIELD(BarType.ADAMANT, SmithingType.TYPE_KITE_SHIELD, 1199, 82), + ADAMANT_KITESHIELD(BarType.ADAMANT, SmithingType.TYPE_KITE_SHIELD, 1199, 60), /** * Adamant Claws */ - ADAMANT_CLAWS(BarType.ADAMANT, SmithingType.TYPE_CLAWS, 3100, 83), + ADAMANT_CLAWS(BarType.ADAMANT, SmithingType.TYPE_CLAWS, 3100, 60), /** * Adamant 2H */ - ADAMANT_TWO_HANDED_SWORD(BarType.ADAMANT, SmithingType.TYPE_TWO_HAND_SWORD, 1317, 84), + ADAMANT_TWO_HANDED_SWORD(BarType.ADAMANT, SmithingType.TYPE_TWO_HAND_SWORD, 1317, 60), /** * Adamant PlateSkirt */ - ADAMANT_PLATE_SKIRT(BarType.ADAMANT, SmithingType.TYPE_PLATE_SKIRT, 1091, 86), + ADAMANT_PLATE_SKIRT(BarType.ADAMANT, SmithingType.TYPE_PLATE_SKIRT, 1091, 60), /** * Adamant PlateLegs */ - ADAMANT_PLATE_LEGS(BarType.ADAMANT, SmithingType.TYPE_PLATELEG, 1073, 86), + ADAMANT_PLATE_LEGS(BarType.ADAMANT, SmithingType.TYPE_PLATELEG, 1073, 60), /** * Adamant PlateBody */ - ADAMANT_PLATE_BODY(BarType.ADAMANT, SmithingType.TYPE_PLATEBODY, 1123, 88), + ADAMANT_PLATE_BODY(BarType.ADAMANT, SmithingType.TYPE_PLATEBODY, 1123, 60), /** * Adamant PickAxe */ - ADAMANT_PICKAXE(BarType.ADAMANT, SmithingType.TYPE_PICKAXE, 1271, 75), + ADAMANT_PICKAXE(BarType.ADAMANT, SmithingType.TYPE_PICKAXE, 1271, 60), /** * Rune Dagger */ - RUNE_DAGGER(BarType.RUNITE, SmithingType.TYPE_DAGGER, 1213, 85), + RUNE_DAGGER(BarType.RUNITE, SmithingType.TYPE_DAGGER, 1213, 70), /** * Rune Hatchet */ - RUNITE_AXE(BarType.RUNITE, SmithingType.TYPE_AXE, 1359, 86), + RUNITE_AXE(BarType.RUNITE, SmithingType.TYPE_AXE, 1359, 70), /** * Rune Mace */ - RUNITE_MACE(BarType.RUNITE, SmithingType.TYPE_MACE, 1432, 87), + RUNITE_MACE(BarType.RUNITE, SmithingType.TYPE_MACE, 1432, 70), /** * Rune Med Helm */ - RUNITE_MEDIUM_HELM(BarType.RUNITE, SmithingType.TYPE_MEDIUM_HELM, 1147, 88), + RUNITE_MEDIUM_HELM(BarType.RUNITE, SmithingType.TYPE_MEDIUM_HELM, 1147, 70), /** * Rune Crossbow Bolt */ - RUNITE_BOLT(BarType.RUNITE, SmithingType.TYPE_CROSSBOW_BOLT, 9381, 88), + RUNITE_BOLT(BarType.RUNITE, SmithingType.TYPE_CROSSBOW_BOLT, 9381, 70), /** * Rune Sword */ - RUNITE_SWORD(BarType.RUNITE, SmithingType.TYPE_SWORD, 1289, 89), + RUNITE_SWORD(BarType.RUNITE, SmithingType.TYPE_SWORD, 1289, 70), /** * Rune Dart Tips */ - RUNITE_DART_TIPS(BarType.RUNITE, SmithingType.TYPE_DART_TIP, 824, 89), + RUNITE_DART_TIPS(BarType.RUNITE, SmithingType.TYPE_DART_TIP, 824, 70), /** * Rune Nails */ - RUNITE_NAILS(BarType.RUNITE, SmithingType.TYPE_NAIL, 4824, 89), + RUNITE_NAILS(BarType.RUNITE, SmithingType.TYPE_NAIL, 4824, 70), /** * Rune Arrow Tips */ - RUNITE_ARROW_TIPS(BarType.RUNITE, SmithingType.TYPE_ARROW_TIP, 44, 90), + RUNITE_ARROW_TIPS(BarType.RUNITE, SmithingType.TYPE_ARROW_TIP, 44, 70), /** * Rune Scmitar */ - RUNITE_SCIMITAR(BarType.RUNITE, SmithingType.TYPE_SCIMITAR, 1333, 90), + RUNITE_SCIMITAR(BarType.RUNITE, SmithingType.TYPE_SCIMITAR, 1333, 70), /** * Rune Crossbow Limbs */ - RUNITE_LIMBS(BarType.RUNITE, SmithingType.TYPE_CROSSBOW_LIMB, 9431, 91), + RUNITE_LIMBS(BarType.RUNITE, SmithingType.TYPE_CROSSBOW_LIMB, 9431, 70), /** * Rune LongSword */ - RUNITE_LONGSWORD(BarType.RUNITE, SmithingType.TYPE_LONGSWORD, 1303, 91), + RUNITE_LONGSWORD(BarType.RUNITE, SmithingType.TYPE_LONGSWORD, 1303, 70), /** * Rune Knife */ - RUNITE_KNIFE(BarType.RUNITE, SmithingType.TYPE_THROWING_KNIFE, 868, 92), + RUNITE_KNIFE(BarType.RUNITE, SmithingType.TYPE_THROWING_KNIFE, 868, 70), /** * Rune Full Helm */ - RUNITE_FULL_HELM(BarType.RUNITE, SmithingType.TYPE_FULL_HELM, 1163, 92), + RUNITE_FULL_HELM(BarType.RUNITE, SmithingType.TYPE_FULL_HELM, 1163, 70), /** * Rune Square Shield */ - RUNITE_SQUARE_SHIELD(BarType.RUNITE, SmithingType.TYPE_SQUARE_SHIELD, 1185, 93), + RUNITE_SQUARE_SHIELD(BarType.RUNITE, SmithingType.TYPE_SQUARE_SHIELD, 1185, 70), /** * Rune Warhammer */ - RUNITE_WARHAMMER(BarType.RUNITE, SmithingType.TYPE_WARHAMMER, 1347, 94), + RUNITE_WARHAMMER(BarType.RUNITE, SmithingType.TYPE_WARHAMMER, 1347, 70), /** * Rune BattleAxe */ - RUNITE_BATTLEAXE(BarType.RUNITE, SmithingType.TYPE_BATTLE_AXE, 1373, 95), + RUNITE_BATTLEAXE(BarType.RUNITE, SmithingType.TYPE_BATTLE_AXE, 1373, 70), /** * Rune ChainBody */ - RUNITE_CHAINBODY(BarType.RUNITE, SmithingType.TYPE_CHAINBODY, 1113, 96), + RUNITE_CHAINBODY(BarType.RUNITE, SmithingType.TYPE_CHAINBODY, 1113, 70), /** * Rune KiteShield */ - RUNITE_KITESHIELD(BarType.RUNITE, SmithingType.TYPE_KITE_SHIELD, 1201, 97), + RUNITE_KITESHIELD(BarType.RUNITE, SmithingType.TYPE_KITE_SHIELD, 1201, 70), /** * Rune Claws */ - RUNITE_CLAWS(BarType.RUNITE, SmithingType.TYPE_CLAWS, 3101, 98), + RUNITE_CLAWS(BarType.RUNITE, SmithingType.TYPE_CLAWS, 3101, 70), /** * Rune 2H */ - RUNITE_TWO_HANDED_SWORD(BarType.RUNITE, SmithingType.TYPE_TWO_HAND_SWORD, 1319, 99), + RUNITE_TWO_HANDED_SWORD(BarType.RUNITE, SmithingType.TYPE_TWO_HAND_SWORD, 1319, 70), /** * Rune PlateSkirt */ - RUNITE_PLATE_SKIRT(BarType.RUNITE, SmithingType.TYPE_PLATE_SKIRT, 1093, 99), + RUNITE_PLATE_SKIRT(BarType.RUNITE, SmithingType.TYPE_PLATE_SKIRT, 1093, 70), /** * Rune PlateLegs */ - RUNITE_PLATE_LEGS(BarType.RUNITE, SmithingType.TYPE_PLATELEG, 1079, 99), + RUNITE_PLATE_LEGS(BarType.RUNITE, SmithingType.TYPE_PLATELEG, 1079, 70), /** * Rune PlateBody */ - RUNITE_PLATE_BODY(BarType.RUNITE, SmithingType.TYPE_PLATEBODY, 1127, 99), + RUNITE_PLATE_BODY(BarType.RUNITE, SmithingType.TYPE_PLATEBODY, 1127, 70), /** * Rune PickAxe */ - RUNITE_PICKAXE(BarType.RUNITE, SmithingType.TYPE_PICKAXE, 1275, 90), + RUNITE_PICKAXE(BarType.RUNITE, SmithingType.TYPE_PICKAXE, 1275, 70), /** * Blurite CrossBow bolts @@ -802,7 +800,7 @@ public enum Bars { /** * Blurite Crossbow Limbs */ - BLURITE_CROSSBOW_LIMBS(BarType.BLURITE, SmithingType.TYPE_CROSSBOW_LIMB, 9422, 13); + BLURITE_CROSSBOW_LIMBS(BarType.BLURITE, SmithingType.TYPE_CROSSBOW_LIMB, 9422, 8); /** * A map of object ids to primary ingredients. 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..e34e71cc7 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); 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 ea65d0818..97a844d51 100644 --- a/Server/src/main/content/global/skill/smithing/SmithingPulse.java +++ b/Server/src/main/content/global/skill/smithing/SmithingPulse.java @@ -12,6 +12,10 @@ import core.game.world.map.Location; import core.game.world.update.flag.context.Animation; 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. * @@ -64,10 +68,14 @@ 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, Quests.DEATH_PLATEAU, false) && bar.getSmithingType() == SmithingType.TYPE_CLAWS) { + sendDialogue(player, "You need to complete Death Plateau to smith claws."); + return false; + } return true; } diff --git a/Server/src/main/content/global/skill/smithing/SmithingType.java b/Server/src/main/content/global/skill/smithing/SmithingType.java index 99054f7ec..e4a05e640 100644 --- a/Server/src/main/content/global/skill/smithing/SmithingType.java +++ b/Server/src/main/content/global/skill/smithing/SmithingType.java @@ -32,7 +32,7 @@ public enum SmithingType { /** * Crossbow bolt */ - TYPE_CROSSBOW_BOLT(1, 50, 51, new int[] { 56, 55, 54, 53 }, 10), + TYPE_CROSSBOW_BOLT(1, 50, 51, new int[] { 56, 55, 54, 53 }, 30), /** * Sword @@ -42,7 +42,7 @@ public enum SmithingType { /** * Dart tips */ - TYPE_DART_TIP(1, 66, 67, new int[] { 72, 71, 70, 69 }, 10), + TYPE_DART_TIP(1, 66, 67, new int[] { 72, 71, 70, 69 }, 30), /** * Nails @@ -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,12 +61,12 @@ 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 */ - TYPE_ARROW_TIP(1, 106, 107, new int[] { 112, 111, 110, 109 }, 15), + TYPE_ARROW_TIP(1, 106, 107, new int[] { 112, 111, 110, 109 }, 45), /** * Scimitar @@ -86,7 +86,7 @@ public enum SmithingType { /** * Throwing Knife */ - TYPE_THROWING_KNIFE(1, 138, 139, new int[] { 144, 143, 142, 141 }, 5), + TYPE_THROWING_KNIFE(1, 138, 139, new int[] { 144, 143, 142, 141 }, 15), /** * Full helm @@ -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/Bar.java b/Server/src/main/content/global/skill/smithing/smelting/Bar.java index 04177c11a..88b4eea74 100644 --- a/Server/src/main/content/global/skill/smithing/smelting/Bar.java +++ b/Server/src/main/content/global/skill/smithing/smelting/Bar.java @@ -15,8 +15,8 @@ public enum Bar { STEEL(30, 17.5, new Item(2353, 1), new Item(453, 2), new Item(440, 1)), GOLD(40, 22.5, new Item(2357, 1), new Item(444, 1)), MITHRIL(50, 30, new Item(2359, 1), new Item(447, 1), new Item(453, 4)), - ADAMANT(70, 37.5, new Item(2361, 1), new Item(449, 1), new Item(453, 6)), - RUNITE(85, 50, new Item(2363, 1), new Item(451, 1), new Item(453, 8)); + ADAMANT(60, 37.5, new Item(2361, 1), new Item(449, 1), new Item(453, 6)), + RUNITE(70, 50, new Item(2363, 1), new Item(451, 1), new Item(453, 8)); /** * The ore required. 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 c1dd2e570..fc5e01696 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; @@ -15,6 +17,8 @@ import core.game.world.update.flag.context.Animation; 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. @@ -86,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()) { @@ -95,9 +99,12 @@ public class SmeltingPulse extends SkillPulse { return false; } for (Item item : bar.getOres()) { + //Snowscape modification: check gold satchel for coal as well, if carried if (!player.getInventory().contains(item.getId(), item.getAmount())) { - player.getPacketDispatch().sendMessage("You do not have the required ores to make this bar."); - return false; + if (!(item.getId() == Items.COAL_453 && inEquipmentOrInventory(player, 10881, 1) && player.goldSatchel.contains(item.getId(), item.getAmount()))) { + player.getPacketDispatch().sendMessage("You do not have the required ores to make this bar."); + return false; + } } } return true; @@ -110,6 +117,7 @@ public class SmeltingPulse extends SkillPulse { player.visualize(Animation.create(725), new Graphics(148, 96)); } else { player.animate(Animation.create(3243)); // Used to be 899 but that looked wonky and broken + playAudio(player, Sounds.FURNACE_2725); } } } @@ -123,7 +131,7 @@ public class SmeltingPulse extends SkillPulse { player.getPacketDispatch().sendMessage("You place the required ores and attempt to create a bar of " + StringUtils.formatDisplayName(bar.toString().toLowerCase()) + "."); } for (Item i : bar.getOres()) { - if (!player.getInventory().remove(i)) { + if (!(player.getInventory().remove(i) || player.goldSatchel.remove(i))) { return true; } } @@ -142,7 +150,8 @@ public class SmeltingPulse extends SkillPulse { player.sendMessage("The magic of the Varrock armour enables you to smelt 2 bars at the same time."); } } - player.getInventory().add(new Item(bar.getProduct().getId(), amt)); + //player.getInventory().add(new Item(bar.getProduct().getId(), amt)); + addItemOrDrop(player, bar.getProduct().getId(), amt); player.dispatch(new ResourceProducedEvent(bar.getProduct().getId(), 1, player, -1)); double xp = bar.getExperience() * amt; // Goldsmith gauntlets @@ -172,16 +181,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. * @@ -190,19 +189,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/ObeliskOptionPlugin.java b/Server/src/main/content/global/skill/summoning/ObeliskOptionPlugin.java index bddff2eee..3ecea4dc9 100644 --- a/Server/src/main/content/global/skill/summoning/ObeliskOptionPlugin.java +++ b/Server/src/main/content/global/skill/summoning/ObeliskOptionPlugin.java @@ -10,6 +10,9 @@ 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.Plugin; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.playAudio; /** * Represents the option used on the summoning obelisk. @@ -31,7 +34,7 @@ public final class ObeliskOptionPlugin extends OptionHandler { return true; } player.visualize(Animation.create(8502), Graphics.create(1308)); - player.getAudioManager().send(4214); + playAudio(player, Sounds.DREADFOWL_BOOST_4214); player.getSkills().setLevel(Skills.SUMMONING, player.getSkills().getStaticLevel(Skills.SUMMONING)); player.dispatch(new SummoningPointsRechargeEvent(node)); player.getPacketDispatch().sendMessage("You renew your summoning points."); @@ -39,7 +42,6 @@ public final class ObeliskOptionPlugin extends OptionHandler { } return false; } - @Override public Plugin newInstance(Object arg) throws Throwable { SceneryDefinition.setOptionHandler("infuse-pouch", this); diff --git a/Server/src/main/content/global/skill/summoning/SummoningCreationPlugin.java b/Server/src/main/content/global/skill/summoning/SummoningCreationPlugin.java index 8e892a2a1..637b63ad6 100644 --- a/Server/src/main/content/global/skill/summoning/SummoningCreationPlugin.java +++ b/Server/src/main/content/global/skill/summoning/SummoningCreationPlugin.java @@ -34,8 +34,9 @@ public final class SummoningCreationPlugin extends ComponentPlugin { switch (button) { case 18: case 17: - SummoningCreator.configure(player, button == 17); - break; + player.getInterfaceManager().close(); + SummoningCreator.configure(player, button == 17); + return true; } switch (component.getId()) { case 669: diff --git a/Server/src/main/content/global/skill/summoning/SummoningCreator.java b/Server/src/main/content/global/skill/summoning/SummoningCreator.java index 68d3e974a..d7a2827c9 100644 --- a/Server/src/main/content/global/skill/summoning/SummoningCreator.java +++ b/Server/src/main/content/global/skill/summoning/SummoningCreator.java @@ -10,6 +10,9 @@ import core.game.node.scenery.Scenery; import core.game.world.map.Location; import core.game.world.map.RegionManager; import core.game.world.update.flag.context.Animation; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.playAudio; /** * Represents a utility class of creating summoning nodes. @@ -157,7 +160,7 @@ public final class SummoningCreator { if (getDelay() == 1) { setDelay(4); player.getPacketDispatch().sendSceneryAnimation(object, Animation.create(8509)); - player.getAudioManager().send(4164); // 4277 also sounds the same + playAudio(player, Sounds.CRAFT_POUCH_4164); // 4277 also sounds the same return false; } player.getPacketDispatch().sendSceneryAnimation(object, Animation.create(8510)); diff --git a/Server/src/main/content/global/skill/summoning/SummoningPouch.java b/Server/src/main/content/global/skill/summoning/SummoningPouch.java index 163bde484..90a502e6d 100644 --- a/Server/src/main/content/global/skill/summoning/SummoningPouch.java +++ b/Server/src/main/content/global/skill/summoning/SummoningPouch.java @@ -14,27 +14,27 @@ public enum SummoningPouch { /** * Represents a spirit wolf pouch. */ - SPIRIT_WOLF_POUCH(0, 12047, 1, 4.8, 6829, 0.1, 1, false, new Item(12158), new Item(12155), new Item(2859), new Item(12183, 7)), + SPIRIT_WOLF_POUCH(0, 12047, 1, 4.8, 6829, 0.1, 1, false, new Item(12158), new Item(12155), new Item(2859), new Item(12183, 1)), /** * Represents a dreadfowl pouch. */ - DREADFOWL_POUCH(1, 12043, 4, 9.3, 6825, 0.1, 1, false, new Item(12158), new Item(12155), new Item(2138), new Item(12183, 8)), + DREADFOWL_POUCH(1, 12043, 4, 9.3, 6825, 0.1, 1, false, new Item(12158), new Item(12155), new Item(2138), new Item(12183, 1)), /** * Represents a spirit spider pouch. */ - SPIRIT_SPIDER_POUCH(2, 12059, 10, 12.6, 6841, 0.2, 2, false, new Item(12158), new Item(12155), new Item(6291), new Item(12183, 8)), + SPIRIT_SPIDER_POUCH(2, 12059, 10, 12.6, 6841, 0.2, 2, false, new Item(12158), new Item(12155), new Item(6291), new Item(12183, 1)), /** * Represents a thorny snail pouch. */ - THORNY_SNAIL_POUCH(3, 12019, 13, 12.6, 6806, 0.2, 2, false, new Item(12158), new Item(12155), new Item(3363), new Item(12183, 9)), + THORNY_SNAIL_POUCH(3, 12019, 13, 12.6, 6806, 0.2, 2, false, new Item(12158), new Item(12155), new Item(3363), new Item(12183, 1)), /** * 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, 1)), /** * Represents a spirit mosquito pouch. @@ -44,97 +44,97 @@ public enum SummoningPouch { /** * Represents a desrrt wyrm pouch. */ - DESERT_WYRM_POUCH(6, 12049, 18, 31.2, 6831, 0.4, 1, false, new Item(12159), new Item(12155), new Item(1783), new Item(12183, 45)), + DESERT_WYRM_POUCH(6, 12049, 18, 31.2, 6831, 0.4, 1, false, new Item(12159), new Item(12155), new Item(1783), new Item(12183, 1)), /** * Represents a spirit scorpion pouch. */ - SPIRIT_SCORPION_POUCH(7, 12055, 19, 83.2, 6837, 0.9, 2, false, new Item(12160), new Item(12155), new Item(3095), new Item(12183, 57)), + SPIRIT_SCORPION_POUCH(7, 12055, 19, 83.2, 6837, 0.9, 2, false, new Item(12160), new Item(12155), new Item(3095), new Item(12183, 1)), /** * Represents a spirit tz-kih pouch. */ - SPIRIT_TZ_KIH_POUCH(8, 12808, 22, 96.8, 7361, 1.1, 3, false, new Item(12160), new Item(12168), new Item(12155), new Item(12183, 64)), + SPIRIT_TZ_KIH_POUCH(8, 12808, 22, 96.8, 7361, 1.1, 3, false, new Item(12160), new Item(12168), new Item(12155), new Item(12183, 1)), /** * Represents an albino rat pouch. */ - ALBINO_RAT_POUCH(9, 12067, 23, 202.4, 6847, 2.3, 1, false, new Item(12163), new Item(12155), new Item(2134), new Item(12183, 75)), + ALBINO_RAT_POUCH(9, 12067, 23, 202.4, 6847, 2.3, 1, false, new Item(12163), new Item(12155), new Item(2134), new Item(12183, 1)), /** * Represents a spirit kalphite pouch. */ - SPIRIT_KALPHITE_POUCH(10, 12063, 25, 220, 6994, 2.5, 3,false, new Item(12163), new Item(12155), new Item(3138), new Item(12183, 51)), + SPIRIT_KALPHITE_POUCH(10, 12063, 25, 220, 6994, 2.5, 3,false, new Item(12163), new Item(12155), new Item(3138), new Item(12183, 1)), /** * Represents a compost mound pouch. */ - COMPOST_MOUND_POUCH(11, 12091, 28, 49.8, 6871, 0.6, 6, false, new Item(12159), new Item(12155), new Item(6032), new Item(12183, 47)), + COMPOST_MOUND_POUCH(11, 12091, 28, 49.8, 6871, 0.6, 6, false, new Item(12159), new Item(12155), new Item(6032), new Item(12183, 1)), /** * Represents a giant chinchompa pouch. */ - GIANT_CHINCHOMPA_POUCH(12, 12800, 29, 255.2, 7353, 2.9, 1, false, new Item(12163), new Item(12155), new Item(10033), new Item(12183, 84)), + GIANT_CHINCHOMPA_POUCH(12, 12800, 29, 255.2, 7353, 2.9, 1, false, new Item(12163), new Item(12155), new Item(10033), new Item(12183, 1)), /** * Represents a vampire bat pouch. */ - VAMPIRE_BAT_POUCH(13, 12053, 31, 136, 6835, 1.5, 4, false, new Item(12160), new Item(12155), new Item(3325), new Item(12183, 81)), + VAMPIRE_BAT_POUCH(13, 12053, 31, 136, 6835, 1.5, 4, false, new Item(12160), new Item(12155), new Item(3325), new Item(12183, 1)), /** * Represents a honey badger pouch. */ - HONEY_BADGER_POUCH(14, 12065, 32, 140.8, 6845, 1.6, 4, false, new Item(12160), new Item(12155), new Item(12156), new Item(12183, 84)), + HONEY_BADGER_POUCH(14, 12065, 32, 140.8, 6845, 1.6, 4, false, new Item(12160), new Item(12155), new Item(12156), new Item(12183, 1)), /** * Represents a beaver pouch. */ - BEAVER_POUCH(15, 12021, 33, 57.6, 6808, 0.7, 4, true, new Item(12159), new Item(12155), new Item(1519), new Item(12183, 72)), + BEAVER_POUCH(15, 12021, 33, 57.6, 6808, 0.7, 4, true, new Item(12159), new Item(12155), new Item(1519), new Item(12183, 1)), /** * Represents a void ravager pouch. */ - VOID_RAVAGER_POUCH(16, 12818, 34, 59.6, 7370, 0.7, 4, false, new Item(12159), new Item(12164), new Item(12155), new Item(12183, 74)), + VOID_RAVAGER_POUCH(16, 12818, 34, 59.6, 7370, 0.7, 4, false, new Item(12159), new Item(12164), new Item(12155), new Item(12183, 1)), /** * Represents a void spinner pouch. */ - VOID_SPINNER_POUCH(17, 12780, 34, 59.6, 7333, 0.7, 4, true, new Item(12163), new Item(12166), new Item(12155), new Item(12183, 74)), + VOID_SPINNER_POUCH(17, 12780, 34, 59.6, 7333, 0.7, 4, true, new Item(12163), new Item(12166), new Item(12155), new Item(12183, 1)), /** * Represents a void torcher pouch. */ - VOID_TORCHER_POUCH(18, 12798, 34, 59.6, 7351, 0.7, 4, false, new Item(12163), new Item(12167), new Item(12155), new Item(12183, 74)), + VOID_TORCHER_POUCH(18, 12798, 34, 59.6, 7351, 0.7, 4, false, new Item(12163), new Item(12167), new Item(12155), new Item(12183, 1)), /** * Represents a void shifter pouch. */ - VOID_SHIFTER_POUCH(19, 12814, 34, 59.6, 7367, 0.7, 4, false, new Item(12163), new Item(12165), new Item(12155), new Item(12183, 74)), + VOID_SHIFTER_POUCH(19, 12814, 34, 59.6, 7367, 0.7, 4, false, new Item(12163), new Item(12165), new Item(12155), new Item(12183, 1)), /** * Represents a bronze minotaur pouch. */ - BRONZE_MINOTAUR_POUCH(64, 12073, 36, 316.8, 6853, 3.6, 3, false, new Item(12163), new Item(12155), new Item(2349), new Item(12183, 102)), + BRONZE_MINOTAUR_POUCH(64, 12073, 36, 316.8, 6853, 3.6, 3, false, new Item(12163), new Item(12155), new Item(2349), new Item(12183, 1)), /** * Represents an iron minotaur pouch. */ - IRON_MINOTAUR_POUCH(65, 12075, 46, 404.8, 6855, 4.6, 9, false, new Item(12163), new Item(12155), new Item(2351), new Item(12183, 125)), + IRON_MINOTAUR_POUCH(65, 12075, 46, 404.8, 6855, 4.6, 9, false, new Item(12163), new Item(12155), new Item(2351), new Item(12183, 1)), /** * Represents a steel minotaur pouch. */ - STEEL_MINOTAUR_POUCH(66, 12077, 56, 492.8, 6857, 5.6, 9, false, new Item(12163), new Item(12155), new Item(2353), new Item(12183, 141)), + STEEL_MINOTAUR_POUCH(66, 12077, 56, 492.8, 6857, 5.6, 9, false, new Item(12163), new Item(12155), new Item(2353), new Item(12183, 1)), /** * Represents a mithril minotaur pouch. */ - MITHRIL_MINOTAUR_POUCH(67, 12079, 66, 580.8, 6859, 6.6, 9,false, new Item(12163), new Item(12155), new Item(2359), new Item(12183, 152)), + MITHRIL_MINOTAUR_POUCH(67, 12079, 66, 580.8, 6859, 6.6, 9,false, new Item(12163), new Item(12155), new Item(2359), new Item(12183, 1)), /** * Represents an adamant minotaur pouch. */ - ADAMANT_MINOTAUR_POUCH(68, 12081, 76, 668.8, 6861, 7.6, 9, false, new Item(12163), new Item(12155), new Item(2361), new Item(12183, 144)), + ADAMANT_MINOTAUR_POUCH(68, 12081, 76, 668.8, 6861, 7.6, 9, false, new Item(12163), new Item(12155), new Item(2361), new Item(12183, 1)), /** * Represents a rune minotaur pouch. @@ -144,132 +144,132 @@ public enum SummoningPouch { /** * Represents a bull ant pouch. */ - BULL_ANT_POUCH(20, 12087, 40, 52.8, 6867, 0.6, 5, false, new Item(12158), new Item(12155), new Item(6010), new Item(12183, 11)), + BULL_ANT_POUCH(20, 12087, 40, 52.8, 6867, 0.6, 5, false, new Item(12158), new Item(12155), new Item(6010), new Item(12183, 1)), /** * Represents a macaw pouch. */ - MACAW_POUCH(21, 12071, 41, 72.4, 6851, 0.8, 5, true, new Item(12159), new Item(12155), new Item(249), new Item(12183, 78)), + MACAW_POUCH(21, 12071, 41, 72.4, 6851, 0.8, 5, true, new Item(12159), new Item(12155), new Item(249), new Item(12183, 1)), /** * Represents an evil turnip pouch. */ - EVIL_TURNIP_POUCH(22, 12051, 42, 184.8, 6833, 2.1, 5, false, new Item(12160), new Item(12155), new Item(12153), new Item(12183, 104)), + EVIL_TURNIP_POUCH(22, 12051, 42, 184.8, 6833, 2.1, 5, false, new Item(12160), new Item(12155), new Item(12153), new Item(12183, 1)), /** * Represents a spirit cockatrice pouch. */ - SPIRIT_COCKATRICE_POUCH(23, 12095, 43, 75.2, 6875, 0.9, 5, false, new Item(12159), new Item(12155), new Item(12109), new Item(12183, 88)), + SPIRIT_COCKATRICE_POUCH(23, 12095, 43, 75.2, 6875, 0.9, 5, false, new Item(12159), new Item(12155), new Item(12109), new Item(12183, 1)), /** * Represents a spirit guthatrice pouch. */ - SPIRIT_GUTHATRICE_POUCH(24, 12097, 43, 75.2, 6877, 0.9, 5, false, new Item(12159), new Item(12155), new Item(12111), new Item(12183, 88)), + SPIRIT_GUTHATRICE_POUCH(24, 12097, 43, 75.2, 6877, 0.9, 5, false, new Item(12159), new Item(12155), new Item(12111), new Item(12183, 1)), /** * Represents a spirit saratrice pouch. */ - SPIRIT_SARATRICE_POUCH(25, 12099, 43, 75.2, 6879, 0.9, 5, false, new Item(12159), new Item(12155), new Item(12113), new Item(12183, 88)), + SPIRIT_SARATRICE_POUCH(25, 12099, 43, 75.2, 6879, 0.9, 5, false, new Item(12159), new Item(12155), new Item(12113), new Item(12183, 1)), /** * Represents a spirit zamatrice pouch. */ - SPIRIT_ZAMATRICE_POUCH(26, 12101, 43, 75.2, 6881, 0.9, 5, false, new Item(12159), new Item(12155), new Item(12115), new Item(12183, 88)), + SPIRIT_ZAMATRICE_POUCH(26, 12101, 43, 75.2, 6881, 0.9, 5, false, new Item(12159), new Item(12155), new Item(12115), new Item(12183, 1)), /** * Represents a spirit pengatrice pouch. */ - SPIRIT_PENGATRICE_POUCH(27, 12103, 43, 75.2, 6883, 0.9, 5, false, new Item(12159), new Item(12155), new Item(12117), new Item(12183, 88)), + SPIRIT_PENGATRICE_POUCH(27, 12103, 43, 75.2, 6883, 0.9, 5, false, new Item(12159), new Item(12155), new Item(12117), new Item(12183, 1)), /** * Represents a coraxatrice pouch. */ - SPIRIT_CORAXATRICE_POUCH(28, 12105, 43, 75.2, 6885, 0.9, 5, false, new Item(12159), new Item(12155), new Item(12119), new Item(12183, 88)), + SPIRIT_CORAXATRICE_POUCH(28, 12105, 43, 75.2, 6885, 0.9, 5, false, new Item(12159), new Item(12155), new Item(12119), new Item(12183, 1)), /** * Represents a vulatrice pouch. */ - SPIRIT_VULATRICE(29, 12107, 43, 75.2, 6887, 0.9, 5, false, new Item(12159), new Item(12155), new Item(12121), new Item(12183, 88)), + SPIRIT_VULATRICE(29, 12107, 43, 75.2, 6887, 0.9, 5, false, new Item(12159), new Item(12155), new Item(12121), new Item(12183, 1)), /** * Represents a pyrelord pouch. */ - PYRELORD_POUCH(30, 12816, 46, 202.4, 7377, 2.3, 5, false, new Item(12160), new Item(12155), new Item(590), new Item(12183, 111)), + PYRELORD_POUCH(30, 12816, 46, 202.4, 7377, 2.3, 5, false, new Item(12160), new Item(12155), new Item(590), new Item(12183, 1)), /** * Represents a magpie pouch. */ - MAGPIE_POUCH(31, 12041, 47, 83.2, 6824, 0.9, 5, true, new Item(12159), new Item(12155), new Item(1635), new Item(12183, 88)), + MAGPIE_POUCH(31, 12041, 47, 83.2, 6824, 0.9, 5, true, new Item(12159), new Item(12155), new Item(1635), new Item(12183, 1)), /** * Represents a bloated leech pouch. */ - BLOATED_LEECH_POUCH(32, 12061, 49, 215.2, 6843, 2.4, 5, false, new Item(12160), new Item(12155), new Item(2132), new Item(12183, 117)), + BLOATED_LEECH_POUCH(32, 12061, 49, 215.2, 6843, 2.4, 5, false, new Item(12160), new Item(12155), new Item(2132), new Item(12183, 1)), /** * Represents a spirit terrorbird pouch. */ - SPIRIT_TERRORBIRD_POUCH(33, 12007, 52, 68.4, 6794, 0.8, 6, true, new Item(12158), new Item(12155), new Item(9978), new Item(12183, 12)), + SPIRIT_TERRORBIRD_POUCH(33, 12007, 52, 68.4, 6794, 0.8, 6, true, new Item(12158), new Item(12155), new Item(9978), new Item(12183, 1)), /** * Represents an abyssal parasite pouch. */ - ABYSSAL_PARASITE_POUCH(34, true,12035, 54, 94.8, 6818, 1.1, 6, false, new Item(12159), new Item(12155), new Item(12161), new Item(12183, 106)), + ABYSSAL_PARASITE_POUCH(34, true,12035, 54, 94.8, 6818, 1.1, 6, false, new Item(12159), new Item(12155), new Item(12161), new Item(12183, 1)), /** * Represents a spirit jelly pouch. */ - SPIRIT_JELLY_POUCH(35, 12027, 55, 484, 6992, 5.5, 6, false, new Item(12163), new Item(12155), new Item(1937), new Item(12183, 151)), + SPIRIT_JELLY_POUCH(35, 12027, 55, 484, 6992, 5.5, 6, false, new Item(12163), new Item(12155), new Item(1937), new Item(12183, 1)), /** * Represents an ibis pouch. */ - IBIS_POUCH(36, 12531, 56, 98.8, 6991, 1.1, 6, true, new Item(12159), new Item(12155), new Item(311), new Item(12183, 109)), + IBIS_POUCH(36, 12531, 56, 98.8, 6991, 1.1, 6, true, new Item(12159), new Item(12155), new Item(311), new Item(12183, 1)), /** * Represents a spirit kyatt pouch. */ - SPIRIT_KYATT_POUCH(37, 12812, 57, 501.6, 7365, 5.7, 6, false, new Item(12163), new Item(12155), new Item(10103), new Item(12183, 153)), + SPIRIT_KYATT_POUCH(37, 12812, 57, 501.6, 7365, 5.7, 6, false, new Item(12163), new Item(12155), new Item(10103), new Item(12183, 1)), /** * Represents a spirit larupia pouch. */ - SPIRIT_LARUPIA_POUCH(38, 12784, 57, 501.6, 7337, 5.7, 6, false, new Item(12163), new Item(12155), new Item(10095), new Item(12183, 155)), + SPIRIT_LARUPIA_POUCH(38, 12784, 57, 501.6, 7337, 5.7, 6, false, new Item(12163), new Item(12155), new Item(10095), new Item(12183, 1)), /** * Represents a spirit graahk pouch. */ - SPIRIT_GRAAHK_POUCH(39, 12810, 57, 501.6, 7363, 5.7, 6, false, new Item(12163), new Item(12155), new Item(10099), new Item(12183, 154)), + SPIRIT_GRAAHK_POUCH(39, 12810, 57, 501.6, 7363, 5.7, 6, false, new Item(12163), new Item(12155), new Item(10099), new Item(12183, 1)), /** * Represents a karamthulhu overlord pouch. */ - KARAMTHULHU_POUCH(40, 12023, 58, 510.4, 6809, 5.8, 6, false, new Item(12163), new Item(12155), new Item(6667), new Item(12183, 144)), + KARAMTHULHU_POUCH(40, 12023, 58, 510.4, 6809, 5.8, 6, false, new Item(12163), new Item(12155), new Item(6667), new Item(12183, 1)), /** * Represents a smoke devil pouch. */ - SMOKE_DEVIL_POUCH(41, 12085, 61, 268, 6865, 3, 7, false, new Item(12160), new Item(12155), new Item(9736), new Item(12183, 141)), + SMOKE_DEVIL_POUCH(41, 12085, 61, 268, 6865, 3, 7, false, new Item(12160), new Item(12155), new Item(9736), new Item(12183, 1)), /** * Represents an abyssal lurker pouch. */ - ABYSSAL_LUKRER(42, true,12037, 62, 109.6, 6820, 1.9, 9, false, new Item(12159), new Item(12155), new Item(12161), new Item(12183, 119)), + ABYSSAL_LUKRER(42, true,12037, 62, 109.6, 6820, 1.9, 9, false, new Item(12159), new Item(12155), new Item(12161), new Item(12183, 1)), /** * Represents a spirit cobra pouch. */ - SPIRIT_COBRA_POUCH(43, 12015, 63, 276.8, 6802, 3.1, 6, false, new Item(12160), new Item(12155), new Item(6287), new Item(12183, 116)), + SPIRIT_COBRA_POUCH(43, 12015, 63, 276.8, 6802, 3.1, 6, false, new Item(12160), new Item(12155), new Item(6287), new Item(12183, 1)), /** * Represents a stranger plant pouch. */ - STRANGER_PLANT_POUCH(44, 12045, 64, 281.6, 6827, 3.2, 6, false, new Item(12160), new Item(12155), new Item(8431), new Item(12183, 128)), + STRANGER_PLANT_POUCH(44, 12045, 64, 281.6, 6827, 3.2, 6, false, new Item(12160), new Item(12155), new Item(8431), new Item(12183, 1)), /** * Represents a barker toad pouch. */ - BARKER_TOAD_POUCH(45, 12123, 66, 87, 6889, 1, 7, false, new Item(12158), new Item(12155), new Item(2150), new Item(12183, 11)), + BARKER_TOAD_POUCH(45, 12123, 66, 87, 6889, 1, 7, false, new Item(12158), new Item(12155), new Item(2150), new Item(12183, 1)), /** * Represents a war tortoise pouch. @@ -279,127 +279,127 @@ public enum SummoningPouch { /** * Represents a bunyip pouch. */ - BUNYIP_POUCH(47, 12029, 68, 119.2, 6813, 1.4, 7, true, new Item(12159), new Item(12155), new Item(383), new Item(12183, 110)), + BUNYIP_POUCH(47, 12029, 68, 119.2, 6813, 1.4, 7, true, new Item(12159), new Item(12155), new Item(383), new Item(12183, 1)), /** * Represents a fruit bat pouch. */ - FRUIT_BAT_POUCH(48, 12033, 69, 121.2, 6817, 1.4, 8, true, new Item(12159), new Item(12155), new Item(1963), new Item(12183, 130)), + FRUIT_BAT_POUCH(48, 12033, 69, 121.2, 6817, 1.4, 8, true, new Item(12159), new Item(12155), new Item(1963), new Item(12183, 1)), /** * Represents a ravenous locust pouch. */ - RAVENOUS_LOCUST_POUCH(49, 12820, 70, 132, 7372, 1.5, 4, false, new Item(12160), new Item(12155), new Item(1933), new Item(12183, 79)), + RAVENOUS_LOCUST_POUCH(49, 12820, 70, 132, 7372, 1.5, 4, false, new Item(12160), new Item(12155), new Item(1933), new Item(12183, 1)), /** * Represents an arctic bear pouch. */ - ARCTIC_BEAR_POUCH(50, 12057, 71, 93.2, 6839, 1.1, 8, false, new Item(12158), new Item(12155), new Item(10117), new Item(12183, 14)), + ARCTIC_BEAR_POUCH(50, 12057, 71, 93.2, 6839, 1.1, 8, false, new Item(12158), new Item(12155), new Item(10117), new Item(12183, 1)), /** * Represents a Phoenix */ - PHOENIX_POUCH(50, 14623, 72, 93.2, 8575, 1.1, 8, false, new Item(12160, 1), new Item(12183, 165), new Item(12155, 1), new Item(14616, 1)), + PHOENIX_POUCH(50, 14623, 72, 93.2, 8575, 1.1, 8, false, new Item(12160, 1), new Item(12183, 1), new Item(12155, 1), new Item(14616, 1)), /** * Represents an obsidian golem pouch. */ - OBSIDIAN_GOLEM_POUCH(51, 12792, 73, 642.4, 7345, 7.3, 8, false, new Item(12163), new Item(12155), new Item(12168), new Item(12183, 195)), + OBSIDIAN_GOLEM_POUCH(51, 12792, 73, 642.4, 7345, 7.3, 8, false, new Item(12163), new Item(12155), new Item(12168), new Item(12183, 1)), /** * Represents a granite lobster pouch. */ - GRANITE_LOBSTER_POUCH(52, 12069, 74, 325.6, 6849, 3.7, 8, false, new Item(12160), new Item(12155), new Item(6979), new Item(12183, 166)), + GRANITE_LOBSTER_POUCH(52, 12069, 74, 325.6, 6849, 3.7, 8, false, new Item(12160), new Item(12155), new Item(6979), new Item(12183, 1)), /** * Represents a praying mantis pouch. */ - PRAYING_MANTIS_POUCH(53, 12011, 75, 329.6, 6798, 3.6, 8, false, new Item(12160), new Item(12155), new Item(2460), new Item(12183, 168)), + PRAYING_MANTIS_POUCH(53, 12011, 75, 329.6, 6798, 3.6, 8, false, new Item(12160), new Item(12155), new Item(2460), new Item(12183, 1)), /** * Represents a forge regent pouch. */ - FORGE_REGENT_BEAST(54, 12782, 76, 134, 7335, 1.5, 9, false, new Item(12159), new Item(12155), new Item(10020), new Item(12183, 141)), + FORGE_REGENT_BEAST(54, 12782, 76, 134, 7335, 1.5, 9, false, new Item(12159), new Item(12155), new Item(10020), new Item(12183, 1)), /** * Represents a talon beast pouch. */ - TALON_BEAST_POUCH(55, 12794, 77, 1015.2, 7347, 3.8, 9, false, new Item(12160), new Item(12155), new Item(12162), new Item(12183, 174)), + TALON_BEAST_POUCH(55, 12794, 77, 1015.2, 7347, 3.8, 9, false, new Item(12160), new Item(12155), new Item(12162), new Item(12183, 1)), /** * Represents a giant ent pouch. */ - GIANT_ENT_POUCH(56, 12013, 78, 136.8, 6800, 1.6, 8, false, new Item(12159), new Item(5933), new Item(12155), new Item(12183, 124)), + GIANT_ENT_POUCH(56, 12013, 78, 136.8, 6800, 1.6, 8, false, new Item(12159), new Item(5933), new Item(12155), new Item(12183, 1)), /** * Represents a hydra pouch. */ - HYDRA_POUCH(60, 12025, 80, 140.8, 6811, 1.6, 9, false, new Item(12159), new Item(571), new Item(12183, 128)), + HYDRA_POUCH(60, 12025, 80, 140.8, 6811, 1.6, 9, false, new Item(12159), new Item(571), new Item(12155), new Item(12183, 1)), /** * Represents a spirit dagannoth pouch. */ - SPIRIT_DAGANNOTH_POUCH(61, 12017, 83, 364.8, 6804, 4.1, 9, false, new Item(12160), new Item(6155), new Item(12183, 1)), + SPIRIT_DAGANNOTH_POUCH(61, 12017, 83, 364.8, 6804, 4.1, 9, false, new Item(12160), new Item(6155), new Item(12155), new Item(12183, 1)), /** * Represents a unicorn stallion pouch. */ - UNICORN_STALLION_POUCH(70, 12039, 88, 154.4, 6822, 1.8, 9, true, new Item(12159), new Item(237), new Item(12183, 203)), + UNICORN_STALLION_POUCH(70, 12039, 88, 154.4, 6822, 1.8, 9, true, new Item(12159), new Item(237), new Item(12155), new Item(12183, 1)), /** * Represents a wolpertinger pouch. */ - WOLPERTINGER_POUCH(72, 12089, 92, 404.8, 6869, 4.5, 10, false, new Item(12160), new Item(2859), new Item(3226), new Item(12183, 203)), + WOLPERTINGER_POUCH(72, 12089, 92, 404.8, 6869, 4.5, 10, false, new Item(12160), new Item(2859), new Item(3226), new Item(12155), new Item(12183, 1)), /** * Represents a pack yak pouch. */ - PACK_YAK_POUCH(75, 12093, 96, 422.4, 6873, 4.8, 10, true, new Item(12160), new Item(10818), new Item(12183, 211)), + PACK_YAK_POUCH(75, 12093, 96, 422.4, 6873, 4.8, 10, true, new Item(12160), new Item(10818), new Item(12155), new Item(12183, 1)), /** * Represents a fire titan pouch. */ - FIRE_TITAN_POUCH(57, 12802, 79, 695.2, 7355, 7.9, 9, false, new Item(12163), new Item(1442), new Item(12155), new Item(12183, 198)), + FIRE_TITAN_POUCH(57, 12802, 79, 695.2, 7355, 7.9, 9, false, new Item(12163), new Item(1442), new Item(12155), new Item(12183, 1)), /** * Represents a moss titan pouch. */ - MOSS_TITAN_POUCH(58, 12804, 79, 695.2, 7357, 7.9, 9, false, new Item(12163), new Item(1440), new Item(12155), new Item(12183, 198)), + MOSS_TITAN_POUCH(58, 12804, 79, 695.2, 7357, 7.9, 9, false, new Item(12163), new Item(1440), new Item(12155), new Item(12183, 1)), /** * Represents an ice titan pouch. */ - ICE_TITAN_POUCH(59, 12806, 79, 695.2, 7359, 7.9, 9, false, new Item(12163), new Item(1438), new Item(1444), new Item(12155), new Item(12183, 198)), + ICE_TITAN_POUCH(59, 12806, 79, 695.2, 7359, 7.9, 9, false, new Item(12163), new Item(1438), new Item(1444), new Item(12155), new Item(12183, 1)), /** * Represents a lava titan pouch. */ - LAVA_TITAN_POUCH(62, 12788, 83, 730.4, 7341, 8.3, 9, false, new Item(12163), new Item(12168), new Item(12155), new Item(12183, 219)), + LAVA_TITAN_POUCH(62, 12788, 83, 730.4, 7341, 8.3, 9, false, new Item(12163), new Item(12168), new Item(12155), new Item(12183, 1)), /** * Represents a swamp titan pouch. */ - SWAMP_TITAN_POUCH(63, 12776, 85, 373.6, 7329, 4.2, 9, false, new Item(12160), new Item(10149), new Item(12155), new Item(12183, 150)), + SWAMP_TITAN_POUCH(63, 12776, 85, 373.6, 7329, 4.2, 9, false, new Item(12160), new Item(10149), new Item(12155), new Item(12183, 1)), /** * Represents a geyser titan pouch. */ - GEYSER_TITAN_POUCH(71, 12786, 89, 783.2, 7339, 8.9, 9, false, new Item(12163), new Item(1444), new Item(12155), new Item(12183, 222)), + GEYSER_TITAN_POUCH(71, 12786, 89, 783.2, 7339, 8.9, 9, false, new Item(12163), new Item(1444), new Item(12155), new Item(12183, 1)), /** * Represents an abyssal titan pouch. */ - ABYSSAL_TITAN_POUCH(73, true,12796, 93, 163.2, 7349, 1.9, 10, false, new Item(12159), new Item(12161), new Item(12155), new Item(12183, 113)), + ABYSSAL_TITAN_POUCH(73, true,12796, 93, 163.2, 7349, 1.9, 10, false, new Item(12159), new Item(12161), new Item(12155), new Item(12183, 1)), /** * Represents an iron titan pouch. */ - IRON_TITAN_POUCH(74, 12822, 95, 417.6, 7375, 4.7, 10, false, new Item(12160), new Item(1115), new Item(12155), new Item(12183, 198)), + IRON_TITAN_POUCH(74, 12822, 95, 417.6, 7375, 4.7, 10, false, new Item(12160), new Item(1115), new Item(12155), new Item(12183, 1)), /** * Represents a steel titan pouch. */ - STEEL_TITAN_POUCH(76, 12790, 99, 435.2, 7343, 4.9, 10, false, new Item(12160), new Item(1119), new Item(12155), new Item(12183, 178)), + STEEL_TITAN_POUCH(76, 12790, 99, 435.2, 7343, 4.9, 10, false, new Item(12160), new Item(1119), new Item(12155), new Item(12183, 1)), SACRED_CLAY_POUCH_1(-1, 14422, 1, 0, 8240, 0, 1, false, new Item(14182)), SACRED_CLAY_POUCH_2(-1, 14424, 20, 0, 8242, 0, 3, false, new Item(14184)), diff --git a/Server/src/main/content/global/skill/summoning/SummoningScroll.java b/Server/src/main/content/global/skill/summoning/SummoningScroll.java index 80ffe286d..a11c706d1 100644 --- a/Server/src/main/content/global/skill/summoning/SummoningScroll.java +++ b/Server/src/main/content/global/skill/summoning/SummoningScroll.java @@ -5,9 +5,85 @@ package content.global.skill.summoning; * @author 'Vexia */ public enum SummoningScroll { - HOWL_SCROLL(0, 12425, 0.1, 1, 12047), DREADFOWL_STRIKE_SCROLL(1, 12445, 0.1, 4, 12043), EGG_SPAWN_SCROLL(2, 12428, 0.2, 10, 12059), SLIME_SPRAY_SCROLL(3, 12459, 0.2, 13, 12019), STONY_SHELL_SCROLL(4, 12533, 0.2, 16, 12009), PESTER_SCROLL(5, 12838, 0.5, 17, 12778), ELECTRIC_LASH_SCROLL(6, 12460, 0.4, 18, 12049), VENOM_SHOT_SCROLL(7, 12432, 0.9, 19, 12055), FIREBALL_ASSAULT_SCROLL(8, 12839, 1.1, 22, 12808), CHEESE_FEAST_SCROLL(9, 12430, 2.3, 23, 12067), SANDSTORM_SCROLL(10, 12446, 2.5, 25, 12063), GENERATE_COMPOST_SCROLL(11, 12440, 0.6, 28, 12091), EXPLODE_SCROLL(12, 12834, 2.9, 29, 12800), VAMPYRE_TOUCH_SCROLL(13, 12447, 1.5, 31, 12053), INSANE_FEROCITY_SCROLL(14, 12433, 1.6, 32, 12065), MULTICHOP_SCROLL(15, 12429, 0.7, 33, 12021), CALL_TO_ARMS_SCROLL1(16, 12443, 0.7, 34, 12818), CALL_TO_ARMS_SCROLL2(17, 12443, 0.7, 34, 12814), CALL_TO_ARMS_SCROLL3(18, 12443, 0.7, 34, 12780), CALL_TO_ARMS_SCROLL4(19, 12443, 0.7, 34, 12798), BRONZE_BULL_RUSH_SCROLL(64, 12461, 3.6, 36, 12073), UNBURDEN_SCROLL(20, 12431, 0.6, 40, 12087), HERBCALL_SCROLL(21, 12422, 0.8, 41, 12071), EVIL_FLAMES_SCROLL(22, 12448, 2.1, 42, 12051), PETRIFYING_GAZE_SCROLL1(23, 12458, 0.9, 43, 12095), PETRIFYING_GAZE_SCROLL2(24, 12458, 0.9, 43, 12097), PETRIFYING_GAZE_SCROLL3(25, 12458, 0.9, 43, 12099), PETRIFYING_GAZE_SCROLL4(26, 12458, 0.9, 43, 12101), PETRIFYING_GAZE_SCROLL5(27, 12458, 0.9, 43, 12103), PETRIFYING_GAZE_SCROLL6(28, 12458, 0.9, 43, 12105), PETRIFYING_GAZE_SCROLL7(29, 12458, 0.9, 43, 12107), IRON_BULL_RUSH_SCROLL(65, 12462, 4.6, 46, 12075), IMMENSE_HEAT_SCROLL(30, 12829, 2.3, 46, 12816), THIEVING_FINGERS_SCROLL(31, 12426, 47, 167, 12041), BLOOD_DRAIN_SCROLL(32, 12444, 2.4, 49, 12061), TIRELESS_RUN_SCROLL(33, 12441, 0.8, 52, 12007), ABYSSAL_DRAIN_SCROLL(34, 12454, 1.1, 54, 12035), DISSOLVE_SCROLL(35, 12453, 5.5, 55, 12027), STEEL_BULL_RUSH_SCROLL(66, 12463, 5.6, 56, 12077), FISH_RAIN_SCROLL(36, 12424, 1.1, 56, 12531), AMBUSH_SCROLL(37, 12836, 5.7, 57, 12812), RENDING_SCROLL(38, 12840, 5.7, 57, 12784), GOAD_SCROLL(39, 12835, 5.7, 57, 12810), DOOMSPHERE_SCROLL(40, 12455, 5.8, 58, -1), DUST_CLOUD_SCROLL(41, 12468, 3.0, 61, 12085), ABYSSAL_STEALTH_SCROLL(42, 12427, 1.9, 62, 12037), OPHIDIAN_INCUBATION_SCROLL(43, 12436, 3.1, 63, 12015), POISONOUS_BLAST_SCROLL(44, 12467, 3.2, 64, 12045), MITHRIL_BULL_RUSH_SCROLL(67, 12464, 6.6, 66, 12079), TOAD_BARK_SCROLL(45, 12452, 1.0, 66, 12123), TESTUDO_SCROLL(46, 12439, 0.7, 67, 12031), SWALLOW_WHOLE_SCROLL(47, 12438, 1.4, 68, 12029), FRUITFALL_SCROLL(48, 12423, 1.4, 69, 12033), FAMINE_SCROLL(49, 12830, 1.5, 70, 12820), ARCTIC_BLAST_SCROLL(50, 12451, 1.1, 71, 12057), + HOWL_SCROLL(0, 12425, 0.1, 1, 12047), + DREADFOWL_STRIKE_SCROLL(1, 12445, 0.1, 4, 12043), + EGG_SPAWN_SCROLL(2, 12428, 0.2, 10, 12059), + SLIME_SPRAY_SCROLL(3, 12459, 0.2, 13, 12019), + STONY_SHELL_SCROLL(4, 12533, 0.2, 16, 12009), + PESTER_SCROLL(5, 12838, 0.5, 17, 12778), + ELECTRIC_LASH_SCROLL(6, 12460, 0.4, 18, 12049), + VENOM_SHOT_SCROLL(7, 12432, 0.9, 19, 12055), + FIREBALL_ASSAULT_SCROLL(8, 12839, 1.1, 22, 12808), + CHEESE_FEAST_SCROLL(9, 12430, 2.3, 23, 12067), + SANDSTORM_SCROLL(10, 12446, 2.5, 25, 12063), + GENERATE_COMPOST_SCROLL(11, 12440, 0.6, 28, 12091), + EXPLODE_SCROLL(12, 12834, 2.9, 29, 12800), + VAMPYRE_TOUCH_SCROLL(13, 12447, 1.5, 31, 12053), + INSANE_FEROCITY_SCROLL(14, 12433, 1.6, 32, 12065), + MULTICHOP_SCROLL(15, 12429, 0.7, 33, 12021), + CALL_TO_ARMS_SCROLL1(16, 12443, 0.7, 34, 12818), + CALL_TO_ARMS_SCROLL2(17, 12443, 0.7, 34, 12814), + CALL_TO_ARMS_SCROLL3(18, 12443, 0.7, 34, 12780), + CALL_TO_ARMS_SCROLL4(19, 12443, 0.7, 34, 12798), + BRONZE_BULL_RUSH_SCROLL(64, 12461, 3.6, 36, 12073), + UNBURDEN_SCROLL(20, 12431, 0.6, 40, 12087), + HERBCALL_SCROLL(21, 12422, 0.8, 41, 12071), + EVIL_FLAMES_SCROLL(22, 12448, 2.1, 42, 12051), + PETRIFYING_GAZE_SCROLL1(23, 12458, 0.9, 43, 12095), + PETRIFYING_GAZE_SCROLL2(24, 12458, 0.9, 43, 12097), + PETRIFYING_GAZE_SCROLL3(25, 12458, 0.9, 43, 12099), + PETRIFYING_GAZE_SCROLL4(26, 12458, 0.9, 43, 12101), + PETRIFYING_GAZE_SCROLL5(27, 12458, 0.9, 43, 12103), + PETRIFYING_GAZE_SCROLL6(28, 12458, 0.9, 43, 12105), + PETRIFYING_GAZE_SCROLL7(29, 12458, 0.9, 43, 12107), + IRON_BULL_RUSH_SCROLL(65, 12462, 4.6, 46, 12075), + IMMENSE_HEAT_SCROLL(30, 12829, 2.3, 46, 12816), + THIEVING_FINGERS_SCROLL(31, 12426, 47, 47, 12041), + BLOOD_DRAIN_SCROLL(32, 12444, 2.4, 49, 12061), + TIRELESS_RUN_SCROLL(33, 12441, 0.8, 52, 12007), + ABYSSAL_DRAIN_SCROLL(34, 12454, 1.1, 54, 12035), + DISSOLVE_SCROLL(35, 12453, 5.5, 55, 12027), + STEEL_BULL_RUSH_SCROLL(66, 12463, 5.6, 56, 12077), + FISH_RAIN_SCROLL(36, 12424, 1.1, 56, 12531), + AMBUSH_SCROLL(37, 12836, 5.7, 57, 12812), + RENDING_SCROLL(38, 12840, 5.7, 57, 12784), + GOAD_SCROLL(39, 12835, 5.7, 57, 12810), + DOOMSPHERE_SCROLL(40, 12455, 5.8, 58, -1), + DUST_CLOUD_SCROLL(41, 12468, 3.0, 61, 12085), + ABYSSAL_STEALTH_SCROLL(42, 12427, 1.9, 62, 12037), + OPHIDIAN_INCUBATION_SCROLL(43, 12436, 3.1, 63, 12015), + POISONOUS_BLAST_SCROLL(44, 12467, 3.2, 64, 12045), + MITHRIL_BULL_RUSH_SCROLL(67, 12464, 6.6, 66, 12079), + TOAD_BARK_SCROLL(45, 12452, 1.0, 66, 12123), + ESTUDO_SCROLL(46, 12439, 0.7, 67, 12031), + SWALLOW_WHOLE_SCROLL(47, 12438, 1.4, 68, 12029), + FRUITFALL_SCROLL(48, 12423, 1.4, 69, 12033), + FAMINE_SCROLL(49, 12830, 1.5, 70, 12820), + ARCTIC_BLAST_SCROLL(50, 12451, 1.1, 71, 12057), + // RISE_FROM_THE_ASHES_SCROLL(51, 14622, 8.0, 277, -1), - VOLCANIC_STRENGTH_SCROLL(51, 12826, 7.3, 73, 12792), CRUSHING_CLAW_SCROLL(52, 12449, 3.7, 74, 12069), MANTIS_STRIKE_SCROLL(53, 12450, 3.7, 75, 12011), INFERNO_SCROLL(54, 12841, 1.5, 76, 12782), ADAMANT_BULL_RUSH_SCROLL(68, 12465, 7.6, 76, 12081), DEADLY_CLAW_SCROLL(55, 12831, 11.0, 77, 12162), ACORN_MISSILE_SCROLL(56, 12457, 1.6, 78, 12013), TITANS_CONSTITUTION_SCROLL1(57, 12824, 7.9, 79, 12802), TITANS_CONSTITUTION_SCROLL2(58, 12824, 7.9, 79, 12806), TITANS_CONSTITUTION_SCROLL3(59, 12824, 7.9, 79, 12804), REGROWTH_SCROLL(60, 12442, 1.6, 80, 12025), SPIKE_SHOT_SCROLL(61, 12456, 4.1, 83, 12017), EBON_THUNDER_SCROLL(62, 12837, 8.3, 83, 12788), SWAMP_PLAGUE_SCROLL(63, 12832, 4.1, 85, 12776), RUNE_BULL_RUSH_SCROLL(69, 12466, 8.6, 86, 12083), HEALING_AURA_SCROLL(70, 12434, 1.8, 88, 12039), BOIL_SCROLL(71, 12833, 8.9, 89, 12786), MAGIC_FOCUS_SCROLL(72, 12437, 4.6, 92, 12089), ESSENCE_SHIPMENT_SCROLL(73, 12827, 1.9, 93, 12796), IRON_WITHIN_SCROLL(74, 12828, 4.7, 95, 12822), WINTER_STORAGE_SCROLL(75, 12435, 4.8, 96, 12093), STEEL_OF_LEGENDS_SCROLL(76, 12825, 4.9, 99, 12790), + VOLCANIC_STRENGTH_SCROLL(51, 12826, 7.3, 73, 12792), + CRUSHING_CLAW_SCROLL(52, 12449, 3.7, 74, 12069), + MANTIS_STRIKE_SCROLL(53, 12450, 3.7, 75, 12011), + INFERNO_SCROLL(54, 12841, 1.5, 76, 12782), + ADAMANT_BULL_RUSH_SCROLL(68, 12465, 7.6, 76, 12081), + DEADLY_CLAW_SCROLL(55, 12831, 11.0, 77, 12162), + ACORN_MISSILE_SCROLL(56, 12457, 1.6, 78, 12013), + TITANS_CONSTITUTION_SCROLL1(57, 12824, 7.9, 79, 12802), + TITANS_CONSTITUTION_SCROLL2(58, 12824, 7.9, 79, 12806), + TITANS_CONSTITUTION_SCROLL3(59, 12824, 7.9, 79, 12804), + REGROWTH_SCROLL(60, 12442, 1.6, 80, 12025), + SPIKE_SHOT_SCROLL(61, 12456, 4.1, 83, 12017), + EBON_THUNDER_SCROLL(62, 12837, 8.3, 83, 12788), + SWAMP_PLAGUE_SCROLL(63, 12832, 4.1, 85, 12776), + RUNE_BULL_RUSH_SCROLL(69, 12466, 8.6, 86, 12083), + HEALING_AURA_SCROLL(70, 12434, 1.8, 88, 12039), + BOIL_SCROLL(71, 12833, 8.9, 89, 12786), + MAGIC_FOCUS_SCROLL(72, 12437, 4.6, 92, 12089), + ESSENCE_SHIPMENT_SCROLL(73, 12827, 1.9, 93, 12796), + IRON_WITHIN_SCROLL(74, 12828, 4.7, 95, 12822), + WINTER_STORAGE_SCROLL(75, 12435, 4.8, 96, 12093), + STEEL_OF_LEGENDS_SCROLL(76, 12825, 4.9, 99, 12790), CLAY_DEPOSIT_SCROLL_1(-1, 14421, 0, 1, 14422), CLAY_DEPOSIT_SCROLL_2(-1, 14421, 0, 20, 14424), CLAY_DEPOSIT_SCROLL_3(-1, 14421, 0, 40, 14426), diff --git a/Server/src/main/content/global/skill/summoning/SummoningTabListener.kt b/Server/src/main/content/global/skill/summoning/SummoningTabListener.kt index be1afbd99..ee39b8470 100644 --- a/Server/src/main/content/global/skill/summoning/SummoningTabListener.kt +++ b/Server/src/main/content/global/skill/summoning/SummoningTabListener.kt @@ -1,52 +1,65 @@ import content.global.skill.summoning.familiar.BurdenBeast import content.global.skill.summoning.familiar.FamiliarSpecial +import content.global.skill.summoning.pet.Pet +import core.api.sendMessage import core.game.interaction.InterfaceListener +import content.global.skill.summoning.familiar.SnowscapeFamiliarInterface + class SummoningTabListener : InterfaceListener { override fun defineInterfaceListeners() { on(662) { player, _, opcode, buttonID, _, _ -> when(buttonID) { 51 -> { - if (player.getFamiliarManager().hasFamiliar()) { - player.getFamiliarManager().getFamiliar().call() + if (player.familiarManager.hasFamiliar()) { + player.familiarManager.familiar.call() } else { - player.getPacketDispatch().sendMessage("You don't have a follower.") + SnowscapeFamiliarInterface.Companion.openInterface(player) + //player.getPacketDispatch().sendMessage("You don't have a follower.") } } 67 -> { - if (player.getFamiliarManager().hasFamiliar()) { - if (!player.getFamiliarManager().getFamiliar().isBurdenBeast()) { + if (player.familiarManager.hasFamiliar()) { + if (player.familiarManager.familiar.isInvisible || !player.familiarManager.familiar.location.withinDistance(player.location)) { + sendMessage(player, "Your familiar is too far away!") + return@on true + } + if (!player.familiarManager.familiar.isBurdenBeast()) { player.getPacketDispatch().sendMessage("Your familiar is not a beast of burden.") return@on true } - val beast = player.getFamiliarManager().getFamiliar() as BurdenBeast; + val beast = player.familiarManager.familiar as BurdenBeast if (beast.getContainer().isEmpty()) { player.getPacketDispatch().sendMessage("Your familiar is not carrying any items.") return@on true } - beast.withdrawAll(); + beast.withdrawAll() return@on true } - player.getPacketDispatch().sendMessage("You don't have a follower."); + player.getPacketDispatch().sendMessage("You don't have a follower.") } 53 -> { - if (player.getFamiliarManager().hasFamiliar()) { + if (player.familiarManager.hasFamiliar()) { if(opcode == 155) { // Dismiss familiar - player.getDialogueInterpreter().open("dismiss_dial"); + player.getDialogueInterpreter().open("dismiss_dial") } else if(opcode == 196) { // Dismiss now - player.getFamiliarManager().dismiss(false); + if (player.getFamiliarManager().getFamiliar() is Pet) { + val pet = player.familiarManager.familiar as Pet + player.familiarManager.removeDetails(pet.getItemId()) + } + player.familiarManager.dismiss() } } else { - player.getPacketDispatch().sendMessage("You don't have a follower."); + player.getPacketDispatch().sendMessage("You don't have a follower.") } } else -> { - if (player.getFamiliarManager().hasFamiliar()) { - player.getFamiliarManager().getFamiliar().executeSpecialMove(FamiliarSpecial(player)); + if (player.familiarManager.hasFamiliar()) { + player.familiarManager.familiar.executeSpecialMove(FamiliarSpecial(player)) } else { - player.getPacketDispatch().sendMessage("You don't have a follower."); + player.getPacketDispatch().sendMessage("You don't have a follower.") } } } diff --git a/Server/src/main/content/global/skill/summoning/SummoningTrainingRoom.java b/Server/src/main/content/global/skill/summoning/SummoningTrainingRoom.java index d061fbbac..6296daef4 100644 --- a/Server/src/main/content/global/skill/summoning/SummoningTrainingRoom.java +++ b/Server/src/main/content/global/skill/summoning/SummoningTrainingRoom.java @@ -33,6 +33,9 @@ import core.net.packet.out.MinimapState; import core.plugin.Plugin; import core.plugin.ClassScanner; +import static core.api.ContentAPIKt.*; +import content.data.Quests; + /** * Handles the summoning training room. * @author Emperor @@ -79,11 +82,11 @@ 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": - player.getConfigManager().set(1178, questVal == 28893 ? 32989 : (1 << 11) + questVal); + setVarp(player, 1178, questVal == 28893 ? 32989 : (1 << 11) + questVal); return true; case "open": if (quest.getStage(player) < 40) { @@ -92,13 +95,13 @@ public final class SummoningTrainingRoom extends OptionHandler { } else if (quest.getStage(player) == 40) { if (player.getAttribute("has-key", false) || player.getInventory().remove(TRAPDOOR_KEY)) { player.setAttribute("has-key", true); - player.getConfigManager().set(1178, (2 << 11) + questVal, true); + setVarp(player, 1178, (2 << 11) + questVal, true); } else { player.getPacketDispatch().sendMessage("The trapdoor is locked."); return true; } } - player.getConfigManager().set(1178, questVal == 28893 ? 28893 : (2 << 11) + questVal); + setVarp(player, 1178, questVal == 28893 ? 28893 : (2 << 11) + questVal); return true; case "climb-down": if (object.getId() == 28653) { @@ -310,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/AbyssalLurkerNPC.java b/Server/src/main/content/global/skill/summoning/familiar/AbyssalLurkerNPC.java index 9fb1effdc..b6184c4a8 100644 --- a/Server/src/main/content/global/skill/summoning/familiar/AbyssalLurkerNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/AbyssalLurkerNPC.java @@ -28,7 +28,7 @@ public class AbyssalLurkerNPC extends BurdenBeast { * @param id The id. */ public AbyssalLurkerNPC(Player owner, int id) { - super(owner, id, 4100, 12037, 3, 7, WeaponInterface.STYLE_CAST); + super(owner, id, 4100, 12037, 3, 24, WeaponInterface.STYLE_CAST); } @Override diff --git a/Server/src/main/content/global/skill/summoning/familiar/AbyssalParasiteNPC.java b/Server/src/main/content/global/skill/summoning/familiar/AbyssalParasiteNPC.java index 795484722..551e2db9a 100644 --- a/Server/src/main/content/global/skill/summoning/familiar/AbyssalParasiteNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/AbyssalParasiteNPC.java @@ -36,7 +36,7 @@ public class AbyssalParasiteNPC extends BurdenBeast { * @param id The id. */ public AbyssalParasiteNPC(Player owner, int id) { - super(owner, id, 3000, 12035, 1, 7); + super(owner, id, 3000, 12035, 1, 18); } @Override diff --git a/Server/src/main/content/global/skill/summoning/familiar/AbyssalTitanNPC.java b/Server/src/main/content/global/skill/summoning/familiar/AbyssalTitanNPC.java deleted file mode 100644 index 716a32629..000000000 --- a/Server/src/main/content/global/skill/summoning/familiar/AbyssalTitanNPC.java +++ /dev/null @@ -1,65 +0,0 @@ -package content.global.skill.summoning.familiar; - -import core.game.node.entity.combat.equipment.WeaponInterface; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; -import core.game.world.update.flag.context.Animation; -import core.plugin.Initializable; -import core.game.world.update.flag.context.Graphics; - -/** - * Represents the Abyssal Titan familiar. - * @author Aero - * @author Splinter - */ -@Initializable -public class AbyssalTitanNPC extends Familiar { - - /** - * Constructs a new {@code AbyssalTitanNPC} {@code Object}. - */ - public AbyssalTitanNPC() { - this(null, 7349); - } - - /** - * Constructs a new {@code AbyssalTitanNPC} {@code Object}. - * @param owner The owner. - * @param id The id. - */ - public AbyssalTitanNPC(Player owner, int id) { - super(owner, id, 3200, 12796, 6, WeaponInterface.STYLE_ACCURATE); - } - - @Override - public Familiar construct(Player owner, int id) { - return new AbyssalTitanNPC(owner, id); - } - - @Override - protected boolean specialMove(FamiliarSpecial special) { - Player player = owner; - if (!player.getInventory().containsItem(new Item(1436, 1)) && !player.getInventory().containsItem(new Item(7936, 1))) { - player.sendMessage("You have no essence to send to the bank."); - return false; - } - int RUNE_ESSENCE_AMOUNT = player.getInventory().getAmount(1436); - int PURE_ESSENCE_AMOUNT = player.getInventory().getAmount(7936); - player.getInventory().remove(new Item(1436, RUNE_ESSENCE_AMOUNT), new Item(7936, PURE_ESSENCE_AMOUNT)); - player.getBank().add(new Item(1436, RUNE_ESSENCE_AMOUNT), new Item(7936, PURE_ESSENCE_AMOUNT)); - player.sendMessage("The titan sends " + RUNE_ESSENCE_AMOUNT + " rune essence and " + PURE_ESSENCE_AMOUNT + " pure essence to your bank."); - return true; - } - - @Override - public void visualizeSpecialMove() { - owner.visualize(Animation.create(7660), Graphics.create(1316)); - this.visualize(Animation.create(7694), Graphics.create(1457)); - } - - @Override - public int[] getIds() { - return new int[] { 7349, 7350 }; - } - -} diff --git a/Server/src/main/content/global/skill/summoning/familiar/AbyssalTitanNPC.kt b/Server/src/main/content/global/skill/summoning/familiar/AbyssalTitanNPC.kt new file mode 100644 index 000000000..eaeb73b45 --- /dev/null +++ b/Server/src/main/content/global/skill/summoning/familiar/AbyssalTitanNPC.kt @@ -0,0 +1,90 @@ +package content.global.skill.summoning.familiar + +import core.api.Container +import core.api.addItem +import core.api.amountInInventory +import core.api.removeItem +import core.game.node.entity.combat.equipment.WeaponInterface +import core.game.node.entity.player.Player +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 org.rs09.consts.Items +import org.rs09.consts.NPCs + +/** + * Represents the Abyssal Titan familiar. + * @author Aero + * @author Splinter + */ +@Initializable +class AbyssalTitanNPC +constructor(owner: Player? = null, id: Int = NPCs.ABYSSAL_TITAN_7349) : + BurdenBeast(owner, id, 3200, Items.ABYSSAL_TITAN_POUCH_12796, 6, 30, WeaponInterface.STYLE_ACCURATE) { + override fun construct(owner: Player, id: Int): Familiar { + return AbyssalTitanNPC(owner, id) + } + + override fun isAllowed(owner: Player, item: Item): Boolean { + return when (item.id) { + Items.RUNE_ESSENCE_1436, Items.PURE_ESSENCE_7936 -> super.isAllowed(owner, item) + else -> { + owner.packetDispatch.sendMessage("Your familiar can only hold unnoted essence.") + false + } + } + } + + override fun specialMove(special: FamiliarSpecial): Boolean { + val playerRuneEssenceAmount = amountInInventory(owner, Items.RUNE_ESSENCE_1436) + val playerPureEssenceAmount = amountInInventory(owner, Items.PURE_ESSENCE_7936) + val beastRuneEssenceAmount = this.container.getAmount(Items.RUNE_ESSENCE_1436) + val beastPureEssenceAmount = this.container.getAmount(Items.PURE_ESSENCE_7936) + val totalRuneEssence = playerRuneEssenceAmount + beastRuneEssenceAmount + val totalPureEssence = playerPureEssenceAmount + beastPureEssenceAmount + + if ((totalRuneEssence + totalPureEssence) == 0) { + owner.sendMessage("You have no essence to send to the bank.") + return false + } + + if (!owner.bank.hasSpaceFor( + Item(Items.RUNE_ESSENCE_1436, totalRuneEssence), + Item(Items.PURE_ESSENCE_7936, totalPureEssence) + ) + ) { + owner.sendMessage("You have no space in your bank to deposit your essence.") + return false + } + + + if (removeItem(owner, Item(Items.RUNE_ESSENCE_1436, playerRuneEssenceAmount))) { + addItem(owner, Items.RUNE_ESSENCE_1436, playerRuneEssenceAmount, Container.BANK) + } + + if (removeItem(owner, Item(Items.PURE_ESSENCE_7936, playerPureEssenceAmount))) { + addItem(owner, Items.PURE_ESSENCE_7936, playerPureEssenceAmount, Container.BANK) + } + + if (this.container.remove(Item(Items.RUNE_ESSENCE_1436, beastRuneEssenceAmount))) { + addItem(owner, Items.RUNE_ESSENCE_1436, beastRuneEssenceAmount, Container.BANK) + } + + if (this.container.remove(Item(Items.PURE_ESSENCE_7936, beastPureEssenceAmount))) { + addItem(owner, Items.PURE_ESSENCE_7936, beastPureEssenceAmount, Container.BANK) + } + + owner.sendMessage("The titan sends $totalRuneEssence rune essence and $totalPureEssence pure essence to your bank.") + return true + } + + override fun visualizeSpecialMove() { + owner.visualize(Animation.create(7660), Graphics.create(1316)) + visualize(Animation.create(7694), Graphics.create(1457)) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.ABYSSAL_TITAN_7349, NPCs.ABYSSAL_TITAN_7350) + } +} \ No newline at end of file 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/BloatedLeechNPC.java b/Server/src/main/content/global/skill/summoning/familiar/BloatedLeechNPC.java index fc8ba7c12..8dfb2ef0e 100644 --- a/Server/src/main/content/global/skill/summoning/familiar/BloatedLeechNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/BloatedLeechNPC.java @@ -5,9 +5,10 @@ import core.game.node.entity.skill.Skills; import core.game.node.entity.combat.ImpactHandler.HitsplatType; import core.game.node.entity.combat.equipment.WeaponInterface; import core.game.node.entity.player.Player; -import core.game.node.entity.state.EntityState; import core.tools.RandomFunction; +import static core.api.ContentAPIKt.*; + /** * Represents the Bloated Leech familiar. * @author Aero @@ -38,14 +39,18 @@ public class BloatedLeechNPC extends Familiar { @Override protected boolean specialMove(FamiliarSpecial special) { - owner.getStateManager().remove(EntityState.POISONED); + curePoison(owner); + removeTimer(owner, "disease"); for (int i = 0; i < Skills.SKILL_NAME.length; i++) { if (owner.getSkills().getLevel(i) < owner.getSkills().getStaticLevel(i)) { - owner.getSkills().setLevel(i, owner.getSkills().getStaticLevel(i)); + owner.getSkills().updateLevel( + i, + (int) Math.ceil(owner.getSkills().getStaticLevel(i) * 0.2), + owner.getSkills().getStaticLevel(i) + ); } } - owner.getSkills().rechargePrayerPoints(); - getImpactHandler().manualHit(owner, RandomFunction.random(2), HitsplatType.NORMAL); + owner.getImpactHandler().manualHit(owner, RandomFunction.random(1, 5), HitsplatType.NORMAL); return true; } diff --git a/Server/src/main/content/global/skill/summoning/familiar/BunyipNPC.java b/Server/src/main/content/global/skill/summoning/familiar/BunyipNPC.java index dab0ee56b..df1c54e37 100644 --- a/Server/src/main/content/global/skill/summoning/familiar/BunyipNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/BunyipNPC.java @@ -94,7 +94,7 @@ public class BunyipNPC extends Familiar { player.sendMessage("You can't use this special on an object like that."); return false; } - Consumable consumable = Consumables.getConsumableById(special.getItem().getId() + 2); + Consumable consumable = Consumables.getConsumableById(special.getItem().getId() + 2).getConsumable(); if (consumable == null) { player.sendMessage("Error: Report to admin."); return false; @@ -138,7 +138,7 @@ public class BunyipNPC extends Familiar { public boolean handle(NodeUsageEvent event) { Player player = event.getPlayer(); Fish fish = Fish.forItem(event.getUsedItem()); - Consumable consumable = Consumables.getConsumableById(fish.getItem().getId() + 2); + Consumable consumable = Consumables.getConsumableById(fish.getItem().getId() + 2).getConsumable(); if (consumable == null) { return true; } diff --git a/Server/src/main/content/global/skill/summoning/familiar/BurdenBeast.java b/Server/src/main/content/global/skill/summoning/familiar/BurdenBeast.java index d6a3100d0..628ff5077 100644 --- a/Server/src/main/content/global/skill/summoning/familiar/BurdenBeast.java +++ b/Server/src/main/content/global/skill/summoning/familiar/BurdenBeast.java @@ -83,6 +83,7 @@ public abstract class BurdenBeast extends Familiar { * @return {@code True} if so. */ public boolean isAllowed(Player owner, Item item) { + /* if (item.getValue() > 50000) { owner.getPacketDispatch().sendMessage("This item is too valuable to trust to this familiar."); return false; @@ -95,6 +96,7 @@ public abstract class BurdenBeast extends Familiar { owner.getPacketDispatch().sendMessage("You can't store " + item.getName().toLowerCase() + " in this familiar."); return false; } + */ if(SummoningPouch.get(this.getPouchId()).abyssal){ if(!item.getName().toLowerCase().contains("essence")) { owner.getPacketDispatch().sendMessage("You can only give unnoted essence to this familiar."); diff --git a/Server/src/main/content/global/skill/summoning/familiar/BurdenInterfacePlugin.java b/Server/src/main/content/global/skill/summoning/familiar/BurdenInterfacePlugin.java index f9a8fd1a2..84de00f93 100644 --- a/Server/src/main/content/global/skill/summoning/familiar/BurdenInterfacePlugin.java +++ b/Server/src/main/content/global/skill/summoning/familiar/BurdenInterfacePlugin.java @@ -11,6 +11,8 @@ import core.plugin.Initializable; import core.plugin.Plugin; import kotlin.Unit; +import content.global.handlers.item.SnowscapeSatchelListener; + /** * Handles the beast of burden interface. * @author Emperor @@ -27,6 +29,14 @@ public final class BurdenInterfacePlugin extends ComponentPlugin { @Override public boolean handle(Player player, Component component, int opcode, int button, int slot, int itemId) { + if (getAttribute(player, "openSatchel", null) != null) { + SnowscapeSatchelListener.Companion.satchelInterfaceAction(player, component, opcode, button, slot, itemId); + return true; + } + if (getAttribute(player, "openSummonStorage", false)) { + SnowscapeFamiliarInterface.Companion.interfaceAction(player, component, opcode, button, slot, itemId); + return true; + } if (!player.getFamiliarManager().hasFamiliar() || !player.getFamiliarManager().getFamiliar().isBurdenBeast()) { return false; } 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/DesertWyrmNPC.java b/Server/src/main/content/global/skill/summoning/familiar/DesertWyrmNPC.java index 9fc7576fd..0510469cd 100644 --- a/Server/src/main/content/global/skill/summoning/familiar/DesertWyrmNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/DesertWyrmNPC.java @@ -3,11 +3,11 @@ package content.global.skill.summoning.familiar; import java.util.ArrayList; import java.util.List; +import content.global.skill.gather.mining.MiningNode; import core.cache.def.impl.NPCDefinition; import core.plugin.Initializable; import core.game.node.entity.skill.SkillBonus; import core.game.node.entity.skill.Skills; -import content.global.skill.gather.SkillingResource; import core.game.interaction.OptionHandler; import core.game.node.Node; import core.game.node.entity.Entity; @@ -95,7 +95,7 @@ public final class DesertWyrmNPC extends Forager { player.getPacketDispatch().sendMessage("There are no rocks around here for the desert wyrm to mine from!"); return true; } - final SkillingResource resource = SkillingResource.forId(rock.getId()); + final MiningNode resource = MiningNode.forId(rock.getId()); if (resource == null) { player.getPacketDispatch().sendMessage("There are no rocks around here for the desert wyrm to mine from!"); return true; @@ -143,8 +143,8 @@ public final class DesertWyrmNPC extends Forager { int ordinal = 0; Scenery o = null; for (Scenery r : rocks) { - SkillingResource resource = SkillingResource.forId(r.getId()); - if (resource != null && resource.ordinal() > ordinal) { + MiningNode resource = MiningNode.forId(r.getId()); + if (resource != null && MiningNode.SILVER_ORE_0.ordinal() > resource.ordinal() && resource.ordinal() > ordinal) { ordinal = resource.ordinal(); o = r; } 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 641f95121..b369bf4d6 100644 --- a/Server/src/main/content/global/skill/summoning/familiar/DismissDialoguePlugin.java +++ b/Server/src/main/content/global/skill/summoning/familiar/DismissDialoguePlugin.java @@ -54,10 +54,12 @@ public final class DismissDialoguePlugin extends DialoguePlugin { case 1: 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.getItemId()); } else { end(); } - player.getFamiliarManager().dismiss(false); + player.getFamiliarManager().dismiss(); stage = 1; break; case 2: 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 ffef05556..636787050 100644 --- a/Server/src/main/content/global/skill/summoning/familiar/Familiar.java +++ b/Server/src/main/content/global/skill/summoning/familiar/Familiar.java @@ -23,16 +23,21 @@ import core.game.world.map.zone.ZoneRestriction; import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Graphics; import core.plugin.Plugin; +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; import java.util.ArrayList; import java.util.List; +import static core.api.ContentAPIKt.*; + +import core.game.component.Component; + /** * Represents a familiar. * @author Emperor @@ -77,7 +82,7 @@ public abstract class Familiar extends NPC implements Plugin { /** * The amount of special points left. */ - protected int specialPoints = 60; + protected int specialPoints = 0; /** * The pouch id. @@ -116,7 +121,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}. @@ -137,6 +165,25 @@ 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; + // Snowscape: removing the initial drain and making it drain over the life of the familiar. Removing the +1 makes the final drain happen on the last tick of the familiar's life. That works for our new system. + int drain = pouch.getLevelRequired(); + this.pointsPerTick = (double) drain / maximumTicks; + } } /** @@ -172,17 +219,28 @@ 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); + //Snowscape: ticks are calculated from the summoning points (including fractional drain), so the familiar will despawn when points reach zero, but the timer goes up if points are restored + //ticks--; + ticks = Double.valueOf(maximumTicks*(owner.getSkills().getLevel(Skills.SUMMONING)-fracDrain)/pouch.getLevelRequired()).intValue(); + if (getWorldTicks() % 100 == 0) { + owner.getSkills().addExperience(Skills.SUMMONING, pouch.getLevelRequired()+10, true); + } + + fracDrain += pointsPerTick; + if (fracDrain > 1.0) { + fracDrain -= 1.0; owner.getSkills().updateLevel(Skills.SUMMONING, -1, 0); + } + if (getWorldTicks() % 3 == 0) { + updateSpecialPoints(-1); if (!getText().isEmpty()) { super.sendChat(getText()); } @@ -201,6 +259,7 @@ public abstract class Familiar extends NPC implements Plugin { } else { owner.getPacketDispatch().sendMessage("Your familiar has vanished."); } + playAudio(owner, Sounds.PRAYER_DRAIN_2672); dismiss(); return; } @@ -227,7 +286,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; @@ -239,13 +298,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."); } } @@ -253,13 +312,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; @@ -336,45 +395,44 @@ public abstract class Familiar extends NPC implements Plugin { private void sendTimeRemaining() { int minutes = ticks / 100; int centiminutes = ticks % 100; - owner.varpManager.get(1176) - .setVarbit(7, minutes) - .setVarbit(6, centiminutes > 49 ? 1 : 0) - .send(owner); + setVarbit(owner, 4534, minutes); + setVarbit(owner, 4290, centiminutes > 49 ? 1 : 0); } /** * Checks if the familiar can execute its special move and does so if able. * @param special The familiar special object. */ - public void executeSpecialMove(FamiliarSpecial special) { + public boolean executeSpecialMove(FamiliarSpecial special) { if (special.getNode() == this) { - return; + return false; } if (specialCost > specialPoints) { owner.getPacketDispatch().sendMessage("Your familiar does not have enough special move points left."); - return; + return false; } SummoningScroll scroll = SummoningScroll.forPouch(pouchId); if (scroll == null) { owner.getPacketDispatch().sendMessage("Invalid scroll for pouch " + pouchId + " - report!"); - return; + return false; } if (!owner.getInventory().contains(scroll.getItemId(), 1)) { owner.getPacketDispatch().sendMessage("You do not have enough scrolls left to do this special move."); - return; + return false; } if (owner.getLocation().getDistance(getLocation()) > 15) { owner.getPacketDispatch().sendMessage("Your familiar is too far away to use that scroll, or it cannot see you."); - return; + return false; } if (specialMove(special)) { setAttribute("special-delay", GameWorld.getTicks() + 3); owner.getInventory().remove(new Item(scroll.getItemId())); - owner.getAudioManager().send(4161); + playAudio(owner, Sounds.SPELL_4161); visualizeSpecialMove(); updateSpecialPoints(specialCost); owner.getSkills().addExperience(Skills.SUMMONING, scroll.getExperience(), true); } + return true; } /** @@ -539,9 +597,9 @@ public abstract class Familiar extends NPC implements Plugin { * Sends the familiar packets. */ public void sendConfiguration() { - owner.getConfigManager().set(448, getPouchId()); - owner.getConfigManager().set(1174, getOriginalId()); - owner.getConfigManager().set(1175, specialCost << 23); + setVarp(owner, 448, getPouchId()); + setVarp(owner, 1174, getOriginalId()); + setVarp(owner, 1175, specialCost << 23); sendTimeRemaining(); updateSpecialPoints(0); } @@ -558,15 +616,17 @@ public abstract class Familiar extends NPC implements Plugin { //spamTimer = 50; return false; } - setInvisible(getZoneMonitor().isRestricted(ZoneRestriction.FOLLOWERS) && !owner.getLocks().isLocked("enable_summoning")); + setInvisible(owner.getZoneMonitor().isRestricted(ZoneRestriction.FOLLOWERS) && !owner.getLocks().isLocked("enable_summoning")); + if (isInvisible()) return true; getProperties().setTeleportLocation(destination); if (!(this instanceof Pet)) { - if(firstCall) { - owner.getAudioManager().send(4366); - firstCall = false; - } else { - owner.getAudioManager().send(188); - } + 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); + } if (size() > 1) { graphics(LARGE_SUMMON_GRAPHIC); } else { @@ -579,7 +639,7 @@ public abstract class Familiar extends NPC implements Plugin { face(owner); } if (!isRenderable() && owner.isActive()) { - // SystemLogger.logErr(this.getClass(), "Familiar in inactive region!"); + // log(this.getClass(), Log.ERR, "Familiar in inactive region!"); getWalkingQueue().update(); getUpdateMasks().prepare(this); } @@ -602,14 +662,13 @@ public abstract class Familiar extends NPC implements Plugin { getPulseManager().clear(); owner.getInterfaceManager().removeTabs(7); owner.getFamiliarManager().setFamiliar(null); - owner.getConfigManager().set(448, -1); - owner.getConfigManager().set(1176, 0); - owner.getConfigManager().set(1175, 182986); - owner.getConfigManager().set(1174, -1); - if (owner.getSkullManager().isWilderness()) { - owner.getAppearance().sync(); - } - owner.getInterfaceManager().setViewedTab(3); + setVarp(owner, 448, -1); + setVarp(owner, 1176, 0); + setVarp(owner, 1175, 182986); + setVarp(owner, 1174, -1); + owner.getAppearance().sync(); + owner.getInterfaceManager().openTab(new Component(662)); + owner.getInterfaceManager().setViewedTab(7); } /** @@ -621,14 +680,14 @@ public abstract class Familiar extends NPC implements Plugin { if (specialPoints > 60) { specialPoints = 60; } - owner.getConfigManager().set(1177, specialPoints); + setVarp(owner, 1177, specialPoints); } @Override public Plugin newInstance(Object arg) throws Throwable { for (int id : getIds()) { if (FamiliarManager.getFamiliars().containsKey(id)) { - SystemLogger.logErr(this.getClass(), "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/FamiliarFeedPlugin.java b/Server/src/main/content/global/skill/summoning/familiar/FamiliarFeedPlugin.java index 309047589..74f73e439 100644 --- a/Server/src/main/content/global/skill/summoning/familiar/FamiliarFeedPlugin.java +++ b/Server/src/main/content/global/skill/summoning/familiar/FamiliarFeedPlugin.java @@ -18,7 +18,7 @@ public final class FamiliarFeedPlugin extends UseWithHandler { * Constructs a new {@code FamiliarFeedPlugin} {@code Object}. */ public FamiliarFeedPlugin() { - super(321, 363, 341, 15264, 345, 377, 353, 389, 7944, 349, 331, 327, 395, 383, 317, 371, 335, 359, 15264, 15270, 1927, 321, 363, 341, 15264, 345, 377, 353, 389, 7944, 349, 331, 327, 395, 383, 317, 371, 335, 359, 15264, 15270, 1927, 321, 363, 341, 15264, 345, 377, 353, 389, 7944, 349, 331, 327, 395, 383, 317, 371, 335, 359, 15264, 15270, 1927, 321, 363, 341, 15264, 345, 377, 353, 389, 7944, 349, 331, 327, 395, 383, 317, 371, 335, 359, 15264, 15270, 1927, 321, 363, 341, 15264, 345, 377, 353, 389, 7944, 349, 331, 327, 395, 383, 317, 371, 335, 359, 15264, 15270, 1927, 321, 363, 341, 15264, 345, 377, 353, 389, 7944, 349, 331, 327, 395, 383, 317, 371, 335, 359, 15264, 15270, 1927, 321, 363, 341, 15264, 345, 377, 353, 389, 7944, 349, 331, 327, 395, 383, 317, 371, 335, 359, 15264, 15270, 1927, 321, 363, 341, 15264, 345, 377, 353, 389, 7944, 349, 331, 327, 395, 383, 317, 371, 335, 359, 15264, 15270, 1927, 2132, 2134, 2136, 2138, 10816, 9986, 9978, 526, 2132, 2134, 2136, 2138, 10816, 9986, 9978, 526, 2132, 2134, 2136, 2138, 10816, 9986, 9978, 526, 2132, 2134, 2136, 2138, 10816, 9986, 9978, 526, 2132, 2134, 2136, 2138, 10816, 9986, 9978, 526, 2132, 2134, 2136, 2138, 10816, 9986, 9978, 526, 2132, 2134, 2136, 2138, 10816, 9986, 9978, 526, 2132, 2134, 2136, 2138, 10816, 9986, 9978, 526, 2132, 2134, 2136, 2138, 10816, 9986, 9978, 526, 2132, 2134, 2136, 2138, 10816, 9986, 9978, 526, 2132, 2134, 2136, 2138, 10816, 9986, 9978, 526, 2132, 2134, 2136, 2138, 10816, 9986, 9978, 526, 2132, 2134, 2136, 2138, 10816, 9986, 9978, 526, 2132, 2134, 2136, 2138, 10816, 9986, 9978, 526, 2132, 2134, 2136, 2138, 10816, 9986, 9978, 526, 2132, 2134, 2136, 2138, 10816, 9986, 9978, 526, 2132, 2134, 2136, 2138, 10816, 9986, 9978, 526, 2132, 2134, 2136, 2138, 10816, 9986, 9978, 526, 1059, 225, 221, 526, 592, 12125, 12127, 12125, 12127, 12125, 12127, 12125, 12127, 12125, 12127, 321, 363, 341, 15264, 345, 377, 353, 389, 7944, 349, 331, 327, 395, 383, 317, 371, 335, 359, 15264, 15270, 313, 12129, 321, 363, 341, 15264, 345, 377, 353, 389, 7944, 349, 331, 327, 395, 383, 317, 371, 335, 359, 15264, 15270, 313, 12129, 321, 363, 341, 15264, 345, 377, 353, 389, 7944, 349, 331, 327, 395, 383, 317, 371, 335, 359, 15264, 15270, 313, 12129, 2970, 321, 363, 341, 15264, 345, 377, 353, 389, 7944, 349, 331, 327, 395, 383, 317, 371, 335, 359, 15264, 15270, 321, 363, 341, 15264, 345, 377, 353, 389, 7944, 349, 331, 327, 395, 383, 317, 371, 335, 359, 15264, 15270, 321, 363, 341, 15264, 345, 377, 353, 389, 7944, 349, 331, 327, 395, 383, 317, 371, 335, 359, 15264, 15270, 1927, 1977, 321, 363, 341, 15264, 345, 377, 353, 389, 7944, 349, 331, 327, 395, 383, 317, 371, 335, 359, 15264, 15270, 321, 363, 341, 15264, 345, 377, 353, 389, 7944, 349, 331, 327, 395, 383, 317, 371, 335, 359, 15264, 15270, 321, 363, 341, 15264, 345, 377, 353, 389, 7944, 349, 331, 327, 395, 383, 317, 371, 335, 359, 15264, 15270, 321, 363, 341, 15264, 345, 377, 353, 389, 7944, 349, 331, 327, 395, 383, 317, 371, 335, 359, 15264, 15270, 321, 363, 341, 15264, 345, 377, 353, 389, 7944, 349, 331, 327, 395, 383, 317, 371, 335, 359, 15264, 15270, 313, 12129, 313, 12129, 313, 12129, 313, 12129, 313, 12129, 313, 12129, 12130, 12130, 12130, 12130, 12130, 313, 12129, 313, 12129, 313, 12129, 13379, 592, 592, 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, 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, 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, 221, 313, 12129, 313, 12129, 313, 12129, 313, 12129, 313, 12129, 313, 12129, 12125, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 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, 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, 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, 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); + super(321, 363, 341, 15264, 345, 377, 353, 389, 7944, 349, 331, 327, 395, 383, 317, 371, 335, 359, 15270, 1927, 321, 2132, 2134, 2136, 2138, 10816, 9986, 9978, 526, 1059, 225, 221, 592, 12125, 12127, 313, 12129, 2970, 1977, 12130, 13379, 1963, 319, 365, 339, 347, 379, 355, 391, 7946, 351, 329, 325, 397, 385, 315, 373, 333, 361, 1927); } @Override 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 e1538f0dd..6b5064acb 100644 --- a/Server/src/main/content/global/skill/summoning/familiar/FamiliarManager.java +++ b/Server/src/main/content/global/skill/summoning/familiar/FamiliarManager.java @@ -19,12 +19,14 @@ import core.game.world.update.flag.context.Animation; import java.util.ArrayList; import java.util.HashMap; -import java.util.List; import java.util.Map; +import static core.api.ContentAPIKt.*; + /** * Handles a player's familiar. * @author Emperor + * @author Player Name */ public final class FamiliarManager { @@ -34,9 +36,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. @@ -58,11 +60,6 @@ public final class FamiliarManager { */ private boolean hasPouch; - /** - * The list of insured pets. - */ - private List insuredPets = new ArrayList<>(20); - /** * Constructs a new {@code FamiliarManager} {@code Object}. * @param player The player. @@ -71,38 +68,92 @@ public final class FamiliarManager { this.player = player; } - public final void parse(JSONObject familiarData){ - 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())); - details.setStage(Integer.parseInt(detail.get("stage").toString())); - this.petDetails.put(Integer.parseInt(detail.get("petId").toString()),details); + public void parse(JSONObject familiarData) { + int currentPet = -1; + if (familiarData.containsKey("currentPet")) { + currentPet = Integer.parseInt(familiarData.get("currentPet").toString()); } - - if(familiarData.containsKey("currentPet")){ - int currentPet = Integer.parseInt( familiarData.get("currentPet").toString()); - PetDetails details = this.petDetails.get(currentPet); - Pets pets = Pets.forId(currentPet); - if (details == null) { - details = new PetDetails(pets.getGrowthRate() == 0.0 ? 100.0 : 0.0); - this.petDetails.put(currentPet, details); + 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()); + } + } } - familiar = new Pet(player, details, currentPet, pets.getNpcId(details.getStage())); - } else if(familiarData.containsKey("familiar")){ + + 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); + } + } + } + if (currentPet != -1) { + 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); + } + 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()); familiar = FAMILIARS.get(familiarId).construct(player,familiarId); familiar.ticks = Integer.parseInt( currentFamiliar.get("ticks").toString()); familiar.specialPoints = Integer.parseInt( currentFamiliar.get("specialPoints").toString()); JSONArray famInv = (JSONArray) currentFamiliar.get("inventory"); - if(famInv != null){ + if (famInv != null) { ((BurdenBeast) familiar).container.parse(famInv); } familiar.setAttribute("hp",Integer.parseInt( currentFamiliar.get("lifepoints").toString())); - } + } else { + //Snowscape: Copied from Familiar.dismiss(), this clears the fields on the summoning tab if no familiar is loaded + setVarp(player, 448, -1); + setVarp(player, 1176, 0); + setVarp(player, 1175, 182986); + setVarp(player, 1174, -1); + } } /** @@ -122,14 +173,14 @@ public final class FamiliarManager { * @param deleteItem we should delete the item. */ public void summon(Item item, boolean pet, boolean deleteItem) { - boolean renew = false; + boolean renew = false; if (hasFamiliar()) { - if(familiar.getPouchId() == item.getId()) { - renew = true; - } else { - player.getPacketDispatch().sendMessage("You already have a follower."); - return; - } + //if (familiar.getPouchId() == item.getId()) { + // renew = true; + //} else { + player.getPacketDispatch().sendMessage("You already have a follower."); + return; + //} } if (player.getZoneMonitor().isRestricted(ZoneRestriction.FOLLOWERS) && !player.getLocks().isLocked("enable_summoning")) { player.getPacketDispatch().sendMessage("This is a Summoning-free area."); @@ -147,38 +198,42 @@ public final class FamiliarManager { player.getPacketDispatch().sendMessage("You need a Summoning level of " + pouch.getLevelRequired() + " to summon this familiar."); return; } + /* Snowscape: removing the drain on summon. This drain will instead be added to the gradual drain over the familiar's life. if (player.getSkills().getLevel(Skills.SUMMONING) < pouch.getSummonCost()) { player.getPacketDispatch().sendMessage("You need at least " + pouch.getSummonCost() + " Summoning points to summon this familiar."); return; } + */ + if (player.getSkills().getLevel(Skills.SUMMONING) < 1) { + player.getPacketDispatch().sendMessage("You need at least one Summoning point to summon this familiar."); + return; + } final int npcId = pouch.getNpcId(); Familiar fam = !renew ? FAMILIARS.get(npcId) : familiar; if (fam == null) { - player.getPacketDispatch().sendMessage("Invalid familiar " + npcId + " - report on 2009scape github"); + player.getPacketDispatch().sendMessage("Invalid familiar " + npcId + " - report on 2009Scape GitLab"); return; } - 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"); - player.getPacketDispatch().sendMessage("area."); - return; - } - } - if (!player.getInventory().remove(item)) { - return; + 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"); + player.getPacketDispatch().sendMessage("area."); + return; + } } - player.getSkills().updateLevel(Skills.SUMMONING, -pouch.getSummonCost(), 0); - player.getSkills().addExperience(Skills.SUMMONING, pouch.getSummonExperience()); - if(!renew) { - familiar = fam; - spawnFamiliar(); - } else { - familiar.refreshTimer(); - } - if (player.getSkullManager().isWilderness()) { - player.getAppearance().sync(); + //if (!player.getInventory().remove(item)) { + // return; + //} + //player.getSkills().updateLevel(Skills.SUMMONING, -pouch.getSummonCost(), 0); + //player.getSkills().addExperience(Skills.SUMMONING, pouch.getSummonExperience()); + if (!renew) { + familiar = fam; + spawnFamiliar(); + } else { + familiar.refreshTimer(); } + player.getAppearance().sync(); } /** @@ -196,11 +251,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); } /** @@ -209,7 +263,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); } /** @@ -217,7 +271,7 @@ 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(); if (itemId > 8850 && itemId < 8900) { return false; @@ -230,23 +284,27 @@ public final class FamiliarManager { player.getDialogueInterpreter().sendDialogue("You need a summoning level of " + pets.getSummoningLevel() + " to summon this."); return false; } - int baseItemId = pets.getBabyItemId(); - PetDetails details = petDetails.get(baseItemId); - if (details == null) { - details = new PetDetails(pets.getGrowthRate() == 0.0 ? 100.0 : 0.0); - petDetails.put(baseItemId, details); + if (!this.petDetails.containsKey(itemId)) { + petDetails.put(itemId, new ArrayList()); } - int id = pets.getItemId(details.getStage()); - if (itemId != id) { - player.getPacketDispatch().sendMessage("This is not the right pet, grow the pet correctly."); - return true; + 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); } - int npcId = pets.getNpcId(details.getStage()); + 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)); - player.getInventory().remove(item); + if (!player.getInventory().remove(item, true)) { + return false; + } } if (morph) { morphFamiliar(location); @@ -313,8 +371,7 @@ public final class FamiliarManager { return; } Pet pet = ((Pet) familiar); - PetDetails details = pet.getDetails(); - if (player.getInventory().add(new Item(pet.getPet().getItemId(details.getStage())))) { + if (player.getInventory().add(new Item(pet.getItemId()))) { player.animate(Animation.create(827)); player.getFamiliarManager().dismiss(); } @@ -361,22 +418,23 @@ public final class FamiliarManager { /** * Dismisses the familiar. - * @param saveDetails the details of a pet. */ - public void dismiss(boolean saveDetails) { - if (hasPet() && !saveDetails) { - removeDetails(((Pet) familiar).getItemId()); - } + public void dismiss() { if (hasFamiliar()) { familiar.dismiss(); } } /** - * Dismisses the familiar. + * 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 dismiss() { - dismiss(true); + public void addDetails(int itemId, PetDetails details) { + if (petDetails.get(itemId) == null) { + petDetails.put(itemId, new ArrayList<>()); + } + petDetails.get(itemId).add(details); } /** @@ -384,11 +442,10 @@ public final class FamiliarManager { * @param itemId The item id of the pet. */ public void removeDetails(int itemId) { - Pets pets = Pets.forId(itemId); - if (pets == null) { - return; + int last = petDetails.get(itemId).size() - 1; + if (last >= 0) { + petDetails.get(itemId).remove(last); } - petDetails.remove(pets.getBabyItemId()); } /** @@ -412,9 +469,9 @@ public final class FamiliarManager { * @param value the value. */ public void setConfig(int value) { - int current = player.getConfigManager().get(1160); + int current = getVarp(player, 1160); int newVal = current + value; - player.getConfigManager().set(1160, newVal); + setVarp(player, 1160, newVal); } /** @@ -446,7 +503,7 @@ public final class FamiliarManager { * @return The usingSummoning. */ public boolean isUsingSummoning() { - return hasPouch || hasFamiliar(); + return hasPouch || (hasFamiliar() && !hasPet()); } /** @@ -482,7 +539,7 @@ public final class FamiliarManager { } - public Map getPetDetails() { + public Map> getPetDetails() { return petDetails; } } diff --git a/Server/src/main/content/global/skill/summoning/familiar/FamiliarNPCOptionPlugin.java b/Server/src/main/content/global/skill/summoning/familiar/FamiliarNPCOptionPlugin.java index cbaf1aa92..6801931b9 100644 --- a/Server/src/main/content/global/skill/summoning/familiar/FamiliarNPCOptionPlugin.java +++ b/Server/src/main/content/global/skill/summoning/familiar/FamiliarNPCOptionPlugin.java @@ -4,7 +4,6 @@ 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.game.world.update.flag.player.FaceLocationFlag; import core.plugin.Initializable; import core.plugin.Plugin; @@ -39,7 +38,7 @@ public final class FamiliarNPCOptionPlugin extends OptionHandler { } switch (option) { case "pick-up": - player.faceLocation(FaceLocationFlag.getFaceLocation(player, familiar)); + player.faceLocation(familiar.getFaceLocation(player.getLocation())); player.getFamiliarManager().pickup(); break; case "interact-with": diff --git a/Server/src/main/content/global/skill/summoning/familiar/ForgeRegentNPC.java b/Server/src/main/content/global/skill/summoning/familiar/ForgeRegentNPC.java index fddb9af88..3c560fc41 100644 --- a/Server/src/main/content/global/skill/summoning/familiar/ForgeRegentNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/ForgeRegentNPC.java @@ -12,7 +12,6 @@ import core.game.system.task.Pulse; import core.game.world.GameWorld; import core.game.world.map.RegionManager; import core.game.world.update.flag.context.Animation; -import core.game.world.update.flag.player.FaceLocationFlag; import core.plugin.Initializable; import core.plugin.Plugin; import core.plugin.ClassScanner; @@ -153,7 +152,7 @@ public class ForgeRegentNPC extends Familiar { familiar.moveStep(); GroundItemManager.destroy(ground); player.getSkills().addExperience(Skills.FIREMAKING, log.getXp() + 10); - familiar.faceLocation(FaceLocationFlag.getFaceLocation(familiar, object)); + familiar.faceLocation(object.getFaceLocation(familiar.getLocation())); SceneryBuilder.add(object, log.getLife(), FireMakingPulse.getAsh(player, log, object)); if (player.getViewport().getRegion().getId() == 10806) { player.getAchievementDiaryManager().finishTask(player, DiaryType.SEERS_VILLAGE, 1, 9); diff --git a/Server/src/main/content/global/skill/summoning/familiar/GiantEntNPC.java b/Server/src/main/content/global/skill/summoning/familiar/GiantEntNPC.java index a55bd3fa5..5ed4db98e 100644 --- a/Server/src/main/content/global/skill/summoning/familiar/GiantEntNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/GiantEntNPC.java @@ -77,10 +77,10 @@ public class GiantEntNPC extends Forager { public void modifyFarmingReward(FarmingPatch fPatch, Item reward) { PatchType patchType = fPatch.getType(); - if(patchType == PatchType.FRUIT_TREE || - patchType == PatchType.BUSH || - patchType == PatchType.BELLADONNA || - patchType == PatchType.CACTUS) { + if(patchType == PatchType.FRUIT_TREE_PATCH || + patchType == PatchType.BUSH_PATCH || + patchType == PatchType.BELLADONNA_PATCH || + patchType == PatchType.CACTUS_PATCH) { if(RandomFunction.roll(2)) { reward.setAmount(2 * reward.getAmount()); } 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/IbisNPC.java b/Server/src/main/content/global/skill/summoning/familiar/IbisNPC.java index 43fc3702a..7e0c82d73 100644 --- a/Server/src/main/content/global/skill/summoning/familiar/IbisNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/IbisNPC.java @@ -40,7 +40,7 @@ public class IbisNPC extends Forager { * @param id The id. */ public IbisNPC(Player owner, int id) { - super(owner, id, 3800, 12531, 12, new Item(361), new Item(373)); + super(owner, id, 3800, 12531, 12, new Item(359), new Item(371)); boosts.add(new SkillBonus(Skills.FISHING, 3)); } @@ -54,7 +54,7 @@ public class IbisNPC extends Forager { @Override public boolean produceItem(Item item) { if (super.produceItem(item)) { - if (item.getId() == 373) { + if (item.getId() == 371) { owner.getSkills().addExperience(Skills.FISHING, 10); } return true; 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/MinotaurFamiliarNPC.java b/Server/src/main/content/global/skill/summoning/familiar/MinotaurFamiliarNPC.java index 33e8f9ba4..2fbdf0d35 100644 --- a/Server/src/main/content/global/skill/summoning/familiar/MinotaurFamiliarNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/MinotaurFamiliarNPC.java @@ -5,7 +5,6 @@ import core.game.node.entity.combat.CombatStyle; import core.game.node.entity.combat.equipment.WeaponInterface; import core.game.node.entity.impl.Projectile; import core.game.node.entity.player.Player; -import core.game.node.entity.state.EntityState; import core.game.system.task.Pulse; import core.game.world.GameWorld; import core.game.world.update.flag.context.Animation; @@ -15,6 +14,8 @@ import core.plugin.ClassScanner; import core.plugin.Initializable; import core.tools.RandomFunction; +import static core.api.ContentAPIKt.stun; + /** * The plugin used to load the minotaur familiar npcs. * @author Vexia @@ -58,7 +59,7 @@ public final class MinotaurFamiliarNPC implements Plugin { GameWorld.getPulser().submit(new Pulse(ticks) { @Override public boolean pulse() { - target.getStateManager().set(EntityState.STUNNED, 4); + stun(target, 4); return true; } }); diff --git a/Server/src/main/content/global/skill/summoning/familiar/PackYakNPC.java b/Server/src/main/content/global/skill/summoning/familiar/PackYakNPC.java index f159bf4c0..b55b6594c 100644 --- a/Server/src/main/content/global/skill/summoning/familiar/PackYakNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/PackYakNPC.java @@ -1,5 +1,8 @@ package content.global.skill.summoning.familiar; +import core.api.Container; +import core.game.node.entity.player.info.LogType; +import core.game.node.entity.player.info.PlayerMonitor; import core.game.system.config.ItemConfigParser; import core.plugin.Initializable; import content.global.skill.summoning.SummoningScroll; @@ -9,6 +12,9 @@ import core.game.node.item.Item; import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Graphics; +import static core.api.ContentAPIKt.addItem; +import static core.api.ContentAPIKt.removeItem; + /** * Represents the Pack Yak familiar. * @author Aero @@ -45,7 +51,7 @@ public class PackYakNPC extends BurdenBeast { return false; } if (!item.getDefinition().getConfiguration(ItemConfigParser.BANKABLE, true)) { - player.sendMessage("A magical force prevents you from banking this item"); + player.sendMessage("A magical force prevents you from banking this item."); return false; } Item remove = item; @@ -53,12 +59,25 @@ public class PackYakNPC extends BurdenBeast { remove = new Item(item.getId(), 1); item = new Item(item.getNoteChange(), 1); } - if (player.getInventory().remove(remove) && player.getBank().add(item)) { + boolean success = addItem(player, item.getId(), item.getAmount(), Container.BANK); + if (success) { + success = removeItem(player, remove, Container.INVENTORY); + if (!success) { + // Add worked, but remove failed. This should never happen (it by definition constitutes an item duplication). + boolean recovered = removeItem(player, item, Container.BANK); + if (recovered) { + PlayerMonitor.log(player, LogType.DUPE_ALERT, "Successfully recovered from potential dupe attempt involving the winter storage scroll"); + } else { + PlayerMonitor.log(player, LogType.DUPE_ALERT, "Failed to recover from potentially successful dupe attempt involving the winter storage scroll"); + } + } + } + if (success) { player.getDialogueInterpreter().close(); graphics(Graphics.create(1358)); - player.getPacketDispatch().sendMessage("The pak yak has sent an item to your bank."); + player.getPacketDispatch().sendMessage("The pack yak has sent an item to your bank."); } else { - player.getPacketDispatch().sendMessage("The pak yak can't send that item to your bank."); + player.getPacketDispatch().sendMessage("The pack yak can't send that item to your bank."); } return true; } diff --git a/Server/src/main/content/global/skill/summoning/familiar/PyreLordNPC.java b/Server/src/main/content/global/skill/summoning/familiar/PyreLordNPC.java index 46f4b2aaf..1e96d0229 100644 --- a/Server/src/main/content/global/skill/summoning/familiar/PyreLordNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/PyreLordNPC.java @@ -21,9 +21,10 @@ import core.game.world.GameWorld; import core.game.world.map.RegionManager; import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Graphics; -import core.game.world.update.flag.player.FaceLocationFlag; import core.plugin.Plugin; import core.plugin.ClassScanner; +import org.rs09.consts.Items; +import static core.api.ContentAPIKt.inInventory; /** * Represents the Pyrelord familiar. @@ -118,30 +119,41 @@ public class PyreLordNPC extends Familiar { player.getPacketDispatch().sendMessage("You can't light a fire here."); return false; } - familiar.lock(ticks); - familiar.animate(FIREMAKE_ANIMATION); - if (player.getInventory().remove(event.getUsedItem())) { - final GroundItem ground = GroundItemManager.create(event.getUsedItem(), familiar.getLocation(), player); + if (player.getSkills().getLevel(Skills.FIREMAKING) < log.getLevel()) { + player.getPacketDispatch().sendMessage("You need a firemaking level of " + log.getLevel() + " to light this log."); + return false; + } + familiar.lock(ticks); + familiar.animate(FIREMAKE_ANIMATION); + // Snowscape: reworked this pulse to repeat GameWorld.getPulser().submit(new Pulse(ticks, player, familiar) { @Override public boolean pulse() { - if (!ground.isActive()) { - return true; - } - final Scenery object = new Scenery(log.getFireId(), familiar.getLocation()); - familiar.moveStep(); - GroundItemManager.destroy(ground); - player.getSkills().addExperience(Skills.FIREMAKING, log.getXp() + 10); - familiar.faceLocation(FaceLocationFlag.getFaceLocation(familiar, object)); - SceneryBuilder.add(object, log.getLife(), FireMakingPulse.getAsh(player, log, object)); - if (player.getViewport().getRegion().getId() == 10806) { - player.getAchievementDiaryManager().finishTask(player, DiaryType.SEERS_VILLAGE, 1, 9); - } - return true; + if (player.getInventory().remove(event.getUsedItem())) { + //familiar.lock(ticks-1); + familiar.animate(FIREMAKE_ANIMATION); + //final GroundItem ground = GroundItemManager.create(event.getUsedItem(), familiar.getLocation(), player); + //if (!ground.isActive()) { + // return false; + //} + final Scenery object = new Scenery(log.getFireId(), familiar.getLocation()); + //familiar.moveStep(); + //GroundItemManager.destroy(ground); + player.getSkills().addExperience(Skills.FIREMAKING, log.getXp() + 10); + familiar.faceLocation(object.getFaceLocation(familiar.getLocation())); + SceneryBuilder.add(object, log.getLife(), FireMakingPulse.getAsh(player, log, object)); + if (player.getViewport().getRegion().getId() == 10806) { + player.getAchievementDiaryManager().finishTask(player, DiaryType.SEERS_VILLAGE, 1, 9); + } + if (inInventory(player, Items.TINDERBOX_590, 1)) { + return false; + } + } + return true; } }); - } return true; + } } diff --git a/Server/src/main/content/global/skill/summoning/familiar/RavenousLocustNPC.java b/Server/src/main/content/global/skill/summoning/familiar/RavenousLocustNPC.java index bd1cb72c4..ad6d5180b 100644 --- a/Server/src/main/content/global/skill/summoning/familiar/RavenousLocustNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/RavenousLocustNPC.java @@ -52,7 +52,7 @@ public class RavenousLocustNPC extends Familiar { if (item == null) { continue; } - Consumable consumable = Consumables.getConsumableById(item.getId()); + Consumable consumable = Consumables.getConsumableById(item.getId()).getConsumable(); if (consumable != null) { p.getInventory().remove(item); break; 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/SnowscapeFamiliarInterface.kt b/Server/src/main/content/global/skill/summoning/familiar/SnowscapeFamiliarInterface.kt new file mode 100644 index 000000000..388b746a0 --- /dev/null +++ b/Server/src/main/content/global/skill/summoning/familiar/SnowscapeFamiliarInterface.kt @@ -0,0 +1,96 @@ +package content.global.skill.summoning.familiar + +import core.api.* +import core.game.container.Container +import content.global.skill.summoning.familiar.BurdenContainerListener +//import core.game.node.Node +import core.game.node.entity.player.Player +import core.game.node.item.Item +//import core.game.interaction.InteractionListener +//import core.game.interaction.IntType +//import org.json.simple.JSONArray +//import org.json.simple.JSONObject +//import org.json.simple.parser.JSONParser +import org.rs09.consts.Items + +import core.game.component.Component +import core.game.component.CloseEvent +import core.game.container.access.InterfaceContainer + +/* Custom Snowscape class. Allows storing summoning pouches in a special storage that can be accessed by using the "call familiar" button when no familiars are summoned. +* Familiars can be summoned from this storage. +* Designed around the snowscape summoning rework, where pouches are not consumed when summoning +*/ + +class SnowscapeFamiliarInterface { + companion object { + + + //Function to handle constructing the interface + public fun openInterface(player: Player) { + val storage = player.summoningPouches + if (storage.getListeners().count() < 1) storage.register(BurdenContainerListener(player)) + + player.getInterfaceManager().open(Component(671)).setCloseEvent(CloseEvent { player, component -> + player.getInterfaceManager().closeSingleTab() + removeAttribute(player, "openSummonStorage") + //Summoning tab cannot be interacted with until it's closed and re-opened for some reason + player.getInterfaceManager().removeTabs(7) + player.getInterfaceManager().openTab(Component(662)) + player.getInterfaceManager().setViewedTab(7) + return@CloseEvent true + } + ) + setAttribute(player, "openSummonStorage", true) + storage.shift() + player.getInterfaceManager().openSingleTab(Component(665)) + InterfaceContainer.generateItems(player, player.getInventory().toArray(), arrayOf("Add"),665,0,7,4,93) + InterfaceContainer.generateItems(player, storage.toArray(), arrayOf("Remove","Summon"),671,27,5,6,30) + } + + + fun interfaceAction(player: Player, component: Component, opcode: Int, button: Int, slot: Int, itemId: Int) { + val inventory = component.getId() == 665 + val container = if (inventory) player.getInventory() else player.summoningPouches + val item = if (slot >= 0 && slot < container.capacity()) container.get(slot) else null + if (item == null && button != 29) return + + if (opcode == 9) player.sendMessage(item!!.getDefinition().getExamine()) + + if (inventory) { + when (opcode) { + 155 -> addPouch(player, item!!) + } + } else { + when (opcode) { + 155 -> if (button != 29) summonPouch(player, item!!) + 196 -> removePouch(player, item!!) + } + } + } + + private fun addPouch(player: Player, item: Item) { + if (!hasOption(item, "Summon")){ + sendMessage(player, "You can only add summoning pouches.") + return + } else if (player.summoningPouches.containsItem(item)) { + sendMessage(player, "You have already added this familiar's pouch.") + return + } else if (player.summoningPouches.add(item)) { + player.getInventory().remove(item) + } + } + + private fun removePouch(player: Player, item: Item) { + if (player.getInventory().add(item)) { + player.summoningPouches.remove(item) + } + } + + private fun summonPouch(player: Player, item: Item) { + player.getInterfaceManager().close() + SummonFamiliarPlugin().handle(player, item, "summon") + } + + } +} \ No newline at end of file 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/SpiritScorpionNPC.java b/Server/src/main/content/global/skill/summoning/familiar/SpiritScorpionNPC.java index f1f3600c2..85bacafa4 100644 --- a/Server/src/main/content/global/skill/summoning/familiar/SpiritScorpionNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/SpiritScorpionNPC.java @@ -8,11 +8,12 @@ import core.game.node.entity.combat.equipment.Weapon; import core.game.node.entity.combat.equipment.WeaponInterface; import core.game.node.entity.impl.Projectile; import core.game.node.entity.player.Player; -import core.game.node.entity.state.EntityState; import core.game.node.item.Item; import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Graphics; +import static core.api.ContentAPIKt.*; + /** * Represents the Spirit Scorpion familiar. * @author Vexia @@ -49,7 +50,7 @@ public class SpiritScorpionNPC extends Familiar { if (isCharged() && new Item(weapon.getId() + 6).getName().startsWith(weapon.getName())) { final Entity victim = state.getVictim(); setCharged(false); - victim.getStateManager().register(EntityState.POISONED, false, 10, owner); + applyPoison(victim, owner, 1); } } } diff --git a/Server/src/main/content/global/skill/summoning/familiar/SpiritWolfNPC.java b/Server/src/main/content/global/skill/summoning/familiar/SpiritWolfNPC.java index bd12dba8d..9c528bde7 100644 --- a/Server/src/main/content/global/skill/summoning/familiar/SpiritWolfNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/SpiritWolfNPC.java @@ -15,6 +15,9 @@ import core.game.world.map.path.Path; import core.game.world.map.path.Pathfinder; import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Graphics; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.playAudio; /** * Handles the Spirit wolf familiar. @@ -105,7 +108,7 @@ public final class SpiritWolfNPC extends Familiar { return false; } visualizeSpecialMove(); - owner.getAudioManager().send(4265); + playAudio(owner, Sounds.WOLF_HOWL2_4265); faceTemporary(npc, owner, 2); super.visualize(Animation.create(8293), new Graphics(1334, 96)); Projectile.magic(this, npc, 1333, 40, 36, 50, 5).send(); diff --git a/Server/src/main/content/global/skill/summoning/familiar/StrangerPlantNPC.java b/Server/src/main/content/global/skill/summoning/familiar/StrangerPlantNPC.java index fee6e4f21..04d26f299 100644 --- a/Server/src/main/content/global/skill/summoning/familiar/StrangerPlantNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/StrangerPlantNPC.java @@ -4,12 +4,13 @@ import core.plugin.Initializable; import core.game.node.entity.Entity; import core.game.node.entity.impl.Projectile; import core.game.node.entity.player.Player; -import core.game.node.entity.state.EntityState; import core.game.node.item.Item; import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Graphics; import core.tools.RandomFunction; +import static core.api.ContentAPIKt.*; + /** * Represents the Stranger Plant familiar. * @author Aero @@ -45,7 +46,7 @@ public class StrangerPlantNPC extends Forager { } Entity target = special.getTarget(); if (RandomFunction.random(2) == 1) { - target.getStateManager().register(EntityState.POISONED, false, 40, target); + applyPoison(target, owner, 20); } animate(Animation.create(8211)); Projectile.ranged(this, target, 1508, 50, 40, 1, 45).send(); 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/UnicornStallionNPC.java b/Server/src/main/content/global/skill/summoning/familiar/UnicornStallionNPC.java index f22c8ed32..fde55d548 100644 --- a/Server/src/main/content/global/skill/summoning/familiar/UnicornStallionNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/UnicornStallionNPC.java @@ -7,11 +7,13 @@ import core.game.interaction.OptionHandler; import core.game.node.Node; import core.game.node.entity.combat.equipment.WeaponInterface; import core.game.node.entity.player.Player; -import core.game.node.entity.state.EntityState; 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 org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.*; /** * Represents the Unicorn Stallion familiar. @@ -44,7 +46,7 @@ public class UnicornStallionNPC extends Familiar { @Override protected boolean specialMove(FamiliarSpecial special) { Player player = (Player) special.getNode(); - player.getAudioManager().send(4372); + playAudio(player, Sounds.HEALING_AURA_4372); visualize(Animation.create(8267), Graphics.create(1356)); player.getSkills().heal((int) (player.getSkills().getMaximumLifepoints() * 0.15)); return true; @@ -72,13 +74,13 @@ public class UnicornStallionNPC extends Familiar { player.sendMessage("You don't have enough summoning points left"); return true; } - if (!owner.getStateManager().hasState(EntityState.POISONED)) { + if (!isPoisoned(owner)) { player.sendMessage("You are not poisoned."); return true; } - player.getAudioManager().send(4372); + playAudio(player, Sounds.HEALING_AURA_4372); familiar.visualize(Animation.create(8267), Graphics.create(1356)); - player.getStateManager().remove(EntityState.POISONED); + curePoison(player); player.getSkills().updateLevel(Skills.SUMMONING, -2, 0); 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/familiar/VoidFamiliarNPC.java b/Server/src/main/content/global/skill/summoning/familiar/VoidFamiliarNPC.java index 4f8d3fab5..817b0501c 100644 --- a/Server/src/main/content/global/skill/summoning/familiar/VoidFamiliarNPC.java +++ b/Server/src/main/content/global/skill/summoning/familiar/VoidFamiliarNPC.java @@ -195,8 +195,9 @@ public final class VoidFamiliarNPC implements Plugin { public void handleFamiliarTick() { super.handleFamiliarTick(); if (healDelay < GameWorld.getTicks()) { - getSkills().heal(1); + owner.getSkills().heal(1); healDelay = GameWorld.getTicks() + 25; + owner.graphics(Graphics.create(1507), 1); } } diff --git a/Server/src/main/content/global/skill/summoning/pet/IncubatorHandler.kt b/Server/src/main/content/global/skill/summoning/pet/IncubatorHandler.kt new file mode 100644 index 000000000..edb11ca18 --- /dev/null +++ b/Server/src/main/content/global/skill/summoning/pet/IncubatorHandler.kt @@ -0,0 +1,73 @@ +package content.global.skill.summoning.pet + +import core.api.* +import core.game.node.Node +import core.game.node.entity.player.Player +import core.game.interaction.* +import core.tools.StringUtils + +class IncubatorHandler : InteractionListener { + val eggIds = IncubatorEgg.values().map { it.egg.id }.toIntArray() + val incubators = intArrayOf(28550, 28352, 28359) + + override fun defineListeners() { + on (incubators, IntType.SCENERY, "inspect", handler = ::handleInspectOption) + on (incubators, IntType.SCENERY, "take-egg", handler = ::handleTakeOption) + onUseWith (IntType.SCENERY, eggIds, *incubators, handler = ::handleEggOnIncubator) + } + + fun handleEggOnIncubator (player: Player, used: Node, with: Node) : Boolean { + val egg = IncubatorEgg.forItem (used.asItem()) ?: return false + val activeEgg = IncubatorTimer.getEggFor (player, player.location.regionId) + + if (activeEgg != null) { + sendMessage (player, "You already have an egg in this incubator.") + return true + } + + if (removeItem(player, used.asItem())) + IncubatorTimer.registerEgg (player, player.location.regionId, egg) + return true + } + + fun handleInspectOption (player: Player, node: Node) : Boolean { + val activeEgg = IncubatorTimer.getEggFor (player, player.location.regionId) + + if (activeEgg == null) { + sendMessage (player, "The incubator is currently empty.") + return true + } + + if (activeEgg.finished) { + sendMessage (player, "The egg inside has finished incubating.") + return true + } + + val creatureName = activeEgg.egg.product.name.lowercase() + sendMessage (player, "There is currently ${if (StringUtils.isPlusN(creatureName)) "an" else "a"} $creatureName egg incubating.") + return true + } + + fun handleTakeOption (player: Player, node: Node) : Boolean { + val region = player.location.regionId + val activeEgg = IncubatorTimer.getEggFor (player, region) ?: return false + + if (!activeEgg.finished) { + sendMessage (player, "That egg hasn't finished incubating!") + return true + } + + if (freeSlots(player) < 1) { + sendMessage (player, "You do not have enough inventory space to do that.") + return true + } + + val egg = IncubatorTimer.removeEgg (player, region) ?: return false + val product = egg.product + val name = product.name.lowercase() + + sendMessage(player, "You take your $name out of the incubator.") + addItem(player, product.id) + return true + } +} diff --git a/Server/src/main/content/global/skill/summoning/pet/IncubatorState.kt b/Server/src/main/content/global/skill/summoning/pet/IncubatorState.kt deleted file mode 100644 index 611d80ca9..000000000 --- a/Server/src/main/content/global/skill/summoning/pet/IncubatorState.kt +++ /dev/null @@ -1,52 +0,0 @@ -package content.global.skill.summoning.pet - -import core.game.node.entity.player.Player -import core.game.node.entity.state.PlayerState -import core.game.node.entity.state.State -import core.game.system.task.Pulse -import org.json.simple.JSONObject - -@PlayerState("incubator") -class IncubatorState(player: Player? = null) : State(player) { - var egg: IncubatorEgg? = null - var ticksLeft = 0 - - override fun newInstance(player: Player?): State { - return IncubatorState(player) - } - - override fun save(root: JSONObject) { - if(ticksLeft <= 0) return - - val data = JSONObject() - data.put("eggOrdinal",(egg?.ordinal ?: 0).toString()) - data.put("ticksLeft",ticksLeft.toString()) - root.put("eggdata",data) - } - - override fun parse(_data: JSONObject) { - if(_data.containsKey("eggdata")){ - val data = _data["eggdata"] as JSONObject - egg = IncubatorEgg.values()[data["eggOrdinal"].toString().toInt()] - ticksLeft = data["ticksLeft"].toString().toInt() - } - } - - override fun createPulse() { - player ?: return - egg ?: return - player.varpManager.get(1160).setVarbit(4,1).send(player) - pulse = object : Pulse(){ - override fun pulse(): Boolean { - if(ticksLeft-- <= 0){ - player.setAttribute("/save:inc", egg!!.ordinal) - player.sendMessage("Your " + egg!!.product.name.toLowerCase() + " has finished hatching.") - pulse = null - return true - } - return false - } - } - } - -} \ No newline at end of file diff --git a/Server/src/main/content/global/skill/summoning/pet/IncubatorTimer.kt b/Server/src/main/content/global/skill/summoning/pet/IncubatorTimer.kt new file mode 100644 index 000000000..899deee2a --- /dev/null +++ b/Server/src/main/content/global/skill/summoning/pet/IncubatorTimer.kt @@ -0,0 +1,105 @@ +package content.global.skill.summoning.pet + +import core.api.* +import core.tools.* +import core.game.system.timer.* +import core.game.node.entity.Entity +import core.game.node.entity.player.Player +import java.util.* +import org.json.simple.* + +class IncubatorTimer : PersistTimer (500, "incubation") { + val incubatingEggs = HashMap() + + override fun getInitialRunDelay() : Int { + return 50 + } + + override fun parse (root: JSONObject, entity: Entity) { + val eggs = root["eggs"] as? JSONArray ?: return + for (eggData in eggs) { + val eggInfo = eggData as JSONArray + val egg = IncubatingEgg ( + eggInfo[0].toString().toInt(), + IncubatorEgg.values()[eggInfo[1].toString().toInt()], + eggInfo[2].toString().toLong(), + eggInfo[3].toString().toBoolean() + ) + incubatingEggs[egg.region] = egg + } + } + + override fun save (root: JSONObject, entity: Entity) { + val arr = JSONArray() + for ((_, eggInfo) in incubatingEggs) { + val eggArr = JSONArray() + eggArr.add(eggInfo.region.toString()) + eggArr.add(eggInfo.egg.ordinal.toString()) + eggArr.add(eggInfo.endTime.toString()) + eggArr.add(eggInfo.finished) + arr.add(eggArr) + } + 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) { + if (egg.finished) continue + if (egg.isDone()) { + sendMessage(entity, colorize("%RYour ${egg.egg.product.name.lowercase()} egg has finished hatching.")) + egg.finished = true + } + } + return !incubatingEggs.isEmpty() + } + + data class IncubatingEgg (val region: Int, val egg: IncubatorEgg, var endTime: Long, var finished: Boolean = false) + fun IncubatingEgg.isDone() : Boolean { + return endTime < System.currentTimeMillis() + } + + companion object { + val TAVERLEY_REGION = 11573 + val TAVERLEY_VARBIT = 4277 + val YANILLE_REGION = 10288 + val YANILLE_VARBIT = 4221 + + fun varbitForRegion (region: Int) : Int { + return when (region) { + TAVERLEY_REGION -> TAVERLEY_VARBIT + YANILLE_REGION -> YANILLE_VARBIT + else -> -1 + } + } + + fun getEggFor (player: Player, region: Int) : IncubatingEgg? { + val playerTimer = getTimer(player) ?: return null + return playerTimer.incubatingEggs[region] + } + + fun registerEgg (player: Player, region: Int, egg: IncubatorEgg) { + val timer = getTimer(player) ?: IncubatorTimer() + timer.incubatingEggs [region] = IncubatingEgg (region, egg, System.currentTimeMillis() + (ticksToSeconds(egg.inucbationTime * 100) * 1000)) + + if (!hasTimerActive(player)) + registerTimer(player, timer) + setVarbit(player, varbitForRegion(region), 1, true) + } + + fun removeEgg (player: Player, region: Int) : IncubatorEgg? { + val egg = getEggFor (player, region) ?: return null + val timer = getTimer(player) ?: return null + timer.incubatingEggs.remove(region) + setVarbit(player, varbitForRegion(region), 0, true) + return egg.egg + } + } +} 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 ee988b453..8925c4c42 100644 --- a/Server/src/main/content/global/skill/summoning/pet/KittenInteractDialogue.java +++ b/Server/src/main/content/global/skill/summoning/pet/KittenInteractDialogue.java @@ -22,7 +22,8 @@ public final class KittenInteractDialogue extends DialoguePlugin { /** * Represents the animation to use. */ - private static final Animation ANIMATION = new Animation(827); + private static final Animation PLAYER_STROKE_ANIMATION = new Animation(9224); + private static final Animation KITTEN_STROKE_ANIMATION = new Animation(9173); /** * Constructs a new {@code KittenInterfactDialogue} {@code Object}. @@ -48,7 +49,7 @@ public final class KittenInteractDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { - interpreter.sendOptions("Interact with Kitten", "Stroke", "Chase-Vermin", "Shoo-away."); + interpreter.sendOptions("Interact with Kitten", "Stroke", "Chase vermin", "Shoo away"); stage = 0; return true; } @@ -59,7 +60,9 @@ public final class KittenInteractDialogue extends DialoguePlugin { case 0: switch (buttonId) { case 1:// stroke - player.animate(ANIMATION); + player.getFamiliarManager().getFamiliar().face(player); + player.animate(PLAYER_STROKE_ANIMATION); + player.getFamiliarManager().getFamiliar().animate(KITTEN_STROKE_ANIMATION); player.getFamiliarManager().getFamiliar().sendChat("Purr...purr..."); interpreter.sendDialogues(player, null, "That cat sure loves to be stroked."); stage = 99; @@ -110,11 +113,14 @@ public final class KittenInteractDialogue extends DialoguePlugin { case 560: switch (buttonId) { case 1:// yes - player.sendChat("Shoo cat!"); - player.getFamiliarManager().getFamiliar().sendChat("Miaow!"); - // player.getFamiliarManager().getFamiliar().dismiss();//TODO: - // Pet - player.getPacketDispatch().sendMessage("The cat has run away."); + if (player.getFamiliarManager().hasFamiliar()) { //in case the cat had already run away from hunger by the time the player clicked 'yes' + player.sendChat("Shoo cat!"); + Pet currentPet = (Pet) player.getFamiliarManager().getFamiliar(); + player.getFamiliarManager().getFamiliar().sendChat("Miaow!"); + player.getFamiliarManager().getFamiliar().dismiss(); + player.getFamiliarManager().removeDetails(currentPet.getItemId()); + player.getPacketDispatch().sendMessage("The cat has run away."); + } end(); break; case 2:// no 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 4533ec24d..99048c140 100644 --- a/Server/src/main/content/global/skill/summoning/pet/Pet.java +++ b/Server/src/main/content/global/skill/summoning/pet/Pet.java @@ -1,13 +1,19 @@ package content.global.skill.summoning.pet; +import content.global.skill.skillcapeperks.SkillcapePerks; 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.*; /** * Represents a pet. * @author Emperor * @author 'Vexia + * @author Player Name */ public final class Pet extends Familiar { @@ -24,13 +30,18 @@ public final class Pet extends Familiar { /** * The growth rate of the pet. */ - private double growthRate; + private final double growthRate; /** * The pets type. */ private final Pets pet; + /** + * Whether a hunger warning has been fired yet; 0 = none, 1 = hungry, 2 = starving. + */ + private int hasWarned = 0; + /** * Constructs a new {@code Pet} {@code Object}. * @param owner the owner. @@ -48,34 +59,47 @@ public final class Pet extends Familiar { @Override public void sendConfiguration() { - owner.getConfigManager().set(1175, ((int) details.getGrowth() << 1) | ((int) details.getHunger() << 9)); - owner.getConfigManager().set(1174, getId()); - owner.getConfigManager().set(448, itemId); + setVarp(owner, 1175, ((int) details.getGrowth() << 1) | ((int) details.getHunger() << 9)); + setVarp(owner, 1174, getId()); + setVarp(owner, 448, itemId); } @Override public void handleTickActions() { final PetDetails petDetails = details; if (getPet().getFood().length > 0) { - petDetails.updateHunger(petDetails.getStage() == 0 ? 0.025 : 0.018); + if(!SkillcapePerks.isActive(SkillcapePerks.PET_MASTERY, owner)) { + double amount = itemId == pet.getBabyItemId() ? 0.025 : 0.018; + if (GameWorld.getSettings().isDevMode()) { + amount *= 100; + } + petDetails.updateHunger(amount); + } } double hunger = petDetails.getHunger(); - if (hunger >= 75.0 && hunger <= 90.0 && hunger % 5 == 0) { + if (hunger >= 75.0 && hunger <= 90.0 && hasWarned < 1) { owner.sendMessage("Your pet is getting hungry."); + hasWarned = 1; } - else if (hunger >= 90.0 && hunger % 1 == 0) { + else if (hunger >= 90.0 && hasWarned < 2) { owner.getPacketDispatch().sendMessage("Your pet is starving, feed it before it runs off."); + hasWarned = 2; } if (hunger >= 100.0 && growthRate != 0 && pet.getFood().length != 0) { - owner.getFamiliarManager().dismiss(false); + owner.getFamiliarManager().dismiss(); + owner.getFamiliarManager().removeDetails(getItemId()); owner.getFamiliarManager().setFamiliar(null); - owner.getConfigManager().set(1175, 0); + setVarp(owner, 1175, 0); owner.sendMessage("Your pet has run away."); return; } double growth = petDetails.getGrowth(); - if (pet.getGrowthRate() > 0.000) { - petDetails.updateGrowth(pet.getGrowthRate()); + double growthrate = pet.getGrowthRate(); + if (growthrate > 0.000) { + if (GameWorld.getSettings().isDevMode()) { + growthrate *= 100; + } + petDetails.updateGrowth(growthrate); if (growth == 100.0) { growNextStage(); } @@ -87,34 +111,28 @@ public final class Pet extends Familiar { } else if (!getPulseManager().hasPulseRunning()) { startFollowing(); } - owner.getConfigManager().set(1175, ((int) details.getGrowth() << 1) | ((int) details.getHunger() << 9)); + setVarp(owner, 1175, ((int) details.getGrowth() << 1) | ((int) details.getHunger() << 9)); } /** * Method used to grow the npc's next stage. */ - public final void growNextStage() { - if (details.getStage() == 3) { - return; - } + public void growNextStage() { if (pet == null) { return; } - int npcId = pet.getNpcId(details.getStage() + 1); - if (npcId < 1) { + int newItemId = pet.getNextStageItemId(itemId); + if (newItemId == -1) { + // then this pet is already overgrown return; } - details.setStage(details.getStage() + 1); - int itemId = pet.getItemId(details.getStage()); - if (pet.getNpcId(details.getStage() + 1) > 0) { - details.updateGrowth(-100.0); + if (pet.isKitten(itemId)) { + owner.incrementAttribute("/save:stats_manager:cats_raised"); } - clear(); - Pet newPet = new Pet(owner, details, itemId, npcId); - newPet.growthRate = growthRate; - owner.getFamiliarManager().setFamiliar(newPet); + 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."); - owner.getFamiliarManager().spawnFamiliar(); } @Override @@ -132,22 +150,6 @@ public final class Pet extends Familiar { return false; } - /** - * Gets the growthRate. - * @return The growthRate. - */ - public double getGrowthRate() { - return growthRate; - } - - /** - * Sets the growthRate. - * @param growthRate The growthRate to set. - */ - public void setGrowthRate(double growthRate) { - this.growthRate = growthRate; - } - /** * Gets the itemId. * @return The itemId. @@ -172,9 +174,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 6bb079f44..b3b0b8f05 100644 --- a/Server/src/main/content/global/skill/summoning/pet/PetDetails.java +++ b/Server/src/main/content/global/skill/summoning/pet/PetDetails.java @@ -2,11 +2,10 @@ package content.global.skill.summoning.pet; -import core.game.world.GameWorld; - /** * A class containing pet details for a certain pet. * @author Emperor + * @author Player Name */ public final class PetDetails { @@ -20,11 +19,6 @@ public final class PetDetails { */ private double growth = 0.0; - /** - * The current stage of the pet (0 - baby, 1 - grown, 2 - overgrown). - */ - private int stage; - /** * Constructs a new {@code PetDetails} {@code Object}. * @param growth The growth value. @@ -38,15 +32,9 @@ public final class PetDetails { * @param amount The amount. */ public void updateHunger(double amount) { - if(GameWorld.getSettings().isDevMode()){ - hunger += amount * 100; - } else { - hunger += amount; - } + hunger += amount; if (hunger < 0.0) { hunger = 0.0; - } else if (hunger > 100.0) { - hunger = 100.0; } } @@ -71,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. @@ -80,19 +75,9 @@ public final class PetDetails { } /** - * Gets the stage. - * @return The stage. + * Sets the growth. (You probably want to use updateGrowth() instead.) */ - public int getStage() { - return stage; + public void setGrowth(double value) { + this.growth = value; } - - /** - * Sets the stage. - * @param stage The stage to set. - */ - public void setStage(int stage) { - this.stage = stage; - } - -} \ No newline at end of file +} 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 6e8cb2618..263d04aef 100644 --- a/Server/src/main/content/global/skill/summoning/pet/Pets.java +++ b/Server/src/main/content/global/skill/summoning/pet/Pets.java @@ -1,20 +1,24 @@ package content.global.skill.summoning.pet; import core.game.node.entity.player.Player; +import core.tools.Log; import java.util.HashMap; import java.util.Map; +import static core.api.ContentAPIKt.log; + /** * An enum containing all the pets and their info. * @author Emperor + * @author Player Name */ public enum Pets { /** * A cat/kitten pet. */ - CAT(1555, 1561, 1567, 761, 768, 774, 0.0154320987654321, 0, 321, 363, 341, 15264, 345, 377, 353, 389, 7944, 349, 331, 327, 395, 383, 317, 371, 335, 359, 15264, 15270, 1927), CAT_1(1556, 1562, 1568, 762, 769, 775, 0.0154320987654321, 0, 321, 363, 341, 15264, 345, 377, 353, 389, 7944, 349, 331, 327, 395, 383, 317, 371, 335, 359, 15264, 15270, 1927), CAT_2(1557, 1563, 1569, 763, 770, 776, 0.0154320987654321, 0, 321, 363, 341, 15264, 345, 377, 353, 389, 7944, 349, 331, 327, 395, 383, 317, 371, 335, 359, 15264, 15270, 1927), CAT_3(1558, 1564, 1570, 764, 771, 777, 0.0154320987654321, 0, 321, 363, 341, 15264, 345, 377, 353, 389, 7944, 349, 331, 327, 395, 383, 317, 371, 335, 359, 15264, 15270, 1927), CAT_4(1559, 1565, 1571, 765, 772, 778, 0.0154320987654321, 0, 321, 363, 341, 15264, 345, 377, 353, 389, 7944, 349, 331, 327, 395, 383, 317, 371, 335, 359, 15264, 15270, 1927), CAT_5(1560, 1566, 1572, 766, 773, 779, 0.0154320987654321, 0, 321, 363, 341, 15264, 345, 377, 353, 389, 7944, 349, 331, 327, 395, 383, 317, 371, 335, 359, 15264, 15270, 1927), HELLCAT(7583, 7582, 7581, 3505, 3504, 3503, 0.0154320987654321, 0, 321, 363, 341, 15264, 345, 377, 353, 389, 7944, 349, 331, 327, 395, 383, 317, 371, 335, 359, 15264, 15270, 1927), CAT_7(14089, 14090, 15092, 8217, 8214, 8216, 0.0154320987654321, 0, 321, 363, 341, 15264, 345, 377, 353, 389, 7944, 349, 331, 327, 395, 383, 317, 371, 335, 359, 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. @@ -176,61 +180,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. */ @@ -446,35 +395,55 @@ public enum Pets { /** * Gets the NPC id for this pet. - * @param stage The stage of the pet. - * @return The NPc id. + * @param itemId An int giving the ID of the item for which we want to know the corresponding NPC id. + * @return The NPC id. */ - public int getNpcId(int stage) { - switch (stage) { - case 0: + public int getNpcId(int itemId) { + if (itemId == babyItemId) { return babyNpcId; - case 1: + } + if (itemId == grownItemId) { return grownNpcId; - case 2: + } + if (itemId == overgrownItemId) { return overgrownNpcId; } - return 0; + log(this.getClass(), Log.ERR, "Could not locate NPC ID for pet item " + itemId); + return -1; } /** - * Gets the item id for this pet. - * @param stage The stage of the pet. - * @return The item id. + * Gets the next growth stage's item ID for this pet. + * @param itemId An int giving the current pet's item ID. + * @return The item ID for the next growth stage, or -1 if there isn't any (i.e. pet is already overgrown). */ - public int getItemId(int stage) { - switch (stage) { - case 0: - return babyItemId; - case 1: + public int getNextStageItemId(int itemId) { + if (itemId == babyItemId) { return grownItemId; - case 2: + } + if (itemId == grownItemId) { return overgrownItemId; } - return 0; + 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; + } + } +} diff --git a/Server/src/main/content/global/skill/thieving/PickableDoorHandler.java b/Server/src/main/content/global/skill/thieving/PickableDoorHandler.java index 315984644..7014d0781 100644 --- a/Server/src/main/content/global/skill/thieving/PickableDoorHandler.java +++ b/Server/src/main/content/global/skill/thieving/PickableDoorHandler.java @@ -19,6 +19,8 @@ import core.tools.RandomFunction; import java.util.ArrayList; import java.util.List; +import static core.api.ContentAPIKt.sendMessage; + /** * Represents a pickable door. * @author Vexia @@ -51,6 +53,9 @@ public class PickableDoorHandler extends OptionHandler { pickableDoors.add(new PickableDoor(new Location[]{Location.create(2579, 3307, 1)}, 61, 50)); pickableDoors.add(new PickableDoor(new Location[]{Location.create(3018, 3187, 0)}, 1, 0.0)); pickableDoors.add(new PickableDoor(new Location[]{Location.create(2601, 9482, 0)}, 82, 0.0, true)); + pickableDoors.add(new PickableDoor(new Location[]{Location.create(3044, 3956, 0)}, 39, 35.0, true, true)); + pickableDoors.add(new PickableDoor(new Location[]{Location.create(3041, 3959, 0)}, 39, 35.0, true, true)); + pickableDoors.add(new PickableDoor(new Location[]{Location.create(3038, 3956, 0)}, 39, 35.0, true, true)); return this; } @@ -66,10 +71,14 @@ public class PickableDoorHandler extends OptionHandler { return true; } if (option.equals("pick-lock")) { + if (door == null) { + sendMessage(player, "This door cannot be unlocked."); + return true; + } door.pickLock(player, (Scenery) node); return true; } - System.out.println("Unhandled door: " + node.getId()); + return false; } @@ -116,6 +125,27 @@ public class PickableDoorHandler extends OptionHandler { */ private final boolean lockpick; + /** + * If the door should be flipped around when checking for which side requires lockpicking. + */ + private final boolean flipped; + + /** + * Constructs a new {@code PickableDoor} {@code Object}. + * @param locations the locations. + * @param level the level. + * @param experience the experience. + * @param lockpick the lock pick. + * @param flipped the door to be flipped when lockpicking. + */ + public PickableDoor(final Location[] locations, int level, double experience, boolean lockpick, boolean flipped) { + this.locations = locations; + this.level = level; + this.experience = experience; + this.lockpick = lockpick; + this.flipped = flipped; + } + /** * Constructs a new {@code PickableDoor} {@code Object}. * @param locations the locations. @@ -124,10 +154,7 @@ public class PickableDoorHandler extends OptionHandler { * @param lockpick the lock pick. */ public PickableDoor(final Location[] locations, int level, double experience, boolean lockpick) { - this.locations = locations; - this.level = level; - this.experience = experience; - this.lockpick = lockpick; + this(locations, level, experience, lockpick, false); } /** @@ -153,7 +180,7 @@ public class PickableDoorHandler extends OptionHandler { * @param object the object. */ public void open(Player player, Scenery object) { - if (isInside(player, object)) { + if (isInside(player, object) != flipped) { DoorActionHandler.handleAutowalkDoor(player, object); player.getPacketDispatch().sendMessage("You go through the door."); } else { @@ -168,7 +195,7 @@ public class PickableDoorHandler extends OptionHandler { */ public void pickLock(Player player, Scenery object) { boolean success = RandomFunction.random(12) >= 4; - if (isInside(player, object)) { + if (isInside(player, object) != flipped) { player.getPacketDispatch().sendMessage("The door is already unlocked."); return; } diff --git a/Server/src/main/content/global/skill/thieving/Pickpockets.kt b/Server/src/main/content/global/skill/thieving/Pickpockets.kt index 8f376ad8b..e4c1eda77 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, 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( @@ -122,6 +122,7 @@ enum class Pickpockets(val ids: IntArray, val requiredLevel: Int, val low: Doubl WeightedItem(Items.TARROMIN_SEED_5293,1,1,50.0), WeightedItem(Items.HARRALANDER_SEED_5294,1,1,25.0), WeightedItem(Items.RANARR_SEED_5295,1,1,8.0), + WeightedItem(Items.SPIRIT_WEED_SEED_12176,1,1,8.0), WeightedItem(Items.TOADFLAX_SEED_5296,1,1,8.0), WeightedItem(Items.IRIT_SEED_5297,1,1,8.0), WeightedItem(Items.AVANTOE_SEED_5298,1,1,8.0), @@ -135,7 +136,7 @@ enum class Pickpockets(val ids: IntArray, val requiredLevel: Int, val low: Doubl GUARD(intArrayOf(9, 32, 206, 296, 297, 298, 299, 344, 345, 346, 368, 678, 812, 9, 32, 296, 297, 298, 299, 2699, 2700, 2701, 2702, 2703, 3228, 3229, 3230, 3231, 3232, 3233, 3241, 3407, 3408, 4307, 4308, 4309, 4310, 4311, 5919, 5920), 40, 50.0, 240.0, 46.5, 2,2,5, WeightBasedTable.create( WeightedItem(Items.COINS_995,30,30,1.0,true) )), - FREMENNIK_CITIZEN(intArrayOf(2462), 45, 65.0, 240.0, 65.0, 2, 2, 5, WeightBasedTable.create( + FREMENNIK_CITIZEN(intArrayOf(1305, 1306, 1308, 1309, 1310, 1311, 1312, 1313, 1314, 2462), 45, 65.0, 240.0, 65.0, 2, 2, 5, WeightBasedTable.create( WeightedItem(Items.COINS_995,40,40,1.0,true) )), BEARDED_BANDIT(intArrayOf(1880, 1881, 6174), 45, 50.0, 240.0, 65.0, 5,5,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 4734385c3..b08185335 100644 --- a/Server/src/main/content/global/skill/thieving/StallThiefPulse.java +++ b/Server/src/main/content/global/skill/thieving/StallThiefPulse.java @@ -1,5 +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; @@ -14,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. @@ -70,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; } @@ -118,18 +121,23 @@ 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)); } return true; } @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('_', ' ')); + } } /** @@ -139,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/ThievableChestPlugin.java b/Server/src/main/content/global/skill/thieving/ThievableChestPlugin.java index 2b7527fc4..f711dad6f 100644 --- a/Server/src/main/content/global/skill/thieving/ThievableChestPlugin.java +++ b/Server/src/main/content/global/skill/thieving/ThievableChestPlugin.java @@ -15,6 +15,8 @@ import core.game.world.update.flag.context.Animation; import core.plugin.Initializable; import core.plugin.Plugin; +import static core.api.ContentAPIKt.*; + /** * Handles the thieving of chests. * @author Vexia @@ -146,19 +148,19 @@ public final class ThievableChestPlugin extends OptionHandler { player.sendMessage("It looks like this chest has already been looted."); return; } - player.lock(); - player.animate(Animation.create(536)); if (player.getSkills().getLevel(Skills.THIEVING) < level) { - player.lock(2); + animate(player, 536, false); + lock(player, 2); player.sendMessage("You search the chest for traps."); player.sendMessage("You find nothing.", 1); - player.unlock(); return; } if (player.getInventory().freeSlots() == 0) { player.getPacketDispatch().sendMessage("Not enough inventory space."); return; } + lock(player, 6); + animate(player, 536, false); player.sendMessage("You find a trap on the chest..."); player.getImpactHandler().setDisabledTicks(6); GameWorld.getPulser().submit(new Pulse(1, player) { @@ -172,10 +174,10 @@ public final class ThievableChestPlugin extends OptionHandler { break; case 4: player.animate(Animation.create(536)); + player.faceLocation(object.getLocation()); player.sendMessage("You open the chest."); break; case 6: - player.unlock(); for (Item i : rewards) { player.getInventory().add(i, player); } diff --git a/Server/src/main/content/global/skill/thieving/ThievingListeners.kt b/Server/src/main/content/global/skill/thieving/ThievingListeners.kt index 1722902e4..c01ef2e6c 100644 --- a/Server/src/main/content/global/skill/thieving/ThievingListeners.kt +++ b/Server/src/main/content/global/skill/thieving/ThievingListeners.kt @@ -1,29 +1,57 @@ package content.global.skill.thieving +import content.global.skill.skillcapeperks.SkillcapePerks +import core.api.* +import core.api.utils.WeightBasedTable import core.game.node.entity.combat.ImpactHandler import core.game.node.entity.impl.Animator -import core.game.node.entity.player.Player -import core.game.node.entity.player.link.audio.Audio import core.game.node.entity.skill.Skills -import core.game.node.entity.state.EntityState import core.game.world.update.flag.context.Animation import core.tools.RandomFunction import org.rs09.consts.Items import core.game.interaction.InteractionListener import core.game.interaction.IntType -import core.tools.secondsToTicks +import core.game.node.entity.player.Player +import core.game.node.item.Item +import org.rs09.consts.Sounds + +import core.game.interaction.InteractionListeners +import core.game.world.GameWorld class ThievingListeners : InteractionListener { - private val PICKPOCKET_ANIM = Animation(881,Animator.Priority.HIGH) - private val NPC_ANIM = Animation(422) - private val SUCCESS = Audio(2581, 1, 0) + companion object { + val PICKPOCKET_ANIM = Animation(881,Animator.Priority.HIGH) + val NPC_ANIM = Animation(422) + + /** Standalone pickpocketing function. For thieving other stuff outside of normal pickpocketing tables. */ + fun pickpocketRoll(player: Player, low: Double, high: Double, table: WeightBasedTable): ArrayList? { + // Able to pickpocket + var successMod = 0.0 + if(SkillcapePerks.isActive(SkillcapePerks.SMOOTH_HANDS, player)) { + successMod += 25 + } + if (player.equipment.contains(Items.GLOVES_OF_SILENCE_10075,1)){ + successMod += 3 + } + + val chance = RandomFunction.randomDouble(1.0, 100.0) + val failThreshold = RandomFunction.getSkillSuccessChance(low, high ,player.skills.getLevel(Skills.THIEVING)) + successMod + + if (chance > failThreshold) { + // Fail Pickpocket + return null // Returns a null, different from an empty table. + } else { + // Success Pickpocket + return table.roll() // You could also successfully pickpocket nothing when the table returns a blank array. + } + } + } override fun defineListeners() { on(IntType.NPC,"pickpocket","pick-pocket"){ player, node -> val pickpocketData = Pickpockets.forID(node.id) ?: return@on false - var successMod = 0 if(player.inCombat()){ player.sendMessage("You can't pickpocket while in combat.") @@ -39,59 +67,41 @@ class ThievingListeners : InteractionListener { player.sendMessage("You don't have enough inventory space to do that.") return@on true } - - if(pickpocketData == Pickpockets.FEMALE_HAM_MEMBER || pickpocketData == Pickpockets.MALE_HAM_MEMBER){ - successMod += getHAMItemCount(player) - } - - if(player.equipment.contains(Items.GLOVES_OF_SILENCE_10075,1)){ - successMod += 3 - } + + //Custom portion. Repeat pickpocket, only proceed if it has been long enough since the last pickpocket action + if (getAttribute(player, "pickpockettick", 0) <= GameWorld.ticks) { player.animator.animate(PICKPOCKET_ANIM) - val chance = RandomFunction.randomDouble(1.0,100.0) - val failThreshold = pickpocketData.getSuccessChance(player) + successMod - - if(chance > failThreshold){ + val lootTable = pickpocketRoll(player, pickpocketData.low, pickpocketData.high, pickpocketData.table) + if(lootTable == null){ node.asNpc().face(player) node.asNpc().animator.animate(NPC_ANIM) - val hitSoundId = 518 + RandomFunction.random(4) // choose 1 of 4 possible hit noises - player.audioManager.send(hitSoundId, 1, 20) // OSRS defines a delay of 20 + playHurtAudio(player, 20) - player.stateManager.set(EntityState.STUNNED, secondsToTicks(pickpocketData.stunTime)) - player.lock(secondsToTicks(pickpocketData.stunTime)) + //stun(player, pickpocketData.stunTime) + stun(player, 0) + setAttribute(player, "pickpockettick", GameWorld.ticks + pickpocketData.stunTime) player.impactHandler.manualHit(node.asNpc(),RandomFunction.random(pickpocketData.stunDamageMin,pickpocketData.stunDamageMax),ImpactHandler.HitsplatType.NORMAL) node.asNpc().face(null) } else { - player.audioManager.send(SUCCESS) - player.lock(2) - pickpocketData.table.roll().forEach { player.inventory.add(it) } + playAudio(player, Sounds.PICK_2581) + //player.lock(2) + setAttribute(player, "pickpockettick", GameWorld.ticks + 2) + lootTable.forEach { player.inventory.add(it) } player.skills.addExperience(Skills.THIEVING,pickpocketData.experience) } - + } + //pickpocket again. + if (player.skills.lifepoints > pickpocketData.stunDamageMax) { + InteractionListeners.run(node.id, IntType.NPC,"Pickpocket",player,node) + } else { + player.sendMessage("It's probably not a good idea to continue with your injuries.") + } + return@on true } - } - - fun getHAMItemCount(player: Player): Int{ - var counter = 0 - for(item in player.equipment.toArray()){ - item ?: continue - counter += when(item.id){ - Items.HAM_LOGO_4306 -> 1 - Items.HAM_ROBE_4300 -> 1 - Items.HAM_HOOD_4302 -> 1 - Items.HAM_CLOAK_4304 -> 1 - Items.BOOTS_4310 -> 1 - Items.GLOVES_4308 -> 1 - else -> 0 - } - } - return counter - } - -} \ No newline at end of file +} 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/state/DiseasedState.kt b/Server/src/main/content/global/state/DiseasedState.kt deleted file mode 100644 index 9dbadb0cf..000000000 --- a/Server/src/main/content/global/state/DiseasedState.kt +++ /dev/null @@ -1,53 +0,0 @@ -package content.global.state - -import core.game.node.entity.combat.ImpactHandler -import core.game.node.entity.player.Player -import core.game.system.task.Pulse -import core.tools.RandomFunction -import org.json.simple.JSONObject -import core.game.node.entity.state.PlayerState -import core.game.node.entity.state.State -import core.game.world.GameWorld - -@PlayerState("disease") -class DiseasedState(player: Player? = null) : State(player){ - var hitsLeft = 25 - - override fun save(root: JSONObject) { - if(hitsLeft > 0){ - root.put("hitsLeft",hitsLeft) - } - } - - override fun parse(_data: JSONObject) { - if(_data.containsKey("hitsLeft")){ - hitsLeft = _data["hitsLeft"].toString().toInt() - } - } - - override fun newInstance(player: Player?): State { - return DiseasedState(player) - } - - override fun createPulse() { - player ?: return - if(player.getAttribute("immunity:disease",0) > GameWorld.ticks){ - return - } - if(hitsLeft <= 0) return - player.sendMessage("You have been diseased!") - pulse = object : Pulse(30){ - override fun pulse(): Boolean { - val damage = RandomFunction.random(1,5) - player.impactHandler.manualHit(player,damage,ImpactHandler.HitsplatType.DISEASE) - var skillId = RandomFunction.random(24) - if(skillId == 3) skillId-- - player.skills.updateLevel(skillId,-damage,0) - hitsLeft-- - if(hitsLeft <= 0) player.sendMessage("The disease has wore off.") - return hitsLeft <= 0 - } - } - } - -} \ No newline at end of file diff --git a/Server/src/main/content/global/travel/EssenceTeleport.kt b/Server/src/main/content/global/travel/EssenceTeleport.kt index bdbecfcb1..cf79f80c4 100644 --- a/Server/src/main/content/global/travel/EssenceTeleport.kt +++ b/Server/src/main/content/global/travel/EssenceTeleport.kt @@ -2,6 +2,7 @@ package content.global.travel import content.global.skill.magic.TeleportMethod import core.game.event.TeleportEvent +import core.api.* import core.api.lock import core.api.teleport import core.api.unlock @@ -17,6 +18,7 @@ import core.game.world.update.flag.context.Animation import core.game.world.update.flag.context.Graphics import core.tools.RandomFunction import core.game.world.GameWorld +import org.rs09.consts.Sounds /** * Represents a utilitity class for rune essence teleporting. @@ -56,9 +58,9 @@ object EssenceTeleport { npc.faceTemporary(player, 1) npc.graphics(GLOWING_HANDS_GFX) lock(player,4) - player.audioManager.send(125) + playGlobalAudio(player.location, Sounds.CURSE_ALL_125, 0, 1) Projectile.create(npc, player, CURSE_PROJECTILE).send() - npc.sendChat("Senventior Disthinte Molesko!") + npc.sendChat("Senventior Disthine Molenko!") GameWorld.Pulser.submit(object : Pulse(1) { var counter = 0 override fun pulse(): Boolean { @@ -142,7 +144,7 @@ object EssenceTeleport { * @return the stage. */ fun getStage(player: Player): Int { - return player.varpManager.get(492).getValue() + return getVarp(player, 492) } /** diff --git a/Server/src/main/content/global/travel/canoe/Canoe.java b/Server/src/main/content/global/travel/canoe/Canoe.java deleted file mode 100644 index d78377227..000000000 --- a/Server/src/main/content/global/travel/canoe/Canoe.java +++ /dev/null @@ -1,86 +0,0 @@ -package content.global.travel.canoe; - -/** - * Represents a canoe to craft. - * @author 'Vexia - * @date 09/11/2013 - */ -public enum Canoe { - LOG(12, 30, 30,0,0), - DUGOUT(27, 60, 31,9,3), - STABLE_DUGOUT(42, 90, 32,10,2), - WAKA(57, 150, 33,8,5); - - /** - * Constructs a new {@code Canoe.java} {@code Object}. - * @param level the woodcutting level requirement. - * @param experience the experience. - * @param child the child. - */ - Canoe(final int level, final double experience, final int child, final int silhouetteChild, final int textChild) { - this.level = level; - this.experience = experience; - this.child = child; - this.silhouetteChild = silhouetteChild; - this.textChild = textChild; - this.maxDist = ordinal() + 1; - } - - public final int silhouetteChild; - public final int textChild; - public final int maxDist; - - /** - * Represents the woodcutting level requirement to craft the canoe. - */ - private final int level; - - /** - * Represents the experience received for crafting the canoe. - */ - private final double experience; - - /** - * Represents the child's id on the interface. - */ - private final int child; - - /** - * Gets the woodcutting level requirement. - * @return The required level. - */ - public int getRequiredLevel() { - return level; - } - - /** - * Gets the experience received. - * @return The experience amount. - */ - public double getExperience() { - return experience; - } - - /** - * Gets the child. - * @return The child. - */ - public int getChild() { - return child; - } - - /** - * Method used to get the canoe from the child. - * @param child the child. - * @return True if so - */ - public static Canoe getCanoeFromChild(final int child) { - for (Canoe canoe : values()) { - if(canoe.getChild() == child) - { - return canoe; - } - } - return null; - } -} \ No newline at end of file diff --git a/Server/src/main/content/global/travel/canoe/CanoeInterfaceListeners.kt b/Server/src/main/content/global/travel/canoe/CanoeInterfaceListeners.kt deleted file mode 100644 index df896f2b9..000000000 --- a/Server/src/main/content/global/travel/canoe/CanoeInterfaceListeners.kt +++ /dev/null @@ -1,141 +0,0 @@ -package content.global.travel.canoe - -import core.api.* -import core.cache.def.impl.VarbitDefinition -import core.game.component.Component -import core.game.node.entity.skill.Skills -import content.data.skill.SkillingTool -import core.game.system.task.Pulse -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 -import core.tools.RandomFunction -import org.rs09.consts.Components -import core.game.interaction.InterfaceListener -import kotlin.math.abs - -class CanoeInterfaceListeners : InterfaceListener { - - val SHAPE_INTERFACE = Components.CANOE_52 - val DESTINATION_INTERFACE = Components.CANOE_STATIONS_MAP_53 - - private val boatChilds = intArrayOf(47, 48, 3, 6, 49) - private val locationChilds = intArrayOf(50, 47, 44, 36) - - - override fun defineInterfaceListeners() { - - onOpen(SHAPE_INTERFACE){player, _ -> - CanoeUtils.checkCanoe(player, Canoe.DUGOUT) - CanoeUtils.checkCanoe(player, Canoe.STABLE_DUGOUT) - CanoeUtils.checkCanoe(player, Canoe.WAKA) - return@onOpen true - } - - on(SHAPE_INTERFACE) { player, _, _, buttonID, _, _ -> - player.interfaceManager.close() - val canoe = Canoe.getCanoeFromChild(buttonID) - val varbit = player.getAttribute("canoe-varbit", VarbitDefinition.forObjectID(0)) - - val axe: SkillingTool? = SkillingTool.getHatchet(player) - if (axe == null) { - player.packetDispatch.sendMessage("You do not have an axe which you have the woodcutting level to use.") - return@on true - } - - player.lock() - animate(player, CanoeUtils.getShapeAnimation(axe)) - player.pulseManager.run(object : Pulse(3) { - override fun pulse(): Boolean { - if (RandomFunction.random(if (canoe == Canoe.WAKA) 8 else 6) == 1) { - player.varpManager.setVarbit(varbit, CanoeUtils.getCraftValue(canoe, false)) - player.skills.addExperience(Skills.WOODCUTTING, canoe.experience) - player.unlock() - return true - } - animate(player, CanoeUtils.getShapeAnimation(axe)) - return false - } - }) - return@on true - } - - onOpen(DESTINATION_INTERFACE){player, component -> - val varbit = player.getAttribute("canoe-varbit",VarbitDefinition.forObjectID(0)) - val canoe = CanoeUtils.getCanoeFromVarbit(player, varbit) - val stationIndex = CanoeUtils.getStationIndex(player.location) - val maxDistance = canoe.maxDist - player.packetDispatch.sendInterfaceConfig(DESTINATION_INTERFACE,boatChilds[stationIndex],true) - player.packetDispatch.sendInterfaceConfig(DESTINATION_INTERFACE,locationChilds[stationIndex],false) - if(canoe != Canoe.WAKA){ - player.packetDispatch.sendInterfaceConfig(DESTINATION_INTERFACE,49,true) - for(i in 0..3){ - if(i == stationIndex) continue - if(abs(i - stationIndex) > maxDistance){ - player.packetDispatch.sendInterfaceConfig(DESTINATION_INTERFACE,boatChilds[i],true) - player.packetDispatch.sendInterfaceConfig(DESTINATION_INTERFACE,locationChilds[i],true) - } - } - } - return@onOpen true - } - - on(DESTINATION_INTERFACE){player, _, _, buttonID, _, _ -> - val dest = CanoeUtils.getDestinationFromButtonID(buttonID) - val destIndex = CanoeUtils.getStationIndex(dest) - val arrivalMessage = CanoeUtils.getNameByIndex(destIndex) - val stationIndex = CanoeUtils.getStationIndex(player.location) - val interfaceAnimationId = CanoeUtils.getTravelAnimation(stationIndex, destIndex) - var travelAnimDur = 15 - val varbit = player.getAttribute("canoe-varbit",VarbitDefinition.forObjectID(0)) - - if (player.familiarManager.hasFamiliar()) { - player.sendMessage("You can't take a follower on a canoe.") - return@on true - } - if (interfaceAnimationId != 0) { - travelAnimDur = Animation(interfaceAnimationId).duration - } - - player.lock() - player.interfaceManager.close() - player.pulseManager.run(object : Pulse(){ - var counter = 0 - override fun pulse(): Boolean { - when(counter++){ - 0 -> { - player.interfaceManager.openOverlay(Component(Components.FADE_TO_BLACK_120)) - player.interfaceManager.open(Component(Components.CANOE_TRAVEL_758)) - animateInterface(player, Components.CANOE_TRAVEL_758, 3, interfaceAnimationId) - } - 2 -> { - PacketRepository.send(MinimapState::class.java, MinimapStateContext(player, 2)) - player.interfaceManager.removeTabs(0, 1, 2, 3, 4, 5, 6, 11, 12) - } - travelAnimDur+1 -> { - player.properties.teleportLocation = dest - player.interfaceManager.close(Component(Components.CANOE_TRAVEL_758)) - player.interfaceManager.closeOverlay() - player.interfaceManager.openOverlay(Component(Components.FADE_FROM_BLACK_170)) - } - travelAnimDur+3 -> { - player.unlock() - player.interfaceManager.restoreTabs() - PacketRepository.send(MinimapState::class.java, MinimapStateContext(player, 0)) - player.sendMessage("You arrive at $arrivalMessage.") - player.sendMessage("Your canoe sinks from the long journey.") - if(destIndex == 4){ - player.sendMessage("There are no trees nearby to make a new canoe. Guess you're walking.") - } - player.varpManager.setVarbit(varbit,0) - return true - } - } - return false - } - }) - return@on true - } - } -} \ No newline at end of file diff --git a/Server/src/main/content/global/travel/canoe/CanoeListener.kt b/Server/src/main/content/global/travel/canoe/CanoeListener.kt new file mode 100644 index 000000000..57553ba37 --- /dev/null +++ b/Server/src/main/content/global/travel/canoe/CanoeListener.kt @@ -0,0 +1,372 @@ +package content.global.travel.canoe + +import content.data.skill.SkillingTool +import core.api.* +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.interaction.InterfaceListener +import core.game.interaction.QueueStrength +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.game.node.scenery.SceneryBuilder +import core.game.system.task.Pulse +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 +import core.tools.RandomFunction +import org.rs09.consts.Components +import org.rs09.consts.Scenery +import org.rs09.consts.Sounds +import kotlin.math.abs + +/** + * Interaction and interface listener for canoe along river Lum. + * This handles Components.CANOE_52, Components.CANOE_STATIONS_MAP_53 and Components.CANOE_TRAVEL_758 globally. + */ +class CanoeListener : InteractionListener, InterfaceListener { + companion object { + const val CANOE_STATION_VARBIT_ATTRIBUTE = "canoeStationVarbit" // Index of Canoe Stations (0-4) + const val CANOE_SELECTED_ATTRIBUTE = "canoeSelected" // Index of Canoes (0-3) + + const val CANOE_SHAPING_INTERFACE = Components.CANOE_52 + const val CANOE_DESTINATION_INTERFACE = Components.CANOE_STATIONS_MAP_53 + const val CANOE_TRAVEL_INTERFACE = Components.CANOE_TRAVEL_758 + + val CANOE_SHAPING_SILHOUETTE = arrayOf(0, 9, 10, 8) + val CANOE_SHAPING_TEXT = arrayOf(0, 3, 2, 5) + val CANOE_SHAPING_BUTTONS = arrayOf(30, 31, 32, 33) + + val CANOE_DESTINATION_BUTTONS = arrayOf(47, 48, 3, 6, 49) + val CANOE_DESTINATION_HIDE_ROW = arrayOf(10, 11, 12, 20, 18) + val CANOE_DESTINATION_YOU_ARE_HERE = arrayOf(25, 24, 23, 19, 0) + + val CANOE_TREE_FALLING_ANIMATION = Animation(3304) + val CANOE_PLAYER_PUSHING_ANIMATION = Animation(3301) + val CANOE_PUSHING_ANIMATION = Animation(3304) + val CANOE_SINKING_ANIMATION = Animation(3305) + /** Canoe travel interface animations. A 2D array mapping [ origin ][ destination ]. Index 0-4 for Lumbridge to Wilderness. */ + val CANOE_TRAVEL_ANIMATIONS = arrayOf( + arrayOf(0, 9890, 9889, 9888, 9887), + arrayOf(9906, 0, 9893, 9892, 9891), + arrayOf(9904, 9905, 0, 9895, 9894), + arrayOf(9901, 9902, 9903, 0, 9896), + arrayOf(9897, 9898, 9899, 9900, 0), + ) + + @JvmStatic + fun getAxeAnimation(axe: SkillingTool): Animation { + return when (axe) { + SkillingTool.BRONZE_AXE -> Animation(6744) + SkillingTool.IRON_AXE -> Animation(6743) + SkillingTool.STEEL_AXE -> Animation(6742) + SkillingTool.BLACK_AXE -> Animation(6741) + SkillingTool.MITHRIL_AXE -> Animation(6740) + SkillingTool.ADAMANT_AXE -> Animation(6739) + SkillingTool.RUNE_AXE -> Animation(6738) + SkillingTool.DRAGON_AXE -> Animation(6745) + else -> axe.animation + } + } + + /** To scale woodcutting success. Follows WoodcuttingNode values at 15,30,45,60 (3 levels 'harder'). */ + private fun checkSuccess(player: Player, resource: Canoes, tool: SkillingTool): Boolean { + val skill = Skills.WOODCUTTING + val level: Int = getDynLevel(player, skill) + getFamiliarBoost(player, skill) + val hostRatio = RandomFunction.randomDouble(100.0) + val lowMod: Double = if (tool == SkillingTool.BLACK_AXE) resource.tierModLow / 2 else resource.tierModLow + val low: Double = resource.baseLow + tool.ordinal * lowMod + val highMod: Double = if (tool == SkillingTool.BLACK_AXE) resource.tierModHigh / 2 else resource.tierModHigh + val high: Double = resource.baseHigh + tool.ordinal * highMod + val clientRatio = RandomFunction.getSkillSuccessChance(low, high, level) + return hostRatio < clientRatio + } + + /** Maps canoe stations stages to related properties. */ + enum class CanoeStationSceneries(val sceneryId: Int, val varbitValue: Int) { + // Stage 1 - Tree is standing, option to chop-down. + TREE_STANDING(Scenery.CANOE_STATION_12144, 0), + + // Stage 2 - The tree is falling via an animation, denies player of options. + TREE_FALLING(Scenery.CANOE_STATION_12145, 9), + + // Stage 3 - Tree is on the cradle after the chop, option to shape the boat via an interface. + TREE_FALLEN(Scenery.CANOE_STATION_12146, 10), + + // Stage 4 - Tree gets shaped into 1 of 4 canoes, option to push it into the water. + TREE_SHAPED_LOG(Scenery.CANOE_STATION_12147, 1), + TREE_SHAPED_DUGOUT(Scenery.CANOE_STATION_12148, 2), + TREE_SHAPED_STABLE_DUGOUT(Scenery.CANOE_STATION_12149, 3), + TREE_SHAPED_WAKA(Scenery.CANOE_STATION_12150, 4), + + // Stage 5 - Canoe is being pushed into the water via an animation, option to travel is available. + CANOE_PUSHING_LOG(Scenery.CANOE_STATION_12151, 5), + CANOE_PUSHING_DUGOUT(Scenery.CANOE_STATION_12152, 6), + CANOE_PUSHING_STABLE_DUGOUT(Scenery.CANOE_STATION_12153, 7), + CANOE_PUSHING_WAKA(Scenery.CANOE_STATION_12154, 8), + + // Stage 6 - Canoe is in the water, option to travel via an interface. + CANOE_FLOATING_LOG(Scenery.CANOE_STATION_12155, 11), + CANOE_FLOATING_DUGOUT(Scenery.CANOE_STATION_12156, 12), + CANOE_FLOATING_STABLE_DUGOUT(Scenery.CANOE_STATION_12157, 13), + CANOE_FLOATING_WAKA(Scenery.CANOE_STATION_12158, 14), + + // Stage 7 - Canoe is animated to be sunk at the end of the ride. NO VARBITS, NOT PART OF THE CANOE_STATION SCENERY. + CANOE_SINKING_LOG(Scenery.A_SINKING_CANOE_12159, 0), + CANOE_SINKING_DUGOUT(Scenery.A_SINKING_CANOE_12160, 0), + CANOE_SINKING_STABLE_DUGOUT(Scenery.A_SINKING_CANOE_12161, 0), + CANOE_SINKING_WAKA(Scenery.A_SINKING_CANOE_12162, 0); + + companion object { + @JvmField + val stationIdMap = values().associateBy { it.sceneryId } + val stationIdArray = stationIdMap.values.map { it.sceneryId }.toIntArray() + } + } + + /** Enums to map canoes to related properties. */ + enum class Canoes(val level: Int, val experience: Double, val maxDistance: Int, val baseLow: Double, val baseHigh: Double, val tierModLow: Double, val tierModHigh: Double, val treeShaped: CanoeStationSceneries, val canoePushing: CanoeStationSceneries, val canoeFloating: CanoeStationSceneries, val canoeSinking: CanoeStationSceneries) { + LOG (12, 30.0, 1, 32.0, 100.0, 16.0, 50.0, CanoeStationSceneries.TREE_SHAPED_LOG, CanoeStationSceneries.CANOE_PUSHING_LOG, CanoeStationSceneries.CANOE_FLOATING_LOG, CanoeStationSceneries.CANOE_SINKING_LOG), + DUGOUT (27, 60.0, 2, 16.0, 50.0, 8.0, 25.0, CanoeStationSceneries.TREE_SHAPED_DUGOUT, CanoeStationSceneries.CANOE_PUSHING_DUGOUT, CanoeStationSceneries.CANOE_FLOATING_DUGOUT, CanoeStationSceneries.CANOE_SINKING_DUGOUT), + STABLE_DUGOUT (42, 90.0, 3, 8.0, 25.0, 4.0, 12.5, CanoeStationSceneries.TREE_SHAPED_STABLE_DUGOUT, CanoeStationSceneries.CANOE_PUSHING_STABLE_DUGOUT, CanoeStationSceneries.CANOE_FLOATING_STABLE_DUGOUT, CanoeStationSceneries.CANOE_SINKING_STABLE_DUGOUT), + WAKA (57, 150.0, 4, 4.0, 12.5, 2.0, 6.25, CanoeStationSceneries.TREE_SHAPED_WAKA, CanoeStationSceneries.CANOE_PUSHING_WAKA, CanoeStationSceneries.CANOE_FLOATING_WAKA, CanoeStationSceneries.CANOE_SINKING_WAKA); + + companion object { + @JvmField + val indexMap = Canoes.values().associateBy { it.ordinal } + } + } + + /** Enums to map canoe stations locations to related properties. */ + enum class CanoeStationLocations(val stationRegion: Int, val stationVarbit: Int, val playerChopLocation: Location, val playerFloatLocation: Location, val playerFacingLocation: Location, val canoeSinkLocation: Location, val playerDestination: Location, val locationName: String) { + LUMBRIDGE(12850, 1839, Location(3243, 3235), Location(3243, 3237), Location(-1, 0), Location(3239, 3242), Location(3240, 3242), "Lumbridge"), + CHAMPIONS(12852, 1840, Location(3204, 3343), Location(3202, 3343), Location(0, -1), Location(3199, 3344), Location(3199, 3344), "the Champion's Guild"), + BARBARIAN(12341, 1841, Location(3112, 3409), Location(3112, 3411), Location(-1, 0), Location(3109, 3411), Location(3109, 3415), "Barbarian Village"), + EDGEVILLE(12342, 1842, Location(3132, 3508), Location(3132, 3510), Location(-1, 0), Location(3132, 3510), Location(3132, 3510), "Edgeville"), + WILDERNESS(12603, 0, Location(0, 0), Location(0, 0), Location(0, 0), Location(3142, 3795), Location(3139, 3796), "the Wilderness Pond"); + + companion object { + private val stationRegionMap = CanoeStationLocations.values().associateBy { it.stationRegion } + + @JvmStatic + fun getCanoeStationbyLocation(location: Location): CanoeStationLocations { + return stationRegionMap[location.regionId]!! + } + } + } + } + + override fun defineDestinationOverrides() { + // Set player's standing location when chopping down the tree. + setDest(IntType.SCENERY, CanoeStationSceneries.stationIdArray, "chop-down") { _, node -> + return@setDest CanoeStationLocations.getCanoeStationbyLocation(node.location).playerChopLocation + } + // Set player's standing location when shaping and floating the canoe. + setDest(IntType.SCENERY, CanoeStationSceneries.stationIdArray, "shape-canoe", "float canoe", "float log", "float waka") { _, node -> + return@setDest CanoeStationLocations.getCanoeStationbyLocation(node.location).playerFloatLocation + } + } + + override fun defineListeners() { + // Stage 1 - 2: Tree is standing, option to chop-down. + on(CanoeStationSceneries.stationIdArray, IntType.SCENERY, "chop-down") { player, node -> + val canoeStation = CanoeStationLocations.getCanoeStationbyLocation(node.location) + val axe: SkillingTool? = SkillingTool.getHatchet(player) + val stationVarbit = node.asScenery().definition.configFile + if (axe == null) { + sendMessage(player, "You do not have an axe which you have the woodcutting level to use.") + return@on true + } + if (getStatLevel(player, Skills.WOODCUTTING) < 12) { + sendMessage(player, "You need a woodcutting level of at least 12 to chop down this tree.") + return@on true + } + lock(player, axe.animation.duration + CANOE_TREE_FALLING_ANIMATION.duration) + face(player, canoeStation.playerChopLocation.transform(canoeStation.playerFacingLocation)) + animate(player, axe.animation) + queueScript(player, axe.animation.duration, QueueStrength.SOFT) { stage: Int -> + when (stage) { + 0 -> { + player.animator.stop() + setVarbit(player, stationVarbit, CanoeStationSceneries.TREE_FALLING.varbitValue) + animateScenery(player, node.asScenery(), CANOE_TREE_FALLING_ANIMATION.id) + return@queueScript delayScript(player, CANOE_TREE_FALLING_ANIMATION.duration) + } + 1 -> { + setVarbit(player, stationVarbit, CanoeStationSceneries.TREE_FALLEN.varbitValue) + unlock(player) + return@queueScript stopExecuting(player) + } + else -> return@queueScript stopExecuting(player) + } + } + return@on true + } + + // Stage 3: Tree is on the cradle after the chop, option to shape-canoe via interface CANOE_52. + on(CanoeStationSceneries.stationIdArray, IntType.SCENERY, "shape-canoe") { player, node -> + val canoeStation = CanoeStationLocations.getCanoeStationbyLocation(node.location) + setAttribute(player, CANOE_STATION_VARBIT_ATTRIBUTE, canoeStation.stationVarbit) + face(player, canoeStation.playerFloatLocation.transform(canoeStation.playerFacingLocation)) + openInterface(player, CANOE_SHAPING_INTERFACE) + openOverlay(player, 333) // Black overlay + return@on true + } + + // Stage 4 - 5: Tree gets shaped into 1 of 4 canoes, option to float-canoe. + on(CanoeStationSceneries.stationIdArray, IntType.SCENERY, "float canoe", "float log", "float waka") { player, node -> + val canoeStation = CanoeStationLocations.getCanoeStationbyLocation(node.location) + val canoe = Canoes.indexMap[getAttribute(player, CANOE_SELECTED_ATTRIBUTE, 0)]!! + setVarbit(player, canoeStation.stationVarbit, canoe.canoePushing.varbitValue) + lock(player, CANOE_PLAYER_PUSHING_ANIMATION.duration) + playAudio(player, Sounds.CANOE_ROLL_2731) + face(player, canoeStation.playerFloatLocation.transform(canoeStation.playerFacingLocation)) + queueScript(player, 0, QueueStrength.SOFT) { stage: Int -> + when (stage) { + 0 -> { + animate(player, CANOE_PLAYER_PUSHING_ANIMATION) + animateScenery(player, node.asScenery(), CANOE_PUSHING_ANIMATION.id) + return@queueScript delayScript(player, CANOE_PLAYER_PUSHING_ANIMATION.duration) + } + 1 -> { + setVarbit(player, canoeStation.stationVarbit, canoe.canoeFloating.varbitValue) + unlock(player) + player.animator.stop() + return@queueScript stopExecuting(player) + } + else -> return@queueScript stopExecuting(player) + } + } + return@on true + } + + // Stage 6: Canoe is in the water, option to paddle via interface CANOE_STATIONS_MAP_53. + on(CanoeStationSceneries.stationIdArray, IntType.SCENERY, "paddle log", "paddle canoe") { player, _ -> + closeInterface(player) + openInterface(player, Components.CANOE_STATIONS_MAP_53) + openOverlay(player, 333) // Black overlay + return@on true + } + } + + var temp = 14 + override fun defineInterfaceListeners() { + + // Stage 3: Opening interface 52 + onOpen(CANOE_SHAPING_INTERFACE) { player, _ -> + Canoes.values().forEach { + if (getStatLevel(player, Skills.WOODCUTTING) >= it.level && it != Canoes.LOG) { + setComponentVisibility(player, CANOE_SHAPING_INTERFACE, CANOE_SHAPING_SILHOUETTE[it.ordinal], true) + setComponentVisibility(player, CANOE_SHAPING_INTERFACE, CANOE_SHAPING_TEXT[it.ordinal], false) + } + } + return@onOpen true + } + + // Stage 3: On button click interface 52 + on(CANOE_SHAPING_INTERFACE) { player, _, _, buttonID, _, _ -> + closeInterface(player) + val canoe = Canoes.indexMap[CANOE_SHAPING_BUTTONS.indexOf(buttonID)]!! + val stationVarbit = getAttribute(player, CANOE_STATION_VARBIT_ATTRIBUTE, 1839) + val axe: SkillingTool? = SkillingTool.getHatchet(player) + if (axe == null) { + sendMessage(player, "You do not have an axe which you have the woodcutting level to use.") + return@on true + } + + lock(player, 4) + animate(player, getAxeAnimation(axe)) + submitIndividualPulse(player, object : Pulse(3) { + override fun pulse(): Boolean { + if (checkSuccess(player, canoe, axe)) { + setAttribute(player, CANOE_SELECTED_ATTRIBUTE, CANOE_SHAPING_BUTTONS.indexOf(buttonID)) + setVarbit(player, stationVarbit, canoe.treeShaped.varbitValue) + rewardXP(player, Skills.WOODCUTTING, canoe.experience) + unlock(player) + return true + } + animate(player, getAxeAnimation(axe)) + return false + } + }) + return@on true + } + + onOpen(CANOE_DESTINATION_INTERFACE) { player, component -> + val canoe = Canoes.indexMap[getAttribute(player, CANOE_SELECTED_ATTRIBUTE, 0)]!! + val origin = CanoeStationLocations.getCanoeStationbyLocation(player.location) + for (i in CanoeStationLocations.values()) { + setComponentVisibility(player, component.id, CANOE_DESTINATION_HIDE_ROW[i.ordinal], true) + if (i == CanoeStationLocations.WILDERNESS) { + if (canoe == Canoes.WAKA) { + // setComponentVisibility(player, component.id, 22, false) // Some red text on Wildy text. + setComponentVisibility(player, component.id, CANOE_DESTINATION_HIDE_ROW[i.ordinal], false) + } + } else if (i.ordinal == origin.ordinal) { + setComponentVisibility(player, component.id, CANOE_DESTINATION_YOU_ARE_HERE[i.ordinal], false) + }else if (abs(i.ordinal - origin.ordinal) <= canoe.maxDistance) { + setComponentVisibility(player, component.id, CANOE_DESTINATION_HIDE_ROW[i.ordinal], false) + } + } + return@onOpen true + } + + on(CANOE_DESTINATION_INTERFACE) { player, _, _, buttonID, _, _ -> + val origin = CanoeStationLocations.getCanoeStationbyLocation(player.location) + var destination: CanoeStationLocations = CanoeStationLocations.LUMBRIDGE + when (buttonID) { + CANOE_DESTINATION_BUTTONS[0] -> destination = CanoeStationLocations.LUMBRIDGE + CANOE_DESTINATION_BUTTONS[1] -> destination = CanoeStationLocations.CHAMPIONS + CANOE_DESTINATION_BUTTONS[2] -> destination = CanoeStationLocations.BARBARIAN + CANOE_DESTINATION_BUTTONS[3] -> destination = CanoeStationLocations.EDGEVILLE + CANOE_DESTINATION_BUTTONS[4] -> destination = CanoeStationLocations.WILDERNESS + } + val arrivalMessage = destination.locationName + val interfaceAnimationId = CANOE_TRAVEL_ANIMATIONS[origin.ordinal][destination.ordinal] + + if (player.familiarManager.hasFamiliar()) { + sendMessage(player, "You can't take a follower on a canoe.") + return@on true + } + lock(player, Animation(interfaceAnimationId).duration + 1 + Animation(Components.FADE_FROM_BLACK_170).duration) + // Tried using queueScript here, but it doesn't work as interfaces and overlays can't open. Maybe its because this is within an interface listener. + submitIndividualPulse(player, object : Pulse() { + var counter = 0 + override fun pulse(): Boolean { + when (counter++) { + 0 -> { + openInterface(player, CANOE_TRAVEL_INTERFACE) + openOverlay(player, 333) // Only call openOverlay(player, 333) after openInterface + animateInterface(player, CANOE_TRAVEL_INTERFACE, 3, interfaceAnimationId) + PacketRepository.send(MinimapState::class.java, MinimapStateContext(player, 2)) + player.interfaceManager.removeTabs(0, 1, 2, 3, 4, 5, 6, 11, 12) + } + Animation(interfaceAnimationId).duration + 1 -> { + teleport(player, destination.playerDestination) + closeInterface(player) + closeOverlay(player) + openOverlay(player, Components.FADE_FROM_BLACK_170) + } + Animation(interfaceAnimationId).duration + 1 + Animation(Components.FADE_FROM_BLACK_170).duration -> { + unlock(player) + player.interfaceManager.restoreTabs() + PacketRepository.send(MinimapState::class.java, MinimapStateContext(player, 0)) + val sinkingScenery = SceneryBuilder.add(core.game.node.scenery.Scenery(Scenery.A_SINKING_CANOE_12159, destination.canoeSinkLocation, 1), 3) as core.game.node.scenery.Scenery + animateScenery(sinkingScenery, CANOE_SINKING_ANIMATION.id) + sendMessage(player, "You arrive at $arrivalMessage.") + sendMessage(player, "Your canoe sinks from the long journey.") + if (destination == CanoeStationLocations.WILDERNESS) { + sendMessage(player, "There are no trees nearby to make a new canoe. Guess you're walking.") + } + setVarbit(player, origin.stationVarbit, 0) + return true + } + } + return false + } + }) + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/global/travel/canoe/CanoeStationListener.kt b/Server/src/main/content/global/travel/canoe/CanoeStationListener.kt deleted file mode 100644 index ee2cf8cd9..000000000 --- a/Server/src/main/content/global/travel/canoe/CanoeStationListener.kt +++ /dev/null @@ -1,98 +0,0 @@ -package content.global.travel.canoe - -import core.game.component.Component -import core.game.node.entity.skill.Skills -import content.data.skill.SkillingTool -import core.game.system.task.Pulse -import core.game.world.update.flag.context.Animation -import org.rs09.consts.Components -import core.game.interaction.InteractionListener -import core.game.interaction.IntType - -class CanoeStationListener : InteractionListener { - - private val STATION_IDs = intArrayOf(12140, 12141, 12142, 12143, 12144, 12145, 12146, 12147, 12148, 12151, 12152, 12153, 12154, 12155, 12156, 12157, 12158, 12149, 12150) - private val STAGE_TREE_NONINTERACTABLE = 9 - private val STAGE_LOG_CHOPPED = 10 - private val SHAPING_INTERFACE = Components.CANOE_52 - private val PUSH = Animation(3301) - private val FALL = Animation(3303) - private val FLOAT = Animation(3304) - - override fun defineDestinationOverrides() { - setDest(IntType.SCENERY,STATION_IDs,"chop-down"){ _, node -> - return@setDest CanoeUtils.getChopLocation(node.location) - } - setDest(IntType.SCENERY,STATION_IDs,"shape-canoe","float canoe","float log","float waka"){ _, node -> - return@setDest CanoeUtils.getCraftFloatLocation(node.location) - } - } - - override fun defineListeners() { - on(STATION_IDs, IntType.SCENERY, "chop-down"){ player, node -> - val axe: SkillingTool? = SkillingTool.getHatchet(player) - val varbit = node.asScenery().definition.configFile - if(varbit.getValue(player) != 0){ - player.varpManager.setVarbit(varbit,0) - } - - if (axe == null) { - player.packetDispatch.sendMessage("You do not have an axe which you have the woodcutting level to use.") - return@on true - } - if (player.skills.getLevel(Skills.WOODCUTTING) < 12) { - player.packetDispatch.sendMessage("You need a woodcutting level of at least 12 to chop down this tree.") - return@on true - } - player.lock() - player.varpManager.get(varbit.varpId).clearBitRange(0,31) - player.faceLocation(CanoeUtils.getFaceLocation(player.location)) - player.animate(axe.animation) - player.varpManager.setVarbit(varbit,STAGE_TREE_NONINTERACTABLE) - player.pulseManager.run(object : Pulse(4){ - override fun pulse(): Boolean { - player.animator.stop() - player.varpManager.setVarbit(varbit,STAGE_LOG_CHOPPED) - player.packetDispatch.sendSceneryAnimation(node.asScenery().getChild(player), FALL, false) - player.unlock() - return true - } - }) - return@on true - } - - on(STATION_IDs, IntType.SCENERY, "shape-canoe"){ player, node -> - val varbit = node.asScenery().definition.configFile - if(varbit.getValue(player) != STAGE_LOG_CHOPPED){ - player.varpManager.setVarbit(varbit,0) - return@on true - } - player.faceLocation(CanoeUtils.getFaceLocation(player.location)) - player.interfaceManager.open(Component(SHAPING_INTERFACE)) - player.setAttribute("canoe-varbit",varbit) - return@on true - } - - on(STATION_IDs, IntType.SCENERY, "float canoe","float log","float waka"){ player, node -> - val varbit = node.asScenery().definition.configFile - val canoe = CanoeUtils.getCanoeFromVarbit(player, varbit) - player.animator.animate(PUSH) - player.lock() - player.faceLocation(CanoeUtils.getFaceLocation(player.location)) - player.pulseManager.run(object : Pulse(){ - override fun pulse(): Boolean { - player.varpManager.setVarbit(varbit, CanoeUtils.getCraftValue(canoe, true)) - player.packetDispatch.sendSceneryAnimation(node.asScenery(), FLOAT, false) - player.unlock() - return true - } - }) - return@on true - } - - on(STATION_IDs, IntType.SCENERY, "paddle log","paddle canoe"){ player, node -> - player.interfaceManager.open(Component(Components.CANOE_STATIONS_MAP_53)) - return@on true - } - } -} \ No newline at end of file diff --git a/Server/src/main/content/global/travel/canoe/CanoeUtils.kt b/Server/src/main/content/global/travel/canoe/CanoeUtils.kt deleted file mode 100644 index 7552d3f8b..000000000 --- a/Server/src/main/content/global/travel/canoe/CanoeUtils.kt +++ /dev/null @@ -1,122 +0,0 @@ -package content.global.travel.canoe - -import core.cache.def.impl.VarbitDefinition -import core.game.node.entity.player.Player -import core.game.node.entity.skill.Skills -import content.data.skill.SkillingTool -import core.game.world.map.Location -import core.game.world.update.flag.context.Animation -import org.rs09.consts.Components - -object CanoeUtils { - private const val SHAPE_INTERFACE = Components.CANOE_52 - - private val FROM_LUMBRIDGE = mapOf(4 to 9887, 3 to 9888, 2 to 9889, 1 to 9890) - private val FROM_CHAMPIONS = mapOf(4 to 9891, 3 to 9892, 2 to 9893, 0 to 9906) - private val FROM_BARBARIAN = mapOf(4 to 9894, 3 to 9895, 1 to 9905, 0 to 9906) - private val FROM_EDGE = mapOf(4 to 9896, 2 to 9903, 1 to 9902, 0 to 9901) - private val FROM_WILDERNESS = mapOf(3 to 9900, 2 to 9899, 1 to 9898, 0 to 9897) - - fun checkCanoe(player: Player, canoe: Canoe){ - if(player.skills.getLevel(Skills.WOODCUTTING) < canoe.requiredLevel) return - player.packetDispatch.sendInterfaceConfig(SHAPE_INTERFACE,canoe.silhouetteChild,true) - player.packetDispatch.sendInterfaceConfig(SHAPE_INTERFACE,canoe.textChild,false) - } - - fun getCanoeFromVarbit(player: Player, varbit: VarbitDefinition): Canoe { - var bit = varbit.getValue(player) - if(bit > 10) bit -= 10 - return Canoe.values()[bit - 1] - } - - fun getCraftValue(canoe: Canoe, floating: Boolean): Int{ - return 1 + (canoe.ordinal + if (floating) 10 else 0) - } - - fun getStationIndex(location: Location): Int{ - return when(location.regionId){ - 12850 -> 0 - 12852,12596 -> 1 - 12341 -> 2 - 12342 -> 3 - 12603 -> 4 - else -> 0 - } - } - - fun getTravelAnimation(stationId: Int, destId: Int): Int { - return when(stationId){ - 0 -> FROM_LUMBRIDGE.getOrDefault(destId,0) - 1 -> FROM_CHAMPIONS.getOrDefault(destId,0) - 2 -> FROM_BARBARIAN.getOrDefault(destId,0) - 3 -> FROM_EDGE.getOrDefault(destId,0) - 4 -> FROM_WILDERNESS.getOrDefault(destId,0) - else -> 0; - } - } - - fun getShapeAnimation(axe: SkillingTool): Animation{ - return when(axe){ - SkillingTool.BRONZE_AXE -> Animation(6744); - SkillingTool.IRON_AXE -> Animation(6743); - SkillingTool.STEEL_AXE -> Animation(6742); - SkillingTool.BLACK_AXE -> Animation(6741); - SkillingTool.MITHRIL_AXE -> Animation(6740); - SkillingTool.ADAMANT_AXE -> Animation(6739); - SkillingTool.RUNE_AXE -> Animation(6738); - SkillingTool.DRAGON_AXE -> Animation(6745); - else -> axe.animation; - } - } - - fun getDestinationFromButtonID(buttonID: Int): Location { - return when(buttonID){ - 47 -> Location.create(3240, 3242, 0) - 48 -> Location.create(3199, 3344, 0) - 3 -> Location.create(3109, 3415, 0) - 6 -> Location.create(3132, 3510, 0) - 49 -> Location.create(3139, 3796, 0) - else -> Location.create(3240, 3242, 0) - } - } - - fun getNameByIndex(index: Int): String{ - return when(index){ - 0 -> "Lumbridge" - 1 -> "the Champion's Guild." - 2 -> "Barbarian Village" - 3 -> "Edgeville" - 4 -> "the Wilderness Pond" - else -> "Uhhh report this." - } - } - - fun getFaceLocation(location: Location): Location{ - return when(getStationIndex(location)){ - 1 -> location.transform(0,-1,0) - 0,2,3 -> location.transform(-1,0,0) - else -> location - } - } - - fun getChopLocation(location: Location): Location{ - return when(getStationIndex(location)){ - 0 -> Location.create(3243, 3235, 0) - 1 -> Location.create(3204, 3343, 0) - 2 -> Location.create(3112, 3409, 0) - 3 -> Location.create(3132, 3508, 0) - else -> Location.create(0,0) - } - } - - fun getCraftFloatLocation(location: Location): Location{ - return when(getStationIndex(location)){ - 0 -> Location.create(3243, 3237, 0) - 1 -> Location.create(3202, 3343, 0) - 2 -> Location.create(3112, 3411, 0) - 3 -> Location.create(3132, 3510, 0) - else -> Location.create(0,0) - } - } - -} \ No newline at end of file diff --git a/Server/src/main/content/global/travel/glider/CaptainDalburDialogue.java b/Server/src/main/content/global/travel/glider/CaptainDalburDialogue.java index 099ae43b7..67f0cbaa0 100644 --- a/Server/src/main/content/global/travel/glider/CaptainDalburDialogue.java +++ b/Server/src/main/content/global/travel/glider/CaptainDalburDialogue.java @@ -1,5 +1,6 @@ package content.global.travel.glider; +import content.data.Quests; import core.game.component.Component; import core.game.dialogue.DialoguePlugin; import core.game.dialogue.FacialExpression; @@ -7,6 +8,9 @@ import core.game.node.entity.npc.NPC; import core.plugin.Initializable; import core.game.node.entity.player.Player; +import static core.api.ContentAPIKt.isQuestComplete; +import static core.tools.DialogueConstKt.END_DIALOGUE; + /** * Represents the dialogue plugin used for the captain dalbur npc. * @author 'Vexia @@ -54,12 +58,14 @@ public final class CaptainDalburDialogue extends DialoguePlugin { stage = 1; break; case 1: - npc("If you wish."); - stage++; - /* - * interpreter.sendDialogues(npc, FacialExpression.NORMAL, - * "I only fly friends of the gnomes!"); stage = 2; - */ + if(!isQuestComplete(player, Quests.THE_GRAND_TREE)){ + interpreter.sendDialogues(npc, FacialExpression.ANNOYED, "I only fly friends of the gnomes!"); + stage = END_DIALOGUE; + } + else { + npc("If you wish."); + stage++; + } break; case 2: end(); diff --git a/Server/src/main/content/global/travel/glider/GliderPlugin.java b/Server/src/main/content/global/travel/glider/GliderPlugin.java index 70ed2de87..b3cbe634c 100644 --- a/Server/src/main/content/global/travel/glider/GliderPlugin.java +++ b/Server/src/main/content/global/travel/glider/GliderPlugin.java @@ -1,13 +1,15 @@ package content.global.travel.glider; +import content.data.Quests; +import core.api.ContentAPIKt; import core.cache.def.impl.NPCDefinition; import core.game.component.Component; -import content.global.travel.glider.Gliders; 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; +import static core.api.ContentAPIKt.isQuestComplete; /** * Represents the plugin used for gliders. @@ -29,8 +31,12 @@ public final class GliderPlugin extends OptionHandler { @Override public boolean handle(Player player, Node node, String option) { - player.getInterfaceManager().open(new Component(138)); - Gliders.sendConfig(node.asNpc(), player); + if(isQuestComplete(player, Quests.THE_GRAND_TREE)){ + player.getInterfaceManager().open(new Component(138)); + Gliders.sendConfig(node.asNpc(), player); + } else { + ContentAPIKt.sendMessage(player,"You must complete The Grand Tree Quest to access the gnome glider."); + } return true; } diff --git a/Server/src/main/content/global/travel/glider/GliderPulse.java b/Server/src/main/content/global/travel/glider/GliderPulse.java index 52a5c44e7..06873a6cd 100644 --- a/Server/src/main/content/global/travel/glider/GliderPulse.java +++ b/Server/src/main/content/global/travel/glider/GliderPulse.java @@ -11,6 +11,9 @@ import core.net.packet.context.MinimapStateContext; import core.net.packet.out.CameraViewPacket; import core.net.packet.out.MinimapState; +import static core.api.ContentAPIKt.*; + + /** * Represents the pulse used for a glider. * @@ -49,7 +52,7 @@ public final class GliderPulse extends Pulse { public boolean pulse() { final boolean crash = glider == Gliders.LEMANTO_ADRA; if (count == 1) { - player.varpManager.get(153).setVarbit(0,glider.getConfig()).send(player); + setVarp(player, 153, glider.getConfig()); PacketRepository.send(MinimapState.class, new MinimapStateContext(player, 2)); } else if (count == 2 && crash) { PacketRepository.send(CameraViewPacket.class, new CameraContext(player, CameraType.SHAKE, 4, 4, 1200, 4, 4)); @@ -62,13 +65,13 @@ public final class GliderPulse extends Pulse { player.getProperties().setTeleportLocation(glider.getLocation()); } else if (count == 5) { if (crash) { - player.getPacketDispatch().sendMessage("The glider becomes uncontrolable and crashes down..."); + player.getPacketDispatch().sendMessage("The glider becomes uncontrollable and crashes down..."); PacketRepository.send(CameraViewPacket.class, new CameraContext(player, CameraType.RESET, 0, 0, 0, 0, 0)); } player.getInterfaceManager().closeOverlay(); player.getInterfaceManager().close(); PacketRepository.send(MinimapState.class, new MinimapStateContext(player, 0)); - player.varpManager.get(153).setVarbit(0,0).send(player); + setVarp(player, 153, 0); // Use the gnome glider to travel to Karamja if (!crash && glider == Gliders.GANDIUS) { player.getAchievementDiaryManager().finishTask(player, DiaryType.KARAMJA, 1, 11); diff --git a/Server/src/main/content/global/travel/glider/Gliders.java b/Server/src/main/content/global/travel/glider/Gliders.java index 7d3f2464f..99f9f780c 100644 --- a/Server/src/main/content/global/travel/glider/Gliders.java +++ b/Server/src/main/content/global/travel/glider/Gliders.java @@ -4,6 +4,8 @@ import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.game.world.map.Location; +import static core.api.ContentAPIKt.setVarp; + /** * Represents an enum of glider locations. * @author 'Vexia @@ -60,7 +62,7 @@ public enum Gliders { if (g == null) { return; } - player.getConfigManager().set(153, g.getConfig()); + setVarp(player, 153, g.getConfig()); } /** diff --git a/Server/src/main/content/global/travel/ship/SeamanDialoguePlugin.java b/Server/src/main/content/global/travel/ship/SeamanDialoguePlugin.java index 0ed7ad2c6..41b226d36 100644 --- a/Server/src/main/content/global/travel/ship/SeamanDialoguePlugin.java +++ b/Server/src/main/content/global/travel/ship/SeamanDialoguePlugin.java @@ -10,6 +10,9 @@ import core.game.node.entity.player.Player; 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. * @@ -44,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)) { @@ -148,6 +151,7 @@ public class SeamanDialoguePlugin extends DialoguePlugin { public void travel() { end(); Ships.PORT_SARIM_TO_KARAMAJA.sail(player); + playJingle(player, 172); } @Override diff --git a/Server/src/main/content/global/travel/ship/ShipCharter.java b/Server/src/main/content/global/travel/ship/ShipCharter.java index e549502b4..9e04a8878 100644 --- a/Server/src/main/content/global/travel/ship/ShipCharter.java +++ b/Server/src/main/content/global/travel/ship/ShipCharter.java @@ -16,7 +16,8 @@ import core.tools.StringUtils; import java.util.ArrayList; import java.util.List; -import static core.api.ContentAPIKt.requireQuest; +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 (player.getQuestRepository().isComplete(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()); @@ -141,10 +143,15 @@ public final class ShipCharter { 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(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."); } }, - 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){ @Override public int getCost(Player player, Destination destination) { @@ -161,7 +168,13 @@ 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), + 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) { + @Override + public boolean checkTravel(Player player) { + return hasRequirement(player, Quests.REGICIDE); + } + + }, 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) { @Override public int getCost(Player player, Destination destination) { @@ -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), + 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) { + @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, 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); + 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) { + @Override + public boolean checkTravel(Player player) { + return hasRequirement(player, Quests.CABIN_FEVER); + } + }; /** * Constructs a new {@code ShipCharter} {@code Object}. @@ -341,6 +364,7 @@ public final class ShipCharter { */ public void sail(final Player player) { player.lock(7); + playJingle(player, 171); Location start = player.getLocation(); GameWorld.getPulser().submit(new Pulse(1) { int count = 0; diff --git a/Server/src/main/content/global/travel/ship/ShipTravelPulse.java b/Server/src/main/content/global/travel/ship/ShipTravelPulse.java index 95f5bc151..5f5808737 100644 --- a/Server/src/main/content/global/travel/ship/ShipTravelPulse.java +++ b/Server/src/main/content/global/travel/ship/ShipTravelPulse.java @@ -8,6 +8,8 @@ import core.net.packet.PacketRepository; import core.net.packet.context.MinimapStateContext; import core.net.packet.out.MinimapState; +import static core.api.ContentAPIKt.setVarp; + /** * Represents a pulse used to travel a player to a location. * @author Vexia @@ -65,7 +67,7 @@ public final class ShipTravelPulse extends Pulse { */ private void arrive() { player.unlock(); - player.getConfigManager().set(75, -1); + setVarp(player, 75, 0); player.getInterfaceManager().close(); PacketRepository.send(MinimapState.class, new MinimapStateContext(player, 0)); if (!ship.getName().equals("Crandor")) { @@ -96,6 +98,6 @@ public final class ShipTravelPulse extends Pulse { player.lock(ship.getDelay() + 1); player.getInterfaceManager().open(new Component(299)); PacketRepository.send(MinimapState.class, new MinimapStateContext(player, 2)); - player.getConfigManager().set(75, ship.getConfig()); + setVarp(player, 75, ship.getConfig()); } } diff --git a/Server/src/main/content/global/travel/ship/TraderCrewmemberDialogue.java b/Server/src/main/content/global/travel/ship/TraderCrewmemberDialogue.java index 189f51d64..405064ad8 100644 --- a/Server/src/main/content/global/travel/ship/TraderCrewmemberDialogue.java +++ b/Server/src/main/content/global/travel/ship/TraderCrewmemberDialogue.java @@ -2,6 +2,7 @@ package content.global.travel.ship; import content.global.travel.ship.ShipCharter.Destination; 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; @@ -54,41 +55,41 @@ public class TraderCrewmemberDialogue extends DialoguePlugin { case 1: switch (buttonId) { case 1: - player("Yes, who are you?"); + interpreter.sendDialogues(player, FacialExpression.ASKING,"Yes, who are you?"); stage = 100; break; case 2: - player("Yes, I would like to charter a ship."); + interpreter.sendDialogues(player, FacialExpression.FRIENDLY,"Yes, I would like to charter a ship."); stage = 2000; break; } break; case 100: - npc("I'm one of the Trader Stan's crew; we are all part of the", "largest fleet of trading and sailing vessels to ever sail the", "seven seas."); + interpreter.sendDialogues(npc, FacialExpression.FRIENDLY,"I'm one of the Trader Stan's crew; we are all part of the", "largest fleet of trading and sailing vessels to ever sail the", "seven seas."); stage = 101; break; case 101: - npc("If you want to get to a port in a hurry then you can", "charter one of our ships to take you there - if the price", "is right..."); + interpreter.sendDialogues(npc, FacialExpression.FRIENDLY,"If you want to get to a port in a hurry then you can", "charter one of our ships to take you there - if the price", "is right..."); stage = 102; break; case 102: - player("So, where exactly can I go with your ships?"); + interpreter.sendDialogues(player, FacialExpression.HALF_ASKING,"So, where exactly can I go with your ships?"); stage = 103; break; case 103: - npc("We run ships from Port Phasmatys over to Port Tyras,", "stopping at Port Sarim, Catherby, Jaramja,", "the Shipyard and Port Khazard."); + interpreter.sendDialogues(npc, FacialExpression.NEUTRAL,"We run ships from Port Phasmatys over to Port Tyras,", "stopping at Port Sarim, Catherby, Karamja,", "the Shipyard and Port Khazard."); stage = 104; break; case 104: - player("Wow, that's a lot of ports. I take it you have some exotic", "stuff to trade?"); + interpreter.sendDialogues(player, FacialExpression.FRIENDLY,"Wow, that's a lot of ports. I take it you have some exotic", "stuff to trade?"); stage = 105; break; case 105: - npc("We certainly do! We have access to items", "bought and sold from around the world."); + interpreter.sendDialogues(npc, FacialExpression.HAPPY,"We certainly do! We have access to items", "bought and sold from around the world."); stage = 106; break; case 106: - npc("Would you like to take a look?"); + interpreter.sendDialogues(npc, FacialExpression.HALF_ASKING,"Would you like to take a look?"); stage = 107; break; case 107: @@ -98,7 +99,7 @@ public class TraderCrewmemberDialogue extends DialoguePlugin { case 108: switch (buttonId) { case 1: - player("Yes."); + interpreter.sendDialogues(player, FacialExpression.FRIENDLY,"Yes."); stage = 1000; break; case 2: @@ -112,7 +113,7 @@ public class TraderCrewmemberDialogue extends DialoguePlugin { npc.openShop(player); break; case 2000: - npc("Certainly sir, where would you like to go?"); + interpreter.sendDialogues(npc, FacialExpression.HAPPY,"Certainly sir, where would you like to go?"); stage = 2001; break; case 2001: @@ -136,7 +137,7 @@ public class TraderCrewmemberDialogue extends DialoguePlugin { return true; } if (!player.getInventory().remove(new Item(995, cost))) { - interpreter.sendDialogues(player, null, "I don't have the money for that."); + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I don't have the money for that."); stage = 30002; return true; } diff --git a/Server/src/main/content/global/travel/trees/GnomeSpiritTreeListener.kt b/Server/src/main/content/global/travel/trees/GnomeSpiritTreeListener.kt index 494a79e12..838473c20 100644 --- a/Server/src/main/content/global/travel/trees/GnomeSpiritTreeListener.kt +++ b/Server/src/main/content/global/travel/trees/GnomeSpiritTreeListener.kt @@ -12,12 +12,12 @@ 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 class GnomeSpiritTreeListener: InteractionListener { val spiritTrees = intArrayOf(1317,1293,1294) @@ -46,7 +46,7 @@ class GnomeSpiritTreeTeleportDialogue: DialogueFile() { 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 diff --git a/Server/src/main/content/minigame/allfiredup/AFUBeacon.kt b/Server/src/main/content/minigame/allfiredup/AFUBeacon.kt index 54aeb81ba..c97013a0f 100644 --- a/Server/src/main/content/minigame/allfiredup/AFUBeacon.kt +++ b/Server/src/main/content/minigame/allfiredup/AFUBeacon.kt @@ -1,66 +1,68 @@ package content.minigame.allfiredup +import core.api.* import core.game.node.entity.player.Player import core.tools.SystemLogger import core.game.world.map.Location +import core.tools.Log /** * Various data for beacons, such as varp and offset, required FM level, etc * @author Ceikry */ -enum class AFUBeacon(val title: String, val fmLevel: Int, val varpId: Int, val offset: Int, val location: Location, val experience: Double, val keeper: Int = 0) { - RIVER_SALVE("",43,1283,0,Location.create(3396, 3464, 0),216.2,8065), - RAG_AND_BONE("",43,1283,3,Location.create(3343, 3510, 0),235.8,8066), - JOLLY_BOAR("",48,1283,6,Location.create(3278, 3525, 0), 193.8,8067), - NORTH_VARROCK_CASTLE("",53,1283,9,Location.create(3236, 3527, 0),178.5,8068), - GRAND_EXCHANGE("",59,1283,12,Location.create(3170, 3536, 0),194.3,8069), - EDGEVILLE("",62,1283,15,Location.create(3087, 3516, 0),86.7,8070), - MONASTERY("",68,1283,18,Location.create(3034, 3518, 0),224.4,8071), - GOBLIN_VILLAGE("",72,1283,21,Location.create(2968, 3516, 0),194.8,8072), - BURTHORPE("",76,1283,24,Location.create(2940, 3565, 0),195.3,8073), - DEATH_PLATEAU("",79,1288,0,Location.create(2944, 3622, 0),249.9,8074), - TROLLHEIM("",83,1288,3,Location.create(2939, 3680, 0),201.0,8075), - GWD("",87,1288,6,Location.create(2937, 3773, 0),255.0,8076), - TEMPLE("",89,1288,9,Location.create(2946, 3836, 0),198.9), - PLATEAU("",92,1288,12,Location.create(2964, 3931, 0),147.9); +enum class AFUBeacon(val title: String, val fmLevel: Int, val varbit: Int, val location: Location, val experience: Double, val keeper: Int = 0) { + RIVER_SALVE("",43,5146,Location.create(3396, 3464, 0),216.2,8065), + RAG_AND_BONE("",43,5147,Location.create(3343, 3510, 0),235.8,8066), + JOLLY_BOAR("",48,5148,Location.create(3278, 3525, 0), 193.8,8067), + NORTH_VARROCK_CASTLE("",53,5149,Location.create(3236, 3527, 0),178.5,8068), + GRAND_EXCHANGE("",59,5150,Location.create(3170, 3536, 0),194.3,8069), + EDGEVILLE("",62,5151,Location.create(3087, 3516, 0),86.7,8070), + MONASTERY("",68,5152,Location.create(3034, 3518, 0),224.4,8071), + GOBLIN_VILLAGE("",72,5153,Location.create(2968, 3516, 0),194.8,8072), + BURTHORPE("",76,5154,Location.create(2940, 3565, 0),195.3,8073), + DEATH_PLATEAU("",79,5155,Location.create(2944, 3622, 0),249.9,8074), + TROLLHEIM("",83,5156,Location.create(2939, 3680, 0),201.0,8075), + GWD("",87,5157,Location.create(2937, 3773, 0),255.0,8076), + TEMPLE("",89,5158,Location.create(2946, 3836, 0),198.9), + PLATEAU("",92,5159,Location.create(2964, 3931, 0),147.9); companion object { - fun forLocation(location: Location): content.minigame.allfiredup.AFUBeacon { + fun forLocation(location: Location): AFUBeacon { for (beacon in values()) { if (beacon.location.equals(location)) return beacon } - return content.minigame.allfiredup.AFUBeacon.RIVER_SALVE.also { SystemLogger.logWarn(this::class.java, "Unhandled Beacon Location ${location.toString()}") } + return AFUBeacon.RIVER_SALVE.also { log(this::class.java, Log.WARN, "Unhandled Beacon Location ${location.toString()}") } } fun resetAllBeacons(player: Player){ for(beacon in values()){ - player.varpManager.get(beacon.varpId).setVarbit(beacon.offset,0).send(player) + setVarbit(player, beacon.varbit, 0) } } } fun light(player: Player){ - player.varpManager.get(varpId).setVarbit(offset,2).send(player) + setVarbit(player, varbit, 2) } fun diminish(player: Player){ - player.varpManager.get(varpId).setVarbit(offset,3).send(player) + setVarbit(player, varbit, 3) } fun extinguish(player: Player){ - player.varpManager.get(varpId).setVarbit(offset,0).send(player) + setVarbit(player, varbit, 0) } fun lightGnomish(player: Player){ - player.varpManager.get(varpId).setVarbit(offset,4).send(player) + setVarbit(player, varbit, 4) } fun fillWithLogs(player: Player){ - player.varpManager.get(varpId).setVarbit(offset,1).send(player) + setVarbit(player, varbit, 1, true) } - fun getState(player: Player): content.minigame.allfiredup.BeaconState { - return content.minigame.allfiredup.BeaconState.values()[player.varpManager.get(varpId).getVarbit(offset) ?: 0] + fun getState(player: Player): BeaconState { + return BeaconState.values()[getVarbit(player, varbit)] } } @@ -70,4 +72,4 @@ enum class BeaconState{ LIT, DYING, WARNING -} \ No newline at end of file +} diff --git a/Server/src/main/content/minigame/allfiredup/AFUBeaconHandler.kt b/Server/src/main/content/minigame/allfiredup/AFUBeaconHandler.kt index 0d6364ce6..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) @@ -23,26 +24,26 @@ class AFUBeaconListeners : InteractionListener { override fun defineListeners() { on(IntType.SCENERY,"add-logs","light"){ player, node -> - val beacon = content.minigame.allfiredup.AFUBeacon.forLocation(node.location) - val questComplete = player.questRepository.isComplete("All Fired Up") - val questStage = player.questRepository.getStage("All Fired Up") + val beacon = AFUBeacon.forLocation(node.location) + val questComplete = player.questRepository.isComplete(Quests.ALL_FIRED_UP) + val questStage = player.questRepository.getStage(Quests.ALL_FIRED_UP) - if ((beacon != content.minigame.allfiredup.AFUBeacon.RIVER_SALVE && beacon != content.minigame.allfiredup.AFUBeacon.RAG_AND_BONE && !questComplete) - || (beacon == content.minigame.allfiredup.AFUBeacon.RIVER_SALVE && questStage < 20 && !questComplete) - || (beacon == content.minigame.allfiredup.AFUBeacon.RAG_AND_BONE && questStage < 50 && !questComplete)) { + if ((beacon != AFUBeacon.RIVER_SALVE && beacon != AFUBeacon.RAG_AND_BONE && !questComplete) + || (beacon == AFUBeacon.RIVER_SALVE && questStage < 20 && !questComplete) + || (beacon == AFUBeacon.RAG_AND_BONE && questStage < 50 && !questComplete)) { player.dialogueInterpreter.sendDialogues(player, core.game.dialogue.FacialExpression.THINKING, "I probably shouldn't mess with this.") return@on true } player.debug(beacon.getState(player).name) when (beacon.getState(player)) { - content.minigame.allfiredup.BeaconState.EMPTY -> fillBeacon(player, beacon, questComplete) + BeaconState.EMPTY -> fillBeacon(player, beacon, questComplete) - content.minigame.allfiredup.BeaconState.DYING -> restoreBeacon(player, beacon, questComplete) + BeaconState.DYING -> restoreBeacon(player, beacon, questComplete) - content.minigame.allfiredup.BeaconState.FILLED -> lightBeacon(player, beacon, questComplete) + BeaconState.FILLED -> lightBeacon(player, beacon, questComplete) - content.minigame.allfiredup.BeaconState.LIT, content.minigame.allfiredup.BeaconState.WARNING -> { + BeaconState.LIT, BeaconState.WARNING -> { player.debug("INVALID BEACON STATE") } } @@ -50,30 +51,32 @@ class AFUBeaconListeners : InteractionListener { } } - fun fillBeacon(player: Player, beacon: content.minigame.allfiredup.AFUBeacon, questComplete: Boolean){ + fun fillBeacon(player: Player, beacon: AFUBeacon, questComplete: Boolean){ when(beacon){ - content.minigame.allfiredup.AFUBeacon.MONASTERY -> { + AFUBeacon.MONASTERY -> { if(player.skills.getLevel(Skills.PRAYER) < 31){ player.dialogueInterpreter.sendDialogues(NPC(beacon.keeper).getShownNPC(player), core.game.dialogue.FacialExpression.ANGRY,"You must join the monastery to light this beacon!") return } } - content.minigame.allfiredup.AFUBeacon.GWD -> { + AFUBeacon.GWD -> { if(!AFURepairClimbHandler.isRepaired(player, beacon)){ player.dialogueInterpreter.sendDialogue("You must repair the windbreak before you","can light this beacon.") return } } - content.minigame.allfiredup.AFUBeacon.GOBLIN_VILLAGE -> { - if(!player.questRepository.isComplete("Lost Tribe")){ + AFUBeacon.GOBLIN_VILLAGE -> { + 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 } } + else -> {} + } if(player.skills.getLevel(Skills.FIREMAKING) < beacon.fmLevel){ @@ -104,7 +107,6 @@ class AFUBeaconListeners : InteractionListener { if(questComplete){ session?.setLogs(beacon.ordinal,logs) } - player.varpManager.flagSave(beacon.varpId); } 2 -> player.unlock().also {player.animator.animate(Animation.RESET); return true } } @@ -116,7 +118,7 @@ class AFUBeaconListeners : InteractionListener { } } - fun lightBeacon(player: Player, beacon: content.minigame.allfiredup.AFUBeacon, questComplete: Boolean){ + fun lightBeacon(player: Player, beacon: AFUBeacon, questComplete: Boolean){ var session: AFUSession? = null if(questComplete){ session = player.getAttribute("afu-session",null) @@ -150,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 } @@ -163,7 +165,7 @@ class AFUBeaconListeners : InteractionListener { } } - fun restoreBeacon(player: Player, beacon: content.minigame.allfiredup.AFUBeacon, questComplete: Boolean){ + fun restoreBeacon(player: Player, beacon: AFUBeacon, questComplete: Boolean){ var session: AFUSession? = null if(questComplete){ session = player.getAttribute("afu-session",null) @@ -182,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 } @@ -213,4 +215,4 @@ class AFUBeaconListeners : InteractionListener { return inventory.containsItem(Item(Items.INFERNO_ADZE_13661)) || bank.containsItem(Item(Items.INFERNO_ADZE_13661)) || equipment.containsItem(Item(Items.INFERNO_ADZE_13661)) } -} \ No newline at end of file +} diff --git a/Server/src/main/content/minigame/allfiredup/AFURepairClimbHandler.kt b/Server/src/main/content/minigame/allfiredup/AFURepairClimbHandler.kt index 84d34c4eb..f95dfb1d9 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 @@ -11,6 +12,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.api.* import java.util.* /** @@ -25,27 +27,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 null + } + private fun repair(player: Player,rco: RepairClimbObject){ + if (rco == RepairClimbObject.TEMPLE){ + // You can do this 2 different ways + val hasSmithingLevel = getDynLevel(player, Skills.SMITHING) >= 70 + val hasConstructionLevel = getDynLevel(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.") - return } var requiresNeedle = false @@ -63,10 +112,7 @@ class AFURepairClimbHandler : InteractionListener { requiresNeedle = true arrayOf(Item(Items.JUTE_FIBRE_5931,3)) } - - RepairClimbObject.TEMPLE -> { - arrayOf(Item(Items.IRON_BAR_2351,2)) - } + else -> return } if(requiresNeedle){ @@ -74,7 +120,7 @@ class AFURepairClimbHandler : InteractionListener { 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.") + player.dialogueInterpreter.sendDialogue("You need a needle and ${requiredItems.map { "${it.amount} ${it.name.lowercase()}s" }.toString().replace("[","").replace("]","")} for this.") return } } else { @@ -88,24 +134,22 @@ class AFURepairClimbHandler : InteractionListener { } player.inventory.remove(*requiredItems) } else { - player.dialogueInterpreter.sendDialogue("You need a hammer and ${requiredItems.map { "${it.amount} ${it.name.toLowerCase()}s" }.toString().replace("[","").replace("]","")} for this.") + player.dialogueInterpreter.sendDialogue("You need a hammer and ${requiredItems.map { "${it.amount} ${it.name.lowercase()}s" }.toString().replace("[","").replace("]","")} for this.") return } } - - player.varpManager.get(rco.varp).setVarbit(rco.offset,1).send(player) - player.varpManager.flagSave(rco.varp) + setVarbit(player, rco.varbit, 1, true) } private fun climb(player: Player, rco: RepairClimbObject, location: Location){ ForceMovement.run(player,location,rco.getOtherLocation(player),rco.getAnimation(player),rco.getAnimation(player),rco.getDirection(player),20).endAnimation = Animation(-1) } - private enum class RepairClimbObject(val varp: Int, val offset: Int, val destinationUp: Location?, val destinationDown: Location?, val levelRequirement: Pair?){ - DEATH_PLATEAU(1282,15,Location.create(2949, 3623, 0),Location.create(2954, 3623, 0), Pair(Skills.CONSTRUCTION,42)), - BURTHORPE(1282,14,Location.create(2941, 3563, 0),Location.create(2934, 3563, 0),Pair(Skills.SMITHING,56)), - GWD(1283,27,null,null,Pair(Skills.CRAFTING,60)), - TEMPLE(1287,29,Location.create(2949, 3835, 0),Location.create(2956, 3835, 0),Pair(Skills.SMITHING,64)); + private enum class RepairClimbObject(val varbit: Int, val destinationUp: Location?, val destinationDown: Location?, val levelRequirement: Pair?){ + 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(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 @@ -124,18 +168,18 @@ class AFURepairClimbHandler : InteractionListener { } fun isRepaired(player: Player): Boolean{ - return player.varpManager.get(varp).getVarbit(offset) == 1 + return getVarbit(player, varbit) == 1 } } companion object { - fun isRepaired(player: Player, beacon: content.minigame.allfiredup.AFUBeacon): Boolean{ - if(beacon == content.minigame.allfiredup.AFUBeacon.DEATH_PLATEAU) return RepairClimbObject.DEATH_PLATEAU.isRepaired(player) - if(beacon == content.minigame.allfiredup.AFUBeacon.BURTHORPE) return RepairClimbObject.BURTHORPE.isRepaired(player) - if(beacon == content.minigame.allfiredup.AFUBeacon.GWD) return RepairClimbObject.GWD.isRepaired(player) - if(beacon == content.minigame.allfiredup.AFUBeacon.TEMPLE) return RepairClimbObject.TEMPLE.isRepaired(player) + fun isRepaired(player: Player, beacon: AFUBeacon): Boolean{ + if(beacon == AFUBeacon.DEATH_PLATEAU) return RepairClimbObject.DEATH_PLATEAU.isRepaired(player) + if(beacon == AFUBeacon.BURTHORPE) return RepairClimbObject.BURTHORPE.isRepaired(player) + if(beacon == AFUBeacon.GWD) return RepairClimbObject.GWD.isRepaired(player) + if(beacon == AFUBeacon.TEMPLE) return RepairClimbObject.TEMPLE.isRepaired(player) else return true } } -} \ No newline at end of file +} diff --git a/Server/src/main/content/minigame/allfiredup/AFUSession.kt b/Server/src/main/content/minigame/allfiredup/AFUSession.kt index ef4f837df..1168a0f0f 100644 --- a/Server/src/main/content/minigame/allfiredup/AFUSession.kt +++ b/Server/src/main/content/minigame/allfiredup/AFUSession.kt @@ -13,7 +13,7 @@ import core.tools.colorize * @author Ceikry */ class AFUSession(val player: Player? = null) : LogoutListener { - private val beaconTimers = Array(14){i -> BeaconTimer(0, content.minigame.allfiredup.AFUBeacon.values()[i]) } + private val beaconTimers = Array(14){i -> BeaconTimer(0, AFUBeacon.values()[i]) } private val logInventories = Array(14){Item(0,0)} private val beaconWatched = Array(14){false} private var isActive = false @@ -61,7 +61,7 @@ class AFUSession(val player: Player? = null) : LogoutListener { beaconTimers[beaconIndex].ticks = ticks } - fun refreshTimer(beacon: content.minigame.allfiredup.AFUBeacon, logID: Int){ + fun refreshTimer(beacon: AFUBeacon, logID: Int){ val ticks = getTicks(logID) * 5 beaconTimers.forEach { if(it.beacon.ordinal == beacon.ordinal) it.ticks += ticks @@ -111,11 +111,11 @@ class AFUSession(val player: Player? = null) : LogoutListener { } override fun logout(player: Player) { - content.minigame.allfiredup.AFUBeacon.resetAllBeacons(player) + AFUBeacon.resetAllBeacons(player) val session: AFUSession? = player.getAttribute("afu-session",null) session?.end() player.removeAttribute("afu-session") } - internal class BeaconTimer(var ticks: Int, val beacon: content.minigame.allfiredup.AFUBeacon) + internal class BeaconTimer(var ticks: Int, val beacon: AFUBeacon) } \ No newline at end of file diff --git a/Server/src/main/content/minigame/allfiredup/BeaconTenderDialogue.kt b/Server/src/main/content/minigame/allfiredup/BeaconTenderDialogue.kt index 4933c0e29..f2b25db41 100644 --- a/Server/src/main/content/minigame/allfiredup/BeaconTenderDialogue.kt +++ b/Server/src/main/content/minigame/allfiredup/BeaconTenderDialogue.kt @@ -1,48 +1,50 @@ package content.minigame.allfiredup -import core.game.dialogue.DialoguePlugin +import core.ServerConstants +import core.game.dialogue.* import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player import core.game.node.entity.skill.Skills import core.game.node.item.Item import core.plugin.Initializable +import content.region.wilderness.dialogue.* import org.rs09.consts.Items private val VALID_LOGS = arrayOf(Items.LOGS_1511, Items.OAK_LOGS_1521, Items.WILLOW_LOGS_1519, Items.MAPLE_LOGS_1517, Items.YEW_LOGS_1515, Items.MAGIC_LOGS_1513) @Initializable //TODO: Add requirements for beacon keepers to watch beacons, most of the requirements were not possible to implement at the time of writing this. -class BeaconTenderDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { +class BeaconTenderDialogue(player: Player? = null) : DialoguePlugin(player) { var index = 0 - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return BeaconTenderDialogue(player) } override fun open(vararg args: Any?): Boolean { + npc = (args[0] as NPC).getShownNPC(player) index = getIndexOf((args[0] as NPC).originalId) - if(index == content.minigame.allfiredup.AFUBeacon.GWD.ordinal && player.skills.getLevel(Skills.SUMMONING) < 81){ + if(index == AFUBeacon.GWD.ordinal && player.skills.getLevel(Skills.SUMMONING) < 81){ npc("Awwf uurrrhur","(You need 81 Summoning to communicate with Nanuq.)") stage = 1000 return true } - if(index == content.minigame.allfiredup.AFUBeacon.MONASTERY.ordinal && player.skills.getLevel(Skills.PRAYER) < 53){ - npc("I will aid you when your devotion is","strong enough.","(You need 53 Prayer for him to watch the beacon.)") + if(index == AFUBeacon.MONASTERY.ordinal && player.skills.getLevel(Skills.PRAYER) < 53){ + npc("I will aid you when your devotion is","strong enough.","(You need 53 Prayer for him to assist you.)") stage = 1000 return true } - npc = (args[0] as NPC).getShownNPC(player) npc("Hello, adventurer.") return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { - val beacon = content.minigame.allfiredup.AFUBeacon.values()[index] + val beacon = AFUBeacon.values()[index] val logs = getLogs(player,5) val session: AFUSession? = player.getAttribute("afu-session",null) when(stage){ 0 -> player("Hello!").also { stage++ } - 1 -> if(beacon.getState(player) == content.minigame.allfiredup.BeaconState.LIT && session?.isWatched(index) == false){ + 1 -> if(beacon.getState(player) == BeaconState.LIT && session?.isWatched(index) == false){ options("Can you watch this beacon for me?","Nevermind.").also { stage = 10 } } else { npc("Carry on, adventurer.").also { stage = 1000 } @@ -88,4 +90,4 @@ class BeaconTenderDialogue(player: Player? = null) : core.game.dialogue.Dialogue return intArrayOf(8067,8068,8069,8070,8071,8072,8073,8074,8075,8076) } -} \ 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 f1d91e854..baeb381ba 100644 --- a/Server/src/main/content/minigame/barbassault/CaptainCainDialogue.kt +++ b/Server/src/main/content/minigame/barbassault/CaptainCainDialogue.kt @@ -1,27 +1,34 @@ package content.minigame.barbassault -import core.api.* +import core.api.Container +import core.api.addItem +import core.api.inInventory +import core.api.removeItem 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 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) : core.game.dialogue.DialoguePlugin(player) { +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?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return CaptainCainDialogue(player) } override fun open(vararg args: Any?): Boolean { - npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Hello, there, adventurer. Say, you wouldn't happen to be interested in purchasing a Fighter Torso would you?") + npcl(FacialExpression.FRIENDLY, "Hello, there, adventurer. Say, you wouldn't happen to be interested in purchasing a Fighter Torso would you?") stage = 0 return true } @@ -31,27 +38,27 @@ class CaptainCainDialogue(player: Player? = null) : core.game.dialogue.DialogueP val now = Date().toInstant() val days = ChronoUnit.DAYS.between(start,now) when(stage){ - 0 -> npcl(core.game.dialogue.FacialExpression.ANNOYED,"I'm having to offer this service because it's been $days days since Ryan promised to give us barbarian assault.").also { stage++ } + 0 -> npcl(FacialExpression.ANNOYED,"I'm having to offer this service because it's been $days days since Ryan promised to give us barbarian assault.").also { stage++ } 1 -> options("Yes, please.","No, thanks.").also { stage++ } 2 -> when(buttonId){ - 1 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY, "Yes, please.").also { stage = 10 } - 2 -> playerl(core.game.dialogue.FacialExpression.HALF_THINKING, "No, thanks.").also { stage = END_DIALOGUE } + 1 -> playerl(FacialExpression.FRIENDLY, "Yes, please.").also { stage = 10 } + 2 -> playerl(FacialExpression.HALF_THINKING, "No, thanks.").also { stage = END_DIALOGUE } } - 10 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Alright, then, that'll be 7,500,000 gold please.").also { stage++ } + 10 -> npcl(FacialExpression.FRIENDLY, "Alright, then, that'll be %,d gold please.".format(TORSO_PRICE)).also { stage++ } 11 -> options("Here you go!","Nevermind.").also { stage++ } 12 -> when(buttonId){ - 1 -> if(inInventory(player, 995, 7500000)) - playerl(core.game.dialogue.FacialExpression.FRIENDLY, "Here you go!").also { stage = 20 } + 1 -> if(inInventory(player, 995, TORSO_PRICE)) + playerl(FacialExpression.FRIENDLY, "Here you go!").also { stage = 20 } else - playerl(core.game.dialogue.FacialExpression.HALF_GUILTY, "Actually, I don't have that much.").also { stage = END_DIALOGUE } + playerl(FacialExpression.HALF_GUILTY, "Actually, I don't have that much.").also { stage = END_DIALOGUE } - 2 -> playerl(core.game.dialogue.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(core.game.dialogue.FacialExpression.FRIENDLY, "Thank you much, kind sir. And here's your torso.") - if(removeItem(player, Item(995,7500000), Container.INVENTORY)) { + npcl(FacialExpression.FRIENDLY, "Thank you much, kind sir. And here's your torso.") + if(removeItem(player, Item(995, TORSO_PRICE), Container.INVENTORY)) { addItem(player, Items.FIGHTER_TORSO_10551, 1) } stage = END_DIALOGUE diff --git a/Server/src/main/content/minigame/barrows/BarrowsActivityPlugin.java b/Server/src/main/content/minigame/barrows/BarrowsActivityPlugin.java index b5089c978..fa8df174c 100644 --- a/Server/src/main/content/minigame/barrows/BarrowsActivityPlugin.java +++ b/Server/src/main/content/minigame/barrows/BarrowsActivityPlugin.java @@ -33,7 +33,9 @@ import core.tools.RandomFunction; import core.game.world.GameWorld; import core.plugin.ClassScanner; -import static core.api.ContentAPIKt.getWorldTicks; +import java.util.stream.IntStream; + +import static core.api.ContentAPIKt.*; /** * Handles the barrows activity plugin. @@ -97,7 +99,7 @@ public final class BarrowsActivityPlugin extends ActivityPlugin { } } int drain = 8; - System.out.println(getWorldTicks() % 30); + //if (p.getLocks().isLocked("barrow:drain") || RandomFunction.random(100) % 2 == 0) { // continue; //} @@ -137,8 +139,8 @@ public final class BarrowsActivityPlugin extends ActivityPlugin { } } Player player = (Player) e; - if ((player.getConfigManager().get(1270) == 1) != tunnel) { - player.getConfigManager().set(1270, tunnel ? 3 : 0, true);//@emp: proper value seems to be 3, val of 1 makes corridors black + if ((getVarp(player, 1270) == 1) != tunnel) { + setVarp(player, 1270, tunnel ? 3 : 0, true); } } } @@ -149,8 +151,8 @@ public final class BarrowsActivityPlugin extends ActivityPlugin { Player player = (Player) e; PacketRepository.send(MinimapState.class, new MinimapStateContext(player, 2)); player.getInterfaceManager().openOverlay(OVERLAY); - player.getConfigManager().set(0, 1); - if (player.getConfigManager().get(452) == 0) { + setVarp(player, 0, 1); + if (getVarp(player, 452) == 0) { shuffleCatacombs(player); } sendConfiguration(player); @@ -212,7 +214,7 @@ public final class BarrowsActivityPlugin extends ActivityPlugin { public static void shuffleCatacombs(Player player) { int value = TUNNEL_CONFIGS[RandomFunction.random(TUNNEL_CONFIGS.length)]; value |= 1 << (6 + RandomFunction.random(4)); - player.getConfigManager().set(452, value); + setVarp(player, 452, value); } @Override @@ -258,15 +260,19 @@ public final class BarrowsActivityPlugin extends ActivityPlugin { switch (object.getId()) { case 6714: case 6733: - int index = -1; - int brother = player.getSavedData().getActivityData().getBarrowTunnelIndex(); - if (!player.getSavedData().getActivityData().getBarrowBrothers()[brother] && RandomFunction.random(15) == 0 && !player.getAttribute("brother:" + brother, false)) { - index = brother; - } - if (index > -1) { - BarrowsCrypt.getCrypt(index).spawnBrother(player, RegionManager.getTeleportLocation(target.getLocation(), 1)); - } DoorActionHandler.handleAutowalkDoor(e, (Scenery) target); + if (RandomFunction.random(15) == 0) { + // spawn a brother, if any haven't yet been killed + boolean[] brothers = player.getSavedData().getActivityData().getBarrowBrothers(); + int[] alive = IntStream.range(0, 6).filter(i -> !brothers[i]).toArray(); + if (alive.length > 0) { + int index = 0; + if (alive.length > 1) { + index = RandomFunction.random(0, alive.length); + } + BarrowsCrypt.getCrypt(alive[index]).spawnBrother(player, RegionManager.getTeleportLocation(target.getLocation(), 1)); + } + } return true; case 6821: BarrowsCrypt.getCrypt(BarrowsCrypt.AHRIM).openSarcophagus((Player) e, object); @@ -288,7 +294,7 @@ public final class BarrowsActivityPlugin extends ActivityPlugin { return true; case 6774: player.lock(1); - brother = player.getSavedData().getActivityData().getBarrowTunnelIndex(); + int brother = player.getSavedData().getActivityData().getBarrowTunnelIndex(); if (!player.getSavedData().getActivityData().getBarrowBrothers()[brother] && !player.getAttribute("brother:" + brother, false)) { BarrowsCrypt.getCrypt(brother).spawnBrother(player, RegionManager.getTeleportLocation(target.getCenterLocation(), 4)); } @@ -330,7 +336,7 @@ public final class BarrowsActivityPlugin extends ActivityPlugin { if (player.getAttribute("barrow:opened_chest", false)) { config |= 1 << 16; } - player.getConfigManager().set(453, config); + setVarp(player, 453, config); } @Override diff --git a/Server/src/main/content/minigame/barrows/RewardChest.kt b/Server/src/main/content/minigame/barrows/RewardChest.kt index 1a79be82c..d12f48a1e 100644 --- a/Server/src/main/content/minigame/barrows/RewardChest.kt +++ b/Server/src/main/content/minigame/barrows/RewardChest.kt @@ -16,56 +16,28 @@ import core.api.utils.WeightedItem /** * The reward chest. * @author Ceikry - * and slightly kermit + * @author kermit + * @author Player Name */ object RewardChest { - /** - * The low profit drop table. - */ - private val DROP_TABLE = WeightBasedTable.create( - //Weighted total = 3050 - WeightedItem(Items.COINS_995, 1, 5306, 950.0), - WeightedItem(Items.MIND_RUNE_558, 60, 60, 300.0), - WeightedItem(Items.MIND_RUNE_558, 100, 850, 300.0), - WeightedItem(Items.CHAOS_RUNE_562, 115, 720, 300.0), - WeightedItem(Items.DEATH_RUNE_560, 15, 15, 300.0), - WeightedItem(Items.DEATH_RUNE_560, 70, 230, 300.0), - WeightedItem(Items.BLOOD_RUNE_565, 35, 230, 300.0), - WeightedItem(Items.BOLT_RACK_4740, 35, 280, 300.0), - //Weight total = 975 - WeightedItem(Items.SUPER_DEFENCE2_165, 1, 1, 325.0), - WeightedItem(Items.PRAYER_POTION2_141, 1, 1, 325.0), - WeightedItem(Items.RESTORE_POTION2_129, 1, 1, 325.0), - //Weight total = 53 - WeightedItem(Items.TOOTH_HALF_OF_A_KEY_985, 1, 1, 25.0), - WeightedItem(Items.LOOP_HALF_OF_A_KEY_987, 1, 1, 25.0), - WeightedItem(Items.DRAGON_MED_HELM_1149, 1, 1, 3.0), - //Weight total = 120 BARROWS ITEMS V - WeightedItem(4708, 1, 1, 5.0), - WeightedItem(4710, 1, 1, 5.0), - WeightedItem(4712, 1, 1, 5.0), - WeightedItem(4714, 1, 1, 5.0), - WeightedItem(4716, 1, 1, 5.0), - WeightedItem(4718, 1, 1, 5.0), - WeightedItem(4720, 1, 1, 5.0), - WeightedItem(4722, 1, 1, 5.0), - WeightedItem(4724, 1, 1, 5.0), - WeightedItem(4726, 1, 1, 5.0), - WeightedItem(4728, 1, 1, 5.0), - WeightedItem(4730, 1, 1, 5.0), - WeightedItem(4732, 1, 1, 5.0), - WeightedItem(4734, 1, 1, 5.0), - WeightedItem(4736, 1, 1, 5.0), - WeightedItem(4738, 1, 1, 5.0), - WeightedItem(4745, 1, 1, 5.0), - WeightedItem(4747, 1, 1, 5.0), - WeightedItem(4749, 1, 1, 5.0), - WeightedItem(4751, 1, 1, 5.0), - WeightedItem(4753, 1, 1, 5.0), - WeightedItem(4755, 1, 1, 5.0), - WeightedItem(4757, 1, 1, 5.0), - WeightedItem(4759, 1, 1, 5.0) + private val REGULAR_DROPS = WeightBasedTable.create( + WeightedItem(Items.COINS_995, 1, 777, 380.0), + WeightedItem(Items.MIND_RUNE_558, 250, 350, 125.0), + WeightedItem(Items.CHAOS_RUNE_562, 115, 135, 125.0), + WeightedItem(Items.DEATH_RUNE_560, 70, 85, 125.0), + WeightedItem(Items.BLOOD_RUNE_565, 35, 45, 125.0), + WeightedItem(Items.BOLT_RACK_4740, 35, 40, 125.0), + WeightedItem(Items.TOOTH_HALF_OF_A_KEY_985, 1, 1, 3.0), + WeightedItem(Items.LOOP_HALF_OF_A_KEY_987, 1, 1, 3.0), + WeightedItem(Items.DRAGON_MED_HELM_1149, 1, 1, 1.0) ) + private val AHRIM = arrayOf(4708, 4710, 4712, 4714) + private val DHAROK = arrayOf(4716, 4718, 4720, 4722) + private val GUTHAN = arrayOf(4724, 4726, 4728, 4730) + private val KARIL = arrayOf(4732, 4734, 4736, 4738) + private val TORAG = arrayOf(4745, 4747, 4749, 4751) + private val VERAC = arrayOf(4753, 4755, 4757, 4759) + private val BARROWS_DROP_IDS = arrayOf(AHRIM, DHAROK, GUTHAN, KARIL, TORAG, VERAC) /** * Rewards the player. @@ -74,28 +46,43 @@ object RewardChest { */ @JvmStatic fun reward(player: Player) { - for (killed in player.savedData.activityData.barrowBrothers) { - if (!killed){ - player.sendMessage("You can't loot the chest until you kill all 6 barrows brothers.") - player.removeAttribute("barrow:looted") - // Because they haven't - // actually looted the - // chest yet. - return + var barrowsRewardsIDs: MutableList = ArrayList() + for (i in 0..5) { + if (player.savedData.activityData.barrowBrothers[i]) { + barrowsRewardsIDs.addAll(BARROWS_DROP_IDS[i]) } } - val rewards: MutableList = ArrayList() - var maxRolls = 2 + RandomFunction.random(1,4) - for (i in 0 until maxRolls) { - rewards.addAll(DROP_TABLE.roll()) + barrowsRewardsIDs.shuffle() + + var rewards: MutableList = ArrayList() + val nKilled = barrowsRewardsIDs.size / 4 + roll@for (i in 0 until nKilled+1) { + if (barrowsRewardsIDs.size > 0 && RandomFunction.roll(450 - 58 * nKilled)) { + // Award a random item from a random brother + val reward = barrowsRewardsIDs[0] + rewards.add(Item(reward, 1)) + barrowsRewardsIDs.removeAt(0) + } else { + // Award a drop from the regular drop table + val drop = REGULAR_DROPS.roll(null, 1)[0] + for (i in 0 until rewards.size) { + // If we have already awarded this item, just add to the previous quantity + if (rewards[i].id == drop.id) { + rewards[i].amount += drop.amount + continue@roll + } + } + rewards.add(drop) + } } - InterfaceContainer.generateItems(player, rewards.toTypedArray(), arrayOf("Examine"), 364, 4,3,4) + + InterfaceContainer.generateItems(player, rewards.toTypedArray(), arrayOf("Examine"), 364, 4, 3, 4) player.interfaceManager.open(Component(Components.TRAIL_REWARD_364)) BossKillCounter.addtoBarrowsCount(player) - for(item in rewards){ - announceIfRare(player, item); - if(!player.inventory.add(item)){ - GroundItemManager.create(item,player) + for (item in rewards) { + announceIfRare(player, item) + if (!player.inventory.add(item)) { + GroundItemManager.create(item, player) } } } diff --git a/Server/src/main/content/minigame/barrows/TunnelEntranceDialogue.java b/Server/src/main/content/minigame/barrows/TunnelEntranceDialogue.java index 6c649e728..22331c47c 100644 --- a/Server/src/main/content/minigame/barrows/TunnelEntranceDialogue.java +++ b/Server/src/main/content/minigame/barrows/TunnelEntranceDialogue.java @@ -7,6 +7,8 @@ import core.game.world.map.Location; import core.plugin.PluginManifest; import core.plugin.PluginType; +import static core.api.ContentAPIKt.*; + /** * The tunnel entrance dialogue handling plugin. * @author Emperor @@ -57,7 +59,7 @@ public final class TunnelEntranceDialogue extends DialoguePlugin { case 1: int offsetX = 0; int offsetY = 0; - int configValue = player.getConfigManager().get(452); + int configValue = getVarp(player, 452); if ((configValue & (1 << 7)) != 0) { offsetX = 34; offsetY = 34; diff --git a/Server/src/main/content/minigame/blastfurnace/BFBeltOre.kt b/Server/src/main/content/minigame/blastfurnace/BFBeltOre.kt new file mode 100644 index 000000000..650696030 --- /dev/null +++ b/Server/src/main/content/minigame/blastfurnace/BFBeltOre.kt @@ -0,0 +1,61 @@ +package content.minigame.blastfurnace + +import core.api.animate +import core.api.queueScript +import core.api.teleport +import core.game.interaction.QueueStrength +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 org.json.simple.JSONObject + +class BFBeltOre (val player: Player, val id: Int, val amount: Int, var location: Location, var npcInstance: NPC? = null) { + val state = BlastFurnace.getPlayerState(player) + + fun tick(): Boolean { + if (location == ORE_END_LOC && npcInstance != null) { + state.container.addOre(id, amount) + npcInstance?.clear() + npcInstance = null + return true + } + else if (npcInstance != null) { + location = location.transform(Direction.SOUTH, 1) + teleport(npcInstance!!, location) + + if (location == ORE_END_LOC) { + val npc = npcInstance!! + queueScript(npc, 1, QueueStrength.STRONG) {_ -> + animate(npc, ORE_DEPOSIT_ANIM) + return@queueScript true + } + } + } + return false + } + + fun createNpc() { + if (npcInstance != null) return + val npcId = BlastFurnace.getNpcForOre(id) + val npc = NPC.create(npcId, location) + npc.isWalks = false + npc.init() + + npcInstance = npc + } + + fun toJson(): JSONObject { + val root = JSONObject() + root["id"] = id.toString() + root["amount"] = amount.toString() + root["location"] = location.toString() + return root + } + + companion object { + val ORE_START_LOC = Location.create(1942, 4966, 0) + val ORE_END_LOC = Location.create(1942, 4963, 0) + val ORE_DEPOSIT_ANIM = 2434 + } +} \ No newline at end of file diff --git a/Server/src/main/content/minigame/blastfurnace/BFOreContainer.kt b/Server/src/main/content/minigame/blastfurnace/BFOreContainer.kt new file mode 100644 index 000000000..5ed5c2cc6 --- /dev/null +++ b/Server/src/main/content/minigame/blastfurnace/BFOreContainer.kt @@ -0,0 +1,194 @@ +package content.minigame.blastfurnace + +import content.global.skill.smithing.smelting.Bar +import content.minigame.blastfurnace.BlastConsts.BAR_LIMIT +import content.minigame.blastfurnace.BlastFurnace.Companion.getBarForOreId +import content.minigame.blastfurnace.BlastFurnace.Companion.getNeededCoal +import core.game.node.item.Item +import org.json.simple.JSONArray +import org.json.simple.JSONObject +import org.rs09.consts.Items + +class BFOreContainer { + private var coalRemaining = 0 //this is the actual important value needed for the calculations and is unit tested + private var ores = Array(BlastConsts.ORE_LIMIT * 2) {-1} + private var barAmounts = Array(9) {0} + + fun addCoal(amount: Int) : Int { + val maxAdd = BlastConsts.COAL_LIMIT - coalRemaining + val toAdd = amount.coerceAtMost(maxAdd) + coalRemaining += toAdd + return amount - toAdd + } + + fun coalAmount() : Int { + return coalRemaining + } + + fun addOre (id: Int, amount: Int): Int { + if (id == Items.COAL_453) + return addCoal(amount) + + var limit = BlastConsts.ORE_LIMIT + if (getBarForOreId(id, -1, 99) == Bar.BRONZE) + limit *= 2 + + var amountLeft = amount + var maxAdd = getAvailableSpace(id, 99) + for (i in 0 until limit) { + if (ores[i] == -1) { + ores[i] = id + if (--amountLeft == 0 || --maxAdd == 0) break + } + } + return amountLeft + } + + fun getOreAmount (id: Int) : Int { + if (id == Items.COAL_453) + return coalAmount() + + var oreCount = 0 + for (i in 0 until BlastConsts.ORE_LIMIT) { + if (ores[i] == id) oreCount++ + } + return oreCount + } + + fun indexOfOre (id: Int) : Int { + for (i in ores.indices) + if (ores[i] == id) return i + return -1 + } + + fun getOreAmounts() : HashMap { + val map = HashMap() + for (ore in ores) { + if (ore == -1) break + map[ore] = (map[ore] ?: 0) + 1 + } + return map + } + + fun convertToBars(level: Int = 99) : Double { + val newOres = Array(BlastConsts.ORE_LIMIT * 2) {-1} + var oreIndex = 0 + var xpReward = 0.0 + for (i in 0 until BlastConsts.ORE_LIMIT) { + val bar = getBarForOreId(ores[i], coalRemaining, level) + + if (bar == null) { + ores[i] = -1 + continue + } + + if (barAmounts[bar.ordinal] >= BAR_LIMIT) { + newOres[oreIndex++] = ores[i] + continue + } + + val coalNeeded = getNeededCoal(bar) + + //special handling for bronze bar edge case, no other ore does this. + if (bar == Bar.BRONZE) { + val indexOfComplement = when (ores[i]) { + Items.COPPER_ORE_436 -> indexOfOre(Items.TIN_ORE_438) + Items.TIN_ORE_438 -> indexOfOre(Items.COPPER_ORE_436) + else -> -1 + } + if (indexOfComplement == -1) { + newOres[oreIndex++] = ores[i] + continue + } + ores[indexOfComplement] = -1 + } + + if (coalRemaining >= coalNeeded) { + coalRemaining -= coalNeeded + ores[i] = -1 + barAmounts[bar.ordinal]++ + xpReward += bar.experience + } else { + newOres[oreIndex++] = ores[i] + } + } + ores = newOres + return xpReward + } + + fun getBarAmount (bar: Bar) : Int { + return barAmounts[bar.ordinal] + } + + fun getTotalBarAmount() : Int { + var total = 0 + for (i in barAmounts) total += i + return total + } + + fun takeBars (bar: Bar, amount: Int) : Item? { + val amt = amount.coerceAtMost(barAmounts[bar.ordinal]) + if (amt == 0) return null + + barAmounts[bar.ordinal] -= amt + return Item(bar.product.id, amt) + } + + fun getAvailableSpace (ore: Int, level: Int = 99) : Int { + if (ore == Items.COAL_453) + return BlastConsts.COAL_LIMIT - coalRemaining + + var freeSlots = 0 + val bar = getBarForOreId(ore, coalRemaining, level)!! + val oreAmounts = HashMap() + for (i in 0 until BlastConsts.ORE_LIMIT) + if (ores[i] == -1) { + var oreLimit = BlastConsts.ORE_LIMIT + if (bar == Bar.BRONZE) oreLimit *= 2 + freeSlots = oreLimit - i + break + } + else oreAmounts[ores[i]] = (oreAmounts[ores[i]] ?: 0) + 1 + + val currentAmount = oreAmounts[ore] ?: 0 + freeSlots = (BlastConsts.ORE_LIMIT - currentAmount).coerceAtMost(freeSlots) + + return (freeSlots - getBarAmount(bar)).coerceAtLeast(0) + } + + fun hasAnyOre() : Boolean { + return ores[0] != -1 + } + + fun toJson() : JSONObject { + val root = JSONObject() + val ores = JSONArray() + val bars = JSONArray() + + for (ore in this.ores) + ores.add(ore.toString()) + for (amount in barAmounts) + bars.add(amount.toString()) + + root["ores"] = ores + root["bars"] = bars + root["coal"] = coalRemaining.toString() + return root + } + + companion object { + fun fromJson (root: JSONObject) : BFOreContainer { + val cont = BFOreContainer() + val jsonOres = root["ores"] as? JSONArray ?: return cont + val jsonBars = root["bars"] as? JSONArray ?: return cont + + + for (i in 0 until BlastConsts.ORE_LIMIT) + cont.ores[i] = jsonOres[i].toString().toIntOrNull() ?: -1 + for (i in 0 until 9) + cont.barAmounts[i] = jsonBars[i].toString().toIntOrNull() ?: 0 + cont.coalRemaining = root["coal"].toString().toIntOrNull() ?: 0 + return cont + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/minigame/blastfurnace/BFPlayerState.kt b/Server/src/main/content/minigame/blastfurnace/BFPlayerState.kt new file mode 100644 index 000000000..a7f44f336 --- /dev/null +++ b/Server/src/main/content/minigame/blastfurnace/BFPlayerState.kt @@ -0,0 +1,156 @@ +package content.minigame.blastfurnace + +import content.global.skill.smithing.smelting.Bar +import core.api.* +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.game.world.map.Location +import org.json.simple.JSONArray +import org.json.simple.JSONObject + +class BFPlayerState (val player: Player) { + var container = BFOreContainer() + val oresOnBelt = ArrayList() + var barsNeedCooled = false + private set + + fun processOresIntoBars() : Boolean { + if (barsNeedCooled && getVarbit(player, DISPENSER_STATE) == 1) { + setVarbit(player, DISPENSER_STATE, 2, true) + return false + } + if (getVarbit(player, DISPENSER_STATE) != 0 || !container.hasAnyOre()) + return false + + val xpReward = container.convertToBars(getStatLevel(player, Skills.SMITHING)) + if (xpReward > 0) { + rewardXP(player, Skills.SMITHING, xpReward) + setVarbit(player, DISPENSER_STATE, 1, true) + barsNeedCooled = true + return true + } + + return false + } + + fun updateOres() { + val toRemove = ArrayList() + for (ore in oresOnBelt) { + if (ore.tick()) + toRemove.add(ore) + } + oresOnBelt.removeAll(toRemove) + } + + fun coolBars() { + barsNeedCooled = false + setVarbit(player, DISPENSER_STATE, 3, true) + } + + fun checkBars() { + if (getVarbit(player, DISPENSER_STATE) == 3) + setVarbit(player, DISPENSER_STATE, 0, true) + } + + fun hasBarsClaimable(): Boolean { + return container.getTotalBarAmount() > 0 + } + + fun claimBars (bar: Bar, amount: Int) : Boolean { + if (barsNeedCooled) return false + + val maxAmt = amount.coerceAtMost(freeSlots(player)) + if (maxAmt == 0) return false + + val reward = container.takeBars(bar, maxAmt) ?: return false + addItem(player, reward.id, reward.amount) + setBarClaimVarbits() + return true + } + + fun setBarClaimVarbits() { + for (bar in Bar.values()) { + val amount = container.getBarAmount(bar) + val varbit = getVarbitForBar(bar) + if (varbit == 0) continue + + if (getVarbit(player, varbit) == amount) + continue + + setVarbit(player, varbit, amount, true) + } + + var totalCoalNeeded = 0 + val level = getStatLevel(player, Skills.SMITHING) + for ((id, amount) in container.getOreAmounts()) { + val barType = BlastFurnace.getBarForOreId(id, container.coalAmount(), level) + totalCoalNeeded += BlastFurnace.getNeededCoal(barType!!) * amount + } + + setVarbit(player, COAL_NEEDED, (totalCoalNeeded - container.coalAmount()).coerceAtLeast(0)) + } + + private fun getVarbitForBar (bar: Bar) : Int { + return when (bar) { + Bar.BRONZE -> BRONZE_COUNT + Bar.IRON -> IRON_COUNT + Bar.STEEL -> STEEL_COUNT + Bar.MITHRIL -> MITHRIL_COUNT + Bar.ADAMANT -> ADDY_COUNT + Bar.RUNITE -> RUNITE_COUNT + Bar.SILVER -> SILVER_COUNT + Bar.GOLD -> GOLD_COUNT + else -> 0 + } + } + + fun toJson() : JSONObject { + val save = JSONObject() + save["bf-ore-cont"] = container.toJson() + + val beltOres = JSONArray() + for (ore in oresOnBelt) { + beltOres.add(ore.toJson()) + } + + if (beltOres.isNotEmpty()) + save["bf-belt-ores"] = beltOres + save["barsHot"] = barsNeedCooled + + return save + } + + fun readJson (data: JSONObject) { + oresOnBelt.clear() + if (data.containsKey("bf-ore-cont")) { + val contJson = data["bf-ore-cont"] as JSONObject + container = BFOreContainer.fromJson(contJson) + } + if (data.containsKey("bf-belt-ores")) { + val beltArray = data["bf-belt-ores"] as JSONArray + for (oreObj in beltArray) { + val oreInfo = oreObj as JSONObject + val id = oreInfo["id"].toString().toInt() + val amount = oreInfo["amount"].toString().toInt() + val location = Location.fromString(oreInfo["location"].toString()) + val ore = BFBeltOre(player, id, amount, location) + oresOnBelt.add(ore) + } + } + if (data.containsKey("barsHot")) + barsNeedCooled = data["barsHot"] as Boolean + } + + companion object { + val DISPENSER_STATE = 936 + val COAL_NEEDED = 940 + val BRONZE_COUNT = 941 + val IRON_COUNT = 942 + val STEEL_COUNT = 943 + val MITHRIL_COUNT = 944 + val ADDY_COUNT = 945 + val RUNITE_COUNT = 946 + val GOLD_COUNT = 947 + val SILVER_COUNT = 948 + } +} \ No newline at end of file diff --git a/Server/src/main/content/minigame/blastfurnace/BFSceneryController.kt b/Server/src/main/content/minigame/blastfurnace/BFSceneryController.kt new file mode 100644 index 000000000..d2d3e1606 --- /dev/null +++ b/Server/src/main/content/minigame/blastfurnace/BFSceneryController.kt @@ -0,0 +1,110 @@ +package content.minigame.blastfurnace + +import core.api.animateScenery +import core.api.getScenery +import core.api.replaceScenery +import core.game.world.map.Location + +class BFSceneryController { + fun updateBreakable (potPipeBroken: Boolean, pumpPipeBroken: Boolean, beltBroken: Boolean, cogBroken: Boolean) { + val beltObj = getScenery(beltGearRight)!! + val gearObj = getScenery(cogRightLoc)!! + val potPipe = getScenery(potPipeLoc)!! + val pumpPipe = getScenery(pumpPipeLoc)!! + + if (potPipeBroken && potPipe.id != BROKEN_POT_PIPE) + replaceScenery(potPipe, BROKEN_POT_PIPE, -1) + else if (!potPipeBroken && potPipe.id == BROKEN_POT_PIPE) + replaceScenery(potPipe, DEFAULT_POT_PIPE, -1) + + if (pumpPipeBroken && pumpPipe.id != BROKEN_PUMP_PIPE) + replaceScenery(pumpPipe, BROKEN_PUMP_PIPE, -1) + else if (!pumpPipeBroken && pumpPipe.id == BROKEN_PUMP_PIPE) + replaceScenery(pumpPipe, DEFAULT_PUMP_PIPE, -1) + + if (beltBroken && beltObj.id != BROKEN_BELT) + replaceScenery(beltObj, BROKEN_BELT, -1) + else if (!beltBroken && beltObj.id == BROKEN_BELT) + replaceScenery(beltObj, DEFAULT_BELT, -1) + + if (cogBroken && gearObj.id != BROKEN_COG) + replaceScenery(gearObj, BROKEN_COG, -1) + else if (!cogBroken && gearObj.id == BROKEN_COG) + replaceScenery(gearObj, DEFAULT_COG, -1) + } + + fun updateAnimations (pedaling: Boolean, beltBroken: Boolean, cogBroken: Boolean) { + val belt1 = getScenery(belt1Loc)!! + val belt2 = getScenery(belt2Loc)!! + val belt3 = getScenery(belt3Loc)!! + val beltGearLeft = getScenery(beltGearLeft)!! + val beltGearRight = getScenery(beltGearRight)!! + val cogLeft = getScenery(cogLeftLoc)!! + val cogRight = getScenery(cogRightLoc)!! + val cogCenter = getScenery(centralGearLoc)!! + + val beltAnim = if (pedaling && !beltBroken && !cogBroken) BELT_ANIM else -1 + val gearAnim = if (pedaling && !beltBroken && !cogBroken) GEAR_ANIM else -1 + + animateScenery(belt1, beltAnim) + animateScenery(belt2, beltAnim) + animateScenery(belt3, beltAnim) + animateScenery(beltGearLeft, gearAnim) + animateScenery(beltGearRight, gearAnim) + animateScenery(cogLeft, gearAnim) + animateScenery(cogRight, gearAnim) + animateScenery(cogCenter, gearAnim) + } + + fun updateStove (temp: Int) { + val stoveObj = getScenery(stoveLoc)!! + + if (temp >= 67 && stoveObj.id != STOVE_HOT) + replaceScenery(stoveObj, STOVE_HOT, -1) + else if (temp in 34..66 && stoveObj.id != STOVE_WARM) + replaceScenery(stoveObj, STOVE_WARM, -1) + else if (temp in 0..33 && stoveObj.id != STOVE_COLD) + replaceScenery(stoveObj, STOVE_COLD, -1) + } + + fun resetAllScenery() { + val beltObj = getScenery(beltGearRight)!! + val gearObj = getScenery(cogRightLoc)!! + val potPipe = getScenery(potPipeLoc)!! + val pumpPipe = getScenery(pumpPipeLoc)!! + val stoveObj = getScenery(stoveLoc)!! + replaceScenery(gearObj, DEFAULT_COG, -1) + replaceScenery(beltObj, DEFAULT_BELT, -1) + replaceScenery(pumpPipe, DEFAULT_PUMP_PIPE, -1) + replaceScenery(potPipe, DEFAULT_POT_PIPE, -1) + replaceScenery(stoveObj, STOVE_COLD, -1) + } + + companion object { + val belt1Loc = Location(1943, 4967, 0) + val belt2Loc = Location(1943, 4966, 0) + val belt3Loc = Location(1943, 4965, 0) + var potPipeLoc = Location(1943, 4961, 0) + var pumpPipeLoc = Location(1947, 4961, 0) + var cogLeftLoc = Location(1945, 4965, 0) + var cogRightLoc = Location(1945, 4967, 0) + var beltGearLeft = Location(1944, 4965, 0) + var beltGearRight = Location(1944, 4967, 0) + var centralGearLoc = Location(1945, 4966, 0) + var stoveLoc = Location(1948, 4963, 0) + + val DEFAULT_BELT = 9102 + val BROKEN_BELT = 9103 + val DEFAULT_COG = 9104 + val BROKEN_COG = 9105 + val DEFAULT_POT_PIPE = 9116 + val BROKEN_POT_PIPE = 9117 + val DEFAULT_PUMP_PIPE = 9120 + val BROKEN_PUMP_PIPE = 9121 + val STOVE_COLD = 9085 + val STOVE_WARM = 9086 + val STOVE_HOT = 9087 + val BELT_ANIM = 2435 + val GEAR_ANIM = 2436 + } +} \ No newline at end of file diff --git a/Server/src/main/content/minigame/blastfurnace/BFTempEntranceTimer.kt b/Server/src/main/content/minigame/blastfurnace/BFTempEntranceTimer.kt new file mode 100644 index 000000000..733c4162a --- /dev/null +++ b/Server/src/main/content/minigame/blastfurnace/BFTempEntranceTimer.kt @@ -0,0 +1,19 @@ +package content.minigame.blastfurnace + +import core.api.resetAnimator +import core.api.teleport +import core.game.node.entity.Entity +import core.game.node.entity.player.Player +import core.game.system.timer.PersistTimer + +class BFTempEntranceTimer : PersistTimer(BlastConsts.FEE_ENTRANCE_DURATION, "bf-tempentrance") { + override fun run(entity: Entity): Boolean { + if (entity !is Player) return false + if (BlastFurnace.insideBorders(entity)) { + teleport(entity, BlastConsts.EXIT_LOC) + entity.pulseManager.clear() + resetAnimator(entity) + } + return false + } +} \ No newline at end of file diff --git a/Server/src/main/content/minigame/blastfurnace/BlastConsts.kt b/Server/src/main/content/minigame/blastfurnace/BlastConsts.kt new file mode 100644 index 000000000..a2d098254 --- /dev/null +++ b/Server/src/main/content/minigame/blastfurnace/BlastConsts.kt @@ -0,0 +1,28 @@ +package content.minigame.blastfurnace + +import core.game.world.map.Location + +object BlastConsts { + val ENTRANCE_LOC = Location (1940, 4958, 0) + val EXIT_LOC = Location (2931, 10197, 0) + val STAIRLOC_ENTRANCE = Location (2930, 10196) + val STAIRLOC_EXIT = Location (1939, 4956) + val STAIR_ENTRANCE_ID = 9084 + val STAIR_EXIT_ID = 9138 + val BELT = 9100 + val PEDALS = 9097 + val STOVE = intArrayOf(BFSceneryController.STOVE_COLD, BFSceneryController.STOVE_WARM, BFSceneryController.STOVE_HOT) + val PUMP = 9090 + val COKE = 9088 + val TEMP_GAUGE = 9089 + val SINK = 9143 + val DISPENSER = intArrayOf(9093, 9094, 9095, 9096) + + val SMITH_REQ = 60 + val ENTRANCE_FEE = 2500 + val FEE_ENTRANCE_DURATION = 1000 + val COAL_LIMIT = 226 + val ORE_LIMIT = 28 + val BAR_LIMIT = 28 + val COKE_LIMIT = 15 +} \ No newline at end of file diff --git a/Server/src/main/content/minigame/blastfurnace/BlastFurnace.kt b/Server/src/main/content/minigame/blastfurnace/BlastFurnace.kt index bd2344dbc..7c8386596 100644 --- a/Server/src/main/content/minigame/blastfurnace/BlastFurnace.kt +++ b/Server/src/main/content/minigame/blastfurnace/BlastFurnace.kt @@ -1,360 +1,284 @@ package content.minigame.blastfurnace +import content.global.skill.smithing.smelting.Bar import core.api.* -import core.game.container.impl.EquipmentContainer +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 content.global.skill.smithing.smelting.Bar -import core.game.system.task.Pulse -import core.game.world.map.Location +import core.game.world.map.zone.ZoneBorders import core.tools.RandomFunction +import org.json.simple.JSONObject import org.rs09.consts.Items -import org.rs09.consts.Items.GOLDSMITH_GAUNTLETS_776 +import org.rs09.consts.NPCs +import kotlin.math.max -/**le Blast Furnace has arrived: - * Handles most of the Blast Furnace's operating logic, there is some crosstalk between - * this file and BlastFurnaceListeners.kt as well as the BlastFurnaceZone.kt. - * This also handles a lot of the varbit fuckery for the furnace. The other - * varbit fuckery is handled in BlastFurnaceListeners. - * Varbits are still slightly busted but not in a way that breaks the operation or usage of - * Blast Furnace, just visual and I've had enough of the funny binary numbers to say fuck it - * and will just come back to it at a later date. - * @author phil lips*/ +class BlastFurnace : MapArea, PersistPlayer, TickListener { + override fun defineAreaBorders(): Array { + return arrayOf(bfArea) + } -object BlastFurnace { - - val belt1 = getScenery(1943, 4967, 0) - val belt2 = getScenery(1943, 4966, 0) - val belt3 = getScenery(1943, 4965, 0) - val disLoc = getScenery(1941, 4963, 0) - var pipe1 = getScenery(1943, 4961, 0) - var pipe2 = getScenery(1947, 4961, 0) - var cogs1 = getScenery(1945, 4965, 0) - var cogs2 = getScenery(1945, 4967, 0) - var beltLoc1 = getScenery(1944, 4965, 0) - var beltLoc2 = getScenery(1944, 4967, 0) - var gearsLoc = getScenery(1945, 4966, 0) - var stoveLoc = getScenery(1948, 4963, 0) - - val mPot = 9098 - - var gaugeViewList = ArrayList() - var blastFurnacePlayerList = ArrayList() - var furnaceTemp = 0 - var pumpPipeBroken = false - var potPipeBroken = false - var stoveCoke = 0 - var stoveTemp = 0 - var beltRunning = false - var makeBars = false - var beltBroken = false - var cogBroken = false - var pumping = false - var pedaling = false - var barsHot = false - var giveSmithXp = 0 - - /**This pulse object handles checks between*/ - var blastPulse = object : Pulse() { - override fun pulse(): Boolean { - gaugeViewList.forEach { - var anim = furnaceTemp + 2452 - animateInterface(it, 30, 4, anim) - } - blastFurnacePlayerList.forEach { - if(it.getAttribute("BlastTimer",0) > 0 && it.getSkills().getLevel(Skills.SMITHING) < 60){ - it.incrementAttribute("BlastTimer",-1) - } else if(it.getAttribute("BlastTimer",0) <= 0 && it.getSkills().getLevel(Skills.SMITHING) < 60){ - it.removeAttribute("BlastTimer") - sendDialogue(it,"Your time in the Blast Furnace has run out!") - it.properties.teleportLocation = Location.create(2931, 10197, 0) - } else if(it.getAttribute("BlastTimer",false) && it.getSkills().getLevel(Skills.SMITHING) >= 60){ - it.removeAttribute("BlastTimer") - } - if(giveSmithXp > 0){ - rewardXP(it,Skills.SMITHING,1.0) - giveSmithXp-- - } - } - interfaceManager() - runConveyor() - stoveCokeTemperature() - furnaceTemperature() - operateFurnace() - breakStuff() - fixStuff() - return false + override fun savePlayer(player: Player, save: JSONObject) { + val state = playerStates[player.details.uid] + if (state != null) { + save["bf-state"] = state.toJson() } } - /**This handles the coke stove temperature, if there is coke in the - * coke oven the temperature will raise, otherwise the temperature - * will lower, 0-33 is cold, 34-66 is warm, 67-100 is hot*/ - fun stoveCokeTemperature() { - if (getWorldTicks() % 10 == 0 && stoveCoke > 0) { - stoveCoke-- + override fun parsePlayer(player: Player, data: JSONObject) { + playerStates.remove(player.details.uid) + if (data.containsKey("bf-state")) { + val stateObj = data["bf-state"] as JSONObject + getPlayerState(player).readJson(stateObj) } - if (stoveCoke > 0) { - if (stoveTemp < 100) { - stoveTemp += 1 + } + + override fun areaEnter(entity: Entity) { + if (entity is Player) { + playersInArea.add(entity) + val state = getPlayerState(entity) + for (ore in state.oresOnBelt) + ore.createNpc() + } + } + + override fun areaLeave(entity: Entity, logout: Boolean) { + if (entity is Player) { + playersInArea.remove(entity) + val state = getPlayerState(entity) + for (ore in state.oresOnBelt) { + ore.npcInstance?.clear() + ore.npcInstance = null } - } else if (stoveTemp > 0) { - stoveTemp-- - } - if (stoveTemp > 66 && stoveLoc!!.id != 9087) { - stoveLoc = getScenery(1948, 4963, 0) - replaceScenery(stoveLoc!!, 9087, -1) - } else if (stoveTemp in 34..66 && stoveLoc!!.id != 9086) { - stoveLoc = getScenery(1948, 4963, 0) - replaceScenery(stoveLoc!!, 9086, -1) - } else if (stoveTemp < 32 && stoveLoc!!.id != 9085) { - stoveLoc = getScenery(1948, 4963, 0) - replaceScenery(stoveLoc!!, 9085, -1) } } - /**This handles the raising and lowering of the furnace temperature - * depending on whether or not the coke stove is hot and if the - * furnace pump is being operated. A hot coke stove will raise the - * temperature the fastest, a warm coke stove will raise it slowly, - * and a cold stove will raise it the slowest*/ - fun furnaceTemperature() { - if (stoveTemp >= 67 && pumping && furnaceTemp < 100 && (!pumpPipeBroken || !pumpPipeBroken)) { - furnaceTemp += 3 - } else if (stoveTemp >= 34 && pumping && furnaceTemp < 100 && (!pumpPipeBroken || !pumpPipeBroken)) { - furnaceTemp += 2 - } else if (stoveTemp > 0 && pumping && furnaceTemp < 100 && (!pumpPipeBroken || !pumpPipeBroken)) { - furnaceTemp += 1 - } else if (furnaceTemp > 0) { - furnaceTemp -= 1 + override fun tick() { + if (state.beltBroken || state.cogBroken) pedaler = null + if (state.potPipeBroken || state.pumpPipeBroken) pumper = null + state.tick(pumper != null, pedaler != null) + + if (playersInArea.size > 0) { + updateVisuals() + if (getWorldTicks() % 2 == 0) { + updatePedaler() + updatePumper() + } + processBars() + } + + //Reset each tick + pumper = null + pedaler = null + } + + private fun updatePumper() { + if (pumper != null) { + if (state.stoveTemp == 0) return + if (state.furnaceTemp == 100 && RandomFunction.roll(5)) { + impact(pumper!!, (0.2 * pumper!!.skills.maximumLifepoints).toInt()) + sendMessage(pumper!!, "A blast of hot air cooks you a bit.") + pumper!!.pulseManager.clear() + return + } + rewardXP(pumper!!, Skills.STRENGTH, 4.0) } } - /**Tells the furnace whether or not to smelt bars depending on if - * the furnace temperature is in the right range. Also contains the - * logic for smelting the players ore given the fact that they actually - * have ore in their ore container and/or have coal in their coal container*/ - fun operateFurnace() { - makeBars = furnaceTemp in 51..66 - blastFurnacePlayerList.forEach { player -> - var playerCoal = player.blastCoal.getAmount(Items.COAL_453) - var playerOre = player.blastOre.toArray().filterNotNull() - var barsAmountFree = player.blastBars.freeSlots() - var barsAmount = player.blastBars.itemCount() - var oreAmount = player.blastOre.itemCount() - var totalAmount = barsAmount + oreAmount - if (barsHot) { - player.varpManager.get(543).setVarbit(8, 2).send(player) - }else if (makeBars && playerOre.isNotEmpty() && barsAmountFree > 0 && totalAmount < 56 && player.getAttribute("OreInPot",false) == true) { - playerOre.forEach { oreID -> - playerCoal = player.blastCoal.getAmount(Items.COAL_453) - var bars = arrayOf(Bar.forOre(oreID.id)!!) - if(oreID.id == Items.IRON_ORE_440) { - bars = arrayOf(Bar.STEEL, Bar.IRON) - } - inner@ for(bar in bars) { - var hasRequirements = true - for(required in bar.ores) { - if(required.id == Items.COAL_453) { - if(!player.blastCoal.contains(Items.COAL_453, required.amount / 2)) { - hasRequirements = false - } - } else if(!player.blastOre.containsItem(required)) { - hasRequirements = false - } - } - if(hasRequirements) { - var removed = true - for(required in bar.ores) { - if(required.id == Items.COAL_453) { - if(!player.blastCoal.remove(Item(Items.COAL_453, required.amount / 2))) { - removed = false - } - } else { - if(!player.blastOre.remove(required)) { - removed = false - } - } - } - if(removed) { - player.varpManager.get(543).setVarbit(8, 1).send(player) - barsHot = true - player.blastBars.add(bar.product) - var experience = bar.experience - if(bar.product.id == Items.GOLD_BAR_2357 && - player.equipment[EquipmentContainer.SLOT_HANDS] != null && - player.equipment[EquipmentContainer.SLOT_HANDS].id == GOLDSMITH_GAUNTLETS_776) { - experience *= 2.5; - } - rewardXP(player, Skills.SMITHING, experience) - totalAmount = barsAmount + oreAmount - giveSmithXp++ - break@inner - } - } + private fun updatePedaler() { + if (pedaler == null) return + var oresPedaled = false + for (state in playerStates.values) { + if (state.oresOnBelt.isEmpty()) continue + state.updateOres() + oresPedaled = true + } + if (oresPedaled) { + rewardXP(pedaler!!, Skills.AGILITY, 2.0) + pedaler!!.settings.runEnergy -= 2 + } + } + + private fun updateVisuals() { + sceneryController.updateStove(state.stoveTemp) + sceneryController.updateBreakable( + state.potPipeBroken, + state.pumpPipeBroken, + state.beltBroken, + state.cogBroken + ) + sceneryController.updateAnimations(pedaler != null, state.beltBroken, state.cogBroken) + } + + private fun processBars() { + if (state.furnaceTemp !in 51..66) return + var totalProcessed = 0 + for (player in playersInArea) { + if (getPlayerState(player).processOresIntoBars()) + totalProcessed++ + } + if (totalProcessed == 0) return + for (player in playersInArea) + rewardXP(player, Skills.SMITHING, totalProcessed.toDouble()) + } + + companion object { + val bfArea = ZoneBorders (1934, 4955, 1958, 4975) + val playersInArea = ArrayList() + val playerStates = HashMap() + val state = BlastState() + val sceneryController = BFSceneryController() + var pedaler: Player? = null + var pumper: Player? = null + + + fun insideBorders (player: Player) : Boolean { + return bfArea.insideBorder(player.location) + } + + fun placeAllOre (p: Player, id: Int = -1, accountForSkill: Boolean = false) { + val oreCounts = HashMap() + val oreContainer = getOreContainer(p) + val level = if (accountForSkill) getStatLevel(p, Skills.SMITHING) else 99 + + for (item in p.inventory.toArray()) { + if (item == null) continue + if (getNpcForOre(item.id) == -1) continue + if (id != -1 && item.id != id) continue + + val bar = getBarForOreId(item.id, oreContainer.coalAmount(), level)!! + if (bar.level > level) continue + + oreCounts[item.id] = (oreCounts[item.id] ?: 0) + item.amount + } + + for ((oreId, amount) in oreCounts) { + var maxAmt = oreContainer.getAvailableSpace(oreId, level) + + if (oreId == Items.COPPER_ORE_436 || oreId == Items.TIN_ORE_438) + maxAmt += (BlastConsts.ORE_LIMIT - getAmountOnBelt(p, oreId)) + + if (oreId == Items.COAL_453) + maxAmt -= getAmountOnBelt(p, oreId) + else + maxAmt -= getTotalOreOnBelt(p) + + maxAmt = maxAmt.coerceAtMost(amount).coerceAtLeast(0) + if (maxAmt == 0) continue + + if (removeItem(p, Item(oreId, maxAmt))) { + sendMessage(p, "You place ${maxAmt} ${Item(oreId).getName()} on the conveyor belt.") + addCoalSecondary(p, oreId, maxAmt) //The belt renders the most recent ore, so add the coal first + addOreToBelt(p, oreId, maxAmt) } + } + } + + //Snowscape feature: coal stored in the gold satchel is automatically added to the belt with ores that require coal. + fun addCoalSecondary (p: Player, oreId: Int, amount: Int) { + if (!inEquipmentOrInventory(p, Items.GOLD_SATCHEL_10881)) return + + var coalAmount = 0 + when(oreId) { + Items.IRON_ORE_440 -> coalAmount = amount + Items.MITHRIL_ORE_447 -> coalAmount = amount * 2 + Items.ADAMANTITE_ORE_449 -> coalAmount = amount * 3 + Items.RUNITE_ORE_451 -> coalAmount = amount * 4 + else -> return + } + + coalAmount = coalAmount.coerceAtMost(getOreContainer(p).getAvailableSpace(Items.COAL_453) - getAmountOnBelt(p, Items.COAL_453)) + + if (coalAmount > 0 && p.goldSatchel.remove(Item(Items.COAL_453, coalAmount))){ + addOreToBelt(p, Items.COAL_453, coalAmount) + sendMessage(p, "You take ${coalAmount} Coal from your gold satchel and place it on the belt.") + } + } + + fun getPlayerState (p: Player) : BFPlayerState { + if (playerStates[p.details.uid] != null) + return playerStates[p.details.uid]!! + val state = BFPlayerState(p) + playerStates[p.details.uid] = state + return state + } + + fun getOreContainer (p: Player) : BFOreContainer { + return getPlayerState(p).container + } + + fun addOreToBelt (p: Player, id: Int, amount: Int) : BFBeltOre { + val beltOre = BFBeltOre(p, id, amount, BFBeltOre.ORE_START_LOC) + beltOre.createNpc() + getPlayerState(p).oresOnBelt.add(beltOre) + return beltOre + } + + fun getAmountOnBelt (p: Player, id: Int) : Int { + var total = 0 + for (ore in getPlayerState(p).oresOnBelt) { + if (ore.id == id) + total += ore.amount + } + return total + } + + fun getTotalOreOnBelt (p: Player) : Int { + var total = 0 + for (ore in getPlayerState(p).oresOnBelt) + if (ore.id != Items.COAL_453) total += ore.amount + return total + } + + fun getNeededCoal (bar: Bar) : Int { + var coalAmount = 0 + + if (bar.ores.size == 1) + return coalAmount + + for (ore in bar.ores) { + if (ore.id == Items.COAL_453) { + coalAmount = ore.amount + break } } - if(!barsHot && playerOre.isNotEmpty()) { - var coalToAdd = 0 - playerOre.forEach { oreID -> - when(oreID.id){ - Items.RUNITE_ORE_451 -> coalToAdd = (player.blastOre.getAmount(451) * 4) - playerCoal - Items.ADAMANTITE_ORE_449 -> coalToAdd = (player.blastOre.getAmount(449) * 3) - playerCoal - Items.MITHRIL_ORE_447 -> coalToAdd = (player.blastOre.getAmount(447) * 2) - playerCoal - Items.IRON_ORE_440 -> coalToAdd = player.blastOre.getAmount(440) - playerCoal - } - } - if (coalToAdd < 0){ - player.varpManager.get(543).setVarbit(12, 0).send(player) - } else player.varpManager.get(543).setVarbit(12, coalToAdd).send(player) + if (coalAmount > 1) coalAmount /= 2 + return coalAmount + } + + + fun getBarForOreId (id: Int, coalAmount: Int, level: Int) : Bar? { + return when (id) { + Items.COPPER_ORE_436, Items.TIN_ORE_438 -> Bar.BRONZE + Items.IRON_ORE_440 -> if (coalAmount >= 1 && level >= Bar.STEEL.level) Bar.STEEL else Bar.IRON + else -> Bar.forOre(id) } - else if(playerOre.isEmpty()) { - player.varpManager.get(543).setVarbit(12,0).send(player) - } - if(playerOre.isEmpty() && player.getAttribute("OreInPot",false)){ - player.removeAttribute("OreInPot") + } + + fun getNpcForOre (id: Int) : Int { + return when (id) { + Items.IRON_ORE_440 -> NPCs.IRON_ORE_2556 + Items.COPPER_ORE_436 -> NPCs.COPPER_ORE_2555 + Items.TIN_ORE_438 -> NPCs.TIN_ORE_2554 + Items.COAL_453 -> NPCs.COAL_2562 + Items.MITHRIL_ORE_447 -> NPCs.MITHRIL_ORE_2557 + Items.ADAMANTITE_ORE_449 -> NPCs.ADAMANTITE_ORE_2558 + Items.SILVER_ORE_442 -> NPCs.SILVER_ORE_2560 + Items.GOLD_ORE_444 -> NPCs.GOLD_ORE_2561 + Items.RUNITE_ORE_451 -> NPCs.RUNITE_ORE_2559 + else -> -1 } } + + fun getEntranceFee (hasCharos: Boolean, smithLevel: Int) : Int { + if (smithLevel >= BlastConsts.SMITH_REQ) return 0 + return if (hasCharos) BlastConsts.ENTRANCE_FEE / 2 else BlastConsts.ENTRANCE_FEE + } + + fun enter (player: Player, feePaid: Boolean) { + if (feePaid && !hasTimerActive(player)) + registerTimer(player, BFTempEntranceTimer()) + teleport(player, BlastConsts.ENTRANCE_LOC) + } } - - /**This handles the conveyor belt, if the pedals are running and the belt - * isn't broken then the conveyor belt will run*/ - fun runConveyor() { - if (pedaling && (!beltBroken && !cogBroken)) { - animateScenery(belt1!!, 2435) - animateScenery(belt2!!, 2435) - animateScenery(belt3!!, 2435) - animateScenery(beltLoc1!!, 2436) - animateScenery(beltLoc2!!, 2436) - animateScenery(cogs1!!, 2436) - animateScenery(cogs2!!, 2436) - animateScenery(gearsLoc!!, 2436) - beltRunning = true - } else { - animateScenery(belt1!!, -1) - animateScenery(belt2!!, -1) - animateScenery(belt3!!, -1) - animateScenery(beltLoc1!!, -1) - animateScenery(beltLoc2!!, -1) - animateScenery(cogs1!!, -1) - animateScenery(cogs2!!, -1) - animateScenery(gearsLoc!!, -1) - beltRunning = false - } - - } - - /**Its just one of those days*/ - fun breakStuff() { - if (pumping && furnaceTemp > 76) { - if (RandomFunction.random(1, 100) > 20 && (!potPipeBroken || !pumpPipeBroken)) { - if (RandomFunction.random(1, 100) > 50 && !potPipeBroken) { - pipe1 = getScenery(1943, 4961, 0) - potPipeBroken = true - } - if (RandomFunction.random(1, 100) <= 50 && !pumpPipeBroken) { - pipe2 = getScenery(1947, 4961, 0) - pumpPipeBroken = true - } - } - } - if (beltRunning && (!beltBroken || !cogBroken)) { - if (RandomFunction.random(1, 100) <= 2) { - beltLoc2 = getScenery(1944, 4967, 0) - beltBroken = true - } else if (RandomFunction.random(1, 100) <= 2) { - cogs2 = getScenery(1945, 4967, 0) - cogBroken = true - } - } - if (beltBroken && beltLoc2!!.id != 9103) { - beltLoc2 = getScenery(1944, 4967, 0) - replaceScenery(beltLoc2!!, 9103, -1) - } - if (cogBroken && cogs2!!.id != 9105) { - cogs2 = getScenery(1945, 4967, 0) - replaceScenery(cogs2!!, 9105, -1) - } - if (potPipeBroken && pipe1!!.id != 9117) { - pipe1 = getScenery(1943, 4961, 0) - replaceScenery(pipe1!!, 9117, -1) - } - if (pumpPipeBroken && pipe2!!.id != 9121) { - pipe2 = getScenery(1947, 4961, 0) - replaceScenery(pipe2!!, 9121, -1) - } - } - - /**This replaces the breakable objects IDs back to their unbroken state*/ - fun fixStuff() { - if (!potPipeBroken && pipe1!!.id != 9116) { - pipe1 = getScenery(1943, 4961, 0) - replaceScenery(pipe1!!, 9116, -1) - } - if (!pumpPipeBroken && pipe2!!.id != 9120) { - pipe2 = getScenery(1947, 4961, 0) - replaceScenery(pipe2!!, 9120, -1) - } - if (!beltBroken && beltLoc2!!.id != 9102) { - beltLoc2 = getScenery(1944, 4967, 0) - replaceScenery(beltLoc2!!, 9102, -1) - } - if (!cogBroken && cogs2!!.id != 9104) { - cogs2 = getScenery(1945, 4967, 0) - replaceScenery(cogs2!!, 9104, -1) - } - } - - /**Hi kids! - * Do you like Varbits? - * Wanna see me stick Nine Binary Bytes, through each one of my eyelids? - * Wanna look at how many bars you have left inside the furnace???*/ - - fun interfaceManager() { - var playerBars = listOf() - var bronzeBit = 1 - var ironBit = 1 - var steelBit = 1 - var mithrilBit = 1 - var adamantiteBit = 1 - var runiteBit = 1 - var silverBit = 1 - var goldBit = 1 - var barTotal = 0 - blastFurnacePlayerList.forEach { player -> - playerBars = player.blastBars.toArray().filterNotNull() - var barAmount = player.blastBars.toArray().filterNotNull().count() - var updateInterface = true - if (playerBars.isNotEmpty() && updateInterface) { - playerBars.forEach { barItem -> - when (barItem.id) { - Items.BRONZE_BAR_2349 -> player.varpManager.get(545).setVarbit(0, bronzeBit++).send(player) - Items.IRON_BAR_2351 -> player.varpManager.get(545).setVarbit(8, ironBit++).send(player) - Items.STEEL_BAR_2353 -> player.varpManager.get(545).setVarbit(16, steelBit++).send(player) - Items.MITHRIL_BAR_2359 -> player.varpManager.get(545).setVarbit(24, mithrilBit++).send(player) - Items.ADAMANTITE_BAR_2361 -> player.varpManager.get(546).setVarbit(0, adamantiteBit++).send(player) - Items.RUNITE_BAR_2363 -> player.varpManager.get(546).setVarbit(8, runiteBit++).send(player) - Items.SILVER_BAR_2355 -> player.varpManager.get(546).setVarbit(24, silverBit++).send(player) - Items.GOLD_BAR_2357 -> player.varpManager.get(546).setVarbit(16, goldBit++).send(player) - } - } - } - if (barTotal > barAmount) { - playerBars.forEach { barItem -> - when (barItem.id) { - Items.BRONZE_BAR_2349 -> player.varpManager.get(545).setVarbit(0, bronzeBit--).send(player) - Items.IRON_BAR_2351 -> player.varpManager.get(545).setVarbit(8, ironBit--).send(player) - Items.STEEL_BAR_2353 -> player.varpManager.get(545).setVarbit(16, steelBit--).send(player) - Items.MITHRIL_BAR_2359 -> player.varpManager.get(545).setVarbit(24, mithrilBit--).send(player) - Items.ADAMANTITE_BAR_2361 -> player.varpManager.get(546).setVarbit(0, adamantiteBit--).send(player) - Items.RUNITE_BAR_2363 -> player.varpManager.get(546).setVarbit(8, runiteBit--).send(player) - Items.SILVER_BAR_2355 -> player.varpManager.get(546).setVarbit(24, silverBit--).send(player) - Items.GOLD_BAR_2357 -> player.varpManager.get(546).setVarbit(16, goldBit--).send(player) - } - } - } - } - } -} +} \ No newline at end of file diff --git a/Server/src/main/content/minigame/blastfurnace/BlastFurnaceInterfaceListener.kt b/Server/src/main/content/minigame/blastfurnace/BlastFurnaceInterfaceListener.kt index 8a30d7689..61986f2e2 100644 --- a/Server/src/main/content/minigame/blastfurnace/BlastFurnaceInterfaceListener.kt +++ b/Server/src/main/content/minigame/blastfurnace/BlastFurnaceInterfaceListener.kt @@ -1,72 +1,51 @@ package content.minigame.blastfurnace +import content.global.skill.smithing.smelting.Bar import core.api.* -import core.game.node.item.Item -import org.rs09.consts.Components -import org.rs09.consts.Items import core.game.interaction.InterfaceListener +import core.game.system.task.Pulse +import org.rs09.consts.Components -/** - * Handles the blast furnace bar stock interface. - * @author definitely phil who didn't get help from ceikry at all haha :) - * @version 69.0 - */ class BlastFurnaceInterfaceListener : InterfaceListener { - override fun defineInterfaceListeners() { + onOpen(Components.BLAST_FURNACE_BAR_STOCK_28) { player, component -> + val state = BlastFurnace.getPlayerState(player) + state.setBarClaimVarbits() + state.checkBars() + return@onOpen true + } + + onOpen(Components.BLAST_FURNACE_TEMP_GAUGE_30) {player, _ -> + submitIndividualPulse(player, object : Pulse() { + override fun pulse(): Boolean { + val anim = BlastFurnace.state.furnaceTemp + 2452 + animateInterface(player, 30, 4, anim) + return false + } + }) + return@onOpen true + } + onClose(Components.BLAST_FURNACE_TEMP_GAUGE_30) {player, _ -> player.pulseManager.clear(); return@onClose true } on(Components.BLAST_FURNACE_BAR_STOCK_28){ player, _, _, buttonID, _, _ -> - val bar = BFBars.forId(buttonID) ?: return@on false - val isAll = buttonID == bar.allButtonId - val playerAmt = player.varpManager.get(bar.varpIndex).getVarbitValue(bar.offset) ?: 0 - if(playerAmt == 0) return@on true - - var amtToWithdraw = if(isAll) playerAmt else 1 - - if(freeSlots(player) < amtToWithdraw){ - amtToWithdraw = freeSlots(player) - } - - val barItemId = when(bar){ - BFBars.BRONZE -> Items.BRONZE_BAR_2349 - BFBars.IRON -> Items.IRON_BAR_2351 - BFBars.STEEL -> Items.STEEL_BAR_2353 - BFBars.MITHRIL -> Items.MITHRIL_BAR_2359 - BFBars.ADAMANT -> Items.ADAMANTITE_BAR_2361 - BFBars.RUNITE -> Items.RUNITE_BAR_2363 - BFBars.SILVER -> Items.SILVER_BAR_2355 - BFBars.GOLD -> Items.GOLD_BAR_2357 - } - - player.varpManager.get(bar.varpIndex).setVarbit(bar.offset, playerAmt - amtToWithdraw).send(player) - - addItem(player, barItemId, amtToWithdraw) - while(amtToWithdraw > 0){ - player.blastBars.remove(Item(barItemId)) - amtToWithdraw-- - } - if(player.blastBars.isEmpty) player.varpManager.get(543).clear().send(player) + val (isAll, bar) = getBarForButton(buttonID) + val state = BlastFurnace.getPlayerState(player) + state.claimBars(bar, if (isAll) state.container.getBarAmount(bar) else 1) return@on true } } - internal enum class BFBars(val buttonId: Int, val varpIndex: Int, val offset: Int, val allButtonId: Int = buttonId + 2){ - BRONZE(43, 545, 0, 44), - IRON(40, 545, 8, 41), - STEEL(36, 545, 16), - MITHRIL(33, 545, 24), - ADAMANT(30, 546, 0), - RUNITE(27, 546, 8), - SILVER(24, 546, 24), - GOLD(21, 546, 16); - - companion object { - private val idMap = values().map { it.buttonId to it }.toMap() - - fun forId(buttonId: Int): BFBars? { - return idMap[buttonId] ?: idMap[buttonId - 2] ?: idMap[buttonId - 1] - } + private fun getBarForButton (id: Int) : Pair { + return when (id) { + 43,44 -> Pair(id == 44, Bar.BRONZE) + 40,41 -> Pair(id == 41, Bar.IRON) + 36,38 -> Pair(id == 38, Bar.STEEL) + 33,35 -> Pair(id == 35, Bar.MITHRIL) + 30,32 -> Pair(id == 32, Bar.ADAMANT) + 27,29 -> Pair(id == 29, Bar.RUNITE) + 24,26 -> Pair(id == 26, Bar.SILVER) + 21,23 -> Pair(id == 23, Bar.GOLD) + else -> Pair(false, Bar.BRONZE) } } - -} \ No newline at end of file +} diff --git a/Server/src/main/content/minigame/blastfurnace/BlastFurnaceListeners.kt b/Server/src/main/content/minigame/blastfurnace/BlastFurnaceListeners.kt index 4674ce91d..627f57d77 100644 --- a/Server/src/main/content/minigame/blastfurnace/BlastFurnaceListeners.kt +++ b/Server/src/main/content/minigame/blastfurnace/BlastFurnaceListeners.kt @@ -1,382 +1,323 @@ package content.minigame.blastfurnace -import core.api.Container +import content.region.misc.keldagrim.handlers.BlastFurnaceDoorDialogue import core.api.* -import core.game.node.entity.skill.Skills -import core.game.node.item.Item -import content.global.skill.smithing.smelting.Bar -import core.game.system.task.Pulse -import core.game.interaction.InteractionListener +import core.game.dialogue.DialogueFile +import core.game.dialogue.Topic import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.interaction.InteractionListeners +import core.game.node.entity.skill.Skills +import core.game.system.task.Pulse import core.game.world.map.Location +import core.tools.END_DIALOGUE import org.rs09.consts.Items -/**"Most" of the listeners for blast furnace live in this funny little file, handles - * listeners for most things from interacting with the temp gauge to putting ore on the - * conveyor belt. The only thing that's not in here as far as listeners goes is Ordan's unnoting. - * That lives in OrdanDialogue.kt - * @author phil lips*/ class BlastFurnaceListeners : InteractionListener { - - val disLoc = getScenery(1941, 4963, 0) - val brokenPotPipe = 9117 - val brokenPumpPipe = 9121 - val pump = 9090 - val stove = intArrayOf(9085, 9086, 9087) - val coke = 9088 - val pedals = 9097 - val conveyorLoad = 9100 - val dispenser = intArrayOf(9093, 9094, 9095, 9096) - val brokenBelt = 9103 - val brokenCog = 9105 - val tGauge = 9089 - val playerOre = intArrayOf( - Items.IRON_ORE_440, - Items.COPPER_ORE_436, - Items.TIN_ORE_438, - Items.COAL_453, - Items.MITHRIL_ORE_447, - Items.ADAMANTITE_ORE_449, - Items.SILVER_ORE_442, - Items.GOLD_ORE_444, - Items.RUNITE_ORE_451 - ) - val notedOre = intArrayOf( - Items.IRON_ORE_441, - Items.COPPER_ORE_437, - Items.TIN_ORE_439, - Items.COAL_454, - Items.MITHRIL_ORE_448, - Items.ADAMANTITE_ORE_450, - Items.SILVER_ORE_443, - Items.GOLD_ORE_445, - Items.RUNITE_ORE_452 - ) + companion object { + var initialized = false + val PUMP_LOC = Location.create(1950, 4961, 0) + val PUMP_FACELOC = Location.create(1949, 4961, 0) + val PEDAL_LOC = Location.create(1947, 4966, 0) + val PEDAL_DISMOUNT = Location.create(1947, 4967, 0) + val PEDAL_FACELOC = Location.create(1946, 4966, 0) + val PUMP_ANIM = 2432 + val PEDAL_ANIM = 2433 + val PEDAL_SCENERY = getScenery(PEDAL_LOC)!! + val PUMP_SCENERY = getScenery(PUMP_LOC)!! + val validOreIds = intArrayOf( + Items.IRON_ORE_440, + Items.COPPER_ORE_436, + Items.TIN_ORE_438, + Items.COAL_453, + Items.MITHRIL_ORE_447, + Items.ADAMANTITE_ORE_449, + Items.SILVER_ORE_442, + Items.GOLD_ORE_444, + Items.RUNITE_ORE_451 + ) + } override fun defineListeners() { + if (initialized) return + initialized = true - /**FINALLY AFK STRENGTH TRAINING*/ - - on(pump, IntType.SCENERY, "operate") { player, node -> - if(player.getSkills().getLevel(Skills.STRENGTH) >= 30) { - val pumpL = Location(1950, 4961, 0) - val pumpF = Location(1949, 4961, 0) - player.properties.teleportLocation = pumpL - submitIndividualPulse(player, object : Pulse() { - override fun pulse(): Boolean { - face(player, pumpF,) - animate(player, 2432) - BlastFurnace.pumping = true - if (!BlastFurnace.pumpPipeBroken || !BlastFurnace.potPipeBroken) rewardXP( - player, - Skills.STRENGTH, - 4.0 - ) - return false - } - - override fun stop() { - BlastFurnace.pumping = false - super.stop() - } - }) - }else{ - sendDialogue(player,"I need 30 Strength to do this!") + on (BlastConsts.STAIR_ENTRANCE_ID, IntType.SCENERY, "climb-down") { p, _ -> + val hasCharos = inEquipment(p, Items.RING_OF_CHAROS_4202) || inEquipment(p, Items.RING_OF_CHAROSA_6465) + val fee = BlastFurnace.getEntranceFee(hasCharos, getStatLevel(p, Skills.SMITHING)) + if (fee > 0 && !hasTimerActive(p)) { + openDialogue(p, BlastFurnaceDoorDialogue(fee)) + return@on true } + BlastFurnace.enter(p, false) return@on true } - /**Logic for the pedals that run the conveyor, rewards Agility XP every tick that they're being - * pedaled but will stop once the conveyor breaks.*/ - - on(pedals, IntType.SCENERY, "pedal") { player, node -> - if(player.getSkills().getLevel(Skills.AGILITY) >= 30) { - val pedalL = Location(1947, 4966, 0) - val pedalF = Location(1946, 4966, 0) - player.properties.teleportLocation = pedalL - submitIndividualPulse(player, object : Pulse() { - override fun pulse(): Boolean { - face(player, pedalF,) - animate(player, 2433) - BlastFurnace.pedaling = true - if (BlastFurnace.beltRunning) rewardXP(player, Skills.AGILITY, 2.0) - return false - } - override fun stop() { - BlastFurnace.pedaling = false - super.stop() - } - }) - }else{ - sendDialogue(player,"I need 30 Agility to do this!") - } + on (BlastConsts.STAIR_EXIT_ID, IntType.SCENERY, "climb-up") { p, _ -> + teleport(p, BlastConsts.EXIT_LOC) return@on true } - /**Lets players use a spade to take coke from the pile of coke*/ + on (BlastConsts.PUMP, IntType.SCENERY, "operate") { p, _ -> + if (getDynLevel(p, Skills.STRENGTH) >= 30 && BlastFurnace.pumper == null) { + forceMove(p, p.location, PUMP_LOC, 0, 30) { + animate(p, PUMP_ANIM) + removeScenery(PUMP_SCENERY) + submitIndividualPulse(p, object : Pulse() { + override fun pulse(): Boolean { + face(p, PUMP_FACELOC) + animate(p, PUMP_ANIM) + BlastFurnace.pumper = p + return false + } - on(coke, IntType.SCENERY, "collect") { player, node -> + override fun stop() { + resetAnimator(p) + BlastFurnace.pumper = null + addScenery(PUMP_SCENERY) + super.stop() + } + }) + } + } + else if (BlastFurnace.pumper != null) + sendMessage(p, "Someone else is already doing that.") + else + sendMessage(p, "You need a Strength level of 30 to do that.") + return@on true + } + setDest(IntType.SCENERY, BlastConsts.PUMP) { _, _ -> Location.create(1951, 4961, 0) } + + on (BlastConsts.PEDALS, IntType.SCENERY, "pedal") { p, n -> + if (getDynLevel(p, Skills.AGILITY) > 30 && BlastFurnace.pedaler == null) { + forceMove(p, p.location, PEDAL_LOC, 0, 30) { + animate(p, PEDAL_ANIM) + removeScenery(PEDAL_SCENERY) + submitIndividualPulse(p, object : Pulse() { + override fun pulse(): Boolean { + if (p.settings.runEnergy < 1.0) { + teleport(p, PEDAL_DISMOUNT) + return true + } + face(p, PEDAL_FACELOC) + animate(p, PEDAL_ANIM) + BlastFurnace.pedaler = p + return false + } + + override fun stop() { + resetAnimator(p) + BlastFurnace.pedaler = null + addScenery(PEDAL_SCENERY) + super.stop() + } + }) + } + } + else if (BlastFurnace.pedaler != null) + sendMessage(p, "Someone else is already doing that.") + else + sendMessage(p, "You need an Agility level of 30 to do that.") + return@on true + } + setDest(IntType.SCENERY, BlastConsts.PEDALS) { _, _ -> Location.create(1948, 4966, 0) } + + on (BlastConsts.COKE, SCENERY, "collect") { player, _ -> if (inInventory(player, Items.SPADE_952, 1)) { - animate(player, 2441) - removeItem(player, Items.SPADE_952, Container.INVENTORY) - addItem(player, Items.SPADEFUL_OF_COKE_6448, 1) - lockInteractions(player,1) + if(removeItem(player, Items.SPADE_952, Container.INVENTORY) && addItem(player, Items.SPADEFUL_OF_COKE_6448, 1)) { + lockInteractions(player,1) + animate(player, 2441) + } + queueScript(player, 0) { + val node = getScenery(1948,4963,0) + InteractionListeners.run(node!!.getId(), SCENERY, "refuel", player, node) + } } else { sendMessage(player, "You need a spade to do this!") } - return@on true + return@on true } - /**This code is for handling the coke stove, lets players interact with it with the spadeful of coke - * and rewards firemaking XP when shoving coke in there, it will not allow players to shovel more - * coke in if it is already full.*/ + on (Items.SPADEFUL_OF_COKE_6448, ITEM, "empty") {p, n -> + if (removeItem(p, n)) addItem(p, Items.SPADE_952) + return@on true + } - on(stove, IntType.SCENERY, "refuel") { player, node -> - if (inInventory(player, Items.SPADEFUL_OF_COKE_6448, 1) && BlastFurnace.stoveCoke < 30 && player.getSkills().getLevel(Skills.FIREMAKING) >= 30) { + on (BlastConsts.STOVE, SCENERY, "refuel") { player, _ -> + if (inInventory(player, Items.SPADEFUL_OF_COKE_6448, 1)) { + if (BlastFurnace.state.cokeInStove >= BlastConsts.COKE_LIMIT) { + sendMessage(player, "The stove is already full of coke!") + return@on true + } + if (getDynLevel(player, Skills.FIREMAKING) < 30) { + sendMessage(player, "You need a Firemaking level of 30 to do this.") + return@on true + } animate(player, 2442) - lockInteractions(player,2) + lock(player,2) submitIndividualPulse(player, object : Pulse() { override fun pulse(): Boolean { - removeItem(player, Items.SPADEFUL_OF_COKE_6448, Container.INVENTORY) - addItem(player, Items.SPADE_952, 1) - animate(player, 2443) - return true + return if(removeItem(player, Items.SPADEFUL_OF_COKE_6448, Container.INVENTORY) && addItem(player, Items.SPADE_952, 1)) { + animate(player, 2443) + rewardXP(player, Skills.FIREMAKING, 5.0) + BlastFurnace.state.addCoke(1) + true + } else { + false + } } }) - rewardXP(player, Skills.FIREMAKING, 5.0) - BlastFurnace.stoveCoke++ - }else if(inInventory(player, Items.SPADEFUL_OF_COKE_6448,1) && BlastFurnace.stoveCoke >= 30 && player.getSkills().getLevel(Skills.FIREMAKING) >= 30){ - sendDialogue(player,"The coke stove is already full!") - } else if(player.getSkills().getLevel(Skills.FIREMAKING) < 30){ - sendDialogue(player,"I need 30 Firemaking to do this") + queueScript(player, 0) { + val node = getScenery(1950,4964,0) + InteractionListeners.run(node!!.getId(), SCENERY, "collect", player, node) + } } else { sendMessage(player,"You need some coke to do that!") } - return@on true + return@on true } - /**This beautiiful block of bullshit handles the logic for putting coal and ores - * on the conveyor belt. It won't let you put anything on there if there's no room - * in your blast ore or blast coal player containers, the way it works is that it - * puts your coal and ore into the blast furnace containers however, the blast furnace will NOT - * smelt your ores because it's waiting for a player attribute to be set by the ore NPCs that this spawns - * Is it fucky? Yes - * Does it work? Also yes*/ - on(conveyorLoad, IntType.SCENERY, "put-ore-on") { player, node -> - val rocksInInven = playerOre.filter {inInventory(player, it)} - var oreToActuallyAdd = 0 - var coalToActuallyAdd = 0 - if(player.blastCoal.freeSlots() > 0 || player.blastOre.freeSlots() > 0) { - player.dialogueInterpreter.sendOptions("Add all your ore to the furnace?", "Yes", "No") - player.dialogueInterpreter.addAction { player, button -> - if (button == 2 && rocksInInven.isNotEmpty()) { - rocksInInven.forEach { oreID -> - val oreAmount = amountInInventory(player, oreID) - val copperInPot = player.blastOre.getAmount(436) - val tinInPot = player.blastOre.getAmount(438) - if(oreID == Items.COAL_453 && oreAmount <= player.blastCoal.freeSlots()){ - coalToActuallyAdd = oreAmount - }else if(oreID == Items.COAL_453 && oreAmount > player.blastCoal.freeSlots() && player.blastCoal.freeSlots() > 0){ - coalToActuallyAdd = player.blastCoal.freeSlots() - } - if(oreID != Items.COAL_453 && oreAmount <= player.blastOre.freeSlots()){ - oreToActuallyAdd = oreAmount - }else if(oreID != Items.COAL_453 && oreAmount > player.blastOre.freeSlots() && player.blastOre.freeSlots() > 0){ - oreToActuallyAdd = player.blastOre.freeSlots() - } - if(oreID == Items.COPPER_ORE_436 && (oreToActuallyAdd + copperInPot) > 14){ - while(oreToActuallyAdd + copperInPot > 14){ - oreToActuallyAdd-- - } - } - if(oreID == Items.TIN_ORE_438 && (oreToActuallyAdd + tinInPot) > 14){ - while(oreToActuallyAdd + tinInPot > 14){ - oreToActuallyAdd-- - } - } - val bar = Bar.forOre(oreID) - if(oreID == Items.COAL_453) { - if (coalToActuallyAdd > 0 && getStatLevel(player, Skills.SMITHING) >= 30) { - player.blastCoal.add(Item(Items.COAL_453,coalToActuallyAdd)) - removeItem(player, Item(oreID, coalToActuallyAdd), Container.INVENTORY) - BlastFurnaceOre(player, BFOreVariant.values()[playerOre.indexOf(oreID)],coalToActuallyAdd).init() - } - else if(getStatLevel(player, Skills.SMITHING) < 30){ - sendDialogue(player, "My Smithing level is not high enough to use Coal!") - } - else sendDialogue(player, "It looks like the melting pot is already full of coal!") - } else if (bar != null) { - if (oreToActuallyAdd > 0 && getStatLevel(player, Skills.SMITHING) >= bar.level) { - player.blastOre.add(Item(oreID,oreToActuallyAdd)) - removeItem(player, Item(oreID, oreToActuallyAdd), Container.INVENTORY) - BlastFurnaceOre(player, BFOreVariant.values()[playerOre.indexOf(oreID)],oreToActuallyAdd).init() - } - else if(getStatLevel(player, Skills.SMITHING) < bar.level){ - sendDialogue(player, "My Smithing level is not high enough to smelt ${bar.name.toLowerCase()}!") - } - else sendDialogue(player, "It looks like the melting pot is already full of ore!") - } + on (BlastConsts.BELT, IntType.SCENERY, "put-ore-on") { p, _ -> + openDialogue(p, object : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + 0 -> sendDialogue(p, "Really place all your ore on the belt?").also { stage++ } + 1 -> showTopics( + Topic("Yes.", 100, true), + Topic("Nevermind.", END_DIALOGUE, true) + ) + 100 -> { + end() + BlastFurnace.placeAllOre(p, accountForSkill = true) } - }else if (button == 2 && rocksInInven.isEmpty()){ - sendDialogue(player,"I should make sure that I have some ore before doing this.") } } - } - return@on true + }) + return@on true } - /**Added this because clicking on ore then the belt and getting "Nothing interesting happens" is annoying - * it's essentially the same thing as above except without the dialogue because if you're clicking on ore - * and trying to use it on the conveyor belt then you know what you're trying to do.*/ - onUseWith(IntType.SCENERY,conveyorLoad,*playerOre){ player, _, oreType -> - val amountInInventory = player.inventory.getAmount(oreType.id) - val spaceForCoal = player.blastCoal.freeSlots() - val spaceForOre = player.blastOre.freeSlots() - val copperInPot = player.blastOre.getAmount(436) - val tinInPot = player.blastOre.getAmount(438) - var amountToAdd = 0 - if(oreType.id == Items.COAL_453 && amountInInventory <= spaceForCoal){ - amountToAdd = amountInInventory - }else if(oreType.id == Items.COAL_453 && amountInInventory > spaceForCoal){ - amountToAdd = spaceForOre - } - if(oreType.id != Items.COAL_453 && amountInInventory <= spaceForOre){ - amountToAdd = amountInInventory - }else if(oreType.id != Items.COAL_453 && amountInInventory > spaceForOre){ - amountToAdd = spaceForOre - } - if(oreType.id == Items.COPPER_ORE_436 && (amountToAdd + copperInPot) > 14){ - while(amountToAdd + copperInPot > 14){ - amountToAdd-- + onUseWith (IntType.SCENERY, validOreIds, BlastConsts.BELT) { p, used, _ -> + BlastFurnace.placeAllOre(p, used.id, accountForSkill = true) + return@onUseWith true + } + + on(BlastConsts.TEMP_GAUGE, IntType.SCENERY, "read") { p, _ -> + openInterface(p, 30) + return@on true + } + + on(BlastConsts.DISPENSER, IntType.SCENERY, "search", "take") { p, _ -> + openInterface(p, 28) + return@on true + } + + onUseWith(SCENERY, Items.BUCKET_OF_WATER_1929, *BlastConsts.DISPENSER){ p, _, _ -> + when (getVarbit(p, BFPlayerState.DISPENSER_STATE)) { + 1,2 -> { + var barsCooled = false + for (player in BlastFurnace.playersInArea) { + val state = BlastFurnace.getPlayerState(player) + if (state.barsNeedCooled) { + state.coolBars() + barsCooled = true + } + } + if (barsCooled) { + removeItem(p, Items.BUCKET_OF_WATER_1929, Container.INVENTORY) + addItem(p, Items.BUCKET_1925) + } } - } - if(oreType.id == Items.TIN_ORE_438 && (amountToAdd + tinInPot) > 14){ - while(amountToAdd + tinInPot > 14){ - amountToAdd-- - } - } - val bar = Bar.forOre(oreType.id) - if(oreType.id == Items.COAL_453) { - if (amountToAdd > 0 && getStatLevel(player, Skills.SMITHING) >= 30) { - player.blastCoal.add(Item(oreType.id,amountToAdd)) - removeItem(player, Item(oreType.id, amountToAdd), Container.INVENTORY) - BlastFurnaceOre(player, BFOreVariant.values()[playerOre.indexOf(oreType.id)],amountToAdd).init() - }else if(getStatLevel(player,Skills.SMITHING) < 30){ - sendDialogue(player,"My Smithing level is not high enough to use Coal!") - } - else sendDialogue(player, "It looks like the melting pot is already full of ore!") - } else if(bar != null) { - if (amountToAdd > 0 && getStatLevel(player, Skills.SMITHING) >= bar.level) { - player.blastOre.add(Item(oreType.id,amountToAdd)) - removeItem(player, Item(oreType.id, amountToAdd), Container.INVENTORY) - BlastFurnaceOre(player, BFOreVariant.values()[playerOre.indexOf(oreType.id)],amountToAdd).init() - }else if(getStatLevel(player,Skills.SMITHING) < bar.level){ - sendDialogue(player,"My Smithing level is not high enough to smelt Iron!") - } - else sendDialogue(player, "It looks like the melting pot is already full of ore!") + 0 -> sendDialogue(p,"There's nothing to cool off!") + 3 -> sendDialogue(p,"These bars have already cooled off!") } return@onUseWith true } - /**This handles interacting with the temperature gauge on the furnace*/ - - on(tGauge, IntType.SCENERY, "read") { player, node -> - player.interfaceManager.openComponent(30) - return@on true - } - - /**This handles taking bars from the dispenser*/ - - on(dispenser, IntType.SCENERY, "search", "take"){ player, node -> - if(player.varpManager.get(543).getVarbitValue(8) == 0 || player.varpManager.get(543).getVarbitValue(8) == 3) { - player.interfaceManager.openComponent(28) - } - return@on true - } - - /**Handles using a bucket of water on the bar dispenser*/ - - onUseWith(IntType.SCENERY,dispenser,Items.BUCKET_OF_WATER_1929){ player, used, with -> - when { - player.varpManager.get(543).getVarbitValue(8) == 2 -> { - removeItem(player,Items.BUCKET_OF_WATER_1929,Container.INVENTORY) - addItem(player,Items.BUCKET_1925) - BlastFurnace.barsHot = false - player.varpManager.get(543).setVarbit(8,3).send(player) - } - player.varpManager.get(543).getVarbitValue(8) == 0 -> { - sendDialogue(player,"There's nothing to cool off!") - } - player.varpManager.get(543).getVarbitValue(8) == 1 -> { - sendDialogue(player,"I should wait until the machine is finished") - } - player.varpManager.get(543).getVarbitValue(8) == 3 -> { - sendDialogue(player,"These bars have already cooled off!") - } - } - return@onUseWith true - } - - /**The sequel to Limp Bizkits hit single, "Fix shit"*/ - - on(brokenPotPipe, IntType.SCENERY, "repair"){ player, _ -> + on(BFSceneryController.BROKEN_POT_PIPE, SCENERY, "repair"){ player, _ -> if(player.getSkills().getLevel(Skills.CRAFTING) >= 30){ + if (!BlastFurnace.state.potPipeBroken) { + sendMessage(player, "You can't fix something that isn't broken.") + return@on true + } if(inInventory(player,Items.HAMMER_2347,1)) { rewardXP(player, Skills.CRAFTING, 50.0) - BlastFurnace.potPipeBroken = false - } - else { + BlastFurnace.state.potPipeBroken = false + } else { sendMessage(player, "I need a hammer to do this!") } - }else{ + } else { + sendDialogue(player,"I need 30 Crafting in order to do this") + } + return@on true + } + + on(BFSceneryController.BROKEN_PUMP_PIPE, SCENERY, "repair"){ player, _ -> + if(player.getSkills().getLevel(Skills.CRAFTING) >= 30){ + if (!BlastFurnace.state.pumpPipeBroken) { + sendMessage(player, "You can't fix something that isn't broken.") + return@on true + } + if(inInventory(player,Items.HAMMER_2347,1)) { + rewardXP(player, Skills.CRAFTING, 50.0) + BlastFurnace.state.pumpPipeBroken = false + } else { + sendMessage(player, "I need a hammer to do this!") + } + } else { + sendDialogue(player,"I need 30 Crafting in order to do this") + } + return@on true + } + + on(BFSceneryController.BROKEN_BELT, SCENERY, "repair"){ player, _ -> + if(player.getSkills().getLevel(Skills.CRAFTING) >= 30){ + if (!BlastFurnace.state.beltBroken) { + sendMessage(player, "You can't fix something that isn't broken.") + return@on true + } + if(inInventory(player,Items.HAMMER_2347,1)) { + rewardXP(player, Skills.CRAFTING, 50.0) + BlastFurnace.state.beltBroken = false + } else { + sendMessage(player, "I need a hammer to do this!") + } + } else { + sendDialogue(player,"I need 30 Crafting in order to do this") + } + return@on true + } + + on(BFSceneryController.BROKEN_COG, SCENERY, "repair"){ player, _ -> + if(player.getSkills().getLevel(Skills.CRAFTING) >= 30){ + if (!BlastFurnace.state.cogBroken) { + sendMessage(player, "You can't fix something that isn't broken.") + return@on true + } + if(inInventory(player,Items.HAMMER_2347,1)) { + rewardXP(player, Skills.CRAFTING, 50.0) + BlastFurnace.state.cogBroken = false + } else { + sendMessage(player, "I need a hammer to do this!") + } + } else { sendDialogue(player,"I need 30 Craft in order to do this") } return@on true } - on(brokenPumpPipe, IntType.SCENERY, "repair"){ player, _ -> - if(player.getSkills().getLevel(Skills.CRAFTING) >= 30){ - if(inInventory(player,Items.HAMMER_2347,1)) { - rewardXP(player, Skills.CRAFTING, 50.0) - BlastFurnace.pumpPipeBroken = false + on(BlastConsts.SINK, SCENERY,"fill-bucket"){ player, _ -> + player.pulseManager.run(object : Pulse(1){ + override fun pulse(): Boolean { + if(removeItem(player, Items.BUCKET_1925)) + { + animate(player, 832) + sendMessage(player, "You fill the bucket from the sink.") + addItemOrDrop(player, Items.BUCKET_OF_WATER_1929) + } + return true } - else { - sendMessage(player, "I need a hammer to do this!") - } - }else{ - sendDialogue(player,"I need 30 Craft in order to do this") - } - return@on true - } - - on(brokenBelt, IntType.SCENERY, "repair"){ player, _ -> - if(player.getSkills().getLevel(Skills.CRAFTING) >= 30){ - if(inInventory(player,Items.HAMMER_2347,1)) { - rewardXP(player, Skills.CRAFTING, 50.0) - BlastFurnace.beltBroken = false - } - else { - sendMessage(player, "I need a hammer to do this!") - } - }else{ - sendDialogue(player,"I need 30 Craft in order to do this") - } - return@on true - } - - on(brokenCog, IntType.SCENERY, "repair"){ player, _ -> - if(player.getSkills().getLevel(Skills.CRAFTING) >= 30){ - if(inInventory(player,Items.HAMMER_2347,1)) { - rewardXP(player, Skills.CRAFTING, 50.0) - BlastFurnace.cogBroken = false - } - else { - sendMessage(player, "I need a hammer to do this!") - } - }else{ - sendDialogue(player,"I need 30 Craft in order to do this") - } + }) return@on true } } diff --git a/Server/src/main/content/minigame/blastfurnace/BlastFurnaceOre.kt b/Server/src/main/content/minigame/blastfurnace/BlastFurnaceOre.kt deleted file mode 100644 index 314bc5159..000000000 --- a/Server/src/main/content/minigame/blastfurnace/BlastFurnaceOre.kt +++ /dev/null @@ -1,91 +0,0 @@ -package content.minigame.blastfurnace - -import core.api.* -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 core.game.world.update.flag.context.Animation -import core.plugin.Initializable -import org.rs09.consts.NPCs - -/**On god this is like 95% ceikry and 5% me adding a setAttribute to the clear code - * @author funny alium man*/ - -@Initializable -class BlastFurnaceOre : AbstractNPC { - //Arios constructor spaghetti - constructor() : super(NPCs.COAL_2562, null, true) {} - private constructor(id: Int, location: Location) : super(id, location) {} - - constructor(owner: Player, variant: BFOreVariant, amount: Int) : super( - when(variant){ - BFOreVariant.IRON -> NPCs.IRON_ORE_2556 - BFOreVariant.COPPER -> NPCs.COPPER_ORE_2555 - BFOreVariant.TIN -> NPCs.TIN_ORE_2554 - BFOreVariant.COAL -> NPCs.COAL_2562 - BFOreVariant.MITHRIL -> NPCs.MITHRIL_ORE_2557 - BFOreVariant.ADAMANT -> NPCs.ADAMANTITE_ORE_2558 - BFOreVariant.SILVER -> NPCs.SILVER_ORE_2560 - BFOreVariant.GOLD -> NPCs.GOLD_ORE_2561 - BFOreVariant.RUNITE -> NPCs.RUNITE_ORE_2559 - }, Location.create(1942, 4966, 0)) {this.owner = owner; isRespawn = false; } - - var owner: Player? = null - var delay = 2 - var counter = 3 - - - override fun construct(id: Int, location: Location, vararg objects: Any): AbstractNPC { - return BlastFurnaceOre(id, location) - } - - override fun getIds(): IntArray { - return intArrayOf( - NPCs.IRON_ORE_2556, - NPCs.COPPER_ORE_2555, - NPCs.TIN_ORE_2554, - NPCs.COAL_2562, - NPCs.MITHRIL_ORE_2557, - NPCs.ADAMANTITE_ORE_2558, - NPCs.SILVER_ORE_2560, - NPCs.GOLD_ORE_2561, - NPCs.PERFECT_GOLD_ORE_2563, - NPCs.RUNITE_ORE_2559) - } - - override fun handleTickActions() { - if (BlastFurnace.beltRunning) { - delay-- - if (delay <= 0 && getWorldTicks() % 2 == 0) { //run every other tick - if (counter > 0) { - properties.teleportLocation = location.transform(0, -1, 0) - counter-- - if (counter == 0) { - val animation = Animation(2434) - animate(animation) - owner?.setAttribute("/save:OreInPot",true) - submitIndividualPulse(this, object : Pulse(animationDuration(animation)) { - override fun pulse(): Boolean { - clear() - return true - } - }) - } - } - } - } - } -} - -enum class BFOreVariant { - IRON, - COPPER, - TIN, - COAL, - MITHRIL, - ADAMANT, - SILVER, - GOLD, - RUNITE -} \ No newline at end of file diff --git a/Server/src/main/content/minigame/blastfurnace/BlastFurnaceZone.kt b/Server/src/main/content/minigame/blastfurnace/BlastFurnaceZone.kt deleted file mode 100644 index 519687625..000000000 --- a/Server/src/main/content/minigame/blastfurnace/BlastFurnaceZone.kt +++ /dev/null @@ -1,55 +0,0 @@ -package content.minigame.blastfurnace - -import core.api.* -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.ZoneBorders -import core.game.world.map.zone.ZoneBuilder -import core.game.world.map.zone.ZoneMonitor -import core.plugin.Initializable -import core.plugin.Plugin - -/**Code for defining the Blast Furnace zone, Blast Furnace will only - * operate and run its logic if there are actual players in this zone - * @author phil lips*/ - - -//Remove this once the funny dupe gets fixed -//@Initializable -class BlastFurnaceZone : MapZone("Blast Furnace Zone",true), Plugin { - - var pulseStarted = false - - override fun newInstance(arg: Any?): Plugin { - ZoneBuilder.configure(this) - return this - } - - override fun fireEvent(identifier: String?, vararg args: Any?): Any { - return Unit - } - - override fun configure() { - super.register(ZoneBorders(1935,4956,1956,4974)) - } - - override fun enter(e: Entity?): Boolean { - if (!pulseStarted){ - submitWorldPulse(BlastFurnace.blastPulse) - pulseStarted = true - } - if (e != null && e.isPlayer) { - BlastFurnace.blastFurnacePlayerList.add(e.asPlayer()) - } - return super.enter(e) - } - - override fun leave(e: Entity?, logout: Boolean): Boolean { - if (e != null && e.isPlayer) { - BlastFurnace.blastFurnacePlayerList.remove(e.asPlayer()) - } - return super.leave(e, logout) - } - -} \ No newline at end of file diff --git a/Server/src/main/content/minigame/blastfurnace/BlastState.kt b/Server/src/main/content/minigame/blastfurnace/BlastState.kt new file mode 100644 index 000000000..07dafe3b5 --- /dev/null +++ b/Server/src/main/content/minigame/blastfurnace/BlastState.kt @@ -0,0 +1,76 @@ +package content.minigame.blastfurnace + +import core.tools.RandomFunction + +class BlastState { + var disableBreaking = false + var forceBreaking = false + var potPipeBroken = false + var pumpPipeBroken = false + var beltBroken = false + var cogBroken = false + var ticksElapsed = 0 + + var stoveTemp = 0 + private set + var furnaceTemp = 0 + private set + var cokeInStove = 0 + private set + + fun tick(pumping: Boolean, pedaling: Boolean) { + ticksElapsed++ + + adjustStoveTemperature() + adjustFurnaceTemperature(pumping) + + checkForCokeDecrement() + checkForBreakage(pedaling, pumping) + } + + private fun adjustStoveTemperature() { + if (cokeInStove > 0) + stoveTemp = (stoveTemp + 1).coerceAtMost(100) + else stoveTemp = (stoveTemp - 1).coerceAtLeast(0) + } + + private fun adjustFurnaceTemperature(pumping: Boolean) { + if (pumping && !pumpPipeBroken && !potPipeBroken && !beltBroken && !cogBroken) { + when (stoveTemp) { + in 1..32 -> furnaceTemp += 1 + in 32..66 -> furnaceTemp += 2 + in 67..100 -> furnaceTemp += 3 + } + } else furnaceTemp-- + + furnaceTemp = furnaceTemp + .coerceAtLeast(0) + .coerceAtMost(100) + } + + private fun checkForBreakage(pedaling: Boolean, pumping: Boolean) { + if (disableBreaking) return + if (pumping && (!potPipeBroken || !pumpPipeBroken)) { + if (RandomFunction.roll(50) || forceBreaking) { + if (RandomFunction.nextBool()) potPipeBroken = true + else pumpPipeBroken = true + } + } + + if (pedaling && (!beltBroken || !cogBroken)) { + if (RandomFunction.roll(50) || forceBreaking) + beltBroken = true + else if (RandomFunction.roll(50) || forceBreaking) + cogBroken = true + } + } + + private fun checkForCokeDecrement() { + if (ticksElapsed % 10 == 0) + cokeInStove = (cokeInStove - 1).coerceAtLeast(0) + } + + fun addCoke (amount: Int) { + cokeInStove += amount.coerceAtMost(BlastConsts.COKE_LIMIT - cokeInStove) + } +} \ No newline at end of file diff --git a/Server/src/main/content/minigame/blastfurnace/PhunnyGaugeTempInterfaceListener.kt b/Server/src/main/content/minigame/blastfurnace/PhunnyGaugeTempInterfaceListener.kt deleted file mode 100644 index 7edbab79c..000000000 --- a/Server/src/main/content/minigame/blastfurnace/PhunnyGaugeTempInterfaceListener.kt +++ /dev/null @@ -1,24 +0,0 @@ -package content.minigame.blastfurnace - -import core.game.interaction.InterfaceListener - -/**Handles adding and removing players to the temp gauge viewing list. - * Only updates the gauge if people are actually looking at it - * @author phil lips*/ - -class PhunnyGaugeTempInterfaceListener : InterfaceListener { - - override fun defineInterfaceListeners() { - onOpen(30) {player, component -> - BlastFurnace.gaugeViewList.add(player) - return@onOpen true - } - - onClose(30) {player, component -> - BlastFurnace.gaugeViewList.remove(player) - return@onClose true - } - } - - -} \ No newline at end of file diff --git a/Server/src/main/content/minigame/bountyhunter/BountyHunterActivity.java b/Server/src/main/content/minigame/bountyhunter/BountyHunterActivity.java index fd258e5d6..08b6c59e7 100644 --- a/Server/src/main/content/minigame/bountyhunter/BountyHunterActivity.java +++ b/Server/src/main/content/minigame/bountyhunter/BountyHunterActivity.java @@ -1,5 +1,6 @@ package content.minigame.bountyhunter; +import core.api.*; import core.game.component.Component; import core.game.component.ComponentDefinition; import core.game.component.ComponentPlugin; @@ -33,6 +34,8 @@ import core.tools.RandomFunction; import java.util.*; +import static core.api.ContentAPIKt.*; + /** * Handles the Bounty hunter activity. * @author Emperor @@ -118,7 +121,7 @@ public final class BountyHunterActivity extends ActivityPlugin { }; /** - * The game pulse (doesn't update every tick on 2009scape as well). + * The game pulse (doesn't update every tick on 2009Scape as well). */ private final Pulse gamePulse = new Pulse(10) { @Override @@ -317,7 +320,7 @@ public final class BountyHunterActivity extends ActivityPlugin { } else if (interfaceId == 271 && buttonId == 25) { if (player.getAttribute("exit_penalty", 0) > GameWorld.getTicks()) { player.getPacketDispatch().sendMessage("You can't use the protect item prayer until your penalty has passed."); - player.getConfigManager().send(PrayerType.PROTECT_ITEMS.getConfig(), 0); + setVarp(player, PrayerType.PROTECT_ITEMS.getConfig(), 0); return true; } } diff --git a/Server/src/main/content/minigame/bountyhunter/BountyLocateSpell.java b/Server/src/main/content/minigame/bountyhunter/BountyLocateSpell.java index 7e24571e5..36693d2f0 100644 --- a/Server/src/main/content/minigame/bountyhunter/BountyLocateSpell.java +++ b/Server/src/main/content/minigame/bountyhunter/BountyLocateSpell.java @@ -10,13 +10,14 @@ 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.entity.player.link.TeleportManager.TeleportType; -import core.game.node.entity.state.EntityState; import core.game.node.item.Item; import core.game.world.GameWorld; import core.game.world.map.Location; import core.game.world.map.RegionManager; import core.plugin.Plugin; +import static core.api.ContentAPIKt.*; + /** * Handles the bounty target locate spell. * @author Emperor @@ -43,8 +44,8 @@ public final class BountyLocateSpell extends MagicSpell { player.getPacketDispatch().sendMessage("You don't have a target to teleport to."); return true; } - if (player.getStateManager().hasState(EntityState.FROZEN) || player.getStateManager().hasState(EntityState.STUNNED)) { - player.getPacketDispatch().sendMessage("You can't use this when " + (player.getStateManager().hasState(EntityState.STUNNED) ? "stunned." : "frozen.")); + if (hasTimerActive(player, "frozen") || isStunned(player)) { + player.getPacketDispatch().sendMessage("You can't use this when " + (isStunned(player) ? "stunned." : "frozen.")); return true; } boolean combat = player.inCombat(); @@ -96,4 +97,4 @@ public final class BountyLocateSpell extends MagicSpell { return this; } -} \ No newline at end of file +} diff --git a/Server/src/main/content/minigame/bountyhunter/MaximillianSackvilleDialogue.kt b/Server/src/main/content/minigame/bountyhunter/MaximillianSackvilleDialogue.kt index 73d3c1369..a5b047041 100644 --- a/Server/src/main/content/minigame/bountyhunter/MaximillianSackvilleDialogue.kt +++ b/Server/src/main/content/minigame/bountyhunter/MaximillianSackvilleDialogue.kt @@ -19,13 +19,13 @@ import core.tools.START_DIALOGUE * @author vddCore */ @Initializable -class MaximillianSackvilleDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { +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( - core.game.dialogue.FacialExpression.NEUTRAL, + 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 } @@ -33,7 +33,7 @@ class MaximillianSackvilleDialogue(player: Player? = null) : core.game.dialogue. else -> { npcl( - core.game.dialogue.FacialExpression.NEUTRAL, + FacialExpression.NEUTRAL, "Good day, how may I help you?" ).also { if (hasAwaitingGrandExchangeCollections(player)) { @@ -46,44 +46,44 @@ class MaximillianSackvilleDialogue(player: Player? = null) : core.game.dialogue. } 1 -> npcl( - core.game.dialogue.FacialExpression.NEUTRAL, + 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( - core.game.dialogue.FacialExpression.ASKING, + FacialExpression.ASKING, "Who are you?" ).also { stage++ } 3 -> npcl( - core.game.dialogue.FacialExpression.NEUTRAL, + 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(core.game.dialogue.FacialExpression.NEUTRAL, "I'd like to access my bank account.", 10), + Topic(FacialExpression.NEUTRAL, "I'd like to access my bank account.", 10), IfTopic( - core.game.dialogue.FacialExpression.NEUTRAL, + FacialExpression.NEUTRAL, "I'd like to switch to my ${getBankAccountName(player, true)} bank account.", 11, hasActivatedSecondaryBankAccount(player) ), - Topic(core.game.dialogue.FacialExpression.NEUTRAL, "I'd like to check my PIN settings.", 12), - Topic(core.game.dialogue.FacialExpression.NEUTRAL, "I'd like to collect items.", 13), - Topic(core.game.dialogue.FacialExpression.ASKING, "Aren't you afraid of working in the Wilderness?", 5) + 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( - core.game.dialogue.FacialExpression.NEUTRAL, + 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( - core.game.dialogue.FacialExpression.NEUTRAL, + 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 } @@ -97,7 +97,7 @@ class MaximillianSackvilleDialogue(player: Player? = null) : core.game.dialogue. toggleBankAccount(player) npcl( - core.game.dialogue.FacialExpression.NEUTRAL, + FacialExpression.NEUTRAL, "Naturally. You can now access your ${getBankAccountName(player)} bank account." ).also { stage = END_DIALOGUE } } 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/CastleWars.kt b/Server/src/main/content/minigame/castlewars/CastleWars.kt new file mode 100644 index 000000000..670ea453c --- /dev/null +++ b/Server/src/main/content/minigame/castlewars/CastleWars.kt @@ -0,0 +1,81 @@ +package rs09.game.content.activity.castlewars + +import core.game.world.map.zone.ZoneBorders +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import org.rs09.consts.Scenery + +object CastleWars { + // Scenery IDs + const val joinSaradominTeamPortal: Int = Scenery.SARADOMIN_PORTAL_4387 + const val joinZamorakTeamPortal: Int = Scenery.ZAMORAK_PORTAL_4388 + const val saradominLeaveLobbyPortal: Int = Scenery.PORTAL_4389 + const val zamorakLeaveLobbyPortal: Int = Scenery.PORTAL_4390 + const val joinGuthixTeamPortal: Int = Scenery.GUTHIX_PORTAL_4408 + const val cwCastleClimbingRope: Int = Scenery.CLIMBING_ROPE_36312 + const val castleWaterTap: Int = Scenery.TAP_4482 + const val cwSteppingStones: Int = Scenery.STEPPING_STONE_4411 + + // Map of the item table scenery id to the item id they give + val cwTableItemRewardMap = mapOf( + Scenery.TABLE_36573 to Items.TOOLKIT_4051, // Saradomin Toolkit Table + Scenery.TABLE_36580 to Items.TOOLKIT_4051, // Zamorak Toolkit Table + Scenery.TABLE_36574 to Items.ROCK_4043, // Saradomin Rock Table + Scenery.TABLE_36581 to Items.ROCK_4043, // Zamorak Rock Table + Scenery.TABLE_36575 to Items.BARRICADE_4053, // Saradomin Barricade Table + Scenery.TABLE_36582 to Items.BARRICADE_4053, // Zamorak Barricade Table + Scenery.TABLE_36576 to Items.CLIMBING_ROPE_4047, // Saradomin Climbing Rope Table + Scenery.TABLE_36583 to Items.CLIMBING_ROPE_4047, // Zamorak Climbing Rope Table + Scenery.TABLE_36577 to Items.EXPLOSIVE_POTION_4045, // Saradomin Explosive Potion Table + Scenery.TABLE_36584 to Items.EXPLOSIVE_POTION_4045, // Zamorak Explosive Potion Table + Scenery.TABLE_36578 to Items.BRONZE_PICKAXE_1265, // Saradomin Pickaxe Table + Scenery.TABLE_36585 to Items.BRONZE_PICKAXE_1265, // Zamorak Pickaxe Table + Scenery.TABLE_36579 to Items.BANDAGES_4049, // Saradomin Bandages Table + Scenery.TABLE_36586 to Items.BANDAGES_4049 // Zamorak Bandages Table + ) + + val cwCastleBattlementsMap = mapOf( + Scenery.BATTLEMENTS_4446 to Scenery.BATTLEMENTS_36313, + Scenery.BATTLEMENTS_4447 to Scenery.BATTLEMENTS_36314 + ) + + // Item IDs + const val saradominTeamHoodedCloak: Int = Items.HOODED_CLOAK_4041 + const val zamorakTeamHoodedCloak: Int = Items.HOODED_CLOAK_4042 + const val saradominFlag: Int = Items.SARADOMIN_BANNER_4037 // Might be 4038 + const val zamorakFlag: Int = Items.ZAMORAK_BANNER_4039 // Might be 4040 + const val cwPickaxe: Int = Items.BRONZE_PICKAXE_1265 + const val cwRock: Int = Items.ROCK_4043 + const val cwExplosivePotion: Int = Items.EXPLOSIVE_POTION_4045 + const val cwClimbingRope: Int = Items.CLIMBING_ROPE_4047 + const val cwBandages: Int = Items.BANDAGES_4049 + const val cwToolkit: Int = Items.TOOLKIT_4051 + const val cwBarricade: Int = Items.BARRICADE_4053 + const val cwManualBook: Int = Items.CASTLEWARS_MANUAL_4055 + const val cwTicketRewardCurrency: Int = Items.CASTLE_WARS_TICKET_4067 + + // NPC IDs + const val sheep = NPCs.SHEEP_1529 + const val imp = NPCs.IMP_1531 + const val rabbit = NPCs.RABBIT_1530 + const val unknownCwarsBarricade1 = NPCs.BARRICADE_1532 + const val unknownCwarsBarricadeOnFire = NPCs.BARRICADE_1533 + const val unknownCwarsBarricade2 = NPCs.BARRICADE_1534 + const val unknownCwarsBarricade3 = NPCs.BARRICADE_1535 + + // Locations + val lobbyBankArea: ZoneBorders = ZoneBorders(2440, 3092, 2444, 3086, 0) + + // Strings + const val saradominName = "Saradomin" + const val zamorakName = "Zamorak" + const val guthixName = "Guthix" + const val portalAttribute = "castlewars_portal" + const val invFullMessage = "Your inventory is too full to hold any more " + + // Ints + const val gameTimeMinutes = 20 + const val gameCooldownMinutes = 5 + const val ropeAliveTicks = -1 // TODO: Find amount of time until wall reverts to non rope state + +} \ No newline at end of file diff --git a/Server/src/main/content/minigame/castlewars/CastleWarsListeners.kt b/Server/src/main/content/minigame/castlewars/CastleWarsListeners.kt new file mode 100644 index 000000000..24fdb55cc --- /dev/null +++ b/Server/src/main/content/minigame/castlewars/CastleWarsListeners.kt @@ -0,0 +1,188 @@ +package rs09.game.content.activity.castlewars + +import core.api.* +import content.global.skill.agility.AgilityHandler +import content.global.skill.summoning.familiar.BurdenBeast +import core.cache.def.impl.ItemDefinition +import core.game.container.Container +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.node.scenery.Scenery +import core.game.node.scenery.SceneryBuilder +import core.game.world.GameWorld +import core.game.world.map.Location +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 + +@Suppress("unused") +class CastleWarsListeners : InteractionListener { + + override fun defineListeners() { + + // Scenery Interactions - PORTALS + on(CastleWars.joinSaradominTeamPortal, IntType.SCENERY, "Enter") { player, _ -> + if (GameWorld.settings?.enable_castle_wars != true) return@on false + if (joinError(player)) return@on true + // MapArea handles joining properties + player.properties.teleportLocation = CastleWarsWaitingArea.saradominWaitingRoom.randomWalkableLoc + player.setAttribute(CastleWars.portalAttribute, CastleWars.saradominName) + return@on true + } + on(CastleWars.joinZamorakTeamPortal, IntType.SCENERY, "Enter") { player, _ -> + if (GameWorld.settings?.enable_castle_wars != true) return@on false + if (joinError(player)) return@on true + // MapArea handles joining properties + player.properties.teleportLocation = CastleWarsWaitingArea.zamorakWaitingRoom.randomWalkableLoc + player.setAttribute(CastleWars.portalAttribute, CastleWars.zamorakName) + return@on true + } + on(CastleWars.saradominLeaveLobbyPortal, IntType.SCENERY, "Exit") { player, _ -> + player.properties.teleportLocation = CastleWars.lobbyBankArea.randomWalkableLoc + // MapArea handles leaving properties + return@on true + } + on(CastleWars.zamorakLeaveLobbyPortal, IntType.SCENERY, "Exit") { player, _ -> + player.properties.teleportLocation = CastleWars.lobbyBankArea.randomWalkableLoc + // MapArea handles leaving properties + return@on true + } + on(CastleWars.joinGuthixTeamPortal, IntType.SCENERY, "Enter") { player, _ -> + if (GameWorld.settings?.enable_castle_wars != true) return@on false + if (joinError(player)) return@on true + + // Join the team with fewer players, if they're equal, join randomly + if (CastleWarsWaitingArea.waitingSaradominPlayers.size < CastleWarsWaitingArea.waitingZamorakPlayers.size) { + player.properties.teleportLocation = CastleWarsWaitingArea.saradominWaitingRoom.randomWalkableLoc + } else if (CastleWarsWaitingArea.waitingSaradominPlayers.size > CastleWarsWaitingArea.waitingZamorakPlayers.size) { + player.properties.teleportLocation = CastleWarsWaitingArea.zamorakWaitingRoom.randomWalkableLoc + } else { + if (Math.random() < 0.5) { + player.properties.teleportLocation = CastleWarsWaitingArea.saradominWaitingRoom.randomWalkableLoc + } else { + player.properties.teleportLocation = CastleWarsWaitingArea.zamorakWaitingRoom.randomWalkableLoc + } + } + player.setAttribute(CastleWars.portalAttribute, CastleWars.guthixName) + return@on true + } + + // Scenery Interactions - Item Tables + on(CastleWars.cwTableItemRewardMap.keys.toIntArray(), SCENERY, "take-from") { player, node -> + // Retrieve the item id from the map (null safe) + val rewardItem: Int = CastleWars.cwTableItemRewardMap.getValue(node.id) + + // If item is added to inventory, play pickup sound + if (addItem(player, rewardItem)) { + playAudio(player, Sounds.PICK2_2582) + + // Warn player inventory full using custom dialogue box + } else { + val formattedItemName = (ItemDefinition.forId(rewardItem).name.lowercase() + "s.") // Get the formatted item name string + .replace("pes.", "pe.") // Replacement for ropes. -> rope. + .replace("bronze ", "") // Replacement for bronze pickaxes. -> pickaxes. + + // Toolkit gets a more grammatically correct sentence + if (rewardItem == Items.TOOLKIT_4051) { + sendDialogue(player, "Your inventory is too full to hold a toolkit.") + } else { + sendDialogue(player, (CastleWars.invFullMessage + formattedItemName)) + } + } + return@on true + } + + // Item/Scenery Interaction - Outside Battlement (Wall) Rope Climb Setup + onUseWith(SCENERY, CastleWars.cwClimbingRope, *CastleWars.cwCastleBattlementsMap.keys.toIntArray()) { player, rope, wall -> + // Remove rope item from inventory + removeItem(player, rope) + // First, replace the scenery with the rope on top of the wall + replaceScenery(wall.asScenery(), CastleWars.cwCastleBattlementsMap.getValue(wall.id), CastleWars.ropeAliveTicks) + // Second, create a new scenery with the rope falling down + val toAdd = Scenery(CastleWars.cwCastleClimbingRope, wall.location, 4, wall.direction.toInteger()) + SceneryBuilder.add(toAdd, CastleWars.ropeAliveTicks) + toAdd.isActive = true + return@onUseWith true + } + + // Scenery Interactions - Outside Battlement (Wall) Player Climbs Rope + on(CastleWars.cwCastleClimbingRope, SCENERY, "climb") { player, rope -> + // Get the direction of the scenery + val dir = rope.asScenery().direction.opposite + // Move player on top of the wall from that specific rope location + teleport(player, Location(dir.stepY + player.location.x, -dir.stepX + player.location.y), TeleportManager.TeleportType.INSTANT) + return@on true + } + + // Scenery Interactions - Water Tap - No pulse + onUseWith(SCENERY, Items.BUCKET_1925, CastleWars.castleWaterTap) { player, used, _ -> + // Lock player + lock(player, 1) + // Bucket fill animation + animate(player, 832) + // Bucket fill sound + playAudio(player, Sounds.TAP_FILL_2609) + // Replace empty -> full water bucket + replaceSlot(player, used.asItem().slot, Item(Items.BUCKET_OF_WATER_1929)) + return@onUseWith true + } + + // Scenery Interactions - Stepping stones + on(CastleWars.cwSteppingStones, SCENERY, "jump-to") { player, stone -> + // Lock the player + lock(player, 3) + // Make the player "Jump" to the next stepping stone + AgilityHandler.forceWalk(player, -1, player.location, stone.location, Animation(741), 10, 0.0, null, 1) + // Delay sound to line up with "jump" movement + runTask(player, 1) { + // Play jumping sound + playAudio(player, Sounds.JUMP_2461) + } + return@on true + } + } + + private fun hasNonCombatItems(container: Container): Boolean { + for (item in container.toArray()) { + if (item?.id != null && (item.id == Items.COINS_995 || item.definition?.noteId == item.id)) + return true + } + return false + } + + private fun capeOrHelmetError(player: Player): String? { + val wornCape = getItemFromEquipment(player, EquipmentSlot.CAPE)?.id ?: -1 + val wornHelmet = getItemFromEquipment(player, EquipmentSlot.HEAD)?.id ?: -1 + + if (wornCape != -1 || wornHelmet != -1) return "You can't wear hats, capes, or helms in the arena." + return null + } + + private fun nonCombatItemsCheck(player: Player): String? { + // Coins & Noted items, possibly others? + if (hasNonCombatItems(player.inventory)) return "You can't take non-combat items into the arena." + + return null + } + + private fun familiarCheck(player: Player): String? { + // https://forum.tip.it/topic/250066-castle-wars-familiar-msg/ + // Your familiar can't hold any non-combat items + + // Check the player's familiar container for Coins or Noted items + val familiar: BurdenBeast = player.familiarManager.familiar as? BurdenBeast ?: return null + + if (hasNonCombatItems(familiar.container)) return "Your familiar can't take non-combat items into the arena." + + return null + } + + private fun joinError(player: Player): Boolean { + val errorMessage = capeOrHelmetError(player) ?: nonCombatItemsCheck(player) ?: familiarCheck(player) ?: return false + player.sendMessage(errorMessage).also { return true } + } +} \ No newline at end of file diff --git a/Server/src/main/content/minigame/castlewars/CastleWarsOverlay.kt b/Server/src/main/content/minigame/castlewars/CastleWarsOverlay.kt new file mode 100644 index 000000000..76a4a8a35 --- /dev/null +++ b/Server/src/main/content/minigame/castlewars/CastleWarsOverlay.kt @@ -0,0 +1,22 @@ +import core.api.* +import core.game.node.entity.player.Player + +/** + * Handles updating the Castle Wars overlay interface + * @author dginovker + */ +object CastleWarsOverlay { + @JvmStatic + fun sendLobbyUpdate(player: Player, bothTeamsHavePlayers: Boolean, gameStartMinutes: Int) { + setVarp(player, 380, if (bothTeamsHavePlayers) gameStartMinutes else 0) + } + + @JvmStatic + fun sendGameUpdate(player: Player) { + // Todo - Figure out underground mine/etc + setVarbit(player, 143, 0) // Flag status - safe = 0, taken = 1, dropped = 2 + setVarbit(player, 145, 5) // Saradomin's score + setVarbit(player, 153, 0) // Flag status - safe = 0, taken = 1, dropped = 2 + setVarbit(player, 155, 7) // Zamorak's score + } +} diff --git a/Server/src/main/content/minigame/castlewars/Lanthus.kt b/Server/src/main/content/minigame/castlewars/Lanthus.kt new file mode 100644 index 000000000..301e178ad --- /dev/null +++ b/Server/src/main/content/minigame/castlewars/Lanthus.kt @@ -0,0 +1,131 @@ +package rs09.game.content.activity.castlewars + +import core.api.TickListener +import core.api.openDialogue +import core.api.sendMessage +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.item.Item +import core.tools.END_DIALOGUE +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +class Lanthus: InteractionListener, TickListener { + override fun defineListeners() { + on(NPCs.LANTHUS_1526, IntType.NPC, "talk-to") { player, npcNode -> + val lanthusNpc = npcNode.asNpc() + openDialogue(player, object : DialogueFile(){ + override fun handle(componentID: Int, buttonID: Int) { + when(stage) { + 0 -> npcl(FacialExpression.FRIENDLY, "Good day, how may I help you?").also { stage = 2 } + 2 -> options( + "What is this place?", + "What do you have for trade?", + "Do you have a manual? I'd like to learn how to play!" + ).also { stage++ } + 3 -> when (buttonID) { + 1 -> player("What is this place?").also { stage = 4 } + 2 -> lanthusNpc.openShop(player).also { stage = END_DIALOGUE } + 3 -> player("Do you have a manual? I'd like to learn how to play!").also { stage = 50 } + } + 4 -> npcl( + FacialExpression.FRIENDLY, + "This is the great Castle Wars arena! Here you can fight for the glory of Saradomin or Zamorak." + ).also { stage++ } + 5 -> options( + "Really, how do I do that?", + "Are there any rules?", + "What can I win?" + ).also { stage++ } + 6 -> when (buttonID) { + 1 -> player("Really, how do I do that?").also { stage = 7 } + 2 -> player("Are there any rules?").also { stage = 80 } + 3 -> player("What can I win?").also { stage = 90 } + } + 7 -> npcl( + FacialExpression.FRIENDLY, + "Easy, you just step through one of the three portals. To join Zamorak, pass through the red portal. To join Saradomin, pass through the blue portal. If you don't mind then pass through the green portal.", + ).also { stage ++ } + 8 -> options( + "Are there any rules?", + "What can I win?", + "What do you have for trade?", + "Do you have a manual? I'd like to learn how to play!" + ).also { stage++ } + 9 -> when (buttonID) { + 1 -> player("Are there any rules?").also { stage = 80 } + 2 -> player("What can I win?").also { stage = 90 } + 3 -> lanthusNpc.openShop(player).also { stage = END_DIALOGUE } + 4 -> player("Do you have a manual? I'd like to learn how to play!").also { stage = 50 } + } + + 80 -> npcl( + FacialExpression.FRIENDLY, "Of course, there are always rules. Firstly you can't wear a cape as you enter the portal, you'll be given your team colours to wear while in the arena. You're also prohibited from taking non-combat related items in") + .also { stage ++ } + + 81 -> npcl( + FacialExpression.FRIENDLY, " with you. So you should only have equipment, potions, and runes on you. Secondly, attacking your own team or your team's defences isn't allowed. You don't want to be angering" + ).also { stage ++ } + + 82 -> npcl( + FacialExpression.FRIENDLY, "your patron god, do you? Other than that, just have fun and enjoy it!" + ).also { stage ++ } + + 50 -> npcl( + FacialExpression.FRIENDLY, + "Sure, here you go.").also { stage = END_DIALOGUE }.also { player.inventory.add(Item(Items.CASTLEWARS_MANUAL_4055)) } + + 83 -> player("Great! Oh, how do I win the game?").also { stage ++ } + 84 -> npcl( + FacialExpression.FRIENDLY, + "The aim is to get into your opponents' castle and take their team standard. Then bring that back and capture it on your team's standard." + ).also { stage ++ } + 85 -> options( + "What can I win?", + "What do you have to trade?", + "Do you have a manual? I'd like to learn how to play!" + ).also { stage ++ } + 86 -> when (buttonID) { + 1 -> player("What can I win?").also { stage = 90 } + 2 -> lanthusNpc.openShop(player).also { stage = END_DIALOGUE } + 3 -> player("Do you have a manual? I'd like to learn how to play!").also { stage = 50 } + } + + 90 -> npcl( + FacialExpression.FRIENDLY, + "Players on the winning team will receive 2 Castle Wars Tickets which you can trade back to me for other items. In the event of a draw every player will get 1 ticket." + ).also { stage ++ } + 91 -> options( + "Are there any rules?", + "What do you have to trade?", + "Do you have a manual? I'd like to learn how to play!" + ).also { stage ++ } + 92 -> when (buttonID) { + 1 -> player("Are there any rules?").also { stage = 80 } + 2 -> lanthusNpc.openShop(player).also { stage = END_DIALOGUE } + 3 -> player("Do you have a manual? I'd like to learn how to play!").also { stage = 50 } + } + // Default + else -> sendMessage(player, "Error - unknown stage $stage").also { stage = END_DIALOGUE } + } + } + }, lanthusNpc) + + return@on true + } + on(NPCs.LANTHUS_1526, IntType.NPC, "trade-with") { player, npcNode -> + npcNode.asNpc().openShop(player) + return@on true + } + } + + override fun tick() { + "The next game will start in 4 minutes" + "The next game will start in 1 minute" + // He also talks to Postie Pete sometimes? + + } +} \ 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 new file mode 100644 index 000000000..c6b336c55 --- /dev/null +++ b/Server/src/main/content/minigame/castlewars/areas/CastleWarsArea.kt @@ -0,0 +1,75 @@ +package rs09.game.content.activity.castlewars.areas + +import content.global.skill.summoning.familiar.BurdenBeast +import core.api.* +import core.game.interaction.InteractionListener +import core.game.node.entity.Entity +import core.game.node.entity.player.Player +import rs09.game.content.activity.castlewars.CastleWars + +abstract class CastleWarsArea : MapArea, LogoutListener, InteractionListener { + + override fun areaLeave(entity: Entity, logout: Boolean) { + super.areaLeave(entity, logout) + exitArea(entity as? Player ?: return) + } + + override fun logout(player: Player) { + if (!defineAreaBorders().any { it.insideBorder(player.location) }) { + return // We're not in this area + } + + // Move the player to the lobby bank area + // Set location directly here, because we want it to take effect immediately + player.location = CastleWars.lobbyBankArea.randomWalkableLoc + exitArea(player) + } + + open fun exitArea(player: Player) { + // Remove any transformation + player.appearance.transformNPC(-1) + + // Give the player their tabs back + player.interfaceManager.restoreTabs() + + // Let the player run + player.walkingQueue.isRunDisabled = false + + // If we're not entering another Castle Wars area, remove the Castle Wars items + if ((CastleWarsGameArea.areaBorders + CastleWarsWaitingArea.areaBorders).none { it.insideBorder(player.location) }) { + exitCastleWars(player) + } + } + + private fun exitCastleWars(player: Player) { + // Close the overlay interface + player.interfaceManager.closeOverlay() + + // Remove teleblock + removeTimer(player, "teleblock") + + // Remove any Castle Wars items + // Todo Remove any tinderboxes or other castle wars items - See Jan 2018 update: https://oldschool.runescape.wiki/w/Castle_Wars + val cwarsItems = intArrayOf(CastleWars.saradominTeamHoodedCloak, + CastleWars.zamorakTeamHoodedCloak, + CastleWars.saradominFlag, + CastleWars.zamorakFlag) + + player.equipment.removeAll(cwarsItems) + player.inventory.removeAll(cwarsItems) + (player.familiarManager.familiar as? BurdenBeast)?.container?.removeAll(cwarsItems) + } + + override fun defineListeners() { + onUnequip(intArrayOf(CastleWars.saradominTeamHoodedCloak, CastleWars.zamorakTeamHoodedCloak)) { player, _ -> + defineAreaBorders().forEach { border -> + if (border.insideBorder(player)) { + sendMessage(player, "You can't remove your team's colours") + return@onUnequip false + } + } + return@onUnequip true + } + } + +} diff --git a/Server/src/main/content/minigame/castlewars/areas/CastleWarsGameArea.kt b/Server/src/main/content/minigame/castlewars/areas/CastleWarsGameArea.kt new file mode 100644 index 000000000..2b32486aa --- /dev/null +++ b/Server/src/main/content/minigame/castlewars/areas/CastleWarsGameArea.kt @@ -0,0 +1,98 @@ +package rs09.game.content.activity.castlewars.areas + +import core.api.* +import core.game.component.Component +import core.game.node.entity.Entity +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.tools.Log +import core.tools.ticksPerMinute +import org.rs09.consts.Components +import rs09.game.content.activity.castlewars.CastleWars + +/** + * Handles the Castle Wars game map + */ +class CastleWarsGameArea : CastleWarsArea(), TickListener { + + companion object { + private val saradominStandardFloor: ZoneBorders = ZoneBorders(Location.create(2426, 3073, 3), Location.create(2430, 3077, 3)) + private val zamorakStandardFloor: ZoneBorders = ZoneBorders(Location.create(2373, 3134, 3), Location.create(2369, 3130, 3)) + private val saradominUpperFloor: ZoneBorders = ZoneBorders(Location.create(2431, 3080, 2), Location.create(2423, 3072, 2)) + private val zamorakUpperFloor: ZoneBorders = ZoneBorders(Location.create(2368, 3127, 2), Location.create(2376, 3135, 2)) + private val saradominFloor: ZoneBorders = ZoneBorders(Location.create(2420, 3072, 1), Location.create(2431, 3083, 1)) + private val zamorakFloor: ZoneBorders = ZoneBorders(Location.create(2379, 3135, 1), Location.create(2368, 3124, 1)) + private val battleField: ZoneBorders = ZoneBorders(Location.create(2368, 3135, 0), Location.create(2431, 3072, 0)) + private val saradominTunnels: ZoneBorders = ZoneBorders(Location.create(2430, 9481, 0), Location.create(2400, 9504, 0)) + private val zamorakTunnels: ZoneBorders = ZoneBorders(Location.create(2401, 9503, 0), Location.create(2366, 9529, 0)) + + val areaBorders = arrayOf(saradominStandardFloor, zamorakStandardFloor, saradominUpperFloor, zamorakUpperFloor, saradominFloor, zamorakFloor, battleField, saradominTunnels, zamorakTunnels) + + val saradominPlayers = mutableSetOf() + val zamorakPlayers = mutableSetOf() + + var ticksLeftInGame = 0 + + fun startGame() { + saradominPlayers.addAll(CastleWarsWaitingArea.waitingSaradominPlayers) + zamorakPlayers.addAll(CastleWarsWaitingArea.waitingZamorakPlayers) + CastleWarsWaitingArea.waitingSaradominPlayers.clear() + CastleWarsWaitingArea.waitingZamorakPlayers.clear() + ticksLeftInGame = CastleWars.gameTimeMinutes * ticksPerMinute + + // Put all the players in their respawn area + saradominPlayers.forEach { player -> + player.properties.teleportLocation = CastleWarsRespawnArea.saradominRespawnRoom.randomWalkableLoc + } + zamorakPlayers.forEach { player -> + player.properties.teleportLocation = CastleWarsRespawnArea.zamorakRespawnRoom.randomWalkableLoc + } + } + } + + private fun endGame() { + saradominPlayers.forEach { player -> + player.properties.teleportLocation = CastleWars.lobbyBankArea.randomWalkableLoc + } + zamorakPlayers.forEach { player -> + player.properties.teleportLocation = CastleWars.lobbyBankArea.randomWalkableLoc + } + saradominPlayers.clear() + zamorakPlayers.clear() + } + + override fun defineAreaBorders(): Array { + return areaBorders + } + + override fun areaEnter(entity: Entity) { + val player = entity as? Player ?: return + super.areaEnter(player) + registerTimer (player, spawnTimer("teleblock", (CastleWars.gameTimeMinutes)*60*2)) + + if (saradominPlayers.contains(player)) { + player.interfaceManager.openOverlay(Component(Components.CASTLEWARS_STATUS_OVERLAY_SARADOMIN_58)) + player.equipment.replace(Item(CastleWars.saradominTeamHoodedCloak), 1) + } else if (zamorakPlayers.contains(player)) { + player.interfaceManager.openOverlay(Component(Components.CASTLEWARS_STATUS_OVERLAY_ZAMORAK_59)) + player.equipment.replace(Item(CastleWars.zamorakTeamHoodedCloak), 1) + } + } + + override fun exitArea(player: Player) { + super.exitArea(player) + // Remove player from the players set (whichever one that is) + saradominPlayers.remove(player) + zamorakPlayers.remove(player) + } + + override fun tick() { + ticksLeftInGame-- + if (ticksLeftInGame == 0) { + endGame() + } + } + +} diff --git a/Server/src/main/content/minigame/castlewars/areas/CastleWarsRespawnArea.kt b/Server/src/main/content/minigame/castlewars/areas/CastleWarsRespawnArea.kt new file mode 100644 index 000000000..671564342 --- /dev/null +++ b/Server/src/main/content/minigame/castlewars/areas/CastleWarsRespawnArea.kt @@ -0,0 +1,40 @@ +package rs09.game.content.activity.castlewars.areas + +import core.api.TickListener +import core.game.node.entity.Entity +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 rs09.game.content.activity.castlewars.CastleWars + +/** + * Handles the Castle Wars respawn rooms + */ +class CastleWarsRespawnArea : CastleWarsArea(), TickListener { + + companion object { + val zamorakRespawnRoom: ZoneBorders = ZoneBorders(Location.create(2376, 3127, 1), Location.create(2368, 3135, 1)) + val saradominRespawnRoom: ZoneBorders = ZoneBorders(Location.create(2423, 3080, 1), Location.create(2431, 3072, 1)) + + val zamorakPlayersInRespawnRoom = mutableSetOf() + val saradominPlayersInRespawnRoom = mutableSetOf() + } + + override fun defineAreaBorders(): Array { + return arrayOf(zamorakRespawnRoom, saradominRespawnRoom) + } + + override fun areaEnter(entity: Entity) { + val player = entity as? Player ?: return + if (saradominRespawnRoom.insideBorder(player.location)) { + player.equipment.replace(Item(CastleWars.saradominTeamHoodedCloak), 1) + } else if (zamorakRespawnRoom.insideBorder(player.location)) { + player.equipment.replace(Item(CastleWars.zamorakTeamHoodedCloak), 1) + } + } + + override fun tick() { + } + +} diff --git a/Server/src/main/content/minigame/castlewars/areas/CastleWarsWaitingArea.kt b/Server/src/main/content/minigame/castlewars/areas/CastleWarsWaitingArea.kt new file mode 100644 index 000000000..3bc53ddd2 --- /dev/null +++ b/Server/src/main/content/minigame/castlewars/areas/CastleWarsWaitingArea.kt @@ -0,0 +1,128 @@ +package rs09.game.content.activity.castlewars.areas + +import CastleWarsOverlay +import core.api.* +import core.game.component.Component +import core.game.node.entity.Entity +import core.game.node.entity.player.Player +import core.game.node.item.Item +import core.game.world.map.zone.ZoneBorders +import core.tools.ticksPerMinute +import org.rs09.consts.Components +import rs09.game.content.activity.castlewars.CastleWars + +/** + * Handles the Castle Wars "waiting room" + */ +class CastleWarsWaitingArea : CastleWarsArea(), TickListener { + + companion object { + val zamorakWaitingRoom: ZoneBorders = ZoneBorders(2432, 9510, 2407, 9534, 0) + val saradominWaitingRoom: ZoneBorders = ZoneBorders(2394, 9497, 2369, 9481, 0) + + val areaBorders = arrayOf(zamorakWaitingRoom, saradominWaitingRoom) + + val waitingSaradominPlayers = mutableSetOf() + val waitingZamorakPlayers = mutableSetOf() + } + + override fun defineAreaBorders(): Array { + return areaBorders + } + + override fun areaEnter(entity: Entity) { + val player = entity as? Player ?: return + super.areaEnter(player) + registerTimer(player, spawnTimer("teleblock", (CastleWars.gameCooldownMinutes + CastleWars.gameTimeMinutes)*60*2)) + + // Set team attribute and equip the hooded cloak on the entity based on which waiting room they're in + if (zamorakWaitingRoom.insideBorder(player.location)) { + player.equipment.replace(Item(CastleWars.zamorakTeamHoodedCloak), 1) + waitingZamorakPlayers.add(player) + } else if (saradominWaitingRoom.insideBorder(player.location)) { + player.equipment.replace(Item(CastleWars.saradominTeamHoodedCloak), 1) + waitingSaradominPlayers.add(player) + } + + var transformed = false + if (player.attributes[CastleWars.portalAttribute] == CastleWars.guthixName + && (hasGodItem(player, God.ZAMORAK) || hasGodItem(player, God.SARADOMIN))) { + // https://www.youtube.com/watch?v=Vg3Om-jZNIY + // Turn the player into a sheep + player.appearance.transformNPC(CastleWars.sheep) + // Send the player a chat dialog + sendDialogue(player, "I pity your faith in my brothers. I shall bless you with some time in the most holy of forms; maybe its wisdom will rub off on you and you'll see the error of your ways.") + transformed = true + } + if (player.attributes[CastleWars.portalAttribute] == CastleWars.zamorakName + && hasGodItem(player, God.SARADOMIN) || hasGodItem(player, God.GUTHIX)) + { + // https://youtu.be/hmF-CU9OEY4?t=63 + // Turn the player into an imp! + player.appearance.transformNPC(CastleWars.imp) + // Send the player a chat dialog + sendDialogue(player, "You're wearing objects of my ignorant brothers and you come to me? Such treachery must be rewarded! Enjoy some time in the most mischievous of forms.") + transformed = true + } + if (player.attributes[CastleWars.portalAttribute] == CastleWars.saradominName + && hasGodItem(player, God.ZAMORAK) || hasGodItem(player, God.GUTHIX)) + { + // https://www.youtube.com/watch?v=Vg3Om-jZNIY + // Turn the player into a rabbit! + player.appearance.transformNPC(CastleWars.rabbit) + // Send the player a chat dialog + sendDialogue(player, "You wear objects of my foolish brothers? Perhaps some time spent as the lowliest of forms will help you appreciate the gifts that I can bestow upon my followers.") + transformed = true + } + + if (transformed) { + // Remove the tabs + player.interfaceManager.removeTabs(0, 1, 2, 3, 4, 5, 6, 11, 12) + // Disable player run + player.walkingQueue.isRunDisabled = true + } + + // Remove the join portal attribute + player.removeAttribute(CastleWars.portalAttribute) + + // Open the white text overlay + player.interfaceManager.openOverlay(Component(Components.CASTLEWARS_STATUS_OVERLAY_57)) + } + + override fun exitArea(player: Player) { + super.exitArea(player) + // Remove player from waiting room set (whichever one that is) + waitingSaradominPlayers.remove(player) + waitingZamorakPlayers.remove(player) + } + + override fun tick() { + var nextStart = Int.MAX_VALUE + // The game will start when there's enough players and CastleWarsGameArea.ticksLeftInGame = -5 minutes + if (CastleWarsGameArea.ticksLeftInGame >= 0) { + // A game is going on, display 5 minutes + remaining game time + nextStart = CastleWarsGameArea.ticksLeftInGame + CastleWars.gameCooldownMinutes * ticksPerMinute + } + else if (waitingSaradominPlayers.isEmpty() || waitingZamorakPlayers.isEmpty()) { + // A game is not going and there aren't enough players - Keep the game over time to the current value + CastleWarsGameArea.ticksLeftInGame = -1 + } + else { + // There are enough players - Set nextStart to gameCooldownMinutes + ticksLeftInGame + nextStart = CastleWars.gameCooldownMinutes * ticksPerMinute + CastleWarsGameArea.ticksLeftInGame + } + + for (player in waitingSaradominPlayers + waitingZamorakPlayers) { + CastleWarsOverlay.sendLobbyUpdate( + player, + waitingSaradominPlayers.isNotEmpty() && waitingZamorakPlayers.isNotEmpty() || CastleWarsGameArea.ticksLeftInGame >= 0, + (nextStart - 1) / ticksPerMinute + 1 // Displays max 5 minutes, min 1 minute, seems authentic + ) + } + + if (nextStart <= 0) { + CastleWarsGameArea.startGame() + } + } + +} diff --git a/Server/src/main/content/minigame/clanwars/ClanWarsActivityPlugin.java b/Server/src/main/content/minigame/clanwars/ClanWarsActivityPlugin.java index fc9afe739..8dc12e8a8 100644 --- a/Server/src/main/content/minigame/clanwars/ClanWarsActivityPlugin.java +++ b/Server/src/main/content/minigame/clanwars/ClanWarsActivityPlugin.java @@ -15,7 +15,6 @@ import core.game.node.entity.Entity; import core.game.node.entity.combat.CombatStyle; import core.game.node.entity.impl.PulseManager; import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.ConfigurationManager.Configuration; import core.game.node.scenery.Scenery; import core.game.node.scenery.SceneryBuilder; import core.game.system.communication.ClanEntry; @@ -34,6 +33,8 @@ import core.game.world.update.flag.context.Animation; import core.plugin.Plugin; import core.tools.RandomFunction; +import static core.api.ContentAPIKt.*; + /** * Handles the clan wars activity. * @author Emperor @@ -239,7 +240,7 @@ public final class ClanWarsActivityPlugin extends ActivityPlugin { if (pulse.getTicksPassed() < 200) { value |= (200 - pulse.getTicksPassed()) << 23; } - p.getConfigManager().set(Configuration.CLAN_WAR_DATA, value); + setVarp(p, 1147, value); p.getPacketDispatch().sendString(name, 265, 2); } diff --git a/Server/src/main/content/minigame/duel/AfrahDialogue.kt b/Server/src/main/content/minigame/duel/AfrahDialogue.kt index 610809294..d480392f7 100644 --- a/Server/src/main/content/minigame/duel/AfrahDialogue.kt +++ b/Server/src/main/content/minigame/duel/AfrahDialogue.kt @@ -11,12 +11,12 @@ import core.plugin.Initializable */ @Initializable -class AfrahDialogue : core.game.dialogue.DialoguePlugin { +class AfrahDialogue : DialoguePlugin { private val conversations = arrayOf (0, 4, 10, 11, 15, 17, 20, 22, 23, 24, 29, 32) override fun open(vararg args: Any): Boolean { - player(core.game.dialogue.FacialExpression.ASKING, "Hi!") + player(FacialExpression.ASKING, "Hi!") stage = conversations.random() npc = args[0] as NPC return true @@ -32,135 +32,135 @@ class AfrahDialogue : core.game.dialogue.DialoguePlugin { override fun handle(interfaceId: Int, buttonId: Int): Boolean { when (stage) { 0 -> { - npc(core.game.dialogue.FacialExpression.ASKING, "Ooh. This is exciting!") + npc(FacialExpression.ASKING, "Ooh. This is exciting!") stage++ } 1 -> { - player(core.game.dialogue.FacialExpression.ASKING, "Yup!") + player(FacialExpression.ASKING, "Yup!") stage = 99 } 2 -> { - npc(core.game.dialogue.FacialExpression.ASKING, "I wouldn't want to be the poor guy that has to", "clean up after the duels.") + npc(FacialExpression.ASKING, "I wouldn't want to be the poor guy that has to", "clean up after the duels.") stage++ } 3 -> { - player(core.game.dialogue.FacialExpression.ASKING, "Me neither.") + player(FacialExpression.ASKING, "Me neither.") stage = 99 } 4 -> { - npc(core.game.dialogue.FacialExpression.ASKING, "My son just won his first duel!") + npc(FacialExpression.ASKING, "My son just won his first duel!") stage++ } 5 -> { - player(core.game.dialogue.FacialExpression.ASKING, "Congratulations!") + player(FacialExpression.ASKING, "Congratulations!") stage++ } 6 -> { - npc(core.game.dialogue.FacialExpression.ASKING, "He ripped his opponent in half!") + npc(FacialExpression.ASKING, "He ripped his opponent in half!") stage++ } 7 -> { - player(core.game.dialogue.FacialExpression.ASKING, "That's gotta hurt!") + player(FacialExpression.ASKING, "That's gotta hurt!") stage++ } 8 -> { - npc(core.game.dialogue.FacialExpression.ASKING, "He's only 10 as well!") + npc(FacialExpression.ASKING, "He's only 10 as well!") stage++ } 9 -> { - player(core.game.dialogue.FacialExpression.ASKING, "You gotta start 'em young!") + player(FacialExpression.ASKING, "You gotta start 'em young!") stage = 99 } 10 -> { - npc(core.game.dialogue.FacialExpression.ASKING, "Hmph.") + npc(FacialExpression.ASKING, "Hmph.") stage = 99 } 11 -> { - npc(core.game.dialogue.FacialExpression.ASKING, "My favourite fighter is Mubariz!") + npc(FacialExpression.ASKING, "My favourite fighter is Mubariz!") stage++ } 12 -> { - player(core.game.dialogue.FacialExpression.ASKING, "The guy at the information kiosk?") + player(FacialExpression.ASKING, "The guy at the information kiosk?") stage++ } 13 -> { - npc(core.game.dialogue.FacialExpression.ASKING, "Yeah! He rocks!") + npc(FacialExpression.ASKING, "Yeah! He rocks!") stage++ } 14 -> { - player(core.game.dialogue.FacialExpression.ASKING, "Takes all sorts, I guess.") + player(FacialExpression.ASKING, "Takes all sorts, I guess.") stage = 99 } 15 -> { - npc(core.game.dialogue.FacialExpression.ASKING, "Hi! I'm here to watch the duels!") + npc(FacialExpression.ASKING, "Hi! I'm here to watch the duels!") stage++ } 16 -> { - player(core.game.dialogue.FacialExpression.ASKING, "Me too!") + player(FacialExpression.ASKING, "Me too!") stage = 99 } 17 -> { - npc(core.game.dialogue.FacialExpression.ASKING, "Did you know they think this place dates","back to the second age?!") + npc(FacialExpression.ASKING, "Did you know they think this place dates","back to the second age?!") stage++ } 18 -> { - player(core.game.dialogue.FacialExpression.ASKING, "Really?") + player(FacialExpression.ASKING, "Really?") stage++ } 19 -> { - npc(core.game.dialogue.FacialExpression.ASKING, "Yeah. The guy at the information kiosk was telling me.") + npc(FacialExpression.ASKING, "Yeah. The guy at the information kiosk was telling me.") stage = 99 } 20 -> { - npc(core.game.dialogue.FacialExpression.ANGRY, "Can't you see I'm watching the duels?") + npc(FacialExpression.ANGRY, "Can't you see I'm watching the duels?") stage++ } 21 -> { - player(core.game.dialogue.FacialExpression.SAD, "I'm sorry!") + player(FacialExpression.SAD, "I'm sorry!") stage = 99 } 22 -> { - npc(core.game.dialogue.FacialExpression.ASKING, "Well. This beats doing the shopping!") + npc(FacialExpression.ASKING, "Well. This beats doing the shopping!") stage = 99 } 23 -> { - npc(core.game.dialogue.FacialExpression.ASKING, "Hi!") + npc(FacialExpression.ASKING, "Hi!") stage = 99 } 24 -> { - npc(core.game.dialogue.FacialExpression.ASKING, "Knock knock!") + npc(FacialExpression.ASKING, "Knock knock!") stage++ } 25 -> { - player(core.game.dialogue.FacialExpression.ASKING, "Who's there?") + player(FacialExpression.ASKING, "Who's there?") stage++ } 26 -> { - npc(core.game.dialogue.FacialExpression.ASKING, "Boo!") + npc(FacialExpression.ASKING, "Boo!") stage++ } 27 -> { - player(core.game.dialogue.FacialExpression.ASKING, "Boo who?") + player(FacialExpression.ASKING, "Boo who?") stage++ } 28 -> { - npc(core.game.dialogue.FacialExpression.LAUGH, "Don't cry, it's just me!") + npc(FacialExpression.LAUGH, "Don't cry, it's just me!") stage = 99 } 29 -> { - npc(core.game.dialogue.FacialExpression.ASKING, "Why did the skeleton burp?") + npc(FacialExpression.ASKING, "Why did the skeleton burp?") stage++ } 30 -> { - player(core.game.dialogue.FacialExpression.ASKING, "I don't know?") + player(FacialExpression.ASKING, "I don't know?") stage++ } 31 -> { - npc(core.game.dialogue.FacialExpression.ASKING, "'Cause it didn't have the guts to fart!") + npc(FacialExpression.ASKING, "'Cause it didn't have the guts to fart!") stage = 99 } 32 -> { - npc(core.game.dialogue.FacialExpression.ASKING, "Waaaaassssssuuuuupp?!.") + npc(FacialExpression.ASKING, "Waaaaassssssuuuuupp?!.") stage = 99 } 99 -> end() @@ -168,7 +168,7 @@ class AfrahDialogue : core.game.dialogue.DialoguePlugin { return true } - override fun newInstance(player: Player): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player): DialoguePlugin { return AfrahDialogue(player) } } \ No newline at end of file diff --git a/Server/src/main/content/minigame/duel/ChallengeOptionPlugin.java b/Server/src/main/content/minigame/duel/ChallengeOptionPlugin.java index 5beb213fa..9f0e4dbbc 100644 --- a/Server/src/main/content/minigame/duel/ChallengeOptionPlugin.java +++ b/Server/src/main/content/minigame/duel/ChallengeOptionPlugin.java @@ -4,6 +4,7 @@ import core.game.interaction.OptionHandler; import core.game.node.Node; import core.game.node.entity.player.Player; import core.plugin.Plugin; +import static core.api.ContentAPIKt.*; /** * Handles the challenge option for dueling. @@ -32,7 +33,7 @@ public class ChallengeOptionPlugin extends OptionHandler { player.getInterfaceManager().open(DuelArenaActivity.DUEL_TYPE_SELECT); player.setAttribute("duel:staked", false); player.setAttribute("duel:partner", other); - player.getConfigManager().set(283, 1 << 26); + setVarp(player, 283, 1 << 26); return true; } diff --git a/Server/src/main/content/minigame/duel/DuelArenaActivity.java b/Server/src/main/content/minigame/duel/DuelArenaActivity.java index 08fd76fb3..75f490704 100644 --- a/Server/src/main/content/minigame/duel/DuelArenaActivity.java +++ b/Server/src/main/content/minigame/duel/DuelArenaActivity.java @@ -19,6 +19,8 @@ import core.plugin.Initializable; import core.plugin.ClassScanner; import core.tools.RandomFunction; +import static core.api.ContentAPIKt.*; + /** * Handles the Duel Arena activity. * @author Emperor @@ -76,7 +78,7 @@ public final class DuelArenaActivity extends ActivityPlugin { Player player = (Player) e; player.getInterfaceManager().openOverlay(OVERLAY); player.getInteraction().set(CHALLENGE_OPTION); - player.getConfigManager().set(286, 0); + setVarp(player, 286, 0); } return super.enter(e); } diff --git a/Server/src/main/content/minigame/duel/DuelComponentPlugin.java b/Server/src/main/content/minigame/duel/DuelComponentPlugin.java index b70f73c8f..38dc31f54 100644 --- a/Server/src/main/content/minigame/duel/DuelComponentPlugin.java +++ b/Server/src/main/content/minigame/duel/DuelComponentPlugin.java @@ -7,6 +7,8 @@ import core.game.node.entity.player.Player; import core.game.world.GameWorld; import core.plugin.Plugin; +import static core.api.ContentAPIKt.*; + /** * Handles the duel arena components. * @author Vexia @@ -60,7 +62,7 @@ public class DuelComponentPlugin extends ComponentPlugin { return false; } player.setAttribute("duel:staked", staked); - player.getConfigManager().set(283, (staked ? 2 : 1) << 26); + setVarp(player, 283, (staked ? 2 : 1) << 26); break; } return true; diff --git a/Server/src/main/content/minigame/duel/DuelSession.java b/Server/src/main/content/minigame/duel/DuelSession.java index d278bf6b9..51b4a6d9b 100644 --- a/Server/src/main/content/minigame/duel/DuelSession.java +++ b/Server/src/main/content/minigame/duel/DuelSession.java @@ -12,7 +12,6 @@ import core.game.container.impl.EquipmentContainer; import core.game.node.entity.player.Player; import core.game.node.entity.player.info.LogType; import core.game.node.entity.player.info.login.PlayerParser; -import core.game.node.entity.state.EntityState; import core.game.node.item.Item; import core.plugin.Plugin; import core.tools.RandomFunction; @@ -205,8 +204,8 @@ public final class DuelSession extends ComponentPlugin { */ public void heal(Player p) { p.fullRestore(); - if (p.getStateManager().hasState(EntityState.POISONED)) { - p.getStateManager().remove(EntityState.POISONED); + if (isPoisoned(p)) { + curePoison(p); } p.getSkills().restore(); } @@ -230,7 +229,7 @@ public final class DuelSession extends ComponentPlugin { * @param player The player. */ private void openRules(Player player, Player opponent) { - player.getConfigManager().set(286, 0); + setVarp(player, 286, 0); if (staked) { StakeContainer container = new StakeContainer(player, this); if (player == this.player) { @@ -249,7 +248,7 @@ public final class DuelSession extends ComponentPlugin { player.getPacketDispatch().sendString(Integer.toString(opponent.getProperties().getCurrentCombatLevel()), 637, 18); player.getPacketDispatch().sendString("", 637, 45); } - player.getConfigManager().set(286, 0); + setVarp(player, 286, 0); } /** @@ -264,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(); @@ -495,8 +494,8 @@ public final class DuelSession extends ComponentPlugin { value |= 1 << rules[i].getConfigIndex(); } } - player.getConfigManager().set(286, value); - other.getConfigManager().set(286, value); + setVarp(player, 286, value); + setVarp(other, 286, value); } /** @@ -1024,4 +1023,4 @@ public final class DuelSession extends ComponentPlugin { public void setFightState(int fightState) { this.fightState = fightState; } -} \ No newline at end of file +} diff --git a/Server/src/main/content/minigame/fishingtrawler/FishingTrawlerActivity.kt b/Server/src/main/content/minigame/fishingtrawler/FishingTrawlerActivity.kt index 1a2a64f1c..1522316c6 100644 --- a/Server/src/main/content/minigame/fishingtrawler/FishingTrawlerActivity.kt +++ b/Server/src/main/content/minigame/fishingtrawler/FishingTrawlerActivity.kt @@ -1,5 +1,6 @@ package content.minigame.fishingtrawler +import core.api.MapArea import core.game.node.entity.player.Player import core.game.system.task.Pulse import core.game.world.GameWorld @@ -10,6 +11,7 @@ import core.plugin.Initializable import core.tools.ticksToSeconds import core.game.activity.ActivityManager import core.game.activity.ActivityPlugin +import core.game.world.map.zone.ZoneBorders import core.tools.colorize @@ -17,13 +19,14 @@ import core.tools.colorize * Handles the fishing trawler "waiting room" * @author Ceikry */ -private val WAIT_TIME = if(GameWorld.settings?.isDevMode == true) 10 else 203 +private val WAIT_TIME = if(GameWorld.settings?.isDevMode == true) 30 else 203 private val waitingPlayers = ArrayList() +private val ftWaitingArea = arrayOf(ZoneBorders(2668, 3165, 2675, 3184)) private val sessions = ArrayList() private var activity: FishingTrawlerActivity? = null private var nextStart = GameWorld.ticks + WAIT_TIME @Initializable -class FishingTrawlerActivity : ActivityPlugin("fishing trawler",false,false,true,ZoneRestriction.CANNON,ZoneRestriction.FIRES,ZoneRestriction.FOLLOWERS,ZoneRestriction.RANDOM_EVENTS) { +class FishingTrawlerActivity : ActivityPlugin("fishing trawler",false,false,true,ZoneRestriction.CANNON,ZoneRestriction.FIRES,ZoneRestriction.FOLLOWERS,ZoneRestriction.RANDOM_EVENTS), MapArea { init { activity = this @@ -86,4 +89,12 @@ class FishingTrawlerActivity : ActivityPlugin("fishing trawler",false,false,true override fun getSpawnLocation(): Location { return Location.create(2667, 3161, 0) } + + override fun defineAreaBorders(): Array { + return ftWaitingArea + } + + override fun getRestrictions(): Array { + return arrayOf(ZoneRestriction.RANDOM_EVENTS, ZoneRestriction.TELEPORT) + } } \ No newline at end of file diff --git a/Server/src/main/content/minigame/fishingtrawler/FishingTrawlerInteractionHandler.kt b/Server/src/main/content/minigame/fishingtrawler/FishingTrawlerInteractionHandler.kt index eff8b243c..d619d6539 100644 --- a/Server/src/main/content/minigame/fishingtrawler/FishingTrawlerInteractionHandler.kt +++ b/Server/src/main/content/minigame/fishingtrawler/FishingTrawlerInteractionHandler.kt @@ -1,9 +1,9 @@ package content.minigame.fishingtrawler +import core.api.clearLogoutListener import core.game.activity.ActivityManager 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.task.Pulse import core.game.world.map.Location @@ -14,7 +14,6 @@ import core.game.interaction.InteractionListener import core.game.interaction.IntType import core.game.system.command.sets.FISHING_TRAWLER_LEAKS_PATCHED import core.game.system.command.sets.STATS_BASE -import core.tools.colorize import kotlin.math.ceil /** @@ -75,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 } @@ -89,6 +88,8 @@ class FishingTrawlerInteractionHandler : InteractionListener { player.dialogueInterpreter.sendDialogue("You climb onto the floating barrel and begin to kick your way to the","shore.","You make it to the shore tired and weary.") player.appearance.setDefaultAnimations() player.appearance.sync() + clearLogoutListener(player, "ft-logout") + player.locks.unlockTeleport() return@on true } @@ -161,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 @@ -172,16 +172,11 @@ class NetLootDialogue(player: Player? = null): core.game.dialogue.DialoguePlugin override fun handle(interfaceId: Int, buttonId: Int): Boolean { val level = player.skills.getLevel(Skills.FISHING) when(buttonId){ - 1 -> TrawlerLoot.getLoot(level, rolls, true).forEach { - if(!player.bank.add(it)) GroundItemManager.create(it,player) - } - 2 -> TrawlerLoot.getLoot(level, rolls, false).forEach { - if(!player.bank.add(it)) GroundItemManager.create(it,player) - } + 1 -> TrawlerLoot.addLootAndMessage(player, level, rolls, true) + 2 -> TrawlerLoot.addLootAndMessage(player, level, rolls, false) } - player.sendMessage(colorize("%RYour reward has been sent to your bank.")) 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/FishingTrawlerOverlay.kt b/Server/src/main/content/minigame/fishingtrawler/FishingTrawlerOverlay.kt index 16a714c9c..9b9ad7e21 100644 --- a/Server/src/main/content/minigame/fishingtrawler/FishingTrawlerOverlay.kt +++ b/Server/src/main/content/minigame/fishingtrawler/FishingTrawlerOverlay.kt @@ -1,6 +1,7 @@ package content.minigame.fishingtrawler import core.game.node.entity.player.Player +import core.api.* private const val configIndex = 391 private const val interfaceID = 366 @@ -17,10 +18,10 @@ private const val timeChild = 30 object FishingTrawlerOverlay { @JvmStatic fun sendUpdate(player: Player, waterPercent: Int, NetRipped: Boolean, fishCaught: Int, timeLeft: Int){ - player.configManager.set(configIndex,waterPercent) + setVarp(player, configIndex, waterPercent) player.packetDispatch.sendInterfaceConfig(interfaceID,if(NetRipped) netRippedChild else netOkayChild, false) player.packetDispatch.sendInterfaceConfig(interfaceID,if(NetRipped) netOkayChild else netRippedChild, true) player.packetDispatch.sendString("${if(fishCaught > 0) fishCaught else "Nothing"}", interfaceID, fishChild) player.packetDispatch.sendString("$timeLeft Minutes", interfaceID, timeChild) } -} \ No newline at end of file +} diff --git a/Server/src/main/content/minigame/fishingtrawler/FishingTrawlerSession.kt b/Server/src/main/content/minigame/fishingtrawler/FishingTrawlerSession.kt index 25750b873..3cabb8d72 100644 --- a/Server/src/main/content/minigame/fishingtrawler/FishingTrawlerSession.kt +++ b/Server/src/main/content/minigame/fishingtrawler/FishingTrawlerSession.kt @@ -1,15 +1,14 @@ package content.minigame.fishingtrawler -import core.api.LogoutListener import core.api.MapArea import core.api.getRegionBorders +import core.api.* import core.game.component.Component import core.game.node.entity.Entity import core.game.node.scenery.Scenery import core.game.node.scenery.SceneryBuilder import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player -import core.game.node.entity.state.EntityState import core.game.node.item.Item import core.game.system.task.Pulse import core.game.world.GameWorld @@ -24,10 +23,10 @@ import org.rs09.consts.Items import core.game.system.command.sets.FISHING_TRAWLER_GAMES_WON import core.game.system.command.sets.FISHING_TRAWLER_SHIPS_SANK import core.game.system.command.sets.STATS_BASE -import core.tools.SystemLogger import core.tools.secondsToTicks import core.tools.ticksToSeconds import java.util.concurrent.TimeUnit +import kotlin.math.ceil import kotlin.random.Random @@ -43,7 +42,7 @@ private const val HOLE_SOUTH_Y = 23 private const val LEAKING_ID = 2167 private const val PATCHED_ID = 2168 -class FishingTrawlerSession(val activity: FishingTrawlerActivity? = null) : LogoutListener, MapArea { +class FishingTrawlerSession(val activity: FishingTrawlerActivity? = null) : MapArea { constructor(region: DynamicRegion, activity: FishingTrawlerActivity) : this(activity) {this.region = region; this.base = region.baseLocation} var players: ArrayList = ArrayList() var netRipped = false @@ -80,7 +79,12 @@ class FishingTrawlerSession(val activity: FishingTrawlerActivity? = null) : Logo updateOverlay(player) player.properties.teleportLocation = base.transform(36,24,0) player.setAttribute("ft-session",this) - player.stateManager.set(EntityState.TELEBLOCK,timeLeft) + registerLogoutListener(player, "ft-logout") { + val session = player.getAttribute("ft-session",null) ?: return@registerLogoutListener + player.location = Location.create(2667, 3161, 0) + session.players.remove(player) + player.locks.unlockTeleport() + } } zone.register(getRegionBorders(region.id)) } @@ -104,7 +108,6 @@ class FishingTrawlerSession(val activity: FishingTrawlerActivity? = null) : Logo player.appearance.setAnimations(Animation(188)) player.properties.teleportLocation = session.base.transform(36,24,0) player.incrementAttribute("/save:$STATS_BASE:$FISHING_TRAWLER_SHIPS_SANK") - player.stateManager.remove(EntityState.TELEBLOCK) } return true } @@ -136,6 +139,9 @@ class FishingTrawlerSession(val activity: FishingTrawlerActivity? = null) : Logo session.isActive = false session.swapBoatType(7755) session.zone.unregister(getRegionBorders(session.region.id)) + for(player in session.players) { + player.locks.lockTeleport(1000000) + } } if(RandomFunction.random(100) <= 9){ @@ -148,12 +154,19 @@ class FishingTrawlerSession(val activity: FishingTrawlerActivity? = null) : Logo 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)) } for(player in session.players){ session.updateOverlay(player) + if(session.timeLeft <= 1) { + lockInteractions(player, 2) + } } session.tickMurphy() return !session.isActive @@ -278,27 +291,21 @@ class FishingTrawlerSession(val activity: FishingTrawlerActivity? = null) : Logo FishingTrawlerOverlay.sendUpdate(player, ((waterAmount / 500.0) * 100).toInt(), netRipped, fishAmount, TimeUnit.SECONDS.toMinutes(ticksToSeconds(timeLeft).toLong()).toInt() + 1) } - override fun logout(player: Player) { - val session = player.getAttribute("ft-session",null) ?: return - player.location = Location.create(2667, 3161, 0) - session.players.remove(player) - } - override fun defineAreaBorders(): Array { return arrayOf() } override fun getRestrictions(): Array { - return arrayOf(ZoneRestriction.CANNON, ZoneRestriction.FIRES, ZoneRestriction.RANDOM_EVENTS) + return arrayOf(ZoneRestriction.CANNON, ZoneRestriction.FIRES, ZoneRestriction.RANDOM_EVENTS, ZoneRestriction.TELEPORT) } override fun areaEnter(entity: Entity) { super.areaEnter(entity) - SystemLogger.logInfo(this::class.java, "ENTERED FTZ") + log(this::class.java, Log.FINE, "ENTERED FTZ") } override fun areaLeave(entity: Entity, logout: Boolean) { super.areaLeave(entity, logout) - SystemLogger.logInfo(this::class.java, "EXITED FTZ") + log(this::class.java, Log.FINE, "EXITED FTZ") } } diff --git a/Server/src/main/content/minigame/fishingtrawler/TrawlerLoot.kt b/Server/src/main/content/minigame/fishingtrawler/TrawlerLoot.kt index 0f9058f58..87ed06dbe 100644 --- a/Server/src/main/content/minigame/fishingtrawler/TrawlerLoot.kt +++ b/Server/src/main/content/minigame/fishingtrawler/TrawlerLoot.kt @@ -1,20 +1,30 @@ 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 import core.tools.RandomFunction +import core.tools.colorize import org.rs09.consts.Items /** * Rolls/stores the loot table for fishing trawler - * @author Ceikry + * @author Ceikry, RiL */ object TrawlerLoot { - val junkItems = arrayOf(Items.BROKEN_ARMOUR_698, Items.BROKEN_ARROW_687, Items.OLD_BOOT_685, Items.BROKEN_GLASS_1469, Items.BROKEN_STAFF_689, Items.BUTTONS_688,Items.DAMAGED_ARMOUR_697, Items.RUSTY_SWORD_686, Items.EMPTY_POT_1931, Items.OYSTER_407) - val trawlerFish = arrayOf(Fish.MANTA_RAY, Fish.SEA_TURTLE, Fish.SHARK, Fish.SWORDFISH, Fish.LOBSTER, Fish.TUNA, Fish.ANCHOVIE, Fish.SARDINE, Fish.SHRIMP) + private val junkItems = arrayOf(Items.BROKEN_ARMOUR_698, Items.BROKEN_ARROW_687, Items.OLD_BOOT_685, Items.BROKEN_GLASS_1469, Items.BROKEN_STAFF_689, Items.BUTTONS_688,Items.DAMAGED_ARMOUR_697, Items.RUSTY_SWORD_686, Items.EMPTY_POT_1931, Items.OYSTER_407) + private val trawlerFish = arrayOf(Fish.MANTA_RAY, Fish.SEA_TURTLE, Fish.SHARK, Fish.SWORDFISH, Fish.LOBSTER, Fish.TUNA, Fish.ANCHOVIE, Fish.SARDINE, Fish.SHRIMP) + private val trawlerFishIds = arrayOf(Items.RAW_MANTA_RAY_389, Items.RAW_SEA_TURTLE_395, Items.RAW_SHARK_383, Items.RAW_SWORDFISH_371, Items.RAW_LOBSTER_377, Items.RAW_TUNA_359, Items.RAW_ANCHOVIES_321, Items.RAW_SARDINE_327, Items.RAW_SHRIMPS_317) + private val trawlerMisc = arrayOf(Items.LOOP_HALF_OF_A_KEY_987, Items.TOOTH_HALF_OF_A_KEY_985, Items.CASKET_405, Items.PIRATES_HAT_2651, Items.LUCKY_CUTLASS_7140) - fun rollTrawlerFish(fishLevel: Int): Item { + private fun rollTrawlerFish(fishLevel: Int): Item { while(true) { for(f in trawlerFish) { if(f.level > fishLevel) { @@ -25,7 +35,7 @@ object TrawlerLoot { //val chance = RandomFunction.getSkillSuccessChance(lo, hi, fishLevel) val chance = (fishLevel.toDouble() - 15.0)*((hi - lo) / (99.0 - 15.0)) + lo if(RandomFunction.random(0.0, 1.0) < chance) { - return f.item + return Item(f.id) } } } @@ -37,23 +47,51 @@ object TrawlerLoot { for(i in 0 until rolls){ val item = RandomFunction.rollWeightedChanceTable(listOf(*lootTable)) if(item.id == 0) { - val item = rollTrawlerFish(fishLevel) - loot.add(item) - } else { + loot.add(rollTrawlerFish(fishLevel)) + } else if (!skipJunk || item.id !in junkItems) { loot.add(item) } } - if(skipJunk){ - val removeList = ArrayList() - for(item in loot){ - if(junkItems.contains(item.id)) removeList.add(item) - } - loot.removeAll(removeList) - } return loot } - val lootTable = arrayOf( + /** + * Add Fishing trawler loot to [player] bank. Send message with summary of the loot + */ + @JvmStatic + fun addLootAndMessage(player: Player, fishLevel: Int, rolls: Int, skipJunk: Boolean) { + if (rolls < 1) return + val frequencyList = listOf>(HashMap(), HashMap(), HashMap()) + getLoot(fishLevel, rolls, skipJunk).forEach { + when (it.id) { + 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) + } + } + // 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 = ".") { "${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( WeightedChanceItem(0,1,1430), WeightedChanceItem(Items.BROKEN_ARROW_687,1,70), WeightedChanceItem(Items.BROKEN_GLASS_1469,1,70), diff --git a/Server/src/main/content/minigame/fog/FOGRewardsInterface.kt b/Server/src/main/content/minigame/fog/FOGRewardsInterface.kt index d775e27ca..d09b39bdc 100644 --- a/Server/src/main/content/minigame/fog/FOGRewardsInterface.kt +++ b/Server/src/main/content/minigame/fog/FOGRewardsInterface.kt @@ -1,5 +1,6 @@ package content.minigame.fog +import core.api.log import core.cache.def.impl.ItemDefinition import core.game.component.Component import core.game.component.ComponentDefinition @@ -8,6 +9,7 @@ import core.game.node.entity.player.Player import core.game.node.item.Item import core.plugin.Initializable import core.plugin.Plugin +import core.tools.Log import core.tools.SystemLogger @Initializable @@ -52,7 +54,7 @@ class FOGRewardsInterface : ComponentPlugin(){ 176 -> choice = Air_Gloves 181 -> choice = Water_Gloves 186 -> choice = Earth_Gloves - else -> SystemLogger.logWarn(this::class.java, "Unhandled button ID for FOG interface: $button").also { return true } + else -> log(this::class.java, Log.WARN, "Unhandled button ID for FOG interface: $button").also { return true } } handleOpcode(choice,opcode,player!!) return true diff --git a/Server/src/main/content/minigame/fog/FogInteractionHandler.kt b/Server/src/main/content/minigame/fog/FogInteractionHandler.kt index 9eea115d1..8f7b2e870 100644 --- a/Server/src/main/content/minigame/fog/FogInteractionHandler.kt +++ b/Server/src/main/content/minigame/fog/FogInteractionHandler.kt @@ -26,8 +26,8 @@ class FogInteractionHandler : PluginInteraction(30204, 30203){ override fun pulse(): Boolean { player.faceLocation(obj.location) when(obj.id) { - 30204 -> core.game.global.action.ClimbActionHandler.climbLadder(player, obj, "climb-down") - 30203 -> core.game.global.action.ClimbActionHandler.climbLadder(player,obj,"climb-up") + 30204 -> ClimbActionHandler.climbLadder(player, obj, "climb-down") + 30203 -> ClimbActionHandler.climbLadder(player,obj,"climb-up") } return true } diff --git a/Server/src/main/content/minigame/gnomecooking/crunchies/GnomeCrunchyInterface.kt b/Server/src/main/content/minigame/gnomecooking/crunchies/GnomeCrunchyInterface.kt index 99f8e445e..4785ef3d3 100644 --- a/Server/src/main/content/minigame/gnomecooking/crunchies/GnomeCrunchyInterface.kt +++ b/Server/src/main/content/minigame/gnomecooking/crunchies/GnomeCrunchyInterface.kt @@ -50,7 +50,7 @@ class GnomeCrunchyInterface : ComponentPlugin() { for(item in crunchy.requiredItems){ if(!player.inventory.containsItem(item)){ hasAll = false - break; + break } } diff --git a/Server/src/main/content/minigame/mta/AlchemyGuardianDialogue.java b/Server/src/main/content/minigame/mta/AlchemyGuardianDialogue.java index 3fed6d0e2..aa09d28ab 100644 --- a/Server/src/main/content/minigame/mta/AlchemyGuardianDialogue.java +++ b/Server/src/main/content/minigame/mta/AlchemyGuardianDialogue.java @@ -73,7 +73,7 @@ public class AlchemyGuardianDialogue extends DialoguePlugin { stage++; break; case 12: - npc("around Keldagrim. Keep an eye on the cost of each", "items as these will change from time-to-time, as will the", "location of the items. Occasionally one of the items will", "be indicated as costing no runestones to convert to"); + npc("around Gielinor. Keep an eye on the cost of each", "item as these will change from time-to-time, as will the", "location of the items. Occasionally one of the items will", "be indicated as costing no runestones to convert to"); stage++; break; case 13: diff --git a/Server/src/main/content/minigame/mta/EnchantSpell.kt b/Server/src/main/content/minigame/mta/EnchantSpell.kt index 37a61430b..07cebf2a6 100644 --- a/Server/src/main/content/minigame/mta/EnchantSpell.kt +++ b/Server/src/main/content/minigame/mta/EnchantSpell.kt @@ -14,6 +14,7 @@ import core.game.world.update.flag.context.Animation import core.game.world.update.flag.context.Graphics import core.plugin.Plugin import org.rs09.consts.Items +import core.api.* /** * Represents the enchant spells. @@ -46,11 +47,12 @@ class EnchantSpell : MagicSpell { if (target !is Item || entity !is Player) { return false } - entity.interfaceManager.setViewedTab(6) + //entity.interfaceManager.setViewedTab(6) val enchanted = jewellery?.getOrDefault(target.id,null) if (enchanted == null) { entity.packetDispatch.sendMessage("You can't use this spell on this item.") + entity.interfaceManager.setViewedTab(6) return false } if (!meetsRequirements(entity, true, true)) { @@ -86,7 +88,14 @@ class EnchantSpell : MagicSpell { } } if (pizazz != 0) { - content.minigame.mta.impl.EnchantingZone.ZONE.incrementPoints(entity, content.minigame.mta.MTAType.ENCHANTERS.ordinal, pizazz) + content.minigame.mta.impl.EnchantingZone.ZONE.incrementPoints(entity, MTAType.ENCHANTERS.ordinal, pizazz) + } + } + + // Snowscape: if there are more items to enchant, run the spell again + if (entity.inventory.containsAtLeastOneItem(target)) { + runTask(entity, 2){ + castSpell(entity as Player, super.book, super.spellId, target) } } return true @@ -113,6 +122,7 @@ class EnchantSpell : MagicSpell { Items.SAPPHIRE_NECKLACE_1656 to Item(Items.GAMES_NECKLACE8_3853), Items.SAPPHIRE_AMULET_1694 to Item(Items.AMULET_OF_MAGIC_1727), Items.SAPPHIRE_BRACELET_11072 to Item(Items.BRACELET_OF_CLAY_11074), + Items.BLUE_DRAGONHIDE_1751 to Item(Items.PLAIN_SATCHEL_10877), //Begin MTA-specific enchantments Items.CUBE_6899 to Item(Items.ORB_6902), Items.CYLINDER_6898 to Item(Items.ORB_6902), @@ -132,6 +142,7 @@ class EnchantSpell : MagicSpell { Items.EMERALD_NECKLACE_1658 to Item(Items.BINDING_NECKLACE_5521), Items.EMERALD_AMULET_1696 to Item(Items.AMULET_OF_DEFENCE_1729), Items.EMERALD_BRACELET_11076 to Item(Items.CASTLEWAR_BRACE3_11079), + Items.GREEN_DRAGONHIDE_1753 to Item(Items.GREEN_SATCHEL_10878), //Begin MTA-Specific Enchantments Items.CUBE_6899 to Item(Items.ORB_6902), Items.CYLINDER_6898 to Item(Items.ORB_6902), @@ -151,6 +162,7 @@ class EnchantSpell : MagicSpell { Items.RUBY_NECKLACE_1660 to Item(Items.DIGSITE_PENDANT_5_11194), Items.RUBY_AMULET_1698 to Item(Items.AMULET_OF_STRENGTH_1725), Items.RUBY_BRACELET_11085 to Item(Items.INOCULATION_BRACE_11088), + Items.RED_DRAGONHIDE_1749 to Item(Items.RED_SATCHEL_10879), //Begin MTA-Specific Enchantments Items.CUBE_6899 to Item(Items.ORB_6902), Items.CYLINDER_6898 to Item(Items.ORB_6902), @@ -169,6 +181,7 @@ class EnchantSpell : MagicSpell { Items.DIAMOND_NECKLACE_1662 to Item(Items.PHOENIX_NECKLACE_11090), Items.DIAMOND_AMULET_1700 to Item(Items.AMULET_OF_POWER_1731), Items.DIAMOND_BRACELET_11092 to Item(Items.FORINTHRY_BRACE5_11095), + Items.DAGANNOTH_HIDE_6155 to Item(Items.GOLD_SATCHEL_10881), //Begin MTA-Specific Enchantments Items.CUBE_6899 to Item(Items.ORB_6902), Items.CYLINDER_6898 to Item(Items.ORB_6902), @@ -188,6 +201,7 @@ class EnchantSpell : MagicSpell { 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.SUQAH_HIDE_9080 to Item(Items.RUNE_SATCHEL_10882), //Begin MTA-Specific Enchantments Items.CUBE_6899 to Item(Items.ORB_6902), Items.CYLINDER_6898 to Item(Items.ORB_6902), @@ -207,6 +221,7 @@ class EnchantSpell : MagicSpell { Items.ONYX_NECKLACE_6577 to Item(Items.BERSERKER_NECKLACE_11128), Items.ONYX_AMULET_6581 to Item(Items.AMULET_OF_FURY_6585), Items.ONYX_BRACELET_11130 to Item(Items.REGEN_BRACELET_11133), + Items.BLACK_DRAGONHIDE_1747 to Item(Items.BLACK_SATCHEL_10880), //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/GraveyardGuardianDialogue.java b/Server/src/main/content/minigame/mta/GraveyardGuardianDialogue.java index 2e79c1657..05d5a7338 100644 --- a/Server/src/main/content/minigame/mta/GraveyardGuardianDialogue.java +++ b/Server/src/main/content/minigame/mta/GraveyardGuardianDialogue.java @@ -67,7 +67,7 @@ public class GraveyardGuardianDialogue extends DialoguePlugin { } break; case 10: - npc("Have you noticed all the bones around the room? These", "are teleported here from all over Keldagrim to help", "clean up the landscape of countless bones left behind", "from combat. What better use for these bones than to"); + npc("Have you noticed all the bones around the room? These", "are teleported here from all over Gielinor to help", "clean up the landscape of countless bones left behind", "from combat. What better use for these bones than to"); stage++; break; case 11: diff --git a/Server/src/main/content/minigame/mta/MTAListeners.kt b/Server/src/main/content/minigame/mta/MTAListeners.kt index 5c823d98c..bc656348b 100644 --- a/Server/src/main/content/minigame/mta/MTAListeners.kt +++ b/Server/src/main/content/minigame/mta/MTAListeners.kt @@ -2,6 +2,7 @@ package content.minigame.mta import content.global.skill.magic.SpellListener import content.global.skill.magic.spellconsts.Modern +import core.api.playAudio import core.game.node.item.Item import core.game.world.map.Direction import core.game.world.update.flag.context.Animation @@ -10,6 +11,7 @@ import org.rs09.consts.Items import org.rs09.consts.NPCs import core.game.interaction.InteractionListener import core.game.interaction.IntType +import org.rs09.consts.Sounds class MTAListeners : InteractionListener { override fun defineListeners() { @@ -18,7 +20,7 @@ class MTAListeners : InteractionListener { return@on true } setDest(IntType.NPC, intArrayOf(NPCs.MAZE_GUARDIAN_3102), "talk-to") { player, node -> - return@setDest node.location.transform(Direction.getDirection(player.location, node.location), -1); + return@setDest node.location.transform(Direction.getDirection(player.location, node.location), -1) } } } @@ -55,7 +57,7 @@ class MTASpellListeners : SpellListener("modern"){ p.lock(3) p.visualize(HIGH_ALCH_ANIM,HIGH_ALCH_GFX) if(p.inventory.remove(Item(item.id,1))){ - p.audioManager.send(97) + playAudio(p, Sounds.HIGH_ALCHEMY_97) if (coins.amount != 0) { p.inventory.add(coins) } @@ -90,7 +92,7 @@ class MTASpellListeners : SpellListener("modern"){ p.lock(3) p.visualize(LOW_ALCH_ANIM,LOW_ALCH_GFX) if(p.inventory.remove(Item(item.id,1))){ - p.audioManager.send(97) + playAudio(p, Sounds.HIGH_ALCHEMY_97) if (coins.amount != 0) { p.inventory.add(coins) } diff --git a/Server/src/main/content/minigame/mta/MTAShop.java b/Server/src/main/content/minigame/mta/MTAShop.java index beb64a1b2..d8db649a8 100644 --- a/Server/src/main/content/minigame/mta/MTAShop.java +++ b/Server/src/main/content/minigame/mta/MTAShop.java @@ -32,8 +32,8 @@ public class MTAShop { /** * The prices. */ - private static final int[][] PRICES = new int[][] { { 30, 30, 300, 30 }, { 60, 60, 600, 60 }, { 150, 200, 1500, 150 }, { 240, 240, 2400, 240 }, { 400, 450, 4000, 400 }, { 350, 400, 3000, 350 }, { 120, 120, 1200, 120 }, { 175, 225, 1500, 175 }, { 450, 500, 5000, 450 }, { 500, 550, 6000, 500 }, { 200, 300, 2000, 200 }, { 1, 1, 15, 1 }, { 1, 1, 15, 1 }, { 1, 1, 15, 1 }, { 1, 1, 15, 1 }, { 1, 1, 15, 1 }, { 1, 1, 15, 1 }, { 0, 0, 5, 0 }, { 0, 1, 5, 1 }, { 0, 1, 0, 1 }, { 2, 1, 20, 1 }, { 2, 0, 0, 0 }, { 2, 2, 25, 2 }, { 2, 2, 25, 2 } }; - + private static final int[][] PRICES = new int[][] { { 6, 6, 60, 6 }, { 12, 12, 120, 12 }, { 30, 40, 300, 30 }, { 48, 48, 480, 48 }, { 80, 90, 800, 80 }, { 70, 80, 600, 70 }, { 24, 24, 240, 24 }, { 35, 45, 300, 35 }, { 90, 100, 1000, 90 }, { 100, 110, 1200, 100 }, { 40, 60, 400, 40 }, { 1, 1, 15, 1 }, { 1, 1, 15, 1 }, { 1, 1, 15, 1 }, { 1, 1, 15, 1 }, { 1, 1, 15, 1 }, { 1, 1, 15, 1 }, { 0, 0, 5, 0 }, { 0, 1, 5, 1 }, { 0, 1, 0, 1 }, { 2, 1, 20, 1 }, { 2, 0, 0, 0 }, { 2, 2, 25, 2 }, { 2, 2, 25, 2 } }; + /** * The container. */ diff --git a/Server/src/main/content/minigame/mta/TelekineticGrabSpell.java b/Server/src/main/content/minigame/mta/TelekineticGrabSpell.java index 7e1ac3530..9c3174d1f 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; @@ -22,6 +23,12 @@ import core.game.world.update.flag.context.Graphics; import core.plugin.Plugin; import core.game.global.action.PickupHandler; import core.game.world.GameWorld; +import org.rs09.consts.Sounds; + +import core.game.world.map.Location; +import core.game.interaction.MovementPulse; + +import static core.api.ContentAPIKt.*; /** * Represents the telekenitic grab spell. @@ -96,13 +103,28 @@ public final class TelekineticGrabSpell extends MagicSpell { @Override public boolean cast(final Entity entity, final Node target) { + if (!(target instanceof GroundItem)) + return false; final GroundItem ground = (GroundItem) target; if (!canCast(entity, ground)) { return false; } + + //Snowscape: pick up all items of the target type within a square centered on the target + int radius = 10; + for (int x = target.getLocation().getX() - radius; x <= target.getLocation().getX() + radius; x++) { + for (int y = target.getLocation().getY() - radius; y <= target.getLocation().getY() + radius; y++) { + GroundItem targetItem = GroundItemManager.get(target.getId(), new Location(x,y), (Player) entity); + if (targetItem != null) { + GameWorld.getPulser().submit(getGrabPulse(entity, targetItem)); + visualize(entity, targetItem); + } + } + } + entity.lock(2); - visualize(entity, target); - GameWorld.getPulser().submit(getGrabPulse(entity, ground)); + //visualize(entity, target); + //GameWorld.getPulser().submit(getGrabPulse(entity, ground)); return true; } @@ -141,13 +163,17 @@ public final class TelekineticGrabSpell extends MagicSpell { player.getPacketDispatch().sendMessage("Too late!"); return true; } - player.getAudioManager().send(3008); + playAudio(player, Sounds.VULNERABILITY_IMPACT_3008); if (!teleZone) { - player.getInventory().add(new Item(g.getId(), g.getAmount(), g.getCharge())); + if (hasSpaceFor(player, g)) { + player.getInventory().add(new Item(g.getId(), g.getAmount(), g.getCharge())); + } else { + return true; + } } else { TelekineticZone zone = TelekineticZone.getZone(player); zone.moveStatue(); - player.lock(getDelay()); + //player.lock(getDelay()); } player.getPacketDispatch().sendPositionedGraphics(END_GRAPHIC, ground.getLocation()); } @@ -171,8 +197,32 @@ 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)) { + //Move to the target, stopping and recasting when within line of sight. + player.getPulseManager().run(new MovementPulse(player, item) { + @Override + public boolean pulse() { + castSpell(player, SpellBook.MODERN, SPELL_ID, item); + return true; + } + + @Override + public boolean update() { + if (CombatSwingHandler.isProjectileClipped(player, item, false)) { + player.getWalkingQueue().reset(); + pulse(); + stop(); + return true; + } + return super.update(); + } + }); + + //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/OutpostSquireDialogue.java b/Server/src/main/content/minigame/pestcontrol/OutpostSquireDialogue.java index 9ca53a0f4..b6d2e5b25 100644 --- a/Server/src/main/content/minigame/pestcontrol/OutpostSquireDialogue.java +++ b/Server/src/main/content/minigame/pestcontrol/OutpostSquireDialogue.java @@ -74,11 +74,11 @@ public final class OutpostSquireDialogue extends DialoguePlugin { stage = 6; break; case 6: - npc("The Void Knights, they are great warriors of balance", "who do Guthix's work here in 2009scape."); + npc("The Void Knights, they are great warriors of balance", "who do Guthix's work here in Gielinor."); stage = 11; break; case 11: - options("Wow, can I join?", "What kind of work?", "What's '2009scape'?", "Uh huh, sure."); + options("Wow, can I join?", "What kind of work?", "What's 'Gielinor'?", "Uh huh, sure."); stage = 12; break; case 12: @@ -92,7 +92,7 @@ public final class OutpostSquireDialogue extends DialoguePlugin { stage = 17; break; case 3: - player("What's '2009scape'?"); + player("What's 'Gielinor'?"); stage = 15; break; case 4: @@ -112,11 +112,11 @@ public final class OutpostSquireDialogue extends DialoguePlugin { end(); break; case 17: - npc("Ah well you see we try to keep 2009scape as Guthix", "intended, it's very challenging. Actually we've been", "having some problems recently, maybe you could help", "us?"); + npc("Ah well you see we try to keep Gielinor as Guthix", "intended, it's very challenging. Actually we've been", "having some problems recently, maybe you could help", "us?"); stage = 18; break; case 18: - options("Yeah ok, what's the problem?", "What's '2009scape'?", "I'd rather not, sorry."); + options("Yeah OK, what's the problem?", "What's 'Gielinor'?", "I'd rather not, sorry."); stage = 19; break; case 19: @@ -126,7 +126,7 @@ public final class OutpostSquireDialogue extends DialoguePlugin { stage = 191; break; case 2: - player("What's '2009scape'?"); + player("What's 'Gielinor'?"); stage = 15; break; case 3: diff --git a/Server/src/main/content/minigame/pestcontrol/PCIslandZone.java b/Server/src/main/content/minigame/pestcontrol/PCIslandZone.java index 6f697b9bd..42cc5179d 100644 --- a/Server/src/main/content/minigame/pestcontrol/PCIslandZone.java +++ b/Server/src/main/content/minigame/pestcontrol/PCIslandZone.java @@ -21,7 +21,7 @@ public final class PCIslandZone extends MapZone { @Override public boolean death(Entity e, Entity killer) { if (e instanceof Player) { // Ensure players can't die on the island. - // System.out.println("[PCIslandZone] Saved player from death!"); + e.getProperties().setTeleportLocation(e.getLocation()); 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/PCPortalNPC.java b/Server/src/main/content/minigame/pestcontrol/PCPortalNPC.java index c19cf1887..0bddb9a24 100644 --- a/Server/src/main/content/minigame/pestcontrol/PCPortalNPC.java +++ b/Server/src/main/content/minigame/pestcontrol/PCPortalNPC.java @@ -88,7 +88,7 @@ public final class PCPortalNPC extends AbstractNPC { /** * If the lifepoints should be updated. */ - private boolean updateLifepoints = true; + public boolean updateLifepoints = true; /** * The pest control session. @@ -294,9 +294,8 @@ public final class PCPortalNPC extends AbstractNPC { } } session.sendString("0", 13 + getPortalIndex()); - updateLifepoints = false; session.getSquire().getSkills().heal(50); - session.getSquire().onImpact(this, null); + ((PCSquireNPC) session.getSquire()).FlagInterfaceUpdate(); } } diff --git a/Server/src/main/content/minigame/pestcontrol/PCSquireNPC.java b/Server/src/main/content/minigame/pestcontrol/PCSquireNPC.java index 1f156b761..14a4eb1a1 100644 --- a/Server/src/main/content/minigame/pestcontrol/PCSquireNPC.java +++ b/Server/src/main/content/minigame/pestcontrol/PCSquireNPC.java @@ -84,10 +84,14 @@ public final class PCSquireNPC extends AbstractNPC { @Override public void onImpact(final Entity entity, BattleState state) { - updateLifepoints = true; + FlagInterfaceUpdate(); super.onImpact(entity, state); } + public void FlagInterfaceUpdate() { + updateLifepoints = true; + } + @Override public int[] getIds() { return new int[] { 3782, 3785 }; diff --git a/Server/src/main/content/minigame/pestcontrol/PestControlActivityPlugin.java b/Server/src/main/content/minigame/pestcontrol/PestControlActivityPlugin.java index 4e2235981..396d22355 100644 --- a/Server/src/main/content/minigame/pestcontrol/PestControlActivityPlugin.java +++ b/Server/src/main/content/minigame/pestcontrol/PestControlActivityPlugin.java @@ -15,7 +15,6 @@ import core.game.node.entity.Entity; import core.game.node.entity.impl.PulseManager; import core.game.node.entity.player.Player; import core.game.node.entity.player.info.Rights; -import core.game.node.entity.state.EntityState; import core.game.node.item.GroundItemManager; import core.game.node.item.Item; import core.game.system.task.Pulse; @@ -30,6 +29,8 @@ import core.plugin.ClassScanner; import core.tools.RandomFunction; import core.tools.StringUtils; +import static core.api.ContentAPIKt.*; + /** * Handles the Pest Control activity. @@ -132,14 +133,14 @@ public final class PestControlActivityPlugin extends ActivityPlugin { // type, // default } else if (success && p.getAttribute("pc_zeal", 0) >= 50) { - int amount = type.ordinal() + 2; + int amount = type.ordinal() == 0 ? 10 : type.ordinal() == 1 ? 15 : 25; p.getSavedData().getActivityData().increasePestPoints(amount); Item coins = new Item(995, p.getProperties().getCurrentCombatLevel() * 10); if (!p.getInventory().add(coins)) { GroundItemManager.create(coins, p); } // default, type, name - p.getDialogueInterpreter().open(3781, true, 1, type.ordinal() == 0 ? "two" : type.ordinal() == 1 ? "three" : "four"); + p.getDialogueInterpreter().open(3781, true, 1, type.ordinal() == 0 ? "ten" : type.ordinal() == 1 ? "fifteen" : "twenty-five"); } else { // default type, default p.getDialogueInterpreter().open(3781, true, 2, true); @@ -147,8 +148,8 @@ public final class PestControlActivityPlugin extends ActivityPlugin { p.removeAttribute("pc_zeal"); p.removeExtension(PestControlSession.class); p.fullRestore(); - if (p.getStateManager().hasState(EntityState.POISONED)) { - p.getStateManager().remove(EntityState.POISONED); + if (isPoisoned(p)) { + curePoison(p); } PulseManager.cancelDeathTask(p); GameWorld.getPulser().submit(new Pulse(1, p) { diff --git a/Server/src/main/content/minigame/pestcontrol/PestControlHelper.kt b/Server/src/main/content/minigame/pestcontrol/PestControlHelper.kt index b9bb69c1c..82c17b0a4 100644 --- a/Server/src/main/content/minigame/pestcontrol/PestControlHelper.kt +++ b/Server/src/main/content/minigame/pestcontrol/PestControlHelper.kt @@ -61,12 +61,12 @@ object PestControlHelper { return false } - fun getMyPestControlSession1(p: PestControlTestBot): content.minigame.pestcontrol.PestControlSession? { - return p.getExtension(content.minigame.pestcontrol.PestControlSession::class.java) + fun getMyPestControlSession1(p: PestControlTestBot): PestControlSession? { + return p.getExtension(PestControlSession::class.java) } - fun getMyPestControlSession2(p: PestControlTestBot2): content.minigame.pestcontrol.PestControlSession? { - return p.getExtension(content.minigame.pestcontrol.PestControlSession::class.java) + fun getMyPestControlSession2(p: PestControlTestBot2): PestControlSession? { + return p.getExtension(PestControlSession::class.java) } } \ No newline at end of file diff --git a/Server/src/main/content/minigame/pestcontrol/PestControlSession.java b/Server/src/main/content/minigame/pestcontrol/PestControlSession.java index f902b5c14..8396f97b8 100644 --- a/Server/src/main/content/minigame/pestcontrol/PestControlSession.java +++ b/Server/src/main/content/minigame/pestcontrol/PestControlSession.java @@ -6,7 +6,6 @@ import core.game.component.Component; 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.ConfigurationManager.Configuration; import core.game.node.scenery.Scenery; import core.game.world.map.Location; import core.game.world.map.Point; @@ -15,6 +14,7 @@ import core.game.world.map.build.DynamicRegion; import core.tools.RandomFunction; import static content.minigame.pestcontrol.PestControlActivityPlugin.MAX_TEAM_SIZE; +import static core.api.ContentAPIKt.*; /** * Represents a pest control session. @@ -166,7 +166,7 @@ public final class PestControlSession { public void sendConfig(int value) { for (Player p : region.getPlanes()[0].getPlayers()) { if (p.isActive()) { - p.getConfigManager().set(Configuration.PC_PORTALS, value); + setVarp(p, 719, value); } } } @@ -264,7 +264,7 @@ public final class PestControlSession { Random r = RandomFunction.RANDOM; Location l = region.getBaseLocation(); p.getProperties().setTeleportLocation(l.transform(32 + r.nextInt(4), 49 + r.nextInt(6), 0)); - p.getConfigManager().set(Configuration.PC_PORTALS, 0); + setVarp(p, 1147, 0); p.getDialogueInterpreter().sendDialogues(3781, FacialExpression.FURIOUS, "You must defend the Void Knight while the portals are", "unsummoned. The ritual takes twenty minutes though,", "so you can help out by destroying them yourselves!", "Now GO GO GO!"); } @@ -386,4 +386,4 @@ public final class PestControlSession { return barricades; } -} \ No newline at end of file +} diff --git a/Server/src/main/content/minigame/pestcontrol/PestControlSquire.java b/Server/src/main/content/minigame/pestcontrol/PestControlSquire.java index d18ffd880..d0e773218 100644 --- a/Server/src/main/content/minigame/pestcontrol/PestControlSquire.java +++ b/Server/src/main/content/minigame/pestcontrol/PestControlSquire.java @@ -8,6 +8,8 @@ import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.plugin.Plugin; +import static core.api.ContentAPIKt.*; + /** * Handles a pest control squire's options. * @author Emperor @@ -38,6 +40,7 @@ public final class PestControlSquire extends OptionHandler { case "leave": if (session == null) { Ships.PEST_TO_PORT_SARIM.sail(player); + playJingle(player, 172); return true; } player.getProperties().setTeleportLocation(session.getActivity().getLeaveLocation()); diff --git a/Server/src/main/content/minigame/pestcontrol/SquireDialogue.java b/Server/src/main/content/minigame/pestcontrol/SquireDialogue.java index f5afd6425..c0e7ca1de 100644 --- a/Server/src/main/content/minigame/pestcontrol/SquireDialogue.java +++ b/Server/src/main/content/minigame/pestcontrol/SquireDialogue.java @@ -7,6 +7,8 @@ import core.game.node.entity.npc.NPC; import core.plugin.Initializable; import core.game.node.entity.player.Player; +import static core.api.ContentAPIKt.*; + /** * Handles the SquireDialogue dialogue. * @author 'Vexia @@ -94,6 +96,7 @@ public class SquireDialogue extends DialoguePlugin { case 503: end(); Ships.PEST_TO_PORT_SARIM.sail(player); + playJingle(player, 172); break; case 1: switch (buttonId) { @@ -124,7 +127,7 @@ public class SquireDialogue extends DialoguePlugin { stage = 12; break; case 12: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "The Void Knights, they are great warriors of balance", "who do Guthix's work here in 2009scape."); + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "The Void Knights, they are great warriors of balance", "who do Guthix's work here in Gielinor."); stage = 13; break; case 13: @@ -157,6 +160,7 @@ public class SquireDialogue extends DialoguePlugin { case 24: end(); Ships.PORT_SARIM_TO_PEST_CONTROL.sail(player); + playJingle(player, 172); break; case 200: end(); diff --git a/Server/src/main/content/minigame/pestcontrol/SquireTypeDialogue.java b/Server/src/main/content/minigame/pestcontrol/SquireTypeDialogue.java index 7277c5d93..d25944841 100644 --- a/Server/src/main/content/minigame/pestcontrol/SquireTypeDialogue.java +++ b/Server/src/main/content/minigame/pestcontrol/SquireTypeDialogue.java @@ -75,7 +75,7 @@ public final class SquireTypeDialogue extends DialoguePlugin { stage = 12; break; case 12: - npc("The Void Knights, they are great warriors of balance", "who do Guthix's work here in 2009scape."); + npc("The Void Knights, they are great warriors of balance", "who do Guthix's work here in Gielinor."); stage = 13; break; case 13: diff --git a/Server/src/main/content/minigame/pestcontrol/bots/CombatState.kt b/Server/src/main/content/minigame/pestcontrol/bots/CombatState.kt index 171da04ce..dbfb2ee4a 100644 --- a/Server/src/main/content/minigame/pestcontrol/bots/CombatState.kt +++ b/Server/src/main/content/minigame/pestcontrol/bots/CombatState.kt @@ -32,7 +32,7 @@ class CombatState(val bot: PestControlTestBot) { removeList.add(port) } else { portal = port - break; + break } } sesh.aportals.removeAll(removeList) @@ -97,30 +97,22 @@ class CombatState(val bot: PestControlTestBot) { } } - //Functions - fun randomWalkTo(loc: Location, radius: Int) { + var newloc = loc.transform(RandomFunction.random(radius, -radius), + RandomFunction.random(radius, -radius), 0) if(!bot.walkingQueue.isMoving) { - GlobalScope.launch { - var newloc = loc.transform(RandomFunction.random(radius, -radius), - RandomFunction.random(radius, -radius), 0) - walkToIterator(newloc) - } + walkToIterator(newloc) } } private fun walkToIterator(loc: Location){ var diffX = loc.x - bot.location.x var diffY = loc.y - bot.location.y - while(!bot.location.transform(diffX, diffY, 0).withinDistance(bot.location)) { - diffX /= 2 - diffY /= 2 - } + GameWorld.Pulser.submit(object : MovementPulse(bot, bot.location.transform(diffX, diffY, 0), Pathfinder.SMART) { override fun pulse(): Boolean { return true } }) } - } diff --git a/Server/src/main/content/minigame/pestcontrol/bots/CombatStateIntermediate.kt b/Server/src/main/content/minigame/pestcontrol/bots/CombatStateIntermediate.kt index fe52e5040..fbea681bd 100644 --- a/Server/src/main/content/minigame/pestcontrol/bots/CombatStateIntermediate.kt +++ b/Server/src/main/content/minigame/pestcontrol/bots/CombatStateIntermediate.kt @@ -31,7 +31,7 @@ class CombatStateIntermediate(val bot: PestControlTestBot2) { removeList.add(port) } else { portal = port - break; + break } } sesh.aportals.removeAll(removeList) @@ -97,30 +97,22 @@ class CombatStateIntermediate(val bot: PestControlTestBot2) { } //Functions - fun randomWalkTo(loc: Location, radius: Int) { + var newloc = loc.transform(RandomFunction.random(radius, -radius), + RandomFunction.random(radius, -radius), 0) if(!bot.walkingQueue.isMoving) { - GlobalScope.launch { - Thread.currentThread().name = "RandomWalkToIteratorBot" - var newloc = loc.transform(RandomFunction.random(radius, -radius), - RandomFunction.random(radius, -radius), 0) - walkToIterator(newloc) - } + walkToIterator(newloc) } } private fun walkToIterator(loc: Location){ var diffX = loc.x - bot.location.x var diffY = loc.y - bot.location.y - while(!bot.location.transform(diffX, diffY, 0).withinDistance(bot.location)) { - diffX /= 2 - diffY /= 2 - } + GameWorld.Pulser.submit(object : MovementPulse(bot, bot.location.transform(diffX, diffY, 0), Pathfinder.SMART) { override fun pulse(): Boolean { return true } }) } - } diff --git a/Server/src/main/content/minigame/pestcontrol/bots/PestControlIntermediateBot.kt b/Server/src/main/content/minigame/pestcontrol/bots/PestControlIntermediateBot.kt index 40333b842..c369030d8 100644 --- a/Server/src/main/content/minigame/pestcontrol/bots/PestControlIntermediateBot.kt +++ b/Server/src/main/content/minigame/pestcontrol/bots/PestControlIntermediateBot.kt @@ -34,7 +34,7 @@ class PestControlTestBot2(l: Location) : PvMBots(legitimizeLocation(l)) { init { val random100 = Random().nextInt(100) if (random100 < 30) { - setAttribute("pc_role","defend_squire"); + setAttribute("pc_role","defend_squire") } else { setAttribute("pc_role","attack_portals") @@ -160,8 +160,8 @@ class PestControlTestBot2(l: Location) : PvMBots(legitimizeLocation(l)) { this.walkToPosSmart(myBoat.outsideBoatBorder.randomLoc) movetimer += RandomFunction.normalPlusWeightRandDist(400, 200) } - movetimer = RandomFunction.normalPlusWeightRandDist(100, 50); - return; + movetimer = RandomFunction.normalPlusWeightRandDist(100, 50) + return } val test = getClosestNodeWithEntry(15, myBoat.ladderId) test ?: randomWalk(1,1) diff --git a/Server/src/main/content/minigame/pestcontrol/bots/PestControlNoviceBot.kt b/Server/src/main/content/minigame/pestcontrol/bots/PestControlNoviceBot.kt index 9c867d71f..6d07a15df 100644 --- a/Server/src/main/content/minigame/pestcontrol/bots/PestControlNoviceBot.kt +++ b/Server/src/main/content/minigame/pestcontrol/bots/PestControlNoviceBot.kt @@ -34,7 +34,7 @@ class PestControlTestBot(l: Location) : PvMBots(legitimizeLocation(l)){ init { val random100 = Random().nextInt(100) if (random100 < 30) { - setAttribute("pc_role","defend_squire"); + setAttribute("pc_role","defend_squire") } else { setAttribute("pc_role","attack_portals") @@ -156,8 +156,8 @@ class PestControlTestBot(l: Location) : PvMBots(legitimizeLocation(l)){ this.walkToPosSmart(myBoat.outsideBoatBorder.randomLoc) movetimer += RandomFunction.normalPlusWeightRandDist(400, 200) } - movetimer = RandomFunction.normalPlusWeightRandDist(100, 50); - return; + movetimer = RandomFunction.normalPlusWeightRandDist(100, 50) + return } val test = getClosestNodeWithEntry(15, myBoat.ladderId) test ?: randomWalk(1,1) diff --git a/Server/src/main/content/minigame/pestcontrol/monsters/PCSpinnerNPC.java b/Server/src/main/content/minigame/pestcontrol/monsters/PCSpinnerNPC.java index d2c667dd6..60cc22a37 100644 --- a/Server/src/main/content/minigame/pestcontrol/monsters/PCSpinnerNPC.java +++ b/Server/src/main/content/minigame/pestcontrol/monsters/PCSpinnerNPC.java @@ -1,5 +1,6 @@ package content.minigame.pestcontrol.monsters; +import content.minigame.pestcontrol.PCPortalNPC; import content.minigame.pestcontrol.PestControlSession; import core.game.interaction.MovementPulse; import core.game.node.entity.Entity; @@ -10,7 +11,6 @@ import core.game.node.entity.impl.PulseType; 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.state.EntityState; import core.game.system.task.Pulse; import core.game.world.GameWorld; import core.game.world.map.Location; @@ -19,6 +19,8 @@ import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Graphics; import core.tools.RandomFunction; +import static core.api.ContentAPIKt.*; + /** * Handles a pest control spinner NPC. * @author Emperor @@ -90,7 +92,7 @@ public final class PCSpinnerNPC extends AbstractNPC { faceTemporary(portal, 1); visualize(new Animation(3911, Priority.HIGH), Graphics.create(658)); portal.getSkills().heal(portal.getSkills().getMaximumLifepoints() / 10); - portal.onImpact(this, null); + ((PCPortalNPC) portal).updateLifepoints = true; } /** @@ -100,8 +102,7 @@ public final class PCSpinnerNPC extends AbstractNPC { animate(getProperties().getDeathAnimation()); for (Player p : RegionManager.getLocalPlayers(this, 1)) { p.getImpactHandler().manualHit(this, 5, HitsplatType.POISON); - p.setAttribute("/save:poison_damage", 18); - p.getStateManager().register(EntityState.POISONED, false, 18, this); + applyPoison(p, this, 1); } GameWorld.getPulser().submit(new Pulse(1, this) { @Override diff --git a/Server/src/main/content/minigame/pestcontrol/reward/PCRewardInterface.java b/Server/src/main/content/minigame/pestcontrol/reward/PCRewardInterface.java index ffab29af7..b2896ea5b 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) / 5; } /** @@ -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/pestcontrol/reward/VoidKnightDialogue.java b/Server/src/main/content/minigame/pestcontrol/reward/VoidKnightDialogue.java index 84263bfcd..7ed3d6ede 100644 --- a/Server/src/main/content/minigame/pestcontrol/reward/VoidKnightDialogue.java +++ b/Server/src/main/content/minigame/pestcontrol/reward/VoidKnightDialogue.java @@ -83,12 +83,12 @@ public final class VoidKnightDialogue extends DialoguePlugin { case 10: npc("I'm a Void Knight, one of the order of Guthix. We are", "warriors of balance who do Guthix's work here in", - "2009scape."); + "Gielinor."); stage = 11; break; case 11: options("Wow, can I join?", "What kind of work?", - "What's '2009scape'?", "Uh huh, sure."); + "What's 'Gielinor'?", "Uh huh, sure."); stage = 12; break; case 12: @@ -102,7 +102,7 @@ public final class VoidKnightDialogue extends DialoguePlugin { stage = 17; break; case 3: - player("What's '2009scape'?"); + player("What's 'Gielinor'?"); stage = 15; break; case 4: @@ -123,14 +123,14 @@ public final class VoidKnightDialogue extends DialoguePlugin { end(); break; case 17: - npc("Ah well you see we try to keep 2009scape as Guthix", + npc("Ah well, you see, we try to keep Gielinor as Guthix", "intended, it's very challenging. Actually we've been", "having some problems recently, maybe you could help", "us?"); stage = 18; break; case 18: - options("Yeah ok, what's the problem?", "What's '2009scape'?", + options("Yeah OK, what's the problem?", "What's 'Gielinor'?", "I'd rather not, sorry."); stage = 19; break; @@ -141,7 +141,7 @@ public final class VoidKnightDialogue extends DialoguePlugin { stage = 191; break; case 2: - player("What's '2009scape'?"); + player("What's 'Gielinor'?"); stage = 15; break; case 3: diff --git a/Server/src/main/content/minigame/puropuro/CropCircleController.kt b/Server/src/main/content/minigame/puropuro/CropCircleController.kt new file mode 100644 index 000000000..0584e4af8 --- /dev/null +++ b/Server/src/main/content/minigame/puropuro/CropCircleController.kt @@ -0,0 +1,97 @@ +package content.minigame.puropuro + +import core.api.* +import core.game.interaction.* +import core.game.world.map.Location +import core.game.node.scenery.Scenery +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.TeleportManager.TeleportType + +class CropCircleController : TickListener, InteractionListener, LoginListener { + override fun tick () { + if (getWorldTicks() < nextCircle) + return + deconstructOldCircle() + + val (name, loc) = possibleLocations.random() + constructCircle(loc) + + sendNews("A crop circle has appeared near $name.") + nextCircle = getWorldTicks() + 1500 + currentLocName = name + } + + override fun defineListeners() { + on (center, IntType.SCENERY, "enter") {player, _ -> + if (hasImpBox(player)) { + sendDialogue (player, "Something prevents you from entering. You think the portal is offended by your imp box. They are not popular on imp and impling planes.") + return@on true + } + teleport (player, puroLocation, TeleportType.PURO_PURO) + setAttribute (player, exitLocation, Location.create(player.location)) + return@on true + } + + on (puroExit, IntType.SCENERY, "leave", "quick-leave") {player, _ -> + var exit = getAttribute (player, exitLocation, Location.create(3158, 3300, 0)) + teleport (player, exit, TeleportType.PURO_PURO) + return@on true + } + } + + override fun login (player: Player) { + sendMessage (player, "A crop circle is active near $currentLocName.") + } + + private fun constructCircle (location: Location) { + activeObjects.add ( + addScenery ( + center, + location, + rotation = 0, + type = 10 + ) + ) + for ((index, tile) in location.surroundingTiles.withIndex()) { + activeObjects.add ( + addScenery ( + surrounding[index % 4], + tile, + rotation = (index / 4) * 2, + type = 10 + ) + ) + } + } + + private fun deconstructOldCircle() { + for (scenery in activeObjects) + removeScenery(scenery) + activeObjects.clear() + } + + private fun hasImpBox (player: Player) : Boolean { + return inInventory(player, 10025) || inInventory(player, 10027) || inInventory(player, 10028) + } + + companion object { + var currentLocName = "" + val exitLocation = "/save:puro-exit" + val possibleLocations = arrayOf ( + Pair ("Doric's Hut", Location.create(2953, 3444, 0)), + Pair ("Yanille", Location.create(2583, 3104, 0)), + Pair ("Draynor", Location.create(3113, 3274, 0)), + Pair ("Rimmington", Location.create(2978, 3216, 0)), + Pair ("The Grand Exchange", Location.create(3141, 3461, 0)), + Pair ("Northern Lumbridge", Location.create(3160, 3296, 0)), + Pair ("Southern Varrock", Location.create(3218, 3348, 0)), + Pair ("Northern Ardougne", Location.create(2644, 3350, 0)) + ) + val activeObjects = ArrayList() + val surrounding = arrayOf (24984, 24985, 24986, 24987) + val center = 24991 + val puroExit = 25014 + val puroLocation = Location.create(2591, 4320, 0) + var nextCircle = 0 + } +} diff --git a/Server/src/main/content/minigame/puropuro/ElnockInquisitorDialogue.java b/Server/src/main/content/minigame/puropuro/ElnockInquisitorDialogue.java index 068a89413..b9cbe7df7 100644 --- a/Server/src/main/content/minigame/puropuro/ElnockInquisitorDialogue.java +++ b/Server/src/main/content/minigame/puropuro/ElnockInquisitorDialogue.java @@ -13,6 +13,8 @@ import core.plugin.ClassScanner; import content.minigame.puropuro.ElnockInquisitorDialogue.ElnockExchangeInterfaceHandler.ElnockExchange; +import static core.api.ContentAPIKt.*; + /** * Handles the elnock inquisitor dialogue. * @author Vexia @@ -225,7 +227,7 @@ public final class ElnockInquisitorDialogue extends DialoguePlugin { public boolean handle(Player player, Component component, int opcode, int button, int slot, int itemId) { ElnockExchange exchange = player.getAttribute("exchange", null); if (button == 34) { - player.getConfigManager().set(1018, 0); + setVarp(player, 1018, 0); if (exchange == null) { player.sendMessage("Making a selection before confirming."); return true; @@ -254,7 +256,7 @@ public final class ElnockInquisitorDialogue extends DialoguePlugin { exchange = ElnockExchange.forButton(button); if (exchange != null) { player.setAttribute("exchange", exchange); - player.getConfigManager().set(1018, exchange.getConfigValue()); + setVarp(player, 1018, exchange.getConfigValue()); } return true; } diff --git a/Server/src/main/content/minigame/puropuro/ImpDefenderBehavior.kt b/Server/src/main/content/minigame/puropuro/ImpDefenderBehavior.kt new file mode 100644 index 000000000..2c1bb509e --- /dev/null +++ b/Server/src/main/content/minigame/puropuro/ImpDefenderBehavior.kt @@ -0,0 +1,93 @@ +package content.minigame.puropuro + +import core.api.* +import core.tools.* +import core.game.node.entity.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.zone.ZoneBorders +import core.game.world.map.RegionManager +import core.game.system.task.Pulse +import content.global.skill.hunter.bnet.BNetTypes + +import org.rs09.consts.Items + +class ImpDefenderBehavior : NPCBehavior(6074) { + override fun onCreation (self: NPC) { + self.isWalks = true + self.isNeverWalks = false + self.walkRadius = 30 + } + + override fun tick (self: NPC) : Boolean { + if (!RandomFunction.roll(10)) return true + + var nextCaptureTick = getAttribute(self, "next-capture-tick", 0) + if (getWorldTicks() < nextCaptureTick) return true + + var players = RegionManager.getLocalPlayers(self, 2) + for (player in players) { + var lowestTierImpling = BNetTypes.getImpling(player) + if (lowestTierImpling == null) continue + var jarItem = lowestTierImpling.reward + setAttribute(self, "capture-target", player) + setAttribute(self, "capture-item", jarItem) + setAttribute(self, "next-capture-tick", getWorldTicks() + RandomFunction.random(25, 100)) + submitIndividualPulse(self, TryReleasePulse(self)) + break + } + + return true + } + + private class TryReleasePulse(val self: NPC) : Pulse() { + companion object { + const val catchPlayerLow = 35.0 //14% chance to avoid catch at level 1 + const val catchPlayerHigh = 280.0 //100% chance to avoid catch at level 90 (level 82 with imp repellent) + const val impRepellentBonus = 20.0 //10% bonus if player has imp repellent + } + + var counter = 0 + override fun pulse() : Boolean { + val player: Player? = getAttribute(self, "capture-target", null) + val jarItem: Item? = getAttribute(self, "capture-item", null) + if (player == null || jarItem == null) return true + when (counter++) { + 0 -> { + face (self, player) + animate (self, 6628) + setDelay(animationDuration(getAnimation(6628))) + return false + } + 1 -> { + var hasRepellent = inInventory(player, Items.IMP_REPELLENT_11262) + var baseRoll = RandomFunction.randomDouble(100.0) + var playerRoll = RandomFunction.getSkillSuccessChance( + catchPlayerLow + if (hasRepellent) impRepellentBonus else 0.0, + catchPlayerHigh + if (hasRepellent) impRepellentBonus else 0.0, + getStatLevel(player, Skills.THIEVING) + ) + if (playerRoll < baseRoll) { + sendChat (self, "Be free!") + animate (self, 6629) + removeItem (player, jarItem) + var loc = ZoneBorders ( + self.location.x - 2, + self.location.y - 2, + self.location.x + 2, + self.location.y + 2 + ).randomLoc + GroundItemManager.create( + Item(Items.IMPLING_JAR_11260), + loc, player + ) + } + resetFace (self) + } + } + return true + } + } +} diff --git a/Server/src/main/content/minigame/puropuro/ImpDefenderNPC.java b/Server/src/main/content/minigame/puropuro/ImpDefenderNPC.java deleted file mode 100644 index 948a6f4ac..000000000 --- a/Server/src/main/content/minigame/puropuro/ImpDefenderNPC.java +++ /dev/null @@ -1,112 +0,0 @@ -package content.minigame.puropuro; - -import java.util.List; -import java.util.Random; - -import core.game.node.entity.skill.Skills; -import content.global.skill.hunter.bnet.BNetTypes; -import content.global.skill.hunter.bnet.ImplingNode; -import core.game.node.entity.npc.AbstractNPC; -import core.game.node.entity.player.Player; -import core.game.node.item.Item; -import core.game.world.GameWorld; -import core.game.world.map.Location; -import core.game.world.map.RegionManager; -import core.tools.RandomFunction; - -/** - * Handles the imp defender npc. - * @author Vexia - */ -public final class ImpDefenderNPC extends AbstractNPC { - - /** - * Constructs a new {@code ImpDefenderNPC} {@code Object}. - */ - public ImpDefenderNPC() { - super(0, null); - } - - /** - * Constructs a new {@code ImpDefenderNPC} {@code Object}. - * @param id the id. - * @param location the location. - */ - public ImpDefenderNPC(int id, Location location) { - super(id, location); - } - - @Override - public void handleTickActions() { - super.handleTickActions(); - if (RandomFunction.random(250) <= 3) { - stealImp(); - } - } - - /** - * Attempts to steal an imp. - */ - private void stealImp() { - List players = RegionManager.getLocalPlayers(this, 3); - for (Player player : players) { - if (canSteal(player)) { - steal(player); - } - break; - } - } - - /** - * Steals an imp from a player. - * @param player the player. - */ - private void steal(Player player) { - if (player == null) { - return; - } - ImplingNode node = BNetTypes.getImpling(player); - if (node != null) { - faceTemporary(player, 1); - sendChat("Be free!"); - player.getInventory().remove(node.getReward()); - player.setAttribute("imp-steal", GameWorld.getTicks() + 500); - } - } - - /** - * Checks if the imp can steal from the player. - * @param player the player. - * @return {@code True} if so. - */ - private boolean canSteal(Player player) { - if (!GameWorld.getSettings().isDevMode() && player.getAttribute("imp-steal", 0) > GameWorld.getTicks()) { - return false; - } - int thievingLevel = player.getSkills().getLevel(Skills.THIEVING); - if (thievingLevel >= 60) { - return false; - } - int level = thievingLevel - 10; - if (level <= 0) { - level = 5; - } - if (player.getInventory().containsItem(new Item(11262))) { - thievingLevel += 15; - } - int currentLevel = RandomFunction.random(thievingLevel) + 1; - double ratio = (double) currentLevel / (new Random().nextInt(level + 5) + 1); - return Math.round(ratio * thievingLevel) < level; - } - - @Override - public AbstractNPC construct(int id, Location location, Object... objects) { - return new ImpDefenderNPC(id, location); - } - - @Override - public int[] getIds() { - return new int[] { 6074 }; - } - -} diff --git a/Server/src/main/content/minigame/puropuro/PuroPuroPlugin.java b/Server/src/main/content/minigame/puropuro/PuroPuroPlugin.java index a0df1d47e..3aa053f8a 100644 --- a/Server/src/main/content/minigame/puropuro/PuroPuroPlugin.java +++ b/Server/src/main/content/minigame/puropuro/PuroPuroPlugin.java @@ -1,21 +1,14 @@ package content.minigame.puropuro; -import java.util.ArrayList; -import java.util.List; - import core.cache.def.impl.ItemDefinition; import core.cache.def.impl.NPCDefinition; -import core.cache.def.impl.SceneryDefinition; import core.game.component.Component; -import core.plugin.Initializable; -import core.game.node.entity.skill.Skills; import core.game.interaction.Option; import core.game.interaction.OptionHandler; import core.game.node.Node; import core.game.node.entity.Entity; -import core.game.node.entity.impl.ForceMovement; import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.TeleportManager.TeleportType; +import core.game.node.entity.skill.Skills; import core.game.node.item.Item; import core.game.node.scenery.Scenery; import core.game.node.scenery.SceneryBuilder; @@ -31,465 +24,490 @@ 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; -import core.plugin.Plugin; import core.plugin.ClassScanner; +import core.plugin.Initializable; +import core.plugin.Plugin; import core.tools.RandomFunction; +import org.rs09.consts.Items; + +import java.util.ArrayList; +import java.util.List; + +import static core.api.ContentAPIKt.*; /** * Handles the puro puro activity. + * * @author Vexia */ @Initializable public final class PuroPuroPlugin extends MapZone implements Plugin { - /** - * The moving wheat. - */ - private static final List WHEAT = new ArrayList<>(20); + /** + * The moving wheat. + */ + private static final List WHEAT = new ArrayList<>(20); - /** - * The pulse. - */ - private static final Pulse PULSE = new Pulse(1) { - @Override - public boolean pulse() { - for (WheatSet set : WHEAT) { - if (set.canWhilt()) { - set.whilt(); - } - } - return false; - } - }; + /** + * The pulse. + */ + private static final Pulse PULSE = new Pulse(1) { + @Override + public boolean pulse() { + for (WheatSet set : WHEAT) { + if (set.canWhilt()) { + set.whilt(); + } + } + return false; + } + }; - /** - * Constructs a new {@code PuroPuroActivity} {@code Object}. - */ - public PuroPuroPlugin() { - super("puro puro", true); - setZoneType(ZoneType.SAFE.getId()); - } + /** + * Constructs a new {@code PuroPuroActivity} {@code Object}. + */ + public PuroPuroPlugin() { + super("puro puro", true); + setZoneType(ZoneType.SAFE.getId()); + } - @Override - public Plugin newInstance(Object arg) throws Throwable { - PULSE.stop(); - ZoneBuilder.configure(this); - ClassScanner.definePlugin(new FairyAerykaDialogue()); - ClassScanner.definePlugin(new WanderingImplingDialogue()); - ClassScanner.definePlugin(new ElnockInquisitorDialogue()); - ClassScanner.definePlugin(new PuroOptionHandler()); - ClassScanner.definePlugin(new ImpDefenderNPC()); - return this; - } + @Override + public Plugin newInstance(Object arg) throws Throwable { + PULSE.stop(); + ZoneBuilder.configure(this); + ClassScanner.definePlugin(new FairyAerykaDialogue()); + ClassScanner.definePlugin(new WanderingImplingDialogue()); + ClassScanner.definePlugin(new ElnockInquisitorDialogue()); + ClassScanner.definePlugin(new PuroOptionHandler()); + return this; + } - @Override - public Object fireEvent(String identifier, Object... args) { - return null; - } + @Override + public Object fireEvent(String identifier, Object... args) { + return null; + } - @Override - public boolean enter(Entity e) { - if (e instanceof Player) { - Player p = e.asPlayer(); - PacketRepository.send(MinimapState.class, new MinimapStateContext(p, 2)); - } - if (!PULSE.isRunning()) { - spawnWheat(); - PULSE.restart(); - PULSE.start(); - GameWorld.getPulser().submit(PULSE); - } - return super.enter(e); - } + @Override + public boolean enter(Entity e) { + if (e instanceof Player) { + Player p = e.asPlayer(); + PacketRepository.send(MinimapState.class, new MinimapStateContext(p, 2)); + } + if (!PULSE.isRunning()) { + spawnWheat(); + PULSE.restart(); + PULSE.start(); + GameWorld.getPulser().submit(PULSE); + } + return super.enter(e); + } - @Override - public boolean leave(Entity e, boolean logout) { - if (e instanceof Player) { - Player p = e.asPlayer(); - if (!logout) { - p.getInterfaceManager().close(); - p.getInterfaceManager().closeOverlay(); - PacketRepository.send(MinimapState.class, new MinimapStateContext(p, 0)); - } - } - return super.leave(e, logout); - } + @Override + public boolean leave(Entity e, boolean logout) { + if (e instanceof Player) { + Player p = e.asPlayer(); + if (!logout) { + p.getInterfaceManager().close(); + p.getInterfaceManager().closeOverlay(); + PacketRepository.send(MinimapState.class, new MinimapStateContext(p, 0)); + } + } + return super.leave(e, logout); + } - @Override - public boolean interact(Entity e, Node target, Option option) { - if (e instanceof Player) { - Player p = (Player) e; - switch (target.getId()) { - case 25016: - case 25029: - case 25019: - case 25018: - case 25020: - case 25021: - pushThrough(p, (Scenery) target); - return true; - case 25014: - p.getTeleporter().send(Location.create(2427, 4446, 0), TeleportType.PURO_PURO); - return true; - } - } - return super.interact(e, target, option); - } + @Override + public boolean interact(Entity e, Node target, Option option) { + if (e instanceof Player) { + Player p = (Player) e; + switch (target.getId()) { + case 25016: + case 25029: + case 25019: + case 25018: + case 25020: + case 25021: + pushThrough(p, (Scenery) target); + return true; + } + } + return super.interact(e, target, option); + } - /** - * Pushes through the wheat. - * @param player the player. - * @param object the object. - */ - private void pushThrough(final Player player, final Scenery object) { - if (player.getSkills().getStaticLevel(Skills.HUNTER) < 17) { - player.sendMessage("You need a Hunting level of at least 17 to enter the maze."); - return; - } - if (hasImplingBox(player)) { - player.getDialogueInterpreter().sendDialogue("Something prevents you from entering. You think the portal is", "offended by your imp boxes. They are not popular on imp", "and impling planes."); - return; - } - final Location dest = object.getLocation().transform(Direction.getLogicalDirection(player.getLocation(), object.getLocation()), 1); - if (RegionManager.getObject(dest) != null) { - player.sendMessage("An object on the other side is in your way."); - return; - } - if (RandomFunction.random(2) == 0) { - player.sendMessage("You use your strength to push through the wheat."); - } else { - player.sendMessage("You use your strength to push through the wheat. It's hard work though."); - } - player.setAttribute("cantMove", true); - GameWorld.getPulser().submit(new Pulse(1) { - @Override - public boolean pulse() { - ForceMovement.run(player, player.getLocation(), dest, Animation.create(6594), Animation.create(6594), Direction.getLogicalDirection(player.getLocation(), object.getLocation()), 3, 3); - return true; - } - }); - } + /** + * Pushes through the wheat. + * + * @param player the player. + * @param object the object. + */ + private void pushThrough(final Player player, final Scenery object) { + if (player.getSkills().getStaticLevel(Skills.HUNTER) < 17) { + player.sendMessage("You need a Hunting level of at least 17 to enter the maze."); + return; + } + if (hasImplingBox(player)) { + player.getDialogueInterpreter().sendDialogue("Something prevents you from entering. You think the portal is", "offended by your imp boxes. They are not popular on imp", "and impling planes."); + return; + } + final Location dest = object.getLocation().transform(Direction.getLogicalDirection(player.getLocation(), object.getLocation()), 1); + if (RegionManager.getObject(dest) != null) { + player.sendMessage("An object on the other side is in your way."); + return; + } + if (RandomFunction.random(2) == 0) { + player.sendMessage("You use your strength to push through the wheat."); + } else { + player.sendMessage("You use your strength to push through the wheat. It's hard work though."); + } + player.setAttribute("cantMove", true); + forceMove(player, player.getLocation(), dest, 0, 265, null, 6595, null); + } - /** - * Spawns the wheat. - */ - private void spawnWheat() { - for (WheatSet set : WHEAT) { - set.init(); - } - } + /** + * Spawns the wheat. + */ + private void spawnWheat() { + for (WheatSet set : WHEAT) { + set.init(); + } + } - /** - * Checks if the player has an impling box. - * @param player the player. - * @return {@code True} if so. - */ - private boolean hasImplingBox(Player player) { - return player.getInventory().contains(10025, 1) || player.getInventory().contains(10027, 1) || player.getInventory().contains(10028, 1); - } + /** + * Checks if the player has an impling box. + * + * @param player the player. + * @return {@code True} if so. + */ + private boolean hasImplingBox(Player player) { + return player.getInventory().contains(10025, 1) || player.getInventory().contains(10027, 1) || player.getInventory().contains(10028, 1); + } - @Override - public void configure() { - registerRegion(10307); - WHEAT.add(new WheatSet(0, Location.create(2606, 4329, 0), Location.create(2606, 4328, 0))); - WHEAT.add(new WheatSet(1, Location.create(2596, 4331, 0), Location.create(2597, 4331, 0))); - WHEAT.add(new WheatSet(0, Location.create(2580, 4326, 0), Location.create(2580, 4325, 0))); - WHEAT.add(new WheatSet(1, Location.create(2595, 4308, 0), Location.create(2596, 4308, 0))); - WHEAT.add(new WheatSet(0, Location.create(2603, 4314, 0), Location.create(2603, 4313, 0))); - WHEAT.add(new WheatSet(1, Location.create(2599, 4305, 0), Location.create(2600, 4305, 0))); - WHEAT.add(new WheatSet(0, Location.create(2577, 4327, 0), Location.create(2577, 4328, 0))); - WHEAT.add(new WheatSet(1, Location.create(2587, 4334, 0), Location.create(2586, 4334, 0))); - WHEAT.add(new WheatSet(0, Location.create(2609, 4310, 0), Location.create(2609, 4309, 0))); - WHEAT.add(new WheatSet(1, Location.create(2586, 4302, 0), Location.create(2587, 4302, 0))); - WHEAT.add(new WheatSet(0, Location.create(2574, 4310, 0), Location.create(2574, 4311, 0))); - WHEAT.add(new WheatSet(1, Location.create(2582, 4337, 0), Location.create(2581, 4337, 0))); - WHEAT.add(new WheatSet(0, Location.create(2571, 4316, 0), Location.create(2571, 4315, 0))); - WHEAT.add(new WheatSet(1, Location.create(2601, 4340, 0), Location.create(2602, 4340, 0))); - WHEAT.add(new WheatSet(0, Location.create(2612, 4324, 0), Location.create(2612, 4323, 0))); - WHEAT.add(new WheatSet(1, Location.create(2584, 4296, 0), Location.create(2583, 4296, 0))); - WHEAT.add(new WheatSet(0, Location.create(2568, 4329, 0), Location.create(2568, 4330, 0))); - WHEAT.add(new WheatSet(1, Location.create(2595, 4343, 0), Location.create(2596, 4343, 0))); - WHEAT.add(new WheatSet(0, Location.create(2615, 4315, 0), Location.create(2615, 4314, 0))); - WHEAT.add(new WheatSet(1, Location.create(2601, 4293, 0), Location.create(2600, 4293, 0))); - WHEAT.add(new WheatSet(0, Location.create(2565, 4310, 0), Location.create(2565, 4311, 0))); - WHEAT.add(new WheatSet(1, Location.create(2582, 4346, 0), Location.create(2583, 4346, 0))); - WHEAT.add(new WheatSet(0, Location.create(2621, 4328, 0), Location.create(2621, 4329, 0))); - WHEAT.add(new WheatSet(1, Location.create(2602, 4349, 0), Location.create(2603, 4349, 0))); - WHEAT.add(new WheatSet(0, Location.create(2562, 4334, 0), Location.create(2562, 4333, 0))); - WHEAT.add(new WheatSet(1, Location.create(2582, 4290, 0), Location.create(2581, 4290, 0))); - WHEAT.add(new WheatSet(0, Location.create(2568, 4348, 0), Location.create(2568, 4347, 0))); - WHEAT.add(new WheatSet(0, Location.create(2615, 4347, 0), Location.create(2615, 4348, 0))); - WHEAT.add(new WheatSet(0, Location.create(2612, 4345, 0), Location.create(2612, 4344, 0))); - WHEAT.add(new WheatSet(0, Location.create(2614, 4292, 0), Location.create(2614, 4291, 0))); - WHEAT.add(new WheatSet(0, Location.create(2568, 4292, 0), Location.create(2568, 4291, 0))); - WHEAT.add(new WheatSet(0, Location.create(2571, 4295, 0), Location.create(2571, 4294, 0))); - WHEAT.add(new WheatSet(0, Location.create(2575, 4297, 0), Location.create(2575, 4298, 0))); - WHEAT.add(new WheatSet(0, Location.create(2584, 4330, 0), Location.create(2584, 4329, 0))); - WHEAT.add(new WheatSet(0, Location.create(2599, 4329, 0), Location.create(2599, 4330, 0))); - WHEAT.add(new WheatSet(1, Location.create(2602, 4312, 0), Location.create(2601, 4312, 0))); - WHEAT.add(new WheatSet(1, Location.create(2610, 4312, 0), Location.create(2611, 4312, 0))); - WHEAT.add(new WheatSet(1, Location.create(2570, 4309, 0), Location.create(2569, 4309, 0))); - WHEAT.add(new WheatSet(0, Location.create(2583, 4304, 0), Location.create(2583, 4303, 0))); - } + @Override + public void configure() { + registerRegion(10307); + WHEAT.add(new WheatSet(0, Location.create(2606, 4329, 0), Location.create(2606, 4328, 0))); + WHEAT.add(new WheatSet(1, Location.create(2596, 4331, 0), Location.create(2597, 4331, 0))); + WHEAT.add(new WheatSet(0, Location.create(2580, 4326, 0), Location.create(2580, 4325, 0))); + WHEAT.add(new WheatSet(1, Location.create(2595, 4308, 0), Location.create(2596, 4308, 0))); + WHEAT.add(new WheatSet(0, Location.create(2603, 4314, 0), Location.create(2603, 4313, 0))); + WHEAT.add(new WheatSet(1, Location.create(2599, 4305, 0), Location.create(2600, 4305, 0))); + WHEAT.add(new WheatSet(0, Location.create(2577, 4327, 0), Location.create(2577, 4328, 0))); + WHEAT.add(new WheatSet(1, Location.create(2587, 4334, 0), Location.create(2586, 4334, 0))); + WHEAT.add(new WheatSet(0, Location.create(2609, 4310, 0), Location.create(2609, 4309, 0))); + WHEAT.add(new WheatSet(1, Location.create(2586, 4302, 0), Location.create(2587, 4302, 0))); + WHEAT.add(new WheatSet(0, Location.create(2574, 4310, 0), Location.create(2574, 4311, 0))); + WHEAT.add(new WheatSet(1, Location.create(2582, 4337, 0), Location.create(2581, 4337, 0))); + WHEAT.add(new WheatSet(0, Location.create(2571, 4316, 0), Location.create(2571, 4315, 0))); + WHEAT.add(new WheatSet(1, Location.create(2601, 4340, 0), Location.create(2602, 4340, 0))); + WHEAT.add(new WheatSet(0, Location.create(2612, 4324, 0), Location.create(2612, 4323, 0))); + WHEAT.add(new WheatSet(1, Location.create(2584, 4296, 0), Location.create(2583, 4296, 0))); + WHEAT.add(new WheatSet(0, Location.create(2568, 4329, 0), Location.create(2568, 4330, 0))); + WHEAT.add(new WheatSet(1, Location.create(2595, 4343, 0), Location.create(2596, 4343, 0))); + WHEAT.add(new WheatSet(0, Location.create(2615, 4315, 0), Location.create(2615, 4314, 0))); + WHEAT.add(new WheatSet(1, Location.create(2601, 4293, 0), Location.create(2600, 4293, 0))); + WHEAT.add(new WheatSet(0, Location.create(2565, 4310, 0), Location.create(2565, 4311, 0))); + WHEAT.add(new WheatSet(1, Location.create(2582, 4346, 0), Location.create(2583, 4346, 0))); + WHEAT.add(new WheatSet(0, Location.create(2568, 4348, 0), Location.create(2568, 4347, 0))); + WHEAT.add(new WheatSet(0, Location.create(2615, 4347, 0), Location.create(2615, 4348, 0))); + WHEAT.add(new WheatSet(0, Location.create(2612, 4345, 0), Location.create(2612, 4344, 0))); + WHEAT.add(new WheatSet(0, Location.create(2614, 4292, 0), Location.create(2614, 4291, 0))); + WHEAT.add(new WheatSet(0, Location.create(2568, 4292, 0), Location.create(2568, 4291, 0))); + WHEAT.add(new WheatSet(0, Location.create(2571, 4295, 0), Location.create(2571, 4294, 0))); + WHEAT.add(new WheatSet(0, Location.create(2575, 4297, 0), Location.create(2575, 4298, 0))); + WHEAT.add(new WheatSet(0, Location.create(2584, 4330, 0), Location.create(2584, 4329, 0))); + WHEAT.add(new WheatSet(0, Location.create(2599, 4329, 0), Location.create(2599, 4330, 0))); + WHEAT.add(new WheatSet(1, Location.create(2602, 4312, 0), Location.create(2601, 4312, 0))); + WHEAT.add(new WheatSet(1, Location.create(2610, 4312, 0), Location.create(2611, 4312, 0))); + WHEAT.add(new WheatSet(1, Location.create(2570, 4309, 0), Location.create(2569, 4309, 0))); + WHEAT.add(new WheatSet(0, Location.create(2583, 4304, 0), Location.create(2583, 4303, 0))); + } - /** - * Handles the puro puro options. - * @author Vexia - */ - public class PuroOptionHandler extends OptionHandler { + /** + * Handles the puro puro options. + * + * @author Vexia + */ + public class PuroOptionHandler extends OptionHandler { - @Override - public Plugin newInstance(Object arg) throws Throwable { - NPCDefinition.forId(6070).getHandlers().put("option:trade", this); - SceneryDefinition.forId(24991).getHandlers().put("option:enter", this); - ItemDefinition.forId(11273).getHandlers().put("option:toggle-view", this); - ItemDefinition.forId(11258).getHandlers().put("option:butterfly-jar", this); - ItemDefinition.forId(11258).getHandlers().put("option:impling-jar", this); - ItemDefinition.forId(11258).getHandlers().put("option:check", this); - return this; - } + @Override + public Plugin newInstance(Object arg) throws Throwable { + NPCDefinition.forId(6070).getHandlers().put("option:trade", this); + ItemDefinition.forId(11273).getHandlers().put("option:toggle-view", this); + ItemDefinition.forId(11258).getHandlers().put("option:butterfly-jar", this); + ItemDefinition.forId(11258).getHandlers().put("option:impling-jar", this); + ItemDefinition.forId(11258).getHandlers().put("option:check", this); + return this; + } - @Override - public boolean handle(Player player, Node node, String option) { - switch (node.getId()) { - case 11258: - handleJarGenerator(player, (Item) node, option); - break; - case 6070: - ElnockInquisitorDialogue.openShop(player); - break; - case 24991: - if (hasImplingBox(player)) { - player.getDialogueInterpreter().sendDialogue("Something prevents you from entering. You think the portal is offended by", "your imp boxes. They are not popular on imp and impling planes."); - break; - } - player.getTeleporter().send(Location.create(2591, 4320, 0), TeleportType.PURO_PURO); - return true; - case 11273: - if (!player.getZoneMonitor().isInZone("puro puro")) { - player.sendMessage("You can only use this in the Puro Puro Maze."); - break; - } else { - if (player.getInterfaceManager().getOverlay() != null) { - player.getInterfaceManager().closeOverlay(); - break; - } - player.getInterfaceManager().openOverlay(new Component(541)); - } - return true; - } - return true; - } + @Override + public boolean handle(Player player, Node node, String option) { + switch (node.getId()) { + case 11258: + handleJarGenerator(player, (Item) node, option); + break; + case 6070: + ElnockInquisitorDialogue.openShop(player); + break; + case 11273: + if (!player.getZoneMonitor().isInZone("puro puro")) { + player.sendMessage("You can only use this in the Puro Puro Maze."); + break; + } else { + if (player.getInterfaceManager().getOverlay() != null) { + player.getInterfaceManager().closeOverlay(); + break; + } - /** - * Handles the jar generator. - * @param player the player. - * @param item the item. - * @param option the option. - */ - private void handleJarGenerator(Player player, Item item, String option) { - switch (option) { - case "butterfly-jar": - case "impling-jar": - generate(player, item, option); - break; - case "check": - player.sendMessage("Your jar generator has a charge percentage of " + getPercent(item) + "."); - break; - } - } + player.getInterfaceManager().openOverlay(new Component(169)); + } + return true; + } + return true; + } - /** - * Generates a new jar. - * @param player the player. - * @param item the item. - * @param option the option. - */ - private void generate(Player player, Item item, String option) { - final Item jar = option.equals("butterfly-jar") ? new Item(10012) : new Item(11260); - final int percent = jar.getId() == 10012 ? 1 : 3; - if (!hasPercent(item, percent)) { - player.sendMessage("Your jar generator doesn't have enough charges to make another " + jar.getName().toLowerCase() + "."); - return; - } - player.lock(5); - player.animate(new Animation(6592)); - player.getInventory().add(jar); - setPercent(item, percent); - player.sendMessage("Your jar generator generates a " + jar.getName().toLowerCase() + "."); - if (getPercent(item) <= 0) { - player.getInventory().remove(item); - player.sendMessage("Your jar generator runs out of charges."); - } - } + /** + * Handles the jar generator. + * + * @param player the player. + * @param item the item. + * @param option the option. + */ + private void handleJarGenerator(Player player, Item item, String option) { + switch (option) { + case "butterfly-jar": + case "impling-jar": + generate(player, item, option); + break; + case "check": + player.sendMessage("Your jar generator has a charge percentage of " + getPercent(item) + "."); + break; + } + } - /** - * Checks if the player has the percent. - * @param item the item. - * @param percent the percent. - * @return - */ - private boolean hasPercent(Item item, int percent) { - return getPercent(item) - percent >= 0; - } + /** + * Generates a new jar. + * + * @param player the player. + * @param item the item. + * @param option the option. + */ + private void generate(Player player, Item item, String option) { + final Item jar = option.equals("butterfly-jar") ? new Item(10012) : new Item(11260); + final int percent = jar.getId() == 10012 ? 1 : 3; + if (!hasPercent(item, percent)) { + player.sendMessage("Your jar generator doesn't have enough charges to make another " + jar.getName().toLowerCase() + "."); + return; + } + player.lock(5); + player.animate(new Animation(6592)); + player.getInventory().add(jar); + setPercent(item, percent); + player.sendMessage("Your jar generator generates a " + jar.getName().toLowerCase() + "."); + if (getPercent(item) <= 0) { + player.getInventory().remove(item); + player.sendMessage("Your jar generator runs out of charges."); + } + } - /** - * The percent to set. - * @param item the item. - * @param percent the percent. - */ - private void setPercent(Item item, int percent) { - item.setCharge(item.getCharge() - percent); - } + /** + * Checks if the player has the percent. + * + * @param item the item. + * @param percent the percent. + * @return + */ + private boolean hasPercent(Item item, int percent) { + return getPercent(item) - percent >= 0; + } - /** - * Gets the percent. - * @param item the item. - * @return the percent. - */ - private int getPercent(Item item) { - int difference = item.getCharge() - 1000; - return difference + 100; - } + /** + * The percent to set. + * + * @param item the item. + * @param percent the percent. + */ + private void setPercent(Item item, int percent) { + item.setCharge(item.getCharge() - percent); + } - @Override - public boolean isWalk(Player p, Node n) { - return !(n instanceof Item); + /** + * Gets the percent. + * + * @param item the item. + * @return the percent. + */ + private int getPercent(Item item) { + int difference = item.getCharge() - 1000; + return difference + 100; + } - } + @Override + public boolean isWalk(Player p, Node n) { + return !(n instanceof Item); - @Override - public boolean isWalk() { - return false; - } - } + } - /** - * A wheat set. - * @author Vexia - */ - public static class WheatSet { + @Override + public boolean isWalk() { + return false; + } + } - /** - * The locations of the wheat. - */ - private final Location[] locations; + /** + * A wheat set. + */ + public static class WheatSet { - /** - * The scenerys. - */ - private Scenery[] objects = new Scenery[2]; + /** + * The locations of the wheat. + */ + private final Location[] locations; - /** - * The rotation. - */ - private int rot; + /** + * The scenerys. + */ + private Scenery[] objects = new Scenery[2]; - /** - * The time until the next whilt. - */ - private int nextWhilt; + /** + * The rotation. + */ + private int rot; - /** - * The busy ticks. - */ - private int busyTicks; + /** + * The time until the next whilt. + */ + private int nextWhilt; - /** - * If the wheat is removed. - */ - private boolean removed; + /** + * The busy ticks. + */ + private int busyTicks; - /** - * Constructs a new {@code WheatSet} {@code Object}. - * @param locations the locations. - */ - public WheatSet(int rot, Location... locations) { - this.rot = rot; - this.locations = locations; - } + /** + * If the wheat is removed. + */ + private boolean removed; - /** - * Initializes the wheat. - */ - public void init() { - int index = 0; - for (Location location : locations) { - Scenery object = new Scenery(25021, location, 22, rot); - SceneryBuilder.add(object); - objects[index] = object; - index++; - } - setNextWhilt(); - } + /** + * Constructs a new {@code WheatSet} {@code Object}. + * + * @param locations the locations. + */ + public WheatSet(int rot, Location... locations) { + this.rot = rot; + this.locations = locations; + } - /** - * Whilts the wheat. - */ - public void whilt() { - busyTicks = GameWorld.getTicks() + 5; - for (Scenery object : objects) { - if (object == null) { - continue; - } - if (removed) { - SceneryBuilder.add(object); - continue; - } - SceneryBuilder.remove(object); - } - removed = !removed; - setNextWhilt(); - } + /** + * Initializes the wheat. + */ + public void init() { + int index = 0; + for (Location location : locations) { + Scenery object = new Scenery(25021, location, 22, rot); + SceneryBuilder.add(object); + objects[index] = object; + index++; + } + setNextWhilt(); + } - /** - * Sets the object spawns. - */ - public void setObjects() { - for (int i = 0; i < locations.length; i++) { - objects[i] = RegionManager.getObject(locations[i]); - } - } + /** + * Whilts the wheat. + */ + public void whilt() { + busyTicks = GameWorld.getTicks() + 5; + for (Scenery object : objects) { + if (object == null) { + continue; + } + if (removed) { + submitWorldPulse(new Pulse() { + int counter = 0; - /** - * Sets the next whilt. - */ - public void setNextWhilt() { - this.nextWhilt = GameWorld.getTicks() + RandomFunction.random(40, 300); - } + @Override + public boolean pulse() { + if (counter++ == 0) { + animateScenery(object, 6596); + setDelay(animationDuration(new Animation(6596))); + return false; + } + return true; + } + }); + SceneryBuilder.add(object); + continue; + } + submitWorldPulse(new Pulse() { + int counter = 0; - /** - * Checks if the wheat can whilt. - * @return {@code True} if so. - */ - public boolean canWhilt() { - return GameWorld.getTicks() > nextWhilt && GameWorld.getTicks() > busyTicks; - } + @Override + public boolean pulse() { + if (counter++ == 0) { + animateScenery(object, 6599); + setDelay(animationDuration(new Animation(6599))); + return false; + } + SceneryBuilder.remove(object); + return true; + } + }); + } + removed = !removed; + setNextWhilt(); + } - /** - * Gets the nextWhilt. - * @return The nextWhilt. - */ - public int getNextWhilt() { - return nextWhilt; - } + /** + * Sets the object spawns. + */ + public void setObjects() { + for (int i = 0; i < locations.length; i++) { + objects[i] = RegionManager.getObject(locations[i]); + } + } - /** - * Sets the nextWhilt. - * @param nextWhilt The nextWhilt to set. - */ - public void setNextWhilt(int nextWhilt) { - this.nextWhilt = nextWhilt; - } + /** + * Sets the next whilt. + */ + public void setNextWhilt() { + this.nextWhilt = GameWorld.getTicks() + RandomFunction.random(40, 300); + } - /** - * Gets the locations. - * @return The locations. - */ - public Location[] getLocations() { - return locations; - } + /** + * Checks if the wheat can whilt. + * + * @return {@code True} if so. + */ + public boolean canWhilt() { + return GameWorld.getTicks() > nextWhilt && GameWorld.getTicks() > busyTicks; + } - } + /** + * Gets the nextWhilt. + * + * @return The nextWhilt. + */ + public int getNextWhilt() { + return nextWhilt; + } + + /** + * Sets the nextWhilt. + * + * @param nextWhilt The nextWhilt to set. + */ + public void setNextWhilt(int nextWhilt) { + this.nextWhilt = nextWhilt; + } + + /** + * Gets the locations. + * + * @return The locations. + */ + public Location[] getLocations() { + return locations; + } + + } } diff --git a/Server/src/main/content/minigame/pyramidplunder/GuardianMummyDialogue.kt b/Server/src/main/content/minigame/pyramidplunder/GuardianMummyDialogue.kt index afc509f39..b342116b1 100644 --- a/Server/src/main/content/minigame/pyramidplunder/GuardianMummyDialogue.kt +++ b/Server/src/main/content/minigame/pyramidplunder/GuardianMummyDialogue.kt @@ -9,52 +9,52 @@ import org.rs09.consts.NPCs import core.tools.END_DIALOGUE @Initializable -class GuardianMummyDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { +class GuardianMummyDialogue(player: Player? = null) : DialoguePlugin(player) { override fun open(vararg args: Any?): Boolean { - npcl(core.game.dialogue.FacialExpression.OLD_NOT_INTERESTED, "*sigh* Not another one.") + npcl(FacialExpression.OLD_NOT_INTERESTED, "*sigh* Not another one.") return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 0 -> playerl(core.game.dialogue.FacialExpression.HALF_ASKING, "Another what?").also { stage++ } - 1 -> npcl(core.game.dialogue.FacialExpression.OLD_NOT_INTERESTED, "Another 'archaeologist'. I'm not going to let you plunder my master's tomb you know.").also { stage++ } - 2 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL, "That's a shame. Have you got anything else I could do while I'm here?").also { stage++ } - 3 -> npcl(core.game.dialogue.FacialExpression.OLD_NOT_INTERESTED, "If it will keep you out of mischief I suppose I could set something up for you... I have a few rooms full of some things you humans might consider valuable, do you want to give it a go?").also { stage++ } + 0 -> playerl(FacialExpression.HALF_ASKING, "Another what?").also { stage++ } + 1 -> npcl(FacialExpression.OLD_NOT_INTERESTED, "Another 'archaeologist'. I'm not going to let you plunder my master's tomb you know.").also { stage++ } + 2 -> playerl(FacialExpression.NEUTRAL, "That's a shame. Have you got anything else I could do while I'm here?").also { stage++ } + 3 -> npcl(FacialExpression.OLD_NOT_INTERESTED, "If it will keep you out of mischief I suppose I could set something up for you... I have a few rooms full of some things you humans might consider valuable, do you want to give it a go?").also { stage++ } 4 -> player.dialogueInterpreter.sendOptions("Play Pyramid Plunder?", "That sounds like fun; what do I do?", "Not right now.", "I know what I'm doing, so let's get on with it.").also { stage++ } 5 -> when(buttonId) { - 1 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY, "That sounds like fun; what do I do?").also { stage = 10 } - 2 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL, "Not right now.").also { stage = END_DIALOGUE } - 3 -> playerl(core.game.dialogue.FacialExpression.ANNOYED, "I know what I'm doing, so let's get on with it.").also { stage = 100 } + 1 -> playerl(FacialExpression.FRIENDLY, "That sounds like fun; what do I do?").also { stage = 10 } + 2 -> playerl(FacialExpression.NEUTRAL, "Not right now.").also { stage = END_DIALOGUE } + 3 -> playerl(FacialExpression.ANNOYED, "I know what I'm doing, so let's get on with it.").also { stage = 100 } } - 10 -> npcl(core.game.dialogue.FacialExpression.OLD_NOT_INTERESTED, "You have five minutes to explore the treasure rooms and collect as many artefacts as you can. The artefacts are in the urns, chests and sarcophagi found in each room.").also { stage++ } - 11 -> npcl(core.game.dialogue.FacialExpression.OLD_NOT_INTERESTED, "There are eight treasure rooms, each subsequent room requires higher thieving skills to both enter the room and thieve from the urns and other containers.").also { stage++ } - 12 -> npcl(core.game.dialogue.FacialExpression.OLD_NOT_INTERESTED, "The rewards also become more lucrative the further into the tomb you go. You will also have to deactivate a trap in order to enter the main part of each room.").also { stage++ } - 13 -> npcl(core.game.dialogue.FacialExpression.OLD_NOT_INTERESTED, "When you want to move onto the next room you need to find the correct door first.").also { stage++ } - 14 -> npcl(core.game.dialogue.FacialExpression.OLD_NOT_INTERESTED, "There are four possible exits... you must open the door before finding out whether it is the exit or not. Opening the doors require picking their locks. Having a lockpick will make this easier.").also { stage++ } + 10 -> npcl(FacialExpression.OLD_NOT_INTERESTED, "You have five minutes to explore the treasure rooms and collect as many artefacts as you can. The artefacts are in the urns, chests and sarcophagi found in each room.").also { stage++ } + 11 -> npcl(FacialExpression.OLD_NOT_INTERESTED, "There are eight treasure rooms, each subsequent room requires higher thieving skills to both enter the room and thieve from the urns and other containers.").also { stage++ } + 12 -> npcl(FacialExpression.OLD_NOT_INTERESTED, "The rewards also become more lucrative the further into the tomb you go. You will also have to deactivate a trap in order to enter the main part of each room.").also { stage++ } + 13 -> npcl(FacialExpression.OLD_NOT_INTERESTED, "When you want to move onto the next room you need to find the correct door first.").also { stage++ } + 14 -> npcl(FacialExpression.OLD_NOT_INTERESTED, "There are four possible exits... you must open the door before finding out whether it is the exit or not. Opening the doors require picking their locks. Having a lockpick will make this easier.").also { stage++ } 15 -> player.dialogueInterpreter.sendOptions("Do you have any more questions?", "How do I leave the game?", "How do I get the artefacts?", "What do I do with the artefacts I collect?", "I'm ready to give it a go now.").also { stage++ } 16 -> when(buttonId) { - 1 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY, "How do I leave the game?").also { stage = 20 } - 2 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY, "How do I get the artefacts?").also { stage = 30 } - 3 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY, "What do I do with the artefacts?").also { stage = 40 } - 4 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY, "I'm ready to give it a go now.").also { stage = 100 } + 1 -> playerl(FacialExpression.FRIENDLY, "How do I leave the game?").also { stage = 20 } + 2 -> playerl(FacialExpression.FRIENDLY, "How do I get the artefacts?").also { stage = 30 } + 3 -> playerl(FacialExpression.FRIENDLY, "What do I do with the artefacts?").also { stage = 40 } + 4 -> playerl(FacialExpression.FRIENDLY, "I'm ready to give it a go now.").also { stage = 100 } } - 20 -> npcl(core.game.dialogue.FacialExpression.OLD_NOT_INTERESTED, "If at any point you decide you need to leave just use a glowing door. The game will end and you will be taken out of the pyramid").also { stage = 15 } + 20 -> npcl(FacialExpression.OLD_NOT_INTERESTED, "If at any point you decide you need to leave just use a glowing door. The game will end and you will be taken out of the pyramid").also { stage = 15 } - 30 -> npcl(core.game.dialogue.FacialExpression.OLD_NOT_INTERESTED, "The artefacts are in the urns, chests and sarcophagi. Urns contain snakes that guard them. The sarcophagi take some strength to open. They take a while to open.").also { stage++ } - 31 -> npcl(core.game.dialogue.FacialExpression.OLD_NOT_INTERESTED, "Of course, Mummies have been known to take a nap in the sarcophagi, so beware. The golden chests generally contain better artefacts, but are also trapped with scarabs!").also { stage = 15 } + 30 -> npcl(FacialExpression.OLD_NOT_INTERESTED, "The artefacts are in the urns, chests and sarcophagi. Urns contain snakes that guard them. The sarcophagi take some strength to open. They take a while to open.").also { stage++ } + 31 -> npcl(FacialExpression.OLD_NOT_INTERESTED, "Of course, Mummies have been known to take a nap in the sarcophagi, so beware. The golden chests generally contain better artefacts, but are also trapped with scarabs!").also { stage = 15 } - 40 -> npcl(core.game.dialogue.FacialExpression.OLD_NOT_INTERESTED, "There are a number of different artefacts, of three main types. The least valuable are the pottery statuettes and scarabs, and the ivory combs.").also { stage++ } - 41 -> npcl(core.game.dialogue.FacialExpression.OLD_NOT_INTERESTED, "Next are the stone scarabs, statuettes and seals, and finally the gold versions of those artefacts. They are not old, but are well made.").also { stage++ } - 42 -> playerl(core.game.dialogue.FacialExpression.HALF_ASKING, "What do I do with artefacts once I've collected them?").also { stage++ } - 43 -> npcl(core.game.dialogue.FacialExpression.OLD_NOT_INTERESTED, "That Simon Simpleton, I mean Templeton, will probably give you some money for them. He couldn't spot a real artefact if it came up to him and bit him in the face.").also { stage++ } - 44 -> npcl(core.game.dialogue.FacialExpression.OLD_NOT_INTERESTED, "He usually slinks about near the pyramid north-east of Sophanem. I expect he's trying to get some poor fools to steal things from that pyramid as well.").also { stage = 15 } + 40 -> npcl(FacialExpression.OLD_NOT_INTERESTED, "There are a number of different artefacts, of three main types. The least valuable are the pottery statuettes and scarabs, and the ivory combs.").also { stage++ } + 41 -> npcl(FacialExpression.OLD_NOT_INTERESTED, "Next are the stone scarabs, statuettes and seals, and finally the gold versions of those artefacts. They are not old, but are well made.").also { stage++ } + 42 -> playerl(FacialExpression.HALF_ASKING, "What do I do with artefacts once I've collected them?").also { stage++ } + 43 -> npcl(FacialExpression.OLD_NOT_INTERESTED, "That Simon Simpleton, I mean Templeton, will probably give you some money for them. He couldn't spot a real artefact if it came up to him and bit him in the face.").also { stage++ } + 44 -> npcl(FacialExpression.OLD_NOT_INTERESTED, "He usually slinks about near the pyramid north-east of Sophanem. I expect he's trying to get some poor fools to steal things from that pyramid as well.").also { stage = 15 } - 100 -> npcl(core.game.dialogue.FacialExpression.OLD_NOT_INTERESTED, "Alright, fine.").also { stage++ } + 100 -> npcl(FacialExpression.OLD_NOT_INTERESTED, "Alright, fine.").also { stage++ } 101 -> { end() PyramidPlunderMinigame.join(player) @@ -68,7 +68,7 @@ class GuardianMummyDialogue(player: Player? = null) : core.game.dialogue.Dialogu return intArrayOf(NPCs.GUARDIAN_MUMMY_4476) } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return GuardianMummyDialogue(player) } } \ No newline at end of file diff --git a/Server/src/main/content/minigame/pyramidplunder/PharaohSceptre.kt b/Server/src/main/content/minigame/pyramidplunder/PharaohSceptre.kt index 5627fd664..a295a8795 100644 --- a/Server/src/main/content/minigame/pyramidplunder/PharaohSceptre.kt +++ b/Server/src/main/content/minigame/pyramidplunder/PharaohSceptre.kt @@ -1,8 +1,6 @@ package content.minigame.pyramidplunder -import core.api.EquipmentSlot -import core.api.openDialogue -import core.api.sendMessage +import core.api.* import core.game.world.GameWorld.Pulser import content.minigame.pyramidplunder.PyramidPlunderMinigame.Companion.GUARDIAN_ROOM import core.game.node.entity.player.Player @@ -15,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 @@ -25,6 +24,9 @@ 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, Quests.ICTHLARINS_LITTLE_HELPER)) + return@on true + val sceptre = node.asItem() if(sceptre.id == SCEPTRES.last()) @@ -121,4 +123,4 @@ class PharaohSceptre : InteractionListener { private val GRAPHICS = Graphics(715) private val ANIMATION = Animation(714) } -} \ No newline at end of file +} diff --git a/Server/src/main/content/minigame/pyramidplunder/PlunderUtils.kt b/Server/src/main/content/minigame/pyramidplunder/PlunderUtils.kt index 2d99034b2..896a08792 100644 --- a/Server/src/main/content/minigame/pyramidplunder/PlunderUtils.kt +++ b/Server/src/main/content/minigame/pyramidplunder/PlunderUtils.kt @@ -144,13 +144,9 @@ object PlunderUtils { fun resetObjectVarbits(player: Player) { - var varp = player.varpManager.get(821) - varp.clear() - varp.send(player) - varp = player.varpManager.get(820) - varp.clear() - varp.send(player) - PlunderData.doorVarbits.forEach { player.varpManager.setVarbit(it, 0) } + setVarp(player, 821, 0) + setVarp(player, 820, 0) + PlunderData.doorVarbits.forEach { setVarbit(player, it, 0) } } fun openOverlay(player: Player) @@ -161,20 +157,22 @@ object PlunderUtils { fun updateOverlay(player: Player) { - player.varpManager.setVarbit(2375, 500 - (PlunderData.timeLeft[player] ?: 0)) - player.varpManager.setVarbit(2376, 11 + (getRoom(player)!!.room * 10)) - player.varpManager.setVarbit(2377, getRoom(player)!!.room) + setVarbit(player, 2375, 500 - (PlunderData.timeLeft[player] ?: 0)) + setVarbit(player, 2376, 11 + (getRoom(player)!!.room * 10)) + setVarbit(player, 2377, getRoom(player)!!.room) } fun resetOverlay(player: Player) { - player.varpManager.setVarbit(2375, 0) + setVarbit(player, 2375, 0) player.packetDispatch.resetInterface(Components.NTK_OVERLAY_428) player.interfaceManager.closeOverlay() } fun getDoor(player: Player) : Int { + if (getRoom(player) == null) + return -1 val room = getRoom(player)!!.room return PlunderData.doors[room - 1] } @@ -223,6 +221,10 @@ object PlunderUtils { fun getChestXp(player: Player): Double { + if (getRoom(player) == null) { + expel(player, false) + return 0.0 + } val room = getRoom(player)!!.room return when(room) { @@ -240,7 +242,7 @@ object PlunderUtils { fun getDoorXp(player: Player, lockpick: Boolean) : Double { - val room = getRoom(player)!!.room + val room = getRoom(player)?.room ?: return 0.0 var reward = when(room) { 1 -> 60.0 @@ -304,4 +306,4 @@ object PlunderUtils { val room = getRoom(player)!!.room return RandomFunction.random(level) > (room * if(charmed) 2 else 4) } -} \ No newline at end of file +} diff --git a/Server/src/main/content/minigame/pyramidplunder/PyramidPlunderMinigame.kt b/Server/src/main/content/minigame/pyramidplunder/PyramidPlunderMinigame.kt index d6ceb84c1..057e5a94e 100644 --- a/Server/src/main/content/minigame/pyramidplunder/PyramidPlunderMinigame.kt +++ b/Server/src/main/content/minigame/pyramidplunder/PyramidPlunderMinigame.kt @@ -3,7 +3,6 @@ package content.minigame.pyramidplunder import core.api.* import core.game.node.entity.player.Player import core.game.node.entity.skill.Skills -import core.game.node.entity.state.EntityState import core.game.system.task.Pulse import core.game.world.map.Direction import core.game.world.map.Location @@ -117,14 +116,14 @@ class PyramidPlunderMinigame : InteractionListener, TickListener, LogoutListener animate(player, URN_BIT) sendMessage(player, "You've been bitten by something moving around in the urn.") impact(player, RandomFunction.random(1,5)) - player.stateManager.register(EntityState.POISONED, true, 20, player) + applyPoison(player, player, 2) } else { animate(player, URN_SUCCESS) sendMessage(player, "You successfully loot the urn.") rewardXP(player, Skills.THIEVING, PlunderUtils.getUrnXp(player, false)) addItemOrDrop(player, PlunderUtils.rollArtifact(player, 1)) - player.varpManager.setVarbit(node.asScenery().definition.varbitID, 1) + setVarbit(player, node.asScenery().definition.varbitID, 1) } } return@on true @@ -137,7 +136,7 @@ class PyramidPlunderMinigame : InteractionListener, TickListener, LogoutListener lock(player, 1) runTask(player, 1){ animate(player, URN_BIT) - player.varpManager.setVarbit(urn.definition.varbitID, 2) + setVarbit(player, urn.definition.varbitID, 2) animateScenery(urn, SNAKE_URN_ANIM) } return@on true @@ -153,7 +152,7 @@ class PyramidPlunderMinigame : InteractionListener, TickListener, LogoutListener animate(player, CHARM_ANIM) runTask(player, 1) { - player.varpManager.setVarbit(node.asScenery().definition.varbitID, 3) + setVarbit(player, node.asScenery().definition.varbitID, 3) sendMessage(player, "You charm the snake with your music.") } return@on true @@ -169,14 +168,14 @@ class PyramidPlunderMinigame : InteractionListener, TickListener, LogoutListener animate(player, URN_BIT) sendMessage(player, "You've been bitten by something moving around in the urn.") impact(player, RandomFunction.random(1,5)) - player.stateManager.register(EntityState.POISONED, true, 20, player) + applyPoison(player, player, 2) } else { animate(player, URN_SUCCESS) sendMessage(player, "You successfully loot the urn.") addItemOrDrop(player, PlunderUtils.rollArtifact(player, 1)) rewardXP(player, Skills.THIEVING, PlunderUtils.getUrnXp(player, false) * 0.66) - player.varpManager.setVarbit(node.asScenery().definition.varbitID, 1) + setVarbit(player, node.asScenery().definition.varbitID, 1) } } return@on true @@ -188,6 +187,10 @@ class PyramidPlunderMinigame : InteractionListener, TickListener, LogoutListener } on(SARCOPHAGUS, IntType.SCENERY, "open") { player, node -> + if (PlunderUtils.getRoom(player) == null) { + PlunderUtils.expel(player, false) + return@on true + } sendMessage(player, "You attempt to push open the massive lid.") val strength = getDynLevel(player, Skills.STRENGTH) animate(player, PUSH_LID_START) @@ -198,12 +201,16 @@ class PyramidPlunderMinigame : InteractionListener, TickListener, LogoutListener if(RandomFunction.random(125) > strength) return false animate(player, PUSH_LID_FINISH) + if (PlunderUtils.getRoom(player) == null) { + PlunderUtils.expel(player, false) + return true + } runTask(player, 3){ - player.varpManager.setVarbit(node.asScenery().definition.varbitID, 1) + setVarbit(player, node.asScenery().definition.varbitID, 1) rewardXP(player, Skills.STRENGTH, PlunderUtils.getSarcophagusXp(player)) if(RandomFunction.roll(25)) { - val mummy = content.minigame.pyramidplunder.PyramidPlunderMummyNPC(player.location, player) + val mummy = PyramidPlunderMummyNPC(player.location, player) mummy.isRespawn = false mummy.init() mummy.attack(player) @@ -220,11 +227,15 @@ class PyramidPlunderMinigame : InteractionListener, TickListener, LogoutListener } on(Scenery.GRAND_GOLD_CHEST_16473, IntType.SCENERY, "search") { player, node -> + if (PlunderUtils.getRoom(player) == null) { + PlunderUtils.expel(player, false) + return@on true + } animate(player, OPEN_CHEST_ANIM) runTask(player){ if(RandomFunction.roll(25)) { - val swarm = content.minigame.pyramidplunder.PyramidPlunderSwarmNPC(player.location, player) + val swarm = PyramidPlunderSwarmNPC(player.location, player) swarm.isRespawn = false swarm.init() swarm.attack(player) @@ -236,7 +247,7 @@ class PyramidPlunderMinigame : InteractionListener, TickListener, LogoutListener if(!PlunderUtils.rollSceptre(player)) addItemOrDrop(player, PlunderUtils.rollArtifact(player, 3)) } - player.varpManager.setVarbit(node.asScenery().definition.varbitID, 1) + setVarbit(player, node.asScenery().definition.varbitID, 1) } return@on true } @@ -268,13 +279,19 @@ class PyramidPlunderMinigame : InteractionListener, TickListener, LogoutListener if(RandomFunction.roll(rate)) { val varbitId = node.asScenery().definition.varbitID - rewardXP(player, Skills.THIEVING, PlunderUtils.getDoorXp(player, rate == 3)) - if(PlunderUtils.getDoor(player) == varbitId) { + val door = PlunderUtils.getDoor(player) + + if (door == -1) + PlunderUtils.expel(player, false) + else + rewardXP(player, Skills.THIEVING, PlunderUtils.getDoorXp(player, rate == 3)) + + if (door == varbitId) { PlunderUtils.loadNextRoom(player) PlunderUtils.resetObjectVarbits(player) } else { sendMessage(player, "The door leads to a dead end.") - player.varpManager.setVarbit(varbitId, 1) + setVarbit(player, varbitId, 1) } } else @@ -375,4 +392,4 @@ class PyramidPlunderMinigame : InteractionListener, TickListener, LogoutListener PlunderUtils.openOverlay(player) } } -} \ No newline at end of file +} diff --git a/Server/src/main/content/minigame/sorceress/GardenObjectsPlugin.kt b/Server/src/main/content/minigame/sorceress/GardenObjectsPlugin.kt index 89c33650a..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 { @@ -452,7 +453,7 @@ class GardenObjectsPlugin : InteractionListener { @Throws(Throwable::class) override fun newInstance(arg: Any?): Plugin { - UseWithHandler.addHandler(233, UseWithHandler.ITEM_TYPE, this) + addHandler(233, ITEM_TYPE, this) return this } @@ -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 5e19d4503..bc3aa169b 100644 --- a/Server/src/main/content/minigame/sorceress/SorceressApprenticeDialogue.java +++ b/Server/src/main/content/minigame/sorceress/SorceressApprenticeDialogue.java @@ -10,6 +10,9 @@ import core.game.world.GameWorld; 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 * @author SonicForce41 @@ -236,6 +239,8 @@ public class SorceressApprenticeDialogue extends DialoguePlugin { } public static void teleport(final NPC npc, final Player player) { + if (!hasRequirement(player, Quests.PRINCE_ALI_RESCUE)) + return; npc.faceTemporary(player, 4); npc.graphics(new Graphics(108)); player.lock(); diff --git a/Server/src/main/content/minigame/vinesweeper/Vinesweeper.kt b/Server/src/main/content/minigame/vinesweeper/Vinesweeper.kt index 8229c1530..9fe119605 100644 --- a/Server/src/main/content/minigame/vinesweeper/Vinesweeper.kt +++ b/Server/src/main/content/minigame/vinesweeper/Vinesweeper.kt @@ -3,33 +3,6 @@ package content.minigame.vinesweeper import BlinkinDialogue import FarmerDialogue.Companion.FARMER_FLAG_LINES import WinkinDialogue -import core.api.* -import core.game.component.Component -import core.game.interaction.MovementPulse -import core.game.node.entity.Entity -import core.game.node.entity.combat.DeathTask -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.skill.Skills -import core.game.node.entity.state.EntityState -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.system.task.Pulse -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.context.Animation -import core.game.world.update.flag.context.Graphics -import core.plugin.Initializable -import core.tools.RandomFunction -import org.rs09.consts.Animations -import org.rs09.consts.Components -import org.rs09.consts.Items -import org.rs09.consts.NPCs import content.minigame.vinesweeper.Vinesweeper.Companion.FARMERS import content.minigame.vinesweeper.Vinesweeper.Companion.FARMER_CLEAR_RADIUS import content.minigame.vinesweeper.Vinesweeper.Companion.HOLES @@ -39,11 +12,37 @@ 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.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 +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.skill.Skills +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.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 +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.* +import kotlin.math.max +import kotlin.math.min import org.rs09.consts.Graphics as Gfx import org.rs09.consts.Scenery as Sceneries @@ -64,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) @@ -86,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 -> @@ -214,9 +218,9 @@ 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; + val xp = points / points_per_xp player.skills.addExperience(Skills.FARMING, xp) player.setAttribute("/save:vinesweeper:points", 0) sendUpdatedPoints(player) @@ -361,8 +365,8 @@ class Vinesweeper : InteractionListener, InterfaceListener, MapArea { val VINESWEEPER_BORDERS = ZoneBorders(1600,4672,1663,4735) fun sendUpdatedPoints(player: Player) { - val points = player.getAttribute("vinesweeper:points", 0); - player.varpManager.get(1195).setVarbit(6, points).send(player) + val points = player.getAttribute("vinesweeper:points", 0) + setVarbit(player, 4449, points) } var SEED_LOCS: HashSet = HashSet() @@ -373,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)) { @@ -382,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() @@ -413,7 +434,7 @@ class Vinesweeper : InteractionListener, InterfaceListener, MapArea { player.skills.addExperience(Skills.HUNTER, 30.0) rabbit.sendChat("Squeak!") rabbit.animate(Animation(Animations.RABBIT_EAT_OGLEROOT_8734)) - rabbit.lock(3) + rabbit.lock(4) GameWorld.Pulser.submit(object : Pulse(3, player) { override fun pulse(): Boolean { rabbit.finalizeDeath(player) @@ -432,7 +453,7 @@ class Vinesweeper : InteractionListener, InterfaceListener, MapArea { val oldPoints = player.getAttribute("vinesweeper:points", 0) player.setAttribute("/save:vinesweeper:points", Math.max(oldPoints-10, 0)) sendUpdatedPoints(player) - player.sendMessage("Oh dear! It looks like you dug up a potato seed by mistake."); + player.sendMessage("Oh dear! It looks like you dug up a potato seed by mistake.") scheduleNPCs(player, loc, false, false) val scenery = getScenery(loc) if(scenery != null) { @@ -498,7 +519,7 @@ class Vinesweeper : InteractionListener, InterfaceListener, MapArea { object VinesweeperTeleport { @JvmStatic fun teleport(npc: NPC, player: Player) { - if (player.stateManager.hasState(EntityState.TELEBLOCK)) { + if (hasTimerActive(player, "teleblock")) { sendNPCDialogue(player, npc.id, "I can't do that, you're teleblocked!", core.game.dialogue.FacialExpression.OLD_ANGRY1) return } @@ -506,7 +527,7 @@ class Vinesweeper : InteractionListener, InterfaceListener, MapArea { npc.faceTemporary(player, 1) npc.graphics(Graphics(108)) player.lock() - player.audioManager.send(125) + playAudio(player, Sounds.CURSE_ALL_125, 0, 1) Projectile.create(npc, player, 109).send() npc.sendChat("Avach nimporto!") GameWorld.Pulser.submit(object : Pulse(1, player) { @@ -535,7 +556,8 @@ class Vinesweeper : InteractionListener, InterfaceListener, MapArea { @Initializable class VinesweeperNPC : AbstractNPC { - var seedDestinations: ArrayList = ArrayList(); + var seedDestinations: ArrayList = ArrayList() + constructor() : super(RABBITS[0], null, true) {} private constructor(id: Int, location: Location) : super(id, location) {} override fun construct(id: Int, location: Location, vararg objects: Any?): AbstractNPC { @@ -551,6 +573,8 @@ class VinesweeperNPC : AbstractNPC { } override fun handleTickActions() { val dest = seedDestinations.find { sd -> sd.loc == location } + if (locks.isMovementLocked() || locks.isInteractionLocked()) + return if(dest != null) { for(npc in RegionManager.getRegionPlane(location).npcs) { if(npc is VinesweeperNPC) { diff --git a/Server/src/main/content/minigame/vinesweeper/VinesweeperDialogues.kt b/Server/src/main/content/minigame/vinesweeper/VinesweeperDialogues.kt index f51a18bde..23c012487 100644 --- a/Server/src/main/content/minigame/vinesweeper/VinesweeperDialogues.kt +++ b/Server/src/main/content/minigame/vinesweeper/VinesweeperDialogues.kt @@ -4,6 +4,7 @@ import core.game.node.item.Item import org.rs09.consts.Items import core.game.dialogue.DialogueFile import core.tools.END_DIALOGUE +import core.api.* abstract class FarmerDialogue : DialogueFile() { @@ -129,10 +130,11 @@ class BlinkinDialogue : FarmerDialogue() { 40 -> playerl("Do you have any Ogleroots to feed the rabbits?").also { stage++ } 41 -> npcl("I sure do. They'll cost ya 10 gold each. Any ya leave with will be returned to us, but ya'll get yer money back for 'em. How many do ya want?").also { stage++ } 42 -> { - player!!.setAttribute("runscript") { amount: Int -> + sendInputDialogue(player!!, InputType.AMOUNT, "Enter an amount:") { value -> + val amount = value as Int val price = Item(Items.COINS_995, 10 * amount) if(price.amount <= 0){ - return@setAttribute + return@sendInputDialogue } if(player!!.inventory.containsItem(price) && player!!.inventory.remove(price)) { if(player!!.inventory.add(Item(Items.OGLEROOT_12624, amount))) { 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 39dd956b9..000000000 --- a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/AchiettiesDialogue.kt +++ /dev/null @@ -1,37 +0,0 @@ -package content.region.asgarnia.burthorpe.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 - -/** - * @author qmqz - */ - -@Initializable -class AchiettiesDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ - - override fun open(vararg args: Any?): Boolean { - npc = args[0] as NPC - npc(core.game.dialogue.FacialExpression.FRIENDLY,"Greetings. Welcome to the Heroes' Guild.") - stage = 99 - return true - } - - override fun handle(interfaceId: Int, buttonId: Int): Boolean { - when(stage){ - 99 -> end() - } - return true - } - - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { - return AchiettiesDialogue(player) - } - - override fun getIds(): IntArray { - return intArrayOf(796) - } -} \ No newline at end of file diff --git a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/AntonDialogue.java b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/AntonDialogue.java deleted file mode 100644 index 9ef0cf071..000000000 --- a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/AntonDialogue.java +++ /dev/null @@ -1,69 +0,0 @@ -package content.region.asgarnia.burthorpe.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 dialogue plugin used to handle the anton npc. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class AntonDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code AntonDialogue} {@code Object}. - */ - public AntonDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code AntonDialogue} {@code Object}. - * @param player the player. - */ - public AntonDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new AntonDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - npc("Ahhh, hello there. How can I help?"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - player("Looks like you have a good", "selection of weapons around here..."); - stage = 1; - break; - case 1: - npc("Indeed so, specially imported from the finest smiths", "around the lands, take a look at my wares."); - stage = 2; - break; - case 2: - end(); - npc.openShop(player); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 4295 }; - } -} diff --git a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/AntonDialogue.kt b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/AntonDialogue.kt new file mode 100644 index 000000000..f777ba082 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/AntonDialogue.kt @@ -0,0 +1,45 @@ +package content.region.asgarnia.burthorpe.dialogue + +import core.api.openDialogue +import core.api.openNpcShop +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 AntonDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, AntonDialogueFile(), npc) + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return AntonDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.ANTON_4295) + } +} + +class AntonDialogueFile : DialogueBuilderFile() { + + override fun create(b: DialogueBuilder) { + b.defaultDialogue().npcl( + FacialExpression.ASKING, + "Ahhh, hello there. How can I help?") + .playerl(FacialExpression.NEUTRAL, + "Looks like you have a good selection of weapons around here...") + .npcl(FacialExpression.FRIENDLY, + "Indeed so, specially imported from the finest smiths around the lands, take a look at my wares.") + .endWith { _, player -> + openNpcShop(player, NPCs.ANTON_4295) + end() + } + } +} \ 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 new file mode 100644 index 000000000..2e1ca6b44 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/BernaldDialogue.kt @@ -0,0 +1,46 @@ +package content.region.asgarnia.burthorpe.dialogue + +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 + +/** + * Bernald Dialogue + * @author ovenbread + * + * https://www.youtube.com/watch?v=4odb7qhBPfA + * https://www.youtube.com/watch?v=nisJKOtqWIo 1:08:53 + */ +@Initializable +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!!, 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 } +// } +// } +// } + when (stage) { + START_DIALOGUE -> npcl(FacialExpression.WORRIED, "Do you know anything about grapevine diseases?").also { stage++ } + 1 -> playerl(FacialExpression.FRIENDLY, "No, I'm afraid I don't.").also { stage++ } + 2 -> npcl(FacialExpression.GUILTY, "Oh, that's a shame. I hope I find someone soon, otherwise I could lose all of this year's crop.").also { stage = END_DIALOGUE } + } + return true + } + + override fun newInstance(player: Player): DialoguePlugin { + return BernaldDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.BERNALD_2580) + } +} diff --git a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/BreocaDialogue.java b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/BreocaDialogue.java deleted file mode 100644 index 552eeea62..000000000 --- a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/BreocaDialogue.java +++ /dev/null @@ -1,65 +0,0 @@ -package content.region.asgarnia.burthorpe.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 breoca dialogue plugin. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class BreocaDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code BreocaDialogue} {@code Object}. - */ - public BreocaDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code BreocaDialogue} {@code Object}. - * @param player the player. - */ - public BreocaDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new BreocaDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Hi!"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Hello stranger."); - stage = 1; - break; - case 1: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 1084 }; - } -} diff --git a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/BreocaDialogue.kt b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/BreocaDialogue.kt new file mode 100644 index 000000000..8a885cd94 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/BreocaDialogue.kt @@ -0,0 +1,49 @@ +package content.region.asgarnia.burthorpe.dialogue + +import core.api.isQuestComplete +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.NPCs +import content.data.Quests + +/** + * Breoca Dialogue + * @author 'Vexia + * @author ovenbread + */ +@Initializable +class BreocaDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int) : Boolean { + 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 } + 2 -> npcl(FacialExpression.HAPPY, "Thank you so much!").also { stage = END_DIALOGUE } + 3 -> npcl(FacialExpression.HAPPY, "Surely we are safe now!").also { stage = END_DIALOGUE } + } + } else { + when (stage) { + START_DIALOGUE -> playerl(FacialExpression.FRIENDLY, "Hi!").also { stage = (1..3).toIntArray().random() } + 1 -> npcl(FacialExpression.FRIENDLY, "Hello stranger.").also { stage = END_DIALOGUE } + 2 -> npcl(FacialExpression.FRIENDLY, "Hi!").also { stage = END_DIALOGUE } + 3 -> npcl(FacialExpression.FRIENDLY, "Welcome to Burthorpe!").also { stage++ } + 4 -> playerl(FacialExpression.FRIENDLY, "Thanks!").also { stage = END_DIALOGUE } + } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return BreocaDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.BREOCA_1084) + } +} + diff --git a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/BurthorpeBusySoldierListener.kt b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/BurthorpeBusySoldierListener.kt new file mode 100644 index 000000000..47982972f --- /dev/null +++ b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/BurthorpeBusySoldierListener.kt @@ -0,0 +1,48 @@ +package content.region.asgarnia.burthorpe.dialogue + +import core.api.* +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import org.rs09.consts.NPCs + +/** + * Burthorpe Busy Soldiers Messages + * @author ovenbread + * + * https://www.youtube.com/watch?v=5RfEUmIdH10 for some busy interactions + * Covers the Sergeant/TrainingSoldier/EatingSoldier/Archer/Guard cases. + * These NPCs do not talk to you as they are busy. + */ +class BurthorpeBusySoldierListener: InteractionListener { + override fun defineListeners() { + // For sergeants. + on(intArrayOf(NPCs.SERGEANT_1061, NPCs.SERGEANT_1062), IntType.NPC, "talk-to") { player, npc -> + sendMessage(player, "The Sergeant is busy training the soldiers.") + return@on true + } + + // For soldiers training. + on(intArrayOf(NPCs.SOLDIER_1063, NPCs.SOLDIER_1064), IntType.NPC, "talk-to") { player, npc -> + sendMessage(player, "The soldier is busy training.") + return@on true + } + + // For soldiers sitting around the fire. + on(intArrayOf(NPCs.SOLDIER_1066, NPCs.SOLDIER_1067, NPCs.SOLDIER_1068), IntType.NPC, "talk-to") { player, npc -> + sendMessage(player, "The soldier is busy eating.") + return@on true + } + + // For archers at the castle. + on(intArrayOf(NPCs.ARCHER_1073, NPCs.ARCHER_1074), IntType.NPC, "talk-to") { player, npc -> + sendMessage(player, "The archer won't talk whilst on duty.") + return@on true + } + + // For soldiers at the castle. + on(intArrayOf(NPCs.GUARD_1076, NPCs.GUARD_1077), IntType.NPC, "talk-to") { player, npc -> + sendMessage(player, "The guard won't talk whilst on duty.") + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/BurthorpeSoldierDialogue.java b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/BurthorpeSoldierDialogue.java deleted file mode 100644 index b34e156ce..000000000 --- a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/BurthorpeSoldierDialogue.java +++ /dev/null @@ -1,69 +0,0 @@ -package content.region.asgarnia.burthorpe.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 burthorpe soldier dialogue plugin. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class BurthorpeSoldierDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code BurthorpeSoldierDialogue} {@code Object}. - */ - public BurthorpeSoldierDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code BurthorpeSoldierDialogue} {@code Object}. - * @param player the player. - */ - public BurthorpeSoldierDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new BurthorpeSoldierDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Hi!"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Vacca foeda."); - stage = 1; - break; - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Er..."); - stage = 2; - break; - case 2: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 1065 }; - } -} diff --git a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/BurthorpeSoldierDialogue.kt b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/BurthorpeSoldierDialogue.kt new file mode 100644 index 000000000..e73aa0ee2 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/BurthorpeSoldierDialogue.kt @@ -0,0 +1,81 @@ +package content.region.asgarnia.burthorpe.dialogue + +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 + +/** + * Burthorpe Soldier Dialogue + * @author 'Vexia + * @author ovenbread + * + * https://www.youtube.com/watch?v=5RfEUmIdH10 for full interaction + * https://www.youtube.com/watch?v=ew0wTgv-2Kc for insults + */ +@Initializable +class BurthorpeSoldierDialogue(player: Player? = null) : DialoguePlugin(player) { + + companion object { + val latinInsults = arrayOf( + "Mihi ignosce. Cum homine de cane debeo congredi.", + "Errare humanum est.", + "Die dulci freure.", + "Carpe Diem!", + "Te audire non possum. Musa sapientum fixa est in aure.", + "Furnulum pani nolo.", + "Fac ut gaudeam.", + "Utinam barbari spatium proprium tuum invadant!", + "Quantum materiae materietur marmota monax si marmota monax materiam possit materiari?", + "Sona si Latine loqueris.", + "Raptus Regaliter", + "Nemo dat quod non habet.", + "Ne auderis delere orbem rigidum meum!", + "Da mihi sis bubulae frustum assae, solana tuberosa in modo gallico fricta, ac quassum lactatum coagulatum crassum.", + "Cogito ergo sum.", + "Vacca foeda.", + "Di! Ecce hora! Uxor mea me necabit!", + "Latine loqui coactus sum.", + "Cave ne ante ullas catapultas ambules.", + "Fac ut vivas!", + "Noli me vocare, ego te vocabo.", + "Meliora cogito.", + "Braccae tuae aperiuntur.", + "Vescere bracis meis.", + "Corripe cervisiam!", + ) + + val randomStages = arrayOf(10, 20, 30, 40, 50) + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage) { + START_DIALOGUE -> playerl(FacialExpression.FRIENDLY, "Hello!").also { stage++ } + 1 -> npcl(FacialExpression.ANGRY, latinInsults.random()).also { stage = randomStages.random() } + + 10 -> playerl(FacialExpression.THINKING, "What?!").also { stage = END_DIALOGUE } + + 20 -> playerl(FacialExpression.THINKING, "Huh?!").also { stage = END_DIALOGUE } + + 30 -> playerl(FacialExpression.HALF_GUILTY, "Er...").also { stage = END_DIALOGUE } + + 40 -> playerl(FacialExpression.HALF_GUILTY, "OK...").also { stage = END_DIALOGUE } + + 50 -> playerl(FacialExpression.THINKING, "Are you insulting me in Latin?").also { stage++ } + 51 -> npcl(FacialExpression.FRIENDLY, "Yes!").also { stage++ } + 52 -> playerl(FacialExpression.HALF_GUILTY, "Hmm...").also { stage = END_DIALOGUE } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return BurthorpeSoldierDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.SOLDIER_1065) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/CeolburgDialogue.java b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/CeolburgDialogue.java deleted file mode 100644 index ea9ee941a..000000000 --- a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/CeolburgDialogue.java +++ /dev/null @@ -1,65 +0,0 @@ -package content.region.asgarnia.burthorpe.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; - -/** - * Rerpesents the dialogue plugin used for the celburg npc. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class CeolburgDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code CeolburgDialogue} {@code Object}. - */ - public CeolburgDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code CeolburgDialogue} {@code Object}. - * @param player the player. - */ - public CeolburgDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new CeolburgDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Hi!"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Trolls!"); - stage = 1; - break; - case 1: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 1089 }; - } -} diff --git a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/CeolburgDialogue.kt b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/CeolburgDialogue.kt new file mode 100644 index 000000000..006641ebd --- /dev/null +++ b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/CeolburgDialogue.kt @@ -0,0 +1,48 @@ +package content.region.asgarnia.burthorpe.dialogue + +import core.api.isQuestComplete +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.NPCs +import content.data.Quests + +/** + * Ceolburg Dialogue + * @author 'Vexia + * @author ovenbread + */ +@Initializable +class CeolburgDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int) : Boolean { + 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 } + 2 -> npcl(FacialExpression.HAPPY, "Thank you so much!").also { stage = END_DIALOGUE } + 3 -> npcl(FacialExpression.HAPPY, "Surely we are safe now!").also { stage = END_DIALOGUE } + } + } else { + when (stage) { + START_DIALOGUE -> playerl(FacialExpression.FRIENDLY, "Hi!").also { stage = (1..3).toIntArray().random() } + 1 -> npcl(FacialExpression.FRIENDLY, "Hello stranger.").also { stage = END_DIALOGUE } + 2 -> npcl(FacialExpression.FRIENDLY, "Hi!").also { stage = END_DIALOGUE } + 3 -> npcl(FacialExpression.FRIENDLY, "Welcome to Burthorpe!").also { stage++ } + 4 -> playerl(FacialExpression.FRIENDLY, "Thanks!").also { stage = END_DIALOGUE } + } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return CeolburgDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.CEOLBURG_1089) + } +} diff --git a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/DenulthDialogue.java b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/DenulthDialogue.java deleted file mode 100644 index de0472c59..000000000 --- a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/DenulthDialogue.java +++ /dev/null @@ -1,78 +0,0 @@ -package content.region.asgarnia.burthorpe.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 denulth dialogue plugin. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class DenulthDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code DenulthDialogue} {@code Object}. - */ - public DenulthDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code DenulthDialogue} {@code Object}. - * @param player the player. - */ - public DenulthDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new DenulthDialogue(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, "Welcome back friend!"); - stage = 1; - break; - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "How goes your fight with trolls?"); - stage = 2; - break; - case 2: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Very good! We are winning."); - stage = 3; - break; - case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Good luck!"); - stage = 4; - break; - case 4: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 1060 }; - } - -} diff --git a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/DenulthDialogue.kt b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/DenulthDialogue.kt new file mode 100644 index 000000000..2724f9c19 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/DenulthDialogue.kt @@ -0,0 +1,106 @@ +package content.region.asgarnia.burthorpe.dialogue + +import content.data.Quests +import content.region.asgarnia.burthorpe.quest.deathplateau.DenulthDialogueFile +import core.api.isQuestComplete +import core.api.isQuestInProgress +import core.api.openDialogue +import core.api.setQuestStage +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 + +/** + * Denulth main dialogue. + * @author ovenbread + */ +@Initializable +class DenulthDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + + // When Troll Stronghold is complete + 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++ } + 2 -> showTopics( + Topic("How goes your fight with the trolls?", 10), + Topic("I thought the White Knights controlled Asgarnia?", 20), + Topic(FacialExpression.HAPPY, "See you about Denulth!", 30) + ) + 10 -> npcl(FacialExpression.FRIENDLY, "We are busy preparing for an attack by night. Godric knows of a secret entrance to the stronghold. Once we destroy the stronghold Burthorpe will be safe! Friend, we are indebted to you!").also { stage++ } + 11 -> playerl(FacialExpression.FRIENDLY, "Good luck!").also { stage = END_DIALOGUE } + + 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++ } + 21 -> npcl(FacialExpression.EVIL_LAUGH, "We are the prince's elite troops! We keep Burthorpe secure!").also { stage++ } + 22 -> npcl(FacialExpression.ANGRY, "The White Knights have overlooked us until now! They are pouring money into their war against the Black Knights, They are looking for an excuse to stop our funding and I'm afraid they may have found it!").also { stage++ } + 23 -> npcl(FacialExpression.HALF_WORRIED, "If we can not destroy the troll camp on Death Plateau then the Imperial Guard will be disbanded and Burthorpe will come under control of the White Knights. We cannot let this happen!").also { stage++ } + 24 -> npcl(FacialExpression.ASKING, "Is there anything else you'd like to know?").also { stage = 2 } + + 30 -> npcl(FacialExpression.FRIENDLY, "Saradomin be with you, friend!").also { stage = END_DIALOGUE } + } + return true + } + + // Troll Stronghold in progress + 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!!, Quests.DEATH_PLATEAU)) { + when(stage) { + START_DIALOGUE -> playerl(FacialExpression.FRIENDLY, "Hello!").also { stage++ } + 1 -> npcl(FacialExpression.HAPPY, "Welcome back friend!").also { stage++ } + 2 -> showTopics( + Topic("How goes your fight with the trolls?", 10), + Topic("I thought the White Knights controlled Asgarnia?", 20), + Topic(FacialExpression.HAPPY, "See you about Denulth!", 30) + ) + 10 -> npcl(FacialExpression.HALF_WORRIED, "I'm afraid I have bad news. We made our attack as planned, but we met unexpected resistance.").also { stage++ } + 11 -> playerl(FacialExpression.HALF_ASKING, "What happened?").also { stage++ } + 12 -> npcl(FacialExpression.WORRIED, "We were ambushed by trolls coming from the north. They captured Dunstan's son, Godric, who we enlisted at your request; we tried to follow but we were repelled at the foot of their stronghold.").also { stage++ } + 13 -> showTopics( + Topic(FacialExpression.SAD, "I'm sorry to hear that.", END_DIALOGUE), + Topic("Is there anything I can do to help?", 14) + ) + 14 -> npcl(FacialExpression.HALF_WORRIED, "The way to the stronghold is treacherous, friend. Even if you manage to climb your way up, there will be many trolls defending the stronghold.").also{ stage++ } + 15 -> showTopics( + Topic("I'll get Godric back!", 16), + Topic("I'm sorry, there's nothing I can do.", END_DIALOGUE) + ) + 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!!, 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++ } + 21 -> npcl(FacialExpression.EVIL_LAUGH, "We are the prince's elite troops! We keep Burthorpe secure!").also { stage++ } + 22 -> npcl(FacialExpression.ANGRY, "The White Knights have overlooked us until now! They are pouring money into their war against the Black Knights, They are looking for an excuse to stop our funding and I'm afraid they may have found it!").also { stage++ } + 23 -> npcl(FacialExpression.HALF_WORRIED, "If we can not destroy the troll camp on Death Plateau then the Imperial Guard will be disbanded and Burthorpe will come under control of the White Knights. We cannot let this happen!").also { stage++ } + 24 -> npcl(FacialExpression.ASKING, "Is there anything else you'd like to know?").also { stage = 2 } + + 30 -> npcl(FacialExpression.FRIENDLY, "Saradomin be with you, friend!").also { stage = END_DIALOGUE } + } + return true + } + + // Fallback to default. Always the start of Death Plateau + openDialogue(player!!, DenulthDialogueFile(), npc) + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return DenulthDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.DENULTH_1060) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/DunstanDialogue.java b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/DunstanDialogue.java deleted file mode 100644 index 43dec9fe4..000000000 --- a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/DunstanDialogue.java +++ /dev/null @@ -1,100 +0,0 @@ -package content.region.asgarnia.burthorpe.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; - -/** - * Represnts the dialogue plugin used for dunstan. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class DunstanDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code DunstanDialogue} {@code Object}. - */ - public DunstanDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code DunstanDialogue} {@code Object}. - * @param player the player. - */ - public DunstanDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new DunstanDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Hi!"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Hi! Did you want something?"); - stage = 1; - break; - case 1: - interpreter.sendOptions("Select an Option", "Is it OK if I use your anvil?", "Nothing, thanks."); - stage = 2; - break; - case 2: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Is it OK if I use your anvil?"); - stage = 10; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Nothing, thanks."); - stage = 20; - break; - } - break; - case 10: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "So you're a smithy are you?"); - stage = 11; - break; - case 11: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I dabble."); - stage = 12; - break; - case 12: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "A fellow smith is welcome to use my anvil!"); - stage = 13; - break; - case 13: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Thanks!"); - stage = 14; - break; - case 14: - end(); - break; - case 20: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 1082 }; - } -} diff --git a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/DunstanDialogue.kt b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/DunstanDialogue.kt new file mode 100644 index 000000000..73759083a --- /dev/null +++ b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/DunstanDialogue.kt @@ -0,0 +1,173 @@ +package content.region.asgarnia.burthorpe.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.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 + +/** + * Dunstan main dialogue. + * @author 'ovenbread + * + * https://www.youtube.com/watch?v=gbqcJp99Zd8 + * https://www.youtube.com/watch?v=ujtIALS1L7A + */ +@Initializable +class DunstanDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + // When Troll Stronghold is complete + 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), + // Sleds topic when Troll Romance is implemented. + ) + 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 = 2 } + 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!!, 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!!, 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), + ) + 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 = 2 } + 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!!, 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 + } + + // Default + when (stage) { + START_DIALOGUE -> playerl(FacialExpression.FRIENDLY, "Hi!").also { stage++ } + 1 -> npcl(FacialExpression.FRIENDLY, "Hi! Did you want something?").also { stage++ } + 2 -> showTopics( + Topic(FacialExpression.THINKING, "Is it OK if I use your anvil?", 10), + Topic(FacialExpression.FRIENDLY, "Nothing, thanks.", END_DIALOGUE), + ) + 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 = 2 } + } + return true + } + + override fun newInstance(player: Player): DialoguePlugin { + return DunstanDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.DUNSTAN_1082) + } +} diff --git a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/EadburgDialogue.java b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/EadburgDialogue.java deleted file mode 100644 index ef0623522..000000000 --- a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/EadburgDialogue.java +++ /dev/null @@ -1,73 +0,0 @@ -package content.region.asgarnia.burthorpe.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 eadburg npc. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class EadburgDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code EadburgDialogue} {@code Object}. - */ - public EadburgDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code EadburgDialogue} {@code Object}. - * @param player the player. - */ - public EadburgDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new EadburgDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "What's cooking, good looking?"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "The stew for the servant's main meal."); - stage = 1; - break; - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Um...er...see you later."); - stage = 2; - break; - case 2: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Bye!"); - stage = 3; - break; - case 3: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 1072 }; - } -} diff --git a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/EadburgDialogue.kt b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/EadburgDialogue.kt new file mode 100644 index 000000000..f2135a729 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/EadburgDialogue.kt @@ -0,0 +1,46 @@ +package content.region.asgarnia.burthorpe.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 EadburgDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, EadburgDialogueFile(), npc) + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return EadburgDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.EADBURG_1072) + } +} + +class EadburgDialogueFile : DialogueBuilderFile() { + + override fun create(b: DialogueBuilder) { + b.defaultDialogue().playerl( + FacialExpression.FRIENDLY, + "What's cooking, good looking?" + ).npcl( + FacialExpression.FRIENDLY, + "The stew for the servant's main meal." + ).playerl( + FacialExpression.HALF_WORRIED, + "Um...er...see you later." + ).npcl( + FacialExpression.FRIENDLY, + "Bye!" + ) + } +} \ No newline at end of file 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 1008ad638..9a200f9e5 100644 --- a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/EmeraldBenedictDialogue.kt +++ b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/EmeraldBenedictDialogue.kt @@ -18,17 +18,17 @@ import core.tools.START_DIALOGUE * @author vddCore */ @Initializable -class EmeraldBenedictDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { +class EmeraldBenedictDialogue(player: Player? = null) : DialoguePlugin(player) { override fun handle(interfaceId: Int, buttonId: Int): Boolean { when (stage) { START_DIALOGUE -> if(hasIronmanRestriction(player, IronmanMode.ULTIMATE)) { npcl( - core.game.dialogue.FacialExpression.ANNOYED, + FacialExpression.ANNOYED, "Get lost, tin can." ).also { stage = END_DIALOGUE } } else { npcl( - core.game.dialogue.FacialExpression.SUSPICIOUS, + FacialExpression.SUSPICIOUS, "Got anything you don't want to lose?" ).also { if (hasAwaitingGrandExchangeCollections(player)) { @@ -40,22 +40,22 @@ class EmeraldBenedictDialogue(player: Player? = null) : core.game.dialogue.Dialo } 1 -> npcl( - core.game.dialogue.FacialExpression.SUSPICIOUS, + FacialExpression.SUSPICIOUS, "By the way, a little bird told me you got some stuff waiting for you " + "on the Grand Exchange." ).also { stage++ } 2 -> showTopics( - Topic(core.game.dialogue.FacialExpression.ASKING, "Yes, actually. Can you help?", 3), + Topic(FacialExpression.ASKING, "Yes, actually. Can you help?", 3), IfTopic( - core.game.dialogue.FacialExpression.ASKING, + FacialExpression.ASKING, "Yes, but can you switch my bank accounts?", 4, hasActivatedSecondaryBankAccount(player) ), - Topic(core.game.dialogue.FacialExpression.ASKING, "Yes, but can you show me my PIN settings?", 5), - Topic(core.game.dialogue.FacialExpression.ASKING, "Yes, but can you show me my collection box?", 6), - Topic(core.game.dialogue.FacialExpression.ANNOYED, "Yes, thanks. And I'll keep hold of it too.", END_DIALOGUE) + 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) ) 3 -> { @@ -66,7 +66,7 @@ class EmeraldBenedictDialogue(player: Player? = null) : core.game.dialogue.Dialo 4 -> { toggleBankAccount(player) npcl( - core.game.dialogue.FacialExpression.SUSPICIOUS, + FacialExpression.SUSPICIOUS, "Sure thing. Feel free to rummage through whatever's in your ${getBankAccountName(player)} now." ).also { stage = END_DIALOGUE } } diff --git a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/EohricDialogue.kt b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/EohricDialogue.kt new file mode 100644 index 000000000..4fb041be6 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/EohricDialogue.kt @@ -0,0 +1,56 @@ +package content.region.asgarnia.burthorpe.dialogue + +import content.data.Quests +import content.region.asgarnia.burthorpe.quest.deathplateau.EohricDialogueFile +import core.api.getQuestStage +import core.api.openDialogue +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 + +/** + * Eohric main dialogue. + * @author ovenbread + */ +@Initializable +class EohricDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + if (getQuestStage(player!!, Quests.DEATH_PLATEAU) >= 5) { + // Call the dialogue file for Eohric from the deathplateau quest folder. + openDialogue(player!!, EohricDialogueFile(), npc) + return true + } + // Fallback to default. + when (stage) { + START_DIALOGUE -> playerl(FacialExpression.HALF_GUILTY, "Hi!").also { stage++ } + 1 -> npc(FacialExpression.ASKING, "Hello. Can I help?").also { stage++ } + 2 -> options("What is this place?", "That's quite an outfit.", "Goodbye.").also { stage++ } + 3 -> when (buttonId) { + 1 -> npcl(FacialExpression.FRIENDLY, "This is Burthorpe Castle, home to His Royal Highness Prince Anlaf, heir to the throne of Asgarnia.").also { stage = 10 } + 2 -> npcl(FacialExpression.HAPPY, "Why, thank you. I designed it myself. I've always found purple such a cheerful colour!").also { stage = 2 } + 3 -> player(FacialExpression.FRIENDLY, "Goodbye.").also { stage = END_DIALOGUE } + } + 10 -> npc(FacialExpression.FRIENDLY, "No doubt you're impressed.").also { stage++ } + 11 -> options("Where is the prince?", "Goodbye.").also { stage++ } + 12 -> when (buttonId) { + 1 -> npcl(FacialExpression.SUSPICIOUS, "I cannot disclose the prince's exact whereabouts for fear of compromising his personal safety.").also { stage = 20 } + 2 -> player(FacialExpression.FRIENDLY, "Goodbye.").also { stage = END_DIALOGUE } + } + 20 -> npcl(FacialExpression.FRIENDLY, "But rest assured that he is working tirelessly to maintain the safety and wellbeing of Burthorpe's people.").also { stage = 2 } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return EohricDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.EOHRIC_1080) + } +} + diff --git a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/HaroldDialogue.kt b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/HaroldDialogue.kt new file mode 100644 index 000000000..62d510ac1 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/HaroldDialogue.kt @@ -0,0 +1,60 @@ +package content.region.asgarnia.burthorpe.dialogue + +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.player.Player +import core.game.world.update.flag.context.Animation +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import core.tools.START_DIALOGUE +import org.rs09.consts.Animations +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +/** + * Harold main dialogue. + * @author ovenbread + */ +@Initializable +class HaroldDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + if (isQuestInProgress(player!!, Quests.DEATH_PLATEAU, 10, 29)) { + // Call the dialogue file for Harold from the deathplateau quest folder. + openDialogue(player!!, HaroldDialogueFile(), npc) + } + // Fallback to default. + when (stage) { + START_DIALOGUE -> player(FacialExpression.FRIENDLY, "Hello there.").also { stage++ } + 1 -> npc(FacialExpression.FRIENDLY, "Hi.").also { stage++ } + 2 -> player(FacialExpression.FRIENDLY, "Can I buy you a drink?").also { stage++ } + 3 -> npc(FacialExpression.HAPPY, "Now you're talking! An Asgarnian Ale, please!").also { stage++ } + 4 -> { + if (removeItem(player!!, Items.ASGARNIAN_ALE_1905)) { + sendMessage(player!!, "You give Harold an Asgarnian Ale.") + sendItemDialogue(player!!, Items.ASGARNIAN_ALE_1905, "You give Harold an Asgarnian Ale.").also { stage++ } + } else { + player(FacialExpression.FRIENDLY, "I'll go and get you one.").also { stage = END_DIALOGUE } + } + } + 5 -> { + end() + animate(npc!!, Animation(Animations.HUMAN_EATTING_829), true) + runTask(npc!!, 3) { + npc(FacialExpression.FRIENDLY, "*burp*").also { stage = END_DIALOGUE } + } + } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return HaroldDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.HAROLD_1078) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/HarrallakMenarous.java b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/HarrallakMenarous.java deleted file mode 100644 index 1bdf360fd..000000000 --- a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/HarrallakMenarous.java +++ /dev/null @@ -1,303 +0,0 @@ -package content.region.asgarnia.burthorpe.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 harrallak menarous npc. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class HarrallakMenarous extends DialoguePlugin { - - /** - * Constructs a new {@code HarrallakMenarous} {@code Object}. - */ - public HarrallakMenarous() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code HarrallakMenarous} {@code Object}. - * @param player the player. - */ - public HarrallakMenarous(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new HarrallakMenarous(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Welcome to my humble guild, " + player.getUsername() + "."); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendOptions("Select an Option", "Quite a place you've got here.", "You any good with a sword?", "Bye!"); - stage = 1; - break; - case 1: - if (buttonId == 1) { - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Quite a place you've got here. Tell me more about it."); - stage = 53; - } else if (buttonId == 2) { - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "You any good with a sword?"); - stage = 5; - } else if (buttonId == 3) { - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Bye!"); - stage = 2; - } - break; - case 2: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Farewell, brave warrior, I do hope you enjoy my guild."); - stage = 120; - break; - case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "You any good with a sword?"); - stage = 4; - break; - case 4: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Am I any good with a sword'? Have you any clue who I", "am?"); - stage = 5; - break; - case 5: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Not really, no."); - stage = 6; - break; - case 6: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Why, I could best any person alive in a rapier duel!"); - stage = 7; - break; - case 7: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Try me, then!"); - stage = 8; - break; - case 8: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "My dear man, I couldn't possibly duel you, I might hurt", "you and then what would happen to my reputation!", "Besides, I have this wonderful guild to run. Why don't", " you take a look at the various activities we have."); - stage = -8; - break; - case -8: - npc("You might even collect enough tokens to be allowed in", "to kill the strange beasts from the east!"); - stage = 10; - break; - case 10: - interpreter.sendOptions("Select an Option", "Tell me about the Strength training Area.", "Tell me about the Attack training area.", "Tell me about the Defence training area.", "Tell me about the Combat training area.", "Tell me about tokens."); - stage = 11; - break; - case 11: - if (buttonId == 1) { - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Tell me about the Strength training area."); - stage = 12; - } else if (buttonId == 2) { - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Tell me about the Attack training area."); - stage = 29; - } else if (buttonId == 3) { - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Tell me about the Defence training area"); - stage = 16; - } else if (buttonId == 4) { - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Tell me about the Combat training area"); - stage = 35; - } else if (buttonId == 5) { - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Tell me about tokens."); - stage = 42; - } - break; - case 12: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Ahh, the mighty warrior, Sloane, guards the Strength", "training area. This intriguing little area consits of two", "shotput lanes for different weights of shot. It's fairly", "simple, the referee or Sloane can explain more. There's"); - stage = 13; - break; - case 13: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "also the store room next door where Jimmy might share", "his talents with you, but don't tell him that I know", "he's not on guard duty!"); - stage = 14; - break; - case 14: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Oh? Why?"); - stage = 15; - break; - case 15: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Well, he's doing no harm and let's face it, with all these", "warriors around, the guild is hardly unguarded. You can", "find the strength area just up the stairs behind the bank."); - stage = 10; - break; - case 16: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "To polish your defensive skills to the very highest level,", "we've employed a most intentive dwarf and a catapult."); - stage = 17; - break; - case 17: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "You're going to throw dwarves at me?"); - stage = 18; - break; - case 18: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Oh my, no! I think Gamfred would object to that most", "strongly."); - stage = 19; - break; - case 19: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "He's an inventor, you see, and has built a marvellous", "contraptiont hat can throw all sorts of things at you.", "Things such as magic missiles..."); - stage = 20; - break; - case 20: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Mmmm?"); - stage = 21; - break; - case 21: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "...spiked iron balls..."); - stage = 22; - break; - case 22: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Er..."); - stage = 23; - break; - case 23: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "...spinning, slashing bladed..."); - stage = 24; - break; - case 24: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Ummm..."); - stage = 25; - break; - case 25: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "...and anvils."); - stage = 26; - break; - case 26: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "...and anvils."); - stage = 27; - break; - case 27: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "ANVILS?"); - stage = 28; - break; - case 28: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "No need to be afraid, it's all under very controlled", "conditions! You can find it just up the stairs and", "behind the bank."); - stage = 10; - break; - case 29: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Ahhh, dummies."); - stage = 30; - break; - case 30: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I'm no dummy, I just want to know what is there!"); - stage = 31; - break; - case 31: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Oh no, my dear man, I did not mean you at all! The", "training area has mechanical dummies that pop up out of", "holes in the floor. The noble dward, Gamfred, invented the", "mechanism and Ajjat can explain more about what to do"); - stage = 32; - break; - case 32: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "there."); - stage = 33; - break; - case 33: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Oh, okay, I'll have to try it out."); - stage = 34; - break; - case 34: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "You can find it just down the corridor and on", "your right."); - stage = 10; - break; - case 35: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Ahh, yes, our redient magician from foreign lands", "created a most amazing gadget that can turn your own", "armour against you! It's really quite intriguing."); - stage = 36; - break; - case 36: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "That sounds dangerous. What if I'm wearing it at the", "time?"); - stage = 37; - break; - case 37: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "So far, that's not happend. You need to speak to", "Shanomi about the specifics of the process, but as I", "understand it, putting a suit of armour in one of these", "devices will make it come to life somehow. The better the"); - stage = 38; - break; - case 38: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "armour, the harder it is to defeat."); - stage = 39; - break; - case 39: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Fighting my own armour sounds weird. I could be", "killed by it..."); - stage = 40; - break; - case 40: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Indeed, we have had a few fatalities from warriors", "overstretching themselves and not knowing their limits.", "Start small and work up, is my motto! That and go see", "Lidio for some food if you need it."); - stage = 41; - break; - case 41: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Okay, thanks for the warning."); - stage = 10; - break; - case 42: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Ahh, yes! The tokens allow you to spend an amount of", "time with my 'discovery', located on the top floor of the", "guild. Now, the amount of tokens you collect from the", "five activities around the guild will dictate how"); - stage = -43; - break; - case -43: - npc("long Kamfreena will allow in the enclosure on the very", "top floor. More tokens equals more time. There are", "also some bonuses available should you take part in all of", "the activites around the guild."); - stage = 44; - break; - case 43: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "will allow in the enclosure on the very top floor. More", "tokens equals more time. There are also some bonuses", "available should you take part in all of the activites", "around the guild."); - stage = 44; - break; - case 44: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Okay, okay. So, how do i earn these tokens?"); - stage = 45; - break; - case 45: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "You can earn them by simply using the traning exercises", "around the guild. The staff will enter your token", "earning into a ledger as you play."); - stage = 46; - break; - case 46: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Sounds easy enough."); - stage = 120; - break; - case 47: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Should you part in all five activites around the guild", "you can choose to pay for your time on the top floor with", "tokens of all types. Should you do this then you'll find you", "spend less tokens overall and have a better chance of"); - stage = 48; - break; - case 48: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "getting the dragon defender, amongst other things."); - stage = 49; - break; - case 49: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Excellent, sounds good. So, what's up on the top floor?"); - stage = 50; - break; - case 50: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Well, wit giving too much away, they're big and mean,", "and you get to fight them for defenders. If you're really", "lucky, they'll summon a cyclossus...although that be", "unlucky. Still, if you manage to defeat him, you could win"); - stage = 51; - break; - case 51: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "his hat."); - stage = 52; - break; - case 52: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Interesting...I will have to explore the top floor then!"); - stage = 10; - break; - case 53: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Yes indeed. What would you like to know?"); - stage = 10; - break; - case 120: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 4286 }; - } -} diff --git a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/HarrallakMenarous.kt b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/HarrallakMenarous.kt new file mode 100644 index 000000000..ce0aef163 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/HarrallakMenarous.kt @@ -0,0 +1,539 @@ +package content.region.asgarnia.burthorpe.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 + +/** + * Harrallak Menarous Dialogue + * Warrior's Guildmaster + * NOTE: org.rs09.consts.NPCs enums are wrong + * NPCs.RESOURCE_NPC_8267 is the correct Harrallak Menarous that animates properly + * NPCs.HARRALLAK_MENAROUS_8299 is the wrong Harrallak Menarous that is stuck in animation + * @author 'Vexia + * @author ovenbread + */ +@Initializable +class HarrallakMenarous(player: Player? = null) : DialoguePlugin(player) { + + override fun open(vararg args: Any): Boolean { + npc = args[0] as NPC + npc.isWalks = true + interpreter.sendDialogues( + npc, + FacialExpression.HALF_GUILTY, + "Welcome to my humble guild, " + player.username + "." + ) + stage = 0 + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage) { + 0 -> { + interpreter.sendOptions( + "Select an Option", + "Quite a place you've got here.", + "You any good with a sword?", + "Bye!" + ) + stage = 1 + } + + 1 -> if (buttonId == 1) { + interpreter.sendDialogues( + player, + FacialExpression.HALF_GUILTY, + "Quite a place you've got here. Tell me more about it." + ) + stage = 53 + } else if (buttonId == 2) { + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "You any good with a sword?") + stage = 5 + } else if (buttonId == 3) { + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Bye!") + stage = 2 + } + + 2 -> { + interpreter.sendDialogues( + npc, + FacialExpression.HALF_GUILTY, + "Farewell, brave warrior, I do hope you enjoy my guild." + ) + stage = 120 + } + + 3 -> { + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "You any good with a sword?") + stage = 4 + } + + 4 -> { + interpreter.sendDialogues( + npc, + FacialExpression.HALF_GUILTY, + "Am I any good with a sword'? Have you any clue who I", + "am?" + ) + stage = 5 + } + + 5 -> { + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Not really, no.") + stage = 6 + } + + 6 -> { + interpreter.sendDialogues( + npc, + FacialExpression.HALF_GUILTY, + "Why, I could best any person alive in a rapier duel!" + ) + stage = 7 + } + + 7 -> { + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Try me, then!") + stage = 8 + } + + 8 -> { + interpreter.sendDialogues( + npc, + FacialExpression.HALF_GUILTY, + "My dear man, I couldn't possibly duel you, I might hurt", + "you and then what would happen to my reputation!", + "Besides, I have this wonderful guild to run. Why don't", + " you take a look at the various activities we have." + ) + stage = -8 + } + + -8 -> { + npc( + "You might even collect enough tokens to be allowed in", + "to kill the strange beasts from the east!" + ) + stage = 10 + } + + 10 -> { + interpreter.sendOptions( + "Select an Option", + "Tell me about the Strength training Area.", + "Tell me about the Attack training area.", + "Tell me about the Defence training area.", + "Tell me about the Combat training area.", + "Tell me about tokens." + ) + stage = 11 + } + + 11 -> if (buttonId == 1) { + interpreter.sendDialogues( + player, + FacialExpression.HALF_GUILTY, + "Tell me about the Strength training area." + ) + stage = 12 + } else if (buttonId == 2) { + interpreter.sendDialogues( + player, + FacialExpression.HALF_GUILTY, + "Tell me about the Attack training area." + ) + stage = 29 + } else if (buttonId == 3) { + interpreter.sendDialogues( + player, + FacialExpression.HALF_GUILTY, + "Tell me about the Defence training area" + ) + stage = 16 + } else if (buttonId == 4) { + interpreter.sendDialogues( + player, + FacialExpression.HALF_GUILTY, + "Tell me about the Combat training area" + ) + stage = 35 + } else if (buttonId == 5) { + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Tell me about tokens.") + stage = 42 + } + + 12 -> { + interpreter.sendDialogues( + npc, + FacialExpression.HALF_GUILTY, + "Ahh, the mighty warrior, Sloane, guards the Strength", + "training area. This intriguing little area consits of two", + "shotput lanes for different weights of shot. It's fairly", + "simple, the referee or Sloane can explain more. There's" + ) + stage = 13 + } + + 13 -> { + interpreter.sendDialogues( + npc, + FacialExpression.HALF_GUILTY, + "also the store room next door where Jimmy might share", + "his talents with you, but don't tell him that I know", + "he's not on guard duty!" + ) + stage = 14 + } + + 14 -> { + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Oh? Why?") + stage = 15 + } + + 15 -> { + interpreter.sendDialogues( + npc, + FacialExpression.HALF_GUILTY, + "Well, he's doing no harm and let's face it, with all these", + "warriors around, the guild is hardly unguarded. You can", + "find the strength area just up the stairs behind the bank." + ) + stage = 10 + } + + 16 -> { + interpreter.sendDialogues( + npc, + FacialExpression.HALF_GUILTY, + "To polish your defensive skills to the very highest level,", + "we've employed a most intentive dwarf and a catapult." + ) + stage = 17 + } + + 17 -> { + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "You're going to throw dwarves at me?") + stage = 18 + } + + 18 -> { + interpreter.sendDialogues( + npc, + FacialExpression.HALF_GUILTY, + "Oh my, no! I think Gamfred would object to that most", + "strongly." + ) + stage = 19 + } + + 19 -> { + interpreter.sendDialogues( + npc, + FacialExpression.HALF_GUILTY, + "He's an inventor, you see, and has built a marvellous", + "contraptiont hat can throw all sorts of things at you.", + "Things such as magic missiles..." + ) + stage = 20 + } + + 20 -> { + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Mmmm?") + stage = 21 + } + + 21 -> { + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "...spiked iron balls...") + stage = 22 + } + + 22 -> { + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Er...") + stage = 23 + } + + 23 -> { + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "...spinning, slashing bladed...") + stage = 24 + } + + 24 -> { + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Ummm...") + stage = 25 + } + + 25 -> { + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "...and anvils.") + stage = 26 + } + + 26 -> { + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "...and anvils.") + stage = 27 + } + + 27 -> { + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "ANVILS?") + stage = 28 + } + + 28 -> { + interpreter.sendDialogues( + npc, + FacialExpression.HALF_GUILTY, + "No need to be afraid, it's all under very controlled", + "conditions! You can find it just up the stairs and", + "behind the bank." + ) + stage = 10 + } + + 29 -> { + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Ahhh, dummies.") + stage = 30 + } + + 30 -> { + interpreter.sendDialogues( + player, + FacialExpression.HALF_GUILTY, + "I'm no dummy, I just want to know what is there!" + ) + stage = 31 + } + + 31 -> { + interpreter.sendDialogues( + npc, + FacialExpression.HALF_GUILTY, + "Oh no, my dear man, I did not mean you at all! The", + "training area has mechanical dummies that pop up out of", + "holes in the floor. The noble dward, Gamfred, invented the", + "mechanism and Ajjat can explain more about what to do" + ) + stage = 32 + } + + 32 -> { + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "there.") + stage = 33 + } + + 33 -> { + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Oh, okay, I'll have to try it out.") + stage = 34 + } + + 34 -> { + interpreter.sendDialogues( + npc, + FacialExpression.HALF_GUILTY, + "You can find it just down the corridor and on", + "your right." + ) + stage = 10 + } + + 35 -> { + interpreter.sendDialogues( + npc, + FacialExpression.HALF_GUILTY, + "Ahh, yes, our redient magician from foreign lands", + "created a most amazing gadget that can turn your own", + "armour against you! It's really quite intriguing." + ) + stage = 36 + } + + 36 -> { + interpreter.sendDialogues( + player, + FacialExpression.HALF_GUILTY, + "That sounds dangerous. What if I'm wearing it at the", + "time?" + ) + stage = 37 + } + + 37 -> { + interpreter.sendDialogues( + npc, + FacialExpression.HALF_GUILTY, + "So far, that's not happend. You need to speak to", + "Shanomi about the specifics of the process, but as I", + "understand it, putting a suit of armour in one of these", + "devices will make it come to life somehow. The better the" + ) + stage = 38 + } + + 38 -> { + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "armour, the harder it is to defeat.") + stage = 39 + } + + 39 -> { + interpreter.sendDialogues( + player, + FacialExpression.HALF_GUILTY, + "Fighting my own armour sounds weird. I could be", + "killed by it..." + ) + stage = 40 + } + + 40 -> { + interpreter.sendDialogues( + npc, + FacialExpression.HALF_GUILTY, + "Indeed, we have had a few fatalities from warriors", + "overstretching themselves and not knowing their limits.", + "Start small and work up, is my motto! That and go see", + "Lidio for some food if you need it." + ) + stage = 41 + } + + 41 -> { + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Okay, thanks for the warning.") + stage = 10 + } + + 42 -> { + interpreter.sendDialogues( + npc, + FacialExpression.HALF_GUILTY, + "Ahh, yes! The tokens allow you to spend an amount of", + "time with my 'discovery', located on the top floor of the", + "guild. Now, the amount of tokens you collect from the", + "five activities around the guild will dictate how" + ) + stage = -43 + } + + -43 -> { + npc( + "long Kamfreena will allow in the enclosure on the very", + "top floor. More tokens equals more time. There are", + "also some bonuses available should you take part in all of", + "the activites around the guild." + ) + stage = 44 + } + + 43 -> { + interpreter.sendDialogues( + npc, + FacialExpression.HALF_GUILTY, + "will allow in the enclosure on the very top floor. More", + "tokens equals more time. There are also some bonuses", + "available should you take part in all of the activites", + "around the guild." + ) + stage = 44 + } + + 44 -> { + interpreter.sendDialogues( + player, + FacialExpression.HALF_GUILTY, + "Okay, okay. So, how do i earn these tokens?" + ) + stage = 45 + } + + 45 -> { + interpreter.sendDialogues( + npc, + FacialExpression.HALF_GUILTY, + "You can earn them by simply using the traning exercises", + "around the guild. The staff will enter your token", + "earning into a ledger as you play." + ) + stage = 46 + } + + 46 -> { + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Sounds easy enough.") + stage = 120 + } + + 47 -> { + interpreter.sendDialogues( + npc, + FacialExpression.HALF_GUILTY, + "Should you part in all five activites around the guild", + "you can choose to pay for your time on the top floor with", + "tokens of all types. Should you do this then you'll find you", + "spend less tokens overall and have a better chance of" + ) + stage = 48 + } + + 48 -> { + interpreter.sendDialogues( + npc, + FacialExpression.HALF_GUILTY, + "getting the dragon defender, amongst other things." + ) + stage = 49 + } + + 49 -> { + interpreter.sendDialogues( + player, + FacialExpression.HALF_GUILTY, + "Excellent, sounds good. So, what's up on the top floor?" + ) + stage = 50 + } + + 50 -> { + interpreter.sendDialogues( + npc, + FacialExpression.HALF_GUILTY, + "Well, wit giving too much away, they're big and mean,", + "and you get to fight them for defenders. If you're really", + "lucky, they'll summon a cyclossus...although that be", + "unlucky. Still, if you manage to defeat him, you could win" + ) + stage = 51 + } + + 51 -> { + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "his hat.") + stage = 52 + } + + 52 -> { + interpreter.sendDialogues( + player, + FacialExpression.HALF_GUILTY, + "Interesting...I will have to explore the top floor then!" + ) + stage = 10 + } + + 53 -> { + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Yes indeed. What would you like to know?") + stage = 10 + } + + 120 -> end() + } + return true + } + + override fun newInstance(player: Player): DialoguePlugin { + return HarrallakMenarous(player) + } + + override fun getIds(): IntArray { + // This has the wrong enum. + return intArrayOf(NPCs.RESOURCE_NPC_8267) + } +} 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.java b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/HildDialogue.java deleted file mode 100644 index 5ad1c4cd7..000000000 --- a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/HildDialogue.java +++ /dev/null @@ -1,65 +0,0 @@ -package content.region.asgarnia.burthorpe.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; - -/** - * Represent the hild dialogue plugin. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class HildDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code HildDialogue} {@code Object}. - */ - public HildDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code HildDialogue} {@code Object}. - * @param player the player. - */ - public HildDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new HildDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Hi!"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Hi!"); - stage = 1; - break; - case 1: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 1090 }; - } -} diff --git a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/HildDialogue.kt b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/HildDialogue.kt new file mode 100644 index 000000000..6b0116cd3 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/HildDialogue.kt @@ -0,0 +1,48 @@ +package content.region.asgarnia.burthorpe.dialogue + +import core.api.isQuestComplete +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.NPCs +import content.data.Quests + +/** + * Hild Dialogue + * @author 'Vexia + * @author ovenbread + */ +@Initializable +class HildDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int) : Boolean { + 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 } + 2 -> npcl(FacialExpression.HAPPY, "Thank you so much!").also { stage = END_DIALOGUE } + 3 -> npcl(FacialExpression.HAPPY, "Surely we are safe now!").also { stage = END_DIALOGUE } + } + } else { + when (stage) { + START_DIALOGUE -> playerl(FacialExpression.FRIENDLY, "Hi!").also { stage = (1..3).toIntArray().random() } + 1 -> npcl(FacialExpression.FRIENDLY, "Hello stranger.").also { stage = END_DIALOGUE } + 2 -> npcl(FacialExpression.FRIENDLY, "Hi!").also { stage = END_DIALOGUE } + 3 -> npcl(FacialExpression.FRIENDLY, "Welcome to Burthorpe!").also { stage++ } + 4 -> playerl(FacialExpression.FRIENDLY, "Thanks!").also { stage = END_DIALOGUE } + } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return CeolburgDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.HILD_1090) + } +} diff --git a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/HistanDialogue.java b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/HistanDialogue.java deleted file mode 100644 index 5779369a3..000000000 --- a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/HistanDialogue.java +++ /dev/null @@ -1,89 +0,0 @@ -package content.region.asgarnia.burthorpe.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 histan dialogue plugin. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class HistanDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code HistanDialogue} {@code Object}. - */ - public HistanDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code HistanDialogue} {@code Object}. - * @param player the player. - */ - public HistanDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new HistanDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Hi!"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Welcome to Burthorpe Supplies. Your last shop before", "heading north into the mountains!"); - stage = 1; - break; - case 1: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Would you like to buy something?"); - stage = 2; - break; - case 2: - interpreter.sendOptions("Select an Option", "Yes, please.", "No, thanks."); - stage = 3; - break; - case 3: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Yes, please."); - stage = 10; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No, thanks."); - stage = 20; - break; - } - break; - case 10: - end(); - npc.openShop(player); - break; - case 20: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 1083 }; - } -} diff --git a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/HygdDialogue.java b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/HygdDialogue.java deleted file mode 100644 index fb1e3490f..000000000 --- a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/HygdDialogue.java +++ /dev/null @@ -1,65 +0,0 @@ -package content.region.asgarnia.burthorpe.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 hygd dialogue plugin. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class HygdDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code HygdDialogue} {@code Object}. - */ - public HygdDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code HygdDialogue} {@code Object}. - * @param player the player. - */ - public HygdDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new HygdDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Hi!"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Hi!"); - stage = 1; - break; - case 1: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 1088 }; - } -} diff --git a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/HygdDialogue.kt b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/HygdDialogue.kt new file mode 100644 index 000000000..3e3b73b90 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/HygdDialogue.kt @@ -0,0 +1,49 @@ +package content.region.asgarnia.burthorpe.dialogue + +import core.api.isQuestComplete +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.NPCs +import content.data.Quests + +/** + * Hygd Dialogue + * @author 'Vexia + * @author ovenbread + */ +@Initializable +class HygdDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int) : Boolean { + 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 } + 2 -> npcl(FacialExpression.HAPPY, "Thank you so much!").also { stage = END_DIALOGUE } + 3 -> npcl(FacialExpression.HAPPY, "Surely we are safe now!").also { stage = END_DIALOGUE } + } + } else { + when (stage) { + START_DIALOGUE -> playerl(FacialExpression.FRIENDLY, "Hi!").also { stage = (1..3).toIntArray().random() } + 1 -> npcl(FacialExpression.FRIENDLY, "Hello stranger.").also { stage = END_DIALOGUE } + 2 -> npcl(FacialExpression.FRIENDLY, "Hi!").also { stage = END_DIALOGUE } + 3 -> npcl(FacialExpression.FRIENDLY, "Welcome to Burthorpe!").also { stage++ } + 4 -> playerl(FacialExpression.FRIENDLY, "Thanks!").also { stage = END_DIALOGUE } + } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return HygdDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.HYGD_1088) + } +} + diff --git a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/JadeDialogue.kt b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/JadeDialogue.kt index 188d96933..85ebda973 100644 --- a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/JadeDialogue.kt +++ b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/JadeDialogue.kt @@ -18,19 +18,19 @@ import core.tools.START_DIALOGUE * @author vddCore */ @Initializable -class JadeDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { +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( - core.game.dialogue.FacialExpression.NEUTRAL, + 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( - core.game.dialogue.FacialExpression.NEUTRAL, + FacialExpression.NEUTRAL, "Greetings warrior, how may I help you?" ).also { if (hasAwaitingGrandExchangeCollections(player)) { @@ -42,36 +42,36 @@ class JadeDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(p } 1 -> npcl( - core.game.dialogue.FacialExpression.NEUTRAL, + 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(core.game.dialogue.FacialExpression.NEUTRAL, "I'd like to access my bank account, please.", 10), + Topic(FacialExpression.NEUTRAL, "I'd like to access my bank account, please.", 10), IfTopic( - core.game.dialogue.FacialExpression.NEUTRAL, + FacialExpression.NEUTRAL, "I'd like to switch to my ${getBankAccountName(player, true)} bank account.", 13, hasActivatedSecondaryBankAccount(player) ), - Topic(core.game.dialogue.FacialExpression.NEUTRAL, "I'd like to check my PIN settings.", 11), - Topic(core.game.dialogue.FacialExpression.NEUTRAL, "I'd like to see my collection box.", 12), - Topic(core.game.dialogue.FacialExpression.ASKING, "How long have you worked here?", 3) + 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( - core.game.dialogue.FacialExpression.FRIENDLY, + FacialExpression.FRIENDLY, "Oh, ever since the Guild opened. I like it here." ).also { stage++ } 4 -> playerl( - core.game.dialogue.FacialExpression.ASKING, + FacialExpression.ASKING, "Why's that?" ).also { stage++ } 5 -> npcl( - core.game.dialogue.FacialExpression.FRIENDLY, + FacialExpression.FRIENDLY, "Well... What with all these warriors around, there's not much chance of my bank being robbed, is there?" ).also { stage = 2 } @@ -93,7 +93,7 @@ class JadeDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(p 13 -> { toggleBankAccount(player) npcl( - core.game.dialogue.FacialExpression.FRIENDLY, + FacialExpression.FRIENDLY, "Of course! Your ${getBankAccountName(player)} account is now active!" ).also { stage = END_DIALOGUE } } diff --git a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/LidioDialogue.java b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/LidioDialogue.java deleted file mode 100644 index 6124dbf0c..000000000 --- a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/LidioDialogue.java +++ /dev/null @@ -1,66 +0,0 @@ -package content.region.asgarnia.burthorpe.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 lidio npc. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class LidioDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code LidioDialogue} {@code Object}. - */ - public LidioDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code LidioDialogue} {@code Object}. - * @param player the player. - */ - public LidioDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new LidioDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Greetings, warrior, how can I fill your stomach today?"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "With food preferrable."); - stage = 1; - break; - case 1: - end(); - npc.openShop(player); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 4293 }; - } -} diff --git a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/LidioDialogue.kt b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/LidioDialogue.kt new file mode 100644 index 000000000..c6c76e0f8 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/LidioDialogue.kt @@ -0,0 +1,42 @@ +package content.region.asgarnia.burthorpe.dialogue + +import core.api.openDialogue +import core.api.openNpcShop +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 LidioDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, LidioDialogueFile(), npc) + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return LidioDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.LIDIO_4293) + } +} + +class LidioDialogueFile : DialogueBuilderFile() { + + override fun create(b: DialogueBuilder) { + b.defaultDialogue().npcl(FacialExpression.ASKING, + "Greetings, warrior, how can I fill your stomach today?" + ).playerl(FacialExpression.NEUTRAL, + "With food preferrable." + ).endWith { _, player -> + openNpcShop(player, NPCs.LIDIO_4293) + end() + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/LillyDialogue.java b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/LillyDialogue.java deleted file mode 100644 index 88455aa82..000000000 --- a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/LillyDialogue.java +++ /dev/null @@ -1,105 +0,0 @@ -package content.region.asgarnia.burthorpe.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 lilly npc. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class LillyDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code LillyDialogue} {@code Object} - */ - public LillyDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code LillyDialogue} {@code Object}. - * @param player the player. - */ - public LillyDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new LillyDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Uh..... hi... didn't see you there. Can.... I help?"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Umm... do you sell potions?"); - stage = 1; - break; - case 1: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Erm... yes. When I'm not drinking them."); - stage = 2; - break; - case 2: - interpreter.sendOptions("What would you like to say?", "I'd like to see what you have for sale.", "That's a pretty wall hanging.", "Bye!"); - stage = 3; - break; - case 3: - if (buttonId == 1) { - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I'd like to see what you have for sale."); - stage = 4; - } else if (buttonId == 2) { - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "That's a pretty wall hanging."); - stage = 5; - } else if (buttonId == 3) { - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Bye!"); - stage = 9; - } - break; - case 4: - end(); - npc.openShop(player); - break; - case 5: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Do you think so? I made it my self."); - stage = 6; - break; - case 6: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Really? Is that why there's all this cloth and dye around?"); - stage = 7; - break; - case 7: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Yes, it's a hobby of mine when I'm.... relaxing."); - stage = 8; - break; - case 8: - end(); - break; - case 9: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Have fun and come back soon!"); - stage = 8; - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 4294 }; - } -} diff --git a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/LillyDialogue.kt b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/LillyDialogue.kt new file mode 100644 index 000000000..553366bc0 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/LillyDialogue.kt @@ -0,0 +1,56 @@ +package content.region.asgarnia.burthorpe.dialogue + +import core.api.openDialogue +import core.api.openNpcShop +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 LillyDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, LillyDialogueFile(), npc) + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return LillyDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.LILLY_4294) + } +} + +class LillyDialogueFile : DialogueBuilderFile() { + + override fun create(b: DialogueBuilder) { + b.defaultDialogue().npcl( + FacialExpression.HALF_GUILTY, "Uh..... hi... didn't see you there. Can.... I help?" + ).playerl( + FacialExpression.HALF_ASKING, " Umm... do you sell potions?" + ).npcl( + FacialExpression.HALF_GUILTY, " Erm... yes. When I'm not drinking them." + ).options().let { optionsBuilder -> + optionsBuilder.option("I'd like to see what you have for sale.") + .playerl(FacialExpression.FRIENDLY, "I'd like to see what you have for sale. ") + .endWith { _, player -> openNpcShop(player, NPCs.LILLY_4294) } + optionsBuilder.option("That's a pretty wall hanging.").playerl( + FacialExpression.FRIENDLY, "That's a pretty wall hanging." + ).npcl( + FacialExpression.HAPPY, " Do you think so? I made it myself." + ).playerl( + FacialExpression.ASKING, " Really? Is that why there's all this cloth and dye around?" + ).npcl( + FacialExpression.HAPPY, " Yes, it's a hobby of mine when I'm.... relaxing." + ).end() + optionsBuilder.option("Bye. ").playerl(FacialExpression.FRIENDLY, "Bye.") + .npcl(FacialExpression.FRIENDLY, " Have fun and come back soon!").end() + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/MartinThwaitDialogue.java b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/MartinThwaitDialogue.java index 69e3a63e5..675d04822 100644 --- a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/MartinThwaitDialogue.java +++ b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/MartinThwaitDialogue.java @@ -1,5 +1,7 @@ package content.region.asgarnia.burthorpe.dialogue; +import core.api.Container; +import core.api.ContentAPIKt; import core.cache.def.impl.NPCDefinition; import core.game.dialogue.DialoguePlugin; import core.game.dialogue.FacialExpression; @@ -12,6 +14,7 @@ import core.game.node.item.Item; import core.plugin.Plugin; import core.plugin.Initializable; import core.plugin.ClassScanner; +import org.rs09.consts.Items; /** * Represents the dialogue used for martin thwait. @@ -141,17 +144,18 @@ public final class MartinThwaitDialogue extends DialoguePlugin { stage = 34; return true; } - if (!player.getInventory().containsItem(COINS)) { - end(); - return true; - } - if (!player.getInventory().remove(COINS)) { - player("Sorry, I don't seem to have enoug coins", "with me at this time."); + if (!ContentAPIKt.inInventory(player, COINS.getId(), COINS.getAmount())) { + player("Sorry, I don't seem to have enough coins", "with me at this time."); stage = 34; return true; } - player.getInventory().add(ITEMS[player.getSkills().getMasteredSkills() > 1 ? 1 : 0], ITEMS[2]); - npc("There you go! Enjoy."); + if (ContentAPIKt.removeItem(player, COINS, Container.INVENTORY)) { + ContentAPIKt.addItemOrDrop(player, ITEMS[player.getSkills().getMasteredSkills() > 1 ? 1 : 0].getId(),1); + ContentAPIKt.addItemOrDrop(player, ITEMS[2].getId(),1); + npc("There you go! Enjoy."); + } else { + player("Sorry, I don't seem to have enough coins", "with me at this time."); + } stage = 34; break; case 34: diff --git a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/OcaDialogue.java b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/OcaDialogue.java deleted file mode 100644 index 0f5dbb101..000000000 --- a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/OcaDialogue.java +++ /dev/null @@ -1,56 +0,0 @@ -package content.region.asgarnia.burthorpe.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 OcaDialogue dialogue. - * @author 'Vexia - */ -@Initializable -public class OcaDialogue extends DialoguePlugin { - - public OcaDialogue() { - - } - - public OcaDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - - return new OcaDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Hi!"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Hello stranger."); - stage = 1; - break; - case 1: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 1085 }; - } -} diff --git a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/OcgaDialogue.kt b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/OcgaDialogue.kt new file mode 100644 index 000000000..0606c4542 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/OcgaDialogue.kt @@ -0,0 +1,49 @@ +package content.region.asgarnia.burthorpe.dialogue + +import core.api.isQuestComplete +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.NPCs +import content.data.Quests + + +/** + * Ocga Dialogue + * @author 'Vexia + * @author ovenbread + */ +@Initializable +class OcgaDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int) : Boolean { + 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 } + 2 -> npcl(FacialExpression.HAPPY, "Thank you so much!").also { stage = END_DIALOGUE } + 3 -> npcl(FacialExpression.HAPPY, "Surely we are safe now!").also { stage = END_DIALOGUE } + } + } else { + when (stage) { + START_DIALOGUE -> playerl(FacialExpression.FRIENDLY, "Hi!").also { stage = (1..3).toIntArray().random() } + 1 -> npcl(FacialExpression.FRIENDLY, "Hello stranger.").also { stage = END_DIALOGUE } + 2 -> npcl(FacialExpression.FRIENDLY, "Hi!").also { stage = END_DIALOGUE } + 3 -> npcl(FacialExpression.FRIENDLY, "Welcome to Burthorpe!").also { stage++ } + 4 -> playerl(FacialExpression.FRIENDLY, "Thanks!").also { stage = END_DIALOGUE } + } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return OcgaDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.OCGA_1085) + } +} diff --git a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/PendaDialogue.java b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/PendaDialogue.java deleted file mode 100644 index 78f6c8e24..000000000 --- a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/PendaDialogue.java +++ /dev/null @@ -1,56 +0,0 @@ -package content.region.asgarnia.burthorpe.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 PendaDialogue dialogue. - * @author 'Vexia - */ -@Initializable -public class PendaDialogue extends DialoguePlugin { - - public PendaDialogue() { - - } - - public PendaDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - - return new PendaDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Hi!"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "The trolls are coming!"); - stage = 2; - break; - case 2: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 1087 }; - } -} diff --git a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/PendaDialogue.kt b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/PendaDialogue.kt new file mode 100644 index 000000000..0570a0d24 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/PendaDialogue.kt @@ -0,0 +1,59 @@ +package content.region.asgarnia.burthorpe.dialogue + +import core.api.isQuestComplete +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.NPCs +import content.data.Quests + +/** + * Penda Dialogue + * @author 'Vexia + * @author ovenbread + */ +@Initializable +class PendaDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int) : Boolean { + 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 } + 2 -> npcl(FacialExpression.WORRIED, "The White Knights are still going to take over.").also { stage = END_DIALOGUE } + 3 -> npcl(FacialExpression.PANICKED, "I hear the Imperial Guard are preparing an attack!").also { stage = END_DIALOGUE } + 4 -> npcl(FacialExpression.HAPPY, "Thank you so much!").also { stage = END_DIALOGUE } + 5 -> npcl(FacialExpression.HAPPY, "Surely we are safe now!").also { stage = END_DIALOGUE } + } + } else { + when (stage) { + START_DIALOGUE -> playerl(FacialExpression.FRIENDLY, "Hi!").also { stage = (1..12).toIntArray().random() } + 1 -> npcl(FacialExpression.ANGRY, "Let me at 'em!").also { stage = END_DIALOGUE } + 2 -> npcl(FacialExpression.ANGRY, "Trolls? Schmolls!").also { stage = END_DIALOGUE } + 3 -> npcl(FacialExpression.PANICKED, "The trolls are coming!").also { stage = END_DIALOGUE } + 4 -> npcl(FacialExpression.PANICKED, "The trolls took my baby son!").also { stage = END_DIALOGUE } + 5 -> npcl(FacialExpression.PANICKED, "Trolls!").also { stage = END_DIALOGUE } + 6 -> npcl(FacialExpression.FRIENDLY, "Hello stranger.").also { stage = END_DIALOGUE } + 7 -> npcl(FacialExpression.ANGRY, "Go away!").also { stage = END_DIALOGUE } + 8 -> npcl(FacialExpression.PANICKED, "Run!").also { stage = END_DIALOGUE } + 9 -> npcl(FacialExpression.FRIENDLY, "Hi!").also { stage = END_DIALOGUE } + 10 -> npcl(FacialExpression.SAD, "The Imperial Guard can no longer protect us!").also { stage = END_DIALOGUE } + 11 -> npcl(FacialExpression.WORRIED, "The White Knights will soon have control!").also { stage = END_DIALOGUE } + 12 -> npcl(FacialExpression.FRIENDLY, "Welcome to Burthorpe!").also { stage++ } + 13 -> playerl(FacialExpression.FRIENDLY, "Thanks!").also { stage = END_DIALOGUE } + } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return PendaDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.PENDA_1087) + } +} diff --git a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/RachaelDialogue.kt b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/RachaelDialogue.kt new file mode 100644 index 000000000..2a189cb4a --- /dev/null +++ b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/RachaelDialogue.kt @@ -0,0 +1,58 @@ +package content.region.asgarnia.burthorpe.dialogue + +import core.api.openNpcShop +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 +class RachaelDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int) : Boolean { + when (stage) { + START_DIALOGUE -> npcl(FacialExpression.FRIENDLY, "Welcome to the Burthorpe Games Rooms!").also { stage++ } + 1 -> showTopics( + Topic(FacialExpression.FRIENDLY, "How do I play board games?", 10), + Topic(FacialExpression.FRIENDLY, "What games can I play?", 30), + Topic(FacialExpression.FRIENDLY, "Can I buy a drink please?", 20), + Topic(FacialExpression.FRIENDLY, "Thanks!", END_DIALOGUE), + ) + 10 -> npcl(FacialExpression.FRIENDLY, "You can challenge someone to a game anywhere in the games rooms by using the right click option. Choose the type of game and then choose the options you want such as time per move. If you want to play a particular").also { stage++ } + 11 -> npcl(FacialExpression.FRIENDLY, "game there are challenge rooms dedicated to that game. In the challenge rooms you can see other players ranks by right clicking them, their skill will be displayed instead of their combat level. Once you have enough").also { stage++ } + 12 -> npcl(FacialExpression.FRIENDLY, "experience you will be able to use the challenge rooms on the first floor! If your opponent accepts the challenge you will be taken to one of the tables in the main room where you will play your game of choice.").also { stage++ } + 13 -> npcl(FacialExpression.FRIENDLY, "Once you have finished your game you will go back to the challenge room where you can challenge again!").also { stage = 1 } + + 20 -> npcl(FacialExpression.FRIENDLY, "Certainly ${if (player.isMale) "sir" else "miss"}, our speciality is Asgarnian Ale, we also serve Wizard's Mind Bomb and Dwarven Stout.").also { stage++ } + 21 -> openNpcShop(player, npc.id).also { + end() + stage = END_DIALOGUE + } + + 30 -> npcl(FacialExpression.FRIENDLY, "Currently we offer Draughts, Runelink, Runesquares, and Runeversi.").also { stage++ } + + 31 -> showTopics( + Topic(FacialExpression.FRIENDLY, "Draughts?", 35), + Topic(FacialExpression.FRIENDLY, "Runelink?", 36), + Topic(FacialExpression.FRIENDLY, "Runesquares?", 37), + Topic(FacialExpression.FRIENDLY, "Runeversi?", 38), + ) + 35 -> npcl(FacialExpression.FRIENDLY, "Draughts uses standard rules, apart from: a draw is declared if no piece has been taken or promoted for forty moves. To play Draughts go to the challenge room in the SW corner.").also { stage = 1 } + 36 -> npcl(FacialExpression.FRIENDLY, "Yup, you have to get four runes in row. The challenge room for Runelink is in the SE corner.").also { stage = 1 } + 37 -> npcl(FacialExpression.FRIENDLY, "Yes, you take it in turns to add a line with the goal of making squares. Everytime you make a square you take another turn. The challenge room for Runesquares is in the SW corner.").also { stage = 1 } + 38 -> npcl(FacialExpression.FRIENDLY, "Yep, the aim is to have more of your runes on the board than your opponent. You can take your opponents pieces by trapping them between your own. The challenge room for Runeversi is in the SE corner.").also { stage = 1 } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return RachaelDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.RACHAEL_1358) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/SamDialogue.kt b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/SamDialogue.kt new file mode 100644 index 000000000..db371d697 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/SamDialogue.kt @@ -0,0 +1,57 @@ +package content.region.asgarnia.burthorpe.dialogue + +import core.api.openNpcShop +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 +class SamDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int) : Boolean { + when (stage) { + START_DIALOGUE -> npcl(FacialExpression.FRIENDLY, "Welcome to the Burthorpe Games Rooms!").also { stage++ } + 1 -> showTopics( + Topic(FacialExpression.FRIENDLY, "How do I play board games?", 10), + Topic(FacialExpression.FRIENDLY, "What games can I play?", 30), + Topic(FacialExpression.FRIENDLY, "Can I buy a drink please?", 20), + Topic(FacialExpression.FRIENDLY, "Thanks!", END_DIALOGUE), + ) + 10 -> npcl(FacialExpression.FRIENDLY, "You can challenge someone to a game anywhere in the games rooms by using the right click option. Choose the type of game and then choose the options you want such as time per move. If you want to play a particular").also { stage++ } + 11 -> npcl(FacialExpression.FRIENDLY, "game there are challenge rooms dedicated to that game. In the challenge rooms you can see other players ranks by right clicking them, their skill will be displayed instead of their combat level. Once you have enough").also { stage++ } + 12 -> npcl(FacialExpression.FRIENDLY, "experience you will be able to use the challenge rooms on the first floor! If your opponent accepts the challenge you will be taken to one of the tables in the main room where you will play your game of choice.").also { stage++ } + 13 -> npcl(FacialExpression.FRIENDLY, "Once you have finished your game you will go back to the challenge room where you can challenge again!").also { stage = 1 } + + 20 -> npcl(FacialExpression.FRIENDLY, "Certainly ${if (player.isMale) "sir" else "miss"}, our speciality is Asgarnian Ale, we also serve Wizard's Mind Bomb and Dwarven Stout.").also { stage++ } + 21 -> openNpcShop(player, npc.id).also { + end() + stage = END_DIALOGUE + } + + 30 -> npcl(FacialExpression.FRIENDLY, "Currently we offer Draughts, Runelink, Runesquares, and Runeversi.").also { stage++ } + 31 -> showTopics( + Topic(FacialExpression.FRIENDLY, "Draughts?", 35), + Topic(FacialExpression.FRIENDLY, "Runelink?", 36), + Topic(FacialExpression.FRIENDLY, "Runesquares?", 37), + Topic(FacialExpression.FRIENDLY, "Runeversi?", 38), + ) + 35 -> npcl(FacialExpression.FRIENDLY, "Draughts uses standard rules, apart from: a draw is declared if no piece has been taken or promoted for forty moves. To play Draughts go to the challenge room in the SW corner.").also { stage = 1 } + 36 -> npcl(FacialExpression.FRIENDLY, "Yup, you have to get four runes in row. The challenge room for Runelink is in the SE corner.").also { stage = 1 } + 37 -> npcl(FacialExpression.FRIENDLY, "Yes, you take it in turns to add a line with the goal of making squares. Everytime you make a square you take another turn. The challenge room for Runesquares is in the SW corner.").also { stage = 1 } + 38 -> npcl(FacialExpression.FRIENDLY, "Yep, the aim is to have more of your runes on the board than your opponent. You can take your opponents pieces by trapping them between your own. The challenge room for Runeversi is in the SE corner.").also { stage = 1 } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return SamDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.SAM_1357) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/ServantDialogue.java b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/ServantDialogue.java deleted file mode 100644 index c0a6e29ea..000000000 --- a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/ServantDialogue.java +++ /dev/null @@ -1,64 +0,0 @@ -package content.region.asgarnia.burthorpe.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 ServantDialogue dialogue. - * @author 'Vexia - */ -@Initializable -public class ServantDialogue extends DialoguePlugin { - - public ServantDialogue() { - - } - - public ServantDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - - return new ServantDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Hi!"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Hi!"); - stage = 1; - break; - case 1: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Look, I'd better not talk. I'll get in trouble."); - stage = 2; - break; - case 2: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "If you want someone to show you round the castle ask", "Eohric, the Head Servant."); - stage = 3; - break; - case 3: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 1081 }; - } -} diff --git a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/ServantDialogue.kt b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/ServantDialogue.kt new file mode 100644 index 000000000..cc3ac53e3 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/ServantDialogue.kt @@ -0,0 +1,52 @@ +package content.region.asgarnia.burthorpe.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 + +/** + * Servant Dialogue + * @author 'Vexia + * @author ovenbread + * @author Trident101 + */ +@Initializable +class ServantDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, ServantDialogueFile(), npc) + return true + } + + override fun newInstance(player: Player): DialoguePlugin { + return ServantDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.SERVANT_1081) + } +} + +class ServantDialogueFile : DialogueBuilderFile() { + + override fun create(b: DialogueBuilder) { + b.defaultDialogue().playerl( + FacialExpression.HALF_GUILTY, + "Hi!" + ).npcl( + FacialExpression.HALF_GUILTY, + "Hi." + ).npcl( + FacialExpression.HALF_GUILTY, + "Look, I'd better not talk. I'll get in trouble." + ).npcl( + FacialExpression.HALF_GUILTY, + "If you want someone to show you round the castle ask Eohric, the Head Servant." + ) + } +} diff --git a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/TostigDialogue.kt b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/TostigDialogue.kt new file mode 100644 index 000000000..9fb7db3ae --- /dev/null +++ b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/TostigDialogue.kt @@ -0,0 +1,48 @@ +package content.region.asgarnia.burthorpe.dialogue + +import core.api.openNpcShop +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 + +/** + * Handles the TostigDialogue dialogue. + * @author 'Vexia + */ +@Initializable +class TostigDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage) { + 0 -> playerl(FacialExpression.FRIENDLY, "Hi, what ales are you serving?").also { stage++ } + 1 -> npcl(FacialExpression.FRIENDLY, "Well ${if (player.isMale) "mister" else "miss"}, our speciality is Asgarnian Ale, we also serve Wizard's Mind Bomb and Dwarven Stout.").also { stage++ } + 2 -> npcl(FacialExpression.FRIENDLY, "Would you like to buy a drink?").also { stage++ } + 3 -> showTopics( + Topic(FacialExpression.FRIENDLY, "Yes, please.", 10), + Topic(FacialExpression.FRIENDLY, "No, thanks.", 20), + ) + 10 -> openNpcShop(player, npc.id).also { + end() + stage = END_DIALOGUE + } + 20 -> npcl(FacialExpression.FRIENDLY, "Ah well... so um... does the grey squirrel sing in the grove?").also { stage++ } + 21 -> showTopics( + Topic(FacialExpression.FRIENDLY, "Huh?", 30), + Topic(FacialExpression.FRIENDLY, "Yes, and the black cat dances in the sink.", 40), + Topic(FacialExpression.FRIENDLY, "No, squirrels can't sing.", 50), + ) + 30 -> npcl(FacialExpression.FRIENDLY, "Er... nevermind.").also { stage = END_DIALOGUE } + 40 -> npcl(FacialExpression.FRIENDLY, "Ah you'll be wanting the trapdoor behind the bar.").also { stage = END_DIALOGUE } + 50 -> npcl(FacialExpression.FRIENDLY, "No... of course they can't...").also { stage = END_DIALOGUE } + } + return true + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.TOSTIG_1079) + } +} diff --git a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/UnferthDialogue.kt b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/UnferthDialogue.kt new file mode 100644 index 000000000..5c0a23214 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/UnferthDialogue.kt @@ -0,0 +1,65 @@ +package content.region.asgarnia.burthorpe.dialogue + +import core.api.isQuestComplete +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 +import content.data.Quests + +/** + * Unferth Dialogue + * @author ovenbread + * @author Trident101 + */ +@Initializable +class UnferthDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, UnferthDialogueFile(), npc) + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return UnferthDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.UNFERTH_2655) + } +} + +class UnferthDialogueFile : DialogueBuilderFile() { + + override fun create(b: DialogueBuilder) { + 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." + ).playerl( + FacialExpression.HALF_GUILTY, "I'm so sorry Unferth." + ).npcl( + FacialExpression.HALF_GUILTY, + "Gertrude asked me if I'd like one of her new kittens. I don't think I'm ready for that yet." + ).playerl( + FacialExpression.FRIENDLY, "Give it time. Things will get better, I promise." + ).npcl( + FacialExpression.HALF_GUILTY, "Thanks, @name." + ).end() + + b.defaultDialogue().npcl( + FacialExpression.GUILTY, + "Hello." + ).playerl( + FacialExpression.FRIENDLY, + "What's wrong?" + ).npcl( + FacialExpression.GUILTY, + "It's fine. Nothing for you to worry about." + ).end() + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/asgarnia/burthorpe/dialogue/WistanDialogue.kt b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/WistanDialogue.kt new file mode 100644 index 000000000..66fde48af --- /dev/null +++ b/Server/src/main/content/region/asgarnia/burthorpe/dialogue/WistanDialogue.kt @@ -0,0 +1,55 @@ +package content.region.asgarnia.burthorpe.dialogue + +import core.api.openDialogue +import core.api.openNpcShop +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 + +/** + * Represents the Wistan dialogue plugin. + * @author 'Vexia + * @author ovenbread + * @author Trident101 + */ +@Initializable +class WistanDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, WistanDialogueFile(), npc) + return true + } + + override fun newInstance(player: Player): DialoguePlugin { + return WistanDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.WISTAN_1083) + } +} + +class WistanDialogueFile : DialogueBuilderFile() { + + override fun create(b: DialogueBuilder) { + b.defaultDialogue().playerl( + FacialExpression.FRIENDLY, "Hi!" + ).npcl( + FacialExpression.FRIENDLY, + "Welcome to Burthorpe Supplies. Your last shop before heading north into the mountains!" + ).npcl( + FacialExpression.FRIENDLY, "Would you like to buy something?" + ).options().let { optionsBuilder -> + optionsBuilder.option("Yes, please.").playerl( + FacialExpression.FRIENDLY, "Yes, Please" + ).endWith { _, player -> openNpcShop(player, NPCs.WISTAN_1083) } + optionsBuilder.option("No, thanks.").playerl( + FacialExpression.FRIENDLY, "No, thanks." + ).end() + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/asgarnia/burthorpe/handlers/BurthorpeOptionPlugin.java b/Server/src/main/content/region/asgarnia/burthorpe/handlers/BurthorpeOptionPlugin.java index 5af1f46d6..c5528bacd 100644 --- a/Server/src/main/content/region/asgarnia/burthorpe/handlers/BurthorpeOptionPlugin.java +++ b/Server/src/main/content/region/asgarnia/burthorpe/handlers/BurthorpeOptionPlugin.java @@ -14,7 +14,7 @@ import core.plugin.Initializable; import core.plugin.Plugin; /** - * Represents the optione plugin to handle interactions with 'nodes' in + * Represents the option plugin to handle interactions with 'nodes' in * burthorpe. * @author 'Vexia * @version 1.0 @@ -34,30 +34,14 @@ public final class BurthorpeOptionPlugin extends OptionHandler { @Override public Plugin newInstance(Object arg) throws Throwable { - NPCDefinition.forId(1063).getHandlers().put("option:talk-to", this);// soldier - NPCDefinition.forId(1061).getHandlers().put("option:talk-to", this);// sergant - NPCDefinition.forId(1066).getHandlers().put("option:talk-to", this);// seated - // soldier - NPCDefinition.forId(1067).getHandlers().put("option:talk-to", this);// seated - // soldier - NPCDefinition.forId(1068).getHandlers().put("option:talk-to", this);// seated - // soldier - NPCDefinition.forId(1064).getHandlers().put("option:talk-to", this);// other - // soldiers - NPCDefinition.forId(1062).getHandlers().put("option:talk-to", this);// other - // sergant SceneryDefinition.forId(7257).getHandlers().put("option:enter", this);// thieving // guide // trapdoor. SceneryDefinition.forId(7258).getHandlers().put("option:enter", this);// thieving // guide // passegeway. - NPCDefinition.forId(1073).getHandlers().put("option:talk-to", this); - NPCDefinition.forId(1074).getHandlers().put("option:talk-to", this); - NPCDefinition.forId(1076).getHandlers().put("option:talk-to", this); - NPCDefinition.forId(1077).getHandlers().put("option:talk-to", this); SceneryDefinition.forId(4624).getHandlers().put("option:climb-down", this); - SceneryDefinition.forId(4627).getHandlers().put("option:climb-down", this); + SceneryDefinition.forId(4627).getHandlers().put("option:climb-up", this); return this; } @@ -68,14 +52,14 @@ public final class BurthorpeOptionPlugin extends OptionHandler { case "climb-down": switch (id) { case 4624: - ClimbActionHandler.climb(player, Animation.create(828), Location.create(2205, 4934, 1)); + ClimbActionHandler.climb(player, null, Location.create(2205, 4934, 1)); break; } break; case "climb-up": switch (id) { case 4627: - ClimbActionHandler.climb(player, Animation.create(828), Location.create(2899, 3565, 0)); + ClimbActionHandler.climb(player, null, Location.create(2899, 3565, 0)); break; } break; @@ -89,31 +73,6 @@ public final class BurthorpeOptionPlugin extends OptionHandler { break; } break; - case "talk-to": - switch (id) { - case 1064: - case 1063:// training soldier. - player.getPacketDispatch().sendMessage("The soldier is busy training."); - break; - case 1062: - case 1061:// sergeant - player.getPacketDispatch().sendMessage("The Sergeant is busy training the soldiers."); - break; - case 1066:// eating soldier. - case 1067:// eating soldier. - case 1068:// eating soldier. - player.getPacketDispatch().sendMessage("The soldier is busy eating."); - break; - case 1073: - case 1074: - player.getPacketDispatch().sendMessage("The archer won't talk whilst on duty."); - break; - case 1076: - case 1077: - player.getPacketDispatch().sendMessage("The soldier won't talk whilst on duty."); - break; - } - break; } return true; } diff --git a/Server/src/main/content/region/asgarnia/burthorpe/handlers/BurthorpePlugin.java b/Server/src/main/content/region/asgarnia/burthorpe/handlers/BurthorpePlugin.java deleted file mode 100644 index efdbe5883..000000000 --- a/Server/src/main/content/region/asgarnia/burthorpe/handlers/BurthorpePlugin.java +++ /dev/null @@ -1,37 +0,0 @@ -package content.region.asgarnia.burthorpe.handlers; - -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.player.Player; -import core.game.node.scenery.Scenery; -import core.game.world.map.Location; -import core.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Handles the burthorpe plugin. - * @author Emperor - */ -@Initializable -public final class BurthorpePlugin extends OptionHandler { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - SceneryDefinition.forId(4627).getHandlers().put("option:climb-up", this); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - Scenery object = (Scenery) node; - switch (object.getId()) { - case 4627: - ClimbActionHandler.climb(player, null, Location.create(2899, 3565, 0)); - return true; - } - return false; - } - -} 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 2cb15bbb8..cc4e8fb62 100644 --- a/Server/src/main/content/region/asgarnia/burthorpe/handlers/HeroGuildPlugin.java +++ b/Server/src/main/content/region/asgarnia/burthorpe/handlers/HeroGuildPlugin.java @@ -17,6 +17,9 @@ import core.plugin.Plugin; import core.plugin.Initializable; import core.plugin.ClassScanner; +import static core.api.ContentAPIKt.hasRequirement; +import content.data.Quests; + /** * Represents the hero guild. * @author Vexia @@ -40,7 +43,8 @@ public final class HeroGuildPlugin extends OptionHandler { switch (id) { case 2624: case 2625: - // player.getPacketDispatch().sendMessage("You need to complete the Heroes' Quest."); + if (!hasRequirement(player, Quests.HEROES_QUEST)) + return true; DoorActionHandler.handleAutowalkDoor(player, (Scenery) node); break; } @@ -79,9 +83,16 @@ public final class HeroGuildPlugin extends OptionHandler { @Override public boolean handle(NodeUsageEvent event) { final Player player = event.getPlayer(); + if (!hasRequirement(player, Quests.HEROES_QUEST)) + return true; final EnchantedJewellery jewellery; assert event.getUsedItem() != null; jewellery = EnchantedJewellery.Companion.getIdMap().get(event.getUsedItem().getId()); + if (!hasRequirement(player, Quests.HEROES_QUEST)) + return true; + if (jewellery == EnchantedJewellery.COMBAT_BRACELET || jewellery == EnchantedJewellery.SKILLS_NECKLACE) + if (!hasRequirement(player, Quests.LEGENDS_QUEST)) + return true; if (jewellery == null && event.getUsedItem().getId() != 2572) { return true; } diff --git a/Server/src/main/content/region/asgarnia/burthorpe/handlers/wguild/animator/AnimationRoom.java b/Server/src/main/content/region/asgarnia/burthorpe/handlers/wguild/animator/AnimationRoom.java index 2e56db3b3..746fd9368 100644 --- a/Server/src/main/content/region/asgarnia/burthorpe/handlers/wguild/animator/AnimationRoom.java +++ b/Server/src/main/content/region/asgarnia/burthorpe/handlers/wguild/animator/AnimationRoom.java @@ -18,6 +18,9 @@ import core.game.world.map.zone.ZoneBuilder; import core.game.world.update.flag.context.Animation; import core.plugin.Initializable; import core.plugin.Plugin; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.playAudio; /** * Handles the animation @@ -132,7 +135,7 @@ public final class AnimationRoom extends MapZone implements Plugin { return true; } } - player.getAudioManager().send(1909); + playAudio(player, Sounds.WARGUILD_ANIMATE_1909); player.logoutListeners.put("animation-room", player1 -> { for(int item : set.getPieces()) player1.getInventory().add(new Item(item)); return Unit.INSTANCE; @@ -144,7 +147,7 @@ public final class AnimationRoom extends MapZone implements Plugin { } if (getDelay() == 4) { setDelay(1); - player.getAudioManager().send(1910); + playAudio(player, Sounds.WARGUILD_ANIMATOR_ACTIVATE_1910); ForceMovement.run(player, player.getLocation().transform(0, 1, 0)).setDirection(Direction.SOUTH); return false; } diff --git a/Server/src/main/content/region/asgarnia/burthorpe/handlers/wguild/barrel/BarrelRoom.java b/Server/src/main/content/region/asgarnia/burthorpe/handlers/wguild/barrel/BarrelRoom.java index de17ad2d0..e01b9b698 100644 --- a/Server/src/main/content/region/asgarnia/burthorpe/handlers/wguild/barrel/BarrelRoom.java +++ b/Server/src/main/content/region/asgarnia/burthorpe/handlers/wguild/barrel/BarrelRoom.java @@ -26,6 +26,8 @@ import core.plugin.Plugin; import core.plugin.ClassScanner; import core.tools.RandomFunction; +import static core.api.ContentAPIKt.*; + /** * Handles the Barrel room. * @author Emperor @@ -172,7 +174,7 @@ public final class BarrelRoom extends MapZone implements Plugin { player.getEquipment().replace(null, EquipmentContainer.SLOT_HAT); player.getAppearance().setAnimations(); player.getAppearance().sync(); - player.getConfigManager().set(793, 0); + setVarp(player, 793, 0); } } diff --git a/Server/src/main/content/region/asgarnia/burthorpe/handlers/wguild/catapult/CatapultRoom.java b/Server/src/main/content/region/asgarnia/burthorpe/handlers/wguild/catapult/CatapultRoom.java index 880802041..a5ae501b3 100644 --- a/Server/src/main/content/region/asgarnia/burthorpe/handlers/wguild/catapult/CatapultRoom.java +++ b/Server/src/main/content/region/asgarnia/burthorpe/handlers/wguild/catapult/CatapultRoom.java @@ -33,6 +33,10 @@ import core.plugin.Plugin; import core.plugin.ClassScanner; import core.plugin.Initializable; import core.tools.RandomFunction; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.playAudio; +import static core.api.ContentAPIKt.setVarp; /** * The catapult room. @@ -146,9 +150,8 @@ public final class CatapultRoom extends MapZone implements Plugin { Scenery object = RegionManager.getObject(Location.create(2840, 3552, 1)); if(object != null) SceneryBuilder.replace(object, object.transform(attack.objectId), 4); - Audio sound = new Audio(1911); for (Player p : players) { - p.getAudioManager().send(sound); + playAudio(p, Sounds.WARGUILD_CATAPULT_ANVIL_1911); } return players.isEmpty(); } @@ -207,7 +210,7 @@ public final class CatapultRoom extends MapZone implements Plugin { if (button >= 9 && button <= 12) { CatapultAttack attack = CatapultAttack.values()[button - 9]; player.setAttribute("catapult_def", attack); - player.getConfigManager().set(788, (button - 8) % 4); + setVarp(player, 788, (button - 8) % 4); return true; } return false; diff --git a/Server/src/main/content/region/asgarnia/burthorpe/handlers/wguild/cyclopes/CyclopesRoom.java b/Server/src/main/content/region/asgarnia/burthorpe/handlers/wguild/cyclopes/CyclopesRoom.java index 0782a28d4..98111f7c1 100644 --- a/Server/src/main/content/region/asgarnia/burthorpe/handlers/wguild/cyclopes/CyclopesRoom.java +++ b/Server/src/main/content/region/asgarnia/burthorpe/handlers/wguild/cyclopes/CyclopesRoom.java @@ -102,13 +102,13 @@ public final class CyclopesRoom extends MapZone implements Plugin { @Override public boolean leave(Entity e, boolean logout) { - if (e instanceof Player && PLAYERS.contains(e)) { + if (e instanceof Player) { leave((Player) e); PLAYERS.remove(e); if (logout) { e.setLocation(Location.create(2846, 3540, 2)); } - clearLogoutListener((Player) e, "cyclopes"); + clearLogoutListener((Player) e, "cyclopes"); } return super.leave(e, logout); } @@ -129,7 +129,7 @@ public final class CyclopesRoom extends MapZone implements Plugin { @Override public void configure() { - super.register(new ZoneBorders(2838, 3534, 2876, 3556)); + super.register(new ZoneBorders(2838, 3534, 2876, 3556, 2)); } @Override diff --git a/Server/src/main/content/region/asgarnia/burthorpe/handlers/wguild/shot/DustHandListener.kt b/Server/src/main/content/region/asgarnia/burthorpe/handlers/wguild/shot/DustHandListener.kt new file mode 100644 index 000000000..297c29e9c --- /dev/null +++ b/Server/src/main/content/region/asgarnia/burthorpe/handlers/wguild/shot/DustHandListener.kt @@ -0,0 +1,27 @@ +package content.region.asgarnia.burthorpe.handlers.wguild.shot + +import core.api.MapArea +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.item.Item +import core.game.world.map.Location +import org.rs09.consts.Items + +/** For ShotPutRoom */ +class DustHandListeners : InteractionListener { + override fun defineListeners() { + on(Items.GROUND_ASHES_8865, IntType.ITEM, "dust-hands") { player, node -> + // Check that player is in the shotput throwing areas. + if (!(player.location.withinDistance(Location(2861, 3553, 1), 1) || + player.location.withinDistance(Location(2861, 3547, 1), 1))) { + player.packetDispatch.sendMessage("You may only dust your hands while in the shotput throwing areas.") + return@on true + } + if (player.inventory.remove(node as Item)) { + player.packetDispatch.sendMessage("You dust your hands with the finely ground ash.") + player.setAttribute("hand_dust", true) + } + return@on true + } + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..24e3f6cbc --- /dev/null +++ b/Server/src/main/content/region/asgarnia/burthorpe/quest/deathplateau/DeathPlateau.kt @@ -0,0 +1,175 @@ +package content.region.asgarnia.burthorpe.quest.deathplateau + +import core.api.addItemOrDrop +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.plugin.Initializable +import org.rs09.consts.Items +import content.data.Quests + +/** + * Death Plateau Quest + * @author bushtail + * @author ovenbread + */ +@Initializable +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(Quests.DEATH_PLATEAU)!! > 0 + + if(!started){ + line(player, "I can start this quest by speaking to !!Denulth?? who is in his", line++) + line(player, "tent at the !!Imperial Guard camp?? in !!Burthorpe", line++) + } else { + if(stage >= 5) { + when(stage) { + in 1..18 -> { + line(player,"I have offered to help !!Denulth?? by finding !!another way?? up",line++,) + line(player,"!!Death Plateau.?? I also need to find the !!combination?? to the",line++,) + line(player,"!!equipment room?? and !!unlock?? the door.",line++,) + } + // Technically this part is to be done in parallel with the ball mechanism part above. + in 19..100 -> { + line(player,"I have found another way up Death Plateau.",line++, stage>26) + line++ + line(player,"I have found the combination to the equipment room and",line++,stage>18) + line(player,"unlocked the door.",line++,stage>18) + line++ + } + } + } + if(stage >= 10) { + when(stage) { + 10 -> { + line(player,"The equipment room guard is staying at the local inn, the",line++,stage>18) + line(player,"!!Toad and Chicken.??",line++,stage>18) + } + in 11 .. 12 -> { + line(player,"The equipment room guard is staying at the local inn, the",line++,stage>18) + line(player,"!!Toad and Chicken.?? the guard wouldn't talk to me!",line++,stage>18) + } + in 13 .. 14 -> { + line(player,"The equipment room guard is staying at the local inn, the",line++,stage>18) + line(player,"!!Toad and Chicken.?? the guard wouldn't talk to me! I bought",line++,stage>18) + line(player,"the guard a drink and he seemed more helpful.",line++,stage>18) + } + in 15..100 -> { + line(player,"The equipment room guard is staying at the local inn, the",line++,stage>18) + line(player,"!!Toad and Chicken.?? the guard wouldn't talk to me! I bought",line++,stage>18) + line(player,"the guard a drink and he seemed more helpful. I gambled",line++,stage>18) + line(player,"with the guard until he ran out of money, he wrote out an !!IOU.??",line++,stage>18) + line++ + } + } + } + if(stage >= 16) { + when(stage) { + 16 -> { + line(player,"It turned out the IOU was written on the back of the",line++,stage>18) + line(player,"combination!",line++,stage>18) + } + in 17..100 -> { + line(player,"It turned out the IOU was written on the back of the",line++,stage>18) + line(player,"combination! I put the stone balls in the right places on the",line++,stage>18) + line(player,"stone mechanism and unlocked the door!",line++,stage>18) + line++ + } + } + } + if(stage >= 20) { + when(stage) { + 20 -> { + line(player,"!!Saba?? says that there is a !!sherpa?? living !!nearby?? that may",line++,false) + line(player,"know another way up Death Plateau.",line++,false) + } + 21 -> { + line(player, "I found the !!sherpa's?? house.", line++, true) + // Note: You must give him the 21 items in one go. The items will cross out when you have them in your inventory. + line(player, "!!Tenzing?? will show me a !!secret way?? up Death Plateau if I get", line++, false) + line(player, "him some items:", line++, false) + line(player, "Ten loaves of bread.", line++, getAttribute(player, "deathplateau:bread", false)) + line(player, "Ten cooked trout.", line++, getAttribute(player, "deathplateau:trout", false)) + line(player, "Spiked boots.", line++, getAttribute(player, "deathplateau:boots", false)) + line++ + line(player, "!!Tenzing?? gave me his !!climbing boots??, I need to take them to", line++, false) + line(player, "!!Dunstan?? in !!Burthorpe?? for the !!spikes??", line++, false) + } + 24 -> { + line(player, "I found the !!sherpa's?? house. I gave Tenzing the ten loaves", line++, true) + line(player, "of bread, ten cooked trout and the Spiked boots. Tenzing", line++, true) + line(player, "has given me a map of the secret way.", line++, true) + line++ + line(player,"I need to !!check?? that the !!secret way?? is !!safe?? for the !!Imperial??", line++, false) + line(player,"!!Guard?? to use.", line++, false) + } + 25 -> { + line(player, "I found the !!sherpa's?? house. I gave Tenzing the ten loaves", line++, true) + line(player, "of bread, ten cooked trout and the Spiked boots. Tenzing", line++, true) + line(player, "has given me a map of the secret way.", line++, true) + line++ + line(player,"I need to !!check?? that the !!secret way?? is !!safe?? for the !!Imperial??", line++, false) + line(player,"!!Guard?? to use.", line++, false) + } + in 26 .. 100 -> { + line(player, "I found the !!sherpa's?? house. I gave Tenzing the ten loaves", line++, true) + line(player, "of bread, ten cooked trout and the Spiked boots. Tenzing", line++, true) + line(player, "has given me a map of the secret way. I checked the", line++, true) + line(player, "secret way and it is safe for the Imperial Guard to use.", line++, true) + line++ + } + } + when(stage) { + in 22 .. 23 -> { + line(player, "!!Dunstan?? will help me if I get his !!son signed up?? for the", line++, false) + line(player, "!!Imperial Guard??. I will need an !!Iron bar?? for the boots", line++, false) + } + in 24 .. 100 -> { + line(player,"I have given Dunstan the certificate to prove that his son", line++, true) + line(player,"has been signed up for the Imperial Guard. Dunstan made", line++, true) + line(player, "me the Spiked boots.", line++, true) + line++ + } + } + } + when(stage) { + in 30 .. 40 -> { + line(player, "I should go and tell !!Denulth?? I've completed my mission.", line++, false) + } + in 41 .. 100 -> { + line(player, "I gave Denulth the secret way map and the combination.", line++, true) + line(player, "Denulth gave me some Steel claws and trained me in.", line++, true) + line(player, "attack. I'm now an honorary member of the Imperial Guard!", line++, true) + } + } + 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 Death Plateau Quest!", 277, 4) + player.packetDispatch.sendItemZoomOnInterface(Items.STEEL_CLAWS_3097, 240, 277, 5) + + drawReward(player,"1 Quest Point", ln++) + drawReward(player,"3,000 Attack XP", ln++) + drawReward(player,"Some Steel Claws", ln++) + drawReward(player,"Ability to make Claws", ln++) + + addItemOrDrop(player, Items.STEEL_CLAWS_3097, 1) + player.skills.addExperience(Skills.ATTACK, 3000.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/deathplateau/DeathPlateauDoorDialogueFile.kt b/Server/src/main/content/region/asgarnia/burthorpe/quest/deathplateau/DeathPlateauDoorDialogueFile.kt new file mode 100644 index 000000000..64c29cec7 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/burthorpe/quest/deathplateau/DeathPlateauDoorDialogueFile.kt @@ -0,0 +1,71 @@ +package content.region.asgarnia.burthorpe.quest.deathplateau + +import content.data.Quests +import core.api.getQuestStage +import core.api.getScenery +import core.api.sendDialogue +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.tools.END_DIALOGUE +import org.rs09.consts.NPCs + +class DeathPlateauDoorDialogueFile(val door: Int) : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + if(door == 1) { + npc = NPC(NPCs.HAROLD_1078) + when (stage) { + 0 -> sendDialogue(player!!, "You knock on the door.").also { stage++ } + 1 -> npcl(FacialExpression.FRIENDLY, "Come on in!").also { stage++ } + 2 -> { + end() + DoorActionHandler.handleAutowalkDoor(player, getScenery(2906, 3543, 1)) + } + } + } + if(door == 2) { + npc = NPC(NPCs.TENZING_1071) + + when (getQuestStage(player!!, Quests.DEATH_PLATEAU)) { + in 0 .. 19 -> { + when (stage) { + 0 -> sendDialogue(player!!, "You knock on the door.").also { stage++ } + 1 -> npcl(FacialExpression.FRIENDLY, "No milk today! Thank you!").also { stage = END_DIALOGUE } + } + } + 20 -> { + when (stage) { + 0 -> sendDialogue(player!!, "You knock on the door.").also { stage++ } + 1 -> npcl(FacialExpression.FRIENDLY, "No milk today! Thank you!").also { stage++ } + 2 -> playerl(FacialExpression.FRIENDLY, "I'm not the milkman, I need your help!").also { stage++ } + 3 -> npcl(FacialExpression.FRIENDLY, "Oh...OK. You'd better come in then.").also { stage++ } + 4 -> { + end() + DoorActionHandler.handleAutowalkDoor(player, getScenery(2823, 3555, 0)) + } + } + } + in 21 .. 100 -> { + end() + DoorActionHandler.handleAutowalkDoor(player, getScenery(2823, 3555, 0)) + } + } + } + if(door == 3) { + npc = NPC(NPCs.TENZING_1071) + 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 } + } + } + in 25..100 -> { + end() + DoorActionHandler.handleAutowalkDoor(player, getScenery(2820, 3558, 0)) + } + } + } + + } +} 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 new file mode 100644 index 000000000..01a253ad4 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/burthorpe/quest/deathplateau/DeathPlateauInteractionListener.kt @@ -0,0 +1,95 @@ +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 +import core.game.interaction.InteractionListener +import core.game.node.item.GroundItemManager +import org.rs09.consts.Items +import org.rs09.consts.Scenery + +class DeathPlateauInteractionListener : InteractionListener { + + companion object { + val stoneBalls = intArrayOf( + Items.STONE_BALL_3109, // Red + Items.STONE_BALL_3110, // Blue + Items.STONE_BALL_3111, // Yellow + Items.STONE_BALL_3112, // Purple + Items.STONE_BALL_3113 // Green + ) + val stoneMechanisms = intArrayOf( + Scenery.STONE_MECHANISM_3676, // 4 Outer stone plates + Scenery.STONE_MECHANISM_3677 // 2 Inner stone plates + ) + } + override fun defineListeners() { + 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 -> + 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 + } + + on(Items.IOU_3103, ITEM, "read") { player, _ -> + openDialogue(player, IOUNoteDialogueFile()) + return@on true + } + + on(Items.COMBINATION_3102, ITEM, "read") { player, _ -> + openInterface(player, 220) + setInterfaceText(player, "Red is North of Blue. Yellow is South of Purple.", 220, 7) + setInterfaceText(player, "Green is North of Purple. Blue is West of", 220, 8) + setInterfaceText(player, "Yellow. Purple is East of Red.", 220, 9) + return@on true + } + + onUseWith(IntType.SCENERY, stoneBalls, *stoneMechanisms) { player, used, with -> + val stoneBall = used.asItem() + val stoneMechanism = with.asScenery() + + // Place item on table + if (removeItem(player, stoneBall)) { + produceGroundItem(player, stoneBall.id, 1, stoneMechanism.location) + } + // Check if order was correct + /** + * Facing north + * NONE [2894, 3564, 0] [2895, 3564, 0] GREEN + * RED [2894, 3563, 0] [2895, 3563, 0] PURPLE + * BLUE [2894, 3562, 0] [2895, 3562, 0] YELLOW + */ + if (GroundItemManager.get(Items.STONE_BALL_3109, location(2894, 3563, 0), player) != null && + GroundItemManager.get(Items.STONE_BALL_3110, location(2894, 3562, 0), player) != null && + 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, Quests.DEATH_PLATEAU) == 16) { + sendMessage(player, "The equipment room door has unlocked.") + setQuestStage(player, Quests.DEATH_PLATEAU, 19) + } + } + return@onUseWith true + } + + on(Scenery.LARGE_DOOR_3743, SCENERY, "open") { player, node -> + if (getQuestStage(player, Quests.DEATH_PLATEAU) > 16) { + DoorActionHandler.handleAutowalkDoor(player, node as core.game.node.scenery.Scenery) + } else { + sendMessage(player, "The door is locked.") + } + return@on true + } + } +} 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 new file mode 100644 index 000000000..2046cf51a --- /dev/null +++ b/Server/src/main/content/region/asgarnia/burthorpe/quest/deathplateau/DenulthDialogueFile.kt @@ -0,0 +1,177 @@ +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.item.Item +import core.tools.END_DIALOGUE +import org.rs09.consts.Items + + +/** + * Denulth sub dialogue file for death plateau. + * Called by EohricDialogue + * @author bushtail + * @author ovenbread + */ + +class DenulthDialogueFile : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (getQuestStage(player!!, Quests.DEATH_PLATEAU)) { + in 0..4 -> { + when (stage) { + 0 -> playerl(FacialExpression.FRIENDLY, "Hello!").also { stage++ } + 1 -> npcl(FacialExpression.FRIENDLY, "Hello citizen, how can I help you?").also { stage++ } + 2 -> showTopics( + Topic("Do you have any quests for me?", 100), + Topic("What is this place?", 200), + Topic(FacialExpression.ANNOYED, "You can't, thanks.", END_DIALOGUE) + ) + 100 -> npcl(FacialExpression.FRIENDLY, "I don't know if you can help us!").also { stage++ } + 101 -> npcl(FacialExpression.FRIENDLY, "The trolls have taken up camp on the Death Plateau! They are using it to launch raids at night on the village. We have tried to attack the camp, but the main path is heavily guarded!").also { stage++ } + 102 -> playerl(FacialExpression.ASKING, "Perhaps there is a way you can sneak up at night?").also { stage++ } + 103 -> npcl(FacialExpression.FRIENDLY, "If there is another way, I do not know of it.").also { stage++ } + 104 -> npcl(FacialExpression.FRIENDLY, "Do you know of such a path?").also { stage++ } + 105 -> showTopics( + Topic(FacialExpression.THINKING,"No, but perhaps I could try and find one?", 300), + Topic(FacialExpression.ANNOYED, "No, sorry.", END_DIALOGUE) + ) + + 200 -> npcl(FacialExpression.HAPPY, "Welcome to the Principality of Burthorpe! We are the Imperial Guard for his Royal Highness Prince Anlaf of Burthorpe. Can I assist you with anything else?").also { stage = 2 } + + 300 -> npc(FacialExpression.FRIENDLY, "Citizen you would be well rewarded!").also { stage++ } + 301 -> npcl(FacialExpression.FRIENDLY, "If you go up to Death Plateau, be very careful as the trolls will attack you on sight!").also { stage++ } + 302 -> playerl(FacialExpression.FRIENDLY, "I'll be careful.").also { stage++ } + 303 -> npcl(FacialExpression.FRIENDLY, "One other thing.").also { stage++ } + 304 -> playerl(FacialExpression.FRIENDLY, "What's that?").also { stage++ } + 305 -> npcl(FacialExpression.FRIENDLY, "All of our equipment is kept in the castle on the hill.").also { stage++ } + 306 -> npcl(FacialExpression.FRIENDLY, "The stupid guard that was on duty last night lost the combination to the lock! I told the Prince that the Imperial Guard should've been in charge of security!").also { stage++ } + 307 -> playerl(FacialExpression.ASKING, "No problem, what does the combination look like?").also { stage++ } + 308 -> npcl(FacialExpression.FRIENDLY, "The equipment room is unlocked when the stone balls are placed in the correct order on the stone mechanism outside it. The right order is written on a piece of paper the guard had.").also { stage++ } + 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!!, Quests.DEATH_PLATEAU, 5) + stage = END_DIALOGUE + } + } + } + in 5..21 -> { + when(stage) { + 0 -> npcl(FacialExpression.HAPPY, "Hello citizen, is there anything you'd like to know?").also { stage++ } + 1 -> showTopics( + Topic("Can you remind me of the quest I am on?", 100), + Topic("I thought the White Knights controlled Asgarnia?", 200), + Topic("What is this place?", 300), + Topic(FacialExpression.HAPPY, "That's all, thanks.", 10) + ) + 10 -> npcl(FacialExpression.HAPPY, "God speed citizen.").also { stage = END_DIALOGUE } + + 100 -> npcl(FacialExpression.HALF_THINKING, "You offered to see if you could find another way up Death Plateau. We could then use it to sneak up and attack the trolls by night.").also { stage++ } + 101 -> playerl(FacialExpression.THINKING, "Ah yes, and the guard had lost the combination to your equipment rooms in the castle on the hill?").also { stage++ } + 102 -> npcl(FacialExpression.HAPPY, "That's right citizen, you offered to recover the combination and unlock the door.").also { stage++ } + 103 -> playerl(FacialExpression.FRIENDLY, "I'll get on it right away!").also { stage++ } + 104 -> npcl(FacialExpression.FRIENDLY, "Good work citizen!").also { stage++ } + 105 -> npcl(FacialExpression.ASKING, "Is there anything else you would like to know?").also { stage = 1 } + + 200 -> 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++ } + 201 -> npcl(FacialExpression.EVIL_LAUGH, "We are the prince's elite troops! We keep Burthorpe secure!").also { stage++ } + 202 -> npcl(FacialExpression.ANGRY, "The White Knights have overlooked us until now! They are pouring money into their war against the Black Knights, They are looking for an excuse to stop our funding and I'm afraid they may have found it!").also { stage++ } + 203 -> npcl(FacialExpression.HALF_WORRIED, "If we can not destroy the troll camp on Death Plateau then the Imperial Guard will be disbanded and Burthorpe will come under control of the White Knights. We cannot let this happen!").also { stage++ } + 204 -> npcl(FacialExpression.ASKING, "Is there anything else you'd like to know?").also { stage = 1 } + + 300 -> npcl(FacialExpression.HAPPY, "Welcome to the Principality of Burthorpe! We are the Imperial Guard for his Royal Highness Prince Anlaf of Burthorpe. Can I assist you with anything else?").also { stage = 1 } + } + } + 22 -> { + when (stage) { + 0 -> playerl(FacialExpression.FRIENDLY, "Hello!").also { stage++ } + 1 -> npcl(FacialExpression.FRIENDLY, "Hello citizen, have you found another way up Death Plateau?").also { stage++ } + 2 -> playerl(FacialExpression.FRIENDLY, "Yes there is another way up Death Plateau!").also { stage++ } + 3 -> npcl(FacialExpression.FRIENDLY, "We are saved!").also { stage++ } + 4 -> playerl(FacialExpression.FRIENDLY, "There's one thing...").also { stage++ } + 5 -> npcl(FacialExpression.FRIENDLY, "And what is that citizen?").also { stage++ } + 6 -> playerl(FacialExpression.FRIENDLY, "There is a Sherpa who will only show me the secret way if I first get some spikes for his climbing boots. The smith will only do this for me if you sign up his son for the Imperial Guard!").also { stage++ } + 7 -> npcl(FacialExpression.FRIENDLY, "Hmm...this is very irregular.").also { stage++ } + 8 -> playerl(FacialExpression.FRIENDLY, "Will you not do this?").also { stage++ } + 9 -> npcl(FacialExpression.FRIENDLY, "I have heard of Dunstan's son, he is a very promising young man. For the sake of your mission we can make an exception!").also { stage++ } + 10 -> sendItemDialogue(player!!, Items.CERTIFICATE_3114, "Denulth has given you a certificate.").also { + addItemOrDrop(player!!, Items.CERTIFICATE_3114) + stage++ + } + 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!!, Quests.DEATH_PLATEAU, 23) + stage = END_DIALOGUE + } + } + } + 23 -> { + when (stage) { + 0 -> playerl(FacialExpression.FRIENDLY, "Hello!").also { stage++ } + 1 -> npcl(FacialExpression.FRIENDLY, "Hello citizen, have you found the secret way up Death Plateau?").also { + if (!inInventory(player!!, Items.CERTIFICATE_3114)) { + stage = 3 + } else { + stage++ + } + } + 2 -> playerl(FacialExpression.FRIENDLY, "I'm working on it, I just need to give the certificate to Dunstan.").also { stage = END_DIALOGUE } + 3 -> playerl(FacialExpression.FRIENDLY, "I'm working on it but I've lost the certificate!").also { stage++ } + 4 -> npcl(FacialExpression.FRIENDLY, "No problem, I have a duplicate.").also { stage++ } + 5 -> sendItemDialogue(player!!, Items.CERTIFICATE_3114, "Denulth has given you a certificate.").also { + addItemOrDrop(player!!, Items.CERTIFICATE_3114) + stage = END_DIALOGUE + } + } + } + in 24 .. 26 -> { + when (stage) { + 0 -> playerl(FacialExpression.FRIENDLY, "Hello!").also { stage++ } + 1 -> npcl(FacialExpression.FRIENDLY, "Hello citizen, have you found the secret way up Death Plateau?").also { stage++ } + 2 -> playerl(FacialExpression.FRIENDLY, "Yes! There is a path that runs from a Sherpa's hut around the back of Death Plateau. The trolls haven't found it yet. The Sherpa made a map I can give you.").also { + if (!inInventory(player!!, Items.SECRET_WAY_MAP_3104)) { + stage = 3 + } else { + stage = 4 + } + } + 3 -> playerl(FacialExpression.FRIENDLY, "I don't have the map on me..").also { stage = END_DIALOGUE } + 4 -> sendItemDialogue(player!!, Items.SECRET_WAY_MAP_3104, "You give Denulth the map of the secret way.").also { + if (removeItem(player!!, Item(Items.SECRET_WAY_MAP_3104))) { + stage++ + } else { + stage = END_DIALOGUE + } + } + 5 -> npcl(FacialExpression.FRIENDLY, "Excellent, this looks perfect. They will never see us coming.").also { stage++ } + 6 -> npcl(FacialExpression.FRIENDLY, "Have you managed to open the equipment room?").also { + if (!inInventory(player!!, Items.COMBINATION_3102)) { + stage = 7 + } else { + stage = 8 + } + } + 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))) { + stage++ + } else { + stage = END_DIALOGUE + } + } + 10 -> npcl(FacialExpression.FRIENDLY, "Well done citizen! We will reward you by training you in attack!").also { stage++ } + 11 -> npcl(FacialExpression.FRIENDLY, "I shall present you with some steel fighting claws. In addition I shall show you the knowledge of creating the fighting claws for yourself.").also { stage++ } + 12 -> npcl(FacialExpression.FRIENDLY, "You are now an honourary member of the Imperial Guard!").also { stage++ } + 13 -> { + stage = END_DIALOGUE + finishQuest(player!!, Quests.DEATH_PLATEAU) + } + } + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/asgarnia/burthorpe/quest/deathplateau/DiceGameInterfaceListener.kt b/Server/src/main/content/region/asgarnia/burthorpe/quest/deathplateau/DiceGameInterfaceListener.kt new file mode 100644 index 000000000..a8ab1692e --- /dev/null +++ b/Server/src/main/content/region/asgarnia/burthorpe/quest/deathplateau/DiceGameInterfaceListener.kt @@ -0,0 +1,156 @@ +package content.region.asgarnia.burthorpe.quest.deathplateau + +import core.api.* +import core.game.interaction.InterfaceListener +import core.game.node.entity.player.Player +import core.game.system.task.Pulse +import org.rs09.consts.Components + +/** + * @author bushtail + * Thanks for your work on this quest Phil, it's appreciated. + * @author ovenbread + */ + +class DiceGameInterfaceListener : InterfaceListener { + companion object { + val GOLDSTACKS = intArrayOf(995, 996, 997, 998, 999, 1000, 1001, 1002, 1003, 1004) + val GOLDTHRESHOLDS = intArrayOf(1, 2, 3, 4, 5, 25, 100, 250, 1000, 10000) + + const val ATTRIBUTE_HAROLD_DICE_1 = "deathplateau:harold1" + const val ATTRIBUTE_HAROLD_DICE_2 = "deathplateau:harold2" + const val ATTRIBUTE_PLAYER_DICE_1 = "deathplateau:player1" + const val ATTRIBUTE_PLAYER_DICE_2 = "deathplateau:player2" + const val ATTRIBUTE_WIN_STATE = "deathplateau:winstate" + } + + override fun defineInterfaceListeners() { + onOpen(Components.DEATH_DICE_99) { player, _ -> + var goldID = 0 + var wager = player.getAttribute("deathplateau:wager", 50) + player.setAttribute(ATTRIBUTE_HAROLD_DICE_1, 0) + player.setAttribute(ATTRIBUTE_HAROLD_DICE_2, 0) + player.setAttribute(ATTRIBUTE_PLAYER_DICE_1, 0) + player.setAttribute(ATTRIBUTE_PLAYER_DICE_2, 0) + player.setAttribute(ATTRIBUTE_WIN_STATE, false) + goldID = when(wager){ + 1 -> GOLDSTACKS[0] + 2 -> GOLDSTACKS[1] + 3 -> GOLDSTACKS[2] + 4 -> GOLDSTACKS[3] + 5 -> GOLDSTACKS[4] + in 25..99 -> GOLDSTACKS[5] + in 100..249 -> GOLDSTACKS[6] + in 250..999 -> GOLDSTACKS[7] + in 1000..9999 -> GOLDSTACKS[8] + else -> GOLDSTACKS[9] + } + sendItemOnInterface(player, Components.DEATH_DICE_99, 7, goldID, wager) + sendItemOnInterface(player, Components.DEATH_DICE_99, 8, goldID, wager) + setComponentVisibility(player, Components.DEATH_DICE_99, 9, true) + setInterfaceText(player, "Harold rolls...", Components.DEATH_DICE_99, 13) + preRoll(player) + setInterfaceText(player, player.username, Components.DEATH_DICE_99,6) + (1..4).forEach { sendAnimationOnInterface(player, 1149, Components.DEATH_DICE_99, it) } + submitRollPulse(player, true) + return@onOpen true + } + + on(Components.DEATH_DICE_99){ player, _, _, buttonID, _, _ -> + when(buttonID) { + 10 -> submitRollPulse(player, false) + 12 -> { + closeInterface(player) + val callback: (() -> Unit)? = + getAttribute(player, "deathplateau:dicegameclose", null) + callback?.invoke() + } + } + return@on true + } + + onClose(Components.DEATH_DICE_99){ player, _ -> + setComponentVisibility(player, Components.DEATH_DICE_99, 9, true) + setComponentVisibility(player, Components.DEATH_DICE_99, 11, true) + return@onClose true + } + } + + private fun onClose(deathDice99: Int, any: Any) { + + } + + private fun preRoll(player: Player, triesRemaining: Int = 5) { + val h1 = (1..5).random() + val h2 = (1..5).random() + val p1 = (1..5).random() + val p2 = (1..5).random() + if(h1 + h2 == p1 + p2 && triesRemaining > 0) { + preRoll(player, triesRemaining - 1) + } else { + player.setAttribute(ATTRIBUTE_HAROLD_DICE_1, h1) + player.setAttribute(ATTRIBUTE_HAROLD_DICE_2, h2) + player.setAttribute(ATTRIBUTE_PLAYER_DICE_1, p1) + player.setAttribute(ATTRIBUTE_PLAYER_DICE_2, p2) + player.setAttribute(ATTRIBUTE_WIN_STATE, h1 + h2 < p1 + p2) + } + } + + private fun submitRollPulse(player: Player, isNPCTurn: Boolean) { + val roll1: Int + val roll2: Int + + if (isNPCTurn) { + roll1 = getAttribute(player, ATTRIBUTE_HAROLD_DICE_1, null) ?: return + roll2 = getAttribute(player, ATTRIBUTE_HAROLD_DICE_2, null) ?: return + } else { + roll1 = getAttribute(player, ATTRIBUTE_PLAYER_DICE_1, null) ?: return + roll2 = getAttribute(player, ATTRIBUTE_PLAYER_DICE_2, null) ?: return + } + + val component1 = if (isNPCTurn) 2 else 3 + val component2 = if (isNPCTurn) 1 else 4 + + submitWorldPulse( + InterfaceUpdatePulse( + player, isNPCTurn, + component1, component2, + roll1, roll2 + ) + ) + } + + private class InterfaceUpdatePulse(val player: Player, val isNPCTurn: Boolean, val component1: Int, val component2: Int, val roll1: Int, val roll2: Int) : Pulse() { + val DICEANIM = intArrayOf(42069, 1150, 1151, 1152, 1153, 1154, 1155) + var counter = 0 + override fun pulse(): Boolean { + when (counter++) { + 1 -> { + setComponentVisibility(player, Components.DEATH_DICE_99, 9, true) + if (isNPCTurn) { + setInterfaceText(player, "Harold rolls...", Components.DEATH_DICE_99, 13) + } else { + setInterfaceText(player, "Your roll...", Components.DEATH_DICE_99, 13) + } + sendAnimationOnInterface(player, DICEANIM[roll1], Components.DEATH_DICE_99, component1) + } + 4 -> sendAnimationOnInterface(player, DICEANIM[roll2], Components.DEATH_DICE_99, component2) + 7 -> { + if (isNPCTurn) { + setComponentVisibility(player, Components.DEATH_DICE_99, 9, false) + } else { + if (player.getAttribute("deathplateau:winstate")) { + setInterfaceText(player,"You win!", Components.DEATH_DICE_99, 13) + } else { + setInterfaceText(player,"You lose!", Components.DEATH_DICE_99, 13) + } + setComponentVisibility(player, Components.DEATH_DICE_99, 11, false) + setComponentVisibility(player, Components.DEATH_DICE_99, 9, true) + } + return true + } + } + return false + } + } +} 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 new file mode 100644 index 000000000..6cf2eabf4 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/burthorpe/quest/deathplateau/DunstanDialogueFile.kt @@ -0,0 +1,106 @@ +package content.region.asgarnia.burthorpe.quest.deathplateau + +import core.api.* +import core.game.dialogue.DialogueFile +import core.game.dialogue.FacialExpression +import core.game.node.item.Item +import core.tools.END_DIALOGUE +import core.tools.START_DIALOGUE +import org.rs09.consts.Items +import content.data.Quests + +class DunstanDialogueFile : DialogueFile() { + + override fun handle(componentID: Int, buttonID: Int) { + when (getQuestStage(player!!, Quests.DEATH_PLATEAU)) { + 21 -> { + when (stage) { + START_DIALOGUE -> playerl(FacialExpression.FRIENDLY, "Hi!").also { stage++ } + 1 -> npcl(FacialExpression.FRIENDLY, "Hi! How can I help?").also { stage++ } + 2 -> playerl(FacialExpression.FRIENDLY, "Tenzing has asked me to bring you his climbing boots, he needs to have spikes put on them.").also { stage++ } + 3 -> npcl(FacialExpression.FRIENDLY, "He does, does he? Well I won't do it till he pays for the last set I made for him!").also { stage++ } + 4 -> playerl(FacialExpression.FRIENDLY, "This is really important!").also { stage++ } + 5 -> npcl(FacialExpression.FRIENDLY, "How so?").also { stage++ } + 6 -> playerl(FacialExpression.FRIENDLY, "Well, I need the Sherpa to show me a secret way up Death Plateau so that the Imperial Guard can destroy the troll camp! He won't help me till I've got the spikes!").also { stage++ } + 7 -> npcl(FacialExpression.FRIENDLY, "Hmm. That's different!").also { stage++ } + 8 -> npcl(FacialExpression.FRIENDLY, "Tell you what, I'll make them for you on one condition.").also { stage++ } + 9 -> playerl(FacialExpression.FRIENDLY, "*sigh* What's the condition?").also { stage++ } + 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!!, Quests.DEATH_PLATEAU, 22) + stage = END_DIALOGUE + } + } + } + 22 -> { + when (stage) { + START_DIALOGUE -> playerl(FacialExpression.FRIENDLY, "Hi!").also { stage++ } + 1 -> npcl(FacialExpression.FRIENDLY, "Have you managed to get my son signed up for the Imperial Guard?").also { stage++ } + 2 -> playerl(FacialExpression.FRIENDLY, "Not yet! I just need to speak to Denulth!").also { stage = END_DIALOGUE } + } + } + 23 -> { + when (stage) { + START_DIALOGUE -> playerl(FacialExpression.FRIENDLY, "Hi!").also { stage++ } + 1 -> npcl(FacialExpression.FRIENDLY, "Have you managed to get my son signed up for the Imperial Guard?").also { + stage++ + } + + 2 -> { + if (!inInventory(player!!, Items.CERTIFICATE_3114)) { + playerl(FacialExpression.FRIENDLY, "I have but I don't have the entrance certificate on me.").also { stage++ } + } else { + sendDialogue(player!!, "You give Dunstan the certificate.").also { stage = 5 } + } + } + + 3 -> npcl(FacialExpression.FRIENDLY, "Good but I need to have the certificate.").also { stage = END_DIALOGUE } + + 5 -> npcl(FacialExpression.FRIENDLY, "Thank you!").also { + // Jumps to the next stage immediately in one continuous dialogue (questStage 24, stage 2). + setQuestStage(player!!, Quests.DEATH_PLATEAU, 24) + stage = 2 + } + } + } + 24 -> { + when (stage) { + START_DIALOGUE -> playerl(FacialExpression.FRIENDLY, "Hi!").also { stage++ } + 1 -> npcl(FacialExpression.FRIENDLY, "Hi!").also { stage++ } + 2 -> npcl(FacialExpression.FRIENDLY, "Now to keep my end of the bargain. Give me the boots and an iron bar and I'll put on the spikes.").also { + if (inInventory(player!!, Items.CLIMBING_BOOTS_3105) && inInventory(player!!, Items.IRON_BAR_2351)) { + stage = 7 + } else if (inInventory(player!!, Items.CLIMBING_BOOTS_3105)){ + stage = 3 + } else if (inInventory(player!!, Items.IRON_BAR_2351)){ + stage = 4 + } else { + stage = 5 + } + } + 3 -> playerl(FacialExpression.FRIENDLY, "I don't have the iron bar.").also { stage = END_DIALOGUE } + 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 { + 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++ + sendMessage(player!!, "Dunstan has given you the spiked boots.") + } + 9 -> playerl(FacialExpression.FRIENDLY, "Thank you!").also { stage++ } + 10 -> npcl(FacialExpression.FRIENDLY, "No problem.").also { + stage = END_DIALOGUE + } + } + } + } + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..8e0d5f0a6 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/burthorpe/quest/deathplateau/EohricDialogueFile.kt @@ -0,0 +1,76 @@ +package content.region.asgarnia.burthorpe.quest.deathplateau + +import core.api.* +import core.game.dialogue.DialogueFile +import core.game.dialogue.FacialExpression +import core.game.dialogue.Topic +import core.tools.END_DIALOGUE +import core.tools.START_DIALOGUE +import content.data.Quests + +/** + * Eohric sub dialogue file for death plateau. + * Called by EohricDialogue + * @author bushtail + * @author ovenbread + */ +class EohricDialogueFile : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (getQuestStage(player!!, Quests.DEATH_PLATEAU)) { + in 5..9 -> { + when (stage) { + START_DIALOGUE -> player(FacialExpression.FRIENDLY, "Hi!").also { stage++ } + 1 -> npc(FacialExpression.FRIENDLY, "Hi, can I help?").also { stage++ } + 2 -> showTopics( + Topic(FacialExpression.THINKING, "I'm looking for the guard that was on the last night.", 10), + Topic(FacialExpression.FRIENDLY, "Do you know of another way up Death Plateau?", 20), + Topic(FacialExpression.FRIENDLY, "No, I'm just looking around.", END_DIALOGUE) + ) + 10 -> npcl(FacialExpression.FRIENDLY,"There was only one guard on last night. Harold. He's a nice lad, if a little dim.").also { stage++ } + 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!!, Quests.DEATH_PLATEAU, 10) + HaroldDialogueFile.resetNpc(player!!) + stage = END_DIALOGUE + } + 20 -> npcl(FacialExpression.FRIENDLY, "No, sorry. I wouldn't want to go north-east from here, it's very rocky and barren.").also { stage = END_DIALOGUE } + } + } + 10 -> { + when (stage) { + START_DIALOGUE -> player(FacialExpression.FRIENDLY, "Hi!").also { stage++ } + 1 -> npc(FacialExpression.FRIENDLY, "Hi, can I help?").also { stage++ } + 2 -> showTopics( + Topic(FacialExpression.THINKING, "Where is the guard that was on last night staying?", 10), + Topic(FacialExpression.FRIENDLY, "Do you know of another way up Death Plateau?", 20), + Topic(FacialExpression.FRIENDLY, "No, I'm just looking around.", END_DIALOGUE) + ) + 10 -> npc(FacialExpression.FRIENDLY, "Harold is staying at the Toad and Chicken.").also { stage = END_DIALOGUE } + 20 -> npcl(FacialExpression.FRIENDLY, "No, sorry. I wouldn't want to go north-east from here, it's very rocky and barren.").also { stage = END_DIALOGUE } + } + } + 11 -> { + when (stage) { + START_DIALOGUE -> player(FacialExpression.FRIENDLY, "Hi!").also { stage++ } + 1 -> npc(FacialExpression.FRIENDLY, "Hi, can I help?").also { stage++ } + 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!!, Quests.DEATH_PLATEAU, 12) + stage = END_DIALOGUE + } + } + } + 12 -> { + when (stage) { + START_DIALOGUE -> player(FacialExpression.FRIENDLY, "Hi!").also { stage++ } + 1 -> npc(FacialExpression.FRIENDLY, "Hi, can I help?").also { stage++ } + 2 -> playerl(FacialExpression.ASKING, "You said Harold had a weakness?").also { stage++ } + 3 -> npcl(FacialExpression.THINKING,"Yes, if you buy Harold a beer he might talk to you. I also know he has a weakness for gambling. Hope that helps!").also { stage++ } + 4 -> playerl(FacialExpression.FRIENDLY, "Thanks for the help!").also {stage = END_DIALOGUE} + } + } + } + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..e4242212b --- /dev/null +++ b/Server/src/main/content/region/asgarnia/burthorpe/quest/deathplateau/HaroldDialogueFile.kt @@ -0,0 +1,402 @@ +package content.region.asgarnia.burthorpe.quest.deathplateau + +import core.api.* +import core.game.dialogue.DialogueFile +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.game.world.update.flag.context.Graphics +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. + * Called by HaroldDialogue + * @author bushtail + * @author ovenbread + */ +class HaroldDialogueFile : DialogueFile() { + + companion object{ + const val ATTRIBUTE_JUMPSTAGE = "deathplateau:jumpStage" + const val ATTRIBUTE_HAROLD_MONEY = "/save:deathplateau:haroldMoney" + + /** Resets the NPC's quest attributes */ + fun resetNpc(player: Player) { + setAttribute(player, ATTRIBUTE_JUMPSTAGE, 0) + setAttribute(player, ATTRIBUTE_HAROLD_MONEY, 200) + } + } + override fun handle(componentID: Int, buttonID: Int) { + // This jumpStage is needed to resume the conversation after the dice interface. + // The dice interface closes the dialogue file. + if(getAttribute(player!!, ATTRIBUTE_JUMPSTAGE, 0) != 0) { + stage = getAttribute(player!!, ATTRIBUTE_JUMPSTAGE, 0) + setAttribute(player!!, ATTRIBUTE_JUMPSTAGE, 0) + } + println(getAttribute(player!!, ATTRIBUTE_HAROLD_MONEY, -1)) + when (getQuestStage(player!!, Quests.DEATH_PLATEAU)) { + 10 -> { // First time meeting. + when (stage) { + START_DIALOGUE -> player(FacialExpression.FRIENDLY, "Hello there.").also { stage++ } + 1 -> npcl(FacialExpression.FRIENDLY, "Hi.").also { stage++ } + 2 -> playerl(FacialExpression.FRIENDLY, "You're the guard that was on duty last night?").also { stage++ } + 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!!, Quests.DEATH_PLATEAU, 11) + stage = END_DIALOGUE + } + } + } + 11 -> { // Asking again. + when (stage) { + START_DIALOGUE -> player(FacialExpression.FRIENDLY, "Hello there.").also { stage++ } + 1 -> npcl(FacialExpression.ANNOYED, "What?").also { stage++ } + 2 -> playerl(FacialExpression.FRIENDLY, "You're the guard that was on duty last night?").also { stage++ } + 3 -> npcl(FacialExpression.FRIENDLY, "I said I didn't want to talk about it!").also { + stage = END_DIALOGUE + } + } + } + 12 -> { // After asking Eohric about opening up. + when (stage) { + START_DIALOGUE -> playerl(FacialExpression.FRIENDLY, "Hello there.").also { stage++ } + 1 -> npcl(FacialExpression.FRIENDLY, "Hi.").also { stage++ } + 2 -> playerl(FacialExpression.FRIENDLY, "Can I buy you a drink?").also { stage++ } + 3 -> npcl(FacialExpression.HAPPY, "Now you're talking! An Asgarnian Ale, please!").also { stage++ } + 4 -> { + if (inInventory(player!!, Items.ASGARNIAN_ALE_1905, 1)) { + removeItem(player!!, Items.ASGARNIAN_ALE_1905) + 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++ } + } else { + playerl(FacialExpression.FRIENDLY, "I'll go and get you one.").also { stage = END_DIALOGUE } + } + } + 5 -> { + end() + 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 } + } + } + } + } + 13 -> { // After getting him an Asgarnian Ale. + when (stage) { + START_DIALOGUE -> player(FacialExpression.FRIENDLY, "Hello there.").also { stage++ } + 1 -> npc(FacialExpression.FRIENDLY, "Hi.").also { stage++ } + 2 -> showTopics( + Topic(FacialExpression.ASKING, "Where were you when you last had the combination?", 10), + Topic(FacialExpression.FRIENDLY, "Would you like to gamble?", 30), + Topic(FacialExpression.FRIENDLY, "Can I buy you a drink?", 20) + ) + + 10 -> npcl(FacialExpression.FRIENDLY, "I honestly don't know! I've looked everywhere. I've searched the castle and my room!").also { stage++ } + 11 -> playerl(FacialExpression.ASKING, "Have you tried looking between here and the castle?").also { stage++ } + 12 -> npcl(FacialExpression.FRIENDLY, "Yeah, I tried that.").also { stage++ } + 13 -> npcl(FacialExpression.FRIENDLY, "I need another beer.").also { stage = END_DIALOGUE } + + 20 -> npcl(FacialExpression.FRIENDLY, "Sounds good! I normally drink Asgarnian Ale but you know what?").also { stage++ } + 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) || 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 { + player(FacialExpression.FRIENDLY, "I'll go and get you one.").also { stage = END_DIALOGUE } + } + } + 24 -> { + end() + setQuestStage(player!!, Quests.DEATH_PLATEAU, 14) + npc!!.isWalks = false + animate(npc!!, Animations.HUMAN_EATTING_829) + runTask(npc!!, 4) { + npc!!.sendChat("Wow!") + runTask(npc!!, 4) { + sendGraphics(Graphics(80, 96), npc!!.location) + runTask(npc!!, 6) { + npc(FacialExpression.DRUNK, "Now THAT hit the spot!").also { + runTask(npc!!, 6) { + npc!!.isWalks = true + end() + stage = END_DIALOGUE + } + } + } + } + } + } + + 30 -> npcl(FacialExpression.FRIENDLY, "Good. Good. I have some dice. How much do you want to offer?").also { stage++ } + 31 -> { + sendInputDialogue(player!!, true, "Enter amount:") { value -> + val wagerAmount = value as Int + // Check wager amount. + if (wagerAmount <= 0) { + sendMessage(player!!, "You have to offer some money.").also { stage = END_DIALOGUE } + } else if (wagerAmount > 1000) { + npcl(FacialExpression.FRIENDLY, "Woah! Do you think I'm made of money? Max bet is 1000 gold.").also { stage = END_DIALOGUE } + } else if (!inInventory(player!!, Items.COINS_995) || amountInInventory(player!!, Items.COINS_995) < value) { + sendMessage(player!!, "You do not have that much money!").also { stage = END_DIALOGUE } + } else if (removeItem(player!!, Item(Items.COINS_995, value))) { + player!!.setAttribute("deathplateau:wager", wagerAmount) + npc(FacialExpression.FRIENDLY, "OK. I'll roll first!").also { stage++ } + } + return@sendInputDialogue + } + } + 32 -> { + npcl(FacialExpression.FRIENDLY, "Don't forget that once I start my roll you can't back out of the bet! If you do you lose your stake!").also { stage++ } + } + 33 -> { + end() + npc!!.isWalks = false + // Dialogue drops when another interface is clicked. So a resume dialogue is needed. + val advanceStage : (() -> Unit) = { + npc!!.isWalks = true + setAttribute(player!!, ATTRIBUTE_JUMPSTAGE, stage + 1) + openDialogue(player!!, HaroldDialogueFile(), npc!!) + } + setAttribute(player!!, "deathplateau:dicegameclose", advanceStage) + openInterface(player!!, 99) + setInterfaceText(player!!, player!!.name, 99, 6) + } + 34 -> { + val wager = getAttribute(player!!, "deathplateau:wager", 0) + val haroldAmount = getAttribute(player!!, ATTRIBUTE_HAROLD_MONEY, 0) + if (getAttribute(player!!, "deathplateau:winstate", false)) { + // Win + if (wager > haroldAmount) { + npcl(FacialExpression.FRIENDLY, "Oh dear, I seem to have run out of money!").also { stage++ } + } else { + addItemOrDrop(player!!, Items.COINS_995, wager * 2) + sendMessage(player!!, "Harold has given you your winnings!") + setAttribute(player!!, ATTRIBUTE_HAROLD_MONEY,haroldAmount - wager) + sendItemDialogue(player!!, Items.COINS_995, "Harold has given you your winnings!").also { stage = END_DIALOGUE } + } + } else { + // Lose + setAttribute(player!!, ATTRIBUTE_HAROLD_MONEY,haroldAmount + wager) + sendItemDialogue(player!!, Items.COINS_995, "You give Harold his winnings.").also { stage = END_DIALOGUE } + } + } + 35 -> npcl(FacialExpression.FRIENDLY, "Here's what I have.").also { stage++ } + 36 -> sendItemDialogue(player!!, Items.COINS_995, "Harold has given you some of your winnings!").also { + sendMessage(player!!, "Harold has given you some of your winnings!") + val haroldAmount = getAttribute(player!!, ATTRIBUTE_HAROLD_MONEY, 200) + setAttribute(player!!, ATTRIBUTE_HAROLD_MONEY,0) + addItemOrDrop(player!!, Items.COINS_995, haroldAmount) + stage++ + } + 37 -> npcl(FacialExpression.FRIENDLY, "I'll write you out an IOU for the rest.").also { stage++ } + 38 -> { + addItemOrDrop(player!!, Items.IOU_3103) + 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} + } + } + } + 14 -> { // After getting him a Blurberry Special. + when (stage) { + START_DIALOGUE -> player(FacialExpression.FRIENDLY, "Hello there.").also { stage++ } + 1 -> npc(FacialExpression.DRUNK, "'Ello matey!'").also { stage++ } + 2 -> showTopics( + Topic(FacialExpression.THINKING, "Where were you when you last had the combination?", 10), + Topic(FacialExpression.FRIENDLY, "Would you like to gamble?", 20), + Topic(FacialExpression.FRIENDLY, "Can I buy you a drink?", 15) + ) + + 10 -> npc(FacialExpression.DRUNK,"Hmm...!").also { stage++ } + 11 -> npc(FacialExpression.DRUNK,"Er...!").also { stage++ } + 12 -> npc(FacialExpression.DRUNK,"What wash the queshtion?").also { stage = END_DIALOGUE } + + 15 -> npcl(FacialExpression.DRUNK, "I fink I've had enough!").also { stage = END_DIALOGUE } + + 20 -> npc(FacialExpression.DRUNK,"Shure!").also { stage++ } + 21 -> npc(FacialExpression.DRUNK,"Place your betsh pleashe!").also { stage++ } + 22 -> npc(FacialExpression.DRUNK,"*giggle*!").also { stage++ } + + 23 -> { + sendInputDialogue(player!!, true, "Enter amount:") { value -> + val wagerAmount = value as Int + // Check wager amount. + if (wagerAmount <= 0) { + sendMessage(player!!, "You have to offer some money.").also { stage = END_DIALOGUE } + } else if (wagerAmount > 1000) { + npcl(FacialExpression.DRUNK, "Eashy tiger! Max bet ish 1000 coinsh.").also { stage = END_DIALOGUE } + } else if (!inInventory(player!!, Items.COINS_995) || amountInInventory(player!!, Items.COINS_995) < value) { + sendMessage(player!!, "You do not have that much money!").also { stage = END_DIALOGUE } + } else if (removeItem(player!!, Item(Items.COINS_995, value))) { + player!!.setAttribute("deathplateau:wager", wagerAmount) + npc(FacialExpression.DRUNK, "Right...er...here goes...").also { stage++ } + } + return@sendInputDialogue + } + } + 24 -> { + end() + npc!!.isWalks = false + // Dialogue drops when another interface is clicked. So a resume dialogue is needed. + val advanceStage : (() -> Unit) = { + setAttribute(player!!, ATTRIBUTE_JUMPSTAGE, stage + (1..4).random()) + openDialogue(player!!, HaroldDialogueFile(), npc!!) + } + setAttribute(player!!, "deathplateau:dicegameclose", advanceStage) + openInterface(player!!, 99) + setInterfaceText(player!!, player!!.name, 99, 6) + } + 25 -> npc(FacialExpression.DRUNK, "Shixteen! How am I shupposhed to beat that!").also { stage = 30 } + 26 -> npc(FacialExpression.DRUNK, "I didn't know you could ushe four dishe. Oh well.").also { stage = 30 } + 27 -> npc(FacialExpression.DRUNK, "*hic*").also { stage = 30 } + 28 -> npc(FacialExpression.DRUNK, "I sheemed to have rolled a one.").also { stage = 30 } + 30 -> { + sendDialogue(player!!, "Harold is so drunk he can hardly see, let alone count!").also { stage++ } + npc!!.isWalks = true + sendMessage(player!!, "Harold is so drunk he can hardly see, let alone count!") + } + 31 -> { + val wager = getAttribute(player!!, "deathplateau:wager", 0) + val haroldAmount = getAttribute(player!!, ATTRIBUTE_HAROLD_MONEY, 0) + // Always Win + if (wager > haroldAmount) { + npcl(FacialExpression.DRUNK, " Um...not enough money.").also { stage++ } + } else { + addItemOrDrop(player!!, Items.COINS_995, wager * 2) + sendMessage(player!!, "Harold has given you your winnings!") + setAttribute(player!!, ATTRIBUTE_HAROLD_MONEY,haroldAmount - wager) + sendItemDialogue(player!!, Items.COINS_995, "Harold has given you your winnings!").also { stage = END_DIALOGUE } + } + } + 32 -> npcl(FacialExpression.DRUNK, "Heresh shome of it.").also { stage++ } + 33 -> sendItemDialogue(player!!, Items.COINS_995, "Harold has given you some of your winnings!").also { + sendMessage(player!!, "Harold has given you some of your winnings!") + val haroldAmount = getAttribute(player!!, ATTRIBUTE_HAROLD_MONEY, 200) + setAttribute(player!!, ATTRIBUTE_HAROLD_MONEY,0) + addItemOrDrop(player!!, Items.COINS_995, haroldAmount) + stage++ + } + 34 -> npcl(FacialExpression.DRUNK, "I owe you the resht!").also { stage++ } + 35 -> { + addItemOrDrop(player!!, Items.IOU_3103) + 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} + } + } + } + 15 -> { // If you lose your IOU + when (stage) { + START_DIALOGUE -> npcl(FacialExpression.FRIENDLY, "Hi.").also { + if (inInventory(player!!, Items.IOU_3103)){ + stage = 5 + } else { + stage++ + } + } + 1 -> playerl(FacialExpression.FRIENDLY, "I've lost the IOU you gave me.").also { stage++ } + 2 -> npcl(FacialExpression.FRIENDLY, "I'll write you another.").also { stage++ } + 3 -> { + addItemOrDrop(player!!, Items.IOU_3103) + sendItemDialogue(player!!, Items.IOU_3103, "Harold has given you an IOU scribbled on some paper.").also {stage = END_DIALOGUE} + } + + 5 -> npc(FacialExpression.FRIENDLY, "Hi.").also { stage++ } + 6 -> showTopics( + Topic(FacialExpression.ASKING, "Where were you when you last had the combination?", 10), + Topic(FacialExpression.FRIENDLY, "Would you like to gamble?", 30), + Topic(FacialExpression.FRIENDLY, "Can I buy you a drink?", 20) + ) + + 10 -> npcl(FacialExpression.FRIENDLY, "I honestly don't know! I've looked everywhere. I've searched the castle and my room!").also { stage++ } + 11 -> playerl(FacialExpression.ASKING, "Have you tried looking between here and the castle?").also { stage++ } + 12 -> npcl(FacialExpression.FRIENDLY, "Yeah, I tried that.").also { stage++ } + 13 -> npcl(FacialExpression.FRIENDLY, "I need another beer.").also { stage = END_DIALOGUE } + + 20 -> npcl(FacialExpression.FRIENDLY, "I've run out of money!").also { stage++ } + 21 -> npcl(FacialExpression.FRIENDLY, "Oh dear. I need beer.").also { stage = END_DIALOGUE } + + 30 -> npcl(FacialExpression.HAPPY, "Now you're talking! An Asgarnian Ale, please!").also { stage++ } + 31 -> { + if (inInventory(player!!, Items.ASGARNIAN_ALE_1905, 1)) { + removeItem(player!!, Items.ASGARNIAN_ALE_1905) + sendMessage(player!!, "You give Harold an Asgarnian Ale.") + sendItemDialogue(player!!, Items.ASGARNIAN_ALE_1905, "You give Harold an Asgarnian Ale.").also { stage++ } + } else { + playerl(FacialExpression.FRIENDLY, "I'll go and get you one.").also { stage = END_DIALOGUE } + } + } + 32 -> { + end() + animate(npc!!, Animations.HUMAN_EATTING_829) + runTask(npc!!, 5) { + npcl(FacialExpression.FRIENDLY, "Arrh. That hit the spot!").also { stage = END_DIALOGUE } + } + } + + } + } + in 16 .. 29 -> { // If you lose your IOU + when (stage) { + START_DIALOGUE -> npcl(FacialExpression.FRIENDLY, "Hi.").also { + if (inInventory(player!!, Items.COMBINATION_3102) || inInventory(player!!, Items.IOU_3103)){ + stage = 5 + } else { + stage++ + } + } + 1 -> playerl(FacialExpression.FRIENDLY, "I've lost the IOU you gave me.").also { stage++ } + 2 -> npcl(FacialExpression.FRIENDLY, "I'll write you another.").also { stage++ } + 3 -> { + addItemOrDrop(player!!, Items.COMBINATION_3102) + sendItemDialogue(player!!, Items.COMBINATION_3102, "Harold has given you the IOU, which you know is the combination.").also {stage = END_DIALOGUE} + } + + 5 -> npc(FacialExpression.FRIENDLY, "Hi.").also { stage++ } + 6 -> showTopics( + Topic(FacialExpression.ASKING, "Where were you when you last had the combination?", 10), + Topic(FacialExpression.FRIENDLY, "Would you like to gamble?", 30), + Topic(FacialExpression.FRIENDLY, "Can I buy you a drink?", 20) + ) + + 10 -> npcl(FacialExpression.FRIENDLY, "I honestly don't know! I've looked everywhere. I've searched the castle and my room!").also { stage++ } + 11 -> playerl(FacialExpression.ASKING, "Have you tried looking between here and the castle?").also { stage++ } + 12 -> npcl(FacialExpression.FRIENDLY, "Yeah, I tried that.").also { stage++ } + 13 -> npcl(FacialExpression.FRIENDLY, "I need another beer.").also { stage = END_DIALOGUE } + + 20 -> npcl(FacialExpression.FRIENDLY, "I've run out of money!").also { stage++ } + 21 -> npcl(FacialExpression.FRIENDLY, "Oh dear. I need beer.").also { stage = END_DIALOGUE } + + 30 -> npcl(FacialExpression.HAPPY, "Now you're talking! An Asgarnian Ale, please!").also { stage++ } + 31 -> { + if (inInventory(player!!, Items.ASGARNIAN_ALE_1905, 1)) { + removeItem(player!!, Items.ASGARNIAN_ALE_1905) + sendMessage(player!!, "You give Harold an Asgarnian Ale.") + sendItemDialogue(player!!, Items.ASGARNIAN_ALE_1905, "You give Harold an Asgarnian Ale.").also { stage++ } + } else { + playerl(FacialExpression.FRIENDLY, "I'll go and get you one.").also { stage = END_DIALOGUE } + } + } + 32 -> { + end() + animate(npc!!, Animations.HUMAN_EATTING_829) + runTask(npc!!, 5) { + npcl(FacialExpression.FRIENDLY, "Arrh. That hit the spot!").also { stage = END_DIALOGUE } + } + } + } + } + } + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..4a4531422 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/burthorpe/quest/deathplateau/IOUNoteDialogueFile.kt @@ -0,0 +1,38 @@ +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.tools.END_DIALOGUE +import org.rs09.consts.Items + +class IOUNoteDialogueFile : DialogueFile() { + var a = 0 + override fun handle(componentID: Int, buttonID: Int) { + 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++ } + 1 -> player(FacialExpression.EXTREMELY_SHOCKED, "Wait just a minute!").also { stage++ } + 2 -> playerl(FacialExpression.EXTREMELY_SHOCKED, "The IOU is written on the back of the combination! The stupid guard had it in his back pocket all the time!").also { stage++ } + 3 -> { + if (removeItem(player!!, Items.IOU_3103)) { + addItemOrDrop(player!!, Items.COMBINATION_3102) + setQuestStage(player!!, Quests.DEATH_PLATEAU, 16) + sendItemDialogue(player!!, Items.COMBINATION_3102, "You have found the combination!").also { stage++ } + } + } + 4 -> { + end() + stage = END_DIALOGUE + openInterface(player!!, 220) + setInterfaceText(player!!, "Red is North of Blue. Yellow is South of Purple.", 220, 7) + setInterfaceText(player!!, "Green is North of Purple. Blue is West of", 220, 8) + setInterfaceText(player!!, "Yellow. Purple is East of Red.", 220, 9) + } + } + } + } + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..820fdf26e --- /dev/null +++ b/Server/src/main/content/region/asgarnia/burthorpe/quest/deathplateau/SabaDialogueFile.kt @@ -0,0 +1,63 @@ +package content.region.asgarnia.burthorpe.quest.deathplateau + +import core.api.* +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!!, Quests.DEATH_PLATEAU)) { + 19 -> { + when (stage) { + 0 -> player(FacialExpression.FRIENDLY, "Hello!").also { stage++ } + 1 -> npc(FacialExpression.ANNOYED, "What?!").also { stage++ } + 2 -> showTopics( + Topic(FacialExpression.THINKING, "I'm looking for the guard that was on the last night.", 10), + Topic(FacialExpression.THINKING, "Do you know of another way up Death Plateau?", 20), + Topic(FacialExpression.HALF_GUILTY, "Nothing, sorry!", END_DIALOGUE), + ) + 10 -> npcl(FacialExpression.ANNOYED,"Who?!").also { stage++ } + 11 -> npcl(FacialExpression.ANNOYED,"Buzz off!").also { stage = END_DIALOGUE } + + 20 -> npcl(FacialExpression.ANNOYED,"Why would I want to go up there? I just want to be left in peace!").also { stage++ } + 21 -> npcl(FacialExpression.ANNOYED,"It used to be just humans trampling past my cave and making a racket. Now there's those blasted trolls too! Not only do they stink and argue with each other loudly but they are always fighting the humans.").also { stage++ } + 22 -> npcl(FacialExpression.ANNOYED,"I just want to be left in peace!").also { stage++ } + 23 -> playerl(FacialExpression.FRIENDLY, "Ah... I might be able to help you.").also { stage++ } + 24 -> npcl(FacialExpression.ANNOYED,"How?!").also { stage++ } + 25 -> playerl(FacialExpression.HALF_THINKING, "I'm trying to help the...er...humans to reclaim back Death Plateau. If you help me then at least you'd be rid of the trolls.").also { stage++ } + 26 -> npcl(FacialExpression.ANNOYED,"Hmph.").also { stage++ } + 27 -> npcl(FacialExpression.ANNOYED,"Let me see...").also { stage++ } + 28 -> npcl(FacialExpression.HALF_GUILTY,"I've only been up Death Plateau once to complain about the noise but those pesky trolls started throwing rocks at me!").also { stage++ } + 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!!, Quests.DEATH_PLATEAU, 20) + stage = END_DIALOGUE } + } + } + 20 -> { + when (stage) { + 0 -> playerl(FacialExpression.FRIENDLY, "Hello.").also { stage++ } + 1 -> npcl(FacialExpression.ANNOYED, "Have you got rid of those pesky trolls yet?").also { stage++ } + 2 -> playerl(FacialExpression.FRIENDLY, "Where did you say this Sherpa was?").also { stage++ } + 3 -> npcl(FacialExpression.ANNOYED, "I dunno but he must live around here somewhere!").also { + stage = END_DIALOGUE } + } + } + in 21 .. 26 -> { + when (stage) { + 0 -> playerl(FacialExpression.FRIENDLY, "Hello.").also { stage++ } + 1 -> npcl(FacialExpression.FRIENDLY, "Have you got rid of those pesky trolls yet?").also { stage++ } + 2 -> playerl(FacialExpression.FRIENDLY, "I'm working on it!").also { stage++ } + 3 -> npcl(FacialExpression.FRIENDLY, "Grr!").also { + stage = END_DIALOGUE } + } + } + } + } + +} \ No newline at end of file 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 new file mode 100644 index 000000000..96d821a9f --- /dev/null +++ b/Server/src/main/content/region/asgarnia/burthorpe/quest/deathplateau/SecretWayLocation.kt @@ -0,0 +1,20 @@ +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 +import core.game.world.map.zone.ZoneBorders + +class SecretWayLocation : MapArea { + override fun defineAreaBorders(): Array { + return arrayOf(ZoneBorders(2866, 3609, 2866, 3609)) + } + + override fun areaEnter(entity: Entity) { + 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, 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 new file mode 100644 index 000000000..c69cb4a94 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/burthorpe/quest/deathplateau/TenzingDialogueFile.kt @@ -0,0 +1,144 @@ +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.item.Item +import core.tools.END_DIALOGUE +import core.tools.START_DIALOGUE +import org.rs09.consts.Items + +class TenzingDialogueFile : DialogueFile() { + + override fun handle(componentID: Int, buttonID: Int) { + when (getQuestStage(player!!, Quests.DEATH_PLATEAU)) { + 20 -> { + when (stage) { + START_DIALOGUE -> playerl(FacialExpression.FRIENDLY, "Hello!").also { stage++ } + 1 -> npcl(FacialExpression.FRIENDLY, "Hello. How can I help?").also { stage++ } + 2 -> playerl(FacialExpression.FRIENDLY, "I'm helping the Imperial Guard. They need to find a way to sneak up Death Plateau to destroy the troll camp! Saba seemed to think you'd be able to help.").also { stage++ } + 3 -> npcl(FacialExpression.FRIENDLY, "Ah...Saba is still alive and kicking?").also { stage++ } + 4 -> playerl(FacialExpression.FRIENDLY, "Yeh, he seemed very bitter.").also { stage++ } + 5 -> npcl(FacialExpression.FRIENDLY, "That's Saba alright!").also { stage++ } + 6 -> npcl(FacialExpression.FRIENDLY, "I do know of a secret way up to Death Plateau, the Imperial Guard would be able to use it at night and not be seen until it was too late!").also { stage++ } + 7 -> npcl(FacialExpression.FRIENDLY, "I'd be happy to show you it if you do something for me first.").also { stage++ } + 8 -> playerl(FacialExpression.FRIENDLY, "Name it.").also { stage++ } + 9 -> npcl(FacialExpression.FRIENDLY, "I don't get into town much and I'm getting low on supplies. I need ten loaves of bread and ten cooked trout, that should see me through the winter.").also { stage++ } + 10 -> playerl(FacialExpression.FRIENDLY, "Anything else?").also { stage++ } + 11 -> npcl(FacialExpression.FRIENDLY, "Yes. My climbing boots need to have new spikes, so can you take them to Dunstan in Burthorpe? He always puts my spikes on for me.").also { stage++ } + 12 -> showTopics( + Topic(FacialExpression.FRIENDLY, "OK, I'll get those for you.", 20 ), + Topic(FacialExpression.FRIENDLY, "I'll find the secret way for myself.", 30 ) + ) + 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!!, Quests.DEATH_PLATEAU, 21) + stage = END_DIALOGUE + } + 30 -> npcl(FacialExpression.ANNOYED, "Hmph.").also { stage = END_DIALOGUE } + } + } + in 21 .. 23 -> { + when (stage) { + START_DIALOGUE -> playerl(FacialExpression.FRIENDLY, "Hello!").also { + if (inInventory(player!!, Items.CLIMBING_BOOTS_3105) || inInventory(player!!, Items.SPIKED_BOOTS_3107)) { + stage = 1 + } else { + stage = 5 + } + } + 1 -> npcl(FacialExpression.FRIENDLY, "Has Dunstan added spikes to my climbing boots yet?").also { stage++ } + 2 -> playerl(FacialExpression.FRIENDLY, "No, not yet.").also { stage++ } + 3 -> npcl(FacialExpression.FRIENDLY, "Well, when he has, bring the boots to me with ten loaves of bread and ten cooked trout. I have to prepare for the winter, after all.").also { + stage = END_DIALOGUE + } + 5 -> npcl(FacialExpression.FRIENDLY, "Have you brought me the items I asked for?").also { stage++ } + 6 -> playerl(FacialExpression.FRIENDLY, "I've lost the climbing boots.").also { stage++ } + 7 -> npcl(FacialExpression.FRIENDLY, "These are expensive, do not lose another pair!").also { stage++ } + 8 -> sendItemDialogue(player!!, Items.CLIMBING_BOOTS_3105, "Tenzing has given you his Climbing boots.").also { + addItemOrDrop(player!!, Items.CLIMBING_BOOTS_3105, 1) + stage = END_DIALOGUE + } + } + } + 24 -> { + when (stage) { + START_DIALOGUE -> playerl(FacialExpression.FRIENDLY, "Hello!").also { stage++ } + 1 -> npcl(FacialExpression.FRIENDLY, "Have you brought me the items I asked for?").also { + if (inInventory(player!!, Items.SPIKED_BOOTS_3107) && inInventory(player!!, Items.BREAD_2309, 10) && inInventory(player!!, Items.TROUT_333, 10)) { + stage = 3 + } else { + stage = 2 + } + } + 2 -> playerl(FacialExpression.FRIENDLY, "I don't have the:" + + (if(!inInventory(player!!, Items.SPIKED_BOOTS_3107)) " Spiked boots." else "") + + (if(!inInventory(player!!, Items.BREAD_2309, 10)) " Ten loaves of bread." else "") + + (if(!inInventory(player!!, Items.TROUT_333, 10)) " Ten cooked trout." else "") + ).also { + stage = END_DIALOGUE + } + 3 -> sendItemDialogue(player!!, Items.SPIKED_BOOTS_3107, "You give Tenzing the Spiked boots.").also { + if (removeItem(player!!, Item(Items.SPIKED_BOOTS_3107))) { + stage++ + } else { + stage = END_DIALOGUE + } + } + 4 -> sendDoubleItemDialogue(player!!, Items.BREAD_2309, Items.TROUT_333, "You give Tenzing the loaves of bread and the cooked trout.").also { + if (removeItem(player!!, Item(Items.BREAD_2309, 10)) && removeItem(player!!, Item(Items.TROUT_333, 10)) ) { + stage++ + } else { + stage = END_DIALOGUE + } + } + 5 -> npcl(FacialExpression.FRIENDLY, "Thank you very much traveller. I'm now ready for the winter!").also { stage++ } + 6 -> playerl(FacialExpression.FRIENDLY, "You said you would show me the secret way to Death Plateau?").also { stage++ } + 7 -> npcl(FacialExpression.FRIENDLY, "Yes, of course! I drew up a map in case I ever needed to use it again.").also { stage++ } + 8 -> sendDialogue(player!!, "Tenzing has given you a map of the secret way!").also { + addItemOrDrop(player!!, Items.SECRET_WAY_MAP_3104) + stage++ + } + 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!!, Quests.DEATH_PLATEAU, 25) + stage = END_DIALOGUE + } + } + } + in 25.. 29 -> { + when (stage) { + START_DIALOGUE -> playerl(FacialExpression.FRIENDLY, "Hello Tenzing!").also { stage++ } + 1 -> npcl(FacialExpression.FRIENDLY, "Hello again traveller. What can I do for you?").also { + if (inInventory(player!!, Items.SECRET_WAY_MAP_3104)) { + stage = 10 + } else { + stage = 2 + } + } + 2 -> playerl(FacialExpression.FRIENDLY, "I've lost the secret way map.").also { stage++ } + 3 -> npcl(FacialExpression.FRIENDLY, "Never mind. I'll quickly draw you another one.").also { stage++ } + 4 -> sendDialogue(player!!, "Tenzing has given you a map of the secret way!").also { + addItemOrDrop(player!!, Items.SECRET_WAY_MAP_3104) + stage = END_DIALOGUE + } + + 10 -> showTopics( + Topic(FacialExpression.FRIENDLY, "I'm lost!", 20 ), + Topic(FacialExpression.FRIENDLY, "Nothing, thanks.", 30 ) + ) + + 20 -> npcl(FacialExpression.FRIENDLY, "To get back to Burthorpe follow the path going east.").also { stage++ } + 21 -> npcl(FacialExpression.FRIENDLY, "I thought you were going to investigate the secret way to Death Plateau? Use the back door to my hut, hop over the stile and follow that path.").also { stage++ } + 22 -> playerl(FacialExpression.FRIENDLY, "Oh yes, of course! Thanks!").also { stage = END_DIALOGUE } + + 30 -> npcl(FacialExpression.FRIENDLY, "Go in peace traveller.").also { stage = END_DIALOGUE } + } + } + } + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..0b04d9ba1 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/burthorpe/quest/trollstronghold/BerryNpc.kt @@ -0,0 +1,52 @@ +package content.region.asgarnia.burthorpe.quest.trollstronghold + +import content.data.Quests +import core.api.isQuestInProgress +import core.api.produceGroundItem +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 org.rs09.consts.Items +import org.rs09.consts.NPCs + +@Initializable +class BerryNpc(id: Int = 0, location: Location? = null) : AbstractNPC(id, location) { + override fun construct(id: Int, location: Location, vararg objects: Any): AbstractNPC { + return BerryNpc(id, location) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.BERRY_1127, NPCs.BERRY_1129) + } + + // Transform from sleeping to awake. + override fun isAttackable(entity: Entity, style: CombatStyle, message: Boolean): Boolean { + val attackable = super.isAttackable(entity, style, message) + if (this.id == NPCs.BERRY_1129) { + val prevLifePoints = this.skills.lifepoints + this.transform(NPCs.BERRY_1127) + this.skills.lifepoints = prevLifePoints + } + return attackable + } + + override fun handleTickActions() { + super.handleTickActions() + // When returning to spawn, transform back to sleeping and keep lifepoints. + if (getAttribute("return-to-spawn", false) && this.id == NPCs.BERRY_1127) { + val prevLifePoints = this.skills.lifepoints + this.transform(NPCs.BERRY_1129) + this.skills.lifepoints = prevLifePoints + } + } + + override fun finalizeDeath(killer: Entity?) { + if (isQuestInProgress(killer as Player, Quests.TROLL_STRONGHOLD, 8, 10)) { + produceGroundItem(killer, Items.CELL_KEY_2_3137, 1, this.location) + } + super.finalizeDeath(killer) + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..a1d76a63e --- /dev/null +++ b/Server/src/main/content/region/asgarnia/burthorpe/quest/trollstronghold/DadDialogue.kt @@ -0,0 +1,49 @@ +package content.region.asgarnia.burthorpe.quest.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.plugin.Initializable +import core.tools.END_DIALOGUE +import core.tools.START_DIALOGUE +import org.rs09.consts.NPCs + +@Initializable +class DadDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + 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++ } + 1 -> showTopics( + Topic(FacialExpression.THINKING, "Why are you called Dad?", 2), + Topic(FacialExpression.FRIENDLY, "I accept your challenge!", 3), + Topic(FacialExpression.SCARED, "Eek! No thanks.", END_DIALOGUE) + ) + + 2 -> npcl(FacialExpression.OLD_HAPPY, "Troll named after first thing try to eat!").also { stage = 1 } + 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!!, Quests.TROLL_STRONGHOLD, 4) + stage = END_DIALOGUE + } + } + } + in 5..100 -> { + sendMessage(player, "He doesn't seem interested in talking right now.") + } + } + return true + } + override fun newInstance(player: Player?): DialoguePlugin { + return DadDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.DAD_1125) + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..4428bf902 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/burthorpe/quest/trollstronghold/DadDialogueFile.kt @@ -0,0 +1,41 @@ +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 +import core.game.dialogue.Topic +import core.tools.END_DIALOGUE +import core.tools.START_DIALOGUE + +class DadDialogueFile(private val dialogueNum: Int = 0) : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when(dialogueNum) { + 1 -> when (stage) { + START_DIALOGUE -> npcl(FacialExpression.OLD_HAPPY, "No human pass through arena without defeating Dad!").also { + stage = END_DIALOGUE + 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!!, Quests.TROLL_STRONGHOLD, 4) + stage = END_DIALOGUE + } + } + 3 -> when (stage) { + START_DIALOGUE -> npcl(FacialExpression.OLD_NORMAL, "Stop! You win. Not hurt Dad.").also { stage++ } + 1 -> showTopics( + Topic(FacialExpression.FRIENDLY, "I'll be going now.", END_DIALOGUE), + Topic(FacialExpression.ANGRY_WITH_SMILE, "I'm not done yet! Prepare to die!", 2) + ) + 2 -> player!!.attack(npc).also { + setQuestStage(player!!, Quests.TROLL_STRONGHOLD, 5) + stage = END_DIALOGUE + } + } + } + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..f489aa1ed --- /dev/null +++ b/Server/src/main/content/region/asgarnia/burthorpe/quest/trollstronghold/DadNpc.kt @@ -0,0 +1,76 @@ +package content.region.asgarnia.burthorpe.quest.trollstronghold + +import content.data.Quests +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.npc.AbstractNPC +import core.game.system.task.Pulse +import core.game.world.map.Location +import core.plugin.Initializable +import core.tools.RandomFunction +import org.rs09.consts.NPCs + +@Initializable +class DadNpc(id: Int = 0, location: Location? = null) : AbstractNPC(id, location) { + override fun construct(id: Int, location: Location, vararg objects: Any): AbstractNPC { + return DadNpc(id, location) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.DAD_1125) + } + + override fun isAttackable(entity: Entity, style: CombatStyle, message: Boolean): Boolean { + val attackable = super.isAttackable(entity, style, message) + val player = entity.asPlayer() + + // Attack Dad. If quest is done, you cannot attack Dad. + 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 } + } + return attackable + } + + override fun checkImpact(state: BattleState) { + super.checkImpact(state) + val player = state.attacker + val opponent = state.victim + // If dad is below 30/120 lifepoints, give player option + // Dad can be killed if you oneshot him past 30. + if (opponent.skills.lifepoints < 30) { + player.properties.combatPulse.stop() + opponent.properties.combatPulse.stop() + if (getQuestStage(player!!.asPlayer(), Quests.TROLL_STRONGHOLD) == 4){ + setQuestStage(player!!.asPlayer(), Quests.TROLL_STRONGHOLD, 5) + } + submitWorldPulse(object : Pulse(){ + var counter = 0 + override fun pulse(): Boolean { + when(counter++){ + 1 -> { + openDialogue(player.asPlayer(), DadDialogueFile(3), opponent.asNpc()) + return true; + } + } + return false + } + }) + } + } + override fun finalizeDeath(killer: Entity?) { + // In case Dad gets one shotted to death. + super.finalizeDeath(killer) + if (getQuestStage(killer!!.asPlayer(), Quests.TROLL_STRONGHOLD) == 4){ + setQuestStage(killer!!.asPlayer(), Quests.TROLL_STRONGHOLD, 5) + } + } + override fun handleTickActions() { + super.handleTickActions() + if(RandomFunction.roll(35)){ + } + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..2575fc4b6 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/burthorpe/quest/trollstronghold/DenulthDialogueFile.kt @@ -0,0 +1,57 @@ +package content.region.asgarnia.burthorpe.quest.trollstronghold + +import content.data.Quests +import core.api.* +import core.game.dialogue.DialogueFile +import core.game.dialogue.FacialExpression +import core.tools.END_DIALOGUE +import core.tools.START_DIALOGUE +import org.rs09.consts.Items + +/** + * Denulth sub dialogue file for troll stronghold. + * Called by DenulthDialogue + * @author ovenbread + */ +class DenulthDialogueFile : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + 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++ } + 1 -> { + if (hasAnItem(player!!, Items.CLIMBING_BOOTS_3105).exists()) { + playerl(FacialExpression.FRIENDLY, " I've got some climbing boots.").also { stage = 2 } + } else { + playerl(FacialExpression.FRIENDLY, "I haven't found a way to climb up yet.").also { stage = 3 } + } + } + 2 -> npcl(FacialExpression.FRIENDLY, "Then hurry, friend! What are you still doing here?").also { stage = END_DIALOGUE } + 3 -> npcl(FacialExpression.FRIENDLY, "Hurry, friend! Who knows what they'll do with Godric?").also { stage = END_DIALOGUE } + } + } + in 8 .. 10 -> { + when (stage) { + START_DIALOGUE -> playerl(FacialExpression.FRIENDLY, "Hello!").also { stage++ } + 1 -> npcl(FacialExpression.FRIENDLY, "Welcome back friend!").also { stage++ } + 2 -> playerl(FacialExpression.FRIENDLY, "I've found my way into the prison.").also { stage++ } + 3 -> npcl(FacialExpression.FRIENDLY, "...and?").also { stage++ } + 4 -> playerl(FacialExpression.FRIENDLY, "That's all.").also { stage++ } + 5 -> npcl(FacialExpression.FRIENDLY, "Hurry, friend. Find a way to free Godric!").also { + stage = END_DIALOGUE + } + } + } + 11 -> { + when (stage) { + START_DIALOGUE -> playerl(FacialExpression.FRIENDLY, "Hello!").also { stage++ } + 1 -> npcl(FacialExpression.FRIENDLY, "Welcome back friend!").also { stage++ } + 2 -> playerl(FacialExpression.FRIENDLY, "I have freed Godric!").also { stage++ } + 3 -> npcl(FacialExpression.FRIENDLY, "Oh, what great news! You should hurry to tell Dunstan, he will be overjoyed!").also { + stage = END_DIALOGUE + } + } + } + } + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..4cd3a3f1a --- /dev/null +++ b/Server/src/main/content/region/asgarnia/burthorpe/quest/trollstronghold/DunstanDialogueFile.kt @@ -0,0 +1,79 @@ +package content.region.asgarnia.burthorpe.quest.trollstronghold + +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!!, 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.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 -> { + when (stage) { + START_DIALOGUE -> playerl(FacialExpression.FRIENDLY, "Has Godric returned home?").also { stage++ } + 1 -> npcl(FacialExpression.FRIENDLY, "He is safe and sound, thanks to you my friend!").also { stage++ } + 2 -> playerl(FacialExpression.FRIENDLY, "I'm glad to hear it.").also { stage++ } + 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!!, Quests.TROLL_STRONGHOLD) + } + } + } + } + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..cee11ddfc --- /dev/null +++ b/Server/src/main/content/region/asgarnia/burthorpe/quest/trollstronghold/TrollGeneralsNpc.kt @@ -0,0 +1,30 @@ +package content.region.asgarnia.burthorpe.quest.trollstronghold + +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 + +@Initializable +class TrollGeneralsNpc(id: Int = 0, location: Location? = null) : AbstractNPC(id,location) { + + override fun construct(id: Int, location: Location?, vararg objects: Any?): AbstractNPC { + return TrollGeneralsNpc(id, location) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.TROLL_GENERAL_1115, NPCs.TROLL_GENERAL_1116, NPCs.TROLL_GENERAL_1117) + } + + override fun finalizeDeath(killer: Entity?) { + if(isQuestInProgress(killer as Player, Quests.TROLL_STRONGHOLD, 1, 7)) { + produceGroundItem(killer, Items.PRISON_KEY_3135, 1, this.location) + } + super.finalizeDeath(killer) + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..6630bfa14 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/burthorpe/quest/trollstronghold/TrollStronghold.kt @@ -0,0 +1,99 @@ +package content.region.asgarnia.burthorpe.quest.trollstronghold + +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(Quests.TROLL_STRONGHOLD,128, 127, 1, 317, 0, 1, 50) { + + override fun drawJournal(player: Player?, stage: Int) { + super.drawJournal(player, stage) + var line = 12 + var stage = getStage(player) + + 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, 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, 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 { + line(player, "I promised !!Denulth?? that I would rescue !!Godric?? from the !!Troll??", line++, stage == 100) + line(player, "!!Stronghold??", line++, stage == 100) + line++ + if (stage >= 5 || inBank(player, Items.CLIMBING_BOOTS_3105) || inInventory(player, Items.CLIMBING_BOOTS_3105) || inEquipment(player, Items.CLIMBING_BOOTS_3105)) { + line(player, "I got some !!climbing boots?? from !!Tenzing??", line++, true) + } else { + line(player, "I have to get some !!climbing boots?? from !!Tenzing??", line++) + } + line++ + if (stage >= 5) { + line(player, "I have defeated the !!Troll Champion??", line++, true) + } else if (stage >= 3) { + line(player, "I have accepted the !!Troll Champion's?? challenge.", 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++ + if (stage >= 8) { + line(player, "I found my way into the !!Prison??", line++, true) + } else if (stage >= 5) { + line(player, "I have to find a way to get into the !!prison??", line++) + } + line++ + if (stage >= 11) { + line(player, "I've rescued !!Godric?? and !!Mad Eadgar??", line++, true) + } else if (stage >= 8) { + line(player, "I have to rescue !!Godric?? and !!Mad Eadgar??", line++) + } + if (stage >= 100) { + line++ + line(player, "I talked to Dunstan and he rewarded me.", line++, true) + } else if (stage >= 11) { + line(player, "I should return and tell !!Dunstan?? his son is safe.", 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 Troll Stronghold quest!", 277, 4) + player.packetDispatch.sendItemZoomOnInterface(Items.MYSTERIOUS_LAMP_13227, 240, 277, 5) + + drawReward(player,"1 Quest Point", ln++) + drawReward(player,"2 Reward lamps giving 10,000", ln++) + drawReward(player,"XP each", ln++) + + addItemOrDrop(player, Items.MYSTERIOUS_LAMP_13227, 2) + } + + 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/trollstronghold/TrollStrongholdListener.kt b/Server/src/main/content/region/asgarnia/burthorpe/quest/trollstronghold/TrollStrongholdListener.kt new file mode 100644 index 000000000..f276b3944 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/burthorpe/quest/trollstronghold/TrollStrongholdListener.kt @@ -0,0 +1,318 @@ +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 +import core.game.interaction.InteractionListener +import core.game.node.Node +import core.game.node.entity.player.Player +import core.game.node.entity.skill.Skills +import core.game.system.task.Pulse +import core.game.world.map.Location +import core.game.world.update.flag.context.Animation +import core.tools.RandomFunction +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import org.rs09.consts.Scenery +import kotlin.math.ceil + +class TrollStrongholdListener: InteractionListener { + + override fun defineListeners() { + // 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, 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, Quests.TROLL_STRONGHOLD) > 0) { + DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) + } else { + sendMessage(player, "You need to start the Troll Stronghold quest.") + } + return@on true; + } + + // 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, Quests.TROLL_STRONGHOLD) < 5){ + openDialogue(player, DadDialogueFile(1), findNPC(NPCs.DAD_1125)!!) + } else { + DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) + } + return@on true; + } + + // Key to unlock the prison door + on(Scenery.PRISON_DOOR_3780, IntType.SCENERY, "unlock"){ player, node -> + if (getQuestStage(player, Quests.TROLL_STRONGHOLD) >= 8){ + DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) + } else { + if (inInventory(player, Items.PRISON_KEY_3135)) { + 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()) + sendMessage(player, "You unlock the prison door.") + } + } else { + sendMessage(player, "The prison door is locked.") + } + } + return@on true; + } + + // Pickpocket Twig + on(NPCs.TWIG_1128, IntType.NPC, "pickpocket") { player, node -> + val npc = node.asNpc() + player.lock() + submitWorldPulse(object : Pulse(){ + var counter = 0 + override fun pulse(): Boolean { + when(counter++){ + 0 -> { + if (hasLevelDyn(player, Skills.THIEVING, 30)){ + animate(player, Animation(881)) + } else { + sendMessage(player, "You need to be a level 30 thief to pickpocket Twig.") + return true + } + } + 3 -> { + val success: Boolean = success(player, Skills.THIEVING) + if(success){ + 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 { + sendMessage(player, "You find nothing on Twig.") + } + } else { + sendChat(npc, "What you think you doing?") + transformNpc(npc, NPCs.TWIG_1126, 50) + npc.attack(player) + } + player.unlock() + return true + } + } + return false + } + }) + return@on true + } + + // Pickpocket Berry + on(NPCs.BERRY_1129, IntType.NPC, "pickpocket") { player, node -> + val npc = node.asNpc() + player.lock() + submitWorldPulse(object : Pulse(){ + var counter = 0 + override fun pulse(): Boolean { + when(counter++){ + 0 -> { + if (hasLevelDyn(player, Skills.THIEVING, 30)){ + animate(player, Animation(881)) + } else { + sendMessage(player, "You need to be a level 30 thief to pickpocket Berry.") + return true + } + } + 3 -> { + val success: Boolean = success(player, Skills.THIEVING) + if(success){ + 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 { + sendMessage(player, "You find nothing on Berry.") + } + } else { + sendChat(npc, "What you think you doing?") + transformNpc(npc, NPCs.BERRY_1127, 50) + npc.attack(player) + } + player.unlock() + return true + } + } + return false + } + }) + return@on true + } + + // Key to unlock Mad Eadgar's cell + fun unlockMadEadgarCellDoor(player: Player, node: Node) { + if (inInventory(player, Items.CELL_KEY_1_3136)){ + sendMessage(player, "You unlock the cell door.") + 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)!! + sendNPCDialogue(player, NPCs.EADGAR_1113, "Thanks! I'm off back home!") + npc.walkRadius = 40 + submitWorldPulse(object : Pulse(0) { + // This is basically chaining a series of walks + and door handler. + var count = 0 + var stage = 0 + var trigger = true + var targetLocation = Location(0, 0, 0) + override fun pulse(): Boolean { + if (npc.location.equals(targetLocation)){ + stage++ + trigger = true + } + if (trigger) { + trigger = false + when (stage) { + 0 -> forceWalk(npc, Location(2831, 10082, 0), "dumb").also { + npc.walkRadius = 11 + npc.setWalks(false) + targetLocation = Location(2831, 10082, 0) + } + 1 -> DoorActionHandler.handleAutowalkDoor(npc, node.asScenery()).also { targetLocation = Location(2832, 10082, 0) } + // Delay 2 ticks for DoorActionHandler to finish + 4 -> forceWalk(npc, Location(2836, 10082, 0), "dumb").also { targetLocation = Location(2836, 10082, 0) } + 5 -> forceWalk(npc, Location(2836, 10061, 0), "dumb").also { targetLocation = Location(2836, 10061, 0) } + 6 -> forceWalk(npc, Location(2824, 10050, 0), "dumb").also { targetLocation = Location(2824, 10050, 0) } + 7 -> { + // Out of sight death to trigger respawn. + npc.teleport(Location(2823, 10035, 0)) + npc.startDeath(null) + npc.setWalks(true) + return true + } + } + } + // Keep counting, if more than 100 ticks, end this pulse as a failsafe. + if (count > 100) { + npc.startDeath(null) + npc.setWalks(true) + return true + } + return false + } + }) + + } else { + sendMessage(player, "The cell door is locked.") + } + } + onUseWith(IntType.SCENERY, Items.CELL_KEY_1_3136, Scenery.CELL_DOOR_3765) { player, used, with -> + unlockMadEadgarCellDoor(player, with) + return@onUseWith true + } + on(Scenery.CELL_DOOR_3765, IntType.SCENERY, "unlock"){ player, node -> + unlockMadEadgarCellDoor(player, node) + return@on true + } + + + // Key to unlock Godric's cell + fun unlockGodricCellDoor(player: Player, node: Node) { + if (inInventory(player, Items.CELL_KEY_2_3137)){ + sendMessage(player, "You unlock the cell door.") + 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)!! + sendNPCDialogue(player, NPCs.GODRIC_1114, "Thank you, my friend.") + submitWorldPulse(object : Pulse(0) { + // This is basically chaining a series of walks + and door handler. + var count = 0 + var stage = 0 + var trigger = true + var targetLocation = Location(0, 0, 0) + override fun pulse(): Boolean { + if (npc.location.equals(targetLocation)){ + stage++ + trigger = true + } + if (trigger) { + trigger = false + when (stage) { + 0 -> forceWalk(npc, Location(2831, 10078, 0), "dumb").also { + npc.walkRadius = 11 + npc.setWalks(false) + targetLocation = Location(2831, 10078, 0) + } + 1 -> DoorActionHandler.handleAutowalkDoor(npc, node.asScenery()).also { targetLocation = Location(2832, 10078, 0) } + // Delay 2 ticks for DoorActionHandler to finish + 4 -> forceWalk(npc, Location(2836, 10078, 0), "dumb").also { targetLocation = Location(2836, 10078, 0) } + 5 -> forceWalk(npc, Location(2836, 10061, 0), "dumb").also { targetLocation = Location(2836, 10061, 0) } + 6 -> forceWalk(npc, Location(2824, 10050, 0), "dumb").also { targetLocation = Location(2824, 10050, 0) } + 7 -> { + // Out of sight death to trigger respawn. + npc.teleport(Location(2823, 10035, 0)) + npc.startDeath(null) + npc.setWalks(true) + return true + } + } + } + // Keep counting, if more than 100 ticks, end this pulse as a failsafe. + count++ + if (count > 100) { + npc.startDeath(null) + npc.setWalks(true) + return true + } + return false + } + }) + + } else { + sendMessage(player, "The cell door is locked.") + } + } + onUseWith(IntType.SCENERY, Items.CELL_KEY_2_3137, Scenery.CELL_DOOR_3767) { player, used, with -> + unlockGodricCellDoor(player, with) + return@onUseWith true + } + on(Scenery.CELL_DOOR_3767, IntType.SCENERY, "unlock"){ player, node -> + unlockGodricCellDoor(player, node) + return@on true; + } + + // Exit + on(Scenery.EXIT_3761, IntType.SCENERY, "open"){ player, _ -> + player.properties.teleportLocation = Location.create(2827, 3646, 0) + return@on true + } + + // Reentry Secret Door + on(Scenery.SECRET_DOOR_3762, IntType.SCENERY, "open"){ player, _ -> + if (getQuestStage(player, Quests.TROLL_STRONGHOLD) >= 8) { + player.properties.teleportLocation = Location.create(2824, 10050, 0) + } else { + sendMessage(player, "The door is locked.") + } + return@on true + } + } + + /** + * Method used to determine the success of a player when thieving. + * Copied from ThievingGuidePlugin + * @param player the player. + * @return `True` if successful, `False` if not. + */ + fun success(player: Player, skill: Int): Boolean { + val level = player.getSkills().getLevel(skill).toDouble() + val req = 30.0 + val successChance = ceil((level * 50 - req) / req / 3 * 4) + val roll = RandomFunction.random(99) + return successChance >= roll + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..deb316aeb --- /dev/null +++ b/Server/src/main/content/region/asgarnia/burthorpe/quest/trollstronghold/TwigNpc.kt @@ -0,0 +1,52 @@ +package content.region.asgarnia.burthorpe.quest.trollstronghold + +import content.data.Quests +import core.api.isQuestInProgress +import core.api.produceGroundItem +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 org.rs09.consts.Items +import org.rs09.consts.NPCs + +@Initializable +class TwigNpc(id: Int = 0, location: Location? = null) : AbstractNPC(id, location) { + override fun construct(id: Int, location: Location, vararg objects: Any): AbstractNPC { + return TwigNpc(id, location) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.TWIG_1126, NPCs.TWIG_1128) + } + + // Transform from sleeping to awake. + override fun isAttackable(entity: Entity, style: CombatStyle, message: Boolean): Boolean { + val attackable = super.isAttackable(entity, style, message) + if (this.id == NPCs.TWIG_1128) { + val prevLifePoints = this.skills.lifepoints + this.transform(NPCs.TWIG_1126) + this.skills.lifepoints = prevLifePoints + } + return attackable + } + + override fun handleTickActions() { + super.handleTickActions() + // When returning to spawn, transform back to sleeping and keep lifepoints. + if (getAttribute("return-to-spawn", false) && this.id == NPCs.TWIG_1126) { + val prevLifePoints = this.skills.lifepoints + this.transform(NPCs.TWIG_1128) + this.skills.lifepoints = prevLifePoints + } + } + + override fun finalizeDeath(killer: Entity?) { + if (isQuestInProgress(killer as Player, Quests.TROLL_STRONGHOLD, 8, 10)) { + produceGroundItem(killer, Items.CELL_KEY_1_3136, 1, this.location) + } + super.finalizeDeath(killer) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/asgarnia/dialogue/CapelessMasterCrafterDialogue.kt b/Server/src/main/content/region/asgarnia/dialogue/CapelessMasterCrafterDialogue.kt new file mode 100644 index 000000000..135dc1cb4 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/dialogue/CapelessMasterCrafterDialogue.kt @@ -0,0 +1,29 @@ +package content.region.asgarnia.dialogue + +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.node.entity.player.Player +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import org.rs09.consts.NPCs + +@Initializable +class CapelessMasterCrafterDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun newInstance(player: Player): DialoguePlugin { + return CapelessMasterCrafterDialogue(player) + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + npcl( + FacialExpression.FRIENDLY, + "Hello, and welcome to the Crafting Guild. Accomplished crafters from all " + + "over the land come here to use our top notch workshops." + ).also { stage = END_DIALOGUE } + return true + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.MASTER_CRAFTER_2732) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/asgarnia/dialogue/MasterCrafterDialogue.kt b/Server/src/main/content/region/asgarnia/dialogue/MasterCrafterDialogue.kt index c74769cb8..054dbf209 100644 --- a/Server/src/main/content/region/asgarnia/dialogue/MasterCrafterDialogue.kt +++ b/Server/src/main/content/region/asgarnia/dialogue/MasterCrafterDialogue.kt @@ -3,10 +3,9 @@ package content.region.asgarnia.dialogue import core.api.* import core.game.dialogue.DialoguePlugin import core.game.dialogue.FacialExpression -import core.game.node.entity.npc.NPC +import core.game.global.Skillcape 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.Items import org.rs09.consts.NPCs @@ -17,52 +16,96 @@ import core.tools.END_DIALOGUE */ @Initializable -class MasterCrafterDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { +class MasterCrafterDialogue(player: Player? = null) : DialoguePlugin(player) { - var CAPE = Items.CRAFTING_CAPE_9780 - var COIN = Items.COINS_995 - - override fun newInstance(player: Player) : core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player): DialoguePlugin { return MasterCrafterDialogue(player) } - override fun open(vararg args: Any?) : Boolean { - npc = args[0] as NPC - stage = 0 - return true - } + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage) { + 0 -> npcl( + FacialExpression.FRIENDLY, + "Hello, and welcome to the Crafting Guild. Accomplished crafters from all " + + "over the land come here to use our top notch workshops." + ).also { stage++ } - override fun handle(interfaceId: Int, buttonId: Int) : Boolean { - when(stage) { - 0 -> if(hasLevelStat(player, Skills.CRAFTING, 99)) { - player(core.game.dialogue.FacialExpression.ASKING, "Hey, could I buy a Skillcape of Crafting?").also{ stage = 10 } - } else { - player(core.game.dialogue.FacialExpression.ASKING,"Hey, what is that cape you're wearing? I don't recognize it.").also { stage++ } + 1 -> { + if (Skillcape.isMaster(player, Skills.CRAFTING)) { + playerl( + FacialExpression.ASKING, + "Are you the person I need to talk to about buying a Skillcape of Crafting?" + ).also { stage = 100 } + } else { + end() + } } - 1 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "This? This is a Skillcape of Crafting. It is a symbol of my ability " + - "and standing here in the Crafting Guild. If you should ever achieve level 99 Crafting come and talk to me " + - "and we'll see if we can sort you out with one.").also{ stage = END_DIALOGUE } - 10 -> npcl(core.game.dialogue.FacialExpression.HAPPY, "Certainly! Right after you pay me 99000 coins.").also{ stage++ } - 11 -> options("Okay, here you go.", "No thanks.").also{ stage++ } - 12 -> when(buttonId) { - 1 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "Okay, here you go.").also{ stage++ } - 2 -> player(core.game.dialogue.FacialExpression.HALF_THINKING, "No, thanks.").also{ stage = END_DIALOGUE } + + 100 -> npcl( + FacialExpression.FRIENDLY, + "I certainly am, and I can see that you are definitely talented enough to own one! " + + "Unfortunately, being such a prestigious item, they are appropriately expensive. " + + "I'm afraid I must ask you for 99000 gold." + ).also { stage++ } + + 101 -> options( + "99000 gold! Are you mad?", + "That's fine." + ).also { stage++ } + + 102 -> { + when (buttonId) { + 1 -> playerl( + FacialExpression.ASKING, + "99000 gold! Are you mad?" + ).also { stage++ } + + 2 -> playerl( + FacialExpression.FRIENDLY, + "That's fine." + ).also { stage = 110 } + } } - 13 -> if(inInventory(player, COIN, 99000)) { - removeItem(player, Item(COIN, 99000), Container.INVENTORY) - addItem(player, CAPE, 1) - npcl(core.game.dialogue.FacialExpression.HAPPY, "There you go! Enjoy.").also{ stage = END_DIALOGUE } - } else { - npcl(core.game.dialogue.FacialExpression.NEUTRAL, "You don't have enough coins for a cape.").also{ stage = END_DIALOGUE } + + 103 -> npcl( + FacialExpression.FRIENDLY, + "Not at all; there are many other adventurers who would love the opportunity to purchase " + + "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 } + } + } } - 20 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Where's your brown apron? You can't come in here unless you're wearing one.").also{ stage++ } - 21 -> player(core.game.dialogue.FacialExpression.HALF_GUILTY, "Err... I haven't got one.").also{ stage = END_DIALOGUE } + + 111 -> npcl( + FacialExpression.FRIENDLY, + "Well, come back and see me when you do." + ).also { stage = END_DIALOGUE } } return true } - override fun getIds() : IntArray { + override fun getIds(): IntArray { return intArrayOf(NPCs.MASTER_CRAFTER_805) } - } \ No newline at end of file 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 d48680f3f..b4d949075 100644 --- a/Server/src/main/content/region/asgarnia/dialogue/TannerDialogue.kt +++ b/Server/src/main/content/region/asgarnia/dialogue/TannerDialogue.kt @@ -1,52 +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) : core.game.dialogue.DialoguePlugin(player) { +class TannerDialogue(player: Player? = null) : DialoguePlugin(player) { - override fun newInstance(player: Player) : core.game.dialogue.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(core.game.dialogue.FacialExpression.NEUTRAL, "Greetings friend. I am a manufacturer of leather.") + 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(core.game.dialogue.FacialExpression.ASKING,"Can I buy some leather then?").also{ stage = 10 } - 2 -> player(core.game.dialogue.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(core.game.dialogue.FacialExpression.FRIENDLY, "Certainly!").also { stage = END_DIALOGUE }.also{ npc.openShop(player) } + 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 } + } - 20 -> npcl(core.game.dialogue.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(core.game.dialogue.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(core.game.dialogue.FacialExpression.FRIENDLY, "I can also tan snake hides and dragonhides, suitable for crafting" + - "into the highest quality armour for rangers.").also{ stage++ } - 23 -> player(core.game.dialogue.FacialExpression.NEUTRAL, "Thanks, I'll bear it in mind.").also { stage = END_DIALOGUE } + 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/TheDoorDialogues.kt b/Server/src/main/content/region/asgarnia/dialogue/TheDoorDialogues.kt index a540c4aac..fd7f6bae0 100644 --- a/Server/src/main/content/region/asgarnia/dialogue/TheDoorDialogues.kt +++ b/Server/src/main/content/region/asgarnia/dialogue/TheDoorDialogues.kt @@ -15,18 +15,15 @@ class TheDoorDialogues(val it: Int) : DialogueFile() { npc = NPC(NPCs.MASTER_CRAFTER_805) when(it) { 0 -> when(stage) { - 0 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Welcome to the Guild of Master craftsmen.").also{ stage = END_DIALOGUE } + 0 -> npcl(FacialExpression.FRIENDLY, "Welcome to the Guild of Master Craftsmen.").also{ stage = END_DIALOGUE } } 1 -> when(stage) { - 0 -> npcl(core.game.dialogue.FacialExpression.ASKING, "Where's your brown apron? You can't come in here unless you're wearing one.").also{ stage++ } - 1 -> player(core.game.dialogue.FacialExpression.SAD, "Err... I haven't got one.").also { stage = END_DIALOGUE } + 0 -> npcl(FacialExpression.ASKING, "Where's your brown apron? You can't come in here unless you're wearing one.").also{ stage++ } + 1 -> player(FacialExpression.SAD, "Err... I haven't got one.").also { stage = END_DIALOGUE } } 2 -> when(stage) { - 0 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Sorry, only experienced crafters are allowed in here. You must be level 40 or above to enter.").also{ stage = END_DIALOGUE } + 0 -> npcl(FacialExpression.NEUTRAL, "Sorry, only experienced crafters are allowed in here. You must be level 40 or above to enter.").also{ stage = END_DIALOGUE } } } - } - - } \ No newline at end of file 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/dialogue/YoungMasterCrafterDialogue.kt b/Server/src/main/content/region/asgarnia/dialogue/YoungMasterCrafterDialogue.kt new file mode 100644 index 000000000..84ae78c11 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/dialogue/YoungMasterCrafterDialogue.kt @@ -0,0 +1,60 @@ +package content.region.asgarnia.dialogue + +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.node.entity.player.Player +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import org.rs09.consts.NPCs + +@Initializable +class YoungMasterCrafterDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun newInstance(player: Player): DialoguePlugin { + return YoungMasterCrafterDialogue(player) + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage) { + 0 -> npcl( + FacialExpression.HALF_ASKING, + "Yeah?" + ).also { stage++ } + + 1 -> playerl( + FacialExpression.FRIENDLY, + "Hello." + ).also { stage++ } + + 2 -> npcl( + FacialExpression.HALF_ASKING, + "Whassup?" + ).also { stage++ } + + 3 -> playerl( + FacialExpression.NEUTRAL, + "So... are you here to give crafting tips?" + ).also { stage++ } + + 4 -> npcl( + FacialExpression.ANNOYED, + "Dude, do I look like I wanna talk to you?" + ).also { stage++ } + + 5 -> playerl( + FacialExpression.NEUTRAL, + "I suppose not." + ).also { stage++ } + + 6 -> npcl( + FacialExpression.NEUTRAL, + "Right on!" + ).also { stage = END_DIALOGUE } + } + return true + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.MASTER_CRAFTER_2733) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/asgarnia/dialogue/ZandarHorfyreDialogue.kt b/Server/src/main/content/region/asgarnia/dialogue/ZandarHorfyreDialogue.kt index d52161633..191f7237e 100644 --- a/Server/src/main/content/region/asgarnia/dialogue/ZandarHorfyreDialogue.kt +++ b/Server/src/main/content/region/asgarnia/dialogue/ZandarHorfyreDialogue.kt @@ -14,34 +14,34 @@ import core.tools.END_DIALOGUE * @author bushtail */ @Initializable -class ZandarHorfyreDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { +class ZandarHorfyreDialogue(player: Player? = null) : DialoguePlugin(player) { - override fun newInstance(player: Player) : core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player) : DialoguePlugin { return ZandarHorfyreDialogue(player) } override fun open(vararg args: Any?) : Boolean { npc = args[0] as NPC - player(core.game.dialogue.FacialExpression.HALF_THINKING,"Who are you?") + player(FacialExpression.HALF_THINKING,"Who are you?") stage = -1 return true } override fun handle(interfaceId: Int, buttonId: Int) : Boolean { when (stage) { - -1 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL,"My name is Zandar Horfyre, and you ${ player.name } are trespassing in my tower, not to mention attacking my students! I thank you to leave immediately!").also{ stage++ } + -1 -> npcl(FacialExpression.NEUTRAL,"My name is Zandar Horfyre, and you, ${ player.username }, are trespassing in my tower, not to mention attacking my students! I thank you to leave immediately!").also{ stage++ } 0 -> options("Ok, I was going anyway.", "No, I think I'll stay for a bit.").also{ stage++ } 1 -> when(buttonId) { 1 -> player("Ok, I was going anyway.").also{ stage = 10 } 2 -> player("No, I think I'll stay for a bit.").also{ stage = 20 } } - 10 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL,"Good! And don't forget to close the door behind you!").also{ stage++ } + 10 -> npcl(FacialExpression.NEUTRAL,"Good! And don't forget to close the door behind you!").also{ stage++ } 11 -> stage = END_DIALOGUE - 20 -> npcl(core.game.dialogue.FacialExpression.ANNOYED,"Actually, that wasn't an invitation. I've tried being polite, now we'll do it the hard way!").also{ teleport(player, Location.create(3217, 3177, 0), TeleportManager.TeleportType.INSTANT) }.also{ stage++ } - 21 -> player(core.game.dialogue.FacialExpression.ANGRY, "Zamorak curse that mage!").also{ stage++ } - 22 -> player(core.game.dialogue.FacialExpression.LAUGH, "Actually, I guess he already has!").also{ stage++ } + 20 -> npcl(FacialExpression.ANNOYED,"Actually, that wasn't an invitation. I've tried being polite, now we'll do it the hard way!").also{ teleport(player, Location.create(3217, 3177, 0), TeleportManager.TeleportType.INSTANT) }.also{ stage++ } + 21 -> player(FacialExpression.ANGRY, "Zamorak curse that mage!").also{ stage++ } + 22 -> player(FacialExpression.LAUGH, "Actually, I guess he already has!").also{ stage++ } 23 -> end() } return true @@ -50,4 +50,4 @@ class ZandarHorfyreDialogue(player: Player? = null) : core.game.dialogue.Dialogu override fun getIds() : IntArray { return intArrayOf(3308) } -} \ No newline at end of file +} diff --git a/Server/src/main/content/region/asgarnia/dwarfmine/dialogue/DrogoDialogue.kt b/Server/src/main/content/region/asgarnia/dwarfmine/dialogue/DrogoDialogue.kt index b51c91575..e5c2343c3 100644 --- a/Server/src/main/content/region/asgarnia/dwarfmine/dialogue/DrogoDialogue.kt +++ b/Server/src/main/content/region/asgarnia/dwarfmine/dialogue/DrogoDialogue.kt @@ -12,11 +12,11 @@ import org.rs09.consts.NPCs */ @Initializable -class DrogoDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class DrogoDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - npc(core.game.dialogue.FacialExpression.OLD_DEFAULT,"'Ello. Welcome to my Mining shop, friend.").also { stage = 0 } + npc(FacialExpression.OLD_DEFAULT,"'Ello. Welcome to my Mining shop, friend.").also { stage = 0 } return true } @@ -24,21 +24,21 @@ class DrogoDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin( when(stage){ 0 -> options("Do you want to trade?", "Hello, shorty.", "Why don't you ever restock ores and bars?").also { stage++ } 1 -> when (buttonId) { - 1 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "Do you want to trade?").also { stage = 10 } - 2 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "Hello, shorty.").also { stage = 20 } - 3 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "Why don't you ever restock ores and bars?").also { stage = 30 } + 1 -> player(FacialExpression.FRIENDLY, "Do you want to trade?").also { stage = 10 } + 2 -> player(FacialExpression.FRIENDLY, "Hello, shorty.").also { stage = 20 } + 3 -> player(FacialExpression.FRIENDLY, "Why don't you ever restock ores and bars?").also { stage = 30 } } 10 -> end().also { npc.openShop(player) } - 20 -> npc(core.game.dialogue.FacialExpression.OLD_ANGRY1,"I may be short, but at least I've got manners.").also { stage = 99 } - 30 -> npc(core.game.dialogue.FacialExpression.OLD_DEFAULT,"The only ores and bars I sell are those sold to me.").also { stage = 99 } + 20 -> npc(FacialExpression.OLD_ANGRY1,"I may be short, but at least I've got manners.").also { stage = 99 } + 30 -> npc(FacialExpression.OLD_DEFAULT,"The only ores and bars I sell are those sold to me.").also { stage = 99 } 99 -> end() } return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return DrogoDialogue(player) } diff --git a/Server/src/main/content/region/asgarnia/dwarfmine/dialogue/DwarfShopDialogue.java b/Server/src/main/content/region/asgarnia/dwarfmine/dialogue/DwarfShopDialogue.java index 780694907..dbcbf2c69 100644 --- a/Server/src/main/content/region/asgarnia/dwarfmine/dialogue/DwarfShopDialogue.java +++ b/Server/src/main/content/region/asgarnia/dwarfmine/dialogue/DwarfShopDialogue.java @@ -7,7 +7,7 @@ import core.plugin.Initializable; import core.game.node.entity.player.Player; /** - * Represents the dialouge plugin used for the dward shop. + * Represents the dialouge plugin used for the dwarf shop. * @author 'Vexia * @version 1.0 */ @@ -39,7 +39,7 @@ public final class DwarfShopDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Can I help you at all?"); + interpreter.sendDialogues(npc, FacialExpression.OLD_HAPPY, "Can I help you at all?"); stage = 0; return true; } diff --git a/Server/src/main/content/region/asgarnia/dwarfmine/dialogue/DwarvenMineGuardDialogue.kt b/Server/src/main/content/region/asgarnia/dwarfmine/dialogue/DwarvenMineGuardDialogue.kt index e5822b72c..e5d7c85a3 100644 --- a/Server/src/main/content/region/asgarnia/dwarfmine/dialogue/DwarvenMineGuardDialogue.kt +++ b/Server/src/main/content/region/asgarnia/dwarfmine/dialogue/DwarvenMineGuardDialogue.kt @@ -12,29 +12,29 @@ import org.rs09.consts.NPCs */ @Initializable -class DwarvenMineGuardDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class DwarvenMineGuardDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - player(core.game.dialogue.FacialExpression.FRIENDLY,"Hello.").also { stage = 0 } + player(FacialExpression.FRIENDLY,"Hello.").also { stage = 0 } return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 0 -> npcl(core.game.dialogue.FacialExpression.OLD_ANGRY1, "Don't distract me while I'm on duty! This mine has to be protected!").also { stage++ } - 1 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "What's going to attack a mine?").also { stage++ } - 2 -> npcl(core.game.dialogue.FacialExpression.OLD_ANGRY1, "Goblins! They wander everywhere, attacking anyone they think is small enough to be an easy victim. We need more cannons to fight them off properly.").also { stage++ } - 3 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "Well, I've done my bit to help with that.").also { stage++ } - 4 -> npcl(core.game.dialogue.FacialExpression.OLD_ANGRY1, "Yes, I heard. Now please let me get on with my guard duties.").also { stage++ } - 5 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "Alright, I'll leave you alone now.").also { stage = 99 } + 0 -> npcl(FacialExpression.OLD_ANGRY1, "Don't distract me while I'm on duty! This mine has to be protected!").also { stage++ } + 1 -> player(FacialExpression.FRIENDLY, "What's going to attack a mine?").also { stage++ } + 2 -> npcl(FacialExpression.OLD_ANGRY1, "Goblins! They wander everywhere, attacking anyone they think is small enough to be an easy victim. We need more cannons to fight them off properly.").also { stage++ } + 3 -> player(FacialExpression.FRIENDLY, "Well, I've done my bit to help with that.").also { stage++ } + 4 -> npcl(FacialExpression.OLD_ANGRY1, "Yes, I heard. Now please let me get on with my guard duties.").also { stage++ } + 5 -> player(FacialExpression.FRIENDLY, "Alright, I'll leave you alone now.").also { stage = 99 } 99 -> end() } return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return DwarvenMineGuardDialogue(player) } diff --git a/Server/src/main/content/region/asgarnia/dwarfmine/dialogue/NurmofDialogue.kt b/Server/src/main/content/region/asgarnia/dwarfmine/dialogue/NurmofDialogue.kt index de5eb0f6c..366bbfaee 100644 --- a/Server/src/main/content/region/asgarnia/dwarfmine/dialogue/NurmofDialogue.kt +++ b/Server/src/main/content/region/asgarnia/dwarfmine/dialogue/NurmofDialogue.kt @@ -1,5 +1,7 @@ package content.region.asgarnia.dwarfmine.dialogue +import core.api.getAttribute +import core.api.setAttribute import core.game.dialogue.DialoguePlugin import core.game.dialogue.FacialExpression import core.game.node.entity.npc.NPC @@ -12,11 +14,11 @@ import org.rs09.consts.NPCs */ @Initializable -class NurmofDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class NurmofDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - npcl(core.game.dialogue.FacialExpression.OLD_NORMAL,"Greetings and welcome to my pickaxe shop. Do you want to buy my premium quality pickaxes?").also { stage = 0 } + npcl(FacialExpression.OLD_NORMAL,"Greetings and welcome to my pickaxe shop. Do you want to buy my premium quality pickaxes?").also { stage = 0 } return true } @@ -26,18 +28,28 @@ class NurmofDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin 1 -> when (buttonId) { 1 -> end().also { npc.openShop(player) } - 2 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "No thank you.").also { stage = 99 } - 3 -> player(core.game.dialogue.FacialExpression.HALF_ASKING, "Are your pickaxes better than other pickaxes, then?").also { stage = 10 } + 2 -> player(FacialExpression.FRIENDLY, "No thank you.").also { stage = 99 } + 3 -> player(FacialExpression.HALF_ASKING, "Are your pickaxes better than other pickaxes, then?").also { stage = 10 } } - 10 -> npcl(core.game.dialogue.FacialExpression.OLD_NORMAL,"Of course they are! My pickaxes are made of higher grade metal than your ordinary bronze pickaxes, allowing you to mine ore just that little bit faster.").also { stage = 99 } - - 99 -> end() + 10 -> npcl(FacialExpression.OLD_NORMAL,"Of course they are! My pickaxes are made of higher grade metal than your ordinary bronze pickaxes, allowing you to mine ore just that little bit faster.").also { + if(!getAttribute(player, "pre-dq:said-hi", true)) { + stage++ + } else { + stage = 99 + } + } + 11 -> playerl(FacialExpression.FRIENDLY, "By the way, Doric says hello!").also { stage++ } + 12 -> npcl(FacialExpression.OLD_HAPPY, "Oh! Thank you for telling me, adventurer!").also { stage = 99 } + 99 -> { + setAttribute(player, "pre-dq:said-hi", true) + end() + } } return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return NurmofDialogue(player) } diff --git a/Server/src/main/content/region/asgarnia/falador/dialogue/AmbassadorSpanfippleDialogue.kt b/Server/src/main/content/region/asgarnia/falador/dialogue/AmbassadorSpanfippleDialogue.kt index 93a574e0c..3d94098c1 100644 --- a/Server/src/main/content/region/asgarnia/falador/dialogue/AmbassadorSpanfippleDialogue.kt +++ b/Server/src/main/content/region/asgarnia/falador/dialogue/AmbassadorSpanfippleDialogue.kt @@ -13,38 +13,38 @@ import org.rs09.consts.NPCs */ @Initializable -class AmbassadorSpanfippleDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class AmbassadorSpanfippleDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - npc(core.game.dialogue.FacialExpression.OLD_DEFAULT,"It's all very white round here, isn't it?").also { stage = 0 } + npc(FacialExpression.OLD_DEFAULT,"It's all very white round here, isn't it?").also { stage = 0 } return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ 0 -> { - player(core.game.dialogue.FacialExpression.THINKING, "Well, it is the White Knights' Castle.").also { stage++ } + player(FacialExpression.THINKING, "Well, it is the White Knights' Castle.").also { stage++ } } 1 -> { - npcl(core.game.dialogue.FacialExpression.OLD_DEFAULT, "I think it would all look better in pink. At least then I wouldn't be squinting all the time.").also { stage++ } + npcl(FacialExpression.OLD_DEFAULT, "I think it would all look better in pink. At least then I wouldn't be squinting all the time.").also { stage++ } } 2 -> { - playerl(core.game.dialogue.FacialExpression.FRIENDLY, "Yes, but then they'd have to become the Pink Knights. I think they'd have problems recruiting then.").also { stage++ } + playerl(FacialExpression.FRIENDLY, "Yes, but then they'd have to become the Pink Knights. I think they'd have problems recruiting then.").also { stage++ } } 3 -> { - npc(core.game.dialogue.FacialExpression.OLD_DEFAULT, "You're probably right. Maybe brown, then.").also { stage++ } + npc(FacialExpression.OLD_DEFAULT, "You're probably right. Maybe brown, then.").also { stage++ } } 4 -> { - player(core.game.dialogue.FacialExpression.HALF_THINKING, "I think that may be worse...").also { stage++ } + player(FacialExpression.HALF_THINKING, "I think that may be worse...").also { stage++ } } 5 -> { - npc(core.game.dialogue.FacialExpression.OLD_ANGRY1, "Bah, humans have no sense of style...").also { stage = 99 } + npc(FacialExpression.OLD_ANGRY1, "Bah, humans have no sense of style...").also { stage = 99 } } 99 -> end() @@ -52,7 +52,7 @@ class AmbassadorSpanfippleDialogue(player: Player? = null) : core.game.dialogue. return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return AmbassadorSpanfippleDialogue(player) } diff --git a/Server/src/main/content/region/asgarnia/falador/dialogue/ApprenticeWorkmanDialogue.kt b/Server/src/main/content/region/asgarnia/falador/dialogue/ApprenticeWorkmanDialogue.kt index 356bf0600..b3aa28213 100644 --- a/Server/src/main/content/region/asgarnia/falador/dialogue/ApprenticeWorkmanDialogue.kt +++ b/Server/src/main/content/region/asgarnia/falador/dialogue/ApprenticeWorkmanDialogue.kt @@ -2,59 +2,36 @@ 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.game.node.entity.player.Player import core.plugin.Initializable +import core.tools.END_DIALOGUE +import core.tools.START_DIALOGUE import org.rs09.consts.NPCs /** * @author qmqz + * https://youtu.be/DwQgAQqaXos */ @Initializable -class ApprenticeWorkmanDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ - - override fun open(vararg args: Any?): Boolean { - npc = args[0] as NPC - player(core.game.dialogue.FacialExpression.FRIENDLY,"Hiya.").also { stage = 0 } - return true - } +class ApprenticeWorkmanDialogue(player: Player? = null) : DialoguePlugin(player) { override fun handle(interfaceId: Int, buttonId: Int): Boolean { - when(stage){ - 0 -> { - npc(core.game.dialogue.FacialExpression.FRIENDLY, "Sorry, I haven't got time to chat.", - "We've only just finished a collossal order of furniture", - "for the Varrock area, and already there's more work", - "coming in.").also { stage++ } - } - - 1 -> { - player(core.game.dialogue.FacialExpression.ASKING, "Varrock?").also { stage++ } - } - - 2 -> { - npc(core.game.dialogue.FacialExpression.FRIENDLY, "Yeah, the Council's had it redecorated.").also { stage++ } - } - - 3 -> { - npc(3236, "Oi - stop gabbing and get that chair finished!").also { stage++ } - } - - 4 -> { - npc(core.game.dialogue.FacialExpression.FRIENDLY, "You'd better let me get on with my work.").also { stage++ } - } - - 5 -> { - player(core.game.dialogue.FacialExpression.FRIENDLY, "Ok, bye.").also { stage = 99 } - } - - 99 -> end() + when (stage) { + START_DIALOGUE -> player(FacialExpression.FRIENDLY, "Hiya.").also { stage++ } + 1 -> npc(FacialExpression.SAD, "Sorry, I haven't got time to chat. We've only just", + "finished a collossal order of furniture for the Varrock", + "area, and already there's more work coming in.").also { stage++ } + 2 -> player(FacialExpression.ASKING, "Varrock?").also { stage++ } + 3 -> npc(FacialExpression.ROLLING_EYES, "Yeah, the Council's had it redecorated.").also { stage++ } + 4 -> npc(3236, "Oi - stop gabbing and get that chair finished!").also { stage++ } // ANNOYED + 5 -> npc(FacialExpression.SAD, "You'd better let me get on with my work.").also { stage++ } + 6 -> player(FacialExpression.NEUTRAL, "Ok, bye.").also { stage = END_DIALOGUE } } return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return ApprenticeWorkmanDialogue(player) } diff --git a/Server/src/main/content/region/asgarnia/falador/dialogue/CassieDialogue.java b/Server/src/main/content/region/asgarnia/falador/dialogue/CassieDialogue.java index 45178296b..d509b21a2 100644 --- a/Server/src/main/content/region/asgarnia/falador/dialogue/CassieDialogue.java +++ b/Server/src/main/content/region/asgarnia/falador/dialogue/CassieDialogue.java @@ -39,7 +39,7 @@ public final class CassieDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "I buy and sell shields; do you want to trade?"); + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "I buy and sell shields; do you want to trade?"); stage = 0; return true; } @@ -58,7 +58,7 @@ public final class CassieDialogue extends DialoguePlugin { npc.openShop(player); break; case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No, thank you."); + interpreter.sendDialogues(player, FacialExpression.HAPPY, "No, thank you."); stage = 20; break; } diff --git a/Server/src/main/content/region/asgarnia/falador/dialogue/ChemistDialogue.java b/Server/src/main/content/region/asgarnia/falador/dialogue/ChemistDialogue.java index b20906efd..8c8255719 100644 --- a/Server/src/main/content/region/asgarnia/falador/dialogue/ChemistDialogue.java +++ b/Server/src/main/content/region/asgarnia/falador/dialogue/ChemistDialogue.java @@ -290,7 +290,7 @@ public final class ChemistDialogue extends DialoguePlugin { break; case 110: - npc("Ah, well it's a diary that helps you keep track of", "particular achievements you've made here on", "2009Scape."); + npc("Ah, well, it's a diary that helps you keep track of", "particular achievements you've made here on Gielinor."); stage = 111; break; case 111: diff --git a/Server/src/main/content/region/asgarnia/falador/dialogue/DoricDialogue.java b/Server/src/main/content/region/asgarnia/falador/dialogue/DoricDialogue.java deleted file mode 100644 index 462a1c2e7..000000000 --- a/Server/src/main/content/region/asgarnia/falador/dialogue/DoricDialogue.java +++ /dev/null @@ -1,261 +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.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 core.plugin.Initializable; -import core.game.world.GameWorld; - -/** - * Represents the dialogue plugin used for the doric npc. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class DoricDialogue extends DialoguePlugin { - - /** - * Represents the pickaxe item. - */ - private static final Item PICKAXE = new Item(1265, 1); - - /** - * Represents the requirement items. - */ - private static final Item[] REQUIREMENTS = new Item[] { new Item(436, 4), new Item(434, 6), new Item(440, 2) }; - - /** - * Constructs a new {@code DoricDialogue} {@code Object}. - */ - public DoricDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code DoricDialogue} {@code Object}. - * @param player the player. - */ - public DoricDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new DoricDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - final Quest quest = player.getQuestRepository().getQuest("Doric's Quest"); - if (!quest.isStarted(player)) { - interpreter.sendDialogues(npc, FacialExpression.OLD_NORMAL, "Hello traveller, what brings you to my humble smithy?"); - stage = 0; - } - if (quest.isStarted(player)) { - interpreter.sendDialogues(npc, FacialExpression.OLD_NORMAL, "Have you got my materials yet, traveller?"); - stage = 100; - } - if (quest.isCompleted(player)) { - interpreter.sendDialogues(npc, FacialExpression.OLD_NORMAL, "Hello traveller, how is your metalworking coming along?"); - stage = 500; - } - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendOptions("Select an Option", "I wanted to use your anvils.", "I want to use your whetstone.", "Mind your own business, shortstuff!", "I was just checking out the landscape.", "What do you make here?"); - stage = 1; - break; - case 1: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.OLD_NORMAL, "I wanted to use your anvils."); - stage = 10; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.OLD_NORMAL, "I want to use your whetsone."); - stage = 20; - break; - case 3: - interpreter.sendDialogues(player, FacialExpression.OLD_NORMAL, "Mind your own business, shortstuff!"); - stage = 30; - break; - case 4: - interpreter.sendDialogues(player, FacialExpression.OLD_NORMAL, "I was just checking out the landscape."); - stage = 40; - break; - case 5: - interpreter.sendDialogues(player, FacialExpression.OLD_NORMAL, "What do you make here?"); - stage = 50; - break; - - } - break; - case 10: - interpreter.sendDialogues(npc, FacialExpression.OLD_NORMAL, "My anvils get enough work with my own use. I make", "pickaxes, and it takes a lot of hard work. If you could", "get me some more materials, then I could let you use", "them."); - stage = 11; - break; - case 11: - interpreter.sendOptions("Select an Option", "Yes, I will get you the materials.", "No, hitting rocks if for the boring people, sorry."); - stage = 12; - break; - case 12: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.OLD_NORMAL, "Yes I will get you the materials."); - stage = 15; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.OLD_NORMAL, "No, hitting rocks is for the boring people, sorry."); - stage = 13; - break; - } - break; - case 13: - interpreter.sendDialogues(npc, FacialExpression.OLD_NORMAL, "That is your choice. Nice to meet you anyway."); - stage = 14; - break; - case 14: - end(); - break; - case 15: - interpreter.sendDialogues(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."); - stage = 16; - break; - case 16: - player.getQuestRepository().getQuest("Doric's Quest").setStage(player, 1); - player.getQuestRepository().syncronizeTab(player); - if (!player.getInventory().add(PICKAXE)) { - GroundItemManager.create(PICKAXE, player.getLocation()); - } - interpreter.sendDialogues(player, FacialExpression.OLD_NORMAL, "Certainly, I'll be right back!"); - stage = 17; - break; - case 17: - end(); - break; - case 20: - interpreter.sendDialogues(npc, FacialExpression.OLD_NORMAL, "The whetstone is for more advanced smithing, but I", "could let you use it as well as my anvils if you could", "get me some more materials."); - stage = 11; - break; - case 30: - interpreter.sendDialogues(npc, FacialExpression.OLD_NORMAL, "How nice to meet someone with such pleasant manners.", "Do come again when you need to shout at someone", "smaller than you!"); - stage = 31; - break; - case 31: - end(); - break; - case 40: - interpreter.sendDialogues(npc, FacialExpression.OLD_NORMAL, "Hope you like it. I do enjoy the solitude of my little", "home. If you get time, please say hi to my friends in", "the Dwarven Mine."); - stage = 41; - break; - case 41: - interpreter.sendDialogues(player, FacialExpression.OLD_NORMAL, "Will do!"); - stage = 42; - break; - case 42: - end(); - break; - case 50: - interpreter.sendDialogues(npc, FacialExpression.OLD_NORMAL, "I make pickaxes. I am the best maker of pickaxes in the", "whole of " + GameWorld.getSettings().getName() + "."); - stage = 51; - break; - case 51: - interpreter.sendDialogues(player, FacialExpression.OLD_NORMAL, "Do you have any to sell?"); - stage = 52; - break; - case 52: - interpreter.sendDialogues(npc, FacialExpression.OLD_NORMAL, "Sorry, but I've got a running order with Nurmof."); - stage = 53; - break; - case 53: - interpreter.sendDialogues(player, FacialExpression.OLD_NORMAL, "Ah, fair enough."); - stage = 54; - break; - case 54: - end(); - break; - case 100: - if (player.getInventory().contains(434, 6) && player.getInventory().contains(440, 2) && player.getInventory().contains(436, 4)) { - interpreter.sendDialogues(player, FacialExpression.OLD_NORMAL, "I have everything you need."); - stage = 200; - } else { - interpreter.sendDialogues(player, FacialExpression.OLD_NORMAL, "Sorry, I don't have them all yet."); - stage = 101; - } - break; - case 101: - interpreter.sendDialogues(npc, FacialExpression.OLD_NORMAL, "Not to worry, stick at it. Remember, I need 6 clay, 4", "copper ore, and 2 iron ore."); - stage = 102; - break; - case 102: - interpreter.sendOptions("Select an Option", "Where can I find those?", "Certainly, I'll be right back."); - stage = 103; - break; - case 103: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.OLD_NORMAL, "Where can I find those?"); - stage = 110; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.OLD_NORMAL, "Certainly, I'll be right back."); - stage = 113; - break; - - } - break; - case 110: - interpreter.sendDialogues(npc, FacialExpression.OLD_NORMAL, "You'll be able to find all those ores in the rocks just", "inside the Dwarven Mine. Head east from here and", "you'll find the entrance in the side of Ice Mountain."); - stage = 111; - break; - case 111: - end(); - break; - case 113: - end(); - break; - case 200: - interpreter.sendDialogues(npc, FacialExpression.OLD_NORMAL, "Many thanks. Pass them here, please. I can spare you", "some coins for your trouble, and please use my anvils", "anytime you want."); - stage = 201; - break; - case 201: - interpreter.sendItemMessage(436, "You hand the clay, copper, and iron to Doric."); - stage = 202; - break; - case 202: - if (player.getInventory().remove(REQUIREMENTS)) { - end(); - player.getQuestRepository().getQuest("Doric's Quest").finish(player); - } - break; - case 500: - interpreter.sendDialogues(player, FacialExpression.OLD_NORMAL, "Not too bad, Doric."); - stage = 501; - break; - case 501: - interpreter.sendDialogues(npc, FacialExpression.OLD_NORMAL, "Good, the love of metal is a thing close to my heart."); - stage = 502; - break; - case 502: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 284 }; - } -} diff --git a/Server/src/main/content/region/asgarnia/falador/dialogue/DoricDialogue.kt b/Server/src/main/content/region/asgarnia/falador/dialogue/DoricDialogue.kt new file mode 100644 index 000000000..0d8e029eb --- /dev/null +++ b/Server/src/main/content/region/asgarnia/falador/dialogue/DoricDialogue.kt @@ -0,0 +1,171 @@ +package content.region.asgarnia.falador.dialogue + +import content.region.asgarnia.falador.quest.doricsquest.DoricDoricsQuestDialogue +import core.api.* +import core.game.activity.Cutscene +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.entity.skill.Skills +import core.game.world.GameWorld.settings +import core.game.world.map.Direction +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, 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) { + openDialogue(player, DoricDoricsQuestDialogue(30), npc) + } else { + npcl(FacialExpression.OLD_HAPPY, "Hello traveller, how is your metalworking coming along?").also { stage = 60 } + } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage) { + 0 -> showTopics( + Topic(FacialExpression.FRIENDLY, "I wanted to use your anvils.", 10), + Topic(FacialExpression.FRIENDLY, "I wanted to use your whetstone.", 20), + IfTopic(FacialExpression.ANNOYED, "Mind your own business, shortstuff!", 30, !getAttribute(player, "pre-dq:doric-calm", false), true), + Topic(FacialExpression.FRIENDLY, "I was just checking out the landscape.", 40), + Topic(FacialExpression.ASKING, "What do you make here?", 50) + ) + + 10 -> openDialogue(player, DoricDoricsQuestDialogue(10), npc) + + 20 -> openDialogue(player, DoricDoricsQuestDialogue(20), npc) + + 30 -> { + if(getAttribute(player, "pre-dq:doric-angy-count", 0) == 10) { + end() + PDC(player).start() + } else { + setAttribute(player, "/save:pre-dq:doric-angy-count", getAttribute(player, "pre-dq:doric-angy-count", 0) + 1) + npcl(FacialExpression.OLD_ANGRY1, "How nice to meet someone with such pleasant manners. Do come again when you need to shout at someone smaller than you!").also { stage = END_DIALOGUE } + } + } + + 40 -> { + npcl(FacialExpression.OLD_HAPPY, "Hope you like it. I do enjoy the solitude of my little home. If you get time, please say hi to my friends in the Dwarven Mine.") + setAttribute(player, "/save:pre-dq:said-hi", false) + stage = END_DIALOGUE + } + + 50 -> npcl(FacialExpression.OLD_NORMAL, "I make pickaxes. I am the best maker of pickaxes in the whole of ${settings!!.name}.").also { stage++ } + 51 -> playerl(FacialExpression.HALF_ASKING, "Do you have any to sell?").also { stage++ } + 52 -> npcl(FacialExpression.OLD_NOT_INTERESTED, "Sorry, but I've got a running order with Nurmof.").also { stage++ } + 53 -> playerl(FacialExpression.FRIENDLY, "Ah, fair enough.").also { stage = END_DIALOGUE } + + 60 -> playerl(FacialExpression.FRIENDLY, "Not too bad, Doric.").also { stage++ } + 61 -> npcl(FacialExpression.OLD_HAPPY, "Good, the love of metal is a thing close to my heart.").also { stage++ } + 62 -> { + if(getAttribute(player, "pre-dq:said-hi", false)) { + playerl(FacialExpression.FRIENDLY, "By the way, I told Nurmof you said hello.") + stage++ + } else { + end() + } + } + 63 -> { + npcl(FacialExpression.OLD_HAPPY, "Thank you traveller! You'll always be welcome in my home.") + removeAttribute(player, "pre-dq:said-hi") + rewardXP(player, Skills.MINING, 5.0) + stage = END_DIALOGUE + } + } + return true + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.DORIC_284) + } +} + +class PDC(player: Player) : Cutscene(player) { + override fun setup() { + setExit(Location(2952, 3450)) + loadRegion(11829) + addNPC(NPCs.DORIC_284, 9, 58, Direction.WEST) + } + + override fun runStage(stage: Int) { + when(stage) { + 0 -> { + fadeToBlack() + timedUpdate(6) + } + 1 -> { + teleport(player, 8, 58) + moveCamera(6, 60, 250) + rotateCamera(8, 58, 75) + player.faceLocation(getNPC(NPCs.DORIC_284)!!.location) + getNPC(NPCs.DORIC_284)!!.faceLocation(player.location) + timedUpdate(2) + } + 2 -> { + fadeFromBlack() + getNPC(NPCs.DORIC_284)!!.faceLocation(player.location) + timedUpdate(2) + } + 3 -> { + sendChat(player, "Mind your own business, shortstuff!") + timedUpdate(6) + } + 4 -> { + sendChat(getNPC(NPCs.DORIC_284)!!, "I guess your mother never taught you manners. Lucky for you, I'll do just that!") + timedUpdate(5) + } + 5 -> { + animate(getNPC(NPCs.DORIC_284)!!, 99) + timedUpdate(1) + } + 6 -> { + animate(player, 837) + timedUpdate(4) + } + 7 -> { + animate(player, 838) + timedUpdate(2) + } + 8 -> { + fadeToBlack() + timedUpdate(5) + } + 9 -> { + animate(player, 0) + timedUpdate(1) + } + 10 -> { + animate(player, 856) + fadeFromBlack() + timedUpdate(2) + } + 11 -> { + sendChat(player, "Okay, I'm sorry!") + animate(player, 856) + timedUpdate(5) + } + 12 -> { + sendChat(getNPC(NPCs.DORIC_284)!!, "That's what I thought. Watch your mouth the next time you speak to me.") + timedUpdate(3) + } + 13 -> { + end { + setAttribute(player, "/save:pre-dq:doric-calm", true) + } + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/asgarnia/falador/dialogue/DrunkenManDialogue.kt b/Server/src/main/content/region/asgarnia/falador/dialogue/DrunkenManDialogue.kt index 7f0528177..8d3372d3a 100644 --- a/Server/src/main/content/region/asgarnia/falador/dialogue/DrunkenManDialogue.kt +++ b/Server/src/main/content/region/asgarnia/falador/dialogue/DrunkenManDialogue.kt @@ -12,43 +12,43 @@ import org.rs09.consts.NPCs */ @Initializable -class DrunkenManDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class DrunkenManDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - player(core.game.dialogue.FacialExpression.FRIENDLY,"Hello.").also { stage = 0 } + player(FacialExpression.FRIENDLY,"Hello.").also { stage = 0 } return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ 0 -> { - npc(core.game.dialogue.FacialExpression.DRUNK, "... whassup?").also { stage++ } + npc(FacialExpression.DRUNK, "... whassup?").also { stage++ } } 1 -> { - player(core.game.dialogue.FacialExpression.ASKING, "Are you alright?").also { stage++ } + player(FacialExpression.ASKING, "Are you alright?").also { stage++ } } 2 -> { - npc(core.game.dialogue.FacialExpression.DRUNK, "... see... two of you... why there two of you?").also { stage++ } + npc(FacialExpression.DRUNK, "... see... two of you... why there two of you?").also { stage++ } } 3 -> { - player(core.game.dialogue.FacialExpression.FRIENDLY, "There's only one of me, friend.").also { stage++ } + player(FacialExpression.FRIENDLY, "There's only one of me, friend.").also { stage++ } } 4 -> { - npc(core.game.dialogue.FacialExpression.DRUNK, "... no, two of you... you can't count...", + npc(FacialExpression.DRUNK, "... no, two of you... you can't count...", "... maybe you drunk too much...").also { stage++ } } 5 -> { - player(core.game.dialogue.FacialExpression.FRIENDLY, "Whatever you say, friend.").also { stage++ } + player(FacialExpression.FRIENDLY, "Whatever you say, friend.").also { stage++ } } 6 -> { - npc(core.game.dialogue.FacialExpression.DRUNK, "... giant hairy cabbages...").also { stage = 99 } + npc(FacialExpression.DRUNK, "... giant hairy cabbages...").also { stage = 99 } } 99 -> end() @@ -56,7 +56,7 @@ class DrunkenManDialogue(player: Player? = null) : core.game.dialogue.DialoguePl return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return DrunkenManDialogue(player) } diff --git a/Server/src/main/content/region/asgarnia/falador/dialogue/FaladorShopKeeperDialogue.java b/Server/src/main/content/region/asgarnia/falador/dialogue/FaladorShopKeeperDialogue.java index 6a5c3f949..051c55474 100644 --- a/Server/src/main/content/region/asgarnia/falador/dialogue/FaladorShopKeeperDialogue.java +++ b/Server/src/main/content/region/asgarnia/falador/dialogue/FaladorShopKeeperDialogue.java @@ -39,7 +39,7 @@ public final class FaladorShopKeeperDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Can I help you at all?"); + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Can I help you at all?"); stage = 0; return true; } @@ -58,7 +58,7 @@ public final class FaladorShopKeeperDialogue extends DialoguePlugin { npc.openShop(player); break; case 2: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "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."); + 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 = 20; break; case 3: 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 58c610f43..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); @@ -161,7 +162,7 @@ public final class FaladorSquireDialogue extends DialoguePlugin { break; case 110: - npc("Ah, well it's a diary that helps you keep track of", "particular achievements you've made here on", "2009Scape."); + npc("Ah, well, it's a diary that helps you keep track of", "particular achievements you've made here on Gielinor."); stage = 111; break; case 111: @@ -195,7 +196,7 @@ public final class FaladorSquireDialogue extends DialoguePlugin { stage = 132; break; case 132: - npc("To claim your Falador Achievement Diary rewards,", "speak to Redbeard the Pirate in Port Sarim, Sir Vyvin's", "squire in the White Knight's Castle, or myself."); + npc("To claim your Falador Achievement Diary rewards,", "speak to Redbeard the Pirate in Port Sarim, The Chemist", "in Rimmington, or myself."); stage = 105; break; @@ -373,7 +374,7 @@ public final class FaladorSquireDialogue extends DialoguePlugin { stage = 1; break; case 1: - npc("A picture eh? Hmmm.... The only one I can think of is", "in a small portrait of Sir Vyvin's father... Sir Vyvin", "keeps it in a cupboard in is room I think."); + npc("A picture eh? Hmmm.... The only one I can think of is", "in a small portrait of Sir Vyvin's father... Sir Vyvin", "keeps it in a cupboard in his room I think."); stage = 2; break; case 2: diff --git a/Server/src/main/content/region/asgarnia/falador/dialogue/FlynnDialogue.java b/Server/src/main/content/region/asgarnia/falador/dialogue/FlynnDialogue.java index c83b0b189..eed86be01 100644 --- a/Server/src/main/content/region/asgarnia/falador/dialogue/FlynnDialogue.java +++ b/Server/src/main/content/region/asgarnia/falador/dialogue/FlynnDialogue.java @@ -39,7 +39,7 @@ public final class FlynnDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Hello. Do you want to buy or sell any maces?"); + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Hello. Do you want to buy or sell any maces?"); stage = 0; return true; } @@ -54,7 +54,7 @@ public final class FlynnDialogue extends DialoguePlugin { case 1: switch (buttonId) { case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No, thanks."); + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "No, thanks."); stage = 10; break; case 2: diff --git a/Server/src/main/content/region/asgarnia/falador/dialogue/HerquinDialogue.java b/Server/src/main/content/region/asgarnia/falador/dialogue/HerquinDialogue.java index 3d49c7126..2ea67f374 100644 --- a/Server/src/main/content/region/asgarnia/falador/dialogue/HerquinDialogue.java +++ b/Server/src/main/content/region/asgarnia/falador/dialogue/HerquinDialogue.java @@ -48,23 +48,22 @@ public final class HerquinDialogue extends DialoguePlugin { public boolean handle(int interfaceId, int buttonId) { switch (stage) { case 0: - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Do you wish to trade?"); + interpreter.sendDialogues(player, FacialExpression.FRIENDLY, "Do you wish to trade?"); stage = 10; break; - case 2: + case 1: interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Sorry, I don't want to talk to you, actually."); + stage = 2; + break; + case 2: + interpreter.sendDialogues(npc, FacialExpression.ROLLING_EYES, "Huh, charming."); stage = 3; break; case 3: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Huh, charming."); - stage = 4; - break; - case 4: end(); break; case 10: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Why, yes, this is a jewel shop after all."); + interpreter.sendDialogues(npc, FacialExpression.FRIENDLY, "Why, yes, this is a jewel shop after all."); stage = 11; break; case 11: diff --git a/Server/src/main/content/region/asgarnia/falador/dialogue/MakeOverMageDialogue.java b/Server/src/main/content/region/asgarnia/falador/dialogue/MakeOverMageDialogue.java index ee56b343e..2d3a191eb 100644 --- a/Server/src/main/content/region/asgarnia/falador/dialogue/MakeOverMageDialogue.java +++ b/Server/src/main/content/region/asgarnia/falador/dialogue/MakeOverMageDialogue.java @@ -34,11 +34,11 @@ public class MakeOverMageDialogue extends DialoguePlugin { npc = (NPC) args[0]; if (args.length == 2) { - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Hmm... you didn't feel any unexpected growths", "aywhere around your head just then did you?"); + interpreter.sendDialogues(npc, FacialExpression.HALF_ASKING, "Hmm... you didn't feel any unexpected growths", "anywhere around your head just then did you?"); stage = 600; return true; } - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Hello there! I am known as the make-over mage! I", "have spent many years researching magics that can", "change your physical appearance!"); + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Hello there! I am known as the make-over mage! I", "have spent many years researching magics that can", "change your physical appearance!"); stage = 0; return true; } @@ -47,7 +47,7 @@ public class MakeOverMageDialogue extends DialoguePlugin { public boolean handle(int interfaceId, int buttonId) { switch (stage) { case 0: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "I can alter your physical form for a small fee of", "only 3000 gold coins! Would you like me to perform my", "magics upon you?"); + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "I can alter your physical form for a small fee of", "only 3000 gold coins! Would you like me to perform my", "magics upon you?"); stage = 1; break; case 1: @@ -61,29 +61,29 @@ public class MakeOverMageDialogue extends DialoguePlugin { case 2: switch (buttonId) { case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Tell me more about this 'make-over'."); + interpreter.sendDialogues(player, FacialExpression.HAPPY, "Tell me more about this 'make-over'."); stage = 10; break; case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Sure. Do it."); + interpreter.sendDialogues(player, FacialExpression.HAPPY, "Sure. Do it."); stage = 20; break; case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No thanks. I'm happy as Saradomin made me."); + interpreter.sendDialogues(player, FacialExpression.HAPPY, "No thanks. I'm happy as Saradomin made me."); stage = 19; break; case 4: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Cool amulet! Can I have one?"); + interpreter.sendDialogues(player, FacialExpression.HAPPY, "Cool amulet! Can I have one?"); stage = 40; break; } break; case 20: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "You of course agree that if by some accident you", "are turned into a frog you have no rights for", "compensation or refund."); + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "You of course agree that if by some accident you", "are turned into a frog you have no rights for", "compensation or refund."); stage = 25; break; case 21: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Well, go get it then. No freebies here!"); + interpreter.sendDialogues(npc, FacialExpression.ANGRY, "Well, go get it then. No freebies here!"); stage = 22; break; case 22: @@ -99,19 +99,19 @@ public class MakeOverMageDialogue extends DialoguePlugin { } break; case 10: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Why, of course! Basically, and I will try and explain", "this so that you will understant it correctly,"); + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Why, of course! Basically, and I will try and explain", "this so that you will understand it correctly,"); stage = 11; break; case 11: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "I use my secret magical technique to melt your body", "down into a puddle of its elememnts."); + interpreter.sendDialogues(npc, FacialExpression.FRIENDLY, "I use my secret magical technique to melt your body", "down into a puddle of its elements."); stage = 12; break; case 12: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "When I have broken down all trace of your body, I", "then rebuild it into the form I am thinking of."); + interpreter.sendDialogues(npc, FacialExpression.FRIENDLY, "When I have broken down all trace of your body, I", "then rebuild it into the form I am thinking of."); stage = 13; break; case 13: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Or, you know, somewhere vaguely close enough", "anyway."); + interpreter.sendDialogues(npc, FacialExpression.FRIENDLY, "Or, you know, somewhere vaguely close enough", "anyway."); stage = 14; break; case 14: @@ -119,11 +119,11 @@ public class MakeOverMageDialogue extends DialoguePlugin { stage = 15; break; case 15: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "It's as safe as houses! Why, I have only had thrity-six", "major accidents this month!"); + interpreter.sendDialogues(npc, FacialExpression.LAUGH, "It's as safe as houses! Why, I have only had thirty-six", "major accidents this month!"); stage = 16; break; case 16: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "So what do you say? Feel like a change?"); + interpreter.sendDialogues(npc, FacialExpression.ASKING, "So what do you say? Feel like a change?"); stage = 17; break; case 17: @@ -133,11 +133,11 @@ public class MakeOverMageDialogue extends DialoguePlugin { case 18: switch (buttonId) { case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Sure. Do it."); + interpreter.sendDialogues(player, FacialExpression.HAPPY, "Sure. Do it."); stage = 20; break; case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No thanks. I'm happy as Saradomin made me."); + interpreter.sendDialogues(player, FacialExpression.HAPPY, "No thanks. I'm happy as Saradomin made me."); stage = 19; break; } @@ -150,7 +150,7 @@ public class MakeOverMageDialogue extends DialoguePlugin { end(); break; case 40: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "No problem, but please remember that the amulet I will", "sell you is only a copy of my own. It contains no", "magical powers; and as such will only cost you 100", "coins."); + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "No problem, but please remember that the amulet I will", "sell you is only a copy of my own. It contains no", "magical powers; and as such will only cost you 100", "coins."); stage = 41; break; case 41: @@ -160,11 +160,11 @@ public class MakeOverMageDialogue extends DialoguePlugin { case 42: switch (buttonId) { case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Sure, here you go."); + interpreter.sendDialogues(player, FacialExpression.HAPPY, "Sure, here you go."); stage = 100; break; case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No way! That's too expensive."); + interpreter.sendDialogues(player, FacialExpression.AMAZED, "No way! That's too expensive."); stage = 400; break; @@ -178,15 +178,15 @@ public class MakeOverMageDialogue extends DialoguePlugin { end(); break; case 600: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Uh... No...?"); + interpreter.sendDialogues(player, FacialExpression.EXTREMELY_SHOCKED, "Uh... No...?"); stage = 601; break; case 601: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Good, good, I was worried for a second there!"); + interpreter.sendDialogues(npc, FacialExpression.WORRIED, "Good, good, I was worried for a second there!"); stage = 602; break; case 602: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Uh... Thanks, I guess."); + interpreter.sendDialogues(player, FacialExpression.SUSPICIOUS, "Uh... Thanks, I guess."); stage = 603; break; case 603: diff --git a/Server/src/main/content/region/asgarnia/falador/dialogue/MiningGuildDwarf.java b/Server/src/main/content/region/asgarnia/falador/dialogue/MiningGuildDwarf.java index b70e95e7c..83db1cc57 100644 --- a/Server/src/main/content/region/asgarnia/falador/dialogue/MiningGuildDwarf.java +++ b/Server/src/main/content/region/asgarnia/falador/dialogue/MiningGuildDwarf.java @@ -47,7 +47,7 @@ public final class MiningGuildDwarf extends DialoguePlugin { return true; } } - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Welcome to the Mining Guild.", "Can I help you with anything?"); + interpreter.sendDialogues(npc, FacialExpression.ASKING, "Welcome to the Mining Guild.", "Can I help you with anything?"); stage = 0; return true; } @@ -59,40 +59,40 @@ public final class MiningGuildDwarf extends DialoguePlugin { end(); break; case 0: - interpreter.sendOptions("What would you like to say?", "What have you got in the Guild?", "What do you dwarves do wiyth the ore you mine?", "No thanks, I'm fine."); + interpreter.sendOptions("What would you like to say?", "What have you got in the Guild?", "What do you dwarves do with the ore you mine?", "No thanks, I'm fine."); stage = 1; break; case 1: switch (buttonId) { case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "What have you got in the Guild?"); + interpreter.sendDialogues(player, FacialExpression.ASKING, "What have you got in the Guild?"); stage = 10; break; case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "What do you dwarves do with the ore you mine?"); + interpreter.sendDialogues(player, FacialExpression.ASKING, "What do you dwarves do with the ore you mine?"); stage = 20; break; case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No thanks, I'm fine."); + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "No thanks, I'm fine."); stage = 30; break; } break; case 10: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Ooh, it's WONDEFRFUL! There are lots of coal rocks,", "and even a few mithril rocks in the guild,", "all exclusively for people with at least level 60 mining", "There's no better mining site anywhere near here."); + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Ooh, it's WONDEFRFUL! There are lots of coal rocks,", "and even a few mithril rocks in the guild,", "all exclusively for people with at least level 60 mining", "There's no better mining site anywhere near here."); stage = 11; break; case 11: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "So you won't let me go in there?"); + interpreter.sendDialogues(player, FacialExpression.HALF_ASKING, "So you won't let me go in there?"); stage = 12; break; case 12: if (player.getSkills().getStaticLevel(Skills.MINING) < 60) { - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Sorry, but the rules are rules. Do some more training", "first."); + interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "Sorry, but the rules are rules. Do some more training", "first."); stage = 13; } else { - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Yes, you can enter if you wish."); + interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "Yes, you can enter if you wish."); stage = 14; } break; @@ -103,19 +103,19 @@ public final class MiningGuildDwarf extends DialoguePlugin { end(); break; case 20: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "What do you think? We smelt it into bars, smith the", "metal to make armour and weapons, then we exchange", "them for goods and services."); + interpreter.sendDialogues(npc, FacialExpression.HALF_THINKING, "What do you think? We smelt it into bars, smith the", "metal to make armour and weapons, then we exchange", "them for goods and services."); stage = 21; break; case 21: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I don't see many dwarves here."); + interpreter.sendDialogues(player, FacialExpression.HALF_THINKING, "I don't see many dwarves here."); stage = 22; break; case 22: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "No, this is only a mining outpost. We dwarves don't", "much like to settle in human cities. Most of the ore is", "carted off to Keldagrim, the great dwarven city-", "They've got a special blast furnace up there - it makes"); + interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "No, this is only a mining outpost. We dwarves don't", "much like to settle in human cities. Most of the ore is", "carted off to Keldagrim, the great dwarven city.", "They've got a special blast furnace up there - it makes"); stage = 23; break; case 23: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "smelting the ore so much easier. There are plenty of", "dwarven traders working in Keldagrim."); + interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "smelting the ore so much easier. There are plenty of", "dwarven traders working in Keldagrim."); stage = 24; break; case 24: diff --git a/Server/src/main/content/region/asgarnia/falador/dialogue/RisingSunInnBartenderDialogue.kt b/Server/src/main/content/region/asgarnia/falador/dialogue/RisingSunInnBartenderDialogue.kt index 43b4b1150..199a545a0 100644 --- a/Server/src/main/content/region/asgarnia/falador/dialogue/RisingSunInnBartenderDialogue.kt +++ b/Server/src/main/content/region/asgarnia/falador/dialogue/RisingSunInnBartenderDialogue.kt @@ -20,15 +20,15 @@ import core.tools.START_DIALOGUE * @author vddCore */ @Initializable -class RisingSunInnBartenderDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { - override fun newInstance(player: Player): core.game.dialogue.DialoguePlugin +class RisingSunInnBartenderDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player): DialoguePlugin = RisingSunInnBartenderDialogue(player) override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC npcl( - core.game.dialogue.FacialExpression.HAPPY, + FacialExpression.HAPPY, "Hi! What can I get you?" ) @@ -39,25 +39,25 @@ class RisingSunInnBartenderDialogue(player: Player? = null) : core.game.dialogue when (stage) { START_DIALOGUE -> if (hasAnyBeerGlasses()) { showTopics( - Topic(core.game.dialogue.FacialExpression.HALF_ASKING, "What ales are you serving?", 10), - Topic(core.game.dialogue.FacialExpression.HALF_GUILTY, "I've got some beer glasses...", 20) + Topic(FacialExpression.ASKING, "What ales are you serving?", 10), + Topic(FacialExpression.HAPPY, "I've got some beer glasses...", 20) ) } else { - playerl(core.game.dialogue.FacialExpression.HALF_ASKING, "What ales are you serving?") + playerl(FacialExpression.ASKING, "What ales are you serving?") .also { stage = 10 } } 10 -> npcl( - core.game.dialogue.FacialExpression.HALF_GUILTY, + FacialExpression.FRIENDLY, "Well, we've got Asgarnian Ale, Wizard's Mind Bomb and Dwarven Stout. " + "Each for only 3 coins." ).also { stage++ } 11 -> showTopics( - Topic(core.game.dialogue.FacialExpression.HAPPY, "One Asgarnian Ale, please.", 12), - Topic(core.game.dialogue.FacialExpression.HAPPY, "I'll try the Mind Bomb.", 13), - Topic(core.game.dialogue.FacialExpression.HALF_ASKING, "Can I have a Dwarven Stout?", 14), - Topic(core.game.dialogue.FacialExpression.NEUTRAL, "I don't feel like any of those.", END_DIALOGUE) + Topic(FacialExpression.HAPPY, "One Asgarnian Ale, please.", 12), + Topic(FacialExpression.HAPPY, "I'll try the Mind Bomb.", 13), + Topic(FacialExpression.ASKING, "Can I have a Dwarven Stout?", 14), + Topic(FacialExpression.NEUTRAL, "I don't feel like any of those.", END_DIALOGUE) ) 12 -> if (ensureHasMoney()) { @@ -74,27 +74,27 @@ class RisingSunInnBartenderDialogue(player: Player? = null) : core.game.dialogue 20 -> { npcl( - core.game.dialogue.FacialExpression.HALF_GUILTY, + FacialExpression.HALF_GUILTY, "Oh, we will buy those from you if you're interested. We offer 2 coins for each glass." ).also { stage ++ } } 21 -> showTopics( - Topic(core.game.dialogue.FacialExpression.HAPPY, "Yes, please!", 22), - Topic(core.game.dialogue.FacialExpression.NEUTRAL, "No thanks, I like my empty beer glasses.", END_DIALOGUE) + Topic(FacialExpression.HAPPY, "Yes, please!", 22), + Topic(FacialExpression.NEUTRAL, "No thanks, I like my empty beer glasses.", END_DIALOGUE) ) 22 -> { trySellAllBeerGlasses() npcl( - core.game.dialogue.FacialExpression.FRIENDLY, + FacialExpression.FRIENDLY, "There you go!" ).also { stage = END_DIALOGUE } } 30 -> { - playerl(core.game.dialogue.FacialExpression.FRIENDLY, "Thanks, ${npc.name}.") + playerl(FacialExpression.FRIENDLY, "Thanks, ${npc.name}.") .also { stage = END_DIALOGUE } } } @@ -109,7 +109,7 @@ class RisingSunInnBartenderDialogue(player: Player? = null) : core.game.dialogue private fun ensureHasMoney(): Boolean { if (!inInventory(player, Items.COINS_995, 3)) { - npcl(core.game.dialogue.FacialExpression.ANGRY, "No freeloaders!") + npcl(FacialExpression.ANGRY, "No freeloaders!") .also { sendMessage(player, "You don't have enough money to buy that.") stage = END_DIALOGUE diff --git a/Server/src/main/content/region/asgarnia/falador/dialogue/SirReniteeDialogue.java b/Server/src/main/content/region/asgarnia/falador/dialogue/SirReniteeDialogue.java index 19b19214e..f8f9695c9 100644 --- a/Server/src/main/content/region/asgarnia/falador/dialogue/SirReniteeDialogue.java +++ b/Server/src/main/content/region/asgarnia/falador/dialogue/SirReniteeDialogue.java @@ -1,5 +1,6 @@ package content.region.asgarnia.falador.dialogue; +import core.ServerConstants; import core.Util; import core.game.dialogue.DialoguePlugin; import core.game.dialogue.FacialExpression; @@ -65,7 +66,7 @@ public class SirReniteeDialogue extends DialoguePlugin { end(); break; case 100: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Hmm, well, mmm, do you have a family crest? I keep", "track of every 2009Scape family, you know, so I might", "be able to find yours."); + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Hmm, well, mmm, do you have a family crest? I keep", "track of every " + ServerConstants.SERVER_NAME + " family, you know, so I might", "be able to find yours."); stage = 110; break; case 110: 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/WayneDialogue.java b/Server/src/main/content/region/asgarnia/falador/dialogue/WayneDialogue.java index 49337c22e..85a9824be 100644 --- a/Server/src/main/content/region/asgarnia/falador/dialogue/WayneDialogue.java +++ b/Server/src/main/content/region/asgarnia/falador/dialogue/WayneDialogue.java @@ -39,7 +39,7 @@ public final class WayneDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Welcome to Wayne's Chains. Do you wanna buy or", "sell some chain mail?"); + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Welcome to Wayne's Chains. Do you wanna buy or", "sell some chain mail?"); stage = 0; return true; } diff --git a/Server/src/main/content/region/asgarnia/falador/dialogue/WorkmanDialogue.kt b/Server/src/main/content/region/asgarnia/falador/dialogue/WorkmanDialogue.kt index 26cbd5feb..81bd9e2f0 100644 --- a/Server/src/main/content/region/asgarnia/falador/dialogue/WorkmanDialogue.kt +++ b/Server/src/main/content/region/asgarnia/falador/dialogue/WorkmanDialogue.kt @@ -5,45 +5,33 @@ 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.WORKMAN_3236 +import core.tools.END_DIALOGUE +import core.tools.START_DIALOGUE +import org.rs09.consts.NPCs /** * @author qmqz + * https://youtu.be/DwQgAQqaXos */ @Initializable -class WorkmanDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ - - override fun open(vararg args: Any?): Boolean { - npc = args[0] as NPC - player(core.game.dialogue.FacialExpression.FRIENDLY,"Hiya.").also { stage = 0 } - return true - } +class WorkmanDialogue(player: Player? = null) : DialoguePlugin(player) { override fun handle(interfaceId: Int, buttonId: Int): Boolean { - when(stage){ - 0 -> { - npc(core.game.dialogue.FacialExpression.ASKING, "What do you want? I've got work to do!").also { stage++ } - } - - 1 -> { - player(core.game.dialogue.FacialExpression.ASKING, "Can you teach me anything?").also { stage++ } - } - - 2 -> { - npcl(core.game.dialogue.FacialExpression.ANNOYED, "No - I've got one lousy apprentice already, and that's quite enough hassle! Go away!").also { stage = 99 } - } - - 99 -> end() + when (stage) { + START_DIALOGUE -> player(FacialExpression.FRIENDLY, "Hiya.").also { stage++ } + 1 -> npc(FacialExpression.ANNOYED, "What do you want? I've got work to do!").also { stage++ } + 2 -> player(FacialExpression.ASKING, "Can you teach me anything?").also { stage++ } + 3 -> npcl(FacialExpression.ANNOYED, "No - I've got one lousy apprentice already, and that's quite enough hassle! Go away!").also { stage = END_DIALOGUE } } return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return WorkmanDialogue(player) } override fun getIds(): IntArray { - return intArrayOf(WORKMAN_3236) + return intArrayOf(NPCs.WORKMAN_3236) } } \ No newline at end of file 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..1d2592ad8 100644 --- a/Server/src/main/content/region/asgarnia/falador/dialogue/WysonTheGardenerDialogue.kt +++ b/Server/src/main/content/region/asgarnia/falador/dialogue/WysonTheGardenerDialogue.kt @@ -121,7 +121,7 @@ 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)) { 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 bdf287db7..4831afd9c 100644 --- a/Server/src/main/content/region/asgarnia/falador/diary/FaladorAchievementDiary.kt +++ b/Server/src/main/content/region/asgarnia/falador/diary/FaladorAchievementDiary.kt @@ -1,19 +1,25 @@ package content.region.asgarnia.falador.diary -import core.api.areEquipped +import core.api.allInEquipment import core.api.inBorders import core.game.node.entity.player.Player import core.game.node.entity.player.link.diary.DiaryType +import core.game.node.entity.skill.Skills.FARMING import core.game.world.map.zone.ZoneBorders import org.rs09.consts.Items import org.rs09.consts.NPCs import org.rs09.consts.Scenery import content.region.asgarnia.falador.dialogue.RisingSunInnBartenderDialogue import content.global.handlers.iface.FairyRing +import content.global.skill.crafting.lightsources.LightSources +import content.global.skill.farming.FarmingPatch +import core.api.getStatLevel import core.game.diary.AreaDiaryTask 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 { @@ -27,6 +33,8 @@ class FaladorAchievementDiary : DiaryEventHookBase(DiaryType.FALADOR) { private val WAYNES_CHAINS_AREA = ZoneBorders(2969, 3310, 2975, 3314) private val SARAHS_FARMING_SHOP_AREA = ZoneBorders(3021, 3285, 3040, 3296) private val FALADOR_GENERAL_AREA = ZoneBorders(2934, 3399, 3399, 3307) + private val CHEMIST_AREA = ZoneBorders(2929, 3213, 2936, 3207) + private val PORT_SARIM_FLOWER_PATCH = ZoneBorders(3053, 3306, 3056, 3309) private val PROSELYTE_FULL_ARMOR_MALE = intArrayOf( @@ -129,8 +137,8 @@ class FaladorAchievementDiary : DiaryEventHookBase(DiaryType.FALADOR) { DiaryLevel.HARD, HardTasks.DARK_WIZARDS_TOWER_ASCEND_IN_FULL_PROSELYTE_ARMOR, ) { player -> - areEquipped(player, *PROSELYTE_FULL_ARMOR_MALE) - || areEquipped(player, *PROSELYTE_FULL_ARMOR_FEMALE) + allInEquipment(player, *PROSELYTE_FULL_ARMOR_MALE) + || allInEquipment(player, *PROSELYTE_FULL_ARMOR_FEMALE) } ) @@ -146,7 +154,8 @@ class FaladorAchievementDiary : DiaryEventHookBase(DiaryType.FALADOR) { } } } - if (event.option == "pickpocket" && (event.target.id in FALADOR_GUARD && inBorders(player, FALADOR_GENERAL_AREA))) { + + if (event.option == "pickpocket" && (event.target.id in FALADOR_GUARD && inBorders(player, FALADOR_GENERAL_AREA) && getStatLevel(player, Skills.THIEVING) >= 40)) { finishTask( player, DiaryLevel.MEDIUM, @@ -154,6 +163,7 @@ class FaladorAchievementDiary : DiaryEventHookBase(DiaryType.FALADOR) { ) } } + override fun onDialogueOptionSelected(player: Player, event: DialogueOptionSelectionEvent) { when (event.dialogue) { is RisingSunInnBartenderDialogue -> { @@ -202,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) { @@ -248,4 +261,40 @@ class FaladorAchievementDiary : DiaryEventHookBase(DiaryType.FALADOR) { ) } } + + override fun onLightSourceLit(player: Player, event: LitLightSourceEvent) { + when { + inBorders(player, CHEMIST_AREA) && (event.litLightSourceId == LightSources.BULLSEYE_LANTERN.litID) -> { + finishTask( + player, + DiaryLevel.MEDIUM, + MediumTasks.CHEMISTS_LIGHT_BULLSEYE_LANTERN + ) + } + } + } + + override fun onUsedWith(player: Player, event: UseWithEvent) { + when { + inBorders(player, PORT_SARIM_FLOWER_PATCH) -> { + if (event.used == Items.SCARECROW_6059 && event.with == 7847 && player.getSkills().hasLevel(FARMING, 23) && FarmingPatch.forObjectID(7840)?.getPatchFor(player)?.plantable == null) { + finishTask( + player, + DiaryLevel.MEDIUM, + MediumTasks.PORT_SARIM_NORTHERN_PATCH_PLACE_SCARECROW + ) + } + } + } + } + + 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/handlers/GiantMoleNPC.java b/Server/src/main/content/region/asgarnia/falador/handlers/GiantMoleNPC.java index 256270b58..6564c96dd 100644 --- a/Server/src/main/content/region/asgarnia/falador/handlers/GiantMoleNPC.java +++ b/Server/src/main/content/region/asgarnia/falador/handlers/GiantMoleNPC.java @@ -1,5 +1,6 @@ package content.region.asgarnia.falador.handlers; +import content.global.skill.skillcapeperks.SkillcapePerks; import core.cache.def.impl.SceneryDefinition; import core.game.component.Component; import core.game.component.ComponentDefinition; @@ -33,6 +34,9 @@ import core.net.packet.out.Interface; import core.plugin.Plugin; import core.plugin.Initializable; import core.tools.RandomFunction; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.playAudio; /** * Handles the Giant Mole NPC. @@ -239,7 +243,7 @@ public final class GiantMoleNPC extends AbstractNPC { public boolean handle(Player player, Component component, int opcode, int button, int slot, int itemId) { if (button == 17) { player.getProperties().setTeleportLocation(Location.create(1752, 5237, 0)); - player.getAudioManager().send(1384); + playAudio(player, Sounds.ROOF_COLLAPSE_1384); player.getPacketDispatch().sendMessage("You seem to have dropped down into a network of mole tunnels."); if (!player.getAchievementDiaryManager().getDiary(DiaryType.FALADOR).isComplete(0, 5)) { @@ -254,7 +258,7 @@ public final class GiantMoleNPC extends AbstractNPC { DigAction action = new DigAction() { @Override public void run(Player player) { - if (!LightSource.hasActiveLightSource(player)) { + if (!SkillcapePerks.isActive(SkillcapePerks.CONSTANT_GLOW, player) && !LightSource.hasActiveLightSource(player)) { player.getPacketDispatch().sendMessage("It's going to be dark down there, I should bring a light source."); return; } diff --git a/Server/src/main/content/region/asgarnia/falador/handlers/partyroom/BalloonManager.java b/Server/src/main/content/region/asgarnia/falador/handlers/partyroom/BalloonManager.java index 7c4aa2ecb..79d70ae79 100644 --- a/Server/src/main/content/region/asgarnia/falador/handlers/partyroom/BalloonManager.java +++ b/Server/src/main/content/region/asgarnia/falador/handlers/partyroom/BalloonManager.java @@ -21,6 +21,9 @@ import core.game.world.update.flag.context.Animation; import core.plugin.Plugin; import core.tools.RandomFunction; +import static core.api.ContentAPIKt.*; + + /** * Manages the droped party balloons. * @author Vexia @@ -83,7 +86,7 @@ public final class BalloonManager extends OptionHandler { public boolean pulse() { int realCount = --countdown - GameWorld.getTicks(); for (ChestViewer viewer : PartyRoomPlugin.getViewers().values()) { - viewer.getPlayer().getConfigManager().set(1135, realCount); + setVarp(viewer.getPlayer(), 1135, realCount); } if (--realCount - GameWorld.getTicks() <= 0) { drop(); diff --git a/Server/src/main/content/region/asgarnia/falador/handlers/partyroom/ChestViewer.java b/Server/src/main/content/region/asgarnia/falador/handlers/partyroom/ChestViewer.java index ab5ce8045..c1b2302a0 100644 --- a/Server/src/main/content/region/asgarnia/falador/handlers/partyroom/ChestViewer.java +++ b/Server/src/main/content/region/asgarnia/falador/handlers/partyroom/ChestViewer.java @@ -13,6 +13,9 @@ import core.net.packet.PacketRepository; import core.net.packet.context.ContainerContext; import core.net.packet.out.ContainerPacket; +import static core.api.ContentAPIKt.*; + + /** * A chest viewer. * @author Vexia @@ -69,7 +72,7 @@ public final class ChestViewer { player.getPacketDispatch().sendRunScript(150, "IviiiIsssssssss", INV_OPTIONS); player.getInterfaceManager().openSingleTab(new Component(648)); player.getInterfaceManager().open(new Component(647).setCloseEvent(new ChestCloseEvent())); - player.getConfigManager().set(1135, 0); + setVarp(player, 1135, 0); update(0, null); update(1, null); return this; diff --git a/Server/src/main/content/region/asgarnia/falador/quest/DoricsQuest.java b/Server/src/main/content/region/asgarnia/falador/quest/DoricsQuest.java deleted file mode 100644 index 7c3130a75..000000000 --- a/Server/src/main/content/region/asgarnia/falador/quest/DoricsQuest.java +++ /dev/null @@ -1,72 +0,0 @@ -package content.region.asgarnia.falador.quest; - -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.GroundItemManager; -import core.game.node.item.Item; -import core.plugin.Initializable; - -/** - * Represents the Doric's Quest - * @author Vexia - * - */ -@Initializable -public class DoricsQuest extends Quest { - - /** - * Constructs a new {@Code DoricsQuest} {@Code Object} - */ - public DoricsQuest() { - super("Doric's Quest", 17, 16, 1, 31, 0, 1, 100); - } - - @Override - public Quest newInstance(Object object) { - return this; - } - - @Override - public void drawJournal(Player player, int stage) { - super.drawJournal(player, stage); - switch (stage) { - case 0: - player.getPacketDispatch().sendString("I can start this quest by speaking to Doric who is North of", 275, 4+ 7); - player.getPacketDispatch().sendString("Falador", 275, 5+ 7); - player.getPacketDispatch().sendString("There aren't any requirements but Level 15 Mining will help", 275, 7+ 7); - break; - case 1: - player.getPacketDispatch().sendString("I have spoken to Doric", 275, 4+ 7); - player.getPacketDispatch().sendString("I need to collect some items and bring them to Doric", 275, 6+ 7); - player.getPacketDispatch().sendString(player.getInventory().contains(434, 6) ? "6 Clay" : "6 Clay", 275, 7+ 7); - player.getPacketDispatch().sendString(player.getInventory().contains(436, 4) ? "4 Copper Ore" : "4 Copper Ore", 275, 8+ 7); - player.getPacketDispatch().sendString(player.getInventory().contains(440, 2) ? "2 Iron Ore" : "2 Iron Ore", 275, 9+ 7); - break; - case 100: - player.getPacketDispatch().sendString("I have spoken to Doric", 275, 4+ 7); - player.getPacketDispatch().sendString("QUEST COMPLETE!", 275, 10+ 7); - player.getPacketDispatch().sendString(" I have collected some Clay, Copper Ore, and Iron Ore", 275, 6+ 7); - player.getPacketDispatch().sendString("Doric rewarded me for all my hard work", 275, 8+ 7); - player.getPacketDispatch().sendString("I can now use Doric's Anvils whenever I want", 275, 9+ 7); - break; - } - } - - @Override - public void finish(Player player) { - super.finish(player); - player.getPacketDispatch().sendString("1 Quest Point", 277, 8 + 2); - player.getPacketDispatch().sendString("1300 Mining XP", 277, 9 + 2); - player.getPacketDispatch().sendString("180 coins", 277, 10 + 2); - player.getPacketDispatch().sendString("Use of Doric's Anvils", 277, 11 + 2); - if (!player.getInventory().add(new Item(995, 180))) { - GroundItemManager.create(new Item(995, 180), player.getLocation()); - } - player.getSkills().addExperience(Skills.MINING, 1300); - player.getPacketDispatch().sendItemZoomOnInterface(1269, 240, 277, 5); - player.getInterfaceManager().closeChatbox(); - player.getPacketDispatch().sendString("You have completed Doric's Quest!", 277, 2 + 2); - } - -} 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/BKFortressPlugin.java b/Server/src/main/content/region/asgarnia/falador/quest/blackknightsfortress/BKFortressPlugin.java index 33fe3a173..9f3c3ad6d 100644 --- a/Server/src/main/content/region/asgarnia/falador/quest/blackknightsfortress/BKFortressPlugin.java +++ b/Server/src/main/content/region/asgarnia/falador/quest/blackknightsfortress/BKFortressPlugin.java @@ -16,6 +16,9 @@ import core.game.world.repository.Repository; import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Graphics; import core.plugin.Plugin; +import org.rs09.consts.Items; + +import static core.api.ContentAPIKt.allInEquipment; /** * Represents the black knights fortress node option plugin. @@ -129,21 +132,32 @@ public final class BKFortressPlugin extends OptionHandler { return true; case 2338: if (player.getLocation().getX() > 3019) { - DoorActionHandler.handleAutowalkDoor(player, (Scenery) node); + DoorActionHandler.handleAutowalkDoor(player, (Scenery) node); // big table room door return true; } player.getDialogueInterpreter().open(4605, Repository.findNPC(4605), true, true); break; - case 2337: - if (!player.getEquipment().containsAtLeastOneItem(1139) || !player.getEquipment().containsAtLeastOneItem(1101)) { - player.getDialogueInterpreter().open(4605, Repository.findNPC(4604), true); - } else { - DoorActionHandler.handleAutowalkDoor(player, (Scenery) node); - } - break; + case 2337: // Guard Door - only check for uniform from outside + switch (player.getLocation().getY()) { + case 3514: // Outside constant Y location, block the player for checks + if(allInEquipment(player, Items.BRONZE_MED_HELM_1139, Items.IRON_CHAINBODY_1101)){ + DoorActionHandler.handleAutowalkDoor(player, (Scenery) node); + } + else player.getDialogueInterpreter().open(4605, Repository.findNPC(4604), true); + break; + case 3515: //Inside constant Y location, let the player through + DoorActionHandler.handleAutowalkDoor(player, (Scenery) node); + default: + break; + } + break; case 74: - case 73: - player.getPacketDispatch().sendMessage("You can't open this door."); + case 73:// large door scenery id 73 + if (player.getLocation().getX() == 3008) { // only opened from inside + DoorActionHandler.handleAutowalkDoor(player, (Scenery) node); + return true; + } + player.getPacketDispatch().sendMessage("You can't open this door."); // large door to the fortress break; case 9589: switch (option) { 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 bf1786b24..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 @@ -1,10 +1,12 @@ package content.region.asgarnia.falador.quest.blackknightsfortress; 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 core.game.world.update.flag.context.Animation; +import content.data.Quests; /** * Represents the dialogue of listening throug a grill during the black knights' @@ -70,12 +72,12 @@ public final class BKListenDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { if (args != null && args.length == 2) { - interpreter.sendDialogues(witch, null, "Where has Greldo got to with that magic cabbage!"); + interpreter.sendDialogues(witch, FacialExpression.NEUTRAL, "Where has Greldo got to with that magic cabbage!"); stage = 10; player.animate(ANIMATION); return true; } - interpreter.sendDialogues(captain, null, "So... how's the secret weapon coming along?"); + interpreter.sendDialogues(captain, FacialExpression.ASKING, "So... how's the secret weapon coming along?"); stage = 0; return true; } @@ -84,65 +86,65 @@ public final class BKListenDialogue extends DialoguePlugin { public boolean handle(int interfaceId, int buttonId) { switch (stage) { case 0: - interpreter.sendDialogues(witch, null, "The invincibility potion is almost ready..."); + interpreter.sendDialogues(witch, FacialExpression.HAPPY, "The invincibility potion is almost ready..."); stage = 1; break; case 1: - interpreter.sendDialogues(witch, null, "It's taken me FIVE YEARS, but it's almost ready."); + interpreter.sendDialogues(witch, FacialExpression.HAPPY, "It's taken me FIVE YEARS, but it's almost ready."); stage = 2; break; case 2: - interpreter.sendDialogues(witch, null, "Greldo the Goblin here is just going to fetch the last", "ingredient for me."); + interpreter.sendDialogues(witch, FacialExpression.NEUTRAL, "Greldo the Goblin here is just going to fetch the last", "ingredient for me."); stage = 3; break; case 3: - interpreter.sendDialogues(witch, null, "It's a specially grown cabbage grown by my cousin", "Helda who lives in Draynor Manor."); + interpreter.sendDialogues(witch, FacialExpression.NEUTRAL, "It's a special grown cabbage grown by my cousin", "Helda who lives in Draynor Manor."); stage = 4; break; case 4: - interpreter.sendDialogues(witch, null, "The soil there is slightly magical and it gives the", "cabbages slight magical properties...."); + interpreter.sendDialogues(witch, FacialExpression.NEUTRAL, "The soil there is slightly magical and it gives the", "cabbages slight magical properties...."); stage = 5; break; case 5: - interpreter.sendDialogues(witch, null, "...not to mention the trees!"); + interpreter.sendDialogues(witch, FacialExpression.AMAZED, "...not to mention the trees!"); stage = 6; break; case 6: - interpreter.sendDialogues(witch, null, "Now remember Greldo, only a Draynor Manor", "cabbage will do! Don't get lazy and bring any old", "cabbage, THAT would ENTIRELY wreck the potion!"); + interpreter.sendDialogues(witch, FacialExpression.NEUTRAL, "Now remember Greldo, only a Draynor Manor", "cabbage will do! Don't get lazy and bring any old", "cabbage, THAT would ENTIRELY wreck the potion!"); stage = 7; break; case 7: - interpreter.sendDialogues(greldo, null, "Yeth, Mithreth."); + interpreter.sendDialogues(greldo, FacialExpression.OLD_NORMAL, "Yeth, Mithreth."); 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: - interpreter.sendDialogues(captain, null, "What's that noise?"); + interpreter.sendDialogues(captain, FacialExpression.ASKING, "What's that noise?"); stage = 11; break; case 11: - interpreter.sendDialogues(witch, null, "Hopefully Greldo with the cabbage... yes, look her it", "co....NOOOOOoooo!"); + interpreter.sendDialogues(witch, FacialExpression.NEUTRAL, "Hopefully Greldo with the cabbage... yes, look her it", "co....NOOOOOoooo!"); stage = 12; break; case 12: - interpreter.sendDialogues(witch, null, "My potion!"); + interpreter.sendDialogues(witch, FacialExpression.EXTREMELY_SHOCKED, "My potion!"); stage = 13; break; case 13: - interpreter.sendDialogues(captain, null, "Oh boy, this doesn't look good!"); + interpreter.sendDialogues(captain, FacialExpression.WORRIED, "Oh boy, this doesn't look good!"); stage = 14; break; case 14: - interpreter.sendDialogues(cat, null, "Meow!"); + interpreter.sendDialogues(cat, FacialExpression.EXTREMELY_SHOCKED, "Meow!"); stage = 15; break; case 15: if (player.getInventory().remove(CABBAGE)) { - player.getQuestRepository().getQuest("Black Knights' Fortress").setStage(player, 30); - interpreter.sendDialogues(player, null, "Looks like my work here is done. Seems like that's", "succesfully sabotaged their little secret weapon plan."); + 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; } break; 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/FortressGuardDialogue.java b/Server/src/main/content/region/asgarnia/falador/quest/blackknightsfortress/FortressGuardDialogue.java index 6f3019f97..51fa14058 100644 --- a/Server/src/main/content/region/asgarnia/falador/quest/blackknightsfortress/FortressGuardDialogue.java +++ b/Server/src/main/content/region/asgarnia/falador/quest/blackknightsfortress/FortressGuardDialogue.java @@ -50,20 +50,20 @@ public class FortressGuardDialogue extends DialoguePlugin { public boolean open(Object... args) { npc = (NPC) args[0]; if (args.length == 2) { - interpreter.sendDialogues(npc, null, "Hey! You can't come in here! This is a high security", "military installation!"); + interpreter.sendDialogues(npc, FacialExpression.ANGRY, "Hey! You can't come in here! This is a high security", "military installation!"); stage = 40; return true; } if (args.length == 3) { - interpreter.sendDialogues(npc, null, "I wouldn't go in there if I were you. Those Black", "Knights are in an important meeting. They said they'd", "kill anyone who went in there!"); + interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "I wouldn't go in there if I were you. Those Black", "Knights are in an important meeting. They said they'd", "kill anyone who went in there!"); stage = 50; return true; } if (!inUniform(player)) { - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Get lost. This is private property."); + interpreter.sendDialogues(npc, FacialExpression.ANGRY, "Get lost. This is private property."); stage = 0; } else { - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Hey! Get back on duty!"); + interpreter.sendDialogues(npc, FacialExpression.FURIOUS, "Hey! Get back on duty!"); stage = 30; } return true; @@ -77,15 +77,15 @@ public class FortressGuardDialogue extends DialoguePlugin { stage = 1; break; case 1: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Well, this is the guards' entrance. I might be new here", "but I can tell you're not a guard."); + interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "Well, this is the guards' entrance. I might be new here", "but I can tell you're not a guard."); stage = 2; break; case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "How can you tell?"); + interpreter.sendDialogues(player, FacialExpression.ASKING, "How can you tell?"); stage = 3; break; case 3: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "You're not even wearing proper guards uniform!"); + interpreter.sendDialogues(npc, FacialExpression.ANGRY, "You're not even wearing proper guards uniform!"); stage = 4; break; case 4: @@ -99,39 +99,39 @@ public class FortressGuardDialogue extends DialoguePlugin { stage = 10; break; case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "So what is this uniform?"); + interpreter.sendDialogues(player, FacialExpression.ASKING, "So what is this uniform?"); stage = 20; break; } break; case 10: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Go away. You're getting annoying."); + interpreter.sendDialogues(npc, FacialExpression.ANNOYED, "Go away. You're getting annoying."); stage = 11; break; case 11: end(); break; case 20: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Well you can see me wearing it. It's an iron chainbody", "and a medium bronze helm."); + interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "Well you can see me wearing it. It's an iron chainbody", "and a medium bronze helm."); stage = 21; break; case 21: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Hmmm... I wonder if I can make that or get some in", "the local towns..."); + interpreter.sendDialogues(player, FacialExpression.HALF_ASKING, "Hmmm... I wonder if I can make that or get some in", "the local towns..."); stage = 22; break; case 22: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "What was that you muttered?"); + interpreter.sendDialogues(npc, FacialExpression.HALF_ASKING, "What was that you muttered?"); stage = 23; break; case 23: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Oh, nothing important!"); + interpreter.sendDialogues(player, FacialExpression.SUSPICIOUS, "Oh, nothing important!"); stage = 24; break; case 24: end(); break; case 30: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Uh..."); + interpreter.sendDialogues(player, FacialExpression.SUSPICIOUS, "Uh..."); stage = 31; break; case 31: @@ -152,24 +152,24 @@ public class FortressGuardDialogue extends DialoguePlugin { stage = 42; break; case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "So who does it belong to?"); + interpreter.sendDialogues(player, FacialExpression.ASKING, "So who does it belong to?"); stage = 44; break; } break; case 42: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Don't let it happen again."); + interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "Don't let it happen again."); stage = 43; break; case 43: end(); break; case 44: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "This fortress belongs to the order of Black Knights", "known as the Kinshra."); + interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "This fortress belongs to the order of Black Knights", "known as the Kinshra."); stage = 45; break; case 45: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Oh. Okay, thanks."); + interpreter.sendDialogues(player, FacialExpression.FRIENDLY, "Oh. Okay, thanks."); stage = 46; break; case 46: @@ -182,17 +182,17 @@ public class FortressGuardDialogue extends DialoguePlugin { case 51: switch (buttonId) { case 1: - interpreter.sendDialogues(player, null, "Okay, I won't."); + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "Okay, I won't."); stage = 52; break; case 2: - interpreter.sendDialogues(player, null, "I don't care. I'm going in anyway."); + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "I don't care. I'm going in anyway."); stage = 54; break; } break; case 52: - interpreter.sendDialogues(npc, null, "Wise move."); + interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "Wise move."); stage = 53; break; case 53: @@ -220,7 +220,7 @@ public class FortressGuardDialogue extends DialoguePlugin { } /** - * Method used to cehck if the player is in the inventory. + * Method used to check if the player is in the inventory. * @param player the player. * @return True if so. */ 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 a07815927..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,12 +1,17 @@ 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; 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 static core.api.ContentAPIKt.openDialogue; +import content.data.Quests; + /** * Represents the sir amik varze dialogue. * @author Vexia @@ -44,23 +49,23 @@ 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, null, "I have ruined the Black Knights' invincibility potion."); + interpreter.sendDialogues(player, FacialExpression.HAPPY, "I have ruined the Black Knights' invincibility potion."); stage = 0; break; case 10: case 20: - interpreter.sendDialogues(npc, null, "How's the mission going?"); + interpreter.sendDialogues(npc, FacialExpression.ASKING, "How's the mission going?"); stage = 0; break; case 100: - interpreter.sendDialogues(player, null, "Hello Sir Amik."); + interpreter.sendDialogues(player, FacialExpression.HAPPY, "Hello Sir Amik."); stage = 0; break; default: - interpreter.sendDialogues(npc, null, "I am the leader of the White Knights of Falador. Why", "do you seek my audience?"); + interpreter.sendDialogues(npc, FacialExpression.ASKING, "I am the leader of the White Knights of Falador. Why", "do you seek my audience?"); stage = 0; break; } @@ -71,48 +76,20 @@ 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, null, "Hello, friend!"); - stage = 1; - break; - case 1: - interpreter.sendDialogues(player, null, "Do you have any other quests for me to do?"); - stage = 2; - break; - case 2: - interpreter.sendDialogues(npc, null, "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, null, "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, null, "Would you like me to put your name forwards to", "them?"); - stage = 5; - break; - case 5: - interpreter.sendDialogues(player, null, "No thanks."); - stage = 6; - break; - case 6: - end(); - break; - } + openDialogue(player, new SirAmikVarzeDialogueFile(), npc); break; case 30: switch (stage) { case 0: - interpreter.sendDialogues(npc, null, "Yes, we have just received a message from the Black", "Knights saying they withdraw their demands, which", "would seem to confirm your story."); + interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "Yes, we have just received a message from the Black", "Knights saying they withdraw their demands, which", "would seem to confirm your story."); stage = 1; break; case 1: - interpreter.sendDialogues(player, null, "No I believe there was some talk of a cash reward..."); + interpreter.sendDialogues(player, FacialExpression.HALF_ASKING, "Now I believe there was some talk of a cash reward..."); stage = 2; break; case 2: - interpreter.sendDialogues(npc, null, "Absolutely right. Please accept this reward."); + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Absolutely right. Please accept this reward."); stage = 3; break; case 3: @@ -132,7 +109,7 @@ public class SirAmikVarzeDialogue extends DialoguePlugin { case 20: switch (stage) { case 0: - interpreter.sendDialogues(player, null, "I have managed to find what the secret weapon is", "I am now in the process of destroying it."); + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "I have managed to find what the secret weapon is", "I am now in the process of destroying it."); stage = 1; break; case 1: @@ -143,19 +120,19 @@ public class SirAmikVarzeDialogue extends DialoguePlugin { case 10: switch (stage) { case 0: - interpreter.sendDialogues(player, null, "I haven't managed to find what the secret weapon is", "yet..."); + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I haven't managed to find what the secret weapon is", "yet..."); stage = 1; break; case 1: - interpreter.sendDialogues(npc, null, "Well keep at it! Falador's future is at stake!"); + interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "Well keep at it! Falador's future is at stake!"); stage = 2; break; case 2: if (!player.getInventory().containsItem(BlackKnightsFortress.DOSSIER) && !player.getBank().containsItem(BlackKnightsFortress.DOSSIER)) { - interpreter.sendDialogues(npc, null, "Here's the dossier on the case."); + interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "Here's the dossier on the case."); stage = 3; } else { - interpreter.sendDialogues(npc, null, "Don't forget to read that dossier of information I gave", "you."); + interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "Don't forget to read that dossier of information I gave", "you."); stage = 4; } break; @@ -172,7 +149,7 @@ public class SirAmikVarzeDialogue extends DialoguePlugin { switch (stage) { case 0: if (player.getQuestRepository().getPoints() < 12) { - interpreter.sendDialogues(player, null, "I don't I'm just looking around."); + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "I don't I'm just looking around."); stage = 2; } else { interpreter.sendOptions("Select an Option", "I seek a quest!", "I don't, I'm just looking around."); @@ -182,11 +159,11 @@ public class SirAmikVarzeDialogue extends DialoguePlugin { case 1: switch (buttonId) { case 1: - interpreter.sendDialogues(player, null, "I seek a quest."); + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "I seek a quest."); stage = 5; break; case 2: - interpreter.sendDialogues(player, null, "I don't I'm just looking around."); + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "I don't I'm just looking around."); stage = 2; break; } @@ -195,14 +172,14 @@ public class SirAmikVarzeDialogue extends DialoguePlugin { end(); break; case 3: - interpreter.sendDialogues(npc, null, "Ok. Please don't break anything."); + interpreter.sendDialogues(npc, FacialExpression.HALF_WORRIED, "Ok. Please don't break anything."); stage = 4; break; case 4: end(); break; case 5: - interpreter.sendDialogues(npc, null, "Well, I need some spy work doing but it's quite", "dangerous. It will involve going into the Black Knights'", "fortress."); + interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "Well, I need some spy work doing but it's quite", "dangerous. It will involve going into the Black Knights'", "fortress."); stage = 6; break; case 6: @@ -212,25 +189,25 @@ public class SirAmikVarzeDialogue extends DialoguePlugin { case 7: switch (buttonId) { case 1: - interpreter.sendDialogues(player, null, "I laugh in the face of danger!"); + interpreter.sendDialogues(player, FacialExpression.HAPPY, "I laugh in the face of danger!"); stage = 15; break; case 2: - interpreter.sendDialogues(player, null, "I go and cower in a corner at the first sign of danger!"); + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I go and cower in a corner at the first sign of danger!"); stage = 8; break; } break; case 8: - interpreter.sendDialogues(npc, null, "Err...."); + interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "Err...."); stage = 9; break; case 9: - interpreter.sendDialogues(npc, null, "Well."); + interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "Well."); stage = 10; break; case 10: - interpreter.sendDialogues(npc, null, "I... suppose spy work DOES involve a little hiding in", "corners."); + interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "I... suppose spy work DOES involve a little hiding in", "corners."); stage = 11; break; case 11: @@ -240,68 +217,68 @@ public class SirAmikVarzeDialogue extends DialoguePlugin { case 12: switch (buttonId) { case 1: - interpreter.sendDialogues(player, null, "Oh. I suppose I'll give it a go then."); + interpreter.sendDialogues(player, FacialExpression.FRIENDLY, "Oh. I suppose I'll give it a go then."); stage = 17; break; case 2: - interpreter.sendDialogues(player, null, "No, I'm not ready to do that."); + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No, I'm not ready to do that."); stage = 13; break; } break; case 13: - interpreter.sendDialogues(npc, null, "Come see me again if you change your mind."); + interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "Come see me again if you change your mind."); stage = 14; break; case 14: end(); break; case 15: - interpreter.sendDialogues(npc, null, "Well that's good. Don't get too overconfident though."); + interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "Well that's good. Don't get too overconfident though."); stage = 16; break; case 16: - interpreter.sendDialogues(npc, null, "You've come along at just the right time actually. All of", "my knights are already known to the Black Knights."); + interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "You've come along at just the right time actually. All of", "my knights are already known to the Black Knights."); stage = 17; break; case 17: - interpreter.sendDialogues(npc, null, "Subtlety isn't exactly our strong point."); + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Subtlety isn't exactly our strong point."); stage = 18; break; case 18: - interpreter.sendDialogues(player, null, "Can't you just take your White Knights' armour off?", "They wouldn't recognise you then!"); + interpreter.sendDialogues(player, FacialExpression.HALF_ASKING, "Can't you just take your White Knights' armour off?", "They wouldn't recognise you then!"); stage = 19; break; case 19: - interpreter.sendDialogues(npc, null, "I am afraid our charter prevents us using espionage in", "any form, that is the domain of the Temple Knights."); + interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "I am afraid our charter prevents us using espionage in", "any form, that is the domain of the Temple Knights."); stage = 20; break; case 20: - interpreter.sendDialogues(player, null, "Temple Knights? Who are they?"); + interpreter.sendDialogues(player, FacialExpression.HALF_ASKING, "Temple Knights? Who are they?"); stage = 21; break; case 21: - interpreter.sendDialogues(npc, null, "The information is classified. I am forbidden to share it", "with outsiders."); + interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "The information is classified. I am forbidden to share it", "with outsiders."); stage = 22; break; case 22: - interpreter.sendDialogues(player, null, "So... What do you need doing?"); + interpreter.sendDialogues(player, FacialExpression.HALF_ASKING, "So... What do you need doing?"); stage = 23; break; case 23: - interpreter.sendDialogues(npc, null, "Well, the Black Knights have started making strange", "threats to us; demanding large amounts of money and", "land, and threatening to invade Falador if we don't pay", "them."); + interpreter.sendDialogues(npc, FacialExpression.ANGRY, "Well, the Black Knights have started making strange", "threats to us; demanding large amounts of money and", "land, and threatening to invade Falador if we don't pay", "them."); stage = 24; break; case 24: - interpreter.sendDialogues(npc, null, "Now, NORMALLY this wouldn't be a problem..."); + interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "Now, NORMALLY this wouldn't be a problem..."); stage = 25; break; case 25: - interpreter.sendDialogues(npc, null, "But they claim to have a powerful new secret weapon."); + interpreter.sendDialogues(npc, FacialExpression.ANGRY, "But they claim to have a powerful new secret weapon."); stage = 26; break; case 26: - interpreter.sendDialogues(npc, null, "Your mission, should you decide to accept it, is to", "infiltrate their fortress, find out what their secret", "weapon is, and then sabotage it."); + interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "Your mission, should you decide to accept it, is to", "infiltrate their fortress, find out what their secret", "weapon is, and then sabotage it."); stage = 27; break; case 27: @@ -311,24 +288,24 @@ public class SirAmikVarzeDialogue extends DialoguePlugin { case 28: switch (buttonId) { case 1: - interpreter.sendDialogues(player, null, "Ok, I'll do my best."); + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "Ok, I'll do my best."); stage = 31; break; case 2: - interpreter.sendDialogues(player, null, "No, I'm not ready to do that."); + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "No, I'm not ready to do that."); stage = 29; break; } break; case 29: - interpreter.sendDialogues(npc, null, "Come see me again if you change your mind."); + interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "Come see me again if you change your mind."); stage = 30; break; case 30: end(); break; case 31: - interpreter.sendDialogues(npc, null, "Good luck! Let me know how you get on. Here's the", "dossier for the case, I've already given you the details."); + interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "Good luck! Let me know how you get on. Here's the", "dossier for the case, I've already given you the details."); stage = 32; break; case 32: 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 new file mode 100644 index 000000000..3c34fca1f --- /dev/null +++ b/Server/src/main/content/region/asgarnia/falador/quest/doricsquest/DoricDoricsQuestDialogue.kt @@ -0,0 +1,79 @@ +package content.region.asgarnia.falador.quest.doricsquest + +import core.api.* +import core.game.dialogue.DialogueFile +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.tools.END_DIALOGUE +import org.rs09.consts.Items +import content.data.Quests + +class DoricDoricsQuestDialogue(private val dStage: Int) : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when(dStage) { + 10 -> handleQuestStartDialogue(player, false) + 20 -> handleQuestStartDialogue(player, true) + 30 -> handleGiveDoricItemsDialogue(player) + } + } + + private fun handleQuestStartDialogue(player: Player?, isWhetstone: Boolean) { + player ?: return + when(stage) { + 0 -> { + if(!isWhetstone) { + npcl(FacialExpression.OLD_NORMAL, "My anvils get enough work with my own use. I make pickaxes, and it takes a lot of hard work. If you could get me some more materials, then I could let you use them.") + stage = 30 + } else { + npcl(FacialExpression.OLD_NORMAL, "The whetstone is for more advanced smithing, but I could let you use it as well as my anvils if you could get me some more materials.") + stage = 30 + } + } + 30 -> showTopics( + Topic(FacialExpression.FRIENDLY, "Yes I will get you the materials.", 40), + Topic(FacialExpression.HALF_GUILTY, "No, hitting rocks is for the boring people, sorry.", 100) + ) + // Yes, this has to be npc, not npcl + 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, Quests.DORICS_QUEST) + if(!inInventory(player, Items.BRONZE_PICKAXE_1265)) addItemOrDrop(player, Items.BRONZE_PICKAXE_1265) + stage = END_DIALOGUE + } + 100 -> npcl(FacialExpression.OLD_NORMAL, "That is your choice. Nice to meet you anyway.").also { stage = END_DIALOGUE } + } + } + + private fun handleGiveDoricItemsDialogue(player: Player?) { + player ?: return + when(stage) { + 0 -> npcl(FacialExpression.OLD_NORMAL, "Have you got my materials yet, traveller?").also { stage++ } + 1 -> { + if(inInventory(player, Items.CLAY_434, 6) && inInventory(player, Items.COPPER_ORE_436, 4) && inInventory(player, Items.IRON_ORE_440, 2)) { + playerl(FacialExpression.HAPPY, "I have everything you need.") + stage++ + } else { + playerl(FacialExpression.HALF_GUILTY, "Sorry, I don't have them all yet.") + stage = 50 + } + } + 2 -> npcl("Many thanks. Pass them here, please. I can spare you some coins for your trouble, and please use my anvils any time you want.").also { stage++ } + 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, Quests.DORICS_QUEST) + stage = END_DIALOGUE + } + } + 50 -> npcl(FacialExpression.OLD_NORMAL, "Not to worry, stick at it. Remember, I need 6 clay, 4 copper ore, and 2 iron ore.").also { stage++ } + 51 -> showTopics( + Topic(FacialExpression.HALF_ASKING, "Where can I find those?", 52), + Topic(FacialExpression.HAPPY, "Certainly, I'll be right back.", END_DIALOGUE) + ) + 52 -> npcl(FacialExpression.OLD_NORMAL, "You'll be able to find all those ores in the rocks just inside the Dwarven Mine. Head east from here and you'll find the entrance in the side of Ice Mountain.").also { stage = END_DIALOGUE } + } + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..0bc49f173 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/falador/quest/doricsquest/DoricsQuest.kt @@ -0,0 +1,64 @@ +package content.region.asgarnia.falador.quest.doricsquest + +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 + +@Initializable +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 = 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) { + // 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) { + line(player, "I have spoken to !!Doric??.", line++, true) + line(player, "I have collected some Clay, Copper Ore, and Iron Ore.", line++, true) + line(player, "Doric rewarded me for all my hard work.", line++, true) + line(player, "I can now use Doric's Anvils whenever I want.", 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.STEEL_PICKAXE_1269) + drawReward(player, "1 Quest Point", line++) + drawReward(player, "1300 Mining XP", line++) + drawReward(player, "180 Coins", line++) + drawReward(player, "Use of Doric's Anvils", line++) + + rewardXP(player, Skills.MINING, 1300.0) + addItemOrDrop(player, Items.COINS_995, 180) + removeAttribute(player, "doric-angy-count") + } +} \ No newline at end of file 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/handlers/GoblinVillagePopulationListener.kt b/Server/src/main/content/region/asgarnia/goblinvillage/handlers/GoblinVillagePopulationListener.kt new file mode 100644 index 000000000..401d09d54 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/goblinvillage/handlers/GoblinVillagePopulationListener.kt @@ -0,0 +1,27 @@ +package content.region.asgarnia.goblinvillage.handlers + +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.combat.DeathTask +import core.game.world.map.RegionManager.getLocalNpcs +import org.rs09.consts.Scenery + +class GoblinVillagePopulationListener : InteractionListener{ + override fun defineListeners() { + on(Scenery.SIGNPOST_31301, IntType.SCENERY, "read") { player, node -> + var population = 3 // This covers General Wartface, General Bentnoze and Grubfoot + val npcs = getLocalNpcs(player, 50) // This radius covers more than the goblin village. + // There should be 18 Goblins walking around. + // 10 are within the village walls where the generals are. + // 8 of them are fighting each other on the west of the path to the village. + // Total should be 21. They respawn really fast. + for (n in npcs) { + if (n.name == "Goblin" && !DeathTask.isDead(n)) { + population++ + } + player.dialogueInterpreter.sendPlainMessage(false, "Welcome to Goblin Village.", "Current population: $population") + } + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/asgarnia/goblinvillage/handlers/GoblinVillagePopulationPlugin.java b/Server/src/main/content/region/asgarnia/goblinvillage/handlers/GoblinVillagePopulationPlugin.java deleted file mode 100644 index 2a1debd1d..000000000 --- a/Server/src/main/content/region/asgarnia/goblinvillage/handlers/GoblinVillagePopulationPlugin.java +++ /dev/null @@ -1,36 +0,0 @@ -package content.region.asgarnia.goblinvillage.handlers; - -import java.util.List; - -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.world.map.RegionManager; -import core.plugin.Initializable; -import core.plugin.Plugin; - -@Initializable -public class GoblinVillagePopulationPlugin extends OptionHandler { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - SceneryDefinition.forId(31301).getHandlers().put("option:read", this); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - int population = 2; - final List npcs = RegionManager.getLocalNpcs(player); - for (NPC n : npcs) { - if (n.getName().equals("Goblin")) { - population++; - player.getDialogueInterpreter().sendPlainMessage(false, "Welcome to Goblin Village.", "Current population: " + population); - } - } - return true; - } - -} 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 1753e0b41..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; @@ -27,6 +28,9 @@ import core.tools.RandomFunction; import core.tools.StringUtils; import core.game.dialogue.FacialExpression; +import static core.api.ContentAPIKt.*; + + /** * Represents the goblin diplomacy cutscene plugin. * @author 'Vexia @@ -51,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); @@ -179,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; @@ -364,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); - player.varpManager.get(465).setVarbit(0,7).send(player); - 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..a85d815cb 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 diff --git a/Server/src/main/content/region/asgarnia/goblinvillage/quest/goblindiplomacy/GrubFoot.java b/Server/src/main/content/region/asgarnia/goblinvillage/quest/goblindiplomacy/GrubFoot.java index 7881e61fc..af5178b10 100644 --- a/Server/src/main/content/region/asgarnia/goblinvillage/quest/goblindiplomacy/GrubFoot.java +++ b/Server/src/main/content/region/asgarnia/goblinvillage/quest/goblindiplomacy/GrubFoot.java @@ -3,6 +3,8 @@ package content.region.asgarnia.goblinvillage.quest.goblindiplomacy; import core.game.node.entity.player.Player; import core.game.node.item.Item; +import static core.api.ContentAPIKt.*; + /** * Represents a grub foot. * @author Vexia @@ -43,7 +45,7 @@ public enum GrubFoot { * @return the grub foot. */ public static GrubFoot forConfig(final Player player) { - int config = player.getConfigManager().get(62); + int config = getVarp(player, 62); for (GrubFoot foot : values()) { if (foot.getValue() == config) { if (foot.ordinal() + 1 >= values().length) { @@ -60,7 +62,7 @@ public enum GrubFoot { * @param player the player. */ public void setConfig(Player player) { - player.getConfigManager().set(62, value); + setVarp(player, 62, value); } /** 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 6cad8f839..32059ef91 100644 --- a/Server/src/main/content/region/asgarnia/handlers/CraftingGuildListeners.kt +++ b/Server/src/main/content/region/asgarnia/handlers/CraftingGuildListeners.kt @@ -1,32 +1,26 @@ 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 /** - * @author bushtail + * @author bushtail, Player Name */ class CraftingGuildListeners : InteractionListener { - private val GUILD_DOOR = Scenery.GUILD_DOOR_2647 - private val APRON = Items.BROWN_APRON_1757 - private val CAPE = Items.CRAFTING_CAPE_9780 - 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 (inEquipment(player, APRON)) { - openDialogue(player, TheDoorDialogues(0)) - core.game.global.action.DoorActionHandler.handleAutowalkDoor(player, door.asScenery()) - return@on true - } else if (inEquipment(player, CAPE)) { + 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 @@ -43,5 +37,10 @@ class CraftingGuildListeners : InteractionListener { return@on true } } + + on(NPCs.TANNER_804, IntType.NPC, "trade") { player, node -> + TanningProduct.open(player, node.id) + return@on true + } } } 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/BettyDialogue.java b/Server/src/main/content/region/asgarnia/portsarim/dialogue/BettyDialogue.java index 4f1196e7d..df1214c82 100644 --- a/Server/src/main/content/region/asgarnia/portsarim/dialogue/BettyDialogue.java +++ b/Server/src/main/content/region/asgarnia/portsarim/dialogue/BettyDialogue.java @@ -39,7 +39,7 @@ public final class BettyDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Welcome to the magic emporium."); + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Welcome to the magic emporium."); stage = 0; return true; } @@ -64,7 +64,7 @@ public final class BettyDialogue extends DialoguePlugin { } break; case 20: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Well, if you see ayone who is into Magic, please send", "them my way."); + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Well, if you see anyone who is into Magic, please send", "them my way."); stage = 21; break; case 21: 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 391b3e357..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.HALF_GUILTY, "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.HALF_GUILTY, "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/GrumDialogue.java b/Server/src/main/content/region/asgarnia/portsarim/dialogue/GrumDialogue.java index 1e50ce5e2..14d964cc2 100644 --- a/Server/src/main/content/region/asgarnia/portsarim/dialogue/GrumDialogue.java +++ b/Server/src/main/content/region/asgarnia/portsarim/dialogue/GrumDialogue.java @@ -39,7 +39,7 @@ public final class GrumDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Would you like to buy or sell some gold jewellery?"); + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Would you like to buy or sell some gold jewellery?"); stage = 0; return true; } @@ -65,7 +65,7 @@ public final class GrumDialogue extends DialoguePlugin { } break; case 20: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Get out, then! We don't want any riff-raff in here."); + interpreter.sendDialogues(npc, FacialExpression.ANNOYED, "Get out, then! We don't want any riff-raff in here."); stage = 21; break; case 21: 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 f12e1a60e..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,9 +52,9 @@ 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.HALF_GUILTY, "Hey, stay off my ship! That's private property!"); + interpreter.sendDialogues(npc, FacialExpression.ANGRY, "Hey, stay off my ship! That's private property!"); stage = 0; return true; } @@ -64,11 +65,11 @@ public final class KlarenseDialogue extends DialoguePlugin { break; default: if (player.getSavedData().getQuestData().getDragonSlayerAttribute("ship")) { - npc("Hello, captain! Here to inspect your new ship? Just a", "little work and she'll be seaworthy again."); + interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "Hello, captain! Here to inspect your new ship? Just a", "little work and she'll be seaworthy again."); stage = 400; return true; } - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "So, are you interested in buying the Lady Lumbridge?", "Now, I'll be straight with you: she's not quite seaworthy", "right now, but give her a bit of work and she'll be the", "nippiest ship this side of Port Khazard."); + interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "So, are you interested in buying the Lady Lumbridge?", "Now, I'll be straight with you: she's not quite seaworthy", "right now, but give her a bit of work and she'll be the", "nippiest ship this side of Port Khazard."); stage = 1; break; } @@ -88,15 +89,15 @@ public final class KlarenseDialogue extends DialoguePlugin { end(); break; case -1: - npc("No, it's not. It may, to the untrained eye, at first", "appear to be the Lady Lumbridge, but it is definitely", "not. It's my new ship. It just happens to look slightly", "similar, is all."); + interpreter.sendDialogues(npc, FacialExpression.SUSPICIOUS,"No, it's not. It may, to the untrained eye, at first", "appear to be the Lady Lumbridge, but it is definitely", "not. It's my new ship. It just happens to look slightly", "similar, is all."); stage = 1; break; case 1: - player("It has Lady Lumbridge pained out and 'Klarense's", "Cruiser' painted over it!"); + interpreter.sendDialogues(player, FacialExpression.ANNOYED,"It has Lady Lumbridge pained out and 'Klarense's", "Cruiser' painted over it!"); stage = 2; break; case 2: - npc("Nope, you're mistaken. It's my new boat."); + interpreter.sendDialogues(npc, FacialExpression.SUSPICIOUS,"Nope, you're mistaken. It's my new boat."); stage = 3; break; case 3: @@ -110,48 +111,48 @@ public final class KlarenseDialogue extends DialoguePlugin { end(); break; case 1: - player("Would you take me to Crandor when she's ready?"); + interpreter.sendDialogues(player, FacialExpression.ASKING, "Would you take me to Crandor when she's ready?"); stage = 2; break; case 2: - npc("Crandor? You're joking, right?"); + interpreter.sendDialogues(npc, FacialExpression.EXTREMELY_SHOCKED, "Crandor? You're joking, right?"); stage = 3; break; case 3: - player("No. I want to go to Crandor."); + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "No. I want to go to Crandor."); stage = 4; break; case 4: - npc("Then you must be crazy."); + interpreter.sendDialogues(npc, FacialExpression.WORRIED, "Then you must be crazy."); stage = 5; break; case 5: - npc("That island is surrounded by reefs that would rip this", "ship to shreds. Even if you found a map, you'd need", "an experienced captain to stand a chance of getting", "through"); + interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "That island is surrounded by reefs that would rip this", "ship to shreds. Even if you found a map, you'd need", "an experienced captain to stand a chance of getting", "through"); stage = 6; break; case 6: - npc("even if you gould get to it, there's no way I'm going", "any closer to that dragon than I have to. They say it", "can destroy whole ships with one bite."); + interpreter.sendDialogues(npc, FacialExpression.NEUTRAL,"even if you gould get to it, there's no way I'm going", "any closer to that dragon than I have to. They say it", "can destroy whole ships with one bite."); stage = 7; break; case 7: - player("I'd like to buy her."); + interpreter.sendDialogues(player, FacialExpression.NEUTRAL,"I'd like to buy her."); stage = 8; break; case 8: - npc("Of course! I'm sure the work needed to do on it", "wouldn't be too expensive."); + interpreter.sendDialogues(npc, FacialExpression.HAPPY,"Of course! I'm sure the work needed to do on it", "wouldn't be too expensive."); stage = 9; break; case 9: - npc("How does, 2,000 gold sound? I'll even throw in my", "cabin boy, Jenkiins, for free! He'll swab the decks and", "splice the mainsails for you!"); + interpreter.sendDialogues(npc, FacialExpression.HAPPY,"How does, 2,000 gold sound? I'll even throw in my", "cabin boy, Jenkins, for free! He'll swab the decks and", "splice the mainsails for you!"); stage = 10; break; case 10: - player("Yep, sounds good."); + interpreter.sendDialogues(player, FacialExpression.HAPPY,"Yep, sounds good."); stage = 11; break; case 11: if (!player.getInventory().containsItem(COINS)) { - player("...except I don't have that kind of money on me..."); + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY,"...except I don't have that kind of money on me..."); stage = 12; } else { if (!player.getInventory().containsItem(COINS)) { @@ -159,7 +160,7 @@ public final class KlarenseDialogue extends DialoguePlugin { return false; } if (player.getInventory().remove(COINS)) { - npc("Okey dokey, she's all yours!"); + interpreter.sendDialogues(npc, FacialExpression.HAPPY,"Okey dokey, she's all yours!"); player.getSavedData().getQuestData().setDragonSlayerAttribute("ship", true); stage = 12; } @@ -182,11 +183,11 @@ public final class KlarenseDialogue extends DialoguePlugin { case 2: switch (buttonId) { case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Do you know when she will be seaworthy?"); + interpreter.sendDialogues(player, FacialExpression.ASKING, "Do you know when she will be seaworthy?"); stage = 10; break; case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Why is she damaged?"); + interpreter.sendDialogues(player, FacialExpression.ASKING, "Why is she damaged?"); stage = 20; break; case 3: @@ -207,15 +208,15 @@ public final class KlarenseDialogue extends DialoguePlugin { stage = 21; break; case 21: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "The Lady Lumbridge is an old Crandorian fishing ship -", "the last one of her kind, as far as I know. That kind of", "ship was always mightily manoeuvrable, but not too", "tough."); + interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "The Lady Lumbridge is an old Crandorian fishing ship -", "the last one of her kind, as far as I know. That kind of", "ship was always mightily manoeuvrable, but not too", "tough."); stage = 22; break; case 22: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "She happened to be somewhere else when Crandor was", "destroyed, and she's had several owners since then. Not", "all of them have looked after her too well,"); + interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "She happened to be somewhere else when Crandor was", "destroyed, and she's had several owners since then. Not", "all of them have looked after her too well,"); stage = 23; break; case 23: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "but once she's patched up, she'll be good as new!"); + interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "but once she's patched up, she'll be good as new!"); stage = 24; break; case 24: 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 8b586b36f..e8c921159 100644 --- a/Server/src/main/content/region/asgarnia/portsarim/dialogue/MonkOfEntranaDialogue.java +++ b/Server/src/main/content/region/asgarnia/portsarim/dialogue/MonkOfEntranaDialogue.java @@ -9,6 +9,8 @@ import core.game.node.entity.player.link.diary.DiaryType; import core.plugin.Initializable; import core.game.node.entity.player.Player; +import static core.api.ContentAPIKt.*; + /** * Represents the dialogue plugin used for the monk of entrana dialogue. * @author 'Vexia @@ -39,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; } @@ -96,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: @@ -104,7 +111,7 @@ public final class MonkOfEntranaDialogue extends DialoguePlugin { break; case 25: end(); - Ships.PORT_SARIM_TO_ENTRANA.sail(player); + 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); } @@ -130,8 +137,7 @@ public final class MonkOfEntranaDialogue extends DialoguePlugin { stage = 511; break; case 511: - end(); - Ships.ENTRANA_TO_PORT_SARIM.sail(player); + sail(player, Ships.ENTRANA_TO_PORT_SARIM); break; case 520: end(); diff --git a/Server/src/main/content/region/asgarnia/portsarim/dialogue/PortSarimBartenderDialogue.java b/Server/src/main/content/region/asgarnia/portsarim/dialogue/PortSarimBartenderDialogue.java index dbae9fb67..a771cf3d1 100644 --- a/Server/src/main/content/region/asgarnia/portsarim/dialogue/PortSarimBartenderDialogue.java +++ b/Server/src/main/content/region/asgarnia/portsarim/dialogue/PortSarimBartenderDialogue.java @@ -32,7 +32,7 @@ public class PortSarimBartenderDialogue extends DialoguePlugin { switch (stage) { case 0: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Hello there!"); + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Hello there!"); stage = 1; break; case 1: @@ -42,21 +42,21 @@ public class PortSarimBartenderDialogue extends DialoguePlugin { case 2: switch (buttonId) { case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Could I buy a beer, please?"); + interpreter.sendDialogues(player, FacialExpression.HAPPY, "Could I buy a beer, please?"); stage = 10; break; case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Bye, then."); + interpreter.sendDialogues(player, FacialExpression.FRIENDLY, "Bye, then."); stage = 20; break; } break; case 10: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Sure, that will be two gold coins, please."); + interpreter.sendDialogues(npc, FacialExpression.FRIENDLY, "Sure, that will be two gold coins, please."); stage = 11; break; case 11: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Okay, here you go."); + interpreter.sendDialogues(player, FacialExpression.FRIENDLY, "Okay, here you go."); stage = 12; break; case 12: @@ -74,7 +74,7 @@ public class PortSarimBartenderDialogue extends DialoguePlugin { end(); break; case 20: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Come back soon!"); + interpreter.sendDialogues(npc, FacialExpression.FRIENDLY, "Come back soon!"); stage = 21; break; case 21: @@ -94,7 +94,7 @@ public class PortSarimBartenderDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Good day to you!"); + interpreter.sendDialogues(player, FacialExpression.FRIENDLY, "Good day to you!"); stage = 0; return true; } diff --git a/Server/src/main/content/region/asgarnia/portsarim/dialogue/PortSarimBrianDialogue.java b/Server/src/main/content/region/asgarnia/portsarim/dialogue/PortSarimBrianDialogue.java index 72ef74168..3065f02aa 100644 --- a/Server/src/main/content/region/asgarnia/portsarim/dialogue/PortSarimBrianDialogue.java +++ b/Server/src/main/content/region/asgarnia/portsarim/dialogue/PortSarimBrianDialogue.java @@ -32,17 +32,17 @@ public class PortSarimBrianDialogue extends DialoguePlugin { case 0: switch (buttonId) { case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "So, are you selling something?"); + interpreter.sendDialogues(player, FacialExpression.FRIENDLY, "So, are you selling something?"); stage = 10; break; case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "'Ello."); + interpreter.sendDialogues(player, FacialExpression.HAPPY, "'Ello."); stage = 20; break; } break; case 10: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Yep, take a look at these great axes."); + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Yep, take a look at these great axes."); stage = 11; break; case 11: @@ -50,7 +50,7 @@ public class PortSarimBrianDialogue extends DialoguePlugin { npc.openShop(player); break; case 20: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "'Ello."); + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "'Ello."); stage = 21; break; case 21: 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 b0653940d..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); @@ -378,7 +379,7 @@ public class RedbeardFrankDialogue extends DialoguePlugin { break; case 110: - npc("Ah, well it's a diary that helps you keep track of", "particular achievements you've made here on", "2009Scape."); + npc("Ah, well, it's a diary that helps you keep track of", "particular achievements you've made here on Gielinor."); stage = 111; break; case 111: diff --git a/Server/src/main/content/region/asgarnia/portsarim/dialogue/TakiDwarfDialogue.java b/Server/src/main/content/region/asgarnia/portsarim/dialogue/TakiDwarfDialogue.java index b55d6efd4..7df140e35 100644 --- a/Server/src/main/content/region/asgarnia/portsarim/dialogue/TakiDwarfDialogue.java +++ b/Server/src/main/content/region/asgarnia/portsarim/dialogue/TakiDwarfDialogue.java @@ -31,7 +31,7 @@ public class TakiDwarfDialogue extends DialoguePlugin { switch (stage) { case 0: - interpreter.sendDialogues(player, FacialExpression.OLD_NORMAL, "Hi little fellow."); + interpreter.sendDialogues(player, FacialExpression.HAPPY, "Hi little fellow."); stage = 1; break; case 1: @@ -39,7 +39,7 @@ public class TakiDwarfDialogue extends DialoguePlugin { stage = 2; break; case 2: - interpreter.sendDialogues(player, FacialExpression.OLD_NORMAL, "Arrr! nothing, nothing at all.."); + interpreter.sendDialogues(player, FacialExpression.GUILTY, "Arrr! nothing, nothing at all.."); stage = 3; break; case 3: diff --git a/Server/src/main/content/region/asgarnia/portsarim/dialogue/WydinDialogue.java b/Server/src/main/content/region/asgarnia/portsarim/dialogue/WydinDialogue.java index 2530bd98a..50b38cc95 100644 --- a/Server/src/main/content/region/asgarnia/portsarim/dialogue/WydinDialogue.java +++ b/Server/src/main/content/region/asgarnia/portsarim/dialogue/WydinDialogue.java @@ -46,7 +46,7 @@ public final class WydinDialogue extends DialoguePlugin { interpreter.sendDialogues(557, FacialExpression.HALF_GUILTY, "Can you put your white apron on before going in there", "please."); stage = 100; } else { - interpreter.sendDialogues(557, null, "Hey, you can't go in there. Only emplyees of the", "grocery store can go in."); + interpreter.sendDialogues(557, FacialExpression.ANGRY, "Hey, you can't go in there. Only employees of the", "grocery store can go in."); stage = 100; } return true; @@ -54,10 +54,10 @@ public final class WydinDialogue extends DialoguePlugin { npc = (NPC) args[0]; } if (player.getSavedData().getGlobalData().isWydinEmployee()) { - interpreter.sendDialogues(557, FacialExpression.HALF_GUILTY, "Is it nice and tidy round the back now?"); + interpreter.sendDialogues(557, FacialExpression.ASKING, "Is it nice and tidy round the back now?"); stage = 0; } else { - interpreter.sendDialogues(557, FacialExpression.HALF_GUILTY, "Welcome to my food store! Would you like to buy", "anything?"); + interpreter.sendDialogues(557, FacialExpression.HAPPY, "Welcome to my food store! Would you like to buy", "anything?"); stage = 0; } return true; @@ -74,11 +74,11 @@ public final class WydinDialogue extends DialoguePlugin { case 1: switch (buttonId) { case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Yes, can I work out front now?"); + interpreter.sendDialogues(player, FacialExpression.ASKING, "Yes, can I work out front now?"); stage = 10; break; case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Yes, are you going to pay me yet?"); + interpreter.sendDialogues(player, FacialExpression.ASKING, "Yes, are you going to pay me yet?"); stage = 20; break; case 3: @@ -93,14 +93,14 @@ public final class WydinDialogue extends DialoguePlugin { } break; case 10: - interpreter.sendDialogues(557, FacialExpression.HALF_GUILTY, "No, I'm the one who works here."); + interpreter.sendDialogues(557, FacialExpression.NEUTRAL, "No, I'm the one who works here."); stage = 11; break; case 11: end(); break; case 20: - interpreter.sendDialogues(557, FacialExpression.HALF_GUILTY, "Umm... No, not yet."); + interpreter.sendDialogues(557, FacialExpression.THINKING, "Umm... No, not yet."); stage = 21; break; case 21: @@ -111,14 +111,14 @@ public final class WydinDialogue extends DialoguePlugin { stage = 31; break; case 31: - interpreter.sendDialogues(557, FacialExpression.HALF_GUILTY, "Ah well, it'll give you something to do, won't it."); + interpreter.sendDialogues(557, FacialExpression.THINKING, "Ah well, it'll give you something to do, won't it."); stage = 32; break; case 32: end(); break; case 40: - interpreter.sendDialogues(557, FacialExpression.HALF_GUILTY, "Yes, ofcourse."); + interpreter.sendDialogues(557, FacialExpression.HAPPY, "Yes, ofcourse."); stage = 41; break; case 41: @@ -138,7 +138,7 @@ public final class WydinDialogue extends DialoguePlugin { case 1: switch (buttonId) { case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Yes please."); + interpreter.sendDialogues(player, FacialExpression.HAPPY, "Yes please."); stage = 10; break; case 2: @@ -146,11 +146,11 @@ public final class WydinDialogue extends DialoguePlugin { stage = 20; break; case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "What can you recommend?"); + interpreter.sendDialogues(player, FacialExpression.ASKING, "What can you recommend?"); stage = 30; break; case 4: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Can I get a job here?"); + interpreter.sendDialogues(player, FacialExpression.ASKING, "Can I get a job here?"); stage = 40; break; } @@ -163,7 +163,7 @@ public final class WydinDialogue extends DialoguePlugin { end(); break; case 30: - interpreter.sendDialogues(557, null, "We have this really exotic fruit all the way from", "Karamja. It's called a banana."); + interpreter.sendDialogues(557, FacialExpression.HAPPY, "We have this really exotic fruit all the way from", "Karamja. It's called a banana."); stage = 31; break; case 31: @@ -173,42 +173,42 @@ public final class WydinDialogue extends DialoguePlugin { case 32: switch (buttonId) { case 1: - interpreter.sendDialogues(player, null, "Hmm, I think I'll try one."); + interpreter.sendDialogues(player, FacialExpression.FRIENDLY, "Hmm, I think I'll try one."); stage = 10; break; case 2: - interpreter.sendDialogues(player, null, "I don't like the sound of that."); + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I don't like the sound of that."); stage = 100; break; } break; case 40: - interpreter.sendDialogues(557, null, "Well, you're keen, I'll give you that. Okay, I'll give you", "a go. Have you got your own white apron?"); + interpreter.sendDialogues(557, FacialExpression.HAPPY, "Well, you're keen, I'll give you that. Okay, I'll give you", "a go. Have you got your own white apron?"); stage = 41; break; case 41: if (!player.getInventory().contains(1005, 1) && !player.getEquipment().contains(1005, 1) && !player.getBank().contains(1005, 1)) { - interpreter.sendDialogues(player, null, "No, I haven't."); + interpreter.sendDialogues(player, FacialExpression.SAD, "No, I haven't."); stage = 42; } else { - interpreter.sendDialogues(player, null, "Yes, I have one."); + interpreter.sendDialogues(player, FacialExpression.HAPPY, "Yes, I have one."); stage = 50; } break; case 42: - interpreter.sendDialogues(557, null, "Well, you can't work here unless you have a white", "apron. Health and safety regulations, you understand."); + interpreter.sendDialogues(557, FacialExpression.FRIENDLY, "Well, you can't work here unless you have a white", "apron. Health and safety regulations, you understand."); stage = 43; break; case 43: - interpreter.sendDialogues(player, null, "Where can I get one of those?"); + interpreter.sendDialogues(player, FacialExpression.ASKING, "Where can I get one of those?"); stage = 44; break; case 44: - interpreter.sendDialogues(557, null, "Well, I get all of mine over at the clothing shop in", "Varrock. They sell them cheap there."); + interpreter.sendDialogues(557, FacialExpression.FRIENDLY, "Well, I get all of mine over at the clothing shop in", "Varrock. They sell them cheap there."); stage = 45; break; case 45: - interpreter.sendDialogues(557, null, "Oh, and I'm sure that I've seen a spare one over in", "Gerrant's fish store somewhere. It's the little place just", "north of here."); + interpreter.sendDialogues(557, FacialExpression.FRIENDLY, "Oh, and I'm sure that I've seen a spare one over in", "Gerrant's fish store somewhere. It's the little place just", "north of here."); stage = 46; break; case 46: @@ -216,7 +216,7 @@ public final class WydinDialogue extends DialoguePlugin { break; case 50: player.getSavedData().getGlobalData().setWydinEmployee(true); - interpreter.sendDialogues(557, null, "Wow - you are well prepared! You're hired. Go through", "to the back and tidy up for me, please."); + interpreter.sendDialogues(557, FacialExpression.HAPPY, "Wow - you are well prepared! You're hired. Go through", "to the back and tidy up for me, please."); stage = 100; break; case 100: diff --git a/Server/src/main/content/region/asgarnia/portsarim/handlers/ElementalWizardNPC.java b/Server/src/main/content/region/asgarnia/portsarim/handlers/ElementalWizardNPC.java index e4d1281fc..c00c9dcc9 100644 --- a/Server/src/main/content/region/asgarnia/portsarim/handlers/ElementalWizardNPC.java +++ b/Server/src/main/content/region/asgarnia/portsarim/handlers/ElementalWizardNPC.java @@ -1,5 +1,6 @@ package content.region.asgarnia.portsarim.handlers; +import core.ServerConstants; import core.game.node.entity.player.link.diary.DiaryType; import core.plugin.Initializable; import core.game.dialogue.DialoguePlugin; @@ -245,7 +246,7 @@ public final class ElementalWizardNPC extends AbstractNPC { stage = 21; break; case 21: - npc("Many say it was the greatest building in the history of", "2009scape, a magnificent monument to human ingenuity."); + npc("Many say it was the greatest building in the history of", ServerConstants.SERVER_NAME + ", a magnificent monument to human ingenuity."); stage = 22; break; case 22: 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/handlers/SeamanListener.kt b/Server/src/main/content/region/asgarnia/portsarim/handlers/SeamanListener.kt new file mode 100644 index 000000000..104d86c3b --- /dev/null +++ b/Server/src/main/content/region/asgarnia/portsarim/handlers/SeamanListener.kt @@ -0,0 +1,15 @@ +package content.region.asgarnia.portsarim.handlers + +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.npc.NPC + +class SeamanListener : InteractionListener { + override fun defineListeners() { + on(IntType.NPC, "pay-fare") { player, node -> + val npc = node as NPC + player.dialogueInterpreter.open(npc.id, npc, true) + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/asgarnia/portsarim/handlers/SeamanPlugin.java b/Server/src/main/content/region/asgarnia/portsarim/handlers/SeamanPlugin.java deleted file mode 100644 index e2c6ed29f..000000000 --- a/Server/src/main/content/region/asgarnia/portsarim/handlers/SeamanPlugin.java +++ /dev/null @@ -1,31 +0,0 @@ -package content.region.asgarnia.portsarim.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.plugin.Initializable; -import core.plugin.Plugin; - -/** - * Represents a plugin used to handle the pay fare option. - * @author 'Vexia - */ -@Initializable -public class SeamanPlugin extends OptionHandler { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - NPCDefinition.setOptionHandler("pay-fare", this); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - final NPC npc = ((NPC) node); - player.getDialogueInterpreter().open(npc.getId(), npc, true); - 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 71c8ddf82..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.HALF_GUILTY, "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.HALF_GUILTY, "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 28e99f079..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.HALF_GUILTY, "How's your magic coming along?"); - stage = 0; - } - switch (quest.getStage(player)) { - case 0: - interpreter.sendDialogues(307, FacialExpression.HALF_GUILTY, "What could you want with an old woman like me?"); - stage = 11; - break; - case 20: - interpreter.sendDialogues(307, FacialExpression.HALF_GUILTY, "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.HALF_GUILTY, "I am in search of a quest."); - stage = 13; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I've heard that you are a witch."); - stage = 20; - break; - } - break; - case 13: - interpreter.sendDialogues(307, FacialExpression.HALF_GUILTY, "Hmmm... Maybe I can think of something for you."); - stage = 14; - break; - case 14: - interpreter.sendDialogues(307, FacialExpression.HALF_GUILTY, "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.HALF_GUILTY, "Yes help me become one with my darker side."); - stage = 30; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No I have m 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.HALF_GUILTY, "Ok I'm going to make a potion to help bring out your", "darker self."); - stage = 31; - break; - case 31: - interpreter.sendDialogues(307, FacialExpression.HALF_GUILTY, "You will need certain ingredients."); - stage = 32; - break; - case 32: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "What do I need?"); - stage = 33; - break; - case 33: - interpreter.sendDialogues(307, FacialExpression.HALF_GUILTY, "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.HALF_GUILTY, "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.HALF_GUILTY, "Yes I have everything!"); - stage = 110; - } - break; - case 110: - interpreter.sendDialogues(307, FacialExpression.HALF_GUILTY, "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.HALF_GUILTY, "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.HALF_GUILTY, "Ok, now drink from the cauldron."); - stage = 114; - } - break; - case 114: - end(); - break; - case 101: - interpreter.sendDialogues(307, FacialExpression.HALF_GUILTY, "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.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 2660e954a..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 euipment I need."); - stage = 20; - break; - - } - break; - case 20: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "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.HALF_GUILTY, "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 c55e827d6..000000000 --- a/Server/src/main/content/region/asgarnia/rimmington/quest/witchpotion/WitchsPotion.java +++ /dev/null @@ -1,91 +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.getConfigManager().set(101, player.getQuestRepository().getPoints()); - 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/BernaldDialogue.java b/Server/src/main/content/region/asgarnia/taverley/dialogue/BernaldDialogue.java deleted file mode 100644 index de5da48e5..000000000 --- a/Server/src/main/content/region/asgarnia/taverley/dialogue/BernaldDialogue.java +++ /dev/null @@ -1,64 +0,0 @@ -package content.region.asgarnia.taverley.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 dialogue plugin used for the bernald npc. - * @author 'Vexia - * @version 1.1 - */ -@Initializable -public final class BernaldDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code BernaldDialogue} {@code Object}. - */ - public BernaldDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code BernaldDialogue} {@code Object}. - * @param player the player. - */ - public BernaldDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new BernaldDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - player("How are your grapes coming along?"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - npc("Marvellous, thanks to your help, " + player.getUsername() + "!"); - stage = 1; - break; - case 1: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 2580 }; - } -} diff --git a/Server/src/main/content/region/asgarnia/taverley/dialogue/JatixDialogue.java b/Server/src/main/content/region/asgarnia/taverley/dialogue/JatixDialogue.java index ecb9e7c11..27702f723 100644 --- a/Server/src/main/content/region/asgarnia/taverley/dialogue/JatixDialogue.java +++ b/Server/src/main/content/region/asgarnia/taverley/dialogue/JatixDialogue.java @@ -39,7 +39,7 @@ public final class JatixDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Hello, adventurer."); + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Hello, adventurer."); stage = 0; return true; } @@ -48,11 +48,11 @@ public final class JatixDialogue extends DialoguePlugin { public boolean handle(int interfaceId, int buttonId) { switch (stage) { case 0: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Hello."); + interpreter.sendDialogues(player, FacialExpression.FRIENDLY, "Hello."); stage = 1; break; case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "What are you selling?"); + interpreter.sendDialogues(player, FacialExpression.FRIENDLY, "What are you selling?"); stage = 2; break; case 2: 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 f6246e77b..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. @@ -46,7 +47,7 @@ public final class KaqemeexDialogue extends DialoguePlugin { stage = 1000; return true; } - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Hello there."); + interpreter.sendDialogues(player, FacialExpression.FRIENDLY, "Hello there."); stage = 0; return true; } @@ -55,26 +56,26 @@ 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) { - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Hello again."); + 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) { - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "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."); + 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; } - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "What brings you to our holy monument?"); + interpreter.sendDialogues(npc, FacialExpression.FRIENDLY, "What brings you to our holy monument?"); 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; @@ -91,48 +92,48 @@ public final class KaqemeexDialogue extends DialoguePlugin { case 2: switch (buttonId) { case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Who are you?"); + interpreter.sendDialogues(player, FacialExpression.ASKING, "Who are you?"); stage = 10; break; case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I'm in search of a quest."); + interpreter.sendDialogues(player, FacialExpression.FRIENDLY, "I'm in search of a quest."); stage = 20; break; case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Did you build this?"); + interpreter.sendDialogues(player, FacialExpression.ASKING, "Did you build this?"); stage = 30; break; } break; case 10: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "We are the druids of Guthix. We worship our god at", "our famous stone circles. You will find them located", "throghout these lands."); + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "We are the druids of Guthix. We worship our god at", "our famous stone circles. You will find them located", "throughout these lands."); stage = 11; break; case 11: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Well, I'll be on my way now."); + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "Well, I'll be on my way now."); stage = 12; break; case 12: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Goodbye adventurer. I feel we shall meet again."); + interpreter.sendDialogues(npc, FacialExpression.FRIENDLY, "Goodbye adventurer. I feel we shall meet again."); stage = 13; break; case 13: end(); break; case 30: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "What, personally? No, ofcoure I didn't. However, our", "four fathers did. The first Druids of Guthix built many", "stone circles across these lands over eight hundred", "years ago."); + interpreter.sendDialogues(npc, FacialExpression.FRIENDLY, "What, personally? No, ofcourse I didn't. However, our", "four fathers did. The first Druids of Guthix built many", "stone circles across these lands over eight hundred", "years ago."); stage = 31; break; case 31: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Unfortunately we only know of two remaining and of", "those only one is usuable by us anymore."); + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Unfortunately we only know of two remaining and of", "those only one is usable by us anymore."); stage = 32; break; case 32: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Well, I'll be on my way now."); + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "Well, I'll be on my way now."); stage = 12; break; case 20: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Hmm. I think I may have a worthwhile quest for you", "actually. I don't know if you are familiar with the stone", "circle south of Varrock or not, but..."); + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Hmm. I think I may have a worthwhile quest for you", "actually. I don't know if you are familiar with the stone", "circle south of Varrock or not, but..."); stage = 21; break; case 21: @@ -140,7 +141,7 @@ public final class KaqemeexDialogue extends DialoguePlugin { stage = 22; break; case 22: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "When they cursed the rocks for their rituals they made", "them useless to us and our magics. We require a brave", "adevnturer to go on a quest for us to help purify the", "circle of Varrock."); + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "When they cursed the rocks for their rituals, they made", "them useless to us and our magics. We require a brave", "adventurer to go on a quest for us to help purify the", "circle of Varrock."); stage = 23; break; case 23: @@ -164,12 +165,12 @@ public final class KaqemeexDialogue extends DialoguePlugin { stage = 13; break; case 26: - player.getQuestRepository().getQuest("Druidic Ritual").start(player); - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "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."); + 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; case 27: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Will do."); + interpreter.sendDialogues(player, FacialExpression.HAPPY, "Will do."); stage = 28; break; case 28: @@ -184,16 +185,16 @@ 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) { case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Who are you?"); + interpreter.sendDialogues(player, FacialExpression.HALF_ASKING, "Who are you?"); stage = 10; break; case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Did you build this?"); + interpreter.sendDialogues(player, FacialExpression.HALF_ASKING, "Did you build this?"); stage = 30; break; } @@ -201,15 +202,15 @@ public final class KaqemeexDialogue extends DialoguePlugin { case 501: switch (buttonId) { case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Can I buy a Skillcape of Herblore?"); + interpreter.sendDialogues(player, FacialExpression.HAPPY, "Can I buy a Skillcape of Herblore?"); stage = 800; break; case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Who are you?"); + interpreter.sendDialogues(player, FacialExpression.ASKING, "Who are you?"); stage = 10; break; case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Did you build this?"); + interpreter.sendDialogues(player, FacialExpression.HALF_ASKING, "Did you build this?"); stage = 30; break; } diff --git a/Server/src/main/content/region/asgarnia/taverley/dialogue/PetshopOwnerTaverly.java b/Server/src/main/content/region/asgarnia/taverley/dialogue/PetshopOwnerTaverly.java index 23c271d76..39f24867c 100644 --- a/Server/src/main/content/region/asgarnia/taverley/dialogue/PetshopOwnerTaverly.java +++ b/Server/src/main/content/region/asgarnia/taverley/dialogue/PetshopOwnerTaverly.java @@ -78,15 +78,15 @@ public final class PetshopOwnerTaverly extends DialoguePlugin { case 1: switch (buttonId) { case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Can I see your shop please?"); + interpreter.sendDialogues(player, FacialExpression.HAPPY, "Can I see your shop please?"); stage = 100; break; case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "How much is that puppy in the window?"); + interpreter.sendDialogues(player, FacialExpression.HALF_ASKING, "How much is that puppy in the window?"); stage = 200; break; case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "So, what sorts of pets are available?"); + interpreter.sendDialogues(player, FacialExpression.HALF_ASKING, "So, what sorts of pets are available?"); stage = 300; break; } @@ -96,7 +96,7 @@ public final class PetshopOwnerTaverly extends DialoguePlugin { npc.openShop(player); break; case 200: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "The one with the waggly tail?"); + interpreter.sendDialogues(npc, FacialExpression.HALF_ASKING, "The one with the waggly tail?"); stage = 201; break; case 201: @@ -107,31 +107,31 @@ public final class PetshopOwnerTaverly extends DialoguePlugin { case 202: break; case 300: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Well, here we sell dogs, but we also have supplies for any", "other creatures you might want to raise."); + interpreter.sendDialogues(npc, FacialExpression.FRIENDLY, "Well, here we sell dogs, but we also have supplies for any", "other creatures you might want to raise."); stage = 301; break; case 301: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Such as?"); + interpreter.sendDialogues(player, FacialExpression.HALF_ASKING, "Such as?"); stage = 302; break; case 302: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Well, we sell nuts. Those can be used to feed squirrels. If", "you want to capture a squirrel, you'll need to use the nuts", "on the trap you set, as the little scamps won't be fooled", "by anything else."); + interpreter.sendDialogues(npc, FacialExpression.FRIENDLY, "Well, we sell nuts. Those can be used to feed squirrels. If", "you want to capture a squirrel, you'll need to use the nuts", "on the trap you set, as the little scamps won't be fooled", "by anything else."); stage = 303; break; case 303: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I'll bear that in mind."); + interpreter.sendDialogues(player, FacialExpression.FRIENDLY, "I'll bear that in mind."); stage = 304; break; case 304: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "There are also a number of fabulous and exotic lizards in", "Karmja. Some can be caught easily in a box trap, while", "other will need to be raised from an egg."); + interpreter.sendDialogues(npc, FacialExpression.FRIENDLY, "There are also a number of fabulous and exotic lizards in", "Karmja. Some can be caught easily in a box trap, while", "other will need to be raised from an egg."); stage = 305; break; case 305: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Thank's alot! You've been very helpfull."); + interpreter.sendDialogues(player, FacialExpression.HAPPY, "Thank's alot! You've been very helpfull."); stage = 306; break; case 306: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "It's always a pleasure to help a fellow animal-lover. Come", "back and visit soon."); + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "It's always a pleasure to help a fellow animal-lover. Come", "back and visit soon."); stage = 307; break; case 307: 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 672af05bc..f953d2783 100644 --- a/Server/src/main/content/region/asgarnia/taverley/dialogue/PikkupstixDialogue.java +++ b/Server/src/main/content/region/asgarnia/taverley/dialogue/PikkupstixDialogue.java @@ -1,5 +1,6 @@ package content.region.asgarnia.taverley.dialogue; +import core.ServerConstants; import core.game.dialogue.DialoguePlugin; import core.game.dialogue.FacialExpression; import core.game.node.entity.skill.Skills; @@ -9,6 +10,9 @@ import core.game.node.entity.player.link.quest.Quest; import core.plugin.Initializable; import core.game.node.item.Item; +import static core.api.ContentAPIKt.*; +import content.data.Quests; + /** * Handles the PikkupstixDialogue dialogue. * @author Vexia @@ -82,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?"); @@ -125,7 +129,7 @@ public final class PikkupstixDialogue extends DialoguePlugin { continue; } if (!player.getInventory().containsItem(i) && !player.getBank().containsItem(i) && player.getAttribute("taken-summoning-supplies") == null) { - if (i.getId() == 12528 && player.getConfigManager().get(1178) == (2 << 11)) { + if (i.getId() == 12528 && getVarp(player, 1178) == (2 << 11)) { continue; } player.setAttribute("taken-summoning-supplies", true); @@ -696,7 +700,7 @@ public final class PikkupstixDialogue extends DialoguePlugin { stage = 104; break; case 104: - npc("This is reflected in your increased ability to raise animals", "as pets. It takes a skilled summoner to be able to raise", "some of 2009scape's more exotic animals, such as the lizards", "of Karamja, or even dragons!"); + npc("This is reflected in your increased ability to raise animals", "as pets. It takes a skilled summoner to be able to raise", "some of " + ServerConstants.SERVER_NAME + "'s more exotic animals, such as the lizards", "of Karamja, or even dragons!"); stage = 105; break; case 105: 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 4f92cd641..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) { - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Did you bring me the required ingredients for the", "potion?"); + 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; @@ -63,13 +64,13 @@ public class SanfewDialogue extends DialoguePlugin { stage = 5; break; case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Actually, I don't need to speak to you."); + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "Actually, I don't need to speak to you."); stage = 3; break; } break; case 3: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Well, we all make mistakes sometimes."); + interpreter.sendDialogues(npc, FacialExpression.FRIENDLY, "Well, we all make mistakes sometimes."); stage = 4; break; case 4: @@ -84,12 +85,12 @@ public class SanfewDialogue extends DialoguePlugin { stage = 7; break; case 7: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Where can I find this cauldron?"); + interpreter.sendDialogues(player, FacialExpression.ASKING, "Where can I find this cauldron?"); stage = 8; 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: @@ -120,7 +121,7 @@ public class SanfewDialogue extends DialoguePlugin { end(); break; case 200: - interpreter.sendDialogues(npc, null, "Well hand 'em over then lad!"); + interpreter.sendDialogues(npc, null, "Well hand 'em over then " + (player.isMale() ? "lad" : "lass") + "!"); stage = 201; break; case 201: @@ -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/TostigDialogue.java b/Server/src/main/content/region/asgarnia/taverley/dialogue/TostigDialogue.java deleted file mode 100644 index 1718d0ea8..000000000 --- a/Server/src/main/content/region/asgarnia/taverley/dialogue/TostigDialogue.java +++ /dev/null @@ -1,92 +0,0 @@ -package content.region.asgarnia.taverley.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 TostigDialogue dialogue. - * @author 'Vexia - */ -@Initializable -public class TostigDialogue extends DialoguePlugin { - - public TostigDialogue() { - - } - - public TostigDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - - return new TostigDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Hi, what ales are you serving?"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Well sir, our speciality is Asgarnian Ale, we also serve", "Wizard's Mind Bomb and Dwarven Stout."); - stage = 1; - break; - case 1: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Would you like to buy a drink?"); - stage = 2; - break; - case 2: - interpreter.sendOptions("Select An Option", "Yes, please.", "No, thanks."); - stage = 3; - break; - case 3: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Yes, please."); - stage = 100; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No, thanks."); - stage = 4; - break; - } - break; - case 4: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Ah well... so um... does the grey squirrel sing in the", "grove?"); - stage = 5; - break; - case 5: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Huh?"); - stage = 6; - break; - case 6: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Er... nevermind."); - stage = 7; - break; - case 7: - end(); - break; - case 100: - end(); - npc.openShop(player); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 1079 }; - } -} 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 fc56ccb5a..b6ea3c4f3 100644 --- a/Server/src/main/content/region/asgarnia/taverley/dialogue/VelrakDialogue.kt +++ b/Server/src/main/content/region/asgarnia/taverley/dialogue/VelrakDialogue.kt @@ -11,17 +11,16 @@ import org.rs09.consts.NPCs import core.tools.END_DIALOGUE @Initializable -class VelrakDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { +class VelrakDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player?): DialoguePlugin { return VelrakDialogue(player) } override fun open(vararg args: Any?): Boolean { if(inInventory(player, Items.DUSTY_KEY_1590)){ - playerl(core.game.dialogue.FacialExpression.HALF_THINKING, "Are you still here?").also { stage = 100 } + playerl(FacialExpression.HALF_THINKING, "Are you still here?").also { stage = 100 } } else { - npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Thank you for rescuing me! It isn't very comfy in this cell.") - removeItem(player, Items.JAIL_KEY_1591, Container.INVENTORY) + npcl(FacialExpression.FRIENDLY, "Thank you for rescuing me! It isn't very comfy in this cell.") } return true } @@ -30,26 +29,26 @@ class VelrakDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin when(stage){ 0 -> options("So... do you know anywhere good to explore?","Do I get a reward for freeing you?").also { stage++ } 1 -> when(buttonId){ - 1 -> playerl(core.game.dialogue.FacialExpression.HALF_THINKING, "So... do you know anywhere good to explore?").also { stage = 10 } - 2 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY, "Do I get a reward for freeing you?").also { stage = 20 } + 1 -> playerl(FacialExpression.HALF_THINKING, "So... do you know anywhere good to explore?").also { stage = 10 } + 2 -> playerl(FacialExpression.FRIENDLY, "Do I get a reward for freeing you?").also { stage = 20 } } - 10 -> npcl(core.game.dialogue.FacialExpression.HALF_THINKING, "Well, this dungeon was quite good to explore...until I got captured, anyway. I was given a key to an inner part of this dungeon by a mysterious stranger!").also { stage++ } - 11 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "It's rather tough for me to get that far into the dungeon however. I just keep getting captured! Would you like to give it a go?").also { stage++ } + 10 -> npcl(FacialExpression.HALF_THINKING, "Well, this dungeon was quite good to explore...until I got captured, anyway. I was given a key to an inner part of this dungeon by a mysterious stranger!").also { stage++ } + 11 -> npcl(FacialExpression.NEUTRAL, "It's rather tough for me to get that far into the dungeon however. I just keep getting captured! Would you like to give it a go?").also { stage++ } 12 -> options("Yes, please!", "No, it's too dangerous for me too.").also { stage++ } 13 -> when(buttonId){ - 1 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY, "Yes, please!").also { stage++ } - 2 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL, "No, it's too dangerous for me too.").also { stage = 15 } + 1 -> playerl(FacialExpression.FRIENDLY, "Yes, please!").also { stage++ } + 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 } - 15 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "I don't blame you!").also { stage = END_DIALOGUE } + 15 -> npcl(FacialExpression.FRIENDLY, "I don't blame you!").also { stage = END_DIALOGUE } - 20 -> npcl(core.game.dialogue.FacialExpression.HALF_GUILTY, "Well, not really. The Black Knights took all of my stuff before throwing me in here to rot!").also { stage = 0 } + 20 -> npcl(FacialExpression.HALF_GUILTY, "Well, not really. The Black Knights took all of my stuff before throwing me in here to rot!").also { stage = 0 } - 100 -> npcl(core.game.dialogue.FacialExpression.HALF_THINKING, "Yes... I'm still plucking up the courage to run out past those Black Knights.").also { stage++ } - 101 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY, "Oh, go on. You can do it!").also { stage = END_DIALOGUE } + 100 -> npcl(FacialExpression.HALF_THINKING, "Yes... I'm still plucking up the courage to run out past those Black Knights.").also { stage++ } + 101 -> playerl(FacialExpression.FRIENDLY, "Oh, go on. You can do it!").also { stage = END_DIALOGUE } } return true diff --git a/Server/src/main/content/region/asgarnia/taverley/handlers/CrystalChestPlugin.java b/Server/src/main/content/region/asgarnia/taverley/handlers/CrystalChestPlugin.java index d4eb1b2be..59fd7beb8 100644 --- a/Server/src/main/content/region/asgarnia/taverley/handlers/CrystalChestPlugin.java +++ b/Server/src/main/content/region/asgarnia/taverley/handlers/CrystalChestPlugin.java @@ -79,7 +79,7 @@ public final class CrystalChestPlugin extends UseWithHandler { player.getInventory().add(i, player); } player.getPacketDispatch().sendMessage("You unlock the chest with your key."); - player.getPacketDispatch().sendMessage("You find some teasure in the chest!"); + player.getPacketDispatch().sendMessage("You find some treasure in the chest!"); } return true; } 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 fe470eef7..000000000 --- a/Server/src/main/content/region/asgarnia/taverley/quest/DruidicRitual.java +++ /dev/null @@ -1,82 +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("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.getPacketDispatch().sendItemZoomOnInterface(249, 240, 277, 3 + 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 6ed7c9283..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,6 +5,11 @@ 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; + /** * Represents the demon slayer quest. @@ -22,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); } } @@ -75,7 +170,7 @@ public class WolfWhistle extends Quest { @Override public int[] getConfig(Player player, int stage) { - int val = player.getConfigManager().get(1178); + int val = getVarp(player, 1178); boolean open = val >= 4096; boolean closed = val == 2048; if (stage == 100) { 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/WitchesDiaryBook.kt b/Server/src/main/content/region/asgarnia/taverley/quest/witchshouse/WitchesDiaryBook.kt new file mode 100644 index 000000000..8417d0cca --- /dev/null +++ b/Server/src/main/content/region/asgarnia/taverley/quest/witchshouse/WitchesDiaryBook.kt @@ -0,0 +1,137 @@ +package content.region.asgarnia.taverley.quest.witchshouse + +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 + +/** + * Witches' Diary Book + * https://www.youtube.com/watch?v=rLsLPVOjNCY + * @author ovenbreado + * @author Ethan Kyle Millard + */ +class WitchesDiaryBook : InteractionListener { + companion object { + private val TITLE = "Witches' Diary" + private val CONTENTS = arrayOf( + PageSet( + Page( + BookLine("2nd of Pentember", 55), + BookLine("Experiment is growing", 56), + BookLine("larger daily. Making", 57), + BookLine("excellent progress now. I", 58), + BookLine("am currently feeding it", 59), + BookLine("on a mixture of fungus,", 60), + BookLine("tar and clay.", 61), + BookLine("It seems to like this", 62), + BookLine("combination a lot!", 63) + ), + Page( + BookLine("3rd of Pentember", 66), + BookLine("Experiment still going", 67), + BookLine("extremely well. Moved it", 68), + BookLine("to the wooden garden", 69), + BookLine("shed; it does too much", 70), + BookLine("damage in the house! it", 71), + BookLine("is getting very strong", 72), + BookLine("now, but unfortunately is", 73), + BookLine("not too intelligent yet. It", 74), + BookLine("has a really mean stare", 75), + BookLine("too!", 76) + ) + ), + PageSet( + Page( + BookLine("4th of Pentember", 55), + BookLine("Sausages for dinner", 56), + BookLine("tonight! Lovely!", 57), + BookLine("5th of Pentember", 59), + BookLine("A guy called Professor", 60), + BookLine("Oddenstein installed a", 61), + BookLine("new security system for", 62), + BookLine("me in the basement. He", 63), + BookLine("seems to have a lot of", 64), + BookLine("good security ideas.", 65) + ), + Page( + BookLine("6th of Pentember", 66), + BookLine("Don't want people getting", 67), + BookLine("into back garden to see", 68), + BookLine("the experiment. Professor", 69), + BookLine("Oddenstein is fitting me a", 70), + BookLine("new security system,", 71), + BookLine("after his successful", 72), + BookLine("installation in the cellar.", 73) + ) + ), + PageSet( + Page( + BookLine("7th of Pentember", 55), + BookLine("That pesky kid keeps", 56), + BookLine("kicking his ball into my", 57), + BookLine("garden. I swear, if he", 58), + BookLine("does it AGAIN, I'm going", 59), + BookLine("to lock his ball away in", 60), + BookLine("the shed.", 61), + BookLine("8th of Pentember.", 63), + BookLine("The security system is", 64), + BookLine("done. By Zamorak! Wow,", 65) + ), + Page( + BookLine("is it contrived! Now, to", 66), + BookLine("open my own back door,", 67), + BookLine("I lure a mouse out of a", 68), + BookLine("hole in the back porch, I", 69), + BookLine("fit a magic curved piece", 70), + BookLine("of metal to the harness", 71), + BookLine("on its back, the mouse", 72), + BookLine("goes back in the hole, and", 73), + BookLine("the door unlocks! The", 74), + BookLine("prof tells me that this is", 75), + BookLine("cutting edge technology!", 76) + ) + ), + PageSet( + Page( + BookLine("As an added precaution I", 55), + BookLine("have hidden the key to", 56), + BookLine("the shed in a secret", 57), + BookLine("compartment of the", 58), + BookLine("fountain in the garden.", 59), + BookLine("No one will ever look", 60), + BookLine("there.", 61), + BookLine("9th of Pentember", 63), + BookLine("Still can't think of a good", 64), + BookLine("name of 'The", 65) + ), + Page( + BookLine("Experiment'. Leaning", 66), + BookLine("Towards 'fritz'... Although", 67), + BookLine("am considering Lucy as", 68), + BookLine("it reminds me of my", 69), + BookLine("Mother!", 70) + ) + ) + ) + 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)) { + setAttribute(player, "/save:readWitchsBook", true) + } + return true + } + } + + override fun defineListeners() { + on(Items.DIARY_2408, 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/asgarnia/taverley/quest/witchshouse/WitchsDiaryBook.java b/Server/src/main/content/region/asgarnia/taverley/quest/witchshouse/WitchsDiaryBook.java deleted file mode 100644 index d80375dbf..000000000 --- a/Server/src/main/content/region/asgarnia/taverley/quest/witchshouse/WitchsDiaryBook.java +++ /dev/null @@ -1,163 +0,0 @@ -package content.region.asgarnia.taverley.quest.witchshouse; - -import core.plugin.Initializable; -import core.game.dialogue.DialoguePlugin; -import core.game.dialogue.book.Book; -import core.game.dialogue.book.BookLine; -import core.game.dialogue.book.Page; -import core.game.dialogue.book.PageSet; -import core.game.node.entity.player.Player; - -/** - * Created for 2009Scape - * User: Ethan Kyle Millard - * Date: March 30, 2020 - * Time: 2:49 PM - */ -@Initializable -public class WitchsDiaryBook extends Book { - - /** - * Represents the shield of arrav book id. - */ - public static int ID = 4501993; - - /** - * Represents the array of pages for this book. - */ - private static final PageSet[] PAGES = new PageSet[]{ - new PageSet( - new Page(new BookLine("2nd of Pentember", 55), - new BookLine("Experiment is growing", 56), - new BookLine("larger daily. Making", 57), - new BookLine("excellent progress now. I", 58), - new BookLine("am currently feeding it", 59), - new BookLine("on a mixture of fungus,", 60), - new BookLine("tar and clay.", 61), - new BookLine("It seems to like this", 62), - new BookLine("combination a lot!", 63)), - new Page(new BookLine("3rd of Pentember", 66), - new BookLine("Experiment still going", 67), - new BookLine("extremely well. Moved it", 68), - new BookLine("to the wooden garden", 69), - new BookLine("shed; it does too much", 70), - new BookLine("damage in the house! it", 71), - new BookLine("is getting very strong", 72), - new BookLine("now, but unfortunately is", 73), - new BookLine("not too intelligent yet. It", 74), - new BookLine("has a really mean stare", 75), - new BookLine("too!", 76))), - new PageSet( - new Page(new BookLine("4th of Pentember", 55), - new BookLine("Sausages for dinner", 56), - new BookLine("tonight! Lovely!", 57), - new BookLine("5th of Pentember", 59), - new BookLine("A guy called Professor", 60), - new BookLine("Oddenstein installed a", 61), - new BookLine("new security system for", 62), - new BookLine("me in the basement. He", 63), - new BookLine("seems to have a lot of", 64), - new BookLine("good security ideas.", 65)), - new Page(new BookLine("6th of Pentember", 66), - new BookLine("Don't want people getting", 67), - new BookLine("into back garden to see", 68), - new BookLine("the experiment. Professor", 69), - new BookLine("Oddenstein is fitting me a", 70), - new BookLine("new security system,", 71), - new BookLine("after his successful", 72), - new BookLine("installation in the cellar.", 73))), - new PageSet( - new Page(new BookLine("7th of Pentember", 55), - new BookLine("That pesky kid keeps", 56), - new BookLine("kicking his ball into my", 57), - new BookLine("garden. I swear, if he", 58), - new BookLine("does it AGAIN, I'm going", 59), - new BookLine("to lock his ball away in", 60), - new BookLine("the shed.", 61), - new BookLine("8th of Pentember.", 63), - new BookLine("The security system is", 64), - new BookLine("done. By Zamorak! Wow,", 65)), - new Page(new BookLine("is it contrived! Now, to", 66), - new BookLine("open my own back door,", 67), - new BookLine("I lure a mouse out of a", 68), - new BookLine("hole in the back porch, I", 69), - new BookLine("fit a magic curved piece", 70), - new BookLine("of metal to the harness", 71), - new BookLine("on its back, the mouse", 72), - new BookLine("goes back in the hole, and", 73), - new BookLine("the door unlocks! The", 74), - new BookLine("prof tells me that this is", 75), - new BookLine("cutting edge technology!", 76))), - new PageSet( - new Page(new BookLine("As an added precaution I", 55), - new BookLine("have hidden the key to", 56), - new BookLine("the shed in a secret", 57), - new BookLine("compartment of the", 58), - new BookLine("fountain in the garden.", 59), - new BookLine("No one will ever look", 60), - new BookLine("there.", 61), - new BookLine("9th of Pentember", 63), - new BookLine("Still can't think of a good", 64), - new BookLine("name of 'The", 65)), - new Page(new BookLine("Experiment'. Leaning", 66), - new BookLine("Towards 'fritz'... Although", 67), - new BookLine("am considering Lucy as", 68), - new BookLine("it reminds me of my", 69), - new BookLine("Mother!", 70))), - }; - - /** - * Constructs a new {@code WitchsDiaryBook} {@code Object}. - */ - public WitchsDiaryBook(final Player player) { - super(player, "Witches' Diary", 2408, PAGES); - } - - /** - * Constructs a new {@code WitchsDiaryBook} {@code Object}. - */ - public WitchsDiaryBook() { - /** - * empty. - */ - } - - @Override - public void finish() { - - } - - @Override - public void display(Page[] set) { - player.lock(); - player.getInterfaceManager().open(getInterface()); - for (int i = 55; i < 77; i++) { - player.getPacketDispatch().sendString("", getInterface().getId(), i); - } - player.getPacketDispatch().sendString(getName(), getInterface().getId(), 6); - player.getPacketDispatch().sendString("", getInterface().getId(), 77); - player.getPacketDispatch().sendString("", getInterface().getId(), 78); - for (Page page : set) { - for (BookLine line : page.getLines()) { - player.getPacketDispatch().sendString(line.getMessage(), getInterface().getId(), line.getChild()); - } - } - player.getPacketDispatch().sendInterfaceConfig(getInterface().getId(), 51, index < 1); - boolean lastPage = index == sets.length - 1; - player.getPacketDispatch().sendInterfaceConfig(getInterface().getId(), 53, lastPage); - if (lastPage) { - finish(); - } - player.unlock(); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new WitchsDiaryBook(player); - } - - @Override - public int[] getIds() { - return new int[]{ID}; - } -} \ No newline at end of file diff --git a/Server/src/main/content/region/asgarnia/taverley/quest/witchshouse/WitchsExperimentNPC.java b/Server/src/main/content/region/asgarnia/taverley/quest/witchshouse/WitchsExperimentNPC.java index cfd71aaf9..5f1e46e06 100644 --- a/Server/src/main/content/region/asgarnia/taverley/quest/witchshouse/WitchsExperimentNPC.java +++ b/Server/src/main/content/region/asgarnia/taverley/quest/witchshouse/WitchsExperimentNPC.java @@ -180,4 +180,4 @@ public class WitchsExperimentNPC extends AbstractNPC { } -} \ No newline at end of file +} 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 eb4b8639e..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,38 +20,36 @@ 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); + } } } @Override public void finish(Player player) { super.finish(player); - player.getConfigManager().set(101, player.getQuestRepository().getPoints()); player.getPacketDispatch().sendString("4 Quest Points", 277, 8 + 2); player.getPacketDispatch().sendString("6325 Hitpoints XP", 277, 9 + 2); player.getSkills().addExperience(Skills.HITPOINTS, 6325); 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 1e00b8efe..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); @@ -159,10 +160,6 @@ public class WitchsHousePlugin extends OptionHandler { case 24673: player.teleport(Location.create(2906, 3468, 0)); break; - case 2408: - player.getDialogueInterpreter().open(4501993, node); - player.setAttribute("/save:readWitchsBook", true); - break; } return true; } diff --git a/Server/src/main/content/region/asgarnia/trollheim/dialogue/GWDKnightDialogue.java b/Server/src/main/content/region/asgarnia/trollheim/dialogue/GWDKnightDialogue.java index 1af6de4b4..750447d19 100644 --- a/Server/src/main/content/region/asgarnia/trollheim/dialogue/GWDKnightDialogue.java +++ b/Server/src/main/content/region/asgarnia/trollheim/dialogue/GWDKnightDialogue.java @@ -11,6 +11,8 @@ import core.game.world.map.Location; import core.plugin.Initializable; import core.plugin.Plugin; +import static core.api.ContentAPIKt.*; + /** * Handles the dying knight dialogue. * @author Emperor @@ -64,7 +66,7 @@ public final class GWDKnightDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - if ((player.getConfigManager().get(1048) & 16) != 0) { + if ((getVarp(player, 1048) & 16) != 0) { player.getPacketDispatch().sendMessage("The knight has already died."); return false; } @@ -91,7 +93,7 @@ public final class GWDKnightDialogue extends DialoguePlugin { case 3: if (player.getInventory().add(SCROLL)) { interpreter.sendItemMessage(11734, "The knight hands you a scroll."); - player.varpManager.get(1048).setVarbit(4,1).send(player); + setVarbit(player, 3936, 1, true); stage = 5; } else { stage = 4; diff --git a/Server/src/main/content/region/asgarnia/trollheim/dialogue/KnightsNotes.kt b/Server/src/main/content/region/asgarnia/trollheim/dialogue/KnightsNotes.kt new file mode 100644 index 000000000..7d267cb7b --- /dev/null +++ b/Server/src/main/content/region/asgarnia/trollheim/dialogue/KnightsNotes.kt @@ -0,0 +1,55 @@ +package content.region.asgarnia.trollheim.dialogue + +import content.global.handlers.iface.ScrollInterface +import content.global.handlers.iface.ScrollLine +import core.api.* +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import org.rs09.consts.Items +import org.rs09.consts.Components + +/** + * @author szu + */ +class KnightsNotes : InteractionListener { + companion object { + val CONTENTS = arrayOf( + ScrollLine("My friend, you were right to send me to investigate the",3), + ScrollLine("dwarf's drunken claims, for I have discovered a treasure",4), + ScrollLine("beyond our wildest dreams. The aviantese are alive, and I",5), + ScrollLine("suspect they still guard the godsword! Beneath the remnants",6), + ScrollLine("of the temple a great battle is being fought between followers",7), + ScrollLine("of Bandos, Armadyl, Saradomin and Zamorak. My command was",8), + ScrollLine("slaughtered and I am grieviously wounded. YOU MUST PREVENT",9), + ScrollLine("THE GODSWORD FROM FALLING INTO THE WRONG HANDS! I do not",10), + ScrollLine("know how I am going to get this message to you, why is that",11), + ScrollLine("talking skull never around when he's needed? Your comrade,",12), + ScrollLine("Sir Gerry.",13), + ) + } + + override fun defineListeners() { + // The scroll has not been opened. This will lead to a different dialogue with Sir Tiffy Cashien. + on(Items.KNIGHTS_NOTES_11734, IntType.ITEM, "read") { player, _ -> + sendDialogueOptions(player, "The scroll is sealed. Do you still want to open it?", "Yes", "No") + player!!.packetDispatch.sendInterfaceConfig(228, 6, true) + player!!.packetDispatch.sendInterfaceConfig(228, 9, false) // childId 7 is left 8 is right + addDialogueAction(player) { player, button -> + if (button == 2) { + sendMessage(player, "You break the wax seal and open the scroll.") + if (removeItem(player, Items.KNIGHTS_NOTES_11734)) { + addItem(player, Items.KNIGHTS_NOTES_11735) + ScrollInterface.scrollSetup(player, Components.MESSAGESCROLL_220, CONTENTS) + } + } + return@addDialogueAction + } + return@on true + } + // The scroll has been read. This will lead to a different dialogue with Sir Tiffy Cashien. + on(Items.KNIGHTS_NOTES_11735, IntType.ITEM, "read") { player, _ -> + ScrollInterface.scrollSetup(player, Components.MESSAGESCROLL_220, CONTENTS) + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/asgarnia/trollheim/dialogue/SabaDialogue.kt b/Server/src/main/content/region/asgarnia/trollheim/dialogue/SabaDialogue.kt new file mode 100644 index 000000000..9eb7daf39 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/trollheim/dialogue/SabaDialogue.kt @@ -0,0 +1,42 @@ +package content.region.asgarnia.trollheim.dialogue + +import content.data.Quests +import content.region.asgarnia.burthorpe.quest.deathplateau.SabaDialogueFile +import core.api.getQuestStage +import core.api.openDialogue +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 + +/** + * Represents the dialogue plugin used for the tenzing npc. + * @author 'Vexia + * @version 1.0 + */ +@Initializable +class SabaDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + if (getQuestStage(player!!, Quests.DEATH_PLATEAU) >= 19) { + openDialogue(player!!, SabaDialogueFile(), npc) + return true + } + // Fallback to default. + when (stage) { + START_DIALOGUE -> playerl(FacialExpression.HALF_GUILTY, "Hi!").also { stage++ } + 1 -> npcl(FacialExpression.ANNOYED, "Why won't people leave me alone?!").also { stage = END_DIALOGUE } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return SabaDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.SABA_1070) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/asgarnia/trollheim/dialogue/TenzingDialogue.kt b/Server/src/main/content/region/asgarnia/trollheim/dialogue/TenzingDialogue.kt new file mode 100644 index 000000000..2101a9b77 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/trollheim/dialogue/TenzingDialogue.kt @@ -0,0 +1,73 @@ +package content.region.asgarnia.trollheim.dialogue + +import content.data.Quests +import content.region.asgarnia.burthorpe.quest.deathplateau.TenzingDialogueFile +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 org.rs09.consts.Items +import org.rs09.consts.NPCs + +/** + * Represents the dialogue plugin used for the tenzing npc. + * @author 'Vexia + * @author ovenbread + */ +@Initializable +class TenzingDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + if (isQuestInProgress(player!!, Quests.DEATH_PLATEAU, 20, 29)) { + openDialogue(player!!, TenzingDialogueFile(), npc) + return true + } + // Fallback to default. + when (stage) { + 0 -> player(FacialExpression.FRIENDLY, "Hello Tenzing!").also { stage++ } + 1 -> npc(FacialExpression.FRIENDLY,"Hello traveler. What can I do for you?").also{ stage++ } + 2 -> showTopics( + Topic(FacialExpression.FRIENDLY, "Can I buy some Climbing boots?", 10), + Topic(FacialExpression.FRIENDLY, "What does a Sherpa do?", 20), + Topic(FacialExpression.FRIENDLY, "How did you find out about the secret way?", 30), + Topic(FacialExpression.FRIENDLY, "Nice place you have here.", 40), + Topic(FacialExpression.FRIENDLY, "Nothing, thanks!", END_DIALOGUE) + ) + 10 -> npc("Sure, I'll sell you some in your size for 12 gold.").also{ stage++ } + 11 -> showTopics( + Topic(FacialExpression.FRIENDLY, "OK, sounds good.", 12), + Topic(FacialExpression.FRIENDLY, "No, thanks.", END_DIALOGUE), + ) + 12 -> { + if (!player.inventory.hasSpaceFor(Item(Items.CLIMBING_BOOTS_3105))) { + player("I don't have enough space in my backpack right this second.").also{ stage = END_DIALOGUE } + } + if (!inInventory(player, Items.COINS_995)) { + player("I don't have enough coins right now.").also{ stage = END_DIALOGUE } + } + if (removeItem(player!!, Item(Items.COINS_995, 12))) { + player("I don't have enough coins right now.").also{ stage = END_DIALOGUE } + } + addItemOrDrop(player, Items.CLIMBING_BOOTS_3105, 1) + sendItemDialogue(player, Items.CLIMBING_BOOTS_3105, "Tenzing has given you some Climbing boots.").also { stage = END_DIALOGUE } + sendMessage(player, "Tenzing has given you some Climbing boots.") + } + + 20 -> npcl(FacialExpression.FRIENDLY, "We are expert guides that take adventurers such as yourself, on mountaineering expeditions.").also { stage = END_DIALOGUE } + 30 -> npcl(FacialExpression.FRIENDLY, "I used to take adventurers up Death Plateau and further north before the trolls came. I know these mountains well.").also { stage = END_DIALOGUE } + 40 -> npcl(FacialExpression.FRIENDLY, "Thanks, I built it myself! I'm usually self sufficient but I can't earn any money with the trolls camped on Death Plateau,").also { stage = END_DIALOGUE } + } + return true + } + + override fun newInstance(player: Player): DialoguePlugin { + return TenzingDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.TENZING_1071) + } +} diff --git a/Server/src/main/content/region/asgarnia/trollheim/handlers/TrollheimPlugin.java b/Server/src/main/content/region/asgarnia/trollheim/handlers/TrollheimPlugin.java index 7fb3b6d65..b62009775 100644 --- a/Server/src/main/content/region/asgarnia/trollheim/handlers/TrollheimPlugin.java +++ b/Server/src/main/content/region/asgarnia/trollheim/handlers/TrollheimPlugin.java @@ -156,11 +156,9 @@ public final class TrollheimPlugin extends OptionHandler { // ladder SceneryDefinition.forId(18833).getHandlers().put("option:climb-down", this);// stronghold // ladder - ClassScanner.definePlugin(new SabaDialogue()); ClassScanner.definePlugin(new WoundedSoldier()); ClassScanner.definePlugin(new WarningZone()); ActivityManager.register(new WarningCutscene()); - ClassScanner.definePlugin(new TenzingDialogue()); ClassScanner.definePlugin(new TrollNPC()); return this; } @@ -293,6 +291,12 @@ public final class TrollheimPlugin extends OptionHandler { } else { bandaid(player, player.getLocation(), Location.create(2911, 3686, 0), JUMP); } + } else if (loc.equals(new Location(2857, 3612, 0)) || loc.equals(new Location(2856, 3612, 0))) { + if (player.getSkills().getLevel(Skills.AGILITY) < 15) { + player.getPacketDispatch().sendMessage("You need an Agility level of 15 in order to climb this rock."); + return true; + } + bandaid(player, player.getLocation(), player.getLocation().getY() < object.getLocation().getY() ? player.getLocation().transform(0, 2, 0) : player.getLocation().transform(0, -2, 0), JUMP); } else { bandaid(player, player.getLocation(), player.getLocation().getY() < object.getLocation().getY() ? player.getLocation().transform(0, 2, 0) : player.getLocation().transform(0, -2, 0), JUMP); } @@ -432,62 +436,6 @@ public final class TrollheimPlugin extends OptionHandler { return null; } - /** - * Represents the saba dialogue plugin. - * @author 'Vexia - * @version 1.0 - */ - public final class SabaDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code TrolleimPlugin} {@code Object}. - */ - public SabaDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code TrolleimPlugin} {@code Object}. - * @param player the player. - */ - public SabaDialogue(final Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new SabaDialogue(player); - } - - @Override - public boolean open(Object... args) { - player("Hello!"); - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - npc("Why won't people leave me alone?!"); - stage = 1; - break; - case 1: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 1070 }; - } - - } - /** * Represents the wounded soldiers dialogue plugin. * @author 'Vexia @@ -697,156 +645,6 @@ public final class TrollheimPlugin extends OptionHandler { } - /** - * Represents the dialogue plugin used for the tenzing npc. - * @author 'Vexia - * @version 1.0 - */ - public static final class TenzingDialogue extends DialoguePlugin { - - /** - * Represents the coins items. - */ - private static final Item COINS = new Item(995, 12); - - /** - * Constructs a new {@code TenzingDialogue} {@code Object}. - */ - public TenzingDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code TenzingDialogue} {@code Object}. - * @param player the player. - */ - public TenzingDialogue(final Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new TenzingDialogue(player); - } - - @Override - public boolean open(Object... args) { - player("Hello Tenzing!"); - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - npc("Hello traveler. What can I do for you?"); - stage = 1; - break; - case 1: - options("Can I buy some Climbing boots?", "What does a Sherpa do?", "How did you find out about the secret way?", "Nice place you have here.", "Nothing, thanks!"); - stage = 2; - break; - case 2: - switch (buttonId) { - case 1: - player("Can I buy some Climbing boots?"); - stage = 10; - break; - case 2: - player("What does a Sherpa do?"); - stage = 20; - break; - case 3: - player("How did you find out about the secret way?"); - stage = 30; - break; - case 4: - player("Nice place you have here."); - stage = 40; - break; - case 5: - player("Nothing, thanks!"); - stage = 50; - break; - } - break; - case 10: - npc("Sure, I'll sell you some in your size for 12 gold."); - stage = 11; - break; - case 11: - options("OK, sounds good.", "No, thanks."); - stage = 12; - break; - case 12: - switch (buttonId) { - case 1: - player("OK, sounds good."); - stage = 13; - break; - case 2: - player("No, thanks."); - stage = 50; - break; - } - break; - case 13: - if (!player.getInventory().hasSpaceFor(CLIMBING_BOOTS)) { - player("I don't have enough space in my backpack right", "this second."); - stage = 50; - return true; - } - if (!player.getInventory().containsItem(COINS)) { - end(); - return true; - } - if (!player.getInventory().remove(COINS)) { - player("I don't have enough coins right now."); - stage = 50; - return true; - } - if (player.getInventory().add(CLIMBING_BOOTS)) { - interpreter.sendItemMessage(CLIMBING_BOOTS, "Tenzing has given you some Climbing boots."); - player.getPacketDispatch().sendMessage("Tenzing has given you some Climbing boots."); - } - stage = 50; - break; - case 20: - npc("We are expert guides that take adventurers such as", "yourself, on mountaineering expeditions."); - stage = 21; - break; - case 21: - end(); - break; - case 30: - npc("I used to take adventurers up Death Plateau and", "further north before the trolls came. I know these", "mountains well."); - stage = 31; - break; - case 31: - end(); - break; - case 40: - npc("Thanks, I built it myself! I'm usually self sufficient but", "I can't earn any money with the trolls camped on", "Death Plateau,"); - stage = 41; - break; - case 41: - end(); - break; - case 50: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 1071 }; - } - } - /** * Represents the a trollheim troll. * @author 'Vexia 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 b8c40462e..d90bd3f35 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 @@ -9,11 +9,12 @@ import core.game.node.entity.combat.equipment.ArmourSet; import core.game.node.entity.impl.Projectile; import core.game.node.entity.impl.Animator.Priority; import core.game.node.entity.player.Player; -import core.game.node.entity.state.EntityState; import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Graphics; import core.tools.RandomFunction; +import static core.api.ContentAPIKt.*; + /** * Handles K'ril Tsutsaroth's combat. * @author Emperor @@ -72,7 +73,7 @@ public final class GWDTsutsarothSwingHandler extends CombatSwingHandler { hit = RandomFunction.random(max); state.setMaximumHit(max); if (style == CombatStyle.MELEE) { - victim.getStateManager().register(EntityState.POISONED, false, 168, entity); + applyPoison(victim, entity, 16); } if (special) { ((Player) victim).getSkills().decrementPrayerPoints((double) hit / 2); @@ -152,4 +153,4 @@ public final class GWDTsutsarothSwingHandler extends CombatSwingHandler { return getType().getSwingHandler().calculateHit(entity, victim, modifier); } -} \ No newline at end of file +} 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 a794f2289..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 @@ -17,6 +17,9 @@ import core.game.world.map.RegionManager; 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. * @author Emperor @@ -42,15 +45,14 @@ public final class GodwarsEntranceHandler extends OptionHandler { player.getPacketDispatch().sendMessage("You don't have a rope to tie around the pillar."); return true; } - player.varpManager.get(1048).setVarbit(0,1).send(player); - player.varpManager.flagSave(1048, false); + setVarbit(player, 3932, 1, true); return true; case 26341: if (player.getSkills().getStaticLevel(Skills.AGILITY) < 15) { player.getPacketDispatch().sendMessage("You need an Agility level of 15 to enter this."); return true; } - if (player.varpManager.get(1048).getVarbit(4) == 0) { + if (getVarbit(player, 3936) == 0) { player.getDialogueInterpreter().sendDialogues(6201, FacialExpression.HALF_GUILTY, "Cough... Hey, over here."); return true; } @@ -66,6 +68,8 @@ public final class GodwarsEntranceHandler extends OptionHandler { }); return true; case 26338: + 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."); return true; diff --git a/Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/GodwarsMapzone.java b/Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/GodwarsMapzone.java index c743fb66e..b2dd1fd25 100644 --- a/Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/GodwarsMapzone.java +++ b/Server/src/main/content/region/asgarnia/trollheim/handlers/gwd/GodwarsMapzone.java @@ -16,6 +16,7 @@ import core.game.node.entity.player.info.Rights; import core.game.node.item.Item; import core.game.node.scenery.Scenery; import core.game.system.task.Pulse; +import core.tools.Log; import core.tools.SystemLogger; import core.game.world.GameWorld; import core.game.world.map.Direction; @@ -34,6 +35,9 @@ import core.plugin.Plugin; import core.tools.StringUtils; import core.game.node.entity.skill.Skills; +import static core.api.ContentAPIKt.*; +import static core.api.ContentAPIKt.log; + /** * Handles the god wars map zone. * @author Emperor @@ -86,7 +90,7 @@ public final class GodwarsMapzone extends MapZone implements Plugin { * @param setting The setting. */ public void setRopeSetting(Player player, int setting) { - player.varpManager.get(1048).setVarbit(setting,1).send(player); + setVarbit(player, setting == 1 ? 3933 : 3934, 1, true); } /** @@ -368,7 +372,7 @@ public final class GodwarsMapzone extends MapZone implements Plugin { } if (DoorActionHandler.handleAutowalkDoor(player, object)) { - SystemLogger.logInfo(this.getClass(), player.getUsername() + " entered " + faction.name() + " gwd boss room"); + log(this.getClass(), Log.FINE, player.getUsername() + " entered " + faction.name() + " gwd boss room"); increaseKillcount(player, faction, -required); } return true; diff --git a/Server/src/main/content/region/asgarnia/trollweiss/handlers/TrollweissListeners.kt b/Server/src/main/content/region/asgarnia/trollweiss/handlers/TrollweissListeners.kt new file mode 100644 index 000000000..55cf63411 --- /dev/null +++ b/Server/src/main/content/region/asgarnia/trollweiss/handlers/TrollweissListeners.kt @@ -0,0 +1,38 @@ +package content.region.asgarnia.trollweiss.handlers + +import core.game.interaction.InteractionListener +import core.game.interaction.IntType +import core.game.world.map.Location +import org.rs09.consts.Scenery + +class TrollweissListeners : InteractionListener { + override fun defineListeners() { + // Keldagrim side tunnel to trollweiss mountain + on(Scenery.TUNNEL_5012, IntType.SCENERY, "enter") { player, _ -> + player.properties.teleportLocation = Location.create(2799, 10134, 0) + return@on true + } + on(Scenery.TUNNEL_5013, IntType.SCENERY, "enter") { player, _ -> + player.properties.teleportLocation = Location.create(2796, 3719, 0) + return@on true + } + // Trollheim north to trollweiss dungeon + on(Scenery.CAVE_ENTRANCE_5007, IntType.SCENERY, "enter") { player, _ -> + player.properties.teleportLocation = Location.create(2803, 10187, 0) + return@on true + } + on(Scenery.CAVE_EXIT_32743, IntType.SCENERY, "exit") { player, _ -> + player.properties.teleportLocation = Location.create(2822, 3744, 0) + return@on true + } + // Keldagrim side tunnel to trollweiss mountain + on(Scenery.CREVASSE_33185, IntType.SCENERY, "enter") { player, _ -> + player.properties.teleportLocation = Location.create(2778, 3869, 0) + return@on true + } + on(Scenery.TUNNEL_5009, IntType.SCENERY, "enter") { player, _ -> + player.properties.teleportLocation = Location.create(2772, 10232, 0) + return@on true + } + } +} \ No newline at end of file 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/AablaDialogue.kt b/Server/src/main/content/region/desert/alkharid/dialogue/AablaDialogue.kt index 80d06b80c..0330f7f7e 100644 --- a/Server/src/main/content/region/desert/alkharid/dialogue/AablaDialogue.kt +++ b/Server/src/main/content/region/desert/alkharid/dialogue/AablaDialogue.kt @@ -2,26 +2,26 @@ package content.region.desert.alkharid.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 org.rs09.consts.NPCs -import core.game.dialogue.Topic import core.tools.END_DIALOGUE +import org.rs09.consts.NPCs /** * @author bushtail */ @Initializable -class AablaDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { +class AablaDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player?): DialoguePlugin { return AablaDialogue(player) } override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - player(core.game.dialogue.FacialExpression.FRIENDLY, "Hi!").also { stage = 0 } + playerl(FacialExpression.FRIENDLY, "Hi!").also { stage = 0 } return true } @@ -33,11 +33,11 @@ class AablaDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin( Topic("Do you come here often?", 201) ) - 101 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "I work here, so yes!").also { stage = END_DIALOGUE } + 101 -> npcl(FacialExpression.FRIENDLY, "I work here, so yes!").also { stage = END_DIALOGUE } - 201 -> npcl(core.game.dialogue.FacialExpression.HALF_THINKING, "Yes I do. Thankfully we can cope with almost anything. Jaraah really is a wonderful surgeon, his methods are a little unorthodox but he gets the job done.").also { stage++ } - 202 -> npcl(core.game.dialogue.FacialExpression.HALF_GUILTY, "I shouldn't tell you this but his nickname is 'The Butcher'.").also { stage++ } - 203 -> player(core.game.dialogue.FacialExpression.HALF_WORRIED, "That's reassuring.").also { stage = END_DIALOGUE } + 201 -> npcl(FacialExpression.HALF_THINKING, "Yes I do. Thankfully we can cope with almost anything. Jaraah really is a wonderful surgeon, his methods are a little unorthodox but he gets the job done.").also { stage++ } + 202 -> npcl(FacialExpression.HALF_GUILTY, "I shouldn't tell you this but his nickname is 'The Butcher'.").also { stage++ } + 203 -> playerl(FacialExpression.HALF_WORRIED, "That's reassuring.").also { stage = END_DIALOGUE } } return true } diff --git a/Server/src/main/content/region/desert/alkharid/dialogue/AlKharidHealListener.kt b/Server/src/main/content/region/desert/alkharid/dialogue/AlKharidHealListener.kt index a9413393d..b7c0356df 100644 --- a/Server/src/main/content/region/desert/alkharid/dialogue/AlKharidHealListener.kt +++ b/Server/src/main/content/region/desert/alkharid/dialogue/AlKharidHealListener.kt @@ -5,6 +5,7 @@ import core.game.node.entity.skill.Skills import org.rs09.consts.Animations import org.rs09.consts.NPCs import core.game.dialogue.DialogueFile +import core.game.dialogue.FacialExpression import core.game.interaction.InteractionListener import core.game.interaction.IntType import core.tools.END_DIALOGUE @@ -35,15 +36,15 @@ class AlKharidHealDialogue(val skipFirst: Boolean) : DialogueFile() { override fun handle(componentID: Int, buttonID: Int) { if (stage == 0 && skipFirst) stage++ when(stage) { - 0 -> player(core.game.dialogue.FacialExpression.ASKING, "Can you heal me?").also { stage++ } - 1 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Of course!").also { stage++ } + 0 -> playerl(FacialExpression.ASKING, "Can you heal me?").also { stage++ } + 1 -> npcl(FacialExpression.FRIENDLY, "Of course!").also { stage++ } 2 -> { animate(npc!!, Animations.HUMAN_PICKPOCKETING_881) if(player!!.skills.lifepoints < getStatLevel(player!!, Skills.HITPOINTS)) { player!!.skills.heal(21) - npcl(core.game.dialogue.FacialExpression.FRIENDLY, "There you go!") + npcl(FacialExpression.FRIENDLY, "There you go!") } else { - npcl(core.game.dialogue.FacialExpression.FRIENDLY, "You look healthy to me!") + npcl(FacialExpression.FRIENDLY, "You look healthy to me!") } stage = END_DIALOGUE } diff --git a/Server/src/main/content/region/desert/alkharid/dialogue/AlKharidShopKeeperDialogue.kt b/Server/src/main/content/region/desert/alkharid/dialogue/AlKharidShopKeeperDialogue.kt index bd1d78359..803581271 100644 --- a/Server/src/main/content/region/desert/alkharid/dialogue/AlKharidShopKeeperDialogue.kt +++ b/Server/src/main/content/region/desert/alkharid/dialogue/AlKharidShopKeeperDialogue.kt @@ -5,8 +5,8 @@ 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 core.tools.END_DIALOGUE +import org.rs09.consts.NPCs /** * @author bushtail @@ -21,35 +21,28 @@ class AlKharidShopKeeperDialogue(player: Player? = null) : DialoguePlugin(player override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - npcl(core.game.dialogue.FacialExpression.HALF_ASKING, "Can I help you at all?") + npcl(FacialExpression.HALF_ASKING, "Can I help you at all?") stage = 100 return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage) { - 100 -> options( - "Yes, please. What are you selling?", - "How should I use your shop?", - "No, thanks." - ).also { stage++ } + 100 -> options("Yes, please. What are you selling?", "How should I use your shop?", "No, thanks.").also { stage++ } 101 -> when(buttonId) { 1 -> { stage = END_DIALOGUE npc.openShop(player) } - 2 -> player(core.game.dialogue.FacialExpression.ASKING, "How should I use your shop?").also { stage = 201 } - 3 -> player(core.game.dialogue.FacialExpression.NEUTRAL, "No, thanks.").also { stage = END_DIALOGUE } + 2 -> playerl(FacialExpression.ASKING, "How should I use your shop?").also { stage = 201 } + 3 -> playerl(FacialExpression.NEUTRAL, "No, thanks.").also { stage = END_DIALOGUE } } - 201 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "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 } + 201 -> npcl(FacialExpression.FRIENDLY, "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 } } return true } override fun getIds(): IntArray { - return intArrayOf( - NPCs.SHOPKEEPER_524, - NPCs.SHOP_ASSISTANT_525 - ) + return intArrayOf(NPCs.SHOPKEEPER_524, NPCs.SHOP_ASSISTANT_525) } } \ No newline at end of file diff --git a/Server/src/main/content/region/desert/alkharid/dialogue/AliMorrisaneDialogue.java b/Server/src/main/content/region/desert/alkharid/dialogue/AliMorrisaneDialogue.java deleted file mode 100644 index 0fbf496d5..000000000 --- a/Server/src/main/content/region/desert/alkharid/dialogue/AliMorrisaneDialogue.java +++ /dev/null @@ -1,131 +0,0 @@ -package content.region.desert.alkharid.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 ali morrisane dialogue. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class AliMorrisaneDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code AliMorrisaneDialogue} {@code Object}. - * @param player the player. - */ - public AliMorrisaneDialogue(final Player player) { - super(player); - } - - /** - * Constructs a new {@code AliMorrisaneDialogue} {@code Object}. - */ - public AliMorrisaneDialogue() { - /** - * empty. - */ - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new AliMorrisaneDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - npc("Good day and welcome back to Al Kharid."); - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - player("Hello to you too."); - stage = 1; - break; - case 1: - npc("My name is Ali Morrisane - the greatest salesman in", "the world."); - stage = 2; - break; - case 2: - options("If you are, then why are you still selling goods from a stall?", "So what are you selling then?"); - stage = 3; - break; - case 3: - switch (buttonId) { - case 1: - player("If you are, then why are you still selling goods from a", "stall?"); - stage = 10; - break; - case 2: - end(); - npc.openShop(player); - break; - } - break; - case 10: - npc("Well one can only do and sell so much. If I had more", "staff I'd be able to sell more, rather than wasting my", "time on menial things I could get on with selling sand", "to the Bedabin and useless tourist trinkets to everyone."); - stage = 11; - break; - case 11: - options("I'm far too busy - adventuring is a full time job you know.", "I'd like to help you but...."); - stage = 12; - break; - case 12: - switch (buttonId) { - case 1: - player("I'm far too busy - adventuring is a full time job you", "know."); - stage = 15; - break; - case 2: - player("I'd like to help you but....."); - stage = 13; - break; - } - break; - case 13: - npc("Yes I know, I know - the life of a shop keeper isn't", "slaying dragon and wooing damsels but it has its", "charms"); - stage = 14; - break; - case 14: - end(); - break; - case 15: - npc("No problem my friend, perhaps another time."); - stage = 16; - break; - case 16: - npc("Anyway, have a look at my wares."); - stage = 17; - break; - case 17: - options("No, I'm really too busy.", "Okay."); - stage = 18; - break; - case 18: - switch (buttonId) { - case 1: - end(); - break; - case 2: - end(); - npc.openShop(player); - break; - } - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 1862 }; - } - -} diff --git a/Server/src/main/content/region/desert/alkharid/dialogue/AliMorrisaneDialogue.kt b/Server/src/main/content/region/desert/alkharid/dialogue/AliMorrisaneDialogue.kt new file mode 100644 index 000000000..a0bc89b30 --- /dev/null +++ b/Server/src/main/content/region/desert/alkharid/dialogue/AliMorrisaneDialogue.kt @@ -0,0 +1,76 @@ +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 core.api.* +import content.data.Quests + +/** + * Represents the ali morrisane dialogue. + * @author 'Vexia + * @version 1.0 + */ +@Initializable +class AliMorrisaneDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun newInstance(player: Player?): DialoguePlugin { + return AliMorrisaneDialogue(player) + } + + override fun open(vararg args: Any): Boolean { + npc = args[0] as NPC + npcl(FacialExpression.FRIENDLY, "Good day and welcome back to Al Kharid.") + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage) { + 0 -> playerl(FacialExpression.FRIENDLY, "Hello to you too.").also { stage = 1 } + 1 -> npcl(FacialExpression.ASKING, "My name is Ali Morrisane - the greatest salesman in the world.").also { stage = 2 } + 2 -> options("If you are, then why are you still selling goods from a stall?", "So what are you selling then?").also { stage = 3 } + 3 -> when (buttonId) { + 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, Quests.THE_FEUD)) + return true + npc.openShop(player) + } + } + 10 -> npcl(FacialExpression.FRIENDLY, "Well one can only do and sell so much. If I had more staff I'd be able to sell more").also { stage = 11 } + 11 -> npcl(FacialExpression.FRIENDLY,"rather than wasting my time on menial things I could get on with selling sand to the Bedabin and useless tourist trinkets to everyone.").also { stage = 12 } + 12 -> options("I'm far too busy - adventuring is a full time job you know.", "I'd like to help you but....").also { stage = 13 } + 13 -> when (buttonId) { + 1 -> playerl(FacialExpression.FRIENDLY, "I'm far too busy - adventuring is a full time job you know.").also { stage = 16 } + 2 -> playerl(FacialExpression.FRIENDLY, "I'd like to help you but.....").also { stage = 14 } + } + 14 -> npcl(FacialExpression.FRIENDLY, "Yes I know, I know - the life of a shop keeper isn't slaying dragon and wooing damsels but it has its charms").also { stage = 15 } + 15 -> end() + 16 -> npcl(FacialExpression.FRIENDLY,"No problem my friend, perhaps another time.").also { stage = 17 } + 17 -> npcl(FacialExpression.FRIENDLY, "Anyway, have a look at my wares.").also { stage = 18 } + 18 -> options("No, I'm really too busy.", "Okay.").also { stage = 19 } + 19 -> when (buttonId) { + 1 -> { + end() + } + 2 -> { + end() + if (!hasRequirement(player, Quests.THE_FEUD)) + return true + npc.openShop(player) + } + } + } + return true + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.ALI_MORRISANE_1862) + } +} + diff --git a/Server/src/main/content/region/desert/alkharid/dialogue/AliTheFarmerDialogue.kt b/Server/src/main/content/region/desert/alkharid/dialogue/AliTheFarmerDialogue.kt index 2465b2a55..46a0e020a 100644 --- a/Server/src/main/content/region/desert/alkharid/dialogue/AliTheFarmerDialogue.kt +++ b/Server/src/main/content/region/desert/alkharid/dialogue/AliTheFarmerDialogue.kt @@ -5,69 +5,60 @@ 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 AliTheFarmerDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class AliTheFarmerDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun newInstance(player: Player?): DialoguePlugin { + return AliTheFarmerDialogue(player) + } override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - player(core.game.dialogue.FacialExpression.FRIENDLY,"Hello there!") - stage = 0 + playerl(FacialExpression.FRIENDLY,"Hello there!") return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 0 -> npc(core.game.dialogue.FacialExpression.FRIENDLY,"Oh, er, hello. What do you want?").also { stage++ } - 1 -> options ("What can you tell me about Al Kharid?", "So, what do you do here?", - "I hear you work for Ali Morrisane...", "I hear you've been threatening the other shopkeepers.").also { stage++ } + 0 -> npcl(FacialExpression.FRIENDLY,"Oh, er, hello. What do you want?").also { stage++ } + 1 -> options("What can you tell me about Al Kharid?", "So, what do you do here?", "I hear you work for Ali Morrisane...", "I hear you've been threatening the other shopkeepers.").also { stage++ } 2 -> when(buttonId){ - 1 -> player(core.game.dialogue.FacialExpression.ASKING, "What can you tell me about Al Kharid?").also { stage = 10 } - 2 -> player(core.game.dialogue.FacialExpression.ASKING, "So, what do you do here?").also { stage = 20 } - 3 -> player(core.game.dialogue.FacialExpression.ASKING, "I hear you work for Ali Morrisane...").also { stage = 30 } - 4 -> player(core.game.dialogue.FacialExpression.ASKING, "I hear you've been threatening the other shopkeepers.").also { stage = 40 } - + 1 -> playerl(FacialExpression.ASKING, "What can you tell me about Al Kharid?").also { stage = 10 } + 2 -> playerl(FacialExpression.ASKING, "So, what do you do here?").also { stage = 20 } + 3 -> playerl(FacialExpression.ASKING, "I hear you work for Ali Morrisane...").also { stage = 30 } + 4 -> playerl(FacialExpression.ASKING, "I hear you've been threatening the other shopkeepers.").also { stage = 40 } + } + 10 -> npcl(FacialExpression.FRIENDLY,"There's not much farming land around here. Only that little patch outside.").also { stage++ } + 11 -> playerl(FacialExpression.ASKING,"Can you give me any advice on farming here in the desert?").also { stage++ } + 12 -> npcl(FacialExpression.FRIENDLY,"Like I said, I only know about cactuses...").also { stage++ } + 13 -> playerl(FacialExpression.FRIENDLY,"Just tell me about cactuses, then.").also { stage++ } + 14 -> npcl(FacialExpression.FRIENDLY,"First you have to weed the patch using a rake.").also { stage++ } + 15 -> playerl(FacialExpression.ASKING,"Can you give me any other advice?").also { stage++ } + 16 -> npcl(FacialExpression.FRIENDLY,"Not really. I've not done much farming recently.").also { stage++ } + 17 -> playerl(FacialExpression.ASKING,"Well, can you at least sell me any gardening tools or seeds?").also { stage++ } + 18 -> npcl(FacialExpression.GUILTY,"Sorry. They haven't been delivered yet.").also { stage = 1 } + 20 -> npcl(FacialExpression.FRIENDLY,"I'm going to set up a shop selling farming implements. That patch out there may be small, but it's all we've got.").also { stage = 11 } + 30 -> npcl(FacialExpression.FRIENDLY,"Yes, he bought these tents and had them put up for us. He says he'll also get our goods in, so we can start selling them soon.").also { stage++ } + 31 -> npcl(FacialExpression.FRIENDLY,"I only know how to farm cactuses, so this spot is perfect.").also { stage++ } + 32 -> playerl(FacialExpression.THINKING,"Maybe I should talk to him...").also { stage++ } + 33 -> npcl(FacialExpression.FRIENDLY,"Of course. He's always happy to talk to possible business partners.").also { stage = 1 } + 40 -> npcl(FacialExpression.FRIENDLY,"Now why would you think that?").also { stage++ } + 41 -> playerl(FacialExpression.FRIENDLY,"One of the shopkeepers told they were threatened by a man with a rake...").also { stage++ } + 42 -> npcl(FacialExpression.FRIENDLY,"Those people just don't want us to succeed. Don't listen to them!").also { stage = 99 } + 99 -> { + end() } - - 10 -> npc(core.game.dialogue.FacialExpression.FRIENDLY,"There's not much farming land around here.", "Only that little patch outside.").also { stage++ } - 11 -> player(core.game.dialogue.FacialExpression.ASKING,"Can you give me any advice on farming here in","the desert?").also { stage++ } - 12 -> npc(core.game.dialogue.FacialExpression.FRIENDLY,"Like I said, I only know about cactuses...").also { stage++ } - 13 -> player(core.game.dialogue.FacialExpression.FRIENDLY,"Just tell me about cactuses, then.").also { stage++ } - 14 -> npc(core.game.dialogue.FacialExpression.FRIENDLY,"First you have to weed the patch using a rake.").also { stage++ } - 15 -> player(core.game.dialogue.FacialExpression.ASKING,"Can you give me any other advice?").also { stage++ } - 16 -> npc(core.game.dialogue.FacialExpression.FRIENDLY,"Not really. I've not done much farming recently.").also { stage++ } - 17 -> player(core.game.dialogue.FacialExpression.ASKING,"Well, can you at least sell me any gardening", "tools or seeds?").also { stage++ } - 18 -> npc(core.game.dialogue.FacialExpression.GUILTY,"Sorry. They haven't been delivered yet.").also { stage = 1 } - - 20 -> npc( - core.game.dialogue.FacialExpression.FRIENDLY,"I'm going to set up a shop selling farming implements.", - "That patch out there may be small, ", "but it's all we've got.").also { stage = 11 } - - 30 -> npc( - core.game.dialogue.FacialExpression.FRIENDLY,"Yes, he bought these tents and had them put up for us.", - "He says he'll also get our goods in,", "so we can start selling them soon.").also { stage++ } - 31 -> npc(core.game.dialogue.FacialExpression.FRIENDLY,"I only know how to farm cactuses, so this spot", "is perfect.").also { stage++ } - 32 -> player(core.game.dialogue.FacialExpression.THINKING,"Maybe I should talk to him...").also { stage++ } - 33 -> npc(core.game.dialogue.FacialExpression.FRIENDLY,"Of course.", "He's always happy to talk to possible business", "partners.").also { stage = 1 } - - 40 -> npc(core.game.dialogue.FacialExpression.FRIENDLY,"Now why would you think that?").also { stage++ } - 41 -> player(core.game.dialogue.FacialExpression.FRIENDLY,"One of the shopkeepers told they were threatened", "by a man with a rake...").also { stage++ } - 42 -> npc(core.game.dialogue.FacialExpression.FRIENDLY,"Those people just don't want us to succeed.", "Don't listen to them!").also { stage = 99 } - - 99 -> end() } return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { - return AliTheFarmerDialogue(player) - } - override fun getIds(): IntArray { - return intArrayOf(2821) + return intArrayOf(NPCs.ALI_THE_FARMER_2821) } } \ No newline at end of file diff --git a/Server/src/main/content/region/desert/alkharid/dialogue/AliTheGuard.kt b/Server/src/main/content/region/desert/alkharid/dialogue/AliTheGuard.kt index a27e67966..7704e0a30 100644 --- a/Server/src/main/content/region/desert/alkharid/dialogue/AliTheGuard.kt +++ b/Server/src/main/content/region/desert/alkharid/dialogue/AliTheGuard.kt @@ -5,66 +5,60 @@ 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 AliTheGuard(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class AliTheGuard(player: Player? = null) : DialoguePlugin(player){ + + override fun newInstance(player: Player?): DialoguePlugin { + return AliTheGuard(player) + } override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - player(core.game.dialogue.FacialExpression.FRIENDLY,"Hello there!") - stage = 0 + playerl(FacialExpression.FRIENDLY,"Hello there!") return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 0 -> npc(core.game.dialogue.FacialExpression.ANNOYED,"I'm working. What do you have to say that's so urgent?").also { stage++ } - 1 -> options ("What can you tell me about Al Kharid?", "So, what do you do here?", - "I hear you work for Ali Morrisane...", "I hear you've been threatening the other shopkeepers.").also { stage++ } + 0 -> npcl(FacialExpression.ANNOYED,"I'm working. What do you have to say that's so urgent?").also { stage++ } + 1 -> options("What can you tell me about Al Kharid?", "So, what do you do here?", "I hear you work for Ali Morrisane...", "I hear you've been threatening the other shopkeepers.").also { stage++ } 2 -> when(buttonId){ - 1 -> player(core.game.dialogue.FacialExpression.ASKING, "What can you tell me about Al Kharid?").also { stage = 10 } - 2 -> player(core.game.dialogue.FacialExpression.ASKING, "So, what do you do here?").also { stage = 20 } - 3 -> player(core.game.dialogue.FacialExpression.ASKING, "I hear you work for Ali Morrisane...").also { stage = 30 } - 4 -> player(core.game.dialogue.FacialExpression.ASKING, "I hear you've been threatening the other shopkeepers.").also { stage = 40 } + 1 -> playerl(FacialExpression.ASKING, "What can you tell me about Al Kharid?").also { stage = 10 } + 2 -> playerl(FacialExpression.ASKING, "So, what do you do here?").also { stage = 20 } + 3 -> playerl(FacialExpression.ASKING, "I hear you work for Ali Morrisane...").also { stage = 30 } + 4 -> playerl(FacialExpression.ASKING, "I hear you've been threatening the other shopkeepers.").also { stage = 40 } + } + 10 -> npcl(FacialExpression.FRIENDLY,"There's a lot of space here. More open space than back home.").also { stage++ } + 11 -> playerl(FacialExpression.ASKING,"So where is back home?").also { stage++ } + 12 -> npcl(FacialExpression.FRIENDLY,"Pollnivneach. It's a town to the south of the Shantay Pass.").also { stage = 1 } + 20 -> npcl(FacialExpression.ANNOYED,"I'm on guard duty. Making sure nobody tries to steal anything from the house and tents in the middle of town.").also { stage++ } + 21 -> playerl(FacialExpression.ASKING,"Why are you only guarding those buildings?").also { stage++ } + 22 -> npcl(FacialExpression.SUSPICIOUS,"That's all I've been hired to guard.").also { stage = 1 } + 30 -> npcl(FacialExpression.FRIENDLY,"Yeah, he hired me. He owns this house and these two tents, too.").also { stage++ } + 31 -> playerl(FacialExpression.HALF_ASKING,"Is the work good?").also { stage++ } + 32 -> npcl(FacialExpression.FRIENDLY,"It pays better than back home.").also { stage++ } + 33 -> playerl(FacialExpression.ASKING,"Why, what did you do back home?").also { stage++ } + 34 -> npcl(FacialExpression.SUSPICIOUS,"Never you mind.").also { stage++ } + 35 -> npcl(FacialExpression.FRIENDLY,"But Ali Morrisane pays us well, at least.").also { stage++ } + 36 -> playerl(FacialExpression.ASKING,"Maybe I should talk to him...").also { stage++ } + 37 -> npcl(FacialExpression.FRIENDLY,"Why not? He always likes to meet potential business partners.").also { stage = 1 } + 40 -> npcl(FacialExpression.ANNOYED,"So? They talk too much.").also { stage++ } + 41 -> playerl(FacialExpression.HALF_ASKING,"You're not going to deny it?").also { stage++ } + 42 -> npcl(FacialExpression.LOUDLY_LAUGHING,"Why bother? None of them can fight back, after all.").also { stage = 99 } + 99 -> { + end() } - - 10 -> npc(core.game.dialogue.FacialExpression.FRIENDLY,"There's a lot of space here. More open space than back home.").also { stage++ } - 11 -> player(core.game.dialogue.FacialExpression.ASKING,"So where is back home?").also { stage++ } - 12 -> npc(core.game.dialogue.FacialExpression.FRIENDLY,"Pollnivneach. It's a town to the south of the", "Shantay Pass.").also { stage = 1 } - - 20 -> npc( - core.game.dialogue.FacialExpression.ANNOYED,"I'm on guard duty.", "Making sure nobody tries to steal anything from", - "the house and tents in the middle of town.").also { stage++ } - 21 -> player(core.game.dialogue.FacialExpression.ASKING,"Why are you only guarding those buildings?").also { stage++ } - 22 -> npc(core.game.dialogue.FacialExpression.SUSPICIOUS,"That's all I've been hired to guard.").also { stage = 1 } - - 30 -> npc(core.game.dialogue.FacialExpression.FRIENDLY,"Yeah, he hired me.", "He owns this house and these two tents, too.").also { stage++ } - 31 -> player(core.game.dialogue.FacialExpression.HALF_ASKING,"Is the work good?").also { stage++ } - 32 -> npc(core.game.dialogue.FacialExpression.FRIENDLY,"It pays better than back home.").also { stage++ } - 33 -> player(core.game.dialogue.FacialExpression.ASKING,"Why, what did you do back home?").also { stage++ } - 34 -> npc(core.game.dialogue.FacialExpression.SUSPICIOUS,"Never you mind.").also { stage++ } - 35 -> npc(core.game.dialogue.FacialExpression.FRIENDLY,"But Ali Morrisane pays us well, at least.").also { stage++ } - 36 -> player(core.game.dialogue.FacialExpression.ASKING,"Maybe I should talk to him...").also { stage++ } - 37 -> npc(core.game.dialogue.FacialExpression.FRIENDLY,"Why not?", "He always likes to meet potential business partners.").also { stage = 1 } - - 40 -> npc(core.game.dialogue.FacialExpression.ANNOYED,"So? They talk too much.").also { stage++ } - 41 -> player(core.game.dialogue.FacialExpression.HALF_ASKING,"You're not going to deny it?").also { stage++ } - 42 -> npc(core.game.dialogue.FacialExpression.LOUDLY_LAUGHING,"Why bother?", "None of them can fight back, after all.").also { stage = 99 } - - 99 -> end() } return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { - return AliTheGuard(player) - } - override fun getIds(): IntArray { - return intArrayOf(2823) + return intArrayOf(NPCs.ALI_THE_GUARD_2823) } } \ No newline at end of file diff --git a/Server/src/main/content/region/desert/alkharid/dialogue/AliTheLeafletDropperDialogue.kt b/Server/src/main/content/region/desert/alkharid/dialogue/AliTheLeafletDropperDialogue.kt index 5e1305d4a..3ff317b3c 100644 --- a/Server/src/main/content/region/desert/alkharid/dialogue/AliTheLeafletDropperDialogue.kt +++ b/Server/src/main/content/region/desert/alkharid/dialogue/AliTheLeafletDropperDialogue.kt @@ -6,9 +6,9 @@ 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 -import core.tools.END_DIALOGUE /** * @author bushtail @@ -16,63 +16,51 @@ import core.tools.END_DIALOGUE */ @Initializable -class AliTheLeafletDropperDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { +class AliTheLeafletDropperDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun newInstance(player: Player?): DialoguePlugin { return AliTheLeafletDropperDialogue(player) } override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - npcl(core.game.dialogue.FacialExpression.CHILD_NORMAL, "I don't have the time to talk right now! Ali Morrisane is paying me to hand out these flyers.") + npcl(FacialExpression.CHILD_NORMAL, "I don't have the time to talk right now! Ali Morrisane is paying me to hand out these flyers.") stage = 100 return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage) { - 100 -> options( - "Who is Ali Morissane?", - "What are the flyers for?", - "What is there to do round here, boy?" - ).also { stage++ } + 100 -> options("Who is Ali Morissane?", "What are the flyers for?", "What is there to do round here, boy?").also { stage++ } 101 -> when(buttonId) { - 1 -> { - player(core.game.dialogue.FacialExpression.ASKING, "Who is Ali Morissane?").also{ stage = 201 } - } - 2 -> { - player(core.game.dialogue.FacialExpression.ASKING, "What are the flyers for?").also { stage = 301 } - } - 3 -> { - player(core.game.dialogue.FacialExpression.ASKING, "What is there to do round here, boy?").also { stage = 401 } - } + 1 -> playerl(FacialExpression.ASKING, "Who is Ali Morissane?").also{ stage = 201 } + 2 -> playerl(FacialExpression.ASKING, "What are the flyers for?").also { stage = 301 } + 3 -> playerl(FacialExpression.ASKING, "What is there to do round here, boy?").also { stage = 401 } } - - 201 -> npcl(core.game.dialogue.FacialExpression.CHILD_FRIENDLY, "Ali Morrisane is the greatest merchant in the east!").also { stage++ } - 202 -> player(core.game.dialogue.FacialExpression.HALF_ASKING, "Were you paid to say that?").also { stage++ } - 203 -> npcl(core.game.dialogue.FacialExpression.CHILD_LOUDLY_LAUGHING, "Of course I was! You can find him on the north edge of town.").also { stage = END_DIALOGUE } - - 301 -> npcl(core.game.dialogue.FacialExpression.CHILD_THINKING, "Well, Ali Morrisane isn't too popular with the other traders in Al Kharid, mainly because he's from Pollnivneach and they feel he has no business trading in their town.").also { stage++ } - 302 -> npcl(core.game.dialogue.FacialExpression.CHILD_FRIENDLY, "I think they're just sour because he's better at making money than them.").also { stage++ } - 303 -> npcl(core.game.dialogue.FacialExpression.CHILD_FRIENDLY, "The flyer advertises the different shops you can find in Al Kharid.").also { stage++ } - 304 -> npcl(core.game.dialogue.FacialExpression.CHILD_THINKING, "It also entitles you to money off your next purchase in any of the shops listed on it. It's Ali's way of getting on the good side of the traders.").also { stage++ } - 305 -> player(core.game.dialogue.FacialExpression.ASKING, "Which shops?").also { + 201 -> npcl(FacialExpression.CHILD_FRIENDLY, "Ali Morrisane is the greatest merchant in the east!").also { stage++ } + 202 -> playerl(FacialExpression.HALF_ASKING, "Were you paid to say that?").also { stage++ } + 203 -> npcl(FacialExpression.CHILD_LOUDLY_LAUGHING, "Of course I was! You can find him on the north edge of town.").also { stage = END_DIALOGUE } + 301 -> npcl(FacialExpression.CHILD_THINKING, "Well, Ali Morrisane isn't too popular with the other traders in Al Kharid, mainly because he's from Pollnivneach and they feel he has no business trading in their town.").also { stage++ } + 302 -> npcl(FacialExpression.CHILD_FRIENDLY, "I think they're just sour because he's better at making money than them.").also { stage++ } + 303 -> npcl(FacialExpression.CHILD_FRIENDLY, "The flyer advertises the different shops you can find in Al Kharid.").also { stage++ } + 304 -> npcl(FacialExpression.CHILD_THINKING, "It also entitles you to money off your next purchase in any of the shops listed on it. It's Ali's way of getting on the good side of the traders.").also { stage++ } + 305 -> playerl(FacialExpression.ASKING, "Which shops?").also { if(player.inventory.containItems(Items.AL_KHARID_FLYER_7922)) { - npcl(core.game.dialogue.FacialExpression.CHILD_SUSPICIOUS, "Are you trying to be funny or has age turned your brain to mush? Look at the flyer you already have!").also { stage = END_DIALOGUE } + npcl(FacialExpression.CHILD_SUSPICIOUS, "Are you trying to be funny or has age turned your brain to mush? Look at the flyer you already have!").also { stage = END_DIALOGUE } } else { if(addItem(player, Items.AL_KHARID_FLYER_7922)) { - npcl(core.game.dialogue.FacialExpression.CHILD_FRIENDLY, "Here! Take one and let me get back to work.").also { stage = END_DIALOGUE } + npcl(FacialExpression.CHILD_FRIENDLY, "Here! Take one and let me get back to work.").also { stage = END_DIALOGUE } } else { end() } } } - - 401 -> npcl(core.game.dialogue.FacialExpression.CHILD_NORMAL, "I'm very busy, so listen carefully! I shall say this only once.").also { stage++ } - 402 -> npcl(core.game.dialogue.FacialExpression.CHILD_THINKING, "Apart from a busy and wonderous market place in Al Kharid to the south, there is the Duel Arena to the south-east where you can challenge other players to a fight.").also { stage++ } - 403 -> npcl(core.game.dialogue.FacialExpression.CHILD_NORMAL, "If you're here to make money, there is a mine to the south.").also { stage++ } - 404 -> npcl(core.game.dialogue.FacialExpression.CHILD_SUSPICIOUS, "Watch out for scorpions though, they'll take a pop at you if you go too near them. To avoid them just follow the western fence as you travel south.").also { stage++ } - 405 -> npcl(core.game.dialogue.FacialExpression.CHILD_FRIENDLY, "If you're in the mood for a little rest and relaxation, there are a couple of nice fishing spots south of the town.").also { stage++ } - 406 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "Thanks for the help!").also { stage = END_DIALOGUE } + 401 -> npcl(FacialExpression.CHILD_NORMAL, "I'm very busy, so listen carefully! I shall say this only once.").also { stage++ } + 402 -> npcl(FacialExpression.CHILD_THINKING, "Apart from a busy and wonderous market place in Al Kharid to the south, there is the Duel Arena to the south-east where you can challenge other players to a fight.").also { stage++ } + 403 -> npcl(FacialExpression.CHILD_NORMAL, "If you're here to make money, there is a mine to the south.").also { stage++ } + 404 -> npcl(FacialExpression.CHILD_SUSPICIOUS, "Watch out for scorpions though, they'll take a pop at you if you go too near them. To avoid them just follow the western fence as you travel south.").also { stage++ } + 405 -> npcl(FacialExpression.CHILD_FRIENDLY, "If you're in the mood for a little rest and relaxation, there are a couple of nice fishing spots south of the town.").also { stage++ } + 406 -> playerl(FacialExpression.FRIENDLY, "Thanks for the help!").also { stage = END_DIALOGUE } } return true } diff --git a/Server/src/main/content/region/desert/alkharid/dialogue/AliTheLeafletDropperListener.kt b/Server/src/main/content/region/desert/alkharid/dialogue/AliTheLeafletDropperListener.kt index 423f7e012..0f662d917 100644 --- a/Server/src/main/content/region/desert/alkharid/dialogue/AliTheLeafletDropperListener.kt +++ b/Server/src/main/content/region/desert/alkharid/dialogue/AliTheLeafletDropperListener.kt @@ -2,13 +2,14 @@ package content.region.desert.alkharid.dialogue import core.api.addItem import core.api.openDialogue +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.tools.END_DIALOGUE import org.rs09.consts.Items import org.rs09.consts.NPCs -import core.game.dialogue.DialogueFile -import core.game.interaction.InteractionListener -import core.game.interaction.IntType -import core.tools.END_DIALOGUE /** * @author bushtail @@ -29,16 +30,17 @@ class AliTheLeafletDropperListener : InteractionListener { return@on true } } + } class DropperDialogue(val it : Int) : DialogueFile() { override fun handle(componentID: Int, buttonID: Int) { when(it) { 1 -> when(stage) { - 0 -> npcl(core.game.dialogue.FacialExpression.CHILD_NORMAL, "Here! Take one and let me get back to work.").also { stage++ } - 1 -> npcl(core.game.dialogue.FacialExpression.CHILD_THINKING, "I still have hundreds of these flyers to hand out. I wonder if Ali would notice if I quietly dumped them somewhere?").also { stage = END_DIALOGUE } + 0 -> npcl(FacialExpression.CHILD_NORMAL, "Here! Take one and let me get back to work.").also { stage++ } + 1 -> npcl(FacialExpression.CHILD_THINKING, "I still have hundreds of these flyers to hand out. I wonder if Ali would notice if I quietly dumped them somewhere?").also { stage = END_DIALOGUE } } - 2 -> npcl(core.game.dialogue.FacialExpression.CHILD_SUSPICIOUS, "Are you trying to be funny or has age turned your brain to mush? You already have a flyer!").also { stage = END_DIALOGUE } + 2 -> npcl(FacialExpression.CHILD_SUSPICIOUS, "Are you trying to be funny or has age turned your brain to mush? You already have a flyer!").also { stage = END_DIALOGUE } } } diff --git a/Server/src/main/content/region/desert/alkharid/dialogue/AliTheSmithDialogue.kt b/Server/src/main/content/region/desert/alkharid/dialogue/AliTheSmithDialogue.kt index 153e3d817..ff79c3deb 100644 --- a/Server/src/main/content/region/desert/alkharid/dialogue/AliTheSmithDialogue.kt +++ b/Server/src/main/content/region/desert/alkharid/dialogue/AliTheSmithDialogue.kt @@ -5,64 +5,63 @@ 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 AliTheSmithDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class AliTheSmithDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun newInstance(player: Player?): DialoguePlugin { + return AliTheSmithDialogue(player) + } override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - player(core.game.dialogue.FacialExpression.FRIENDLY,"Hello there!") - stage = 0 + playerl(FacialExpression.FRIENDLY,"Hello there!") return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 0 -> npc(core.game.dialogue.FacialExpression.FRIENDLY,"You seem rather cheerful. Is there anything you're after?").also { stage++ } - 1 -> options ("What can you tell me about Al Kharid?", "So, what do you do here?", - "I hear you work for Ali Morrisane...", "I hear you've been threatening the other shopkeepers.").also { stage++ } + 0 -> npcl(FacialExpression.FRIENDLY,"You seem rather cheerful. Is there anything you're after?").also { stage++ } + 1 -> options("What can you tell me about Al Kharid?", "So, what do you do here?", "I hear you work for Ali Morrisane...", "I hear you've been threatening the other shopkeepers.").also { stage++ } 2 -> when(buttonId){ - 1 -> player(core.game.dialogue.FacialExpression.ASKING, "What can you tell me about Al Kharid?").also { stage = 10 } - 2 -> player(core.game.dialogue.FacialExpression.ASKING, "So, what do you do here?").also { stage = 20 } - 3 -> player(core.game.dialogue.FacialExpression.ASKING, "I hear you work for Ali Morrisane...").also { stage = 30 } - 4 -> player(core.game.dialogue.FacialExpression.HALF_ASKING, "I hear you've been threatening the other shopkeepers.").also { stage = 40 } + 1 -> playerl(FacialExpression.ASKING, "What can you tell me about Al Kharid?").also { stage = 10 } + 2 -> playerl(FacialExpression.ASKING, "So, what do you do here?").also { stage = 20 } + 3 -> playerl(FacialExpression.ASKING, "I hear you work for Ali Morrisane...").also { stage = 30 } + 4 -> playerl(FacialExpression.HALF_ASKING, "I hear you've been threatening the other shopkeepers.").also { stage = 40 } } - 10 ->npc(core.game.dialogue.FacialExpression.FRIENDLY,"Well, it's hot and full of sand.").also { stage++ } - 11 ->player(core.game.dialogue.FacialExpression.ASKING,"Anything else?").also { stage++ } - 12 ->npc(core.game.dialogue.FacialExpression.FRIENDLY,"Don't ask me, I'm not from around here.").also { stage++ } - 13 ->player(core.game.dialogue.FacialExpression.HALF_ASKING,"So where are you from?").also { stage++ } - 14 ->npc(core.game.dialogue.FacialExpression.HALF_GUILTY,"A town to the south of the Shantay Pass,","called Pollnivneach.").also { stage = 1 } + 10 ->npcl(FacialExpression.FRIENDLY,"Well, it's hot and full of sand.").also { stage++ } + 11 ->playerl(FacialExpression.ASKING,"Anything else?").also { stage++ } + 12 ->npcl(FacialExpression.FRIENDLY,"Don't ask me, I'm not from around here.").also { stage++ } + 13 ->playerl(FacialExpression.HALF_ASKING,"So where are you from?").also { stage++ } + 14 ->npcl(FacialExpression.HALF_GUILTY,"A town to the south of the Shantay Pass, called Pollnivneach.").also { stage = 1 } - 20 ->npc(core.game.dialogue.FacialExpression.SAD,"Not very much, at the moment.", "I came from further south to set up a smithy here...").also { stage++ } - 21 ->npc(core.game.dialogue.FacialExpression.SAD,"...but we still haven't set up the shops,", "so we have no customers yet.").also { stage = 1 } + 20 ->npcl(FacialExpression.SAD,"Not very much, at the moment. I came from further south to set up a smithy here...").also { stage++ } + 21 ->npcl(FacialExpression.SAD,"...but we still haven't set up the shops, so we have no customers yet.").also { stage = 1 } - 30 -> npc(core.game.dialogue.FacialExpression.FRIENDLY,"Yes, he's the one who persuaded us to come here.").also { stage++ } - 31 -> npc( - core.game.dialogue.FacialExpression.HALF_GUILTY,"He said we'd have lots of customers once we set up", - "our shops, and all he asks for is a cut"," of the profits.").also { stage++ } - 32 -> player(core.game.dialogue.FacialExpression.ASKING,"Maybe I should talk to him...").also { stage++ } - 33 -> npc(core.game.dialogue.FacialExpression.FRIENDLY,"Of course.", "He's always happy to talk to possible business partners.").also { stage = 1 } + 30 -> npcl(FacialExpression.FRIENDLY,"Yes, he's the one who persuaded us to come here.").also { stage++ } + 31 -> npcl(FacialExpression.HALF_GUILTY,"He said we'd have lots of customers once we set up our shops, and all he asks for is a cut of the profits.").also { stage++ } + 32 -> playerl(FacialExpression.ASKING,"Maybe I should talk to him...").also { stage++ } + 33 -> npcl(FacialExpression.FRIENDLY,"Of course. He's always happy to talk to possible business partners.").also { stage = 1 } - 40 -> npc(core.game.dialogue.FacialExpression.SUSPICIOUS,"What? Who's spreading that rumour?").also { stage++ } - 41 -> player(core.game.dialogue.FacialExpression.THINKING,"Well, one of the shopkeepers told me a man with", "a large hammer came to threaten them.").also { stage++ } - 42 -> npc(core.game.dialogue.FacialExpression.SUSPICIOUS,"Don't pay any attention to those people.").also { stage++ } - 43 -> npc(core.game.dialogue.FacialExpression.LAUGH,"They're just worried that they'll lose money", "when we open our shops.").also { stage = 99 } + 40 -> npcl(FacialExpression.SUSPICIOUS,"What? Who's spreading that rumour?").also { stage++ } + 41 -> playerl(FacialExpression.THINKING,"Well, one of the shopkeepers told me a man with a large hammer came to threaten them.").also { stage++ } + 42 -> npcl(FacialExpression.SUSPICIOUS,"Don't pay any attention to those people.").also { stage++ } + 43 -> npcl(FacialExpression.LAUGH,"They're just worried that they'll lose money when we open our shops.").also { stage = 99 } + 99 -> { + end() + } - 99 -> end() } return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { - return AliTheSmithDialogue(player) - } - override fun getIds(): IntArray { - return intArrayOf(2820) + return intArrayOf(NPCs.ALI_THE_SMITH_2820) } } \ No newline at end of file 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/DommikDialogue.java b/Server/src/main/content/region/desert/alkharid/dialogue/DommikDialogue.java deleted file mode 100644 index 1fe401e9a..000000000 --- a/Server/src/main/content/region/desert/alkharid/dialogue/DommikDialogue.java +++ /dev/null @@ -1,72 +0,0 @@ -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.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Handles the DommikDialogue dialogue. - * @author 'Vexia - */ -@Initializable -public class DommikDialogue extends DialoguePlugin { - - public DommikDialogue() { - - } - - public DommikDialogue(Player player) { - super(player); - } - - @Override - public int[] getIds() { - return new int[] { 545 }; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendOptions("Select an Option", "No, thanks, I've got all the Crafting 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 Crafting equipment I need."); - stage = 10; - break; - case 2: - end(); - npc.openShop(player); - break; - } - break; - case 10: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Okay. Fare well on your travels."); - stage = 11; - break; - case 11: - end(); - break; - } - return true; - } - - @Override - public DialoguePlugin newInstance(Player player) { - - return new DommikDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Would you like to buy some Crafting equipment?"); - stage = 0; - return true; - } -} diff --git a/Server/src/main/content/region/desert/alkharid/dialogue/DommikDialogue.kt b/Server/src/main/content/region/desert/alkharid/dialogue/DommikDialogue.kt new file mode 100644 index 000000000..75d9cd46c --- /dev/null +++ b/Server/src/main/content/region/desert/alkharid/dialogue/DommikDialogue.kt @@ -0,0 +1,47 @@ +package content.region.desert.alkharid.dialogue + +import core.api.sendDialogueOptions +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 + +/** + * Handles the DommikDialogue dialogue. + * @author 'Vexia + */ +@Initializable +class DommikDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player?): DialoguePlugin { + return DommikDialogue(player) + } + + override fun open(vararg args: Any): Boolean { + npc = args[0] as NPC + npcl(FacialExpression.HAPPY, "Would you like to buy some Crafting equipment?") + stage = 0 + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage) { + 0 -> options("No, thanks, I've got all the Crafting equipment I need.", "Let's see what you've got, then.").also { stage = 1 } + 1 -> when (buttonId) { + 1 -> playerl(FacialExpression.FRIENDLY, "No, thanks, I've got all the Crafting equipment I need.").also { stage = 2 } + 2 -> { + end() + npc.openShop(player) + } + } + 2 -> npcl(FacialExpression.FRIENDLY, "Okay. Fare well on your travels.").also { stage = 3 } + 3 -> end() + } + return true + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.DOMMIK_545) + } +} 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/FadliDialogue.kt b/Server/src/main/content/region/desert/alkharid/dialogue/FadliDialogue.kt index 40c3d6967..c82704891 100644 --- a/Server/src/main/content/region/desert/alkharid/dialogue/FadliDialogue.kt +++ b/Server/src/main/content/region/desert/alkharid/dialogue/FadliDialogue.kt @@ -1,54 +1,56 @@ package content.region.desert.alkharid.dialogue +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 org.rs09.consts.NPCs -import core.game.dialogue.Topic -import core.game.interaction.InteractionListener -import core.game.interaction.IntType import core.tools.END_DIALOGUE +import org.rs09.consts.NPCs /** * @author bushtail */ @Initializable -class FadliDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { +class FadliDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player?): DialoguePlugin { return FadliDialogue(player) } override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - player(core.game.dialogue.FacialExpression.FRIENDLY, "Hi!").also { stage = 0 } + player(FacialExpression.FRIENDLY, "Hi!").also { stage = 0 } return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage) { - 0 -> npcl(core.game.dialogue.FacialExpression.HALF_ASKING, "What?").also { stage++ } + 0 -> npcl(FacialExpression.HALF_ASKING, "What?").also { stage++ } 1 -> showTopics( Topic("What do you do?", 101), Topic("What is this place?", 201), - Topic(core.game.dialogue.FacialExpression.FRIENDLY,"I'd like to store some items, please.", 301), + Topic(FacialExpression.FRIENDLY,"I'd like to store some items, please.", 301), Topic("Do you watch any matches?", 401) ) - 101 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "You can store your stuff here if you want. You can dump anything you don't want to carry whilst your fighting duels and then pick it up again on the way out.").also { stage++ } - 102 -> npcl(core.game.dialogue.FacialExpression.SAD, "To be honest I'm wasted here.").also { stage++ } - 103 -> npcl(core.game.dialogue.FacialExpression.EVIL_LAUGH, "I should be winning duels in an arena! I'm the best warrior in Al Kharid!").also { stage++ } - 104 -> player(core.game.dialogue.FacialExpression.HALF_WORRIED, "Easy, tiger!").also { stage = END_DIALOGUE } + 101 -> npcl(FacialExpression.FRIENDLY, "You can store your stuff here if you want. You can dump anything you don't want to carry whilst your fighting duels and then pick it up again on the way out.").also { stage++ } + 102 -> npcl(FacialExpression.SAD, "To be honest I'm wasted here.").also { stage++ } + 103 -> npcl(FacialExpression.EVIL_LAUGH, "I should be winning duels in an arena! I'm the best warrior in Al Kharid!").also { stage++ } + 104 -> player(FacialExpression.HALF_WORRIED, "Easy, tiger!").also { stage = END_DIALOGUE } - 201 -> npcl(core.game.dialogue.FacialExpression.LAUGH, "Isn't it obvious?").also { stage++ } - 202 -> npcl(core.game.dialogue.FacialExpression.ANNOYED, "This is the Duel Arena...duh!").also { stage = END_DIALOGUE } + 201 -> npcl(FacialExpression.LAUGH, "Isn't it obvious?").also { stage++ } + 202 -> npcl(FacialExpression.ANNOYED, "This is the Duel Arena...duh!").also { stage = END_DIALOGUE } - 301 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Sure.").also { + 301 -> npcl(FacialExpression.FRIENDLY, "Sure.").also { end() player.bank.open() } - 401 -> npcl(core.game.dialogue.FacialExpression.LAUGH, "Most aren't any good so I throw rotten fruit at them!").also { stage = END_DIALOGUE } + 401 -> npcl(FacialExpression.LAUGH, "Most aren't any good so I throw rotten fruit at them!").also { stage = END_DIALOGUE } } return true } 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 3e489d020..cd6bd5d5e 100644 --- a/Server/src/main/content/region/desert/alkharid/dialogue/GemTraderDialogue.kt +++ b/Server/src/main/content/region/desert/alkharid/dialogue/GemTraderDialogue.kt @@ -4,6 +4,10 @@ import core.game.dialogue.DialoguePlugin 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 @@ -12,24 +16,15 @@ import core.plugin.Initializable */ @Initializable -class GemTraderDialogue (player: Player? = null): core.game.dialogue.DialoguePlugin(player){ - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { +class GemTraderDialogue (player: Player? = null): DialoguePlugin(player){ + override fun newInstance(player: Player?): DialoguePlugin { return GemTraderDialogue(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 } @@ -44,16 +39,16 @@ class GemTraderDialogue (player: Player? = null): core.game.dialogue.DialoguePlu } 10 -> when(buttonId){ - 1 -> npc.openShop(player).also { stage = 1000 }; - 2 -> player("No Thank you").also{stage = 1000} + 1 -> npc.openShop(player).also { stage = 1000 } + 2 -> player("No, thank you.").also{stage = 1000} 3 -> npc("Fitzharmon, eh? Hmmm... If I'm not mistaken, ", "that's the family name of a member ", "of the Varrockian nobility.").also{stage = 100} } 20 -> when(buttonId){ - 1 -> npc.openShop(player).also { stage = 1000 }; - 2 -> player("No Thank you").also{stage = 1000} + 1 -> npc.openShop(player).also { stage = 1000 } + 2 -> player("No, thank you.").also{stage = 1000} } 100 -> npc("You know, I HAVE seen someone of that" , @@ -71,8 +66,8 @@ class GemTraderDialogue (player: Player? = null): core.game.dialogue.DialoguePlu "high standards of 'gold perfection', then...").also{stage++} 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) + stage = 1000 + setQuestStage(player, Quests.FAMILY_CREST, 13) } 1000 -> end() @@ -81,7 +76,7 @@ class GemTraderDialogue (player: Player? = null): core.game.dialogue.DialoguePlu } override fun getIds(): IntArray { - return intArrayOf(540) + return intArrayOf(NPCs.GEM_TRADER_540) } 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/dialogue/JaraahDialogue.kt b/Server/src/main/content/region/desert/alkharid/dialogue/JaraahDialogue.kt index a074e972d..d114990c4 100644 --- a/Server/src/main/content/region/desert/alkharid/dialogue/JaraahDialogue.kt +++ b/Server/src/main/content/region/desert/alkharid/dialogue/JaraahDialogue.kt @@ -4,34 +4,34 @@ import core.api.animate import core.api.getStatLevel 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.entity.skill.Skills import core.plugin.Initializable +import core.tools.END_DIALOGUE import org.rs09.consts.Animations import org.rs09.consts.NPCs -import core.game.dialogue.Topic -import core.tools.END_DIALOGUE /** * @author bushtail */ @Initializable -class JaraahDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { +class JaraahDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player?): DialoguePlugin { return JaraahDialogue(player) } override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - player(core.game.dialogue.FacialExpression.FRIENDLY, "Hi!").also { stage = 0 } + player(FacialExpression.FRIENDLY, "Hi!").also { stage = 0 } return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage) { - 0 -> npcl(core.game.dialogue.FacialExpression.ANNOYED, "What? Can't you see I'm busy?!").also { stage++ } + 0 -> npcl(FacialExpression.ANNOYED, "What? Can't you see I'm busy?!").also { stage++ } 1 -> showTopics( Topic("Can you heal me?", 101), Topic("You must see some gruesome things?", 201), @@ -43,19 +43,19 @@ class JaraahDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin animate(npc!!, Animations.HUMAN_PICKPOCKETING_881) if(player!!.skills.lifepoints < getStatLevel(player!!, Skills.HITPOINTS)) { player!!.skills.heal(21) - npcl(core.game.dialogue.FacialExpression.FRIENDLY, "There you go!") + npcl(FacialExpression.FRIENDLY, "There you go!") } else { - npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Okay, this will hurt you more than it will me.") + npcl(FacialExpression.FRIENDLY, "Okay, this will hurt you more than it will me.") } stage = END_DIALOGUE } - 201 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "It's a gruesome business and with the tools they give me it gets mroe gruesome before it gets better!").also { stage = END_DIALOGUE } + 201 -> npcl(FacialExpression.FRIENDLY, "It's a gruesome business and with the tools they give me it gets mroe gruesome before it gets better!").also { stage = END_DIALOGUE } - 301 -> npcl(core.game.dialogue.FacialExpression.HALF_THINKING, "'The Butcher'?").also { stage++ } - 302 -> npcl(core.game.dialogue.FacialExpression.LAUGH, "Ha!").also { stage++ } - 303 -> npcl(core.game.dialogue.FacialExpression.HALF_ASKING, "Would you like me to demonstrate?").also { stage++ } - 304 -> player(core.game.dialogue.FacialExpression.AFRAID, "Er...I'll give it a miss, thanks.").also { stage = END_DIALOGUE } + 301 -> npcl(FacialExpression.HALF_THINKING, "'The Butcher'?").also { stage++ } + 302 -> npcl(FacialExpression.LAUGH, "Ha!").also { stage++ } + 303 -> npcl(FacialExpression.HALF_ASKING, "Would you like me to demonstrate?").also { stage++ } + 304 -> player(FacialExpression.AFRAID, "Er...I'll give it a miss, thanks.").also { stage = END_DIALOGUE } } return true } diff --git a/Server/src/main/content/region/desert/alkharid/dialogue/KarimDialogue.java b/Server/src/main/content/region/desert/alkharid/dialogue/KarimDialogue.java index 49c499b4a..41c010f1a 100644 --- a/Server/src/main/content/region/desert/alkharid/dialogue/KarimDialogue.java +++ b/Server/src/main/content/region/desert/alkharid/dialogue/KarimDialogue.java @@ -50,7 +50,7 @@ public final class KarimDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Would you like to buy a nice kebab? Only one gold."); + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Would you like to buy a nice kebab? Only one gold."); stage = 0; return true; } @@ -69,7 +69,7 @@ public final class KarimDialogue extends DialoguePlugin { stage = 10; break; case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Yes please."); + interpreter.sendDialogues(player, FacialExpression.HAPPY, "Yes please."); stage = 20; break; diff --git a/Server/src/main/content/region/desert/alkharid/dialogue/LouiLegsDialogue.java b/Server/src/main/content/region/desert/alkharid/dialogue/LouiLegsDialogue.java index d05b7e69d..f20999304 100644 --- a/Server/src/main/content/region/desert/alkharid/dialogue/LouiLegsDialogue.java +++ b/Server/src/main/content/region/desert/alkharid/dialogue/LouiLegsDialogue.java @@ -39,7 +39,7 @@ public final class LouiLegsDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Hey, wanna buy some armour?"); + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Hey, wanna buy some armour?"); stage = 0; return true; } @@ -54,18 +54,18 @@ public final class LouiLegsDialogue extends DialoguePlugin { case 1: switch (buttonId) { case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "What have you got?"); + interpreter.sendDialogues(player, FacialExpression.THINKING, "What have you got?"); stage = 10; break; case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No, thank you."); + interpreter.sendDialogues(player, FacialExpression.FRIENDLY, "No, thank you."); stage = 20; break; } break; case 10: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "I provide items to help you keep your legs!"); + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "I provide items to help you keep your legs!"); stage = 11; break; case 11: diff --git a/Server/src/main/content/region/desert/alkharid/dialogue/RanaelDialogue.java b/Server/src/main/content/region/desert/alkharid/dialogue/RanaelDialogue.java index b12f36b9e..cb52690ce 100644 --- a/Server/src/main/content/region/desert/alkharid/dialogue/RanaelDialogue.java +++ b/Server/src/main/content/region/desert/alkharid/dialogue/RanaelDialogue.java @@ -62,7 +62,7 @@ public class RanaelDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Do you want to buy any armoured skirts? Designed", "especially for ladies who like to fight."); + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Do you want to buy any armoured skirts? Designed", "especially for ladies who like to fight."); stage = 0; return true; } diff --git a/Server/src/main/content/region/desert/alkharid/dialogue/SabreenDialogue.kt b/Server/src/main/content/region/desert/alkharid/dialogue/SabreenDialogue.kt index e3c20d5e6..7bfeb1651 100644 --- a/Server/src/main/content/region/desert/alkharid/dialogue/SabreenDialogue.kt +++ b/Server/src/main/content/region/desert/alkharid/dialogue/SabreenDialogue.kt @@ -2,26 +2,26 @@ package content.region.desert.alkharid.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 org.rs09.consts.NPCs -import core.game.dialogue.Topic import core.tools.END_DIALOGUE +import org.rs09.consts.NPCs /** * @author bushtail */ @Initializable -class SabreenDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { +class SabreenDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player?): DialoguePlugin { return SabreenDialogue(player) } override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - player(core.game.dialogue.FacialExpression.FRIENDLY, "Hi!").also { stage = 0 } + player(FacialExpression.FRIENDLY, "Hi!").also { stage = 0 } return true } @@ -33,11 +33,11 @@ class SabreenDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugi Topic("Do you come here often?", 201) ) - 101 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "I work here, so yes!").also { stage = END_DIALOGUE } + 101 -> npcl(FacialExpression.FRIENDLY, "I work here, so yes!").also { stage = END_DIALOGUE } - 201 -> npcl(core.game.dialogue.FacialExpression.HALF_THINKING, "Yes I do. Thankfully we can cope with almost anything. Jaraah really is a wonderful surgeon, his methods are a little unorthodox but he gets the job done.").also { stage++ } - 202 -> npcl(core.game.dialogue.FacialExpression.HALF_GUILTY, "I shouldn't tell you this but his nickname is 'The Butcher'.").also { stage++ } - 203 -> player(core.game.dialogue.FacialExpression.HALF_WORRIED, "That's reassuring.").also { stage = END_DIALOGUE } + 201 -> npcl(FacialExpression.HALF_THINKING, "Yes I do. Thankfully we can cope with almost anything. Jaraah really is a wonderful surgeon, his methods are a little unorthodox but he gets the job done.").also { stage++ } + 202 -> npcl(FacialExpression.HALF_GUILTY, "I shouldn't tell you this but his nickname is 'The Butcher'.").also { stage++ } + 203 -> playerl(FacialExpression.HALF_WORRIED, "That's reassuring.").also { stage = END_DIALOGUE } } return true } diff --git a/Server/src/main/content/region/desert/alkharid/dialogue/SurgeonGeneralTafaniDialogue.kt b/Server/src/main/content/region/desert/alkharid/dialogue/SurgeonGeneralTafaniDialogue.kt index f23720b63..eb6428519 100644 --- a/Server/src/main/content/region/desert/alkharid/dialogue/SurgeonGeneralTafaniDialogue.kt +++ b/Server/src/main/content/region/desert/alkharid/dialogue/SurgeonGeneralTafaniDialogue.kt @@ -1,7 +1,7 @@ package content.region.desert.alkharid.dialogue +import core.api.hasLevelStat import core.game.dialogue.DialoguePlugin -import core.api.* import core.game.dialogue.FacialExpression import core.game.dialogue.IfTopic import core.game.dialogue.Topic @@ -10,22 +10,22 @@ 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 import core.tools.END_DIALOGUE +import org.rs09.consts.NPCs /** * @author bushtail */ @Initializable -class SurgeonGeneralTafaniDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { +class SurgeonGeneralTafaniDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player?): DialoguePlugin { return SurgeonGeneralTafaniDialogue(player) } override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Hi. How can I help?").also { stage = 100 } + npcl(FacialExpression.FRIENDLY, "Hi. How can I help?").also { stage = 100 } return true } @@ -39,26 +39,26 @@ class SurgeonGeneralTafaniDialogue(player: Player? = null) : core.game.dialogue. IfTopic("Where can I get a cape like yours?", 501, !hasLevelStat(player, Skills.HITPOINTS, 99)) ) - 201 -> npcl(core.game.dialogue.FacialExpression.HALF_THINKING, "Yes I do. Thankfully we can cope with almost anything. Jaraah really is a wonderful surgeon, his methods are a little unorthodox but he gets the job done.").also { stage++ } - 202 -> npcl(core.game.dialogue.FacialExpression.HALF_GUILTY, "I shouldn't tell you this but his nickname is 'The Butcher'.").also { stage++ } - 203 -> player(core.game.dialogue.FacialExpression.HALF_WORRIED, "That's reassuring.").also { stage = END_DIALOGUE } + 201 -> npcl(FacialExpression.HALF_THINKING, "Yes I do. Thankfully we can cope with almost anything. Jaraah really is a wonderful surgeon, his methods are a little unorthodox but he gets the job done.").also { stage++ } + 202 -> npcl(FacialExpression.HALF_GUILTY, "I shouldn't tell you this but his nickname is 'The Butcher'.").also { stage++ } + 203 -> playerl(FacialExpression.HALF_WORRIED, "That's reassuring.").also { stage = END_DIALOGUE } - 301 -> npcl(core.game.dialogue.FacialExpression.LAUGH, "I work here, so yes!").also { stage = END_DIALOGUE } + 301 -> npcl(FacialExpression.LAUGH, "I work here, so yes!").also { stage = END_DIALOGUE } - 401 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Why, certainly my friend. However, owning such an item makes you part of an elite group and that privilege will cost you 99000 coins.").also { stage++ } + 401 -> npcl(FacialExpression.FRIENDLY, "Why, certainly my friend. However, owning such an item makes you part of an elite group and that privilege will cost you 99000 coins.").also { stage++ } 402 -> showTopics( - Topic(core.game.dialogue.FacialExpression.HALF_GUILTY, "Sorry, that's much too pricey.", 411), - Topic(core.game.dialogue.FacialExpression.HAPPY, "Sure, that's not too expensive for such a magnificent cape.", 421) + Topic(FacialExpression.HALF_GUILTY, "Sorry, that's much too pricey.", 411), + Topic(FacialExpression.HAPPY, "Sure, that's not too expensive for such a magnificent cape.", 421) ) - 411 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "I'm sorry you feel that way. Still, if you change your mind...").also { stage = END_DIALOGUE } - 421 -> if(core.game.global.Skillcape.purchase(player, Skills.HITPOINTS)) { - npcl(core.game.dialogue.FacialExpression.FRIENDLY, "There you go! I hope you enjoy it.").also { stage = END_DIALOGUE } + 411 -> npcl(FacialExpression.NEUTRAL, "I'm sorry you feel that way. Still, if you change your mind...").also { stage = END_DIALOGUE } + 421 -> if(Skillcape.purchase(player, Skills.HITPOINTS)) { + npcl(FacialExpression.FRIENDLY, "There you go! I hope you enjoy it.").also { stage = END_DIALOGUE } } else { - npcl(core.game.dialogue.FacialExpression.HALF_GUILTY, "Sorry, you can't buy that right now.").also { stage = END_DIALOGUE } + npcl(FacialExpression.HALF_GUILTY, "Sorry, you can't buy that right now.").also { stage = END_DIALOGUE } } - 501 -> npcl(core.game.dialogue.FacialExpression.HALF_THINKING, "Well, these capes are only available for people who have achieved a Hitpoint level of 99. You should go and train some more and come back to me when your Hitpoints are higher.").also { stage = END_DIALOGUE } + 501 -> npcl(FacialExpression.HALF_THINKING, "Well, these capes are only available for people who have achieved a Hitpoint level of 99. You should go and train some more and come back to me when your Hitpoints are higher.").also { stage = END_DIALOGUE } } return true } diff --git a/Server/src/main/content/region/desert/alkharid/dialogue/ZekeDialogue.java b/Server/src/main/content/region/desert/alkharid/dialogue/ZekeDialogue.java index 9c7b5e0ff..0ffb354f0 100644 --- a/Server/src/main/content/region/desert/alkharid/dialogue/ZekeDialogue.java +++ b/Server/src/main/content/region/desert/alkharid/dialogue/ZekeDialogue.java @@ -39,7 +39,7 @@ public final class ZekeDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "A thousand greetings, sir."); + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "A thousand greetings, sir."); stage = 0; return true; } @@ -54,21 +54,21 @@ public final class ZekeDialogue extends DialoguePlugin { case 1: switch (buttonId) { case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Do you want to trade?"); + interpreter.sendDialogues(player, FacialExpression.HAPPY, "Do you want to trade?"); stage = 10; break; case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Nice cloak."); + interpreter.sendDialogues(player, FacialExpression.HAPPY, "Nice cloak."); stage = 20; break; case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Could you sell me a dragon scimitar?"); + interpreter.sendDialogues(player, FacialExpression.HAPPY, "Could you sell me a dragon scimitar?"); stage = 30; break; } break; case 10: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Yes, certainly. I deal in scimitars."); + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Yes, certainly. I deal in scimitars."); stage = 11; break; case 11: @@ -76,34 +76,34 @@ public final class ZekeDialogue extends DialoguePlugin { end(); break; case 20: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Thank you."); + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Thank you."); stage = 21; break; case 21: end(); break; case 30: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "A dragon scimitar? A DRAGON scimitar?"); + interpreter.sendDialogues(npc, FacialExpression.EXTREMELY_SHOCKED, "A dragon scimitar? A DRAGON scimitar?"); stage = 31; break; case 31: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "No way, man!"); + interpreter.sendDialogues(npc, FacialExpression.EXTREMELY_SHOCKED, "No way, man!"); stage = 32; break; case 32: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "The banana-brained nitwits who make them would never", "dream of selling any to to me."); + interpreter.sendDialogues(npc, FacialExpression.ANGRY, "The banana-brained nitwits who make them would never", "dream of selling any to me."); stage = 33; break; case 33: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Seriously, you'll be a monkey's uncle before you'll ever", "hold a dragon scimitar."); + interpreter.sendDialogues(npc, FacialExpression.FRIENDLY, "Seriously, you'll be a monkey's uncle before you'll ever", "hold a dragon scimitar."); stage = 34; break; case 34: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Hmmm, funny you should say that..."); + interpreter.sendDialogues(player, FacialExpression.SUSPICIOUS, "Hmmm, funny you should say that..."); stage = 35; break; case 35: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Perhaps you'd lie to take a look at my stock?"); + interpreter.sendDialogues(npc, FacialExpression.ASKING, "Perhaps you'd like to take a look at my stock?"); stage = 36; break; case 36: diff --git a/Server/src/main/content/region/desert/alkharid/handlers/AlKharidStairsPlugin.kt b/Server/src/main/content/region/desert/alkharid/handlers/AlKharidStairsPlugin.kt index 782de0b4c..cec65c1a5 100644 --- a/Server/src/main/content/region/desert/alkharid/handlers/AlKharidStairsPlugin.kt +++ b/Server/src/main/content/region/desert/alkharid/handlers/AlKharidStairsPlugin.kt @@ -1,4 +1,4 @@ -package content.region.desert.alkharid.handlers; +package content.region.desert.alkharid.handlers import core.api.* import core.cache.def.impl.SceneryDefinition @@ -30,7 +30,7 @@ class AlKharidStairsPlugin : OptionHandler() { if(node.location == zekeDoorOpened.location || node.location == craftingDoorOpened.location){ sendMessage(player,"This door appears to be stuck open.") } else{ - core.game.global.action.DoorActionHandler.handleDoor(player,node.asScenery()) + DoorActionHandler.handleDoor(player,node.asScenery()) } return true } diff --git a/Server/src/main/content/region/desert/alkharid/handlers/AlKharidWarriorNPC.kt b/Server/src/main/content/region/desert/alkharid/handlers/AlKharidWarriorNPC.kt index 7f6cc4b4d..ba1b4662d 100644 --- a/Server/src/main/content/region/desert/alkharid/handlers/AlKharidWarriorNPC.kt +++ b/Server/src/main/content/region/desert/alkharid/handlers/AlKharidWarriorNPC.kt @@ -1,16 +1,14 @@ package content.region.desert.alkharid.handlers -import core.cache.def.impl.NPCDefinition -import core.game.interaction.OptionHandler import core.game.node.Node 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 core.plugin.Plugin -import core.plugin.ClassScanner +import org.rs09.consts.NPCs /** * Handles the Al-Kharid Warrior @@ -19,6 +17,7 @@ import core.plugin.ClassScanner @Initializable class AlKharidWarriorNPC : AbstractNPC { var target: Player? = null + private val supportRange: Int = 5 //Constructor spaghetti because Arios I guess constructor() : super(18, null, true) {} @@ -27,11 +26,6 @@ class AlKharidWarriorNPC : AbstractNPC { return AlKharidWarriorNPC(id, location) } - override fun newInstance(arg: Any?): Plugin { - ClassScanner.definePlugin(WarriorOptionPlugin()) - return super.newInstance(arg) - } - //Maintains target combat (resolves issue where they would just give up/walk away) override fun tick() { if(target != null && !inCombat() && skills.lifepoints > 0){ @@ -42,43 +36,38 @@ class AlKharidWarriorNPC : AbstractNPC { //Clear target on death override fun finalizeDeath(killer: Entity?) { - super.finalizeDeath(killer) target = null + super.finalizeDeath(killer) + } + + override fun attack(node: Node?) { + if (node is Player) target = node + super.attack(node) + } + + override fun onImpact(entity: Entity?, state: BattleState?) { + if (entity is Player) { + if (target == null) { + target = entity + RegionManager.getLocalNpcs(entity, supportRange).forEach { + if (it.id == NPCs.AL_KHARID_WARRIOR_18 && !it.properties.combatPulse.isAttacking && it != this) { + it.sendChat("Brother, I shall help thee with this infidel!") + it.attack(entity) + } + } + } + } + super.onImpact(entity, state) } override fun getIds(): IntArray { return ID } - inner class WarriorOptionPlugin : OptionHandler() { - override fun newInstance(arg: Any?): Plugin { - for (id in ID) { - NPCDefinition.forId(id).handlers["option:attack"] = this - } - return this - } - - override fun handle(player: Player, node: Node, option: String): Boolean { - RegionManager.getLocalNpcs(player,6).forEach{ - if(it.id == 18 && it != node.asNpc() && !it.properties.combatPulse.isAttacking){ - it.sendChat("Brother, I shall help thee with this infidel!") - (it as AlKharidWarriorNPC).target = player - it.attack(player) - } - } - player.attack(node.asNpc()) - return true - } - - override fun isWalk(): Boolean { - return false - } - } - companion object { /** * The NPC ids of NPCs using this plugin. */ - private val ID = intArrayOf(18) + private val ID = intArrayOf(NPCs.AL_KHARID_WARRIOR_18) } } \ No newline at end of file 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/NedPARDialogue.kt b/Server/src/main/content/region/desert/alkharid/quest/princealirescue/NedPARDialogue.kt index 2c88973e7..3994545f6 100644 --- a/Server/src/main/content/region/desert/alkharid/quest/princealirescue/NedPARDialogue.kt +++ b/Server/src/main/content/region/desert/alkharid/quest/princealirescue/NedPARDialogue.kt @@ -19,7 +19,7 @@ class NedPARDialogue(val questStage: Int) : DialogueFile() { START_DIALOGUE -> options( "Ned, could you make other things from wool?", "Yes, I would like some rope.", - "No thanks Ned, I don't need any." + "No thanks, Ned, I don't need any." ).also { stage++ } 1 -> when(buttonID){ @@ -97,4 +97,4 @@ class NedPARDialogue(val questStage: Int) : DialogueFile() { } } -} \ No newline at end of file +} 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 77a16f466..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 @@ -87,8 +88,8 @@ public class PrinceAliRescue extends Quest { line(player, "Palace. he told me I should speak to Osman the spymaster.", 5+ 7); line(player, "I should go and speak to Osman for details on the quest.", 6+ 7); line(player, RED + "Prince Ali " + BLUE + "has been " + RED + "kidnapped " + BLUE + "but luckily the spy " + RED + "Leela " + BLUE + "has", 7+ 7); - line(player, BLUE + "found he is being held near " + RED + "draynor village. " + BLUE + "I will need to", 8+ 7); - line(player, RED + "disguise " + BLUE + "the " + RED + "Price " + BLUE + "and " + RED + "tie " + BLUE + "up his " + RED + "captop " + BLUE + "to " + RED + "free " + BLUE + "him from", 9+ 7); + line(player, BLUE + "found he is being held near " + RED + "Draynor village. " + BLUE + "I will need to", 8+ 7); + line(player, RED + "disguise " + BLUE + "the " + RED + "Prince " + BLUE + "and " + RED + "tie " + BLUE + "up his " + RED + "captor " + BLUE + "to " + RED + "free " + BLUE + "him from", 9+ 7); line(player, BLUE + "their " + RED + "clutches.", 10+ 7); line(player, BLUE + "To do this I should:-", 11+ 7); line(player, BLUE + "Talk to " + RED + "Leela " + BLUE + "near " + RED + "Draynor Village " + BLUE + "for advice.", 12+ 7); 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/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..f8c69da7c --- /dev/null +++ b/Server/src/main/content/region/desert/bandits/handlers/BanditNPC.kt @@ -0,0 +1,62 @@ +package content.region.desert.bandits.handlers + +import core.api.God +import core.api.hasGodItem +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.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)) { + 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/EblisDialogue.java b/Server/src/main/content/region/desert/dialogue/EblisDialogue.java deleted file mode 100644 index 1ea4b0216..000000000 --- a/Server/src/main/content/region/desert/dialogue/EblisDialogue.java +++ /dev/null @@ -1,107 +0,0 @@ -package content.region.desert.dialogue; - -import core.game.dialogue.DialoguePlugin; -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 org.rs09.consts.Items; - -/** - * Handles Eblis's dialogue. - * @author ceik - * @version 1.0 - */ -@Initializable -public class EblisDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code EblisDialogue} {@code Object}. - */ - public EblisDialogue() { - /** - * Empty - */ - } - - /** - * Constructs a new {@code EblisDialogue} {@code Object}. - * @param player The player to construct the class for. - */ - public EblisDialogue(final Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new EblisDialogue(player); - } - - @Override - public boolean open(Object... args) { - //TODO: Add proper dialogue once DT is implemented - npc = (NPC) args[0]; - if(!player.getAttribute("DT:staff-bought",false)) { - player("Hey."); - stage = 0; - } else { - player("Say, what's up with your dialogue?"); - stage = 20; - } - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - //TODO: Add proper dialogue once DT is implemented - switch(stage){ - case 0: - npc("Hey, you wanna buy this funny stick thing?"); - stage = 1; - break; - case 1: - options("Yes please.","No thanks."); - stage = 2; - break; - case 2: - switch(buttonId){ - case 1: - npc("Alright, that'll be 80,000 golden shekels."); - stage = 10; - break; - case 2: - player("Nah, I'm good, homie."); - stage = 30; - break; - } - break; - case 10: - player("Hmm.... ok."); - stage = 11; - break; - case 11: - if(!player.getInventory().contains(Items.COINS_995,80000)){ - npc("Oi vey... you dont have enough.."); - } else { - player.getInventory().remove(new Item(Items.COINS_995,80000)); - player.getInventory().add(new Item(Items.ANCIENT_STAFF_4675)); - } - stage = 30; - break; - case 20: - npc("Well I don't have me bloody quest yet, mate","so I needed SOMETHING to say,","oi vey."); - stage = 30; - break; - case 30: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 1923 }; - } - -} diff --git a/Server/src/main/content/region/desert/dialogue/RugMerchantDialogue.java b/Server/src/main/content/region/desert/dialogue/RugMerchantDialogue.java index a82cd69b2..a4a3c626a 100644 --- a/Server/src/main/content/region/desert/dialogue/RugMerchantDialogue.java +++ b/Server/src/main/content/region/desert/dialogue/RugMerchantDialogue.java @@ -22,7 +22,8 @@ import core.game.world.update.flag.context.Animation; import core.plugin.Plugin; import core.plugin.ClassScanner; import org.rs09.consts.Items; - +import org.rs09.consts.Sounds; +import content.data.Quests; /** @@ -156,7 +157,14 @@ public final class RugMerchantDialogue extends DialoguePlugin { stage = 20; return; } + end(); destination = options.length == 1 ? options[0] : options[buttonId - 1]; + if (destination == RugDestination.UZER && !hasRequirement(player, Quests.THE_GOLEM)) + break; + else if (destination == RugDestination.BEDABIN_CAMP && !hasRequirement(player, Quests.THE_TOURIST_TRAP)) + break; + 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.")); } else { @@ -164,7 +172,6 @@ public final class RugMerchantDialogue extends DialoguePlugin { destination.travel(current, player); } } - end(); break; case 20: end(); @@ -284,11 +291,13 @@ public final class RugMerchantDialogue extends DialoguePlugin { */ public void travel(final RugDestination current, final Player player) { player.lock(); - player.getConfigManager().set(499, 0); + setVarp(player, 499, 0); player.getImpactHandler().setDisabledTicks(GameWorld.getTicks() + 200); player.getInterfaceManager().removeTabs(0,1,2,3,4,5,6,7,8,9,10,11,12,13); player.getEquipment().replace(new Item(Items.MAGIC_CARPET_5614),EquipmentContainer.SLOT_WEAPON); player.getPacketDispatch().sendInterfaceConfig(548,69,true); + playAudio(player, Sounds.CARPET_RISE_1196); + playJingle(player, 132); registerLogoutListener(player, "magic-carpet", (pl) -> { removeItem(pl, Items.MAGIC_CARPET_5614, Container.EQUIPMENT); @@ -322,7 +331,7 @@ public final class RugMerchantDialogue extends DialoguePlugin { player.faceLocation(getLocation()); break; case 4: - player.getConfigManager().set(499, 1); + setVarp(player, 499, 1); break; case 200: break; @@ -333,7 +342,8 @@ public final class RugMerchantDialogue extends DialoguePlugin { player.getImpactHandler().setDisabledTicks(0); player.unlock(); player.animate(new Animation(-1)); - player.getConfigManager().set(499, 0); + setVarp(player, 499, 0); + playAudio(player, Sounds.CARPET_DESCEND_1195); clearLogoutListener(player, "magic-carpet"); diff --git a/Server/src/main/content/region/desert/dialogue/SimonTempleton.java b/Server/src/main/content/region/desert/dialogue/SimonTempleton.java index 06267e58a..fe695172a 100644 --- a/Server/src/main/content/region/desert/dialogue/SimonTempleton.java +++ b/Server/src/main/content/region/desert/dialogue/SimonTempleton.java @@ -1,10 +1,15 @@ package content.region.desert.dialogue; +import core.ServerConstants; +import core.api.Container; import core.game.dialogue.DialoguePlugin; 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 org.rs09.consts.Items; + +import static core.api.ContentAPIKt.*; /** * Handles the dialog for Simon Templeton @@ -39,7 +44,7 @@ public final class SimonTempleton extends DialoguePlugin { return true; } } - npc("G'day, mate!. Got any new", "pyramid artefacts for me?"); + npc("G'day, mate. Got any new", "pyramid artefacts for me?"); return true; } @@ -47,6 +52,8 @@ public final class SimonTempleton extends DialoguePlugin { public boolean handle(int interfaceId, int buttonId){ boolean hasArtefacts = false; boolean hasPyramidTopper = false; + int goldReward = 1000; + if (ServerConstants.BETTER_AGILITY_PYRAMID_GP) goldReward =(int)(1000 + ((getStatLevel(player, 16) / 99.0) * 9000)); switch(stage){ case 0: for(int i = 0; i < ARTIFACTS.length; i++){ @@ -92,7 +99,7 @@ public final class SimonTempleton extends DialoguePlugin { stage = 999; break; case 6: - npc("Hmmm, very nice. I'll buy them for 10k each."); + npc("Hmmm, very nice. I'll buy them for " + goldReward + " each."); stage = 7; break; case 7: @@ -102,13 +109,9 @@ public final class SimonTempleton extends DialoguePlugin { case 8: switch(buttonId){ case 1: - for(int j = 0; j < 28; j++){ - switch(player.getInventory().getId(j)){ - case 6970: - player.getInventory().remove(new Item(6970),j,true); - player.getInventory().add(new Item(995, 10000)); - break; - } + int pyramidTopsInInv = amountInInventory(player, Items.PYRAMID_TOP_6970); + if (removeItem(player, new Item(Items.PYRAMID_TOP_6970, pyramidTopsInInv), Container.INVENTORY)) { + addItem(player, Items.COINS_995, goldReward * pyramidTopsInInv, Container.INVENTORY); } end(); break; diff --git a/Server/src/main/content/region/desert/handlers/DesertZone.java b/Server/src/main/content/region/desert/handlers/DesertZone.java index c761fa198..070889206 100644 --- a/Server/src/main/content/region/desert/handlers/DesertZone.java +++ b/Server/src/main/content/region/desert/handlers/DesertZone.java @@ -17,6 +17,10 @@ import core.game.world.update.flag.context.Animation; import core.plugin.Plugin; import core.plugin.Initializable; import core.tools.RandomFunction; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.impact; +import static core.api.ContentAPIKt.playAudio; /** * The desert zone map. @@ -75,7 +79,7 @@ public final class DesertZone extends MapZone implements Plugin { if (drink(p)) { return; } - p.getImpactHandler().manualHit(p, RandomFunction.random(1, p.getLocation().getY() < 2990 ? 12 : 8), HitsplatType.NORMAL); + impact(p, RandomFunction.random(1, p.getLocation().getY() < 2990 ? 12 : 8), HitsplatType.NORMAL); p.getPacketDispatch().sendMessage("You start dying of thirst while you're in the desert."); } @@ -104,6 +108,7 @@ public final class DesertZone extends MapZone implements Plugin { p.getInventory().add(new Item(i.getId() + 2)); p.animate(ANIMATION); p.getPacketDispatch().sendMessage("You take a drink of water."); + playAudio(p, Sounds.LIQUID_2401); return true; } } 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 5a4e59557..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; } @@ -315,7 +315,7 @@ public final class KalphiteQueenNPC extends AbstractNPC { return; } if (style == CombatStyle.MAGIC) { - BattleState[] states = new BattleState[entity.getViewport().getCurrentPlane().getPlayers().size()]; + BattleState[] states = new BattleState[entity.getViewport().getCurrentPlane().getPlayers().size() + 1]; int index = 1; if (states.length == 0) { 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 27084d03b..74ae051e1 100644 --- a/Server/src/main/content/region/desert/handlers/ShantayPassPlugin.java +++ b/Server/src/main/content/region/desert/handlers/ShantayPassPlugin.java @@ -1,10 +1,12 @@ package content.region.desert.handlers; +import core.api.Container; import core.cache.def.impl.NPCDefinition; import core.cache.def.impl.SceneryDefinition; import core.game.component.Component; import core.game.component.ComponentDefinition; import core.game.component.ComponentPlugin; +import core.game.dialogue.FacialExpression; import core.game.global.action.DoorActionHandler; import content.global.skill.agility.AgilityHandler; import core.game.interaction.OptionHandler; @@ -16,6 +18,10 @@ import core.game.node.scenery.Scenery; import core.game.world.map.Location; import core.plugin.Initializable; import core.plugin.Plugin; +import org.rs09.consts.Items; + +import static core.api.ContentAPIKt.*; +import static core.tools.TickUtilsKt.ticksToCycles; /** * Represents the plugin to handle the shantay pass. @@ -84,11 +90,15 @@ public class ShantayPassPlugin extends OptionHandler { player.getInterfaceManager().open(new Component(565)); break; case "quick-pass": - if (player.getLocation().getY() < 3117) { - AgilityHandler.walk(player, 0, player.getLocation(), player.getLocation().transform(0, player.getLocation().getY() > 3116 ? -2 : 2, 0), null, 0, null); - return true; + 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, false); + return true; + } + if (!removeItem(player, Items.SHANTAY_PASS_1854, Container.INVENTORY)) return true; + sendMessage(player, "You hand your Shantay pass to the guard and pass through the gate."); } - player.getDialogueInterpreter().open(838, 838, true); + forceMove(player, player.getLocation(), player.getLocation().transform(0, player.getLocation().getY() > 3116 ? -2 : 2, 0), 0, ticksToCycles(2), null, 819, null); break; } 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/nardah/dialogue/Nkuku.kt b/Server/src/main/content/region/desert/nardah/dialogue/Nkuku.kt index 8046f44af..11ceafab5 100644 --- a/Server/src/main/content/region/desert/nardah/dialogue/Nkuku.kt +++ b/Server/src/main/content/region/desert/nardah/dialogue/Nkuku.kt @@ -12,18 +12,18 @@ import org.rs09.consts.NPCs */ @Initializable -class Nkuku(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class Nkuku(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - player(core.game.dialogue.FacialExpression.FRIENDLY,"Good day to you.").also { stage = 0 } + player(FacialExpression.FRIENDLY,"Good day to you.").also { stage = 0 } return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ 0 -> { - npc(core.game.dialogue.FacialExpression.FRIENDLY, "May Saradomin be with you.").also { stage = 99 } + npc(FacialExpression.FRIENDLY, "May Saradomin be with you.").also { stage = 99 } } 99 -> end() @@ -31,7 +31,7 @@ class Nkuku(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return Nkuku(player) } diff --git a/Server/src/main/content/region/desert/nardah/dialogue/ZahurDialoguePlugin.java b/Server/src/main/content/region/desert/nardah/dialogue/ZahurDialoguePlugin.java new file mode 100644 index 000000000..591e33009 --- /dev/null +++ b/Server/src/main/content/region/desert/nardah/dialogue/ZahurDialoguePlugin.java @@ -0,0 +1,90 @@ +package content.region.desert.nardah.dialogue; + +import core.game.node.item.Item; +import core.cache.def.impl.NPCDefinition; +import core.plugin.Initializable; +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.player.Player; +import core.plugin.Plugin; +import core.plugin.ClassScanner; +import java.util.List; +import kotlin.Pair; + +import static core.api.ContentAPIKt.decantContainer; + +/** + * Handles the zahur dialogue. + * @author Empathy + */ +@Initializable +public class ZahurDialoguePlugin extends DialoguePlugin { + /** + * Constructs a new {@code ZahurDialoguePlugin} {@code Object}. + */ + public ZahurDialoguePlugin() { + /** + * empty. + */ + } + + /** + * Constructs a new {@code ZahurDialoguePlugin} {@code Object}. + * @param player the player. + */ + public ZahurDialoguePlugin(Player player) { + super(player); + } + + @Override + public DialoguePlugin newInstance(Player player) { + return new ZahurDialoguePlugin(player); + } + + @Override + public boolean open(Object... args) { + npc("I can combine your potion vials to try and make", "the potions fit into fewer vials. This service is free.", "Would you like to do this?"); + stage = 1; + return true; + } + + @Override + public boolean handle(int interfaceId, int buttonId) { + switch (stage) { + case 1: + options("Yes", "No"); + stage = 2; + break; + case 2: + if (buttonId == 1) { + Pair,List> decantResult = decantContainer(player.getInventory()); + List toRemove = decantResult.getFirst(); + List toAdd = decantResult.getSecond(); + + for (Item item : toRemove) { + player.getInventory().remove(item); + } + + for (Item item : toAdd) { + player.getInventory().add(item); + } + + npc("There, all done."); + } + stage = 3; + break; + case 3: + end(); + break; + } + return true; + } + + @Override + public int[] getIds() { + return new int[] { 3037 }; + } + +} diff --git a/Server/src/main/content/region/desert/nardah/handlers/ZahurOptionPlugin.java b/Server/src/main/content/region/desert/nardah/handlers/ZahurOptionPlugin.java deleted file mode 100644 index 11318c265..000000000 --- a/Server/src/main/content/region/desert/nardah/handlers/ZahurOptionPlugin.java +++ /dev/null @@ -1,101 +0,0 @@ -package content.region.desert.nardah.handlers; - -import core.cache.def.impl.NPCDefinition; -import core.plugin.Initializable; -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.player.Player; -import core.plugin.Plugin; -import core.plugin.ClassScanner; - -import content.global.skill.herblore.PotionDecantingPlugin; - -/** - * Handles the zahur options. - * @author Empathy - * - */ -@Initializable -public class ZahurOptionPlugin extends OptionHandler { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - NPCDefinition.forId(3037).getHandlers().put("option:combine", this); - ClassScanner.definePlugin(new ZahurDialoguePlugin()); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - PotionDecantingPlugin.decant(player); - player.getDialogueInterpreter().sendDialogues(node.asNpc(), FacialExpression.HALF_GUILTY, "There, all done."); - return true; - } - - - /** - * Handles the Zahur Dialogue. - * @author Empathy - * - */ - public class ZahurDialoguePlugin extends DialoguePlugin { - - /** - * Constructs a new {@code ZahurDialoguePlugin} {@code Object}. - */ - public ZahurDialoguePlugin() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code ZahurDialoguePlugin} {@code Object}. - * @param player the player. - */ - public ZahurDialoguePlugin(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new ZahurDialoguePlugin(player); - } - - @Override - public boolean open(Object... args) { - npc("I can combine your potion vials to try and make", "the potions fit into fewer vials. This service is free.", "Would you like to do this?"); - stage = 1; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 1: - options("Yes", "No"); - stage = 2; - break; - case 2: - if (buttonId == 1) { - PotionDecantingPlugin.decant(player); - npc("There, all done."); - } - stage = 3; - break; - case 3: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 3037 }; - } - - } -} diff --git a/Server/src/main/content/region/desert/pollniv/dialogue/AliTheHagDialogue.kt b/Server/src/main/content/region/desert/pollniv/dialogue/AliTheHagDialogue.kt index 84265e1eb..24bafe58e 100644 --- a/Server/src/main/content/region/desert/pollniv/dialogue/AliTheHagDialogue.kt +++ b/Server/src/main/content/region/desert/pollniv/dialogue/AliTheHagDialogue.kt @@ -11,35 +11,35 @@ import core.plugin.Initializable */ @Initializable -class AliTheHagDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class AliTheHagDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - player(core.game.dialogue.FacialExpression.FRIENDLY,"Good day, old hag.") + player(FacialExpression.FRIENDLY,"Good day, old hag.") stage = 0 return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 0 -> npc(core.game.dialogue.FacialExpression.LAUGH,"Old hag indeed! I have a name you know!").also { stage++ } - 1 -> player(core.game.dialogue.FacialExpression.THINKING,"Let me guess it wouldn't be Ali would it?").also { stage++ } - 2 -> npc(core.game.dialogue.FacialExpression.ANNOYED,"Well how else would you abbreviate Alice then?", "And no, you can't call me Al!").also { stage++ } + 0 -> npc(FacialExpression.LAUGH,"Old hag indeed! I have a name you know!").also { stage++ } + 1 -> player(FacialExpression.THINKING,"Let me guess it wouldn't be Ali would it?").also { stage++ } + 2 -> npc(FacialExpression.ANNOYED,"Well how else would you abbreviate Alice then?", "And no, you can't call me Al!").also { stage++ } 3 -> npc( - core.game.dialogue.FacialExpression.ASKING,"Now what do you want from the Old hag of Pollnivneach?", + FacialExpression.ASKING,"Now what do you want from the Old hag of Pollnivneach?", "To hex someone? Power, beauty, eternal youth", "or something else drab like that?").also { stage++ } - 4 -> player(core.game.dialogue.FacialExpression.HALF_GUILTY,"Actually none of those, I'm new in town and", "I just wanted to get to know the locals.").also { stage++ } + 4 -> player(FacialExpression.HALF_GUILTY,"Actually none of those, I'm new in town and", "I just wanted to get to know the locals.").also { stage++ } 5 -> npc( - core.game.dialogue.FacialExpression.ANNOYED,"I'm busy brewing potions, so if you ", + FacialExpression.ANNOYED,"I'm busy brewing potions, so if you ", "disturb me again without reason,","I will turn you into a frog!").also { stage++ } - 6 -> player(core.game.dialogue.FacialExpression.SCARED,"Oh I'm sorry I won't let it happen again.").also { stage = 99 } + 6 -> player(FacialExpression.SCARED,"Oh I'm sorry I won't let it happen again.").also { stage = 99 } 99 -> end() } return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return AliTheHagDialogue(player) } diff --git a/Server/src/main/content/region/desert/pollniv/dialogue/AliTheKebabSeller.java b/Server/src/main/content/region/desert/pollniv/dialogue/AliTheKebabSeller.java index fd540eabd..5e1fe2989 100644 --- a/Server/src/main/content/region/desert/pollniv/dialogue/AliTheKebabSeller.java +++ b/Server/src/main/content/region/desert/pollniv/dialogue/AliTheKebabSeller.java @@ -1,5 +1,6 @@ package content.region.desert.pollniv.dialogue; +import core.ServerConstants; import core.game.dialogue.DialoguePlugin; import core.game.dialogue.FacialExpression; import core.game.node.entity.npc.NPC; @@ -62,7 +63,7 @@ public class AliTheKebabSeller extends DialoguePlugin { stage = 8; break; case 8: - npc("I offer two different types of kebabs: the standard","run-of-the-mill kebab seen throughout 2009scape and","enjoyed by many a intoxicated dwarf, and my specialty,","the extra-hot kebab. So which shall it be?"); + npc("I offer two different types of kebabs: the standard","run-of-the-mill kebab seen throughout " + ServerConstants.SERVER_NAME + " and","enjoyed by many a intoxicated dwarf, and my specialty,","the extra-hot kebab. So which shall it be?"); stage = 9; break; case 9: diff --git a/Server/src/main/content/region/desert/pollniv/dialogue/AliTheMayorDialogue.kt b/Server/src/main/content/region/desert/pollniv/dialogue/AliTheMayorDialogue.kt index b7fe15400..b1108b757 100644 --- a/Server/src/main/content/region/desert/pollniv/dialogue/AliTheMayorDialogue.kt +++ b/Server/src/main/content/region/desert/pollniv/dialogue/AliTheMayorDialogue.kt @@ -11,12 +11,12 @@ import core.plugin.Initializable */ @Initializable -class AliTheMayorDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class AliTheMayorDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC npc( - core.game.dialogue.FacialExpression.FRIENDLY,"Welcome adventurer to the town of Pollnivneach,", + FacialExpression.FRIENDLY,"Welcome adventurer to the town of Pollnivneach,", "the gateway to Menaphos and Al-Kharid. My name is Ali", "and I'm the mayor of this town.", "I hope you enjoy your stay here.") @@ -27,21 +27,21 @@ class AliTheMayorDialogue(player: Player? = null) : core.game.dialogue.DialogueP override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ 0 -> player( - core.game.dialogue.FacialExpression.FRIENDLY, "Thank you.", + FacialExpression.FRIENDLY, "Thank you.", "That is the warmest welcome I've had anywhere", "for a while at least. People generally treat", "travelling adventurers with suspicion.").also { stage++ } 1 -> npc( - core.game.dialogue.FacialExpression.FRIENDLY,"All are welcome here, such is the way", + FacialExpression.FRIENDLY,"All are welcome here, such is the way", "of things in border regions.", "Now more than ever I suppose.").also { stage++ } - 2 -> player(core.game.dialogue.FacialExpression.FRIENDLY,"What do you mean by that?").also { stage++ } - 3 -> npc(core.game.dialogue.FacialExpression.FRIENDLY,"There's trouble in town, and a lot of ", "villagers have left as a result.").also { stage++ } - 4 -> player(core.game.dialogue.FacialExpression.FRIENDLY,"I'm looking for someone called Ali.").also { stage++ } - 5 -> npc(core.game.dialogue.FacialExpression.FRIENDLY,"I doubt that's easy in Pollnivneach.").also { stage++ } - 6 -> player(core.game.dialogue.FacialExpression.FRIENDLY,"Well can you help me?").also { stage++ } + 2 -> player(FacialExpression.FRIENDLY,"What do you mean by that?").also { stage++ } + 3 -> npc(FacialExpression.FRIENDLY,"There's trouble in town, and a lot of ", "villagers have left as a result.").also { stage++ } + 4 -> player(FacialExpression.FRIENDLY,"I'm looking for someone called Ali.").also { stage++ } + 5 -> npc(FacialExpression.FRIENDLY,"I doubt that's easy in Pollnivneach.").also { stage++ } + 6 -> player(FacialExpression.FRIENDLY,"Well can you help me?").also { stage++ } 7 -> npc( - core.game.dialogue.FacialExpression.FRIENDLY,"I'm more than a little busy at the moment, ", + FacialExpression.FRIENDLY,"I'm more than a little busy at the moment, ", "I'm sure there are plenty of people ", "in town who could help you.").also { stage = 99 } 99 -> end() @@ -49,7 +49,7 @@ class AliTheMayorDialogue(player: Player? = null) : core.game.dialogue.DialogueP return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return AliTheMayorDialogue(player) } diff --git a/Server/src/main/content/region/desert/pollniv/dialogue/AliTheOperator.kt b/Server/src/main/content/region/desert/pollniv/dialogue/AliTheOperator.kt index 3377b74cf..d2c6ff057 100644 --- a/Server/src/main/content/region/desert/pollniv/dialogue/AliTheOperator.kt +++ b/Server/src/main/content/region/desert/pollniv/dialogue/AliTheOperator.kt @@ -11,47 +11,47 @@ import core.plugin.Initializable */ @Initializable -class AliTheOperator(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class AliTheOperator(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - player(core.game.dialogue.FacialExpression.FRIENDLY,"Hello, good sir.") + player(FacialExpression.FRIENDLY,"Hello, good sir.") stage = 0 return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 0 -> npc(core.game.dialogue.FacialExpression.ANNOYED,"What do you want?").also { stage++ } - 1 -> player(core.game.dialogue.FacialExpression.HALF_ASKING,"I'm just new in town and have a few questions.").also { stage++ } - 2 -> npc(core.game.dialogue.FacialExpression.ASKING,"What do you want to know?").also { stage++ } + 0 -> npc(FacialExpression.ANNOYED,"What do you want?").also { stage++ } + 1 -> player(FacialExpression.HALF_ASKING,"I'm just new in town and have a few questions.").also { stage++ } + 2 -> npc(FacialExpression.ASKING,"What do you want to know?").also { stage++ } 3 -> options ("Tell me about yourself.", "Tell me about the other people in the town.", "I'm looking for Ali.").also { stage++ } 4 -> when(buttonId){ 1 -> npc( - core.game.dialogue.FacialExpression.ANNOYED, "That information is available on a need to know basis", + FacialExpression.ANNOYED, "That information is available on a need to know basis", "and right now, you don't need to know.").also { stage = 10 } 2 -> npc( - core.game.dialogue.FacialExpression.SUSPICIOUS,"Sheep, ready for the slau... ", + FacialExpression.SUSPICIOUS,"Sheep, ready for the slau... ", "hang on I shouldn't be saying..., ", "listen I don't want to talk about them.").also { stage = 10 } 3 -> npc( - core.game.dialogue.FacialExpression.ANNOYED,"You will have to be a lot more specific if ", + FacialExpression.ANNOYED,"You will have to be a lot more specific if ", "you want help finding him. ", "Everyone here is called Ali.").also { stage = 30 } } - 10 -> npc(core.game.dialogue.FacialExpression.HALF_ASKING,"Can I help you with anything else?").also { stage++ } + 10 -> npc(FacialExpression.HALF_ASKING,"Can I help you with anything else?").also { stage++ } 11 -> options ("Yes I'd like to ask you about something else.", "No thanks.").also { stage++ } 12 -> when(buttonId) { - 1 -> npc(core.game.dialogue.FacialExpression.ASKING,"What do you want to know?").also { stage = 3 } + 1 -> npc(FacialExpression.ASKING,"What do you want to know?").also { stage = 3 } 2 -> end() } 30 -> player( - core.game.dialogue.FacialExpression.HALF_ASKING,"I've discovered that. Well he has an uncle called", + FacialExpression.HALF_ASKING,"I've discovered that. Well he has an uncle called", "Ali Morrisane, a market vendor in Al Kharid and","that's all I really know about him.").also { stage++ } 31 -> npc( - core.game.dialogue.FacialExpression.ANNOYED,"Say no more. I too am looking for him. ", + FacialExpression.ANNOYED,"Say no more. I too am looking for him. ", "The little tyke robbed me too. If we work together,", "perhaps we can catch him and teach him ","a lesson.").also { stage = 10 } 99 -> end() @@ -59,7 +59,7 @@ class AliTheOperator(player: Player? = null) : core.game.dialogue.DialoguePlugin return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return AliTheOperator(player) } 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 4ad567996..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 -class ArchaeologistDialogue(player: Player? = null) : core.game.dialogue.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(core.game.dialogue.FacialExpression.FRIENDLY,"Hello there.").also { stage = 0 } - } else { - player(core.game.dialogue.FacialExpression.FRIENDLY,"Hello there.").also { stage = 999 } - } - - } else { - - } - - return true - } - +/** Known in RS3 as Asgarnia Smith */ +class ArchaeologistDialogue(player: Player? = null) : DialoguePlugin(player){ 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(core.game.dialogue.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(core.game.dialogue.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(core.game.dialogue.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(core.game.dialogue.FacialExpression.FRIENDLY, "could go and take it back to the digsite for me and get", - "it examined.").also { stage++ } - 22 -> npc(core.game.dialogue.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(core.game.dialogue.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(core.game.dialogue.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(core.game.dialogue.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?): core.game.dialogue.DialoguePlugin { + 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..c8b9af6d9 --- /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) { + + var clearTime = 0 + + 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 { + val player: Player? = getAttribute(self, "target", null) + if (clearTime++ > 800) { + clearTime = 0 + if (player != null) { + sendMessage(player, "Damis has vanished once more into the shadows...") + removeAttribute(player, DesertTreasure.attributeDamisInstance) + } + poofClear(self) + } + return true + } + + 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..5c521e6b6 --- /dev/null +++ b/Server/src/main/content/region/desert/quest/deserttreasure/DessousBehavior.kt @@ -0,0 +1,142 @@ +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) { + + var clearTime = 0 + + 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{ + // 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. + val player: Player? = getAttribute(self, "target", null) + 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) + } + if (clearTime++ > 800) { + self.transform(NPCs.DESSOUS_1914) + clearTime = 0 + if (player != null) { + removeAttribute(player, DesertTreasure.attributeDessousInstance) + sendMessage(player, "Dessous returns to his grave, bored of toying with you.") + } + poofClear(self) + } + return false + } + + 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..615a9b22c --- /dev/null +++ b/Server/src/main/content/region/desert/quest/deserttreasure/DiamondOfShadowListeners.kt @@ -0,0 +1,265 @@ +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..be98d963c --- /dev/null +++ b/Server/src/main/content/region/desert/quest/deserttreasure/FareedBehavior.kt @@ -0,0 +1,76 @@ +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) { + + var clearTime = 0 + + 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 { + val player: Player? = getAttribute(self, "target", null) + if (clearTime++ > 800) { + clearTime = 0 + if (player != null) { + 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) + } +// val weapon = getItemFromEquipment(victim, EquipmentSlot.WEAPON) +// if(weapon != null && removeItem(victim, weapon.id, Container.EQUIPMENT)) { +// addItemOrDrop(victim, weapon.id) +// } + sendMessage(victim, "The heat from the warrior causes you to drop your weapon.") + } + } + } + + 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..e5ddbdae1 --- /dev/null +++ b/Server/src/main/content/region/desert/quest/deserttreasure/KamilBehavior.kt @@ -0,0 +1,79 @@ +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) { + + var clearTime = 0 + + 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 { + val player: Player? = getAttribute(self, "target", null) + if (clearTime++ > 800) { + clearTime = 0 + if (player != null) { + 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")) { + registerTimer(victim, spawnTimer("frozen", 7, true)) + sendMessage(victim, "You've been frozen!") + sendChat(entity as NPC, "Sallamakar Ro!") // Salad maker roll. + sendGraphics(539, victim.location) + victim.properties.combatPulse.stop() // Force the victim to stop fighting. Whatever. + // Audio? + }else { + animate(entity!!, Animation(440)) + } + } + super.impact(entity, victim, state) + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..1daba317d --- /dev/null +++ b/Server/src/main/content/region/desert/quest/shadowofthestorm/DarklightListener.kt @@ -0,0 +1,21 @@ +package content.region.desert.quest.shadowofthestorm + +import core.api.addItem +import core.api.hasRequirement +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, 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 + } + } +} 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 a741f81f9..c1331e614 100644 --- a/Server/src/main/content/region/desert/quest/thegolem/TheGolemDialogue.kt +++ b/Server/src/main/content/region/desert/quest/thegolem/TheGolemDialogue.kt @@ -2,21 +2,17 @@ package content.region.desert.quest.thegolem import core.api.* import core.game.dialogue.DialoguePlugin -import core.game.dialogue.book.Book -import core.game.dialogue.book.BookLine -import core.game.dialogue.book.Page -import core.game.dialogue.book.PageSet 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 core.game.dialogue.DialogueBuilder import core.game.dialogue.DialogueBuilderFile +import content.data.Quests @Initializable -public final class ClayGolemDialoguePlugin(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { - override fun newInstance(player: Player): core.game.dialogue.DialoguePlugin { +public final class ClayGolemDialoguePlugin(player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player): DialoguePlugin { return ClayGolemDialoguePlugin(player) } override fun open(vararg objects: Any?): Boolean { @@ -32,24 +28,24 @@ public final class ClayGolemDialoguePlugin(player: Player? = null) : core.game.d } } -class ClayGolemDialogueFile : core.game.dialogue.DialogueBuilderFile() { - override fun create(b: core.game.dialogue.DialogueBuilder) { - val opt1 = b.onQuestStages("The Golem", 0) +class ClayGolemDialogueFile : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + 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.") @@ -57,43 +53,43 @@ class ClayGolemDialogueFile : core.game.dialogue.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() { 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 : core.game.dialogue.DialogueBuilderFile() { - override fun create(b: core.game.dialogue.DialogueBuilder) { - b.onQuestStages("The Golem", 8) +class ClayGolemProgramDialogueFile : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + 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 : core.game.dialogue.DialogueBuilderFile() { - override fun create(b: core.game.dialogue.DialogueBuilder) { - val opt1 = b.onQuestStages("The Golem", 3) +class CuratorHaigHalenGolemDialogue : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + 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.") @@ -126,306 +122,6 @@ val LETTER_LINES = arrayOf( "Your loving Elissa.", ) -val VARMEN_NOTES = arrayOf( - core.game.dialogue.book.PageSet( - core.game.dialogue.book.Page( - core.game.dialogue.book.BookLine("Septober 19:", 55), - core.game.dialogue.book.BookLine("The nomads were right:", 56), - core.game.dialogue.book.BookLine("there is a city here,", 57), - core.game.dialogue.book.BookLine("probably buried for", 58), - core.game.dialogue.book.BookLine("millenia and revealed by", 59), - core.game.dialogue.book.BookLine("the random motions of", 60), - core.game.dialogue.book.BookLine("the sand. The", 61), - core.game.dialogue.book.BookLine("architecture is impressive", 62), - core.game.dialogue.book.BookLine("even in ruin, and must", 63), - core.game.dialogue.book.BookLine("once have been amazing.", 64), - core.game.dialogue.book.BookLine("One puzzling factor is the", 65), - ), - core.game.dialogue.book.Page( - core.game.dialogue.book.BookLine("pottery -- there are", 66), - core.game.dialogue.book.BookLine("fragments all over the", 67), - core.game.dialogue.book.BookLine("ruins, surely too much", 68), - core.game.dialogue.book.BookLine("for a city even of this", 69), - core.game.dialogue.book.BookLine("size. We have set up", 70), - core.game.dialogue.book.BookLine("camp and will do a proper", 71), - core.game.dialogue.book.BookLine("survey tomorrow.", 72), - ) - ), - core.game.dialogue.book.PageSet( - core.game.dialogue.book.Page( - core.game.dialogue.book.BookLine("Septober 20:", 55), - core.game.dialogue.book.BookLine("The meaning of the", 56), - core.game.dialogue.book.BookLine("pottery was explained", 57), - core.game.dialogue.book.BookLine("today in a most", 58), - core.game.dialogue.book.BookLine("surprising manner. We", 59), - core.game.dialogue.book.BookLine("found a mostly-intact clay", 60), - core.game.dialogue.book.BookLine("statue buried up to its", 61), - core.game.dialogue.book.BookLine("waist in sand, and as", 62), - core.game.dialogue.book.BookLine("soon as we dug it out, it", 63), - core.game.dialogue.book.BookLine("started to walk around! It", 64), - core.game.dialogue.book.BookLine("is a clay golem, built by", 65), - ), - core.game.dialogue.book.Page( - core.game.dialogue.book.BookLine("the city's inhabitants and", 66), - core.game.dialogue.book.BookLine("dormant all this time. Its", 67), - core.game.dialogue.book.BookLine("head is badly damaged", 68), - core.game.dialogue.book.BookLine("and it is", 69), - core.game.dialogue.book.BookLine("uncommunicative, but its", 70), - core.game.dialogue.book.BookLine("existence tells us that the", 71), - core.game.dialogue.book.BookLine("city's inhabitants were", 72), - core.game.dialogue.book.BookLine("expert magical craftsmen.", 73), - core.game.dialogue.book.BookLine("The huge kilns in some", 74), - core.game.dialogue.book.BookLine("of the buildings indicate", 75), - core.game.dialogue.book.BookLine("that at some point before", 76), - ) - ), - core.game.dialogue.book.PageSet( - core.game.dialogue.book.Page( - core.game.dialogue.book.BookLine("its destruction the whole", 55), - core.game.dialogue.book.BookLine("city was converted to the", 56), - core.game.dialogue.book.BookLine("manufacture of these", 57), - core.game.dialogue.book.BookLine("golems.", 58), - core.game.dialogue.book.BookLine("", 59), - core.game.dialogue.book.BookLine("We have also examined", 60), - core.game.dialogue.book.BookLine("the carvings on the large", 61), - core.game.dialogue.book.BookLine("building in the centre.", 62), - core.game.dialogue.book.BookLine("These are symbols", 63), - core.game.dialogue.book.BookLine("depicting several of the", 64), - core.game.dialogue.book.BookLine("ancient gods, including", 65), - ), - core.game.dialogue.book.Page( - core.game.dialogue.book.BookLine("Saradomin, Zamorak, and", 66), - core.game.dialogue.book.BookLine("Armadyl, but there is", 67), - core.game.dialogue.book.BookLine("another prominent symbol", 68), - core.game.dialogue.book.BookLine("that I cannot identify. As", 69), - core.game.dialogue.book.BookLine("it seems we will need to", 70), - core.game.dialogue.book.BookLine("be here for longer than I", 71), - core.game.dialogue.book.BookLine("had thought, I have sent", 72), - core.game.dialogue.book.BookLine("to Elissa for books on", 73), - core.game.dialogue.book.BookLine("golems and religious", 74), - core.game.dialogue.book.BookLine("symbols.", 75), - ) - ), - core.game.dialogue.book.PageSet( - core.game.dialogue.book.Page( - core.game.dialogue.book.BookLine("Septober 21:", 55), - core.game.dialogue.book.BookLine("As we examine the ruins", 56), - core.game.dialogue.book.BookLine("one thing becomes", 57), - core.game.dialogue.book.BookLine("increasingly clear: most", 58), - core.game.dialogue.book.BookLine("of the damage was not", 59), - core.game.dialogue.book.BookLine("due to weathering. The", 60), - core.game.dialogue.book.BookLine("buildings were destroyed", 61), - core.game.dialogue.book.BookLine("by force, as if torn down", 62), - core.game.dialogue.book.BookLine("by giant hands.", 63), - ), - core.game.dialogue.book.Page( - core.game.dialogue.book.BookLine("Septober 22:", 66), - core.game.dialogue.book.BookLine("A breakthrough! We have", 67), - core.game.dialogue.book.BookLine("found the staircase into", 68), - core.game.dialogue.book.BookLine("the lower levels of the", 69), - core.game.dialogue.book.BookLine("temple. This part has", 70), - core.game.dialogue.book.BookLine("been untouched by the", 71), - core.game.dialogue.book.BookLine("elements, and the", 72), - core.game.dialogue.book.BookLine("carvings here are more", 73), - core.game.dialogue.book.BookLine("intact, especially four", 74), - core.game.dialogue.book.BookLine("beautiful statuettes in", 75), - core.game.dialogue.book.BookLine("alcoves framing the large", 76), - ) - ), - core.game.dialogue.book.PageSet( - core.game.dialogue.book.Page( - core.game.dialogue.book.BookLine("door. I have removed one", 55), - core.game.dialogue.book.BookLine("of them. The door will", 56), - core.game.dialogue.book.BookLine("not open. I am glad I", 57), - core.game.dialogue.book.BookLine("sent for a book on", 58), - core.game.dialogue.book.BookLine("symbols, as the", 59), - core.game.dialogue.book.BookLine("unidentified symbol is", 60), - core.game.dialogue.book.BookLine("even more prominent", 61), - core.game.dialogue.book.BookLine("here, especially on the", 62), - core.game.dialogue.book.BookLine("door.", 63), - ), - core.game.dialogue.book.Page( - core.game.dialogue.book.BookLine("Septober 23:", 66), - core.game.dialogue.book.BookLine("Our messenger returned", 67), - core.game.dialogue.book.BookLine("with the books I asked for", 68), - core.game.dialogue.book.BookLine("and a letter from Elissa.", 69), - core.game.dialogue.book.BookLine("It is unfortunate that the", 70), - core.game.dialogue.book.BookLine("museum will not be able", 71), - core.game.dialogue.book.BookLine("to finance a full-scale", 72), - core.game.dialogue.book.BookLine("excavation here as well as", 73), - core.game.dialogue.book.BookLine("the one closer to Varrock,", 74), - core.game.dialogue.book.BookLine("although I am of course", 75), - core.game.dialogue.book.BookLine("pleased that the other city", 76), - ) - ), - core.game.dialogue.book.PageSet( - core.game.dialogue.book.Page( - core.game.dialogue.book.BookLine("has been uncovered. But", 55), - core.game.dialogue.book.BookLine("with the books I am able", 56), - core.game.dialogue.book.BookLine("to piece together more of", 57), - core.game.dialogue.book.BookLine("the story of this city.", 58), - core.game.dialogue.book.BookLine("", 59), - core.game.dialogue.book.BookLine("The unidentified symbol", 60), - core.game.dialogue.book.BookLine("in the ruins is that of the", 61), - core.game.dialogue.book.BookLine("demon Thammaron, who", 62), - core.game.dialogue.book.BookLine("was Zamorak's chief", 63), - core.game.dialogue.book.BookLine("lieutenant during the", 64), - core.game.dialogue.book.BookLine("godwars of the Third", 65), - ), - core.game.dialogue.book.Page( - core.game.dialogue.book.BookLine("Age. With that", 66), - core.game.dialogue.book.BookLine("information I can say", 67), - core.game.dialogue.book.BookLine("with confidence that these", 68), - core.game.dialogue.book.BookLine("are the ruins of Uzer, an", 69), - core.game.dialogue.book.BookLine("advanced human", 70), - core.game.dialogue.book.BookLine("civilization said to have", 71), - core.game.dialogue.book.BookLine("been destroyed towards", 72), - core.game.dialogue.book.BookLine("the end of the Third Age", 73), - core.game.dialogue.book.BookLine("(roughly 2,500 years", 74), - core.game.dialogue.book.BookLine("ago). It was allied with", 75), - core.game.dialogue.book.BookLine("Saradomin and enjoyed", 76), - ) - ), - core.game.dialogue.book.PageSet( - core.game.dialogue.book.Page( - core.game.dialogue.book.BookLine("his protection, as well as", 55), - core.game.dialogue.book.BookLine("that of its own mages and", 56), - core.game.dialogue.book.BookLine("warriors. Thammaron was", 57), - core.game.dialogue.book.BookLine("able to open a portal from", 58), - core.game.dialogue.book.BookLine("his own domain straight", 59), - core.game.dialogue.book.BookLine("into the heart of the city,", 60), - core.game.dialogue.book.BookLine("bypassing its defences.", 61), - core.game.dialogue.book.BookLine("With Saradomin's help the", 62), - core.game.dialogue.book.BookLine("army of Uzer was able to", 63), - core.game.dialogue.book.BookLine("drive Thammaron back,", 64), - core.game.dialogue.book.BookLine("but the record ends at", 65), - ), - core.game.dialogue.book.Page( - core.game.dialogue.book.BookLine("that point and it has", 66), - core.game.dialogue.book.BookLine("always been assumed that", 67), - core.game.dialogue.book.BookLine("a later attack, either by", 68), - core.game.dialogue.book.BookLine("Thammaron or by", 69), - core.game.dialogue.book.BookLine("Zamorak's other forces,", 70), - core.game.dialogue.book.BookLine("finished the city off.", 71), - core.game.dialogue.book.BookLine("", 72), - core.game.dialogue.book.BookLine("Examining the door", 73), - core.game.dialogue.book.BookLine("again, I now see that it is", 74), - core.game.dialogue.book.BookLine("exactly the sort of door", 75), - core.game.dialogue.book.BookLine("that could be used to seal", 76), - ) - ), - core.game.dialogue.book.PageSet( - core.game.dialogue.book.Page( - core.game.dialogue.book.BookLine("Thammaron's portal. I am", 55), - core.game.dialogue.book.BookLine("suddently glad I was not", 56), - core.game.dialogue.book.BookLine("able to open it! I surmise", 57), - core.game.dialogue.book.BookLine("that the army of golems", 58), - core.game.dialogue.book.BookLine("was created in order to", 59), - core.game.dialogue.book.BookLine("fight the demon, since", 60), - core.game.dialogue.book.BookLine("Uzer's army had been", 61), - core.game.dialogue.book.BookLine("wiped out and", 62), - core.game.dialogue.book.BookLine("Saradomin's forces were", 63), - core.game.dialogue.book.BookLine("increasingly stretched.", 64), - core.game.dialogue.book.BookLine("However, this approach", 65), - ), - core.game.dialogue.book.Page( - core.game.dialogue.book.BookLine("evidently failed, since the", 66), - core.game.dialogue.book.BookLine("city was eventually", 67), - core.game.dialogue.book.BookLine("destroyed.", 68), - core.game.dialogue.book.BookLine("", 69), - core.game.dialogue.book.BookLine("The art of the", 70), - core.game.dialogue.book.BookLine("construction of golems", 71), - core.game.dialogue.book.BookLine("has been lost since the", 72), - core.game.dialogue.book.BookLine("Third Age, and, although", 73), - core.game.dialogue.book.BookLine("they are sometimes", 74), - core.game.dialogue.book.BookLine("discovered lying dormant", 75), - core.game.dialogue.book.BookLine("in the ground, no", 76), - ) - ), - core.game.dialogue.book.PageSet( - core.game.dialogue.book.Page( - core.game.dialogue.book.BookLine("concerted effort has been", 55), - core.game.dialogue.book.BookLine("made to regain it, thanks", 56), - core.game.dialogue.book.BookLine("largely to the modern", 57), - core.game.dialogue.book.BookLine("Saradomist Church's view", 58), - core.game.dialogue.book.BookLine("of them as unnatural.", 59), - core.game.dialogue.book.BookLine("This view is without", 60), - core.game.dialogue.book.BookLine("foundation, as golems are", 61), - core.game.dialogue.book.BookLine("neither good nor evil but", 62), - core.game.dialogue.book.BookLine("follow instructions they", 63), - core.game.dialogue.book.BookLine("are given to the letter", 64), - core.game.dialogue.book.BookLine("and without imagination,", 65), - ), - core.game.dialogue.book.Page( - core.game.dialogue.book.BookLine("indeed experiencing", 66), - core.game.dialogue.book.BookLine("extreme discomfort for as", 67), - core.game.dialogue.book.BookLine("long as a task assigned to", 68), - core.game.dialogue.book.BookLine("them remains incomplete.", 69), - core.game.dialogue.book.BookLine("Some golems were", 70), - core.game.dialogue.book.BookLine("constructed to obey", 71), - core.game.dialogue.book.BookLine("verbal instructions, but", 72), - core.game.dialogue.book.BookLine("the main method of", 73), - core.game.dialogue.book.BookLine("instruction was to place", 74), - core.game.dialogue.book.BookLine("magical words into the", 75), - core.game.dialogue.book.BookLine("golem's skull cavity.", 76), - ) - ), - core.game.dialogue.book.PageSet( - core.game.dialogue.book.Page( - core.game.dialogue.book.BookLine("These were written on", 55), - core.game.dialogue.book.BookLine("papyrus using a naturally", 56), - core.game.dialogue.book.BookLine("occurring source of ink,", 57), - core.game.dialogue.book.BookLine("and their magical power", 58), - core.game.dialogue.book.BookLine("derived from the use of a", 59), - core.game.dialogue.book.BookLine("phoenix tail feather as a", 60), - core.game.dialogue.book.BookLine("pen. These would be used", 61), - core.game.dialogue.book.BookLine("for long-term or", 62), - core.game.dialogue.book.BookLine("important tasks, and", 63), - core.game.dialogue.book.BookLine("would override any verbal", 64), - core.game.dialogue.book.BookLine("instructions.", 65), - ) - ) -) - -@Initializable -class VarmensNotesHandler : core.game.dialogue.book.Book { - constructor() {} - constructor(player: Player?) : super(player, "The Ruins of Uzer", Items.VARMENS_NOTES_4616, *VARMEN_NOTES) {} - override fun finish() { - player.setAttribute("/save:the-golem:varmen-notes-read", true) - } - - override fun display(set: Array) { - player.interfaceManager.open(getInterface()) - - for (i in 55..76) { - player.packetDispatch.sendString("", getInterface().id, i) - } - player.packetDispatch.sendString("", getInterface().id, 77) - player.packetDispatch.sendString("", getInterface().id, 78) - player.packetDispatch.sendString(getName(), getInterface().id, 6) - for (page in set) { - for (line in page.lines) { - player.packetDispatch.sendString(line.message, getInterface().id, line.child) - } - } - player.packetDispatch.sendInterfaceConfig(getInterface().id, 51, index < 1) - val lastPage = index == sets.size - 1 - player.packetDispatch.sendInterfaceConfig(getInterface().id, 53, lastPage) - if (lastPage) { - finish() - } - } - - override fun newInstance(player: Player): core.game.dialogue.DialoguePlugin { - return VarmensNotesHandler(player) - } - - override fun getIds(): IntArray { - return intArrayOf(Items.VARMENS_NOTES_4616) - } -} - val DISPLAY_CASE_TEXT = arrayOf("3rd age - yr 3000-4000", "", "This statuette was found in an underground", 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 aba40c179..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 } @@ -72,7 +73,7 @@ class TheGolemQuest : Quest("The Golem", 70, 69, 1, 437, 0, 1, 10) { line(player, "The demon doesn't think its task is complete.", ln++, stage > 7) } if(stage >= 100) { - line(player, "QUEST COMPLETE", ln++, false) + line(player, "%%QUEST COMPLETE!&&", ln++, false) } } @@ -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) } } } @@ -194,7 +195,7 @@ class TheGolemListeners : InteractionListener { fun initializeStatuettes(player: Player) { if(!player.getAttribute("the-golem:statuette-rotation:initialized", false)) { for(i in 0 until 4) { - player.setAttribute("/save:the-golem:statuette-rotation:${i}", RandomFunction.random(2)); + player.setAttribute("/save:the-golem:statuette-rotation:${i}", RandomFunction.random(2)) } player.setAttribute("/save:the-golem:statuette-rotation:initialized", true) } @@ -207,31 +208,29 @@ class TheGolemListeners : InteractionListener { val statuetteTaken = if(hasStatuette(player)) { 1 } else { 0 } val statuettePlaced = if(player.getAttribute("the-golem:placed-statuette", false)) { 1 } else { 0 } initializeStatuettes(player) + val rotation0 = player.getAttribute("the-golem:statuette-rotation:0", 0) val rotation1 = player.getAttribute("the-golem:statuette-rotation:1", 0) val rotation2 = player.getAttribute("the-golem:statuette-rotation:2", 0) val rotation3 = player.getAttribute("the-golem:statuette-rotation:3", 0) - val rotation4 = player.getAttribute("the-golem:statuette-rotation:4", 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) } - player.varpManager.get(437) - .setVarbit(0, clientStage) - .setVarbit(7, clayUsed) - .setVarbit(16, gemsTaken) - .setVarbit(17, statuetteTaken) - .setVarbit(10, rotation1) - .setVarbit(11, rotation2) - .setVarbit(12, rotation3) - .setVarbit(13, statuettePlaced * (rotation4 + 1)) - .send(player) + setVarbit(player, 346, clientStage) + setVarbit(player, 348, clayUsed) + setVarbit(player, 354, gemsTaken) + setVarbit(player, 355, statuetteTaken) + setVarbit(player, 349, rotation0) + setVarbit(player, 350, rotation1) + setVarbit(player, 351, rotation2) + setVarbit(player, 352, statuettePlaced * (rotation3 + 1)) } } @@ -266,7 +265,7 @@ class TheGolemListeners : InteractionListener { } fun placeStatuette(player: Player): Boolean { - System.out.println("Hello from placeStatuette") + if(player.inventory.remove(Item(4618))) { player.sendMessage("You insert the statuette into the alcove.") player.setAttribute("/save:the-golem:placed-statuette", true) @@ -281,10 +280,10 @@ class TheGolemListeners : InteractionListener { return true } val index = when(node.wrapper.id) { - 6303 -> 1 - 6304 -> 2 - 6305 -> 3 - 6306 -> 4 + 6303 -> 0 + 6304 -> 1 + 6305 -> 2 + 6306 -> 3 else -> return true } initializeStatuettes(player) @@ -299,12 +298,12 @@ class TheGolemListeners : InteractionListener { fun checkDoor(player: Player) { if(!player.getAttribute("the-golem:door-open", false)) { + val rotation0 = player.getAttribute("the-golem:statuette-rotation:0", 0) val rotation1 = player.getAttribute("the-golem:statuette-rotation:1", 0) val rotation2 = player.getAttribute("the-golem:statuette-rotation:2", 0) val rotation3 = player.getAttribute("the-golem:statuette-rotation:3", 0) - val rotation4 = player.getAttribute("the-golem:statuette-rotation:4", 0) val placed = player.getAttribute("the-golem:placed-statuette", false) - if(rotation1 == 1 && rotation2 == 1 && rotation3 == 0 && rotation4 == 0 && placed) { + if(rotation0 == 1 && rotation1 == 1 && rotation2 == 0 && rotation3 == 0 && placed) { player.sendMessage("The door grinds open.") player.setAttribute("/save:the-golem:door-open", true) } @@ -333,7 +332,7 @@ class TheGolemListeners : InteractionListener { fun penOnPapyrus(player: Player): Boolean { if(!player.getAttribute("the-golem:varmen-notes-read", false)) { - player.sendMessage("You don't know what to write."); + player.sendMessage("You don't know what to write.") return true } if(player.inventory.remove(Item(Items.PAPYRUS_970, 1))) { @@ -345,12 +344,12 @@ class TheGolemListeners : InteractionListener { fun implementOnGolem(player: Player): Boolean { if(!player.getAttribute("the-golem:varmen-notes-read", false)) { - player.sendMessage("You don't know what that would do."); + 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 } @@ -395,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/thegolem/VarmensNotes.kt b/Server/src/main/content/region/desert/quest/thegolem/VarmensNotes.kt new file mode 100644 index 000000000..75cafd258 --- /dev/null +++ b/Server/src/main/content/region/desert/quest/thegolem/VarmensNotes.kt @@ -0,0 +1,298 @@ +package content.region.desert.quest.thegolem + +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.InteractionListener +import core.game.interaction.IntType +import core.game.node.entity.player.Player +import org.rs09.consts.Items + +/** + * Varmen's Notes Book + * @author ovenbreado + */ +class VarmensNotes : InteractionListener { + companion object { + + private val TITLE = "The Ruins of Uzer" + val CONTENTS = arrayOf( + PageSet( + Page( + BookLine("Septober 19:", 55), + BookLine("The nomads were right:", 56), + BookLine("there is a city here,", 57), + BookLine("probably buried for", 58), + BookLine("millenia and revealed by", 59), + BookLine("the random motions of", 60), + BookLine("the sand. The", 61), + BookLine("architecture is impressive", 62), + BookLine("even in ruin, and must", 63), + BookLine("once have been amazing.", 64), + BookLine("One puzzling factor is the", 65), + ), + Page( + BookLine("pottery -- there are", 66), + BookLine("fragments all over the", 67), + BookLine("ruins, surely too much", 68), + BookLine("for a city even of this", 69), + BookLine("size. We have set up", 70), + BookLine("camp and will do a proper", 71), + BookLine("survey tomorrow.", 72), + ) + ), + PageSet( + Page( + BookLine("Septober 20:", 55), + BookLine("The meaning of the", 56), + BookLine("pottery was explained", 57), + BookLine("today in a most", 58), + BookLine("surprising manner. We", 59), + BookLine("found a mostly-intact clay", 60), + BookLine("statue buried up to its", 61), + BookLine("waist in sand, and as", 62), + BookLine("soon as we dug it out, it", 63), + BookLine("started to walk around! It", 64), + BookLine("is a clay golem, built by", 65), + ), + Page( + BookLine("the city's inhabitants and", 66), + BookLine("dormant all this time. Its", 67), + BookLine("head is badly damaged", 68), + BookLine("and it is", 69), + BookLine("uncommunicative, but its", 70), + BookLine("existence tells us that the", 71), + BookLine("city's inhabitants were", 72), + BookLine("expert magical craftsmen.", 73), + BookLine("The huge kilns in some", 74), + BookLine("of the buildings indicate", 75), + BookLine("that at some point before", 76), + ) + ), + PageSet( + Page( + BookLine("its destruction the whole", 55), + BookLine("city was converted to the", 56), + BookLine("manufacture of these", 57), + BookLine("golems.", 58), + BookLine("", 59), + BookLine("We have also examined", 60), + BookLine("the carvings on the large", 61), + BookLine("building in the centre.", 62), + BookLine("These are symbols", 63), + BookLine("depicting several of the", 64), + BookLine("ancient gods, including", 65), + ), + Page( + BookLine("Saradomin, Zamorak, and", 66), + BookLine("Armadyl, but there is", 67), + BookLine("another prominent symbol", 68), + BookLine("that I cannot identify. As", 69), + BookLine("it seems we will need to", 70), + BookLine("be here for longer than I", 71), + BookLine("had thought, I have sent", 72), + BookLine("to Elissa for books on", 73), + BookLine("golems and religious", 74), + BookLine("symbols.", 75), + ) + ), + PageSet( + Page( + BookLine("Septober 21:", 55), + BookLine("As we examine the ruins", 56), + BookLine("one thing becomes", 57), + BookLine("increasingly clear: most", 58), + BookLine("of the damage was not", 59), + BookLine("due to weathering. The", 60), + BookLine("buildings were destroyed", 61), + BookLine("by force, as if torn down", 62), + BookLine("by giant hands.", 63), + ), + Page( + BookLine("Septober 22:", 66), + BookLine("A breakthrough! We have", 67), + BookLine("found the staircase into", 68), + BookLine("the lower levels of the", 69), + BookLine("temple. This part has", 70), + BookLine("been untouched by the", 71), + BookLine("elements, and the", 72), + BookLine("carvings here are more", 73), + BookLine("intact, especially four", 74), + BookLine("beautiful statuettes in", 75), + BookLine("alcoves framing the large", 76), + ) + ), + PageSet( + Page( + BookLine("door. I have removed one", 55), + BookLine("of them. The door will", 56), + BookLine("not open. I am glad I", 57), + BookLine("sent for a book on", 58), + BookLine("symbols, as the", 59), + BookLine("unidentified symbol is", 60), + BookLine("even more prominent", 61), + BookLine("here, especially on the", 62), + BookLine("door.", 63), + ), + Page( + BookLine("Septober 23:", 66), + BookLine("Our messenger returned", 67), + BookLine("with the books I asked for", 68), + BookLine("and a letter from Elissa.", 69), + BookLine("It is unfortunate that the", 70), + BookLine("museum will not be able", 71), + BookLine("to finance a full-scale", 72), + BookLine("excavation here as well as", 73), + BookLine("the one closer to Varrock,", 74), + BookLine("although I am of course", 75), + BookLine("pleased that the other city", 76), + ) + ), + PageSet( + Page( + BookLine("has been uncovered. But", 55), + BookLine("with the books I am able", 56), + BookLine("to piece together more of", 57), + BookLine("the story of this city.", 58), + BookLine("", 59), + BookLine("The unidentified symbol", 60), + BookLine("in the ruins is that of the", 61), + BookLine("demon Thammaron, who", 62), + BookLine("was Zamorak's chief", 63), + BookLine("lieutenant during the", 64), + BookLine("godwars of the Third", 65), + ), + Page( + BookLine("Age. With that", 66), + BookLine("information I can say", 67), + BookLine("with confidence that these", 68), + BookLine("are the ruins of Uzer, an", 69), + BookLine("advanced human", 70), + BookLine("civilization said to have", 71), + BookLine("been destroyed towards", 72), + BookLine("the end of the Third Age", 73), + BookLine("(roughly 2,500 years", 74), + BookLine("ago). It was allied with", 75), + BookLine("Saradomin and enjoyed", 76), + ) + ), + PageSet( + Page( + BookLine("his protection, as well as", 55), + BookLine("that of its own mages and", 56), + BookLine("warriors. Thammaron was", 57), + BookLine("able to open a portal from", 58), + BookLine("his own domain straight", 59), + BookLine("into the heart of the city,", 60), + BookLine("bypassing its defences.", 61), + BookLine("With Saradomin's help the", 62), + BookLine("army of Uzer was able to", 63), + BookLine("drive Thammaron back,", 64), + BookLine("but the record ends at", 65), + ), + Page( + BookLine("that point and it has", 66), + BookLine("always been assumed that", 67), + BookLine("a later attack, either by", 68), + BookLine("Thammaron or by", 69), + BookLine("Zamorak's other forces,", 70), + BookLine("finished the city off.", 71), + BookLine("", 72), + BookLine("Examining the door", 73), + BookLine("again, I now see that it is", 74), + BookLine("exactly the sort of door", 75), + BookLine("that could be used to seal", 76), + ) + ), + PageSet( + Page( + BookLine("Thammaron's portal. I am", 55), + BookLine("suddently glad I was not", 56), + BookLine("able to open it! I surmise", 57), + BookLine("that the army of golems", 58), + BookLine("was created in order to", 59), + BookLine("fight the demon, since", 60), + BookLine("Uzer's army had been", 61), + BookLine("wiped out and", 62), + BookLine("Saradomin's forces were", 63), + BookLine("increasingly stretched.", 64), + BookLine("However, this approach", 65), + ), + Page( + BookLine("evidently failed, since the", 66), + BookLine("city was eventually", 67), + BookLine("destroyed.", 68), + BookLine("", 69), + BookLine("The art of the", 70), + BookLine("construction of golems", 71), + BookLine("has been lost since the", 72), + BookLine("Third Age, and, although", 73), + BookLine("they are sometimes", 74), + BookLine("discovered lying dormant", 75), + BookLine("in the ground, no", 76), + ) + ), + PageSet( + Page( + BookLine("concerted effort has been", 55), + BookLine("made to regain it, thanks", 56), + BookLine("largely to the modern", 57), + BookLine("Saradomist Church's view", 58), + BookLine("of them as unnatural.", 59), + BookLine("This view is without", 60), + BookLine("foundation, as golems are", 61), + BookLine("neither good nor evil but", 62), + BookLine("follow instructions they", 63), + BookLine("are given to the letter", 64), + BookLine("and without imagination,", 65), + ), + Page( + BookLine("indeed experiencing", 66), + BookLine("extreme discomfort for as", 67), + BookLine("long as a task assigned to", 68), + BookLine("them remains incomplete.", 69), + BookLine("Some golems were", 70), + BookLine("constructed to obey", 71), + BookLine("verbal instructions, but", 72), + BookLine("the main method of", 73), + BookLine("instruction was to place", 74), + BookLine("magical words into the", 75), + BookLine("golem's skull cavity.", 76), + ) + ), + PageSet( + Page( + BookLine("These were written on", 55), + BookLine("papyrus using a naturally", 56), + BookLine("occurring source of ink,", 57), + BookLine("and their magical power", 58), + BookLine("derived from the use of a", 59), + BookLine("phoenix tail feather as a", 60), + BookLine("pen. These would be used", 61), + BookLine("for long-term or", 62), + BookLine("important tasks, and", 63), + BookLine("would override any verbal", 64), + BookLine("instructions.", 65), + ) + ) + ) + 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)) { + player.setAttribute("/save:the-golem:varmen-notes-read", true) + } + return true + } + } + + override fun defineListeners() { + on(Items.VARMENS_NOTES_4616, 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/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 e4a1b05d6..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)) { @@ -94,7 +95,8 @@ public final class BedabinNomadDialogue extends DialoguePlugin { case 2: end(); final Scenery door = RegionManager.getObject(new Location(3169, 3046, 0)); - SceneryBuilder.replace(door, door.transform(2701), 2); + if (door.getId() != 2701) + SceneryBuilder.replace(door, door.transform(2701), 2); player.getWalkingQueue().reset(); player.getWalkingQueue().addPath(3169, 3046); player.getPacketDispatch().sendMessage("You walk into the tent."); 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 688320289..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."); @@ -109,7 +110,7 @@ public final class MinecartDriverDialogue extends DialoguePlugin { stage++; break; case 7: - player("One good turn deserves another�."); + player("One good turn deserves another."); stage++; break; case 8: 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 be6ba786a..75f4d6134 100644 --- a/Server/src/main/content/region/desert/quest/thetouristrap/TouristTrap.java +++ b/Server/src/main/content/region/desert/quest/thetouristrap/TouristTrap.java @@ -13,6 +13,9 @@ import core.game.world.map.zone.ZoneBorders; 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. * @author Aero @@ -21,12 +24,6 @@ import core.plugin.ClassScanner; */ @Initializable public final class TouristTrap extends Quest { - - /** - * The name of the quest. - */ - public static final String NAME = "The Tourist Trap"; - /** * The metal key item. */ @@ -111,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 @@ -253,7 +250,7 @@ public final class TouristTrap extends Quest { * @param player the player. */ public static void addConfig(final Player player, final int value) { - player.getConfigManager().set(CONFIG_ID, value, true); + setVarp(player, CONFIG_ID, value, true); } /** 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 370f262b8..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; @@ -38,6 +39,8 @@ import core.tools.RandomFunction; import content.region.desert.quest.thetouristrap.TouristTrapPlugin.AnnaCartHandler.AnnaCartCutscene; import content.region.desert.quest.thetouristrap.TouristTrapPlugin.BedabinAnvilHandler.AnnaWinchHandler; +import static core.api.ContentAPIKt.*; + /** * Represents the plugin used to handle interactions for tourist trap. * @@ -108,6 +111,7 @@ public final class TouristTrapPlugin extends OptionHandler { SceneryDefinition.forId(18951).getHandlers().put("option:use", this); SceneryDefinition.forId(2684).getHandlers().put("option:search", this); SceneryDefinition.forId(2684).getHandlers().put("option:look at", this); + SceneryDefinition.forId(18875).getHandlers().put("option:inspect", this); NPCDefinition.forId(830).getHandlers().put("option:watch", this); NPCDefinition.forId(4975).getHandlers().put("option:talk-to", this); NPCDefinition.forId(4976).getHandlers().put("option:talk-to", this); @@ -132,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": @@ -353,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; @@ -429,7 +433,7 @@ public final class TouristTrapPlugin extends OptionHandler { @Override public boolean pulse() { player.getPacketDispatch().sendMessage("You bend the bars back."); - player.getConfigManager().set(907, player.getConfigManager().get(907) + 1); + setVarp(player, 907, getVarp(player, 907) + 1); return true; } }); @@ -480,6 +484,11 @@ public final class TouristTrapPlugin extends OptionHandler { }); } break; + case "inspect": + if (id == 18875) { + sendDialogue(player, "You remember that Irena mentioned something about Ana wearing a red scarf before she left for the desert."); + } + break; } return true; } @@ -625,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: @@ -692,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); @@ -792,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); @@ -897,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); @@ -1295,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; @@ -1394,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; @@ -1428,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/dialogue/EmbalmerDialogue.kt b/Server/src/main/content/region/desert/sophanem/dialogue/EmbalmerDialogue.kt index 60e13e00f..99ee9e3db 100644 --- a/Server/src/main/content/region/desert/sophanem/dialogue/EmbalmerDialogue.kt +++ b/Server/src/main/content/region/desert/sophanem/dialogue/EmbalmerDialogue.kt @@ -12,26 +12,26 @@ import org.rs09.consts.NPCs */ @Initializable -class EmbalmerDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class EmbalmerDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - npc(core.game.dialogue.FacialExpression.SUSPICIOUS,"What are you doing?").also { stage = 0 } + npc(FacialExpression.SUSPICIOUS,"What are you doing?").also { stage = 0 } return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 0 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY, "I have this acne potion which I thought might help ease your itching.").also { stage++ } - 1 -> npcl(core.game.dialogue.FacialExpression.ANNOYED, "If I thought that these spots could be cured by some potion, I would have mixed up one myself before now.").also { stage++ } - 2 -> player(core.game.dialogue.FacialExpression.SAD, "Sorry, I was just trying to help.").also { stage = 99 } + 0 -> playerl(FacialExpression.FRIENDLY, "I have this acne potion which I thought might help ease your itching.").also { stage++ } + 1 -> npcl(FacialExpression.ANNOYED, "If I thought that these spots could be cured by some potion, I would have mixed up one myself before now.").also { stage++ } + 2 -> player(FacialExpression.SAD, "Sorry, I was just trying to help.").also { stage = 99 } 99 -> end() } return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return EmbalmerDialogue(player) } diff --git a/Server/src/main/content/region/desert/sophanem/dialogue/TarikDialogue.kt b/Server/src/main/content/region/desert/sophanem/dialogue/TarikDialogue.kt index e12795c91..ac6aaa061 100644 --- a/Server/src/main/content/region/desert/sophanem/dialogue/TarikDialogue.kt +++ b/Server/src/main/content/region/desert/sophanem/dialogue/TarikDialogue.kt @@ -10,7 +10,7 @@ import org.rs09.consts.NPCs import core.tools.END_DIALOGUE @Initializable -class TarikDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { +class TarikDialogue(player: Player? = null) : DialoguePlugin(player) { override fun open(vararg args: Any?): Boolean { npcl(FacialExpression.WORRIED, "Ouch!") return true @@ -77,7 +77,7 @@ class TarikDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin( return intArrayOf(NPCs.TARIK_4478) } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return TarikDialogue(player) } } \ No newline at end of file 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 2b03f8923..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; @@ -12,9 +13,12 @@ import core.game.world.update.flag.context.Animation; 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 @@ -24,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; } @@ -32,13 +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, 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 new file mode 100644 index 000000000..f2b790453 --- /dev/null +++ b/Server/src/main/content/region/desert/ullek/handlers/UllekListeners.kt @@ -0,0 +1,42 @@ +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 { + override fun defineListeners() { + on(Scenery.STAIRS_DOWN_28481, IntType.SCENERY, "enter") { player, _ -> + player.properties.teleportLocation = Location.create(3448, 9252, 1) + return@on true + } + on(Scenery.EXIT_28401, IntType.SCENERY, "leave through") { player, _ -> + player.properties.teleportLocation = Location.create(3412, 2848, 1) + return@on true + } + on(Scenery.FALLEN_PILLAR_28516, IntType.SCENERY, "climb") { player, _ -> + player.properties.teleportLocation = Location.create(3419, 2801, 0) + return@on true + } + on(Scenery.FALLEN_PILLAR_28515, IntType.SCENERY, "climb") { player, _ -> + 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 4d192382e..19e639678 100644 --- a/Server/src/main/content/region/fremennik/dialogue/LokarSearunnerDialogue.java +++ b/Server/src/main/content/region/fremennik/dialogue/LokarSearunnerDialogue.java @@ -11,6 +11,8 @@ import core.net.packet.PacketRepository; 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. @@ -20,7 +22,7 @@ import core.net.packet.out.MinimapState; public class LokarSearunnerDialogue extends DialoguePlugin { /** - * The location of relekka. + * The location of Rellekka. */ private static final Location RELEKKA = new Location(2621, 3687, 0); @@ -58,10 +60,10 @@ public class LokarSearunnerDialogue extends DialoguePlugin { npc = (NPC) args[0]; switch (npc.getId()) { case 4536: - npc("Hi, would you like to take a boat trip to relekka?"); + npc("Hi, would you like to take a boat trip to Rellekka?"); break; case 4537: - npc("Hi, would you like to take a boat trip to pirate's cove?"); + npc("Hi, would you like to take a boat trip to Pirates' Cove?"); break; } return true; @@ -109,6 +111,8 @@ public class LokarSearunnerDialogue extends DialoguePlugin { * @param location the location. */ private void travel(final Player player, final Location location) { + if (!hasRequirement(player, Quests.LUNAR_DIPLOMACY)) + return; player.lock(); GameWorld.getPulser().submit(new Pulse(1, player) { int counter; diff --git a/Server/src/main/content/region/fremennik/diary/FremennikAchievementDiary.kt b/Server/src/main/content/region/fremennik/diary/FremennikAchievementDiary.kt index fe82a6617..51d160974 100644 --- a/Server/src/main/content/region/fremennik/diary/FremennikAchievementDiary.kt +++ b/Server/src/main/content/region/fremennik/diary/FremennikAchievementDiary.kt @@ -1,18 +1,23 @@ package content.region.fremennik.diary -import core.game.node.entity.player.Player -import core.game.node.entity.player.link.diary.DiaryType -import core.game.world.map.zone.ZoneBorders -import org.rs09.consts.Items -import org.rs09.consts.NPCs -import org.rs09.consts.Scenery -import content.minigame.barbassault.CaptainCainDialogue -import content.region.fremennik.rellekka.dialogue.HuntingExpertRellekkaDialogue import content.global.handlers.iface.FairyRing +import content.minigame.barbassault.CaptainCainDialogue +import content.region.fremennik.jatizso.dialogue.TowerGuardDialogue +import content.region.fremennik.rellekka.dialogue.HuntingExpertRellekkaDialogue +import content.region.fremennik.rellekka.quest.thefremenniktrials.ChieftanBrundt +import core.api.inBorders +import core.api.inEquipment import core.game.diary.AreaDiaryTask import core.game.diary.DiaryEventHookBase import core.game.diary.DiaryLevel import core.game.event.* +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.SpellBookManager +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 { @@ -20,8 +25,13 @@ class FremennikAchievementDiary : DiaryEventHookBase(DiaryType.FREMENNIK) { private const val ATTRIBUTE_ROCK_CRAB_KILLCOUNT = "diary:fremennik:rock-crabs-killed" private const val ATTRIBUTE_BARBARIAN_FISHING_TRAINING = "barbtraining:fishing" private const val ATTRIBUTE_BARBARIAN_HUNTING_TRAINING = "barbtraining:hunting" + private const val ATTRIBUTE_DAGANNOTHS_KILLCOUNT = "diary:fremennik:dagannoths-killed" private val WINDSWEPT_TREE_AREA = ZoneBorders(2743, 3718, 2750, 3737) + private val YRSA_SHOP_BORDERS = ZoneBorders(2622, 3672, 2626, 3676) + private val RELLEKKA_HUNTING_AREA = ZoneBorders(2723, 3776, 2743, 3796) + private val ECTERNIA_TREE_AREA = ZoneBorders(2592, 3887,2620, 3899) + private val RELLEKKA_BLACKSMITH_AREA = ZoneBorders(2616, 3658, 2621, 3668) private val FISHING_SPOTS = arrayOf( NPCs.FISHING_SPOT_309, NPCs.FISHING_SPOT_334, @@ -37,6 +47,17 @@ class FremennikAchievementDiary : DiaryEventHookBase(DiaryType.FREMENNIK) { NPCs.CAVE_CRAWLER_1602, NPCs.CAVE_CRAWLER_1603 ) + private val DAGANNOTHS = arrayOf( + NPCs.DAGANNOTH_2455, NPCs.DAGANNOTH_2456, + NPCs.DAGANNOTH_PRIME_2882, NPCs.DAGANNOTH_REX_2883, NPCs.DAGANNOTH_SUPREME_2881 + ) + + private val ICE_TROLLS = arrayOf( + NPCs.ICE_TROLL_RUNT_5521, NPCs.ICE_TROLL_MALE_5522, NPCs.ICE_TROLL_FEMALE_5523, NPCs.ICE_TROLL_GRUNT_5524, + NPCs.ICE_TROLL_RUNT_5525, NPCs.ICE_TROLL_MALE_5526, NPCs.ICE_TROLL_FEMALE_5527, NPCs.ICE_TROLL_GRUNT_5528, + NPCs.ICE_TROLL_RUNT_5473, NPCs.ICE_TROLL_MALE_5474, NPCs.ICE_TROLL_FEMALE_5475, NPCs.ICE_TROLL_GRUNT_5476 + ) + object EasyTasks { const val SLAYER_CAVE_KILL_CAVE_CRAWLER = 0 const val KILL_5_ROCK_CRABS = 1 @@ -121,15 +142,64 @@ class FremennikAchievementDiary : DiaryEventHookBase(DiaryType.FREMENNIK) { } } + override fun onDialogueOptionSelected(player: Player, event: DialogueOptionSelectionEvent) { + when (event.dialogue) { + is TowerGuardDialogue -> { + if (event.currentStage == 10) { + finishTask( + player, + DiaryLevel.MEDIUM, + MediumTasks.TOWER_GUARDS_WATCH_SHOUTING_MATCH + ) + } + } + + is ChieftanBrundt -> { + if (event.currentStage == 615){ + finishTask( + player, + DiaryLevel.MEDIUM, + MediumTasks.LEARN_HISTORY_FROM_CHIEFTAIN_BRUNDT + ) + } + } + } + } + override fun onResourceProduced(player: Player, event: ResourceProducedEvent) { when (player.viewport.region.id) { 10553 -> if (event.source.id in FISHING_SPOTS) { finishTask( - player, - DiaryLevel.EASY, + player, + DiaryLevel.EASY, EasyTasks.PIER_CATCH_FISH ) + } else if (event.itemId == Items.CHEESE_1985) { + finishTask( + player, + DiaryLevel.MEDIUM, + MediumTasks.MAKE_CHEESE_WITH_CHURN + ) + } else if (event.source.id == Scenery.FISH_STALL_4277) { + finishTask( + player, + DiaryLevel.MEDIUM, + MediumTasks.STEAL_FISH_FROM_STALL + ) } + + /* After Royal trouble quest + 10300 -> when (event.itemId) { + Items.MAHOGANY_LOGS_6332 -> { + if (inBorders(player, ECTERNIA_TREE_AREA)) { + finishTask( + player, + DiaryLevel.HARD, + HardTasks.GET_MAHOGANY_FROM_ETCETERIA + ) + } + } + }*/ } } @@ -160,6 +230,32 @@ class FremennikAchievementDiary : DiaryEventHookBase(DiaryType.FREMENNIK) { EasyTasks.KILL_ADULT_BLACK_UNICORN ) } + + 9886, 10142, 11589 -> if (event.npc.id in DAGANNOTHS) { + progressIncrementalTask( + player, + DiaryLevel.HARD, + HardTasks.KILL_3_DAGANNOTHS, + ATTRIBUTE_DAGANNOTHS_KILLCOUNT, + 3 + ) + } + + 6995 -> if (event.npc.id == NPCs.MITHRIL_DRAGON_5363) { + finishTask( + player, + DiaryLevel.HARD, + HardTasks.KILL_MITHRIL_DRAGON + ) + } + + 9276, 9532 -> if (event.npc.id in ICE_TROLLS && inEquipment(player, Items.YAK_HIDE_ARMOUR_10822,1)) { + finishTask( + player, + DiaryLevel.MEDIUM, + MediumTasks.KILL_ICE_TROLL_WHILE_WEARING_YAK_ARMOR + ) + } } } @@ -198,4 +294,65 @@ class FremennikAchievementDiary : DiaryEventHookBase(DiaryType.FREMENNIK) { } } } + + override fun onInteracted(player: Player, event: InteractionEvent) { + when (player.viewport.region.id) { + 10811 -> if (event.target.id == Scenery.COLLAPSED_TRAP_19233 && inBorders(player, RELLEKKA_HUNTING_AREA) && event.option == "dismantle") { + finishTask( + player, + DiaryLevel.MEDIUM, + MediumTasks.HUNT_SABRE_TOOTHED_KYATT + ) + } + } + + if (event.option == "watch-shouting") { + finishTask( + player, + DiaryLevel.MEDIUM, + MediumTasks.TOWER_GUARDS_WATCH_SHOUTING_MATCH + ) + } + + if (event.target.id == Items.PET_ROCK_3695 && event.option == "interact") { + finishTask( + player, + DiaryLevel.MEDIUM, + MediumTasks.INTERACT_WITH_PET_ROCK + ) + } + + // 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(Quests.THE_FREMENNIK_TRIALS) && inBorders(player, YRSA_SHOP_BORDERS)) { + finishTask( + player, + DiaryLevel.MEDIUM, + MediumTasks.BROWSE_BOOT_COLORS_YRSA + ) + } + } + + override fun onSpellCast(player: Player, event: SpellCastEvent) { + if (event.spellBook == SpellBookManager.SpellBook.LUNAR && event.spellId == 15) { + finishTask( + player, + DiaryLevel.HARD, + HardTasks.BAKE_PIE_WITH_MAGIC + ) + } + } + + override fun onButtonClicked(player: Player, event: ButtonClickEvent) { + when { + inBorders(player, RELLEKKA_BLACKSMITH_AREA) -> { + if (event.iface == Components.CRAFTING_GLASS_542 && event.buttonId == 38 && player.skills.getLevel(Skills.CRAFTING) >= 33) { + finishTask( + player, + DiaryLevel.MEDIUM, + MediumTasks.MAKE_3_VIALS + ) + } + } + } + } } \ No newline at end of file diff --git a/Server/src/main/content/region/fremennik/jatizso/dialogue/CitizenDialogue.kt b/Server/src/main/content/region/fremennik/jatizso/dialogue/CitizenDialogue.kt index 41ad1963c..a964ae552 100644 --- a/Server/src/main/content/region/fremennik/jatizso/dialogue/CitizenDialogue.kt +++ b/Server/src/main/content/region/fremennik/jatizso/dialogue/CitizenDialogue.kt @@ -8,9 +8,9 @@ import org.rs09.consts.NPCs import core.tools.END_DIALOGUE @Initializable -class CitizenDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { +class CitizenDialogue(player: Player? = null) : DialoguePlugin(player) { val stages = intArrayOf(0, 100, 200, 300) - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return CitizenDialogue(player) } @@ -22,30 +22,30 @@ class CitizenDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugi override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 0 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL, "It's a bit grey round here, isn't it?").also { stage++ } - 1 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "It gets you down after a while, you know. There are 273 shades of grey, you know, and we have them all.").also { stage++ } - 2 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL, "That's grey-t.").also { stage++ } - 3 -> npcl(core.game.dialogue.FacialExpression.SAD, "That attempt at humour merely made me more depressed. Leave me alone.").also { stage = END_DIALOGUE } + 0 -> playerl(FacialExpression.NEUTRAL, "It's a bit grey round here, isn't it?").also { stage++ } + 1 -> npcl(FacialExpression.NEUTRAL, "It gets you down after a while, you know. There are 273 shades of grey, you know, and we have them all.").also { stage++ } + 2 -> playerl(FacialExpression.NEUTRAL, "That's grey-t.").also { stage++ } + 3 -> npcl(FacialExpression.SAD, "That attempt at humour merely made me more depressed. Leave me alone.").also { stage = END_DIALOGUE } - 100 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL, "Cheer up! It's not the end of the world.").also { stage++ } - 101 -> npcl(core.game.dialogue.FacialExpression.SAD, "I'd prefer that, if it meant I didn't have to talk to people as inanely happy as you.").also { stage++ } - 102 -> playerl(core.game.dialogue.FacialExpression.AMAZED, "Whoa! I think you need to get out more.").also { stage = END_DIALOGUE } + 100 -> playerl(FacialExpression.NEUTRAL, "Cheer up! It's not the end of the world.").also { stage++ } + 101 -> npcl(FacialExpression.SAD, "I'd prefer that, if it meant I didn't have to talk to people as inanely happy as you.").also { stage++ } + 102 -> playerl(FacialExpression.AMAZED, "Whoa! I think you need to get out more.").also { stage = END_DIALOGUE } - 200 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL, "How's the King treating you then?").also { stage++ } - 201 -> npcl(core.game.dialogue.FacialExpression.SAD, "Like serfs.").also { stage++ } - 202 -> playerl(core.game.dialogue.FacialExpression.HALF_THINKING, "Serf?").also { stage++ } - 203 -> npcl(core.game.dialogue.FacialExpression.SAD, "Yes, you know - peons, plebs, the downtrodden. He treats us like his own personal possessions.").also { stage++ } - 204 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL, "You should leave this place.").also { stage++ } - 205 -> npcl(core.game.dialogue.FacialExpression.SAD, "I keep trying to save up enough to leave, but the King keeps taxing us! We have no money left." ).also { stage++ } - 206 -> playerl(core.game.dialogue.FacialExpression.SAD, "Oh dear." ).also { stage = END_DIALOGUE } + 200 -> playerl(FacialExpression.NEUTRAL, "How's the King treating you then?").also { stage++ } + 201 -> npcl(FacialExpression.SAD, "Like serfs.").also { stage++ } + 202 -> playerl(FacialExpression.HALF_THINKING, "Serf?").also { stage++ } + 203 -> npcl(FacialExpression.SAD, "Yes, you know - peons, plebs, the downtrodden. He treats us like his own personal possessions.").also { stage++ } + 204 -> playerl(FacialExpression.NEUTRAL, "You should leave this place.").also { stage++ } + 205 -> npcl(FacialExpression.SAD, "I keep trying to save up enough to leave, but the King keeps taxing us! We have no money left." ).also { stage++ } + 206 -> playerl(FacialExpression.SAD, "Oh dear." ).also { stage = END_DIALOGUE } - 300 -> playerl(core.game.dialogue.FacialExpression.HALF_THINKING, "How are you today?").also { stage++ } - 301 -> npcl(core.game.dialogue.FacialExpression.SAD, "**sigh**").also { stage++ } - 302 -> playerl(core.game.dialogue.FacialExpression.ASKING, "That good? Everyone around here seems a little depressed. ").also { stage++ } - 303 -> npcl(core.game.dialogue.FacialExpression.SAD, "**sigh**").also { stage++ } - 304 -> playerl(core.game.dialogue.FacialExpression.HALF_THINKING, "And not particularly talkative.").also { stage++ } - 305 -> npcl(core.game.dialogue.FacialExpression.SAD, "**sigh**").also { stage++ } - 306 -> playerl(core.game.dialogue.FacialExpression.HALF_THINKING,"I'll leave you to your sighing. It looks like you have plenty to do." ).also { stage = END_DIALOGUE } + 300 -> playerl(FacialExpression.HALF_THINKING, "How are you today?").also { stage++ } + 301 -> npcl(FacialExpression.SAD, "**sigh**").also { stage++ } + 302 -> playerl(FacialExpression.ASKING, "That good? Everyone around here seems a little depressed. ").also { stage++ } + 303 -> npcl(FacialExpression.SAD, "**sigh**").also { stage++ } + 304 -> playerl(FacialExpression.HALF_THINKING, "And not particularly talkative.").also { stage++ } + 305 -> npcl(FacialExpression.SAD, "**sigh**").also { stage++ } + 306 -> playerl(FacialExpression.HALF_THINKING,"I'll leave you to your sighing. It looks like you have plenty to do." ).also { stage = END_DIALOGUE } } return true diff --git a/Server/src/main/content/region/fremennik/jatizso/dialogue/EricDialogue.kt b/Server/src/main/content/region/fremennik/jatizso/dialogue/EricDialogue.kt index 2d736c299..de6d0b84f 100644 --- a/Server/src/main/content/region/fremennik/jatizso/dialogue/EricDialogue.kt +++ b/Server/src/main/content/region/fremennik/jatizso/dialogue/EricDialogue.kt @@ -4,22 +4,22 @@ 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 import core.tools.END_DIALOGUE +import org.rs09.consts.NPCs @Initializable -class EricDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { +class EricDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player?): DialoguePlugin { return EricDialogue(player) } override fun open(vararg args: Any?): Boolean { - npcl(core.game.dialogue.FacialExpression.HALF_GUILTY, "Spare us a few coppers mister") + npcl(FacialExpression.HALF_GUILTY, "Spare us a few coppers mister") return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { - playerl(core.game.dialogue.FacialExpression.ANGRY, "NO!") + playerl(FacialExpression.ANGRY, "NO!") stage = END_DIALOGUE return true } diff --git a/Server/src/main/content/region/fremennik/jatizso/dialogue/GruvaPatrullDialogue.kt b/Server/src/main/content/region/fremennik/jatizso/dialogue/GruvaPatrullDialogue.kt index 1a5da89d1..cdaec8ac9 100644 --- a/Server/src/main/content/region/fremennik/jatizso/dialogue/GruvaPatrullDialogue.kt +++ b/Server/src/main/content/region/fremennik/jatizso/dialogue/GruvaPatrullDialogue.kt @@ -8,21 +8,21 @@ import org.rs09.consts.NPCs import core.tools.END_DIALOGUE @Initializable -class GruvaPatrullDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { +class GruvaPatrullDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player?): DialoguePlugin { return GruvaPatrullDialogue(player) } override fun open(vararg args: Any?): Boolean { - npcl(core.game.dialogue.FacialExpression.FRIENDLY,"Ho! Outerlander.") + npcl(FacialExpression.FRIENDLY,"Ho! Outerlander.") return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 0 -> playerl(core.game.dialogue.FacialExpression.HALF_ASKING, "What's down the scary-looking staircase?").also { stage++ } - 1 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "These are the stairs down to the mining caves. There are rich veins of many types down there, and miners too. Though be careful; some of the trolls occasionally sneak into the far end of the cave.").also { stage++ } - 2 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL, "Thanks. I'll look out for them.").also { stage = END_DIALOGUE } + 0 -> playerl(FacialExpression.HALF_ASKING, "What's down the scary-looking staircase?").also { stage++ } + 1 -> npcl(FacialExpression.NEUTRAL, "These are the stairs down to the mining caves. There are rich veins of many types down there, and miners too. Though be careful; some of the trolls occasionally sneak into the far end of the cave.").also { stage++ } + 2 -> playerl(FacialExpression.NEUTRAL, "Thanks. I'll look out for them.").also { stage = END_DIALOGUE } } return true } diff --git a/Server/src/main/content/region/fremennik/jatizso/dialogue/HringHringDialogue.kt b/Server/src/main/content/region/fremennik/jatizso/dialogue/HringHringDialogue.kt new file mode 100644 index 000000000..b7c7a585d --- /dev/null +++ b/Server/src/main/content/region/fremennik/jatizso/dialogue/HringHringDialogue.kt @@ -0,0 +1,55 @@ +package content.region.fremennik.jatizso.dialogue + +import core.api.openNpcShop +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 + +@Initializable +class HringHringDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun newInstance(player: Player?): DialoguePlugin { + return HringHringDialogue(player) + } + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + npcl( + FacialExpression.FRIENDLY, + " Oh, hello again. Want some ore?" + ).also { stage = START_DIALOGUE } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage) { + 0 -> options("I'll have a look.", "Not right now.").also { stage++ } + 1 -> when (buttonId) { + 1 -> playerl( + FacialExpression.FRIENDLY, + "I'll have a look." + ).also { stage++ } + + 2 -> playerl( + FacialExpression.FRIENDLY, + "Not right now." + ).also { stage = END_DIALOGUE } + } + + 2 -> { + openNpcShop(player, NPCs.HRING_HRING_5483) + end() + } + } + return true + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.HRING_HRING_5483) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/fremennik/jatizso/dialogue/LeftieRightieDialogue.kt b/Server/src/main/content/region/fremennik/jatizso/dialogue/LeftieRightieDialogue.kt index eb35d7f98..b2c3b14ee 100644 --- a/Server/src/main/content/region/fremennik/jatizso/dialogue/LeftieRightieDialogue.kt +++ b/Server/src/main/content/region/fremennik/jatizso/dialogue/LeftieRightieDialogue.kt @@ -12,21 +12,21 @@ class LeftieRightieDialogue() : DialogueFile() { val leftie = NPC(NPCs.GUARD_5492) override fun handle(componentID: Int, buttonID: Int) { when(stage){ - 0 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Are you all right? Leftie?").also { stage++ } + 0 -> npcl(FacialExpression.NEUTRAL, "Are you all right? Leftie?").also { stage++ } 1 -> npc2("No, I'm on the left.").also { stage++ } - 2 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Only from your perspective. Someone entering the gate should call you Rightie, right Leftie?").also { stage++ } + 2 -> npcl(FacialExpression.NEUTRAL, "Only from your perspective. Someone entering the gate should call you Rightie, right Leftie?").also { stage++ } 3 -> npc2("Right, Rightie. So you'd be Leftie not Rightie, right?").also { stage++ } - 4 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "That's right Leftie, that's right.").also { stage++ } + 4 -> npcl(FacialExpression.NEUTRAL, "That's right Leftie, that's right.").also { stage++ } 5 -> npc2("Rightie-oh Rightie, or should I call you Leftie?").also { stage++ } - 6 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "No, Rightie's fine Leftie.").also { stage++ } - 7 -> playerl(core.game.dialogue.FacialExpression.ANGRY, "Aaagh! Enough! If either of you mention left or right in my presence I'll have to scream! Can I come through the gate?" ).also { stage++ } + 6 -> npcl(FacialExpression.NEUTRAL, "No, Rightie's fine Leftie.").also { stage++ } + 7 -> playerl(FacialExpression.ANGRY, "Aaagh! Enough! If either of you mention left or right in my presence I'll have to scream! Can I come through the gate?" ).also { stage++ } 8 -> npc2("Don't let us stop you.").also { stage++ } - 9 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Yes, head right on in, sir.").also { stage++ } - 10 -> playerl(core.game.dialogue.FacialExpression.ANGRY, "You said it! You said it! ARRRRRRRRGH!").also { stage = END_DIALOGUE } + 9 -> npcl(FacialExpression.NEUTRAL, "Yes, head right on in, sir.").also { stage++ } + 10 -> playerl(FacialExpression.ANGRY, "You said it! You said it! ARRRRRRRRGH!").also { stage = END_DIALOGUE } } } fun npc2(messages: String){ - sendNormalDialogue(leftie, core.game.dialogue.FacialExpression.NEUTRAL, *splitLines(messages)) + sendNormalDialogue(leftie, FacialExpression.NEUTRAL, *splitLines(messages)) } } \ No newline at end of file diff --git a/Server/src/main/content/region/fremennik/jatizso/dialogue/MinerDialogue.kt b/Server/src/main/content/region/fremennik/jatizso/dialogue/MinerDialogue.kt index db3a4d65a..8a9212353 100644 --- a/Server/src/main/content/region/fremennik/jatizso/dialogue/MinerDialogue.kt +++ b/Server/src/main/content/region/fremennik/jatizso/dialogue/MinerDialogue.kt @@ -8,9 +8,9 @@ import org.rs09.consts.NPCs import core.tools.END_DIALOGUE @Initializable -class MinerDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { +class MinerDialogue(player: Player? = null) : DialoguePlugin(player) { val stages = intArrayOf(0, 100, 200, 300, 400, 500) - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return MinerDialogue(player) } @@ -22,35 +22,35 @@ class MinerDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin( override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 0 -> npcl(core.game.dialogue.FacialExpression.SAD, "I've been here for two days straight. When I close my eyes, I see rocks.").also { stage++ } - 1 -> playerl(core.game.dialogue.FacialExpression.HALF_ASKING, "Why would anyone stay here for so long?").also { stage++ } - 2 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "I fear the outside. I fear the big light.").also { stage++ } - 3 -> playerl(core.game.dialogue.FacialExpression.HALF_GUILTY, "Oh dear. Being underground for so long may have driven you mad.").also { stage = END_DIALOGUE } + 0 -> npcl(FacialExpression.SAD, "I've been here for two days straight. When I close my eyes, I see rocks.").also { stage++ } + 1 -> playerl(FacialExpression.HALF_ASKING, "Why would anyone stay here for so long?").also { stage++ } + 2 -> npcl(FacialExpression.NEUTRAL, "I fear the outside. I fear the big light.").also { stage++ } + 3 -> playerl(FacialExpression.HALF_GUILTY, "Oh dear. Being underground for so long may have driven you mad.").also { stage = END_DIALOGUE } - 100 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "My back is killing me!").also { stage++ } - 101 -> playerl(core.game.dialogue.FacialExpression.HALF_GUILTY, "Could be worse, it could be the trolls killing you." ).also { stage++ } - 102 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "How very droll.").also { stage++ } - 103 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL, "No, troll.").also { stage++ } - 104 -> npcl(core.game.dialogue.FacialExpression.SAD, "Bah! I resist your attempts at jollity.").also { stage = END_DIALOGUE } + 100 -> npcl(FacialExpression.NEUTRAL, "My back is killing me!").also { stage++ } + 101 -> playerl(FacialExpression.HALF_GUILTY, "Could be worse, it could be the trolls killing you." ).also { stage++ } + 102 -> npcl(FacialExpression.NEUTRAL, "How very droll.").also { stage++ } + 103 -> playerl(FacialExpression.NEUTRAL, "No, troll.").also { stage++ } + 104 -> npcl(FacialExpression.SAD, "Bah! I resist your attempts at jollity.").also { stage = END_DIALOGUE } - 200 -> npcl(core.game.dialogue.FacialExpression.SCARED, "Gah! Trolls everywhere. There's no escape!").also { stage++ } - 201 -> playerl(core.game.dialogue.FacialExpression.HALF_THINKING, "You could just leave the mine.").also { stage++ } - 202 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Oh, I'd never thought of that.").also { stage = END_DIALOGUE } + 200 -> npcl(FacialExpression.SCARED, "Gah! Trolls everywhere. There's no escape!").also { stage++ } + 201 -> playerl(FacialExpression.HALF_THINKING, "You could just leave the mine.").also { stage++ } + 202 -> npcl(FacialExpression.NEUTRAL, "Oh, I'd never thought of that.").also { stage = END_DIALOGUE } - 300 -> playerl(core.game.dialogue.FacialExpression.HALF_ASKING, "How's your prospecting going?").also { stage++ } - 301 -> npcl(core.game.dialogue.FacialExpression.LAUGH, "Mine's been going pretty well.").also { stage++ } - 302 -> playerl(core.game.dialogue.FacialExpression.HALF_THINKING, "...").also { stage++ } - 303 -> npcl(core.game.dialogue.FacialExpression.LAUGH, "Mine? Mine...you get it?").also { stage++ } - 304 -> playerl(core.game.dialogue.FacialExpression.HALF_THINKING, "Oh, I got it. That doesn't make it funny though.").also { stage++ } - 305 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Suit yourself.").also { stage = END_DIALOGUE } + 300 -> playerl(FacialExpression.HALF_ASKING, "How's your prospecting going?").also { stage++ } + 301 -> npcl(FacialExpression.LAUGH, "Mine's been going pretty well.").also { stage++ } + 302 -> playerl(FacialExpression.HALF_THINKING, "...").also { stage++ } + 303 -> npcl(FacialExpression.LAUGH, "Mine? Mine...you get it?").also { stage++ } + 304 -> playerl(FacialExpression.HALF_THINKING, "Oh, I got it. That doesn't make it funny though.").also { stage++ } + 305 -> npcl(FacialExpression.NEUTRAL, "Suit yourself.").also { stage = END_DIALOGUE } - 400 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "High hoe, High hoe!").also { stage++ } - 401 -> playerl(core.game.dialogue.FacialExpression.ASKING, "Why are you singing about farming implements at altitude?").also { stage++ } - 402 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "I don't know, I've never thought about it. Ask my Dad, he taught me the song.").also { stage = END_DIALOGUE } + 400 -> npcl(FacialExpression.NEUTRAL, "High hoe, High hoe!").also { stage++ } + 401 -> playerl(FacialExpression.ASKING, "Why are you singing about farming implements at altitude?").also { stage++ } + 402 -> npcl(FacialExpression.NEUTRAL, "I don't know, I've never thought about it. Ask my Dad, he taught me the song.").also { stage = END_DIALOGUE } - 500 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "This place rocks!").also { stage++ } - 501 -> playerl(core.game.dialogue.FacialExpression.HALF_THINKING, "No it doesn't, it stays perfectly still.").also { stage++ } - 502 -> npcl(core.game.dialogue.FacialExpression.HALF_THINKING, "Bah! Be quiet, pedant." ).also { stage = END_DIALOGUE } + 500 -> npcl(FacialExpression.NEUTRAL, "This place rocks!").also { stage++ } + 501 -> playerl(FacialExpression.HALF_THINKING, "No it doesn't, it stays perfectly still.").also { stage++ } + 502 -> npcl(FacialExpression.HALF_THINKING, "Bah! Be quiet, pedant." ).also { stage = END_DIALOGUE } } return true } 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 8cc1eb110..e4ed6b691 100644 --- a/Server/src/main/content/region/fremennik/jatizso/dialogue/MordGunnarsDialogue.kt +++ b/Server/src/main/content/region/fremennik/jatizso/dialogue/MordGunnarsDialogue.kt @@ -7,6 +7,8 @@ import org.rs09.consts.NPCs 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) { @@ -34,6 +36,8 @@ class MordGunnarsDialogue(player: Player? = null) : core.game.dialogue.DialogueP 2 -> { end() + 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) } } @@ -44,4 +48,4 @@ class MordGunnarsDialogue(player: Player? = null) : core.game.dialogue.DialogueP return intArrayOf(NPCs.MORD_GUNNARS_5482, NPCs.MORD_GUNNARS_5481) } -} \ No newline at end of file +} diff --git a/Server/src/main/content/region/fremennik/jatizso/handlers/JatizsoListeners.kt b/Server/src/main/content/region/fremennik/jatizso/handlers/JatizsoListeners.kt index b67f84798..65824ab40 100644 --- a/Server/src/main/content/region/fremennik/jatizso/handlers/JatizsoListeners.kt +++ b/Server/src/main/content/region/fremennik/jatizso/handlers/JatizsoListeners.kt @@ -2,7 +2,6 @@ package content.region.fremennik.jatizso.handlers import core.api.* import core.game.node.entity.npc.NPC -import core.game.node.entity.player.link.audio.Audio import core.game.system.task.Pulse import core.game.world.map.Direction import core.game.world.map.Location @@ -11,6 +10,7 @@ import org.rs09.consts.NPCs import content.region.fremennik.jatizso.dialogue.LeftieRightieDialogue import core.game.interaction.InteractionListener import core.game.interaction.IntType +import org.rs09.consts.Sounds class JatizsoListeners : InteractionListener { val GATES_CLOSED = intArrayOf(21403,21405) @@ -71,7 +71,7 @@ class JatizsoListeners : InteractionListener { replaceScenery(other.asScenery(), other.id, -1, Direction.SOUTH) } } - playAudio(player, getAudio(81)) + playAudio(player, Sounds.NICEDOOR_OPEN_81) return@on true } @@ -121,7 +121,7 @@ class JatizsoListeners : InteractionListener { } on(BELL, IntType.SCENERY, "ring-bell"){ player, _ -> - playAudio(player, Audio(15)) + playAudio(player, Sounds.STEEL_15) sendMessage(player, "You ring the warning bell, but everyone ignores it!") return@on true } diff --git a/Server/src/main/content/region/fremennik/lunarisle/dialogue/BabaYagaDialogue.kt b/Server/src/main/content/region/fremennik/lunarisle/dialogue/BabaYagaDialogue.kt index e64b9f872..b597ce416 100644 --- a/Server/src/main/content/region/fremennik/lunarisle/dialogue/BabaYagaDialogue.kt +++ b/Server/src/main/content/region/fremennik/lunarisle/dialogue/BabaYagaDialogue.kt @@ -13,31 +13,31 @@ import org.rs09.consts.NPCs */ @Initializable -class BabaYagaDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class BabaYagaDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - player(core.game.dialogue.FacialExpression.FRIENDLY,"Hello there.").also { stage = 0 } + player(FacialExpression.FRIENDLY,"Hello there.").also { stage = 0 } return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 0 -> npc(core.game.dialogue.FacialExpression.ASKING, "Ah, a stranger to our island. How can I help?").also { stage ++ } + 0 -> npc(FacialExpression.ASKING, "Ah, a stranger to our island. How can I help?").also { stage ++ } 1 -> options("Have you got anything to trade?", "It's a very interesting house you have here.", "I'm good thanks, bye.").also { stage ++ } 2 -> when(buttonId) { 1 -> end().also { npc.openShop(player) } - 2 -> playerl(core.game.dialogue.FacialExpression.ASKING, "It's a very interesting house you have here. Does he have a name?").also { stage = 10 } + 2 -> playerl(FacialExpression.ASKING, "It's a very interesting house you have here. Does he have a name?").also { stage = 10 } 3 -> end() } - 10 -> npc(core.game.dialogue.FacialExpression.FRIENDLY, "Why of course. It's Berty.").also { stage++ } - 11 -> player(core.game.dialogue.FacialExpression.THINKING, "Berty? Berty the Chicken leg house?").also { stage++ } - 12 -> npc(core.game.dialogue.FacialExpression.LAUGH, "Yes.").also { stage++ } - 13 -> player(core.game.dialogue.FacialExpression.ASKING, "May I ask why?").also { stage++ } - 14 -> npcl(core.game.dialogue.FacialExpression.LAUGH, "It just has that certain ring to it, don't you think? Beeerteeee!").also { stage++ } - 15 -> player(core.game.dialogue.FacialExpression.HALF_WORRIED, "You're ins...").also { stage++ } + 10 -> npc(FacialExpression.FRIENDLY, "Why of course. It's Berty.").also { stage++ } + 11 -> player(FacialExpression.THINKING, "Berty? Berty the Chicken leg house?").also { stage++ } + 12 -> npc(FacialExpression.LAUGH, "Yes.").also { stage++ } + 13 -> player(FacialExpression.ASKING, "May I ask why?").also { stage++ } + 14 -> npcl(FacialExpression.LAUGH, "It just has that certain ring to it, don't you think? Beeerteeee!").also { stage++ } + 15 -> player(FacialExpression.HALF_WORRIED, "You're ins...").also { stage++ } 16 -> npc("Insane? Very.").also { stage = 99 } 99 -> end() @@ -45,7 +45,7 @@ class BabaYagaDialogue(player: Player? = null) : core.game.dialogue.DialoguePlug return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return BabaYagaDialogue(player) } diff --git a/Server/src/main/content/region/fremennik/lunarisle/dialogue/BouquetMacHyacinthDialogue.kt b/Server/src/main/content/region/fremennik/lunarisle/dialogue/BouquetMacHyacinthDialogue.kt index dde5b6ade..29f063219 100644 --- a/Server/src/main/content/region/fremennik/lunarisle/dialogue/BouquetMacHyacinthDialogue.kt +++ b/Server/src/main/content/region/fremennik/lunarisle/dialogue/BouquetMacHyacinthDialogue.kt @@ -18,7 +18,7 @@ import java.util.concurrent.TimeUnit */ @Initializable -class BouquetMacHyacinthDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class BouquetMacHyacinthDialogue(player: Player? = null) : DialoguePlugin(player){ private val Rellekka = Location.create(2663, 3644, 0) private var teled = false @@ -28,9 +28,9 @@ class BouquetMacHyacinthDialogue(player: Player? = null) : core.game.dialogue.Di if (!teled) { if (player.inventory.contains(Items.SEAL_OF_PASSAGE_9083, 1) || player.equipment.contains(Items.SEAL_OF_PASSAGE_9083, 1)) { - player(core.game.dialogue.FacialExpression.FRIENDLY, "Hi! What are you up to?").also { stage = 0 } + player(FacialExpression.FRIENDLY, "Hi! What are you up to?").also { stage = 0 } } else { - player(core.game.dialogue.FacialExpression.FRIENDLY, "Hi, I...").also { stage = 5 } + player(FacialExpression.FRIENDLY, "Hi, I...").also { stage = 5 } } } return true @@ -38,15 +38,15 @@ class BouquetMacHyacinthDialogue(player: Player? = null) : core.game.dialogue.Di override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 0 -> npc(core.game.dialogue.FacialExpression.FRIENDLY,"Watering the pretty flowers, you want to help?").also { stage++ } - 1 -> playerl(core.game.dialogue.FacialExpression.HALF_WORRIED, "I don't have time to water flowers, I have people to save!").also { stage++ } - 2 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Pft, you should take time to enjoy the simple things.").also { stage++ } - 3 -> player(core.game.dialogue.FacialExpression.NEUTRAL, "I'm not a simple person.").also { stage++ } - 4 -> npc(core.game.dialogue.FacialExpression.LAUGH, "So it seems.").also { stage = 99 } + 0 -> npc(FacialExpression.FRIENDLY,"Watering the pretty flowers, you want to help?").also { stage++ } + 1 -> playerl(FacialExpression.HALF_WORRIED, "I don't have time to water flowers, I have people to save!").also { stage++ } + 2 -> npcl(FacialExpression.NEUTRAL, "Pft, you should take time to enjoy the simple things.").also { stage++ } + 3 -> player(FacialExpression.NEUTRAL, "I'm not a simple person.").also { stage++ } + 4 -> npc(FacialExpression.LAUGH, "So it seems.").also { stage = 99 } - 5 -> npc(core.game.dialogue.FacialExpression.ANNOYED, "What are you doing here, Fremennik?!").also { stage++ } - 6 -> player(core.game.dialogue.FacialExpression.WORRIED, "I have a seal of pass...").also { stage++ } - 7 -> npc(core.game.dialogue.FacialExpression.ANNOYED, "No you do not! Begone!").also { stage++ } + 5 -> npc(FacialExpression.ANNOYED, "What are you doing here, Fremennik?!").also { stage++ } + 6 -> player(FacialExpression.WORRIED, "I have a seal of pass...").also { stage++ } + 7 -> npc(FacialExpression.ANNOYED, "No you do not! Begone!").also { stage++ } 8 -> teleport(player, Rellekka, TeleportManager.TeleportType.LUNAR).also { wait1() } 99 -> end() @@ -58,7 +58,7 @@ class BouquetMacHyacinthDialogue(player: Player? = null) : core.game.dialogue.Di Executors.newSingleThreadScheduledExecutor().schedule({ if (player.location.isInRegion(Rellekka.regionId)) { playerl( - core.game.dialogue.FacialExpression.WORRIED, + FacialExpression.WORRIED, "Ooops. Suppose I need a seal of passage when I'm walking around that island." ).also { end() } } else { @@ -67,7 +67,7 @@ class BouquetMacHyacinthDialogue(player: Player? = null) : core.game.dialogue.Di }, 1, TimeUnit.SECONDS) } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return BouquetMacHyacinthDialogue(player) } diff --git a/Server/src/main/content/region/fremennik/lunarisle/dialogue/CabinBoyDialogue.kt b/Server/src/main/content/region/fremennik/lunarisle/dialogue/CabinBoyDialogue.kt index e4ad4cd4d..4ce80aecf 100644 --- a/Server/src/main/content/region/fremennik/lunarisle/dialogue/CabinBoyDialogue.kt +++ b/Server/src/main/content/region/fremennik/lunarisle/dialogue/CabinBoyDialogue.kt @@ -15,15 +15,15 @@ import org.rs09.consts.NPCs */ @Initializable -class CabinBoyDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class CabinBoyDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC if (player.inventory.contains(Items.EMERALD_LENS_9066, 1) || player.bank.contains(Items.EMERALD_LENS_9066, 1)) { - playerl(core.game.dialogue.FacialExpression.FRIENDLY, "So you've plucked up the courage to come and confront that girl!").also { stage = 0 } + playerl(FacialExpression.FRIENDLY, "So you've plucked up the courage to come and confront that girl!").also { stage = 0 } } else { - player(core.game.dialogue.FacialExpression.FRIENDLY, "Hi.").also { stage = 20 } + player(FacialExpression.FRIENDLY, "Hi.").also { stage = 20 } } return true @@ -31,38 +31,38 @@ class CabinBoyDialogue(player: Player? = null) : core.game.dialogue.DialoguePlug override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 0 -> npc(core.game.dialogue.FacialExpression.CHILD_FRIENDLY, "That I did, that I did!").also { stage++ } - 1 -> player(core.game.dialogue.FacialExpression.ASKING, "And?").also { stage++ } - 2 -> npcl(core.game.dialogue.FacialExpression.CHILD_FRIENDLY, "She turned out to be really nice! She's joining us to become a pirate!").also { stage++ } - 3 -> playerl(core.game.dialogue.FacialExpression.HALF_WORRIED, "Really? And you're not sceptical about this? You know, after what she did last time?").also { stage++ } - 4 -> npc(core.game.dialogue.FacialExpression.CHILD_SUSPICIOUS, "She---has---no---spell---on---me.").also { stage++ } - 5 -> player(core.game.dialogue.FacialExpression.HALF_THINKING, "Huh? Why are you talking like that?").also { stage++ } - 6 -> npc(core.game.dialogue.FacialExpression.CHILD_THINKING, "Like what?").also { stage++ } - 7 -> playerl(core.game.dialogue.FacialExpression.HALF_ASKING, "Like someone else was talking for you. Are you sure she hasn't put a spell on you again? Did she wave a watch in front of your face? You know, tell you to look deep into her eyes?").also { stage++ } - 8 -> npc(core.game.dialogue.FacialExpression.CHILD_SUSPICIOUS, "She---has---no---spell---on---me.").also { stage++ } - 9 -> playerl(core.game.dialogue.FacialExpression.HALF_THINKING, "I think you've been hypnotised. I wonder what happens if I click my fingers?").also { + 0 -> npc(FacialExpression.CHILD_FRIENDLY, "That I did, that I did!").also { stage++ } + 1 -> player(FacialExpression.ASKING, "And?").also { stage++ } + 2 -> npcl(FacialExpression.CHILD_FRIENDLY, "She turned out to be really nice! She's joining us to become a pirate!").also { stage++ } + 3 -> playerl(FacialExpression.HALF_WORRIED, "Really? And you're not sceptical about this? You know, after what she did last time?").also { stage++ } + 4 -> npc(FacialExpression.CHILD_SUSPICIOUS, "She---has---no---spell---on---me.").also { stage++ } + 5 -> player(FacialExpression.HALF_THINKING, "Huh? Why are you talking like that?").also { stage++ } + 6 -> npc(FacialExpression.CHILD_THINKING, "Like what?").also { stage++ } + 7 -> playerl(FacialExpression.HALF_ASKING, "Like someone else was talking for you. Are you sure she hasn't put a spell on you again? Did she wave a watch in front of your face? You know, tell you to look deep into her eyes?").also { stage++ } + 8 -> npc(FacialExpression.CHILD_SUSPICIOUS, "She---has---no---spell---on---me.").also { stage++ } + 9 -> playerl(FacialExpression.HALF_THINKING, "I think you've been hypnotised. I wonder what happens if I click my fingers?").also { player.sendChat("*click*").also { stage++ } } - 10 -> npc(core.game.dialogue.FacialExpression.CHILD_SAD, "*Cluck* *cluck* *bwaarrk*").also { stage++ } - 11 -> npc(core.game.dialogue.FacialExpression.CHILD_SAD, "").also { stage++ } - 12 -> player(core.game.dialogue.FacialExpression.WORRIED, "Oh dear. Oh well, I'm sure you'll learn one day.").also { stage = 99 } + 10 -> npc(FacialExpression.CHILD_SAD, "*Cluck* *cluck* *bwaarrk*").also { stage++ } + 11 -> npc(FacialExpression.CHILD_SAD, "").also { stage++ } + 12 -> player(FacialExpression.WORRIED, "Oh dear. Oh well, I'm sure you'll learn one day.").also { stage = 99 } - 20 -> npc(core.game.dialogue.FacialExpression.CHILD_FRIENDLY, "I bet you're after another lens!").also { stage++ } - 21 -> player(core.game.dialogue.FacialExpression.HALF_THINKING, "How could you possibly know that?").also { stage++ } - 22 -> npcl(core.game.dialogue.FacialExpression.CHILD_FRIENDLY, "Hey, I think I'm learning a thing or two from these Moon Clan ladies.").also { stage++ } + 20 -> npc(FacialExpression.CHILD_FRIENDLY, "I bet you're after another lens!").also { stage++ } + 21 -> player(FacialExpression.HALF_THINKING, "How could you possibly know that?").also { stage++ } + 22 -> npcl(FacialExpression.CHILD_FRIENDLY, "Hey, I think I'm learning a thing or two from these Moon Clan ladies.").also { stage++ } 23 -> options("Please", "No thanks").also { stage++ } 24 -> when (buttonId) { - 1 -> player(core.game.dialogue.FacialExpression.HALF_ASKING, "Oi! I need another lens, boy.").also { stage = 25} - 2 -> player(core.game.dialogue.FacialExpression.SUSPICIOUS, "I think I'll pass. But thanks..").also { stage = 99 } + 1 -> player(FacialExpression.HALF_ASKING, "Oi! I need another lens, boy.").also { stage = 25} + 2 -> player(FacialExpression.SUSPICIOUS, "I think I'll pass. But thanks..").also { stage = 99 } } - 25 -> npc(core.game.dialogue.FacialExpression.CHILD_FRIENDLY, "Huh, oh ok, I suppose I owe you one.").also { addItemOrDrop(player, Items.EMERALD_LENS_9066, 1) }.also { stage = 99 } + 25 -> npc(FacialExpression.CHILD_FRIENDLY, "Huh, oh ok, I suppose I owe you one.").also { addItemOrDrop(player, Items.EMERALD_LENS_9066, 1) }.also { stage = 99 } 99 -> end() } return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return CabinBoyDialogue(player) } diff --git a/Server/src/main/content/region/fremennik/lunarisle/dialogue/MelenaMoonlanderDialogue.kt b/Server/src/main/content/region/fremennik/lunarisle/dialogue/MelenaMoonlanderDialogue.kt index 743139924..4992dbf2e 100644 --- a/Server/src/main/content/region/fremennik/lunarisle/dialogue/MelenaMoonlanderDialogue.kt +++ b/Server/src/main/content/region/fremennik/lunarisle/dialogue/MelenaMoonlanderDialogue.kt @@ -12,49 +12,49 @@ import org.rs09.consts.NPCs */ @Initializable -class MelenaMoonlanderDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class MelenaMoonlanderDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - player(core.game.dialogue.FacialExpression.FRIENDLY,"Hello there.").also { stage = 0 } + player(FacialExpression.FRIENDLY,"Hello there.").also { stage = 0 } return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 0 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Hi. Welcome to the general store. How might I help you?").also { stage++ } + 0 -> npcl(FacialExpression.FRIENDLY, "Hi. Welcome to the general store. How might I help you?").also { stage++ } 1 -> options("What can you sell me?", "I have a question...", "I'm good thanks, bye.").also { stage++ } 2 -> when (buttonId) { 1 -> end().also { npc.openShop(player) } - 2 -> player(core.game.dialogue.FacialExpression.HALF_ASKING, "I have a question...").also { stage = 11 } - 3 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "I'm good thanks, bye.").also { stage = 99 } + 2 -> player(FacialExpression.HALF_ASKING, "I have a question...").also { stage = 11 } + 3 -> player(FacialExpression.FRIENDLY, "I'm good thanks, bye.").also { stage = 99 } } - 11 -> npc(core.game.dialogue.FacialExpression.FRIENDLY, "About magic of course.").also { stage++ } - 12 -> player(core.game.dialogue.FacialExpression.SUSPICIOUS, "Sorry?").also { stage++ } - 13 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "I said about magic of course. You know, in response to your question.").also { stage++ } - 14 -> player(core.game.dialogue.FacialExpression.HALF_THINKING, "But I didn't ask anything yet.").also { stage++ } - 15 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Yes, but you were thinking of asking me how I was floating.").also { stage++ } - 16 -> player(core.game.dialogue.FacialExpression.AMAZED, "That's true! How could you possibly know that?").also { stage++ } - 17 -> npc(core.game.dialogue.FacialExpression.HALF_THINKING, "Don't you realise we can read your mind.").also { stage++ } - 18 -> player(core.game.dialogue.FacialExpression.SUSPICIOUS, "Oh, of course. How do you manage to do that?").also { stage++ } - 19 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "It's quite simple, everyone has a resonance that is responded to by the moon. This resonance changes depending on what we are thinking. You can tune yourself in to listen to this").also { stage++ } - 20 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "resonance with practice - it's a life long quest for the members of the Moon Clan, but its especially easy to read with outsiders like yourself,").also { stage++ } - 21 -> npc(core.game.dialogue.FacialExpression.FRIENDLY, "as you are far louder and unguarded.").also { stage++ } - 22 -> player(core.game.dialogue.FacialExpression.SUSPICIOUS, "I see. I best be careful what I think of then.").also { stage++ } - 23 -> player(core.game.dialogue.FacialExpression.WORRIED, "...").also { stage++ } - 24 -> player(core.game.dialogue.FacialExpression.THINKING, "...").also { stage++ } - 25 -> npc(core.game.dialogue.FacialExpression.DISGUSTED_HEAD_SHAKE, "That's disgusting!").also { stage++ } - 26 -> player(core.game.dialogue.FacialExpression.SAD, "Sorry.").also { stage = 99 } + 11 -> npc(FacialExpression.FRIENDLY, "About magic of course.").also { stage++ } + 12 -> player(FacialExpression.SUSPICIOUS, "Sorry?").also { stage++ } + 13 -> npcl(FacialExpression.FRIENDLY, "I said about magic of course. You know, in response to your question.").also { stage++ } + 14 -> player(FacialExpression.HALF_THINKING, "But I didn't ask anything yet.").also { stage++ } + 15 -> npcl(FacialExpression.FRIENDLY, "Yes, but you were thinking of asking me how I was floating.").also { stage++ } + 16 -> player(FacialExpression.AMAZED, "That's true! How could you possibly know that?").also { stage++ } + 17 -> npc(FacialExpression.HALF_THINKING, "Don't you realise we can read your mind.").also { stage++ } + 18 -> player(FacialExpression.SUSPICIOUS, "Oh, of course. How do you manage to do that?").also { stage++ } + 19 -> npcl(FacialExpression.FRIENDLY, "It's quite simple, everyone has a resonance that is responded to by the moon. This resonance changes depending on what we are thinking. You can tune yourself in to listen to this").also { stage++ } + 20 -> npcl(FacialExpression.FRIENDLY, "resonance with practice - it's a life long quest for the members of the Moon Clan, but its especially easy to read with outsiders like yourself,").also { stage++ } + 21 -> npc(FacialExpression.FRIENDLY, "as you are far louder and unguarded.").also { stage++ } + 22 -> player(FacialExpression.SUSPICIOUS, "I see. I best be careful what I think of then.").also { stage++ } + 23 -> player(FacialExpression.WORRIED, "...").also { stage++ } + 24 -> player(FacialExpression.THINKING, "...").also { stage++ } + 25 -> npc(FacialExpression.DISGUSTED_HEAD_SHAKE, "That's disgusting!").also { stage++ } + 26 -> player(FacialExpression.SAD, "Sorry.").also { stage = 99 } 99 -> end() } return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return MelenaMoonlanderDialogue(player) } diff --git a/Server/src/main/content/region/fremennik/lunarisle/dialogue/PaulinePolarisDialogue.kt b/Server/src/main/content/region/fremennik/lunarisle/dialogue/PaulinePolarisDialogue.kt index 835f25d01..a0ede3811 100644 --- a/Server/src/main/content/region/fremennik/lunarisle/dialogue/PaulinePolarisDialogue.kt +++ b/Server/src/main/content/region/fremennik/lunarisle/dialogue/PaulinePolarisDialogue.kt @@ -13,32 +13,32 @@ import org.rs09.consts.NPCs */ @Initializable -class PaulinePolarisDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class PaulinePolarisDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - player(core.game.dialogue.FacialExpression.FRIENDLY,"Hello there.").also { stage = 0 } + player(FacialExpression.FRIENDLY,"Hello there.").also { stage = 0 } return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 0 -> npc(core.game.dialogue.FacialExpression.ASKING, "Ah, a stranger to our island. How can I help?").also { stage ++ } - 1 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY, "Well, I've actually come here on a diplomatic mission. I want to try and settle some of the disputes between the Fremenniks and your clan.").also { stage++ } - 2 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "The Rremenniks? Pah! They are just too ignorant and stubborn to listen to anything we have to say - how can we possibly associate with a race that won't listen?").also { stage++ } - 3 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY, "I think they are very eager to listen, but they feel like you are keeping secrets from them.").also { stage++ } - 4 -> npc(core.game.dialogue.FacialExpression.HALF_THINKING, "Secrets?").also { stage++ } - 5 -> playerl(core.game.dialogue.FacialExpression.HALF_ASKING, "Yes, as in your magic ways. To be honest, I think it is fear of the unknown.").also { stage++ } - 6 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Well, when the day comes that a Fremennik can prove they have the patience and interest in learning our ways, then we will perhaps share our secrets.").also { stage++ } - 7 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "I'm hoping I might be able to achieve that.").also { stage++ } - 8 -> npc(core.game.dialogue.FacialExpression.FRIENDLY, "Good luck. It's far from simple.").also { stage = 99 } + 0 -> npc(FacialExpression.ASKING, "Ah, a stranger to our island. How can I help?").also { stage ++ } + 1 -> playerl(FacialExpression.FRIENDLY, "Well, I've actually come here on a diplomatic mission. I want to try and settle some of the disputes between the Fremenniks and your clan.").also { stage++ } + 2 -> npcl(FacialExpression.FRIENDLY, "The Rremenniks? Pah! They are just too ignorant and stubborn to listen to anything we have to say - how can we possibly associate with a race that won't listen?").also { stage++ } + 3 -> playerl(FacialExpression.FRIENDLY, "I think they are very eager to listen, but they feel like you are keeping secrets from them.").also { stage++ } + 4 -> npc(FacialExpression.HALF_THINKING, "Secrets?").also { stage++ } + 5 -> playerl(FacialExpression.HALF_ASKING, "Yes, as in your magic ways. To be honest, I think it is fear of the unknown.").also { stage++ } + 6 -> npcl(FacialExpression.FRIENDLY, "Well, when the day comes that a Fremennik can prove they have the patience and interest in learning our ways, then we will perhaps share our secrets.").also { stage++ } + 7 -> player(FacialExpression.FRIENDLY, "I'm hoping I might be able to achieve that.").also { stage++ } + 8 -> npc(FacialExpression.FRIENDLY, "Good luck. It's far from simple.").also { stage = 99 } 99 -> end() } return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return PaulinePolarisDialogue(player) } diff --git a/Server/src/main/content/region/fremennik/lunarisle/dialogue/RimaeSirsalisDialogue.kt b/Server/src/main/content/region/fremennik/lunarisle/dialogue/RimaeSirsalisDialogue.kt index 7c37ba63c..6046a7931 100644 --- a/Server/src/main/content/region/fremennik/lunarisle/dialogue/RimaeSirsalisDialogue.kt +++ b/Server/src/main/content/region/fremennik/lunarisle/dialogue/RimaeSirsalisDialogue.kt @@ -12,11 +12,11 @@ import org.rs09.consts.NPCs */ @Initializable -class RimaeSirsalisDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class RimaeSirsalisDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - player(core.game.dialogue.FacialExpression.FRIENDLY,"Hello there.").also { stage = 0 } + player(FacialExpression.FRIENDLY,"Hello there.").also { stage = 0 } return true } @@ -26,33 +26,33 @@ class RimaeSirsalisDialogue(player: Player? = null) : core.game.dialogue.Dialogu 1 -> when (buttonId) { 1 -> end().also { npc.openShop(player) } - 2 -> player(core.game.dialogue.FacialExpression.HALF_ASKING, "Do you have any use for suqah teeth or hides?").also { stage = 5 } - 3 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Why thank you. It's been our haven for a great many generations.").also { stage = 10 } - 4 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "I'm good thanks, bye.").also { stage = 99 } + 2 -> player(FacialExpression.HALF_ASKING, "Do you have any use for suqah teeth or hides?").also { stage = 5 } + 3 -> npcl(FacialExpression.FRIENDLY, "Why thank you. It's been our haven for a great many generations.").also { stage = 10 } + 4 -> player(FacialExpression.FRIENDLY, "I'm good thanks, bye.").also { stage = 99 } } - 5 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Most certainly! The hides are great for making clothes and the teeth are particularly useful for broaches, necklaces and the like. But I won't accept them from anyone.").also { stage = 99 } + 5 -> npcl(FacialExpression.FRIENDLY, "Most certainly! The hides are great for making clothes and the teeth are particularly useful for broaches, necklaces and the like. But I won't accept them from anyone.").also { stage = 99 } - 10 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY, "Everything seems to have a magical feel to it.").also { stage++ } - 11 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Of course. We integrate magic into all areas of our lives. It is a part of everyone, so why deny it? It's best to make the most of this innate gift we have all been given.").also { stage++ } - 12 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "What sort of things do you use your magic for?").also { stage++ } - 13 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Take a look around. We use it in our day to day lives, from making a cup of tea to travelling around the island.").also { stage++ } - 14 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "You see our ancestors were the ones that found the first rune essence and put it to use! The various factions were eventually created,").also { stage++ } - 15 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "with people having different ideas on how the essence should be used (or not in some cases!)").also { stage++ } - 16 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "What has kept you so secluded on this island?").also { stage++ } - 17 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "It may be a bit beyond you, but although magic comes from within, we are all strongly linked to the moon and the").also { stage++ } - 18 -> npc(core.game.dialogue.FacialExpression.FRIENDLY, "effects it has on us cannot be denied!").also { stage++ } - 19 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "It can't?").also { stage++ } - 20 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Of course not. This very island has a great link to our moon, which helps us understand ourselves - especially our dreams,").also { stage++ } - 21 -> npc(core.game.dialogue.FacialExpression.FRIENDLY, "which is the path to understanding magic.").also { stage++ } - 22 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY, "I think I will just have to take your word on that.").also { stage = 99 } + 10 -> playerl(FacialExpression.FRIENDLY, "Everything seems to have a magical feel to it.").also { stage++ } + 11 -> npcl(FacialExpression.FRIENDLY, "Of course. We integrate magic into all areas of our lives. It is a part of everyone, so why deny it? It's best to make the most of this innate gift we have all been given.").also { stage++ } + 12 -> player(FacialExpression.FRIENDLY, "What sort of things do you use your magic for?").also { stage++ } + 13 -> npcl(FacialExpression.FRIENDLY, "Take a look around. We use it in our day to day lives, from making a cup of tea to travelling around the island.").also { stage++ } + 14 -> npcl(FacialExpression.FRIENDLY, "You see our ancestors were the ones that found the first rune essence and put it to use! The various factions were eventually created,").also { stage++ } + 15 -> npcl(FacialExpression.FRIENDLY, "with people having different ideas on how the essence should be used (or not in some cases!)").also { stage++ } + 16 -> player(FacialExpression.FRIENDLY, "What has kept you so secluded on this island?").also { stage++ } + 17 -> npcl(FacialExpression.FRIENDLY, "It may be a bit beyond you, but although magic comes from within, we are all strongly linked to the moon and the").also { stage++ } + 18 -> npc(FacialExpression.FRIENDLY, "effects it has on us cannot be denied!").also { stage++ } + 19 -> player(FacialExpression.FRIENDLY, "It can't?").also { stage++ } + 20 -> npcl(FacialExpression.FRIENDLY, "Of course not. This very island has a great link to our moon, which helps us understand ourselves - especially our dreams,").also { stage++ } + 21 -> npc(FacialExpression.FRIENDLY, "which is the path to understanding magic.").also { stage++ } + 22 -> playerl(FacialExpression.FRIENDLY, "I think I will just have to take your word on that.").also { stage = 99 } 99 -> end() } return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return RimaeSirsalisDialogue(player) } diff --git a/Server/src/main/content/region/fremennik/lunarisle/dialogue/SeleneDialog.kt b/Server/src/main/content/region/fremennik/lunarisle/dialogue/SeleneDialog.kt index df71877b0..8e0f00f75 100644 --- a/Server/src/main/content/region/fremennik/lunarisle/dialogue/SeleneDialog.kt +++ b/Server/src/main/content/region/fremennik/lunarisle/dialogue/SeleneDialog.kt @@ -18,7 +18,7 @@ import java.util.concurrent.TimeUnit */ @Initializable -class SeleneDialog(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class SeleneDialog(player: Player? = null) : DialoguePlugin(player){ private val Rellekka = Location.create(2663, 3644, 0) private var teled = false @@ -28,9 +28,9 @@ class SeleneDialog(player: Player? = null) : core.game.dialogue.DialoguePlugin(p if (!teled) { if (player.inventory.contains(Items.SEAL_OF_PASSAGE_9083, 1) || player.equipment.contains(Items.SEAL_OF_PASSAGE_9083, 1)) { - player(core.game.dialogue.FacialExpression.FRIENDLY, "Can you tell me a bit about your people?").also { stage = 0; } + player(FacialExpression.FRIENDLY, "Can you tell me a bit about your people?").also { stage = 0; } } else { - player(core.game.dialogue.FacialExpression.FRIENDLY, "Hi, I...").also { stage = 10 } + player(FacialExpression.FRIENDLY, "Hi, I...").also { stage = 10 } } } return true @@ -38,20 +38,20 @@ class SeleneDialog(player: Player? = null) : core.game.dialogue.DialoguePlugin(p override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 0 -> npc(core.game.dialogue.FacialExpression.FRIENDLY,"Ok. Like what?").also { stage++ } - 1 -> player(core.game.dialogue.FacialExpression.HALF_ASKING, "How about the values of the Moon clan!").also { stage++ } - 2 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Let me see... We value knowledge of self because it is this that gives us our strength! It is most important!").also { stage++ } - 3 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY, "Well... I know things about myself. I know I like hot chocolate!").also { stage++ } - 4 -> npcl(core.game.dialogue.FacialExpression.LAUGH, "I was meaning something a little deeper than that. We also like to see someone listen. You know how they say a wise man listens?").also { stage++ } - 5 -> player(core.game.dialogue.FacialExpression.HALF_WORRIED, "....").also { stage++ } - 6 -> npc(core.game.dialogue.FacialExpression.HALF_WORRIED, "Did you hear me?").also { stage++ } - 7 -> player(core.game.dialogue.FacialExpression.HALF_WORRIED, ".... I'm listening.").also { stage++ } - 8 -> npc(core.game.dialogue.FacialExpression.HALF_WORRIED, "Most wise.").also { stage++ } - 9 -> player(core.game.dialogue.FacialExpression.HALF_WORRIED, "Huh?").also { stage = 99 } + 0 -> npc(FacialExpression.FRIENDLY,"Ok. Like what?").also { stage++ } + 1 -> player(FacialExpression.HALF_ASKING, "How about the values of the Moon clan!").also { stage++ } + 2 -> npcl(FacialExpression.NEUTRAL, "Let me see... We value knowledge of self because it is this that gives us our strength! It is most important!").also { stage++ } + 3 -> playerl(FacialExpression.FRIENDLY, "Well... I know things about myself. I know I like hot chocolate!").also { stage++ } + 4 -> npcl(FacialExpression.LAUGH, "I was meaning something a little deeper than that. We also like to see someone listen. You know how they say a wise man listens?").also { stage++ } + 5 -> player(FacialExpression.HALF_WORRIED, "....").also { stage++ } + 6 -> npc(FacialExpression.HALF_WORRIED, "Did you hear me?").also { stage++ } + 7 -> player(FacialExpression.HALF_WORRIED, ".... I'm listening.").also { stage++ } + 8 -> npc(FacialExpression.HALF_WORRIED, "Most wise.").also { stage++ } + 9 -> player(FacialExpression.HALF_WORRIED, "Huh?").also { stage = 99 } - 10 -> npc(core.game.dialogue.FacialExpression.ANNOYED, "What are you doing here, Fremennik?!").also { stage++ } - 11 -> player(core.game.dialogue.FacialExpression.WORRIED, "I have a seal of pass...").also { stage++ } - 12 -> npc(core.game.dialogue.FacialExpression.ANNOYED, "No you do not! Begone!").also { stage++ } + 10 -> npc(FacialExpression.ANNOYED, "What are you doing here, Fremennik?!").also { stage++ } + 11 -> player(FacialExpression.WORRIED, "I have a seal of pass...").also { stage++ } + 12 -> npc(FacialExpression.ANNOYED, "No you do not! Begone!").also { stage++ } 13 -> teleport(player, Rellekka, TeleportManager.TeleportType.LUNAR).also { wait1() } 99 -> end() @@ -62,14 +62,14 @@ class SeleneDialog(player: Player? = null) : core.game.dialogue.DialoguePlugin(p private fun wait1() { Executors.newSingleThreadScheduledExecutor().schedule({ if (player.location.isInRegion(Rellekka.regionId)) { - playerl(core.game.dialogue.FacialExpression.WORRIED, "Ooops. Suppose I need a seal of passage when I'm walking around that island.").also { end() } + playerl(FacialExpression.WORRIED, "Ooops. Suppose I need a seal of passage when I'm walking around that island.").also { end() } } else { wait1() } }, 1, TimeUnit.SECONDS) } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return SeleneDialog(player) } diff --git a/Server/src/main/content/region/fremennik/lunarisle/dialogue/SirsalBankerDialogue.kt b/Server/src/main/content/region/fremennik/lunarisle/dialogue/SirsalBankerDialogue.kt index 7e90bf9e9..ebe225e11 100644 --- a/Server/src/main/content/region/fremennik/lunarisle/dialogue/SirsalBankerDialogue.kt +++ b/Server/src/main/content/region/fremennik/lunarisle/dialogue/SirsalBankerDialogue.kt @@ -18,20 +18,20 @@ import core.tools.START_DIALOGUE * @author vddCore */ @Initializable -class SirsalBankerDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { +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( - core.game.dialogue.FacialExpression.NEUTRAL, + 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( - core.game.dialogue.FacialExpression.NEUTRAL, + FacialExpression.NEUTRAL, "Good day, how may I help you?" ).also { if (hasAwaitingGrandExchangeCollections(player)) { @@ -42,47 +42,47 @@ class SirsalBankerDialogue(player: Player? = null) : core.game.dialogue.Dialogue } } } else { - playerl(core.game.dialogue.FacialExpression.HALF_WORRIED, "Hi, I...") + playerl(FacialExpression.HALF_WORRIED, "Hi, I...") stage = 30 } 1 -> npcl( - core.game.dialogue.FacialExpression.NEUTRAL, + 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(core.game.dialogue.FacialExpression.NEUTRAL, "I'd like to access my bank account, please.", 10), + Topic(FacialExpression.NEUTRAL, "I'd like to access my bank account, please.", 10), IfTopic( - core.game.dialogue.FacialExpression.NEUTRAL, + FacialExpression.NEUTRAL, "I'd like to switch to my ${getBankAccountName(player, true)} bank account.", 13, hasActivatedSecondaryBankAccount(player) ), IfTopic( - core.game.dialogue.FacialExpression.NEUTRAL, + FacialExpression.NEUTRAL, "I'd like to open a secondary bank account.", 20, !hasActivatedSecondaryBankAccount(player) ), - Topic(core.game.dialogue.FacialExpression.NEUTRAL, "I'd like to check my PIN settings.", 11), - Topic(core.game.dialogue.FacialExpression.NEUTRAL, "I'd like to collect items.", 12), - Topic(core.game.dialogue.FacialExpression.ASKING, "What is this place?", 3), + 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( - core.game.dialogue.FacialExpression.NEUTRAL, + FacialExpression.NEUTRAL, "This is a branch of the Bank of Gielinor. We have branches in many towns." ).also { stage++ } 4 -> playerl( - core.game.dialogue.FacialExpression.ASKING, + FacialExpression.ASKING, "And what do you do?" ).also { stage++ } 5 -> npcl( - core.game.dialogue.FacialExpression.NEUTRAL, + 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 } @@ -106,51 +106,51 @@ class SirsalBankerDialogue(player: Player? = null) : core.game.dialogue.Dialogue toggleBankAccount(player) npcl( - core.game.dialogue.FacialExpression.NEUTRAL, + FacialExpression.NEUTRAL, "Your active bank account has been switched. " + "You can now access your ${getBankAccountName(player)} account." ).also { stage = END_DIALOGUE } } 20 -> npcl( - core.game.dialogue.FacialExpression.NEUTRAL, + FacialExpression.NEUTRAL, "Certainly. We offer secondary accounts to all our customers." ).also { stage++ } 21 -> npcl( - core.game.dialogue.FacialExpression.NEUTRAL, + 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( - core.game.dialogue.FacialExpression.NEUTRAL, + 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( - core.game.dialogue.FacialExpression.ASKING, + 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.NEUTRAL, "Yes, I am still interested.", 25), - Topic(core.game.dialogue.FacialExpression.NEUTRAL, "Actually, I've changed my mind.", 26) + 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( - core.game.dialogue.FacialExpression.NEUTRAL, + FacialExpression.NEUTRAL, "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.NEUTRAL, + 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." @@ -159,7 +159,7 @@ class SirsalBankerDialogue(player: Player? = null) : core.game.dialogue.Dialogue SecondaryBankAccountActivationResult.NOT_ENOUGH_MONEY -> { npcl( - core.game.dialogue.FacialExpression.NEUTRAL, + 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." @@ -168,7 +168,7 @@ class SirsalBankerDialogue(player: Player? = null) : core.game.dialogue.Dialogue SecondaryBankAccountActivationResult.SUCCESS -> { npcl( - core.game.dialogue.FacialExpression.NEUTRAL, + 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 } @@ -177,23 +177,23 @@ class SirsalBankerDialogue(player: Player? = null) : core.game.dialogue.Dialogue } 26 -> npcl( - core.game.dialogue.FacialExpression.NEUTRAL, + 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( - core.game.dialogue.FacialExpression.ANNOYED, + FacialExpression.ANNOYED, "What are you doing here, Fremennik?!" ).also { stage++ } 31 -> playerl( - core.game.dialogue.FacialExpression.WORRIED, + FacialExpression.WORRIED, "I have a Seal of Pass..." ).also { stage++ } 32 -> npcl( - core.game.dialogue.FacialExpression.ANGRY, + FacialExpression.ANGRY, "No you don't! Begone!" ).also { stage = END_DIALOGUE } diff --git a/Server/src/main/content/region/fremennik/neitiznot/dialogue/FridleifShieldsonDialogue.kt b/Server/src/main/content/region/fremennik/neitiznot/dialogue/FridleifShieldsonDialogue.kt index 06f0edfa2..c94e5c096 100644 --- a/Server/src/main/content/region/fremennik/neitiznot/dialogue/FridleifShieldsonDialogue.kt +++ b/Server/src/main/content/region/fremennik/neitiznot/dialogue/FridleifShieldsonDialogue.kt @@ -4,23 +4,23 @@ 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 import core.tools.END_DIALOGUE +import org.rs09.consts.NPCs @Initializable -class FridleifShieldsonDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { +class FridleifShieldsonDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player?): DialoguePlugin { return FridleifShieldsonDialogue(player) } override fun open(vararg args: Any?): Boolean { - npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Greetings!") + npcl(FacialExpression.FRIENDLY, "Greetings!") return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 0 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY, "Hello!").also { stage = END_DIALOGUE } + 0 -> playerl(FacialExpression.FRIENDLY, "Hello!").also { stage = END_DIALOGUE } } return true } diff --git a/Server/src/main/content/region/fremennik/neitiznot/dialogue/GunnarHoldstromDialogue.kt b/Server/src/main/content/region/fremennik/neitiznot/dialogue/GunnarHoldstromDialogue.kt index c1077a15b..5a2e9f1c2 100644 --- a/Server/src/main/content/region/fremennik/neitiznot/dialogue/GunnarHoldstromDialogue.kt +++ b/Server/src/main/content/region/fremennik/neitiznot/dialogue/GunnarHoldstromDialogue.kt @@ -8,21 +8,21 @@ import org.rs09.consts.NPCs import core.tools.END_DIALOGUE @Initializable -class GunnarHoldstromDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { +class GunnarHoldstromDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player?): DialoguePlugin { return GunnarHoldstromDialogue(player) } override fun open(vararg args: Any?): Boolean { - npcl(core.game.dialogue.FacialExpression.HAPPY, "Ah, isn't it a lovely day?") + npcl(FacialExpression.HAPPY, "Ah, isn't it a lovely day?") return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 0 -> playerl(core.game.dialogue.FacialExpression.ASKING, "It's not bad. What puts you in such a good mood?").also { stage++ } - 1 -> npcl(core.game.dialogue.FacialExpression.HAPPY, "Oh, I just love my job. The smell of the sea breeze, the smell of the arctic pine sap, the smell of the yaks. I find it all so bracing.").also { stage++ } - 2 -> playerl(core.game.dialogue.FacialExpression.ASKING, "Bracing? Hmmm. I think I might have chosen a different word.").also { stage = END_DIALOGUE } + 0 -> playerl(FacialExpression.ASKING, "It's not bad. What puts you in such a good mood?").also { stage++ } + 1 -> npcl(FacialExpression.HAPPY, "Oh, I just love my job. The smell of the sea breeze, the smell of the arctic pine sap, the smell of the yaks. I find it all so bracing.").also { stage++ } + 2 -> playerl(FacialExpression.ASKING, "Bracing? Hmmm. I think I might have chosen a different word.").also { stage = END_DIALOGUE } } return true } diff --git a/Server/src/main/content/region/fremennik/neitiznot/dialogue/JofridrMordstatterDialogue.kt b/Server/src/main/content/region/fremennik/neitiznot/dialogue/JofridrMordstatterDialogue.kt index 895adbebe..3188fd345 100644 --- a/Server/src/main/content/region/fremennik/neitiznot/dialogue/JofridrMordstatterDialogue.kt +++ b/Server/src/main/content/region/fremennik/neitiznot/dialogue/JofridrMordstatterDialogue.kt @@ -9,14 +9,14 @@ import org.rs09.consts.NPCs import core.tools.END_DIALOGUE @Initializable -class JofridrMordstatterDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { +class JofridrMordstatterDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player?): DialoguePlugin { return JofridrMordstatterDialogue(player) } override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Hello there. Would you like to see the goods I have for sale?") + npcl(FacialExpression.NEUTRAL, "Hello there. Would you like to see the goods I have for sale?") return true } @@ -24,18 +24,18 @@ class JofridrMordstatterDialogue(player: Player? = null) : core.game.dialogue.Di when (stage) { 0 -> options("Yes please, Jofridr.", "No thank you, Jofridr.", "Why do you have so much wool in your store?").also { stage++ } 1 -> when (buttonId) { - 1 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL, "Yes please, Jofridr.").also { stage = END_DIALOGUE }.also { npc.openShop(player) } - 2 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL, "No thank you, Jofridr.").also { stage = 5 } - 3 -> playerl(core.game.dialogue.FacialExpression.THINKING, "Why do you have so much wool in your store? I haven't seen any sheep anywhere.").also { stage = 11} + 1 -> playerl(FacialExpression.NEUTRAL, "Yes please, Jofridr.").also { stage = END_DIALOGUE }.also { npc.openShop(player) } + 2 -> playerl(FacialExpression.NEUTRAL, "No thank you, Jofridr.").also { stage = 5 } + 3 -> playerl(FacialExpression.THINKING, "Why do you have so much wool in your store? I haven't seen any sheep anywhere.").also { stage = 11} } - 5 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL,"Fair thee well.").also { stage = END_DIALOGUE } + 5 -> npcl(FacialExpression.NEUTRAL,"Fair thee well.").also { stage = END_DIALOGUE } - 11 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY,"Ah, I have contacts on the mainland. I have a sailor friend who brings me crates of wool on a regular basis.").also { stage++ } - 12 -> playerl(core.game.dialogue.FacialExpression.ASKING,"What do you trade for it?").also { stage++ } - 13 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY,"Rope of course! What else can we sell? Fish would go off before it got so far south.").also { stage++ } - 14 -> playerl(core.game.dialogue.FacialExpression.ASKING,"Where does all this rope go?").also { stage++ } - 15 -> npcl(core.game.dialogue.FacialExpression.THINKING,"Err, I don't remember the name of the place very well. Dreinna? Drennor? Something like that.").also { stage++ } - 16 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL,"That's very interesting. Thanks Jofridr.").also { stage = END_DIALOGUE } + 11 -> npcl(FacialExpression.FRIENDLY,"Ah, I have contacts on the mainland. I have a sailor friend who brings me crates of wool on a regular basis.").also { stage++ } + 12 -> playerl(FacialExpression.ASKING,"What do you trade for it?").also { stage++ } + 13 -> npcl(FacialExpression.FRIENDLY,"Rope of course! What else can we sell? Fish would go off before it got so far south.").also { stage++ } + 14 -> playerl(FacialExpression.ASKING,"Where does all this rope go?").also { stage++ } + 15 -> npcl(FacialExpression.THINKING,"Err, I don't remember the name of the place very well. Dreinna? Drennor? Something like that.").also { stage++ } + 16 -> playerl(FacialExpression.NEUTRAL,"That's very interesting. Thanks Jofridr.").also { stage = END_DIALOGUE } } return true } diff --git a/Server/src/main/content/region/fremennik/neitiznot/dialogue/KjedeligUppsenDialogue.kt b/Server/src/main/content/region/fremennik/neitiznot/dialogue/KjedeligUppsenDialogue.kt index b78ca7b3a..e0e0b6416 100644 --- a/Server/src/main/content/region/fremennik/neitiznot/dialogue/KjedeligUppsenDialogue.kt +++ b/Server/src/main/content/region/fremennik/neitiznot/dialogue/KjedeligUppsenDialogue.kt @@ -4,23 +4,23 @@ 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 import core.tools.END_DIALOGUE +import org.rs09.consts.NPCs @Initializable -class KjedeligUppsenDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { +class KjedeligUppsenDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player?): DialoguePlugin { return KjedeligUppsenDialogue(player) } override fun open(vararg args: Any?): Boolean { - npcl(core.game.dialogue.FacialExpression.NEUTRAL, "I'm guarding the king, I cannot speak.") + npcl(FacialExpression.NEUTRAL, "I'm guarding the king, I cannot speak.") return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 0 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL, "Sorry.").also { stage = END_DIALOGUE } + 0 -> playerl(FacialExpression.NEUTRAL, "Sorry.").also { stage = END_DIALOGUE } } return true } diff --git a/Server/src/main/content/region/fremennik/neitiznot/dialogue/LisseIsaaksonDialogue.kt b/Server/src/main/content/region/fremennik/neitiznot/dialogue/LisseIsaaksonDialogue.kt index bffff896f..b99892d90 100644 --- a/Server/src/main/content/region/fremennik/neitiznot/dialogue/LisseIsaaksonDialogue.kt +++ b/Server/src/main/content/region/fremennik/neitiznot/dialogue/LisseIsaaksonDialogue.kt @@ -8,25 +8,25 @@ import org.rs09.consts.NPCs import core.tools.END_DIALOGUE @Initializable -class LisseIsaaksonDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { +class LisseIsaaksonDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player?): DialoguePlugin { return LisseIsaaksonDialogue(player) } override fun open(vararg args: Any?): Boolean { - npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Hello, visitor!") + npcl(FacialExpression.FRIENDLY, "Hello, visitor!") return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 0 -> playerl(core.game.dialogue.FacialExpression.ASKING, "Hello. What are you up to?").also { stage++ } - 1 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Ah, I was about to collect some yak's milk to make yak cheese.").also { stage++ } - 2 -> playerl(core.game.dialogue.FacialExpression.HALF_WORRIED, "Eughr! Though I am curious. Can I try some?").also { stage++ } - 3 -> npcl(core.game.dialogue.FacialExpression.SAD, "Sorry, no. The last outlander who ate my cheese was ill for a month.").also { stage++ } - 4 -> playerl(core.game.dialogue.FacialExpression.ASKING, "So why don't you get ill as well?").also { stage++ } - 5 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Well, we eat yak milk products every day, from when we're born. So I suppose we're used to it. Anyway I should stop yakking - haha - and get on with my work.").also { stage++ } - 6 -> playerl(core.game.dialogue.FacialExpression.HAPPY, "I'm glad to see that puns are common everywhere in Gielinor; even here.").also { stage = END_DIALOGUE } + 0 -> playerl(FacialExpression.ASKING, "Hello. What are you up to?").also { stage++ } + 1 -> npcl(FacialExpression.FRIENDLY, "Ah, I was about to collect some yak's milk to make yak cheese.").also { stage++ } + 2 -> playerl(FacialExpression.HALF_WORRIED, "Eughr! Though I am curious. Can I try some?").also { stage++ } + 3 -> npcl(FacialExpression.SAD, "Sorry, no. The last outlander who ate my cheese was ill for a month.").also { stage++ } + 4 -> playerl(FacialExpression.ASKING, "So why don't you get ill as well?").also { stage++ } + 5 -> npcl(FacialExpression.FRIENDLY, "Well, we eat yak milk products every day, from when we're born. So I suppose we're used to it. Anyway I should stop yakking - haha - and get on with my work.").also { stage++ } + 6 -> playerl(FacialExpression.HAPPY, "I'm glad to see that puns are common everywhere in Gielinor; even here.").also { stage = END_DIALOGUE } } return true } diff --git a/Server/src/main/content/region/fremennik/neitiznot/dialogue/MortenHoldstromDialogue.kt b/Server/src/main/content/region/fremennik/neitiznot/dialogue/MortenHoldstromDialogue.kt index 159be36f5..5d44c591c 100644 --- a/Server/src/main/content/region/fremennik/neitiznot/dialogue/MortenHoldstromDialogue.kt +++ b/Server/src/main/content/region/fremennik/neitiznot/dialogue/MortenHoldstromDialogue.kt @@ -8,25 +8,25 @@ import org.rs09.consts.NPCs import core.tools.END_DIALOGUE @Initializable -class MortenHoldstromDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { +class MortenHoldstromDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player?): DialoguePlugin { return MortenHoldstromDialogue(player) } override fun open(vararg args: Any?): Boolean { - npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Good day to you.") + npcl(FacialExpression.NEUTRAL, "Good day to you.") return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 0 -> playerl(core.game.dialogue.FacialExpression.ASKING, "Hello. What are you up to?").also { stage++ } - 1 -> npcl(core.game.dialogue.FacialExpression.HAPPY, "Ah, today is a surströmming day! The herring I buried six months ago is ready to be dug up.").also { stage++ } - 2 -> playerl(core.game.dialogue.FacialExpression.DISGUSTED, "Eughr! What are you going to do with it?").also { stage++ } - 3 -> npcl(core.game.dialogue.FacialExpression.HAPPY, "Eat it, of course! It will be fermented just-right by now.").also { stage++ } - 4 -> playerl(core.game.dialogue.FacialExpression.ASKING, "Fermented? You eat rotten fish?").also { stage++ } - 5 -> npcl(core.game.dialogue.FacialExpression.HAPPY, "Hmmm, tasty. I'm guessing you don't want to come round and try it?").also { stage++ } - 6 -> playerl(core.game.dialogue.FacialExpression.ANNOYED, "You guess correctly.").also { stage = END_DIALOGUE } + 0 -> playerl(FacialExpression.ASKING, "Hello. What are you up to?").also { stage++ } + 1 -> npcl(FacialExpression.HAPPY, "Ah, today is a surströmming day! The herring I buried six months ago is ready to be dug up.").also { stage++ } + 2 -> playerl(FacialExpression.DISGUSTED, "Eughr! What are you going to do with it?").also { stage++ } + 3 -> npcl(FacialExpression.HAPPY, "Eat it, of course! It will be fermented just-right by now.").also { stage++ } + 4 -> playerl(FacialExpression.ASKING, "Fermented? You eat rotten fish?").also { stage++ } + 5 -> npcl(FacialExpression.HAPPY, "Hmmm, tasty. I'm guessing you don't want to come round and try it?").also { stage++ } + 6 -> playerl(FacialExpression.ANNOYED, "You guess correctly.").also { stage = END_DIALOGUE } } return true } diff --git a/Server/src/main/content/region/fremennik/neitiznot/dialogue/SlugHemliggsenDialogue.kt b/Server/src/main/content/region/fremennik/neitiznot/dialogue/SlugHemliggsenDialogue.kt index 6f2e5af3e..5959779a3 100644 --- a/Server/src/main/content/region/fremennik/neitiznot/dialogue/SlugHemliggsenDialogue.kt +++ b/Server/src/main/content/region/fremennik/neitiznot/dialogue/SlugHemliggsenDialogue.kt @@ -4,22 +4,22 @@ 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 import core.tools.END_DIALOGUE +import org.rs09.consts.NPCs @Initializable -class SlugHemliggsenDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { +class SlugHemliggsenDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player?): DialoguePlugin { return SlugHemliggsenDialogue(player) } override fun open(vararg args: Any?): Boolean { - npcl(core.game.dialogue.FacialExpression.WORRIED, "Shhh. Go away. I'm not allowed to talk to you.") + npcl(FacialExpression.WORRIED, "Shhh. Go away. I'm not allowed to talk to you.") return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { - playerl(core.game.dialogue.FacialExpression.ANNOYED, "Fine, whatever ...") + playerl(FacialExpression.ANNOYED, "Fine, whatever ...") stage = END_DIALOGUE return true } diff --git a/Server/src/main/content/region/fremennik/neitiznot/dialogue/ThakkradSigmundsonDialogue.kt b/Server/src/main/content/region/fremennik/neitiznot/dialogue/ThakkradSigmundsonDialogue.kt index 082b569cb..6ecb15192 100644 --- a/Server/src/main/content/region/fremennik/neitiznot/dialogue/ThakkradSigmundsonDialogue.kt +++ b/Server/src/main/content/region/fremennik/neitiznot/dialogue/ThakkradSigmundsonDialogue.kt @@ -4,23 +4,23 @@ 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 import core.tools.END_DIALOGUE +import org.rs09.consts.NPCs @Initializable -class ThakkradSigmundsonDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { +class ThakkradSigmundsonDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player?): DialoguePlugin { return ThakkradSigmundsonDialogue(player) } override fun open(vararg args: Any?): Boolean { - npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Greetings! I can cure your Yak Hides if you'd like!") + npcl(FacialExpression.FRIENDLY, "Greetings! I can cure your Yak Hides if you'd like!") return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 0 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY, "Good to know!").also { stage = END_DIALOGUE } + 0 -> playerl(FacialExpression.FRIENDLY, "Good to know!").also { stage = END_DIALOGUE } } return true } diff --git a/Server/src/main/content/region/fremennik/neitiznot/dialogue/TrogenKonungardeDialogue.kt b/Server/src/main/content/region/fremennik/neitiznot/dialogue/TrogenKonungardeDialogue.kt index da55ecc0d..83eec864d 100644 --- a/Server/src/main/content/region/fremennik/neitiznot/dialogue/TrogenKonungardeDialogue.kt +++ b/Server/src/main/content/region/fremennik/neitiznot/dialogue/TrogenKonungardeDialogue.kt @@ -4,23 +4,23 @@ 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 import core.tools.END_DIALOGUE +import org.rs09.consts.NPCs @Initializable -class TrogenKonungardeDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { +class TrogenKonungardeDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player?): DialoguePlugin { return TrogenKonungardeDialogue(player) } override fun open(vararg args: Any?): Boolean { - npcl(core.game.dialogue.FacialExpression.NEUTRAL, "I'm guarding the king, I cannot speak.") + npcl(FacialExpression.NEUTRAL, "I'm guarding the king, I cannot speak.") return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 0 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL, "Sorry.").also { stage = END_DIALOGUE } + 0 -> playerl(FacialExpression.NEUTRAL, "Sorry.").also { stage = END_DIALOGUE } } return true } 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 bd0f4d9af..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,41 +8,42 @@ 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 */ @Initializable -class BjornAndEldgrimDialogues(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class BjornAndEldgrimDialogues(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - if (!isQuestComplete(player, "Fremennik Trials")) { - player(core.game.dialogue.FacialExpression.FRIENDLY, "Hello there.").also { stage = 0 } - } else if (isQuestComplete(player, "Fremennik Trials")) { - npc(core.game.dialogue.FacialExpression.DRUNK, "Hey! Itsh you again! Whatshyerfashe!").also { stage = 10 } + if (!isQuestComplete(player, Quests.THE_FREMENNIK_TRIALS)) { + player(FacialExpression.FRIENDLY, "Hello there.").also { stage = 0 } + } else if (isQuestComplete(player, Quests.THE_FREMENNIK_TRIALS)) { + npc(FacialExpression.DRUNK, "Hey! Itsh you again! Whatshyerfashe!").also { stage = 10 } } return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 0 -> npcl(core.game.dialogue.FacialExpression.DRUNK, "Hey! scheck it out! Itsh an outerlandub! Yer shud go shpeak to the chieftain!").also { stage++ } - 1 -> player(core.game.dialogue.FacialExpression.ASKING, "The who?").also { stage++ } - 2 -> npcl(core.game.dialogue.FacialExpression.DRUNK, "That guy over there by that stuff! (hic) Yeh, abshoultely! He's da bosh!").also { stage = END_DIALOGUE } + 0 -> npcl(FacialExpression.DRUNK, "Hey! scheck it out! Itsh an outerlandub! Yer shud go shpeak to the chieftain!").also { stage++ } + 1 -> player(FacialExpression.ASKING, "The who?").also { stage++ } + 2 -> npcl(FacialExpression.DRUNK, "That guy over there by that stuff! (hic) Yeh, abshoultely! He's da bosh!").also { stage = END_DIALOGUE } - 10 -> player(core.game.dialogue.FacialExpression.ASKING, "${player.name}?").also { stage++ } - 11 -> npcl(core.game.dialogue.FacialExpression.DRUNK, "Nah nah nah, not them, the other one, whatshyerfashe!").also { stage++ } - 12 -> player(core.game.dialogue.FacialExpression.ASKING, "${player.getAttribute("fremennikname","fremmyname")}?").also { stage++ } - 13 -> npc(core.game.dialogue.FacialExpression.DRUNK, "Thatsh what I said diddle I?").also { stage++ } - 14 -> player(core.game.dialogue.FacialExpression.ASKING, "Um.... okay. I'll leave you to your drinking.").also { stage++ } - 15 -> npc(core.game.dialogue.FacialExpression.DRUNK, "Thanksh pal! You're alright!").also { stage = END_DIALOGUE } + 10 -> player(FacialExpression.ASKING, "${player.username}?").also { stage++ } + 11 -> npcl(FacialExpression.DRUNK, "Nah nah nah, not them, the other one, whatshyerfashe!").also { stage++ } + 12 -> player(FacialExpression.ASKING, "${player.getAttribute("fremennikname","fremmyname")}?").also { stage++ } + 13 -> npc(FacialExpression.DRUNK, "Thatsh what I said diddle I?").also { stage++ } + 14 -> player(FacialExpression.ASKING, "Um.... okay. I'll leave you to your drinking.").also { stage++ } + 15 -> npc(FacialExpression.DRUNK, "Thanksh pal! You're alright!").also { stage = END_DIALOGUE } } return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return BjornAndEldgrimDialogues(player) } 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 6eaa88a99..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,33 +8,34 @@ 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 */ @Initializable -class BlaninDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class BlaninDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - if (!isQuestComplete(player, "Fremennik Trials")) { - player(core.game.dialogue.FacialExpression.FRIENDLY, "Good day.").also { stage = 0 } + if (!isQuestComplete(player, Quests.THE_FREMENNIK_TRIALS)) { + player(FacialExpression.FRIENDLY, "Good day.").also { stage = 0 } } else { - player(core.game.dialogue.FacialExpression.FRIENDLY, "That's one less thing to worry about.").also { stage = 10 } + player(FacialExpression.FRIENDLY, "That's one less thing to worry about.").also { stage = 10 } } return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 0 -> npc(core.game.dialogue.FacialExpression.FRIENDLY, "Good day to you, sir.").also { stage = END_DIALOGUE } - 10 -> npc(core.game.dialogue.FacialExpression.FRIENDLY, "Glad I could help.").also { stage = END_DIALOGUE } + 0 -> npc(FacialExpression.FRIENDLY, "Good day to you, sir.").also { stage = END_DIALOGUE } + 10 -> npc(FacialExpression.FRIENDLY, "Glad I could help.").also { stage = END_DIALOGUE } } return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return BlaninDialogue(player) } 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 1659cac86..c54085080 100644 --- a/Server/src/main/content/region/fremennik/rellekka/dialogue/CouncilWorkerDialogue.kt +++ b/Server/src/main/content/region/fremennik/rellekka/dialogue/CouncilWorkerDialogue.kt @@ -1,18 +1,19 @@ package content.region.fremennik.rellekka.dialogue import content.region.fremennik.rellekka.quest.thefremenniktrials.CouncilWorkerFTDialogue -import core.api.questStage +import core.api.getQuestStage 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.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(questStage(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/CouncilWorkerDiaryDialogue.kt b/Server/src/main/content/region/fremennik/rellekka/dialogue/CouncilWorkerDiaryDialogue.kt index 2b92f624c..a4600a34b 100644 --- a/Server/src/main/content/region/fremennik/rellekka/dialogue/CouncilWorkerDiaryDialogue.kt +++ b/Server/src/main/content/region/fremennik/rellekka/dialogue/CouncilWorkerDiaryDialogue.kt @@ -6,23 +6,31 @@ import org.rs09.consts.Items import core.game.dialogue.DialogueFile import core.tools.END_DIALOGUE import core.tools.START_DIALOGUE +import core.game.node.entity.player.link.diary.AchievementDiary +import core.game.node.entity.player.link.diary.DiaryType const val COUNCIL_WORKER = 1287 class CouncilWorkerDiaryDialogue() : DialogueFile() { override fun handle(componentID: Int, buttonID: Int) { - + //todo all this dialogue is inauthentic and sucks, this is just a patch to fix the absolutely garbage and incorrect way of granting the diary rewards that doesn't even give the lamp. -Ceik June 2023 when (stage) { START_DIALOGUE -> { - player(core.game.dialogue.FacialExpression.FRIENDLY, "About my achievement diary...");stage++ + player(FacialExpression.FRIENDLY, "About my achievement diary...");stage++ } 1 -> { - npc(COUNCIL_WORKER, "You have completed the Fremennik Easy Diary!");stage++ + if (!AchievementDiary.hasClaimedLevelRewards(player, DiaryType.FREMENNIK, 0)) { + AchievementDiary.flagRewarded(player, DiaryType.FREMENNIK, 0) + npc(COUNCIL_WORKER, "You have completed the Fremennik Easy Diary!") + stage++ + } else if (AchievementDiary.canReplaceReward(player, DiaryType.FREMENNIK, 0)) { + player(FacialExpression.FRIENDLY, "I need a new pair of boots.") + stage = 10 + } //player.achievementDiaryManager.getDiary(DiaryType.FREMENNIK).setLevelRewarded(0) } 2 -> { - player?.let { addItem(it, Items.FREMENNIK_SEA_BOOTS_1_14571) } player?.let { sendItemDialogue( it, @@ -37,6 +45,12 @@ class CouncilWorkerDiaryDialogue() : DialogueFile() { "You can now use Peer the Seer to deposit your items!" ).also { stage = END_DIALOGUE } } + + 10 -> { + npc(COUNCIL_WORKER, "Sure!") + AchievementDiary.grantReplacement(player, DiaryType.FREMENNIK, 0) + stage = 2 + } } } -} \ No newline at end of file +} 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 82499543e..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,33 +8,34 @@ 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 */ @Initializable -class DronDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class DronDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - if (!isQuestComplete(player, "Making History")) { - player(core.game.dialogue.FacialExpression.FRIENDLY, "Excuse me.").also { stage = 0 } + if (!isQuestComplete(player, Quests.MAKING_HISTORY)) { + player(FacialExpression.FRIENDLY, "Excuse me.").also { stage = 0 } } else { - player(core.game.dialogue.FacialExpression.FRIENDLY, "Excuse me.").also { stage = 10 } + player(FacialExpression.FRIENDLY, "Excuse me.").also { stage = 10 } } return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 0 -> npc(core.game.dialogue.FacialExpression.ANNOYED, "Leave me or I'll destroy you!").also { stage = END_DIALOGUE } - 10 -> npc(core.game.dialogue.FacialExpression.ANNOYED, "You have your answers, now go away!").also { stage = END_DIALOGUE } + 0 -> npc(FacialExpression.ANNOYED, "Leave me or I'll destroy you!").also { stage = END_DIALOGUE } + 10 -> npc(FacialExpression.ANNOYED, "You have your answers, now go away!").also { stage = END_DIALOGUE } } return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return DronDialogue(player) } 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 5ebcc8385..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,20 +8,21 @@ 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 */ @Initializable -class FishmongerRellekkaDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class FishmongerRellekkaDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - if (!isQuestComplete(player, "Fremennik Trials")) { - npc(core.game.dialogue.FacialExpression.ANNOYED, "I don't sell to outerlanders.").also { stage = END_DIALOGUE } + if (!isQuestComplete(player, Quests.THE_FREMENNIK_TRIALS)) { + npc(FacialExpression.ANNOYED, "I don't sell to outerlanders.").also { stage = END_DIALOGUE } } else { - npcl(core.game.dialogue.FacialExpression.FRIENDLY,"Hello there, ${player.getAttribute("fremennikname","fremmyname")}. Looking for fresh fish?").also { stage = 0 } + npcl(FacialExpression.FRIENDLY,"Hello there, ${player.getAttribute("fremennikname","fremmyname")}. Looking for fresh fish?").also { stage = 0 } } return true } @@ -33,7 +34,7 @@ class FishmongerRellekkaDialogue(player: Player? = null) : core.game.dialogue.Di return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return FishmongerRellekkaDialogue(player) } 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 dddc630cb..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,20 +8,21 @@ 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 */ @Initializable -class FurTraderDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class FurTraderDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - if (!isQuestComplete(player, "Fremennik Trials")) { - npc(core.game.dialogue.FacialExpression.ANNOYED, "I don't sell to outerlanders.").also { stage = END_DIALOGUE } + if (!isQuestComplete(player, Quests.THE_FREMENNIK_TRIALS)) { + npc(FacialExpression.ANNOYED, "I don't sell to outerlanders.").also { stage = END_DIALOGUE } } else { - npcl(core.game.dialogue.FacialExpression.FRIENDLY,"Welcome back, ${player.getAttribute("fremennikname","fremmyname")}. Have you seen the furs I have today?").also { stage = 10 } + npcl(FacialExpression.FRIENDLY,"Welcome back, ${player.getAttribute("fremennikname","fremmyname")}. Have you seen the furs I have today?").also { stage = 10 } } return true } @@ -33,7 +34,7 @@ class FurTraderDialogue(player: Player? = null) : core.game.dialogue.DialoguePlu return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return FurTraderDialogue(player) } diff --git a/Server/src/main/content/region/fremennik/rellekka/dialogue/HuntingExpertRellekkaDialogue.kt b/Server/src/main/content/region/fremennik/rellekka/dialogue/HuntingExpertRellekkaDialogue.kt index 00ded9800..baa10c58f 100644 --- a/Server/src/main/content/region/fremennik/rellekka/dialogue/HuntingExpertRellekkaDialogue.kt +++ b/Server/src/main/content/region/fremennik/rellekka/dialogue/HuntingExpertRellekkaDialogue.kt @@ -15,9 +15,9 @@ import core.tools.START_DIALOGUE * @author vddcore */ @Initializable -class HuntingExpertRellekkaDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { +class HuntingExpertRellekkaDialogue(player: Player? = null) : DialoguePlugin(player) { - override fun newInstance(player: Player): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player): DialoguePlugin { return HuntingExpertRellekkaDialogue(player) } @@ -25,7 +25,7 @@ class HuntingExpertRellekkaDialogue(player: Player? = null) : core.game.dialogue npc = args[0] as NPC npcl( - core.game.dialogue.FacialExpression.HAPPY, + FacialExpression.HAPPY, "Good day, you seem to have a keen eye. " + "Maybe even some hunter's blood in that body of yours?" ) @@ -36,48 +36,48 @@ class HuntingExpertRellekkaDialogue(player: Player? = null) : core.game.dialogue override fun handle(interfaceId: Int, buttonId: Int): Boolean { when (stage) { START_DIALOGUE -> showTopics( - Topic(core.game.dialogue.FacialExpression.ASKING, "Is there anything you can teach me?", 1), - Topic(core.game.dialogue.FacialExpression.NEUTRAL, "Nevermind.", END_DIALOGUE) + Topic(FacialExpression.ASKING, "Is there anything you can teach me?", 1), + Topic(FacialExpression.NEUTRAL, "Nevermind.", END_DIALOGUE) ) 1 -> npcl( - core.game.dialogue.FacialExpression.FRIENDLY, + FacialExpression.FRIENDLY, "I can teach you how to hunt." ).also { stage++ } 2 -> playerl( - core.game.dialogue.FacialExpression.THINKING, + FacialExpression.THINKING, "What kind of creatures can I hunt?" ).also { stage++ } 3 -> npcl( - core.game.dialogue.FacialExpression.FRIENDLY, + FacialExpression.FRIENDLY, "Many creatures in many ways. You need to make some traps " + "and catch birds!" ).also { stage++ } 4 -> playerl( - core.game.dialogue.FacialExpression.HALF_ASKING, + FacialExpression.HALF_ASKING, "Birds?" ).also { stage++ } 5 -> npcl( - core.game.dialogue.FacialExpression.ANNOYED, + FacialExpression.ANNOYED, "Yes, birds! Like the ones here!" ).also { stage++ } 6 -> npcl( - core.game.dialogue.FacialExpression.ANNOYED, + FacialExpression.ANNOYED, "Look. Just... Get some hunting gear and go set up some traps." ).also { stage++ } 7 -> playerl( - core.game.dialogue.FacialExpression.HALF_ROLLING_EYES, + FacialExpression.HALF_ROLLING_EYES, "Is that it?" ).also { stage++ } 8 -> npcl( - core.game.dialogue.FacialExpression.FURIOUS, + FacialExpression.FURIOUS, "JUST GO DO IT!" ).also { stage = END_DIALOGUE } } 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 02bd29c44..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,38 +8,39 @@ 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 */ @Initializable -class IngridHradsonDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class IngridHradsonDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - if (!isQuestComplete(player, "Fremennik Trials")) { - npcl(core.game.dialogue.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")) { - npc(core.game.dialogue.FacialExpression.FRIENDLY, "Good afternoon! How do you like our village?").also { stage = 0 } - } else if (isQuestComplete(player, "Fremennik Trials") && isQuestComplete(player, "Olaf's Quest")) { - npc(core.game.dialogue.FacialExpression.ASKING, "Hello again! Have you any word from my husband?").also { stage = 10 } + 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, 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, 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 } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 0 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY, "It's lovely. You have a fine collection of goats.").also { stage++ } - 1 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "We polish them every day to get them nice and clean.").also { stage = END_DIALOGUE } + 0 -> playerl(FacialExpression.FRIENDLY, "It's lovely. You have a fine collection of goats.").also { stage++ } + 1 -> npcl(FacialExpression.FRIENDLY, "We polish them every day to get them nice and clean.").also { stage = END_DIALOGUE } - 10 -> playerl(core.game.dialogue.FacialExpression.HALF_GUILTY, "Err, no, not yet. It takes a while for the messages to reach me you know.").also { stage++ } - 11 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Well, when you do, tell him we'll be more than happy to see him again.").also { stage = END_DIALOGUE } + 10 -> playerl(FacialExpression.HALF_GUILTY, "Err, no, not yet. It takes a while for the messages to reach me you know.").also { stage++ } + 11 -> npcl(FacialExpression.FRIENDLY, "Well, when you do, tell him we'll be more than happy to see him again.").also { stage = END_DIALOGUE } } return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return IngridHradsonDialogue(player) } 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 4e041a938..000000000 --- a/Server/src/main/content/region/fremennik/rellekka/dialogue/JarvaldDialogue.java +++ /dev/null @@ -1,249 +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; - -/** - * 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: - player.getConfigManager().set(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 (player.getConfigManager().get(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..81e33debf --- /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(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 d804c10f7..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,33 +8,34 @@ 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 */ @Initializable -class LonghallBouncerDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class LonghallBouncerDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - if (!isQuestComplete(player, "Fremennik Trials")) { - npcl(core.game.dialogue.FacialExpression.ANNOYED, "Hey, outerlander. You can't go through there. Talent only, backstage.").also { stage = END_DIALOGUE } + 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(core.game.dialogue.FacialExpression.ANNOYED, "You can't go through there. Talent only, backstage.").also { stage = 0 } + npcl(FacialExpression.ANNOYED, "You can't go through there. Talent only, backstage.").also { stage = 0 } } return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 0 -> player(core.game.dialogue.FacialExpression.PANICKED, "But I'm a Bard!").also { stage++ } - 1 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "No you're not. I saw your performance. I was paid well to keep you from ever setting foot on stage here again.").also { stage = END_DIALOGUE } + 0 -> player(FacialExpression.PANICKED, "But I'm a Bard!").also { stage++ } + 1 -> npcl(FacialExpression.FRIENDLY, "No you're not. I saw your performance. I was paid well to keep you from ever setting foot on stage here again.").also { stage = END_DIALOGUE } } return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return LonghallBouncerDialogue(player) } 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 e29283ccf..b998fa8af 100644 --- a/Server/src/main/content/region/fremennik/rellekka/dialogue/MariaGunnarsDialogue.java +++ b/Server/src/main/content/region/fremennik/rellekka/dialogue/MariaGunnarsDialogue.java @@ -6,6 +6,9 @@ import core.game.node.entity.player.Player; 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. * @author Vexia @@ -62,12 +65,14 @@ public class MariaGunnarsDialogue extends DialoguePlugin { stage++; break; case 3: + end(); + if (!hasRequirement(player, Quests.THE_FREMENNIK_TRIALS)) + break; if (npc.getId() == 5508) { RellekkaUtils.sail(player, RellekkaDestination.RELLEKKA_TO_NEITIZNOT); } else { RellekkaUtils.sail(player, RellekkaDestination.NEITIZNOT_TO_RELLEKKA); } - end(); break; case 4: npc("Thanks!"); 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 e01bb016d..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,20 +8,21 @@ 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 */ @Initializable -class ReesoDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class ReesoDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - if (!isQuestComplete(player, "Fremennik Trials")) { - npcl(core.game.dialogue.FacialExpression.ANNOYED, "Please do not disturb me, outerlander. I have much to do.").also { stage = END_DIALOGUE } + 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(core.game.dialogue.FacialExpression.STRUGGLE, "Sorry, ${player.getAttribute("fremennikname","fremmyname")}, I must get on with my work.").also { stage = END_DIALOGUE } + npcl(FacialExpression.STRUGGLE, "Sorry, ${player.getAttribute("fremennikname","fremmyname")}, I must get on with my work.").also { stage = END_DIALOGUE } } return true @@ -31,7 +32,7 @@ class ReesoDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin( return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return ReesoDialogue(player) } 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 ae6fd91c7..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,34 +8,35 @@ 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. */ @Initializable -class TalkToChiefDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class TalkToChiefDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - if (!isQuestComplete(player, "Fremennik Trials")) { - npcl(core.game.dialogue.FacialExpression.ANNOYED, "I cannot speak to you outerlander! Talk to Brundt, the Chieftain!").also { stage = END_DIALOGUE } + 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(core.game.dialogue.FacialExpression.FRIENDLY, "Hello.").also { stage = 0 } + player(FacialExpression.FRIENDLY, "Hello.").also { stage = 0 } } return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 0 -> npc(core.game.dialogue.FacialExpression.FRIENDLY, "Hello to you, too!").also { stage = END_DIALOGUE } + 0 -> npc(FacialExpression.FRIENDLY, "Hello to you, too!").also { stage = END_DIALOGUE } } return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return TalkToChiefDialogue(player) } 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 dfcc0bff6..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,37 +8,38 @@ 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 */ @Initializable -class VolfOlasfsonDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class VolfOlasfsonDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - if (!isQuestComplete(player, "Fremennik Trials")) { - npc(core.game.dialogue.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")) { - npc(core.game.dialogue.FacialExpression.FRIENDLY, "Hello there. Enjoying the view?").also { stage = 0 } - } else if (isQuestComplete(player, "Fremennik Trials") && isQuestComplete(player, "Olaf's Quest")) { - npcl(core.game.dialogue.FacialExpression.ASKING, "Hello again, friend! Does my father send any word... or treasures like before?").also { stage = 10 } + 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, Quests.THE_FREMENNIK_TRIALS) && !isQuestComplete(player, Quests.OLAFS_QUEST)) { + npc(FacialExpression.FRIENDLY, "Hello there. Enjoying the view?").also { stage = 0 } + } 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 } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 0 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "Yes I am. You have a lovely yurt.").also { stage++ } - 1 -> npc(core.game.dialogue.FacialExpression.FRIENDLY, "Thanks! I exercise it regularly.").also { stage = END_DIALOGUE } + 0 -> player(FacialExpression.FRIENDLY, "Yes I am. You have a lovely yurt.").also { stage++ } + 1 -> npc(FacialExpression.FRIENDLY, "Thanks! I exercise it regularly.").also { stage = END_DIALOGUE } - 10 -> playerl(core.game.dialogue.FacialExpression.HALF_GUILTY, "Not today, but if he does, you will be the first to know.").also { stage = END_DIALOGUE } + 10 -> playerl(FacialExpression.HALF_GUILTY, "Not today, but if he does, you will be the first to know.").also { stage = END_DIALOGUE } } return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return VolfOlasfsonDialogue(player) } diff --git a/Server/src/main/content/region/fremennik/rellekka/dialogue/WaitingOnTheShowDialogues.kt b/Server/src/main/content/region/fremennik/rellekka/dialogue/WaitingOnTheShowDialogues.kt index 70ae42bce..d9b9fd99d 100644 --- a/Server/src/main/content/region/fremennik/rellekka/dialogue/WaitingOnTheShowDialogues.kt +++ b/Server/src/main/content/region/fremennik/rellekka/dialogue/WaitingOnTheShowDialogues.kt @@ -5,19 +5,19 @@ 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 core.tools.END_DIALOGUE +import org.rs09.consts.NPCs /** * @author qmqz */ @Initializable -class WaitingOnTheShowDialogues(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class WaitingOnTheShowDialogues(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - npc(core.game.dialogue.FacialExpression.ANNOYED, "Shhh! I'm waiting for the show!").also { stage = END_DIALOGUE } + npc(FacialExpression.ANNOYED, "Shhh! I'm waiting for the show!").also { stage = END_DIALOGUE } return true } @@ -25,7 +25,7 @@ class WaitingOnTheShowDialogues(player: Player? = null) : core.game.dialogue.Dia return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return WaitingOnTheShowDialogues(player) } 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 ddb16861c..cac9e67e8 100644 --- a/Server/src/main/content/region/fremennik/rellekka/handlers/RellekkaListeners.kt +++ b/Server/src/main/content/region/fremennik/rellekka/handlers/RellekkaListeners.kt @@ -1,9 +1,11 @@ package content.region.fremennik.rellekka.handlers +import core.api.* +import core.game.interaction.IntType +import core.game.interaction.InteractionListener import core.game.world.map.Location import org.rs09.consts.NPCs -import core.game.interaction.InteractionListener -import core.game.interaction.IntType +import content.data.Quests /** * File to be used for anything Rellekka related. @@ -47,23 +49,31 @@ class RellekkaListeners : InteractionListener { } on(NPCs.MARIA_GUNNARS_5508, IntType.NPC, "ferry-neitiznot"){ player, _ -> + if (!hasRequirement(player, Quests.THE_FREMENNIK_TRIALS)) + return@on true RellekkaUtils.sail(player, RellekkaDestination.RELLEKKA_TO_NEITIZNOT) + playJingle(player, 171) return@on true } on(NPCs.MARIA_GUNNARS_5507, IntType.NPC, "ferry-rellekka"){ player, node -> RellekkaUtils.sail(player, RellekkaDestination.NEITIZNOT_TO_RELLEKKA) + playJingle(player, 171) return@on true } on(NPCs.MORD_GUNNARS_5481, IntType.NPC, "ferry-jatizso"){ player, node -> + if (!hasRequirement(player, Quests.THE_FREMENNIK_TRIALS)) + return@on true RellekkaUtils.sail(player, RellekkaDestination.RELLEKKA_TO_JATIZSO) + playJingle(player, 171) return@on true } on(NPCs.MORD_GUNNARS_5482, IntType.NPC, "ferry-rellekka"){ player, node -> RellekkaUtils.sail(player, RellekkaDestination.JATIZSO_TO_RELLEKKA) + playJingle(player, 171) return@on true } } -} \ No newline at end of file +} 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 22196cdde..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,33 +6,34 @@ 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. -class AskeladdenDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { +class AskeladdenDialogue(player: Player? = null) : DialoguePlugin(player) { override fun open(vararg args: Any?): Boolean { player?.let { if(player.inventory.contains(3709,1)){ - playerl(core.game.dialogue.FacialExpression.ASKING,"I thought you really liked the long hall?") + playerl(FacialExpression.ASKING,"I thought you really liked the long hall?") stage = 27 return true } else if(player?.getAttribute("sigmund-steps", 0) == 13){ - playerl(core.game.dialogue.FacialExpression.ASKING,"I don't suppose you have any idea where I could find a written promise from Askeladden to stay out of the Longhall?") + playerl(FacialExpression.ASKING,"I don't suppose you have any idea where I could find a written promise from Askeladden to stay out of the Longhall?") stage = 15 return true } else if(player?.getAttribute("sigmundreturning",false) == true){ - playerl(core.game.dialogue.FacialExpression.ASKING,"I've lost one of the items I was supposed to be trading.") + playerl(FacialExpression.ASKING,"I've lost one of the items I was supposed to be trading.") stage = 35 return true } - else if(player.questRepository.isComplete("Fremennik Trials")){ - playerl(core.game.dialogue.FacialExpression.HAPPY,"Hello again Askeladden.") + 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 @@ -43,7 +44,7 @@ class AskeladdenDialogue(player: Player? = null) : core.game.dialogue.DialoguePl return true } else{ - playerl(core.game.dialogue.FacialExpression.HAPPY,"Hello there.") + playerl(FacialExpression.HAPPY,"Hello there.") stage = 55 return true } @@ -51,86 +52,86 @@ class AskeladdenDialogue(player: Player? = null) : core.game.dialogue.DialoguePl return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { - return content.region.fremennik.rellekka.quest.thefremenniktrials.AskeladdenDialogue(player) + override fun newInstance(player: Player?): DialoguePlugin { + return AskeladdenDialogue(player) } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when (stage) { - 0 -> npc(core.game.dialogue.FacialExpression.CHILD_LOUDLY_LAUGHING, "HAHAHA! Yeah, that Lalli... what a maroon!").also { stage++ } + 0 -> npc(FacialExpression.CHILD_LOUDLY_LAUGHING, "HAHAHA! Yeah, that Lalli... what a maroon!").also { stage++ } 1 -> player("So how did you manage to get the wool?").also { stage++ } - 2 -> npc(core.game.dialogue.FacialExpression.CHILD_FRIENDLY, "Well, as you know, I am doing the same trials that you", "are as part of my test of manhood, and that troll is the", "only one who can get that wool.").also { stage++ } - 3 -> npc(core.game.dialogue.FacialExpression.CHILD_NORMAL, "You might have noticed he's kind of... messed in the", "head buddy! He's real paranoid about people stealing his", "golden apples, isn't he?").also { stage++ } - 4 -> player(core.game.dialogue.FacialExpression.HALF_ASKING,"Indeed he is. So how did you manage to get some", "golden wool from him?").also { stage++ } - 5 -> npc(core.game.dialogue.FacialExpression.CHILD_NORMAL, "It was easy buddy! I persuaded him he needed a pet to", "help him guard his apples. A pet that would never sleep!", "A pet that would never need food or exercise!").also { stage++ } - 6 -> npc(core.game.dialogue.FacialExpression.CHILD_NORMAL, "A pet that would never need him to clean up its... well,", "you know, buddy. A pet that would always be loyal to", "him! A faithful companion for life!").also { stage++ } - 7 -> player(core.game.dialogue.FacialExpression.HALF_ASKING, "What pet is this then?").also { stage++ } - 8 -> npc(core.game.dialogue.FacialExpression.CHILD_LOUDLY_LAUGHING, "A pet ROCK!").also { stage++ } - 9 -> npc(core.game.dialogue.FacialExpression.CHILD_NORMAL, "Man, can you believe that stupid troll traded me some", "of his golden wool for a worthless ROCK?").also { stage++ } - 10 -> npc(core.game.dialogue.FacialExpression.CHILD_FRIENDLY, "Buddy, I hafta say: if brains were explosives, that troll", "wouldn't have enough to blow his nose!").also { stage++ } - 11 -> player(core.game.dialogue.FacialExpression.HALF_ASKING, "Do you have any spare rocks then?").also { stage++ } - 12 -> npc(core.game.dialogue.FacialExpression.CHILD_NEUTRAL, "Sure thing buddy, although I have to say, I doubt even", "that troll is stupid enough to fall for the SAME trick", "TWICE in a row! You can try anyways though!").also { + 2 -> npc(FacialExpression.CHILD_FRIENDLY, "Well, as you know, I am doing the same trials that you", "are as part of my test of manhood, and that troll is the", "only one who can get that wool.").also { stage++ } + 3 -> npc(FacialExpression.CHILD_NORMAL, "You might have noticed he's kind of... messed in the", "head buddy! He's real paranoid about people stealing his", "golden apples, isn't he?").also { stage++ } + 4 -> player(FacialExpression.HALF_ASKING,"Indeed he is. So how did you manage to get some", "golden wool from him?").also { stage++ } + 5 -> npc(FacialExpression.CHILD_NORMAL, "It was easy buddy! I persuaded him he needed a pet to", "help him guard his apples. A pet that would never sleep!", "A pet that would never need food or exercise!").also { stage++ } + 6 -> npc(FacialExpression.CHILD_NORMAL, "A pet that would never need him to clean up its... well,", "you know, buddy. A pet that would always be loyal to", "him! A faithful companion for life!").also { stage++ } + 7 -> player(FacialExpression.HALF_ASKING, "What pet is this then?").also { stage++ } + 8 -> npc(FacialExpression.CHILD_LOUDLY_LAUGHING, "A pet ROCK!").also { stage++ } + 9 -> npc(FacialExpression.CHILD_NORMAL, "Man, can you believe that stupid troll traded me some", "of his golden wool for a worthless ROCK?").also { stage++ } + 10 -> npc(FacialExpression.CHILD_FRIENDLY, "Buddy, I hafta say: if brains were explosives, that troll", "wouldn't have enough to blow his nose!").also { stage++ } + 11 -> player(FacialExpression.HALF_ASKING, "Do you have any spare rocks then?").also { stage++ } + 12 -> npc(FacialExpression.CHILD_NEUTRAL, "Sure thing buddy, although I have to say, I doubt even", "that troll is stupid enough to fall for the SAME trick", "TWICE in a row! You can try anyways though!").also { player.inventory.add(Item(Items.PET_ROCK_3695)) - player.setAttribute("/save:fremtrials:askeladden-talkedto",true); + player.setAttribute("/save:fremtrials:askeladden-talkedto",true) stage = 1000} - 15 -> npcl(core.game.dialogue.FacialExpression.CHILD_LOUDLY_LAUGHING,"What? I can't believe she asked you to get a written promise from me to stay out!").also { stage++ } - 16 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL,"Yup, she really did.").also { stage++ } - 17 -> npcl(core.game.dialogue.FacialExpression.CHILD_FRIENDLY,"Awwwwwww.... but the longhall is just SO MUCH FUN! I'd live there if I could! I suppose you really need that promise to help become a Fremennik, huh?").also { stage++ } - 18 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL,"Yeah, I really do...").also { stage++ } - 19 -> npcl(core.game.dialogue.FacialExpression.CHILD_FRIENDLY,"Well I'll tell you what buddy; As it's you, I'll give you that written promise. All I ask in return for it is a measly 5000 gold. What do you say?").also { stage++ } + 15 -> npcl(FacialExpression.CHILD_LOUDLY_LAUGHING,"What? I can't believe she asked you to get a written promise from me to stay out!").also { stage++ } + 16 -> playerl(FacialExpression.NEUTRAL,"Yup, she really did.").also { stage++ } + 17 -> npcl(FacialExpression.CHILD_FRIENDLY,"Awwwwwww.... but the longhall is just SO MUCH FUN! I'd live there if I could! I suppose you really need that promise to help become a Fremennik, huh?").also { stage++ } + 18 -> playerl(FacialExpression.NEUTRAL,"Yeah, I really do...").also { stage++ } + 19 -> npcl(FacialExpression.CHILD_FRIENDLY,"Well I'll tell you what buddy; As it's you, I'll give you that written promise. All I ask in return for it is a measly 5000 gold. What do you say?").also { stage++ } 20 -> options("Yes","No").also { stage++ } 21 -> when(buttonId){ 1 -> if(player?.inventory?.contains(995,5000)!! && !player.inventory.isFull){ - playerl(core.game.dialogue.FacialExpression.HAPPY,"That's all you want in return? Sure thing. Here you go.") + playerl(FacialExpression.HAPPY,"That's all you want in return? Sure thing. Here you go.") stage = 22 }else stage = 25 2 -> stage = 25 } - 22 -> npcl(core.game.dialogue.FacialExpression.CHILD_FRIENDLY,"Done, and done. Let me know if you got any more cash burning a hole in your pocket I can relieve you of, buddy.").also { + 22 -> npcl(FacialExpression.CHILD_FRIENDLY,"Done, and done. Let me know if you got any more cash burning a hole in your pocket I can relieve you of, buddy.").also { player?.inventory?.remove(Item(Items.COINS_995,5000)) player?.inventory?.add(Item(Items.PROMISSORY_NOTE_3709)) player?.incrementAttribute("sigmund-steps",1) stage = 1000 } - 25 -> playerl(core.game.dialogue.FacialExpression.SAD,"I don't think so... That's really quite a lot of money...").also { stage++ } - 26 -> npcl(core.game.dialogue.FacialExpression.CHILD_LOUDLY_LAUGHING,"Hey, suit yourself buddy. You change your mind, the bank of Askeladden is open for deposits 24 hours a day! Eh-heh- heh-heh-heh.").also { stage = 1000 } + 25 -> playerl(FacialExpression.SAD,"I don't think so... That's really quite a lot of money...").also { stage++ } + 26 -> npcl(FacialExpression.CHILD_LOUDLY_LAUGHING,"Hey, suit yourself buddy. You change your mind, the bank of Askeladden is open for deposits 24 hours a day! Eh-heh- heh-heh-heh.").also { stage = 1000 } - 27 -> npcl(core.game.dialogue.FacialExpression.CHILD_FRIENDLY,"I do!").also { stage++ } - 28 -> playerl(core.game.dialogue.FacialExpression.ASKING,"Then why did you sign this guarantee that you will never enter it again?").also { stage++ } - 29 -> npcl(core.game.dialogue.FacialExpression.CHILD_FRIENDLY,"Aha! It is because I am cunning! That guarantee says that Askeladden will never enter the longhall again!").also { stage++ } - 30 -> npcl(core.game.dialogue.FacialExpression.CHILD_FRIENDLY,"But when I have completed my Fremennik trials, and passed my trial of manhood,").also { stage++ } - 31 -> npcl(core.game.dialogue.FacialExpression.CHILD_LOUDLY_LAUGHING,"I will be given a new name, as is our custom, and will therefore not be Askeladden anymore!").also { stage++ } - 32 -> npcl(core.game.dialogue.FacialExpression.CHILD_LOUDLY_LAUGHING,"That guarantee isn't worth the paper it's written on!! You didn't think I would give up going to the longhall for only 5000 did you?").also { stage++ } - 33 -> playerl(core.game.dialogue.FacialExpression.ANNOYED,"Knowing you, I guess I didn't.").also { stage = 1000 } + 27 -> npcl(FacialExpression.CHILD_FRIENDLY,"I do!").also { stage++ } + 28 -> playerl(FacialExpression.ASKING,"Then why did you sign this guarantee that you will never enter it again?").also { stage++ } + 29 -> npcl(FacialExpression.CHILD_FRIENDLY,"Aha! It is because I am cunning! That guarantee says that Askeladden will never enter the longhall again!").also { stage++ } + 30 -> npcl(FacialExpression.CHILD_FRIENDLY,"But when I have completed my Fremennik trials, and passed my trial of manhood,").also { stage++ } + 31 -> npcl(FacialExpression.CHILD_LOUDLY_LAUGHING,"I will be given a new name, as is our custom, and will therefore not be Askeladden anymore!").also { stage++ } + 32 -> npcl(FacialExpression.CHILD_LOUDLY_LAUGHING,"That guarantee isn't worth the paper it's written on!! You didn't think I would give up going to the longhall for only 5000 did you?").also { stage++ } + 33 -> playerl(FacialExpression.ANNOYED,"Knowing you, I guess I didn't.").also { stage = 1000 } - 35 -> npcl(core.game.dialogue.FacialExpression.CHILD_NORMAL,"That's a shame. You should speak to the merchant.").also { stage = 1000 } + 35 -> npcl(FacialExpression.CHILD_NORMAL,"That's a shame. You should speak to the merchant.").also { stage = 1000 } - 40 -> npcl(core.game.dialogue.FacialExpression.CHILD_FRIENDLY,"Hey buddy! That ain't my name no more! My new Fremennik name is Larravak! What's yours?").also { stage++ } - 41 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"My Fremennik name is ${player.getAttribute("fremennikname","lebron james")}.").also { stage++ } - 42 -> npcl(core.game.dialogue.FacialExpression.CHILD_LOUDLY_LAUGHING,"Ha! Ain't as good as my name buddy! So what can I do ya for?").also { stage++ } + 40 -> npcl(FacialExpression.CHILD_FRIENDLY,"Hey buddy! That ain't my name no more! My new Fremennik name is Larravak! What's yours?").also { stage++ } + 41 -> playerl(FacialExpression.HAPPY,"My Fremennik name is ${player.getAttribute("fremennikname","lebron james")}.").also { stage++ } + 42 -> npcl(FacialExpression.CHILD_LOUDLY_LAUGHING,"Ha! Ain't as good as my name buddy! So what can I do ya for?").also { stage++ } 43 -> options("Ask about things to do","Ask for a new pet rock").also { stage++ } 44 -> when(buttonId){ - 1 -> playerl(core.game.dialogue.FacialExpression.ASKING,"So, Askeladd- sorry, Larravak; What is there to do around here now that we are both Fremenniks?").also { stage++ } - 2 -> playerl(core.game.dialogue.FacialExpression.ASKING,"Can I have another pet rock? I lost mine...").also { stage = 50 } + 1 -> playerl(FacialExpression.ASKING,"So, Askeladd- sorry, Larravak; What is there to do around here now that we are both Fremenniks?").also { stage++ } + 2 -> playerl(FacialExpression.ASKING,"Can I have another pet rock? I lost mine...").also { stage = 50 } } - 45 -> npcl(core.game.dialogue.FacialExpression.CHILD_THINKING,"I guess you could do a bit of shopping. We got fresh fish at the docks, and some armour and weapons at Skulgrimen's place.").also { stage++ } - 46 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"Okay, thanks.").also { stage = 1000 } + 45 -> npcl(FacialExpression.CHILD_THINKING,"I guess you could do a bit of shopping. We got fresh fish at the docks, and some armour and weapons at Skulgrimen's place.").also { stage++ } + 46 -> playerl(FacialExpression.HAPPY,"Okay, thanks.").also { stage = 1000 } - 50 -> npcl(core.game.dialogue.FacialExpression.CHILD_FRIENDLY,"Sure thing buddy! I'd say take better care of this one, but it's just a rock! I have hundreds of them! Go wild!").also { + 50 -> npcl(FacialExpression.CHILD_FRIENDLY,"Sure thing buddy! I'd say take better care of this one, but it's just a rock! I have hundreds of them! Go wild!").also { player.inventory.add(Item(Items.PET_ROCK_3695)) stage = 1000 } - 55 -> npcl(core.game.dialogue.FacialExpression.CHILD_FRIENDLY,"Hey there, buddy. You're an outerlander, huh? I'm not really supposed to talk to you.").also { stage++ } - 56 -> playerl(core.game.dialogue.FacialExpression.ASKING,"Why not?").also { stage++ } - 57 -> npcl(core.game.dialogue.FacialExpression.CHILD_THINKING,"I dunno buddy. Some stupid tradition or other. We're always told not to talk to outerlanders unless the Chieftain has allowed it.").also { stage++ } - 58 -> playerl(core.game.dialogue.FacialExpression.ASKING,"The Chieftain? Who is that?").also { stage++ } - 59 -> npcl(core.game.dialogue.FacialExpression.CHILD_FRIENDLY,"His names Brundt, buddy. He's a real boring guy. You can find him in the long hall there.").also { stage++ } - 60 -> playerl(core.game.dialogue.FacialExpression.ASKING,"Can you take me to see him?").also { stage++ } - 61 -> npcl(core.game.dialogue.FacialExpression.CHILD_FRIENDLY,"Sorry buddy, I'm not allowed into the long hall. Don't worry, he's easy to spot. He's the one in the fancy helmet. Oh, and he only has one eye.").also { stage++ } - 62 -> playerl(core.game.dialogue.FacialExpression.ASKING,"He's a cyclops?").also { stage++ } - 63 -> npcl(core.game.dialogue.FacialExpression.CHILD_NORMAL,"Nah, nah, nah... He just wears an eyepatch! Well, see ya around buddy!").also { stage = 1000 } + 55 -> npcl(FacialExpression.CHILD_FRIENDLY,"Hey there, buddy. You're an outerlander, huh? I'm not really supposed to talk to you.").also { stage++ } + 56 -> playerl(FacialExpression.ASKING,"Why not?").also { stage++ } + 57 -> npcl(FacialExpression.CHILD_THINKING,"I dunno buddy. Some stupid tradition or other. We're always told not to talk to outerlanders unless the Chieftain has allowed it.").also { stage++ } + 58 -> playerl(FacialExpression.ASKING,"The Chieftain? Who is that?").also { stage++ } + 59 -> npcl(FacialExpression.CHILD_FRIENDLY,"His names Brundt, buddy. He's a real boring guy. You can find him in the long hall there.").also { stage++ } + 60 -> playerl(FacialExpression.ASKING,"Can you take me to see him?").also { stage++ } + 61 -> npcl(FacialExpression.CHILD_FRIENDLY,"Sorry buddy, I'm not allowed into the long hall. Don't worry, he's easy to spot. He's the one in the fancy helmet. Oh, and he only has one eye.").also { stage++ } + 62 -> playerl(FacialExpression.ASKING,"He's a cyclops?").also { stage++ } + 63 -> npcl(FacialExpression.CHILD_NORMAL,"Nah, nah, nah... He just wears an eyepatch! Well, see ya around buddy!").also { stage = 1000 } 1000 -> end() 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 e2e1e289a..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 @@ -1,7 +1,7 @@ package content.region.fremennik.rellekka.quest.thefremenniktrials -import core.api.addItem -import core.api.removeItem +import core.api.* +import core.game.node.item.Item import core.game.node.entity.player.Player import core.plugin.Initializable import core.game.dialogue.DialoguePlugin @@ -9,64 +9,67 @@ import core.game.dialogue.FacialExpression import core.tools.END_DIALOGUE import kotlin.random.Random +import org.rs09.consts.* +import content.data.Quests + @Initializable -class ChieftanBrundt(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class ChieftanBrundt(player: Player? = null) : DialoguePlugin(player){ val gender = if (player?.isMale == true){"brother"} else "sister" val fName = player?.getAttribute("fremennikname","bringle") override fun open(vararg args: Any?): Boolean { if(player?.inventory?.contains(3701,1) == true){ - playerl(core.game.dialogue.FacialExpression.HAPPY,"I got Sigli's hunting map for you.") + playerl(FacialExpression.HAPPY,"I got Sigli's hunting map for you.") stage = 515 return true } else if(player?.inventory?.contains(3708,1) == true){ - playerl(core.game.dialogue.FacialExpression.ASKING,"So cutting sales tax isn't going to ruin your economy here or anything?") + playerl(FacialExpression.ASKING,"So cutting sales tax isn't going to ruin your economy here or anything?") stage = 520 return true } else if(player?.getAttribute("sigmundreturning",false) == true){ - playerl(core.game.dialogue.FacialExpression.ASKING,"I've got this trade item. Is it for you?") + playerl(FacialExpression.ASKING,"I've got this trade item. Is it for you?") stage = 525 return true } if(player?.getAttribute("sigmund-steps",0) == 5){ - playerl(core.game.dialogue.FacialExpression.ASKING,"I don't suppose you have any idea where I could find a map to unspoiled hunting grounds, do you?") + playerl(FacialExpression.ASKING,"I don't suppose you have any idea where I could find a map to unspoiled hunting grounds, do you?") stage = 510 return true } else if(player?.getAttribute("sigmund-steps",0) == 4){ - playerl(core.game.dialogue.FacialExpression.ASKING,"I don't suppose you have any idea where I could find a guarantee of a reduction on sales taxes, do you?") + playerl(FacialExpression.ASKING,"I don't suppose you have any idea where I could find a guarantee of a reduction on sales taxes, do you?") stage = 500 return true } - else if(player.getAttribute("fremtrials:votes",0) >= 7){ - npcl(core.game.dialogue.FacialExpression.HAPPY," Greetings again outerlander! How goes your attempts to gain votes with the council of elders?") + 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){ - npcl(core.game.dialogue.FacialExpression.HAPPY," Greetings again outerlander! How goes your attempts to gain votes with the council of elders?") + 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){ - npcl(core.game.dialogue.FacialExpression.HAPPY," Greetings again outerlander! How goes your attempts to gain votes with the council of elders?") + 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){ - npcl(core.game.dialogue.FacialExpression.HAPPY," Greetings again outerlander! How goes your attempts to gain votes with the council of elders?") + 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")){ - npcl(core.game.dialogue.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?") + 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 @@ -77,27 +80,27 @@ class ChieftanBrundt(player: Player? = null) : core.game.dialogue.DialoguePlugin //Pre-Quest 0 -> { options("What is this place?", "Why will no-one talk to me?", "Do you have any quests?", "Nice hat!");stage++} 1 -> when(buttonId){ - 1 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"What is this place?").also { stage ++ } - 2 -> playerl(core.game.dialogue.FacialExpression.ASKING,"Why will no-one talk to me?").also { stage = 5 } + 1 -> playerl(FacialExpression.HAPPY,"What is this place?").also { stage ++ } + 2 -> playerl(FacialExpression.ASKING,"Why will no-one talk to me?").also { stage = 5 } 3 -> player("Do you have any quests?").also {stage = 300} - 4 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"Nice hat!").also { stage = 15 } + 4 -> playerl(FacialExpression.HAPPY,"Nice hat!").also { stage = 15 } } - 2 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"This place? Why, this is Relleka! Homeland of all Fremennik! I do not recognise your face outerlander; Where do you come from?").also { stage++ } - 3 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"Hmmm... I will not press the issue then outerlander. How may my tribe and I help you?").also { stage = 0 } + 2 -> npcl(FacialExpression.HAPPY,"This place? Why, this is Relleka! Homeland of all Fremennik! I do not recognise your face outerlander; Where do you come from?").also { stage++ } + 3 -> playerl(FacialExpression.HAPPY,"Hmmm... I will not press the issue then outerlander. How may my tribe and I help you?").also { stage = 0 } - 5 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Do not take it personally, outerlander! We are a simple people, and it is our experience that keeping ourselves to ourselves is best.").also { stage++ } - 6 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"This is why speaking to outerlanders is forbidden.").also { stage++ } - 7 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"We do not wish to enter war with the outerlanders and their strange magics, so we limit all unauthorised communication.").also { stage++ } - 8 -> playerl(core.game.dialogue.FacialExpression.ASKING,"Then how come you're talking to me?").also { stage++ } - 9 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Ah, this is because I am the chieftan. I am the one who authorised contact. You will not find many of my tribe so forthcoming with you, as I.").also { stage++ } - 10 -> playerl(core.game.dialogue.FacialExpression.ASKING,"Is there a way for you to authorise your tribe to talk to me then?").also { stage++ } - 11 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Well, there is one way... but I doubt it is of any interest to you.").also { stage = 0 } + 5 -> npcl(FacialExpression.HAPPY,"Do not take it personally, outerlander! We are a simple people, and it is our experience that keeping ourselves to ourselves is best.").also { stage++ } + 6 -> npcl(FacialExpression.HAPPY,"This is why speaking to outerlanders is forbidden.").also { stage++ } + 7 -> npcl(FacialExpression.HAPPY,"We do not wish to enter war with the outerlanders and their strange magics, so we limit all unauthorised communication.").also { stage++ } + 8 -> playerl(FacialExpression.ASKING,"Then how come you're talking to me?").also { stage++ } + 9 -> npcl(FacialExpression.HAPPY,"Ah, this is because I am the chieftan. I am the one who authorised contact. You will not find many of my tribe so forthcoming with you, as I.").also { stage++ } + 10 -> playerl(FacialExpression.ASKING,"Is there a way for you to authorise your tribe to talk to me then?").also { stage++ } + 11 -> npcl(FacialExpression.HAPPY,"Well, there is one way... but I doubt it is of any interest to you.").also { stage = 0 } - 15 -> npcl(core.game.dialogue.FacialExpression.ANNOYED,"Don't mock me outerlander; this helm has saved my life more times than I care to recall right now.").also { stage = 1000 } + 15 -> npcl(FacialExpression.ANNOYED,"Don't mock me outerlander; this helm has saved my life more times than I care to recall right now.").also { stage = 1000 } //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){ @@ -122,7 +125,7 @@ class ChieftanBrundt(player: Player? = null) : core.game.dialogue.DialoguePlugin //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} @@ -130,132 +133,142 @@ class ChieftanBrundt(player: Player? = null) : core.game.dialogue.DialoguePlugin //No, I'm not interested 340 -> TODO("Not implemented yet") - 500 -> npcl(core.game.dialogue.FacialExpression.THINKING,"A reduction on sales taxes? Why, I am the only one in the Fremennik who may authorise such a thing. What does an outerlander want with that?").also { stage++ } - 501 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"Actually, it's not for me. I need to get it as part of my trials").also { stage++ } - 502 -> npcl(core.game.dialogue.FacialExpression.THINKING,"Hmmm. Interesting. Your trials seem to be very different to those I took as a young lad.").also { stage++ } - 503 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL,"Well, I am not adverse in principle to giving a slight tax break to our shops.").also { stage++ } - 504 -> npcl(core.game.dialogue.FacialExpression.THINKING,"There will of course be a shortfall in the tribe's income, that will need to be made up for elsewhere, however.").also { stage++ } - 505 -> npcl(core.game.dialogue.FacialExpression.ASKING,"How about this. For many years Sigli has been the only one in the tribe who knows the locations of the best hunting grounds where game is easiest to catch.").also { stage++ } - 506 -> npcl(core.game.dialogue.FacialExpression.ASKING,"If you can persuade him to let the entire tribe know these hunting grounds, then we can increase productivity within the tribe, and any shortfall caused by lowering sales taxes will be covered.").also { stage++ } - 507 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"I think this is a more than fair arrangement to make, dont you?").also { stage++ } - 508 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"Yeah, that sounds very fair.").also { stage++ } - 509 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Speak to Sigli then, and you may have my promise to reduce our sales taxes. And best of luck with the rest of your trials.").also { + 500 -> npcl(FacialExpression.THINKING,"A reduction on sales taxes? Why, I am the only one in the Fremennik who may authorise such a thing. What does an outerlander want with that?").also { stage++ } + 501 -> playerl(FacialExpression.HAPPY,"Actually, it's not for me. I need to get it as part of my trials").also { stage++ } + 502 -> npcl(FacialExpression.THINKING,"Hmmm. Interesting. Your trials seem to be very different to those I took as a young lad.").also { stage++ } + 503 -> npcl(FacialExpression.NEUTRAL,"Well, I am not adverse in principle to giving a slight tax break to our shops.").also { stage++ } + 504 -> npcl(FacialExpression.THINKING,"There will of course be a shortfall in the tribe's income, that will need to be made up for elsewhere, however.").also { stage++ } + 505 -> npcl(FacialExpression.ASKING,"How about this. For many years Sigli has been the only one in the tribe who knows the locations of the best hunting grounds where game is easiest to catch.").also { stage++ } + 506 -> npcl(FacialExpression.ASKING,"If you can persuade him to let the entire tribe know these hunting grounds, then we can increase productivity within the tribe, and any shortfall caused by lowering sales taxes will be covered.").also { stage++ } + 507 -> npcl(FacialExpression.HAPPY,"I think this is a more than fair arrangement to make, dont you?").also { stage++ } + 508 -> playerl(FacialExpression.HAPPY,"Yeah, that sounds very fair.").also { stage++ } + 509 -> npcl(FacialExpression.HAPPY,"Speak to Sigli then, and you may have my promise to reduce our sales taxes. And best of luck with the rest of your trials.").also { player?.incrementAttribute("sigmund-steps",1) stage = 1000 } - 510 -> npcl(core.game.dialogue.FacialExpression.ANNOYED,"Ah, outerlander... if you wish to become a Fremennik you should try and pay more attention to what people tell you... ").also { stage++ } - 511 -> npcl(core.game.dialogue.FacialExpression.ANNOYED,"Sigli the hunter is the only one who knows of such hunting grounds. Go and request his knowledge.").also { stage = 1000 } + 510 -> npcl(FacialExpression.ANNOYED,"Ah, outerlander... if you wish to become a Fremennik you should try and pay more attention to what people tell you... ").also { stage++ } + 511 -> npcl(FacialExpression.ANNOYED,"Sigli the hunter is the only one who knows of such hunting grounds. Go and request his knowledge.").also { stage = 1000 } - 515 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Excellent work outerlander! And so quickly, too! Here, you may take my financial report promising reduced sales taxes on all goods.").also { + 515 -> npcl(FacialExpression.HAPPY,"Excellent work outerlander! And so quickly, too! Here, you may take my financial report promising reduced sales taxes on all goods.").also { removeItem(player,3701) addItem(player,3708) stage = 1000 } - 520 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Not at all outerlander; now that we have Sigli's map we can increase the amount of hunts we run, and make up any shortfall that way.").also { stage = 1000 } + 520 -> npcl(FacialExpression.HAPPY,"Not at all outerlander; now that we have Sigli's map we can increase the amount of hunts we run, and make up any shortfall that way.").also { stage = 1000 } - 525 -> npcl(core.game.dialogue.FacialExpression.ANNOYED,"Not unless it's a map of the hunting grounds.").also { stage = 1000 } + 525 -> npcl(FacialExpression.ANNOYED,"Not unless it's a map of the hunting grounds.").also { stage = 1000 } - 530 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"I don't have any votes yet.").also { stage++ } - 531 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Go and speak to the twelve members of the council of elders who live in this village. I am sure at least a few will be prepared to vote for you.").also { stage = 550 } + 530 -> playerl(FacialExpression.HAPPY,"I don't have any votes yet.").also { stage++ } + 531 -> npcl(FacialExpression.HAPPY,"Go and speak to the twelve members of the council of elders who live in this village. I am sure at least a few will be prepared to vote for you.").also { stage = 550 } - 535 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"I only have one vote so far.").also { stage++ } - 536 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Hmmm... well that is certainly a good start I would say. Keep up the good work!").also { stage++ } - 537 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Remember: You need to get at least seven council votes to be accepted as a member of the Fremennik.").also { stage = 550 } + 535 -> playerl(FacialExpression.HAPPY,"I only have one vote so far.").also { stage++ } + 536 -> npcl(FacialExpression.HAPPY,"Hmmm... well that is certainly a good start I would say. Keep up the good work!").also { stage++ } + 537 -> npcl(FacialExpression.HAPPY,"Remember: You need to get at least seven council votes to be accepted as a member of the Fremennik.").also { stage = 550 } - 540 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"I only have ${player.getAttribute("fremtrials:votes",0)} votes so far.").also { stage++ } - 541 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Hmmm... you are doing very well so far, outerlander. Keep up the good work!").also { stage = 537 } + 540 -> playerl(FacialExpression.HAPPY,"I only have ${player.getAttribute("fremtrials:votes",0)} votes so far.").also { stage++ } + 541 -> npcl(FacialExpression.HAPPY,"Hmmm... you are doing very well so far, outerlander. Keep up the good work!").also { stage = 537 } - 545 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"I have seven members of the council prepared to vote in my favour now!").also { stage++ } - 546 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"I know outerlander, for I have been closely monitoring your progress so far!").also {stage++} - 547 -> npcl(core.game.dialogue.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()) + 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 { 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(core.game.dialogue.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(core.game.dialogue.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 { + 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 } - 600 -> options("I just came to say hello.","Do you have any quests?","Nice hat!","Can I hear the history of your people?").also { stage++ } + 600 -> options("I just came to say hello.","Do you have any quests?","Nice hat!","Can I hear the history of your people?", "Can I have a seal of passage?").also { stage++ } 601 -> when(buttonId){ - 1 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"I just came by to say hello.").also { stage++ } - 2 -> playerl(core.game.dialogue.FacialExpression.ASKING,"Do you have any quests?").also { stage = 605 } - 3 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"Nice hat!").also { stage = 610 } - 4 -> playerl(core.game.dialogue.FacialExpression.ASKING,"Can I hear the history of your people?").also { stage = 615 } + 1 -> playerl(FacialExpression.HAPPY,"I just came by to say hello.").also { stage++ } + 2 -> playerl(FacialExpression.ASKING,"Do you have any quests?").also { stage = 605 } + 3 -> playerl(FacialExpression.HAPPY,"Nice hat!").also { stage = 610 } + 4 -> playerl(FacialExpression.ASKING,"Can I hear the history of your people?").also { stage = 615 } + 5 -> playerl(FacialExpression.HAPPY, "Can I have a seal of passage?").also { stage = 1200 } } - 602 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Well met, $gender $fName. it is always good to see your face in glorious Rellekka once more!").also { stage = 1000 } + 602 -> npcl(FacialExpression.HAPPY,"Well met, $gender $fName. it is always good to see your face in glorious Rellekka once more!").also { stage = 1000 } - 605 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Not at the moment, $fName. Rest assured, should your services to the Fremennik be required, I will call upon you").also { stage = 1000 } + 605 -> npcl(FacialExpression.HAPPY,"Not at the moment, $fName. Rest assured, should your services to the Fremennik be required, I will call upon you").also { stage = 1000 } - 610 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Aye that it is, $gender $fName. Should you desire one of your own, you should go to Skulgrimen's shop and see what they have available!").also { stage = 1000 } + 610 -> npcl(FacialExpression.HAPPY,"Aye that it is, $gender $fName. Should you desire one of your own, you should go to Skulgrimen's shop and see what they have available!").also { stage = 1000 } - 615 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Why, of course, $fName! Do not say 'your people' like that, for you are now a Fremennik yourself! What did you want to hear of?").also { stage++ } + 615 -> npcl(FacialExpression.HAPPY,"Why, of course, $fName! Do not say 'your people' like that, for you are now a Fremennik yourself! What did you want to hear of?").also { stage++ } 616 -> options("Tell me of the finding of Koschei","Tell me of the lands to the North","Tell me of the outerlanders","Don't tell me anything").also { stage++ } 617 -> when(buttonId){ - 1 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"I'm interested in finding out about that mysterious warrior I fought as part of Thorvalds' combat trial. His name is Koschei I believe.").also { stage++ } - 2 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"I would like to hear of the lands across the ocean to the North.").also { stage = 635 } - 3 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"I would like to hear a little of the history between the Fremenniks and the outerlanders.").also { stage = 645 } - 4 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"Actually, history isn't really my thing.").also { stage = 665 } + 1 -> playerl(FacialExpression.HAPPY,"I'm interested in finding out about that mysterious warrior I fought as part of Thorvalds' combat trial. His name is Koschei I believe.").also { stage++ } + 2 -> playerl(FacialExpression.HAPPY,"I would like to hear of the lands across the ocean to the North.").also { stage = 635 } + 3 -> playerl(FacialExpression.HAPPY,"I would like to hear a little of the history between the Fremenniks and the outerlanders.").also { stage = 645 } + 4 -> playerl(FacialExpression.HAPPY,"Actually, history isn't really my thing.").also { stage = 665 } } - 618 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Ah... the deathless one... We do not even know if Koschei is his name, when we found him he had no memory,").also { stage++ } - 619 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"he was simply repeating that single word. Whatever happened to him must have rattled his soul so hard that his memory ran away from him,").also { stage++ } - 620 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"and he has not yet found its hiding place.").also { stage++ } - 621 -> playerl(core.game.dialogue.FacialExpression.ASKING,"So how exactly did you find him?").also { stage++ } - 622 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Well, it was a raiding party like any other. We had just struck one of the smaller northern islands to harvest supplies by combat, as our laws allow,").also { stage++ } - 623 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"when we saw a figure in the icey waters. As we were at least a 3 week sail from any port, and saw no other ships nearby, he must have been there some time.").also { stage++ } - 624 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"We could not leave someone to the cold death of the ocean, even an outerlander, so we fished him aboard. He muttered continually, but his words made no sense.").also { stage++ } - 625 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"He had no clothes or possessions with him, and seemed to have been burnt somehow before he had landed in the water. I cannot help but wonder what burnt him,").also { stage++ } - 626 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"for as you may have seen, he is incredibly powerful and resistant to damage by any means I know of! Anyway, we decided to bring him back to Rellekka,").also { stage++ } - 627 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"for he did not seem of good health or sound mind, and simply repeated the word Koschei over and over. Yrsa tended to him for many weeks, until one day it was as though a new soul had entered his burnt and broken body.").also { stage++ } - 628 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"He suddenly left her house one day, fully healed, and able to speak to us as though nothing had happened to him.").also { stage++ } - 629 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"He still had no recollection of how he had come to be in the icey ocean, or of who he was, or where he had come from, but this did not matter to us or him.").also { stage++ } - 630 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"He took his trials of adulthood, as you did, and has been a boon to our clan ever since. Someday I fear he may regain his memory, and leave us for his past...").also { stage++ } - 631 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"But I will always respect any decision he decides to make should that day ever come upon us. Until then, he is more than welcome to stay here.").also { stage++ } - 632 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"So, $fName, was there anything more you wished to hear?").also { stage = 616 } + 618 -> npcl(FacialExpression.HAPPY,"Ah... the deathless one... We do not even know if Koschei is his name, when we found him he had no memory,").also { stage++ } + 619 -> npcl(FacialExpression.HAPPY,"he was simply repeating that single word. Whatever happened to him must have rattled his soul so hard that his memory ran away from him,").also { stage++ } + 620 -> npcl(FacialExpression.HAPPY,"and he has not yet found its hiding place.").also { stage++ } + 621 -> playerl(FacialExpression.ASKING,"So how exactly did you find him?").also { stage++ } + 622 -> npcl(FacialExpression.HAPPY,"Well, it was a raiding party like any other. We had just struck one of the smaller northern islands to harvest supplies by combat, as our laws allow,").also { stage++ } + 623 -> npcl(FacialExpression.HAPPY,"when we saw a figure in the icey waters. As we were at least a 3 week sail from any port, and saw no other ships nearby, he must have been there some time.").also { stage++ } + 624 -> npcl(FacialExpression.HAPPY,"We could not leave someone to the cold death of the ocean, even an outerlander, so we fished him aboard. He muttered continually, but his words made no sense.").also { stage++ } + 625 -> npcl(FacialExpression.HAPPY,"He had no clothes or possessions with him, and seemed to have been burnt somehow before he had landed in the water. I cannot help but wonder what burnt him,").also { stage++ } + 626 -> npcl(FacialExpression.HAPPY,"for as you may have seen, he is incredibly powerful and resistant to damage by any means I know of! Anyway, we decided to bring him back to Rellekka,").also { stage++ } + 627 -> npcl(FacialExpression.HAPPY,"for he did not seem of good health or sound mind, and simply repeated the word Koschei over and over. Yrsa tended to him for many weeks, until one day it was as though a new soul had entered his burnt and broken body.").also { stage++ } + 628 -> npcl(FacialExpression.HAPPY,"He suddenly left her house one day, fully healed, and able to speak to us as though nothing had happened to him.").also { stage++ } + 629 -> npcl(FacialExpression.HAPPY,"He still had no recollection of how he had come to be in the icey ocean, or of who he was, or where he had come from, but this did not matter to us or him.").also { stage++ } + 630 -> npcl(FacialExpression.HAPPY,"He took his trials of adulthood, as you did, and has been a boon to our clan ever since. Someday I fear he may regain his memory, and leave us for his past...").also { stage++ } + 631 -> npcl(FacialExpression.HAPPY,"But I will always respect any decision he decides to make should that day ever come upon us. Until then, he is more than welcome to stay here.").also { stage++ } + 632 -> npcl(FacialExpression.HAPPY,"So, $fName, was there anything more you wished to hear?").also { stage = 616 } - 635 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Well, I think the best thing for you would be to go to the docks and ask to join one of our longboats on a journey. My affairs are concentrated mostly on Rellekka,").also { stage++ } - 636 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"So my knowledge is not great, but I will happily share what little I do know with you, $fName. North of this town the atmosphere becomes bitterly cold.").also { stage++ } - 637 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"There are a number of small inhabited islands that we have sea routes with. The largest of these is possibly Miscellania,").also { stage++ } - 638 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Although I hear that there has been some recent incident with their monarch, but I don't know the details.").also { stage++ } - 639 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"There is also of course the island of the moon clan, who change with the tides. They are our bitterest enemies, and we are currently at war with them.").also { stage++ } - 640 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"They are an evil people, equally cursed and blessed by the magic in their blood. Beyond those islands, there is the large frozen wasteland we call Acheron.").also { stage++ } - 641 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"It is an inhospitable land, and you will need an agile and sturdy body just to keep alive in its perils.").also { stage++ } - 642 -> npcl(core.game.dialogue.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(core.game.dialogue.FacialExpression.HAPPY,"Was there anything else you wished to hear tell of?").also { stage = 616 } + 635 -> npcl(FacialExpression.HAPPY,"Well, I think the best thing for you would be to go to the docks and ask to join one of our longboats on a journey. My affairs are concentrated mostly on Rellekka,").also { stage++ } + 636 -> npcl(FacialExpression.HAPPY,"So my knowledge is not great, but I will happily share what little I do know with you, $fName. North of this town the atmosphere becomes bitterly cold.").also { stage++ } + 637 -> npcl(FacialExpression.HAPPY,"There are a number of small inhabited islands that we have sea routes with. The largest of these is possibly Miscellania,").also { stage++ } + 638 -> npcl(FacialExpression.HAPPY,"Although I hear that there has been some recent incident with their monarch, but I don't know the details.").also { stage++ } + 639 -> npcl(FacialExpression.HAPPY,"There is also of course the island of the moon clan, who change with the tides. They are our bitterest enemies, and we are currently at war with them.").also { stage++ } + 640 -> npcl(FacialExpression.HAPPY,"They are an evil people, equally cursed and blessed by the magic in their blood. Beyond those islands, there is the large frozen wasteland we call Acheron.").also { stage++ } + 641 -> npcl(FacialExpression.HAPPY,"It is an inhospitable land, and you will need an agile and sturdy body just to keep alive in its perils.").also { stage++ } + 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(core.game.dialogue.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++ } - 646 -> npcl(core.game.dialogue.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(core.game.dialogue.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(core.game.dialogue.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++ } - 649 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Unfortunately, the other races that had also migrated here did not like this. They waged war against us at every opportunity.").also { stage++ } - 650 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"We were driven mostly to the coastlines, where we could escape by boat when attacked. Remember, at this time we had no real way to defend against constant attack.").also { stage++ } - 651 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Then one day one of our Seers, whose full name has been lost in the telling of this tale, discovered a cave full of strange rocks.").also { stage++ } - 652 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"This rock would never be consumed, no matter how much we took, and sparkled at its peak, and broke off in small and regular sized chunks when mined.").also { stage++ } - 653 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Stranger than this, we found that these rocks would become filled with power when taken to certain places across this land! There was a terrible argument in our tribe!").also { stage++ } - 654 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Some said that we should take as many of these rocks as we could, and use them in defending against our enemies' attacks!").also { stage++ } - 655 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Others said that we had found a place that belonged only to the gods, and that should we steal what was not ours we would find only torment and misery.").also { stage++ } - 656 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"We voted on this, and decided that it was in the best interests of the tribe to leave these strange rocks where they lay, for the gods can be spiteful and cruel,").also { stage++ } - 657 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"especially to those who do not treat them with the respect they desire. Some of our number refused to accept this ruling by our council however.").also { stage++ } - 658 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"They began to mine these rocks, and set up transport systems to the various places of power that enchanted them. They even created temples at each of these places!").also { stage++ } - 659 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"This was going too far! We had no choice, but to expel them from our tribe forever!").also { stage++ } - 660 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"We turned our backs upon them, and let them know they would never be welcome back to our tribes until they had released themselves from the rocks.").also { stage++ } - 661 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"This is the tale of how the outerlanders came about; through stealing from the gods, and from betraying our ideals. This is why we show them no trust.").also { stage++ } - 662 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Was there something else you wished to hear?").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 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++ } + 649 -> npcl(FacialExpression.HAPPY,"Unfortunately, the other races that had also migrated here did not like this. They waged war against us at every opportunity.").also { stage++ } + 650 -> npcl(FacialExpression.HAPPY,"We were driven mostly to the coastlines, where we could escape by boat when attacked. Remember, at this time we had no real way to defend against constant attack.").also { stage++ } + 651 -> npcl(FacialExpression.HAPPY,"Then one day one of our Seers, whose full name has been lost in the telling of this tale, discovered a cave full of strange rocks.").also { stage++ } + 652 -> npcl(FacialExpression.HAPPY,"This rock would never be consumed, no matter how much we took, and sparkled at its peak, and broke off in small and regular sized chunks when mined.").also { stage++ } + 653 -> npcl(FacialExpression.HAPPY,"Stranger than this, we found that these rocks would become filled with power when taken to certain places across this land! There was a terrible argument in our tribe!").also { stage++ } + 654 -> npcl(FacialExpression.HAPPY,"Some said that we should take as many of these rocks as we could, and use them in defending against our enemies' attacks!").also { stage++ } + 655 -> npcl(FacialExpression.HAPPY,"Others said that we had found a place that belonged only to the gods, and that should we steal what was not ours we would find only torment and misery.").also { stage++ } + 656 -> npcl(FacialExpression.HAPPY,"We voted on this, and decided that it was in the best interests of the tribe to leave these strange rocks where they lay, for the gods can be spiteful and cruel,").also { stage++ } + 657 -> npcl(FacialExpression.HAPPY,"especially to those who do not treat them with the respect they desire. Some of our number refused to accept this ruling by our council however.").also { stage++ } + 658 -> npcl(FacialExpression.HAPPY,"They began to mine these rocks, and set up transport systems to the various places of power that enchanted them. They even created temples at each of these places!").also { stage++ } + 659 -> npcl(FacialExpression.HAPPY,"This was going too far! We had no choice, but to expel them from our tribe forever!").also { stage++ } + 660 -> npcl(FacialExpression.HAPPY,"We turned our backs upon them, and let them know they would never be welcome back to our tribes until they had released themselves from the rocks.").also { stage++ } + 661 -> npcl(FacialExpression.HAPPY,"This is the tale of how the outerlanders came about; through stealing from the gods, and from betraying our ideals. This is why we show them no trust.").also { stage++ } + 662 -> npcl(FacialExpression.HAPPY,"Was there something else you wished to hear?").also { stage = 616 } - 665 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Well let me know should you wish to hear our lore and histories. We value them highly.").also { stage = 1000 } + 665 -> npcl(FacialExpression.HAPPY,"Well let me know should you wish to hear our lore and histories. We value them highly.").also { stage = 1000 } + + 1200 -> npcl(FacialExpression.HALF_THINKING, "Well, I ought not to give you such things lightly...").also { stage++ } + 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, 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++ } + } + 1204 -> { + sendItemDialogue (player, Items.SEAL_OF_PASSAGE_9083, "Chieftan Brundt hands you a Seal of Passage.") + addItem(player, Items.SEAL_OF_PASSAGE_9083) + stage = END_DIALOGUE + } 1000 -> end() } @@ -263,7 +276,7 @@ class ChieftanBrundt(player: Player? = null) : core.game.dialogue.DialoguePlugin } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return ChieftanBrundt(player) } @@ -335,4 +348,4 @@ fun cleanupAttributes(player: Player){ player.removeAttribute("fremtrials:thorvald-vote") player.removeAttribute("fremtrials:sigli-vote") player.removeAttribute("riddlesolved") -} \ No newline at end of file +} 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 0258fc945..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(questStage(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 a95a90c81..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,76 +8,77 @@ 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) : core.game.dialogue.DialoguePlugin(player) { +class FishermanDialogue(player: Player? = null) : DialoguePlugin(player) { override fun open(vararg args: Any?): Boolean { if(player?.inventory?.contains(3704,1) == true){ - playerl(core.game.dialogue.FacialExpression.HAPPY,"Here. I got you your map.") + playerl(FacialExpression.HAPPY,"Here. I got you your map.") stage = 15 return true } else if(player?.inventory?.contains(3703,1) == true){ - playerl(core.game.dialogue.FacialExpression.ASKING,"I don't see what's so special about this so called exotic fish.") + playerl(FacialExpression.ASKING,"I don't see what's so special about this so called exotic fish.") stage = 16 return true } else if(player?.getAttribute("sigmundreturning",false) == true){ - playerl(core.game.dialogue.FacialExpression.ASKING,"Is this trade item for you?") + playerl(FacialExpression.ASKING,"Is this trade item for you?") stage = 17 return true } if(player?.getAttribute("sigmund-steps", 0) == 8){ - playerl(core.game.dialogue.FacialExpression.ASKING,"I don't suppose you have any idea where I could find a map of deep sea fishing spots do you?") + playerl(FacialExpression.ASKING,"I don't suppose you have any idea where I could find a map of deep sea fishing spots do you?") stage = 10 return true } else if(player?.getAttribute("sigmund-steps", 0) == 7){ - playerl(core.game.dialogue.FacialExpression.ASKING,"I don't suppose you have any idea where I could find an exotic and extremely odd fish, do you?") + 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")){ - player(core.game.dialogue.FacialExpression.FRIENDLY, "Hello there.").also { stage = 100 } - } else if (!isQuestComplete(player, "Fremennik Trials")) { - player(core.game.dialogue.FacialExpression.FRIENDLY, "Hello there.").also { stage = 200 } + } else if (isQuestComplete(player, Quests.THE_FREMENNIK_TRIALS)){ + player(FacialExpression.FRIENDLY, "Hello there.").also { stage = 100 } + } else if (!isQuestComplete(player, Quests.THE_FREMENNIK_TRIALS)) { + player(FacialExpression.FRIENDLY, "Hello there.").also { stage = 200 } } return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 1 -> npcl(core.game.dialogue.FacialExpression.AMAZED,"Ah, so even outerlanders have heard of my amazing catch the other day!").also { stage++ } - 2 -> playerl(core.game.dialogue.FacialExpression.THINKING,"You have it? Can I trade you something for it?").also { stage++ } - 3 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"As exotic looking as it is, it is bad eating. I will happily trade it if you can find me the secret map of the best fishing spots that the navigator has hidden away.").also { stage++ } - 4 -> playerl(core.game.dialogue.FacialExpression.AMAZED,"Is that all?").also { stage++ } - 5 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Indeed it is, outerlander. The only reason I sit out here in the cold all day long is so I don't have to pay his outrageous prices.").also { stage++ } - 6 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"By getting me his copy of that map, I will finally be self sufficient. I might even make a profit!").also { stage++ } - 7 -> playerl(core.game.dialogue.FacialExpression.THINKING,"I'll see what I can do.").also { + 1 -> npcl(FacialExpression.AMAZED,"Ah, so even outerlanders have heard of my amazing catch the other day!").also { stage++ } + 2 -> playerl(FacialExpression.THINKING,"You have it? Can I trade you something for it?").also { stage++ } + 3 -> npcl(FacialExpression.HAPPY,"As exotic looking as it is, it is bad eating. I will happily trade it if you can find me the secret map of the best fishing spots that the navigator has hidden away.").also { stage++ } + 4 -> playerl(FacialExpression.AMAZED,"Is that all?").also { stage++ } + 5 -> npcl(FacialExpression.HAPPY,"Indeed it is, outerlander. The only reason I sit out here in the cold all day long is so I don't have to pay his outrageous prices.").also { stage++ } + 6 -> npcl(FacialExpression.HAPPY,"By getting me his copy of that map, I will finally be self sufficient. I might even make a profit!").also { stage++ } + 7 -> playerl(FacialExpression.THINKING,"I'll see what I can do.").also { player?.incrementAttribute("sigmund-steps",1) stage = 1000 } - 10 -> npcl(core.game.dialogue.FacialExpression.ANNOYED,"You should pay attention when I speak! I already told you, that rip off navigator has it, and I want it!").also { stage = 1000 } + 10 -> npcl(FacialExpression.ANNOYED,"You should pay attention when I speak! I already told you, that rip off navigator has it, and I want it!").also { stage = 1000 } - 15 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Great work outerlander! With this, I can finally catch enough fish to make an honest living from it! Here, have the stupid fish.").also { + 15 -> npcl(FacialExpression.HAPPY,"Great work outerlander! With this, I can finally catch enough fish to make an honest living from it! Here, have the stupid fish.").also { removeItem(player,3704) addItem(player,3703) stage = 1000 } - 16 -> npcl(core.game.dialogue.FacialExpression.ANNOYED,"Me neither, outerlander. That is why I gave it to you.").also { stage = 1000 } + 16 -> npcl(FacialExpression.ANNOYED,"Me neither, outerlander. That is why I gave it to you.").also { stage = 1000 } - 17 -> npcl(core.game.dialogue.FacialExpression.ANNOYED,"I don't think so.").also { stage = 1000 } + 17 -> npcl(FacialExpression.ANNOYED,"I don't think so.").also { stage = 1000 } - 100 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Welcome back, ${if (player.isMale) "brother " else "sister "}${player.getAttribute("fremennikname","fremmyname")}. It has been too long since last we spoke! The fish are really biting today!").also { stage = END_DIALOGUE } - 200 -> npcl(core.game.dialogue.FacialExpression.ANNOYED, "Don't talk to me outerlander. Anything you have to say to me, you can say to the Chieftain. Goodbye.").also { stage = END_DIALOGUE } + 100 -> npcl(FacialExpression.FRIENDLY, "Welcome back, ${if (player.isMale) "brother " else "sister "}${player.getAttribute("fremennikname","fremmyname")}. It has been too long since last we spoke! The fish are really biting today!").also { stage = END_DIALOGUE } + 200 -> npcl(FacialExpression.ANNOYED, "Don't talk to me outerlander. Anything you have to say to me, you can say to the Chieftain. Goodbye.").also { stage = END_DIALOGUE } 1000 -> end() } return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return FishermanDialogue(player) } diff --git a/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/FremennikSailorDialogue.kt b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/FremennikSailorDialogue.kt index 9ec63a43b..84f926395 100644 --- a/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/FremennikSailorDialogue.kt +++ b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/FremennikSailorDialogue.kt @@ -8,30 +8,30 @@ import core.game.node.entity.player.Player import core.plugin.Initializable @Initializable -class SailorDialogue (player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { +class SailorDialogue (player: Player? = null) : DialoguePlugin(player) { override fun open(vararg args: Any?): Boolean { if(player?.inventory?.contains(3699,1) == true){ - playerl(core.game.dialogue.FacialExpression.HAPPY,"You'll be glad to know I have had a love song written just for you by Olaf. So can I have that flower of yours now?") + playerl(FacialExpression.HAPPY,"You'll be glad to know I have had a love song written just for you by Olaf. So can I have that flower of yours now?") stage = 15 } else if(player?.inventory?.contains(3698,1) == true){ - playerl(core.game.dialogue.FacialExpression.ASKING,"So tell me... who is this woman that you are trying to impress anyway?") + playerl(FacialExpression.ASKING,"So tell me... who is this woman that you are trying to impress anyway?") stage = 20 return true } else if(player?.getAttribute("sigmundreturning",false) == true){ - playerl(core.game.dialogue.FacialExpression.ASKING,"I've got an item to trade.") + playerl(FacialExpression.ASKING,"I've got an item to trade.") stage = 25 return true } else if(player?.getAttribute("sigmund-steps",0) == 2){ - playerl(core.game.dialogue.FacialExpression.ASKING,"I don't suppose you have any idea where I could find a love ballad, do you?") + playerl(FacialExpression.ASKING,"I don't suppose you have any idea where I could find a love ballad, do you?") stage = 10 return true } else if(player?.getAttribute("sigmund-started",false)!!){ - playerl(core.game.dialogue.FacialExpression.ASKING,"I don't suppose you have any idea where I could find a unique flower from across the sea, do you?") + playerl(FacialExpression.ASKING,"I don't suppose you have any idea where I could find a unique flower from across the sea, do you?") stage = 1 return true } @@ -40,36 +40,36 @@ class SailorDialogue (player: Player? = null) : core.game.dialogue.DialoguePlugi override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 1 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Ah! Even the outerlanders have heard of my mysterious flower! I found it in a country far far away from here!").also { stage++ } - 2 -> playerl(core.game.dialogue.FacialExpression.ASKING,"Can I buy it from you?").also { stage++ } - 3 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL,"I'm afraid not, outerlander. There is a woman in this village whose heart I seek to capture, and I think giving her this strange flower might be my best bet with her.").also { stage++ } - 4 -> playerl(core.game.dialogue.FacialExpression.THINKING,"Maybe you could let me have the flower and do something else to impress her?").also { stage++ } - 5 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Hmm... that is not a totally stupid idea outerlander. I know she is a lover of music, and a romantic ballad might be just the thing with which to woo her.").also { stage++ } - 6 -> npcl(core.game.dialogue.FacialExpression.THINKING,"Unfortunately I don't have a musical bone in my entire body, so someone else will have to write it for me.").also { stage++ } - 7 -> playerl(core.game.dialogue.FacialExpression.ASKING,"So if I can find someone to write you a romantic ballad, you will give me your flower?").also { stage++ } - 8 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"That sounds like a fair deal to me, outerlander.").also { + 1 -> npcl(FacialExpression.HAPPY,"Ah! Even the outerlanders have heard of my mysterious flower! I found it in a country far far away from here!").also { stage++ } + 2 -> playerl(FacialExpression.ASKING,"Can I buy it from you?").also { stage++ } + 3 -> npcl(FacialExpression.NEUTRAL,"I'm afraid not, outerlander. There is a woman in this village whose heart I seek to capture, and I think giving her this strange flower might be my best bet with her.").also { stage++ } + 4 -> playerl(FacialExpression.THINKING,"Maybe you could let me have the flower and do something else to impress her?").also { stage++ } + 5 -> npcl(FacialExpression.HAPPY,"Hmm... that is not a totally stupid idea outerlander. I know she is a lover of music, and a romantic ballad might be just the thing with which to woo her.").also { stage++ } + 6 -> npcl(FacialExpression.THINKING,"Unfortunately I don't have a musical bone in my entire body, so someone else will have to write it for me.").also { stage++ } + 7 -> playerl(FacialExpression.ASKING,"So if I can find someone to write you a romantic ballad, you will give me your flower?").also { stage++ } + 8 -> npcl(FacialExpression.HAPPY,"That sounds like a fair deal to me, outerlander.").also { player.incrementAttribute("sigmund-steps",1) stage = 1000 } - 10 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL,"Well, the only musician I know of in these parts would be that terrible bard Olaf... Ask him.").also { stage = 1000 } + 10 -> npcl(FacialExpression.NEUTRAL,"Well, the only musician I know of in these parts would be that terrible bard Olaf... Ask him.").also { stage = 1000 } - 15 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Oh. It's by Olaf? Hmm. Well, a deal's a deal. I just hope it's better than the usual rubbish he comes up with, or my chances are worse than ever.").also { + 15 -> npcl(FacialExpression.HAPPY,"Oh. It's by Olaf? Hmm. Well, a deal's a deal. I just hope it's better than the usual rubbish he comes up with, or my chances are worse than ever.").also { removeItem(player,3699) addItem(player,3698) stage = 1000 } - 20 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"It's Thora, the longhall barkeep. Please don't tell her though. She's not like the rest of the Fremennik girls, she has a secret desire to see the world.").also { stage++ } - 21 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Being a sailor, I can really relate to that.").also { stage = 1000 } + 20 -> npcl(FacialExpression.HAPPY,"It's Thora, the longhall barkeep. Please don't tell her though. She's not like the rest of the Fremennik girls, she has a secret desire to see the world.").also { stage++ } + 21 -> npcl(FacialExpression.HAPPY,"Being a sailor, I can really relate to that.").also { stage = 1000 } - 25 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Remember, it's not for me unless it's a love ballad.") + 25 -> npcl(FacialExpression.HAPPY,"Remember, it's not for me unless it's a love ballad.") 1000 -> end() } return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return SailorDialogue(player) } 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 94e12b15a..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++) @@ -45,8 +43,8 @@ class FremennikTrials : Quest("Fremennik Trials",64,63,3,347,0,1,10){ line(player,"Thorvald the Warrior",line++,player.getAttribute("fremtrials:thorvald-vote",false)) line(player,"Sigmund the Merchant",line++,player.getAttribute("fremtrials:sigmund-vote",false)) line(player,"Peer the Seer",line++,player.getAttribute("fremtrials:peer-vote",false)) - val voteCount = player.getAttribute("fremtrials:votes",0); - var voteText = "${voteCount} votes"; + val voteCount = player.getAttribute("fremtrials:votes",0) + var voteText = "${voteCount} votes" if (voteCount === 1) { voteText = "1 vote" } diff --git a/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/KoscheiNPC.kt b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/KoscheiNPC.kt index b78d36bd8..1a936e792 100644 --- a/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/KoscheiNPC.kt +++ b/Server/src/main/content/region/fremennik/rellekka/quest/thefremenniktrials/KoscheiNPC.kt @@ -78,7 +78,7 @@ class KoscheiNPC constructor(id: Int = 0, location: Location? = null, session: K } else { session?.player?.sendMessage("Congratulations! You have completed the warriors trial!") session?.player?.setAttribute("/save:fremtrials:thorvald-vote",true) - session?.player?.setAttribute("/save:fremtrials:votes", session?.player?.getAttribute("fremtrials:votes", 0) + 1) + session?.player?.setAttribute("/save:fremtrials:votes", session.player.getAttribute("fremtrials:votes", 0) + 1) session?.player?.removeAttribute("fremtrials:warrior-accepted") addItemOrDrop(session?.player!!, Items.FREMENNIK_BLADE_3757, 1) session.close() @@ -196,7 +196,7 @@ class KoscheiNPC constructor(id: Int = 0, location: Location? = null, session: K when(counter++) { 0 -> player?.lock().also { player?.animate(Animation(1332)).also { player?.sendMessage("Oh dear you are...") }} 1 -> player?.setAttribute("/save:fremtrials:thorvald-vote",true).also { - player?.setAttribute("/save:fremtrials:votesplayer", player.getAttribute("fremtrials:votesplayer", 0) + 1); + player?.setAttribute("/save:fremtrials:votesplayer", player.getAttribute("fremtrials:votesplayer", 0) + 1) player?.removeAttribute("fremtrials:warrior-accepted") } 3 -> player?.teleport(Location.create(2666,3694,1)).also { koschei.session?.close() } 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 80226454b..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,34 +9,35 @@ 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) : core.game.dialogue.DialoguePlugin(player){ +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")){ - playerl(core.game.dialogue.FacialExpression.NEUTRAL,"Hello there.") + if (it.questRepository.isComplete(Quests.THE_FREMENNIK_TRIALS)){ + playerl(FacialExpression.NEUTRAL,"Hello there.") stage = 100 return true } if (it.getAttribute("lalliStewCabbageAdded", false)!! && it.getAttribute("lalliStewOnionAdded", false)!! && it.getAttribute("lalliStewPotatoAdded", false)!! && it.getAttribute("lalliStewRockAdded", false)!!){ - npcl(core.game.dialogue.FacialExpression.OLD_NORMAL,"It am ready now?") + npcl(FacialExpression.OLD_NORMAL,"It am ready now?") stage = 60 return true } if(it.getAttribute("lalliStewCabbageAdded", false)!! || it.getAttribute("lalliStewOnionAdded", false)!! || it.getAttribute("lalliStewPotatoAdded", false)!! || it.getAttribute("lalliStewRockAdded", false)!!){ - npcl(core.game.dialogue.FacialExpression.OLD_NORMAL,"It am ready now?") + npcl(FacialExpression.OLD_NORMAL,"It am ready now?") stage = 58 return true } if (it.getAttribute("lalliEatStew", false)!!){ - playerl(core.game.dialogue.FacialExpression.NEUTRAL,"Hello there.") + playerl(FacialExpression.NEUTRAL,"Hello there.") stage = 65 return true } if (it.getAttribute("hasWool", false)!!){ - playerl(core.game.dialogue.FacialExpression.NEUTRAL,"Hello there.") + playerl(FacialExpression.NEUTRAL,"Hello there.") stage = 75 return true } @@ -45,12 +46,12 @@ class LalliDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin( stage = 50 return true } - if(player.questRepository.isComplete("Fremennik Trials")){ - playerl(core.game.dialogue.FacialExpression.HAPPY,"Hello there.") + 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 } } } @@ -59,18 +60,18 @@ class LalliDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin( - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return LalliDialogue(player) } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 0 -> npc(core.game.dialogue.FacialExpression.OLD_SNEAKY,"Bah! Puny humans always try steal Lalli's golden","apples! You go away now!").also { stage++ } - 1 -> player(core.game.dialogue.FacialExpression.ANNOYED,"Actually, I'm not after your golden apples. I was","wondering if I could have some golden wool, I need it","to string a lyre.").also { stage++ } - 2 -> npc(core.game.dialogue.FacialExpression.OLD_SNEAKY,"Ha! You not fool me human! Me am smart! Other","trolls so jealous of how brainy I are, they kick me out","of camp and make me live down here in cave! But me","have last funny!").also { stage++ } - 3 -> npc(core.game.dialogue.FacialExpression.OLD_SNEAKY,"Me find golden apples on tree and me build wall to stop","anyone who not Lalli eating lovely golden apples! Did","me not tell you I are smart?").also { stage++ } + 0 -> npc(FacialExpression.OLD_SNEAKY,"Bah! Puny humans always try steal Lalli's golden","apples! You go away now!").also { stage++ } + 1 -> player(FacialExpression.ANNOYED,"Actually, I'm not after your golden apples. I was","wondering if I could have some golden wool, I need it","to string a lyre.").also { stage++ } + 2 -> npc(FacialExpression.OLD_SNEAKY,"Ha! You not fool me human! Me am smart! Other","trolls so jealous of how brainy I are, they kick me out","of camp and make me live down here in cave! But me","have last funny!").also { stage++ } + 3 -> npc(FacialExpression.OLD_SNEAKY,"Me find golden apples on tree and me build wall to stop","anyone who not Lalli eating lovely golden apples! Did","me not tell you I are smart?").also { stage++ } 4 -> player("Yes, yes, you are incredibly clever. Now please can I","have some golden wool?").also { stage++ } - 5 -> npc(core.game.dialogue.FacialExpression.OLD_SNEAKY,"Hmm, me think you not really think I are clever. Me","think you is trying to trick Lalli. Me not like you as much","as other human. He give me present. I give him wool.").also { stage++ } + 5 -> npc(FacialExpression.OLD_SNEAKY,"Hmm, me think you not really think I are clever. Me","think you is trying to trick Lalli. Me not like you as much","as other human. He give me present. I give him wool.").also { stage++ } 6 -> options("Other human?","No, honest, you're REALLY clever.","Can I give you a present?").also { stage++ } 7 -> when(buttonId){ 1 -> player("Other human? You mean someone else has been here","and you gave them wool?").also { stage = 10 } @@ -79,29 +80,29 @@ class LalliDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin( } //Other human? - 10 -> npc(core.game.dialogue.FacialExpression.OLD_SNEAKY,"Human call itself Askeladden! It not trick Lalli. Lalli do","good deal with human! Stupid human get some dumb","wool, but did not get golden apples!").also { stage++ } + 10 -> npc(FacialExpression.OLD_SNEAKY,"Human call itself Askeladden! It not trick Lalli. Lalli do","good deal with human! Stupid human get some dumb","wool, but did not get golden apples!").also { stage++ } 11 -> player("I see... okay, well, bye!").also { player.setAttribute("/save:fremtrials:lalli-talkedto",true); stage = 1000 } //Honest, you're clever - 20 -> npc(core.game.dialogue.FacialExpression.OLD_SNEAKY,"Me no believe you tell truth. Go away.").also { stage = 1000 } + 20 -> npc(FacialExpression.OLD_SNEAKY,"Me no believe you tell truth. Go away.").also { stage = 1000 } //Can I give you a present - 30 -> npc(core.game.dialogue.FacialExpression.OLD_SNEAKY,"Me think about that.").also { stage = 1000 } + 30 -> npc(FacialExpression.OLD_SNEAKY,"Me think about that.").also { stage = 1000 } - 50 -> npc(core.game.dialogue.FacialExpression.OLD_SNEAKY,"Bah! Puny humans always try steal Lalli's golden","apples! You go away now!").also { stage++ } + 50 -> npc(FacialExpression.OLD_SNEAKY,"Bah! Puny humans always try steal Lalli's golden","apples! You go away now!").also { stage++ } 51 -> player("Wait! I have something that might interest you... a pet", "rock!").also { stage++ } - 52 -> npc(core.game.dialogue.FacialExpression.OLD_LAUGH2, "Hah! You think me stupid or something human? Me", "already got one! Me don't want lots of baby rocks either,", "so me don't want another one around the place!").also { stage++ } - 53 -> player(core.game.dialogue.FacialExpression.HALF_CRYING,"Please... all I want is some of your golden wool...").also { stage++ } - 54 -> npc(core.game.dialogue.FacialExpression.OLD_SNEAKY, "Stupid human think he can trick me into giving away", "some golden apples! Hah! Golden apples is all me got to", "eat. you not get them. no way!").also { stage++ } - 55 -> player(core.game.dialogue.FacialExpression.HALF_THINKING, "Hmm... So you're hungry? I think I will have the", "perfect thing for you to eat... I just need to get myself", "an onion, a potato, and a cabbage...").also { stage++ } + 52 -> npc(FacialExpression.OLD_LAUGH2, "Hah! You think me stupid or something human? Me", "already got one! Me don't want lots of baby rocks either,", "so me don't want another one around the place!").also { stage++ } + 53 -> player(FacialExpression.HALF_CRYING,"Please... all I want is some of your golden wool...").also { stage++ } + 54 -> npc(FacialExpression.OLD_SNEAKY, "Stupid human think he can trick me into giving away", "some golden apples! Hah! Golden apples is all me got to", "eat. you not get them. no way!").also { stage++ } + 55 -> player(FacialExpression.HALF_THINKING, "Hmm... So you're hungry? I think I will have the", "perfect thing for you to eat... I just need to get myself", "an onion, a potato, and a cabbage...").also { stage++ } 56 -> interpreter.sendDialogue("You have a cunning plan to trick this troll. You need your pet rock,", "a cabbage, a potato and an onion.").also { player.removeAttribute("fremtrials:fremtrials:lalli-talkedto");stage = 1000 } - 58 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL,"Not just yet...").also { stage = 1000 } + 58 -> playerl(FacialExpression.NEUTRAL,"Not just yet...").also { stage = 1000 } - 60 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"Indeed it is. Try it and see.").also { stage++ } - 61 -> npcl(core.game.dialogue.FacialExpression.OLD_HAPPY,"Hmm... YUM! That are delicious! Me never know human know to make soup out of stone? It some special stone?").also { stage++ } - 62 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL,"Indeed it is. But I'm willing to trade it.").also { stage++ } - 63 -> npcl(core.game.dialogue.FacialExpression.OLD_SNEAKY,"Let me think about that, me like to think.").also { + 60 -> playerl(FacialExpression.HAPPY,"Indeed it is. Try it and see.").also { stage++ } + 61 -> npcl(FacialExpression.OLD_HAPPY,"Hmm... YUM! That are delicious! Me never know human know to make soup out of stone? It some special stone?").also { stage++ } + 62 -> playerl(FacialExpression.NEUTRAL,"Indeed it is. But I'm willing to trade it.").also { stage++ } + 63 -> npcl(FacialExpression.OLD_SNEAKY,"Let me think about that, me like to think.").also { player.setAttribute("/save:lalliEatStew",true) player.removeAttribute("lalliStewOnionAdded") player.removeAttribute("lalliStewPotatoAdded") @@ -110,31 +111,31 @@ class LalliDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin( stage = 1000 } - 65 -> npcl(core.game.dialogue.FacialExpression.OLD_HAPPY,"Your soup very tasty, human! But me still not want trade golden apples for your stone. Me think pet rock get jealous.").also { stage++ } - 66 -> playerl(core.game.dialogue.FacialExpression.ANGRY,"I. DON'T. WANT. ANY. GOLDEN. APPLES. ALL. I. WANT. IS. A. GOLDEN. FLEECE.").also { stage++ } - 67 -> npcl(core.game.dialogue.FacialExpression.OLD_ALMOST_CRYING,"Gee, sorry human, all you have do is ask, me not need you to shout. You act like you think Lalli am stupid or something...").also { + 65 -> npcl(FacialExpression.OLD_HAPPY,"Your soup very tasty, human! But me still not want trade golden apples for your stone. Me think pet rock get jealous.").also { stage++ } + 66 -> playerl(FacialExpression.ANGRY,"I. DON'T. WANT. ANY. GOLDEN. APPLES. ALL. I. WANT. IS. A. GOLDEN. FLEECE.").also { stage++ } + 67 -> npcl(FacialExpression.OLD_ALMOST_CRYING,"Gee, sorry human, all you have do is ask, me not need you to shout. You act like you think Lalli am stupid or something...").also { if(player.inventory.isFull){ stage = 68 }else{ stage = 70 } } - 68 -> npcl(core.game.dialogue.FacialExpression.OLD_NORMAL,"Me would give you golden fleece if me think you have enough room in your inventory.").also { stage = 1000 } - 70 -> npcl(core.game.dialogue.FacialExpression.OLD_HAPPY,"Here you go. Hah! Me trick you Human! All you got is worthless golden fleece! Me got very rare soup-making stone!").also { stage = 71 } - 71 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"Glad you're happy Lalli!").also { + 68 -> npcl(FacialExpression.OLD_NORMAL,"Me would give you golden fleece if me think you have enough room in your inventory.").also { stage = 1000 } + 70 -> npcl(FacialExpression.OLD_HAPPY,"Here you go. Hah! Me trick you Human! All you got is worthless golden fleece! Me got very rare soup-making stone!").also { stage = 71 } + 71 -> playerl(FacialExpression.HAPPY,"Glad you're happy Lalli!").also { player.inventory.add(Item(Items.GOLDEN_FLEECE_3693)) player.setAttribute("/save:hasWool",true) player.removeAttribute("lalliEatStew") stage = 1000 } - 75 -> npcl(core.game.dialogue.FacialExpression.OLD_HAPPY,"Hah! How you like worthless golden fleece! Me very like rare soup-making stone!").also { stage++ } - 76 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"Oh it's great, thank you again for the fleece Lalli, I hope you're enjoying your soup").also { stage = 1000 } + 75 -> npcl(FacialExpression.OLD_HAPPY,"Hah! How you like worthless golden fleece! Me very like rare soup-making stone!").also { stage++ } + 76 -> playerl(FacialExpression.HAPPY,"Oh it's great, thank you again for the fleece Lalli, I hope you're enjoying your soup").also { stage = 1000 } - 100 -> npcl(core.game.dialogue.FacialExpression.OLD_HAPPY,"Ha! Stupid human! You no get any golden apples! Even though me much like stone soup!").also { stage++ } - 101 -> playerl(core.game.dialogue.FacialExpression.ASKING,"Can I please have some more golden wool?").also { stage++ } - 102 -> npcl(core.game.dialogue.FacialExpression.OLD_HAPPY,"Me will sell you some golden wool, but me no sell you any golden apples!").also { stage++ } - 103 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"How much for the golden wool?").also { stage++ } - 104 -> npcl(core.game.dialogue.FacialExpression.OLD_HAPPY,"Me want 1000 coins! You pay or go!").also { + 100 -> npcl(FacialExpression.OLD_HAPPY,"Ha! Stupid human! You no get any golden apples! Even though me much like stone soup!").also { stage++ } + 101 -> playerl(FacialExpression.ASKING,"Can I please have some more golden wool?").also { stage++ } + 102 -> npcl(FacialExpression.OLD_HAPPY,"Me will sell you some golden wool, but me no sell you any golden apples!").also { stage++ } + 103 -> playerl(FacialExpression.HAPPY,"How much for the golden wool?").also { stage++ } + 104 -> npcl(FacialExpression.OLD_HAPPY,"Me want 1000 coins! You pay or go!").also { stage = if(inInventory(player,Items.COINS_995,1000)){ 105 } @@ -142,17 +143,17 @@ class LalliDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin( } 105 -> options("Pay","Don't pay").also { stage++ } 106 -> when(buttonId){ - 1 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"Is that all? Here you go!").also { + 1 -> playerl(FacialExpression.HAPPY,"Is that all? Here you go!").also { removeItem(player,Item(Items.COINS_995,1000)) addItem(player,Items.GOLDEN_FLEECE_3693) stage++ } - 2 -> npcl(core.game.dialogue.FacialExpression.OLD_HAPPY,"You no pay, then you go! You no get any golden apples!").also { stage = 1000 } + 2 -> npcl(FacialExpression.OLD_HAPPY,"You no pay, then you go! You no get any golden apples!").also { stage = 1000 } } - 107 -> npcl(core.game.dialogue.FacialExpression.OLD_HAPPY,"Ha! Stupid human think he buy golden apples, but he actually get stupid wool! Hahaha!").also { stage = 1000 } + 107 -> npcl(FacialExpression.OLD_HAPPY,"Ha! Stupid human think he buy golden apples, but he actually get stupid wool! Hahaha!").also { stage = 1000 } - 115 -> playerl(core.game.dialogue.FacialExpression.THINKING,"I don't even have that much cash on me...").also { stage++ } - 116 -> npcl(core.game.dialogue.FacialExpression.OLD_NORMAL,"Well stupid bad trade human don't get no wool then! Or any golden apples!").also { stage = 1000 } + 115 -> playerl(FacialExpression.THINKING,"I don't even have that much cash on me...").also { stage++ } + 116 -> npcl(FacialExpression.OLD_NORMAL,"Well stupid bad trade human don't get no wool then! Or any golden apples!").also { stage = 1000 } 1000 -> end() } 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 7f734bc1f..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 @@ -51,16 +53,16 @@ class ManniDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin( return true } else { npc("Come back when you're ready to begin", "the contest.") - stage = 1000; + stage = 1000 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 8a73499eb..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,36 +5,37 @@ 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) : core.game.dialogue.DialoguePlugin(player){ +class OlafTheBard(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { if(player.inventory.contains(3700,1)){ - playerl(core.game.dialogue.FacialExpression.HAPPY,"Hello Olaf. Do you have a beautiful love song written for me?") + playerl(FacialExpression.HAPPY,"Hello Olaf. Do you have a beautiful love song written for me?") stage = 65 } else if(player.inventory.contains(3699,1)){ - playerl(core.game.dialogue.FacialExpression.ASKING,"So you think this song is pretty good then?") + playerl(FacialExpression.ASKING,"So you think this song is pretty good then?") stage = 70 return true } else if(player?.getAttribute("sigmundreturning",false) == true){ - playerl(core.game.dialogue.FacialExpression.ASKING,"I've got a trade item; is it for you?") + playerl(FacialExpression.ASKING,"I've got a trade item; is it for you?") stage = 75 return true } else if(player?.getAttribute("sigmund-steps",0) == 3){ - playerl(core.game.dialogue.FacialExpression.ASKING,"I don't suppose you have any idea where I could find some custom sturdy boots, do you?") + playerl(FacialExpression.ASKING,"I don't suppose you have any idea where I could find some custom sturdy boots, do you?") stage = 60 return true } else if(player?.getAttribute("sigmund-steps",0)!! == 2){ - playerl(core.game.dialogue.FacialExpression.ASKING,"I don't suppose you have any idea where I could find a love ballad, do you?") + playerl(FacialExpression.ASKING,"I don't suppose you have any idea where I could find a love ballad, do you?") stage = 50 return true } else if(player?.getAttribute("lyreConcertPlayed",false)!!){ - playerl(core.game.dialogue.FacialExpression.HAPPY,"So can I rely on your vote with the council of elders in my favour?") + playerl(FacialExpression.HAPPY,"So can I rely on your vote with the council of elders in my favour?") stage = 40 return true } @@ -43,18 +44,18 @@ class OlafTheBard(player: Player? = null) : core.game.dialogue.DialoguePlugin(pl stage = 1000 return true } - else if(player.questRepository.isComplete("Fremennik Trials")){ - npcl(core.game.dialogue.FacialExpression.HAPPY,"Hello again to you, ${player.getAttribute("fremennikname","schlonko")}. Us bards should stick together, what can I do for you?") + 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 } else{ - playerl(core.game.dialogue.FacialExpression.HAPPY,"Hello there. So you're a bard?") + playerl(FacialExpression.HAPPY,"Hello there. So you're a bard?") stage = 150 return true } @@ -99,9 +100,9 @@ class OlafTheBard(player: Player? = null) : core.game.dialogue.DialoguePlugin(pl 28 -> npc("Is that clear enough, outerlander? Would you like me","to repeat anything?").also { stage++ } 29 -> player("No thanks. I think I've got it.").also { stage = 1000 } - 40 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"You have a truly poetic soul! Anyone who can compose such a beautiful epic, and then perform it so flawlessly can only bring good to our clan!").also { stage++ } - 41 -> playerl(core.game.dialogue.FacialExpression.THINKING,"Erm... so that's a yes, then?").also { stage++ } - 42 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Absolutely! We must collaborate together on a duet sometime, don't you think?").also { + 40 -> npcl(FacialExpression.HAPPY,"You have a truly poetic soul! Anyone who can compose such a beautiful epic, and then perform it so flawlessly can only bring good to our clan!").also { stage++ } + 41 -> playerl(FacialExpression.THINKING,"Erm... so that's a yes, then?").also { stage++ } + 42 -> npcl(FacialExpression.HAPPY,"Absolutely! We must collaborate together on a duet sometime, don't you think?").also { setAttribute(player, "/save:fremtrials:olaf-vote",true) setAttribute(player, "/save:fremtrials:votes",getAttribute(player, "fremtrials:votes",0) + 1) stage = 1000 @@ -109,53 +110,53 @@ class OlafTheBard(player: Player? = null) : core.game.dialogue.DialoguePlugin(pl //Sigmund bullshit - 50 -> npcl(core.game.dialogue.FacialExpression.HAPPY," Well, as official Fremennik bard, it falls within my remit to compose all music for the tribe. I am fully versed in all the various types of romantic music.").also { stage++ } - 51 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"Great! Can you write me one then?").also { stage++ } - 52 -> npcl(core.game.dialogue.FacialExpression.THINKING,"Well... Normally I would be thrilled at the chance to show my skill as a poet in composing a seductively romantic ballad...").also { stage++ } - 53 -> playerl(core.game.dialogue.FacialExpression.ANNOYED,"Let me guess; Here comes the 'but'.").also { stage++ } - 54 -> npcl(core.game.dialogue.FacialExpression.SAD,"...but unfortunately I cannot concentrate fully upon my work recently.").also { stage++ } - 55 -> playerl(core.game.dialogue.FacialExpression.ASKING,"Why is that then?").also { stage++ } - 56 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL,"It is these old worn out shoes of mine... As a bard I am expected to wander the lands, singing of the glorious battles of our warriors.").also { stage++ } - 57 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL,"If you can find me a pair of sturdy boots to replace these old worn out ones of mine, I will be happy to spend the time on composing you a romantic ballad.").also { + 50 -> npcl(FacialExpression.HAPPY," Well, as official Fremennik bard, it falls within my remit to compose all music for the tribe. I am fully versed in all the various types of romantic music.").also { stage++ } + 51 -> playerl(FacialExpression.HAPPY,"Great! Can you write me one then?").also { stage++ } + 52 -> npcl(FacialExpression.THINKING,"Well... Normally I would be thrilled at the chance to show my skill as a poet in composing a seductively romantic ballad...").also { stage++ } + 53 -> playerl(FacialExpression.ANNOYED,"Let me guess; Here comes the 'but'.").also { stage++ } + 54 -> npcl(FacialExpression.SAD,"...but unfortunately I cannot concentrate fully upon my work recently.").also { stage++ } + 55 -> playerl(FacialExpression.ASKING,"Why is that then?").also { stage++ } + 56 -> npcl(FacialExpression.NEUTRAL,"It is these old worn out shoes of mine... As a bard I am expected to wander the lands, singing of the glorious battles of our warriors.").also { stage++ } + 57 -> npcl(FacialExpression.NEUTRAL,"If you can find me a pair of sturdy boots to replace these old worn out ones of mine, I will be happy to spend the time on composing you a romantic ballad.").also { player?.incrementAttribute("sigmund-steps",1) stage = 1000 } - 60 -> npcl(core.game.dialogue.FacialExpression.ANNOYED,"I'm sorry outerlander... If I did, I would not trouble you to go and find them for me, would I?").also { stage = 1000 } + 60 -> npcl(FacialExpression.ANNOYED,"I'm sorry outerlander... If I did, I would not trouble you to go and find them for me, would I?").also { stage = 1000 } - 65 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"That depends outerlander... Do you have some new boots for me? My feet get so tired roaming the land...").also { stage++ } - 66 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"As a matter of fact - I do!").also { + 65 -> npcl(FacialExpression.HAPPY,"That depends outerlander... Do you have some new boots for me? My feet get so tired roaming the land...").also { stage++ } + 66 -> playerl(FacialExpression.HAPPY,"As a matter of fact - I do!").also { removeItem(player,3700) addItem(player,3699) stage++ } - 67 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Oh! Superb! Those are great! They're just what I was looking for! Here, take this song with my compliments! It is one of my finest works yet!").also { stage = 1000 } + 67 -> npcl(FacialExpression.HAPPY,"Oh! Superb! Those are great! They're just what I was looking for! Here, take this song with my compliments! It is one of my finest works yet!").also { stage = 1000 } - 70 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Ahhh.... outerlander... it is the most beautiful romantic ballad I have ever been inspired to write...").also { stage++ } - 71 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Only a woman with a heart of icy stone could fail be to be moved by its beauty!").also { stage++ } - 72 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"Thanks! That sounds perfect!").also { stage = 1000 } + 70 -> npcl(FacialExpression.HAPPY,"Ahhh.... outerlander... it is the most beautiful romantic ballad I have ever been inspired to write...").also { stage++ } + 71 -> npcl(FacialExpression.HAPPY,"Only a woman with a heart of icy stone could fail be to be moved by its beauty!").also { stage++ } + 72 -> playerl(FacialExpression.HAPPY,"Thanks! That sounds perfect!").also { stage = 1000 } - 75 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Only if it's a new pair of boots.").also { stage = 1000 } + 75 -> npcl(FacialExpression.HAPPY,"Only if it's a new pair of boots.").also { stage = 1000 } 98 -> options("I was wondering...","I forget now...").also { stage++ } 99 -> when(buttonId){ - 1 -> playerl(core.game.dialogue.FacialExpression.ASKING,"I was wondering... Can I learn to play my lyre again?").also { stage++ } - 2 -> playerl(core.game.dialogue.FacialExpression.THINKING,"I forget now...").also { stage = 1000 } + 1 -> playerl(FacialExpression.ASKING,"I was wondering... Can I learn to play my lyre again?").also { stage++ } + 2 -> playerl(FacialExpression.THINKING,"I forget now...").also { stage = 1000 } } - 100 -> npcl(core.game.dialogue.FacialExpression.HAPPY, "Well that is an interesting question. Let me let you into a little secret. If you make another offering to the Fossegrimen you will learn a secret melody.").also { stage++ } - 101 -> playerl(core.game.dialogue.FacialExpression.ASKING,"What kind of melody?").also { stage++ } - 102 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"It is the song of Rellekka. When you play it, it will bring you back to this town where you are in this world.").also { stage++ } - 103 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"We often go adventuring with bards for precisely this reason. No matter where we have ended up we can return safe and sound to home.").also { stage++ } - 104 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"Thanks, Olaf!").also { stage = 1000 } + 100 -> npcl(FacialExpression.HAPPY, "Well that is an interesting question. Let me let you into a little secret. If you make another offering to the Fossegrimen you will learn a secret melody.").also { stage++ } + 101 -> playerl(FacialExpression.ASKING,"What kind of melody?").also { stage++ } + 102 -> npcl(FacialExpression.HAPPY,"It is the song of Rellekka. When you play it, it will bring you back to this town where you are in this world.").also { stage++ } + 103 -> npcl(FacialExpression.HAPPY,"We often go adventuring with bards for precisely this reason. No matter where we have ended up we can return safe and sound to home.").also { stage++ } + 104 -> playerl(FacialExpression.HAPPY,"Thanks, Olaf!").also { stage = 1000 } - 150 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"I am afraid I cannot speak to outerlanders. Besides, I am busy composing an epic.").also { stage = 1000 } + 150 -> npcl(FacialExpression.HAPPY,"I am afraid I cannot speak to outerlanders. Besides, I am busy composing an epic.").also { stage = 1000 } 1000 -> end() } return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return OlafTheBard(player) } 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 d464c4500..58714b206 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 @@ -2,16 +2,15 @@ package content.region.fremennik.rellekka.quest.thefremenniktrials import core.api.addItem import core.api.dumpContainer -import core.api.questStage +import core.api.getQuestStage import core.api.removeItem -import core.game.dialogue.DialoguePlugin -import core.game.dialogue.FacialExpression 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 @Initializable class PeerTheSeerDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { @@ -66,22 +65,22 @@ class PeerTheSeerDialogue(player: Player? = null) : core.game.dialogue.DialogueP stage = 110 return true } - else if(player.getAttribute("fremtrials:peer-vote",false)){ + else if (getQuestStage(player, Quests.THE_FREMENNIK_TRIALS) > 0 && player.getAttribute("fremtrials:peer-vote",false)) { npcl(core.game.dialogue.FacialExpression.SAD,"Uuuh... What was that dark presence I felt?") stage = 120 return true } - else if(player.questRepository.isComplete("Fremennik Trials")){ + else if(player.questRepository.isComplete(Quests.THE_FREMENNIK_TRIALS)){ npcl(core.game.dialogue.FacialExpression.SAD,"Uuuh... What was that dark presence I felt?") stage = 150 return true } - else if(player.questRepository.hasStarted("Fremennik Trials")){ + else if(getQuestStage(player, Quests.THE_FREMENNIK_TRIALS) > 0){ npcl(core.game.dialogue.FacialExpression.SAD,"Uuuh... What was that dark presence I felt?") stage = 50 return true } - if (questStage(player, "Fremennik Trials") == 0) { + if (getQuestStage(player, Quests.THE_FREMENNIK_TRIALS) == 0) { npc(core.game.dialogue.FacialExpression.SAD,"Uuuh... What was that dark presence I felt?").also { stage = 300 } } return true @@ -212,7 +211,7 @@ class PeerTheSeerDialogue(player: Player? = null) : core.game.dialogue.DialogueP 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 } - //After Fremennik Trials + //After The 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)){ 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 44391fd20..f214c66d2 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 @@ -6,19 +6,20 @@ import core.game.node.item.Item import core.plugin.Initializable import core.tools.END_DIALOGUE import core.tools.START_DIALOGUE +import content.data.Quests @Initializable -class PoisonSalesman(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { +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.isComplete(Quests.MURDER_MYSTERY) + val fremennikTrialsStage = player.questRepository.getStage(Quests.THE_FREMENNIK_TRIALS) when (stage) { START_DIALOGUE -> when (buttonId) { @@ -26,7 +27,7 @@ class PoisonSalesman(player: Player? = null) : core.game.dialogue.DialoguePlugin 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 } @@ -41,7 +42,7 @@ class PoisonSalesman(player: Player? = null) : core.game.dialogue.DialoguePlugin } } 11 -> { player("Didn't you used to sell poison?"); stage++ } - 12 -> { npc("That I did indeed! Peter Potter's Patented","Multipurpose poison! A miracle of modern apothecarys!","My exclusive concoction has been test on..."); stage++ } + 12 -> { npc("That I did indeed! Peter Potter's Patented","Multipurpose poison! A miracle of modern apothecarys!","My exclusive concoction has been tested on..."); stage++ } 13 -> { player("Uh, yeah. I've already heard the sales pitch."); stage++ } 14 -> { npc("Sorry stranger, old habits die hard I guess."); stage++ } 15 -> { player("So you don't sell poison anymore?"); stage++ } @@ -92,7 +93,7 @@ class PoisonSalesman(player: Player? = null) : core.game.dialogue.DialoguePlugin return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return PoisonSalesman(player) } 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 cbf3ed8ff..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 @@ -11,8 +12,11 @@ import core.game.world.map.zone.ZoneBorders import core.game.world.update.flag.context.Animation import org.rs09.consts.Items import core.game.dialogue.DialogueFile +import core.game.global.action.DoorActionHandler import core.game.interaction.InteractionListener import core.game.interaction.IntType +import org.rs09.consts.NPCs +import org.rs09.consts.Sounds class SeersHouseListeners : InteractionListener { @@ -99,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 @@ -107,14 +111,16 @@ class SeersHouseListeners : InteractionListener { else if(player.getAttribute("PeerRiddle",5) < 5){ player.dialogueInterpreter.open(DoorRiddleDialogue(player), Scenery(WESTDOOR,node.location)) } - else if(player.getAttribute("riddlesolved",false)){ - if(player.location.x == 2631){ + else if(player.getAttribute("riddlesolved",false)) { + val insideHouse = (player.location == Location.create(2631, 3666, 0)) + if(insideHouse) { + DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) player.inventory.clear() - core.game.global.action.DoorActionHandler.handleAutowalkDoor(player,node.asScenery()) - }else{ - core.game.global.action.DoorActionHandler.handleAutowalkDoor(player,node.asScenery()) + } else if(player.inventory.isEmpty && player.equipment.isEmpty) { + DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) + } else { + openDialogue(player, NPCs.PEER_THE_SEER_1288, findNPC(NPCs.PEER_THE_SEER_1288)!!) } - core.game.global.action.DoorActionHandler.handleAutowalkDoor(player,node.asScenery()) } return@on true } @@ -265,7 +271,7 @@ class SeersHouseListeners : InteractionListener { onUseWith(IntType.SCENERY,REDHERRING,COOKINGRANGE) { player, used, with -> player.animate(Animation(883,Animator.Priority.HIGH)) - player.audioManager.send(Audio(2577, 1, 1)) + playAudio(player, Sounds.FRY_2577) removeItem(player,REDHERRING) addItem(player,REDGOOP) sendDialogue(player,"As you cook the herring on the stove, the colouring on it peels off separately as a red sticky goop...") @@ -873,7 +879,7 @@ class SeersHouseListeners : InteractionListener { onUseWith(IntType.SCENERY, FULLVASE, FROZENTABLE) { player, used, with -> player.animate(Animation(883,Animator.Priority.HIGH)) - removeItem(player,VASE) + removeItem(player,FULLVASE) addItem(player,FROZENVASE) sendMessage(player,"The icy table immediately freezes the water in your vase.") return@onUseWith true @@ -890,7 +896,7 @@ class SeersHouseListeners : InteractionListener { onUseWith(IntType.SCENERY, FROZENBUCKET, COOKINGRANGE) { player, used, with -> player.animate(Animation(883,Animator.Priority.HIGH)) - player.audioManager.send(Audio(2577, 1, 1)) + playAudio(player, Sounds.FRY_2577) removeItem(player,FROZENBUCKET) addItem(player,EMPTYBUCKET) sendMessage(player,"You place the frozen bucket on the range. The ice turns to steam.") @@ -899,7 +905,7 @@ class SeersHouseListeners : InteractionListener { onUseWith(IntType.SCENERY, FROZENJUG, COOKINGRANGE) { player, used, with -> player.animate(Animation(883,Animator.Priority.HIGH)) - player.audioManager.send(Audio(2577, 1, 1)) + playAudio(player, Sounds.FRY_2577) removeItem(player,FROZENJUG) addItem(player,EMPTYJUG) sendMessage(player,"You place the frozen jug on the range. The ice turns to steam.") @@ -908,7 +914,7 @@ class SeersHouseListeners : InteractionListener { onUseWith(IntType.SCENERY, FROZENVASE, COOKINGRANGE) { player, used, with -> player.animate(Animation(883,Animator.Priority.HIGH)) - player.audioManager.send(Audio(2577, 1, 1)) + playAudio(player, Sounds.FRY_2577) removeItem(player,FROZENVASE) addItem(player,VASE) sendMessage(player,"You place the frozen vase on the range. The ice turns into steam.") @@ -917,7 +923,7 @@ class SeersHouseListeners : InteractionListener { onUseWith(IntType.SCENERY, FROZENKEY, COOKINGRANGE) { player, used, with -> player.animate(Animation(883,Animator.Priority.HIGH)) - player.audioManager.send(Audio(2577, 1, 1)) + playAudio(player, Sounds.FRY_2577) removeItem(player,FROZENKEY) addItem(player,SEERSKEY) sendMessage(player,"The heat of the range melts the ice around the key.") @@ -928,7 +934,7 @@ class SeersHouseListeners : InteractionListener { if(player.inventory.contains(SEERSKEY,1)){ player.setAttribute("/save:housepuzzlesolved",true) player.inventory.clear() - core.game.global.action.DoorActionHandler.handleAutowalkDoor(player,node.asScenery()) + DoorActionHandler.handleAutowalkDoor(player,node.asScenery()) player.setAttribute("/save:fremtrials:peer-vote",true) player.setAttribute("/save:fremtrials:votes",player.getAttribute("fremtrials:votes",0) + 1) sendNPCDialogue(player,1288,"Incredible! To have solved my puzzle so quickly! I have no choice but to vote in your favour!") 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 b5232819b..3ccd4e421 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,43 +1,44 @@ 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 @Initializable -class SigliTheHuntsman(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class SigliTheHuntsman(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { if(player?.inventory?.contains(3702,1) == true){ - npcl(core.game.dialogue.FacialExpression.HAPPY,"Greetings outerlander.") + npcl(FacialExpression.HAPPY,"Greetings outerlander.") stage = 165 return true } else if(player?.inventory?.contains(3701,1) == true){ - playerl(core.game.dialogue.FacialExpression.ASKING,"So you really don't mind giving this away to me?") + playerl(FacialExpression.ASKING,"So you really don't mind giving this away to me?") stage = 170 return true } else if(player?.getAttribute("sigmundreturning",false) == true){ - playerl(core.game.dialogue.FacialExpression.ASKING,"I have an item to trade.") + playerl(FacialExpression.ASKING,"I have an item to trade.") stage = 175 return true } if(player?.getAttribute("sigmund-steps", 0) == 6){ - npcl(core.game.dialogue.FacialExpression.HAPPY,"Greetings outerlander.") + npcl(FacialExpression.HAPPY,"Greetings outerlander.") stage = 160 return true } else if(player?.getAttribute("sigmund-steps", 0) == 5){ - npcl(core.game.dialogue.FacialExpression.HAPPY,"Greetings outerlander.") + npcl(FacialExpression.HAPPY,"Greetings outerlander.") 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,18 +48,18 @@ class SigliTheHuntsman(player: Player? = null) : core.game.dialogue.DialoguePlug stage = 100 return true } - else if(player.questRepository.isComplete("Fremennik Trials")){ - playerl(core.game.dialogue.FacialExpression.HAPPY,"Hello again Sigli.") + 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 } else{ - npcl(core.game.dialogue.FacialExpression.HAPPY,"I do not speak to outerlanders. If you have anything of import to say, go and speak to the Chieftain.") + npcl(FacialExpression.HAPPY,"I do not speak to outerlanders. If you have anything of import to say, go and speak to the Chieftain.") stage = 1000 return true } @@ -103,50 +104,50 @@ class SigliTheHuntsman(player: Player? = null) : core.game.dialogue.DialoguePlug //Draugen killed 100 -> player("Thanks!").also { - player.removeAttribute("fremtrials:draugen-killed"); - player.setAttribute("/save:fremtrials:sigli-vote",true) + 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 } - 150 -> playerl(core.game.dialogue.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(core.game.dialogue.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(core.game.dialogue.FacialExpression.ASKING,"No, I guess not. So can I have it?").also { stage++ } - 153 -> npcl(core.game.dialogue.FacialExpression.ANNOYED,"Directions to my hunting grounds could mean the end of my livelihood. The only way I would be prepared to give them up would be...").also { stage++ } - 154 -> playerl(core.game.dialogue.FacialExpression.THINKING,"What? Power? Money? Women? Wine?").also { stage++ } - 155 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"...a new string for my hunting bow. Not just any bowstring; I need a custom bowstring, balanced for my bow precisely to keep my hunt competitive.").also { stage++ } - 156 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Only in this way would I allow the knowledge of my hunting grounds to be passed on to strangers.").also { stage++ } - 157 -> playerl(core.game.dialogue.FacialExpression.THINKING,"So where would I get that?").also { stage++ } - 158 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"I have no idea. But then again, I'm happy with my old bowstring and being the only person who knows where my hunting ground is.").also { + 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++ } + 153 -> npcl(FacialExpression.ANNOYED,"Directions to my hunting grounds could mean the end of my livelihood. The only way I would be prepared to give them up would be...").also { stage++ } + 154 -> playerl(FacialExpression.THINKING,"What? Power? Money? Women? Wine?").also { stage++ } + 155 -> npcl(FacialExpression.HAPPY,"...a new string for my hunting bow. Not just any bowstring; I need a custom bowstring, balanced for my bow precisely to keep my hunt competitive.").also { stage++ } + 156 -> npcl(FacialExpression.HAPPY,"Only in this way would I allow the knowledge of my hunting grounds to be passed on to strangers.").also { stage++ } + 157 -> playerl(FacialExpression.THINKING,"So where would I get that?").also { stage++ } + 158 -> npcl(FacialExpression.HAPPY,"I have no idea. But then again, I'm happy with my old bowstring and being the only person who knows where my hunting ground is.").also { player?.incrementAttribute("sigmund-steps",1) stage = 1000 } - 160 -> playerl(core.game.dialogue.FacialExpression.ASKING,"I don't suppose you have any idea where I could find a finely balanced custom bowstring, do you?").also { stage++ } - 161 -> npcl(core.game.dialogue.FacialExpression.ANNOYED,"If I knew I would not have asked you to go and get me one, now would I?").also { stage = 1000 } + 160 -> playerl(FacialExpression.ASKING,"I don't suppose you have any idea where I could find a finely balanced custom bowstring, do you?").also { stage++ } + 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(core.game.dialogue.FacialExpression.HAPPY,"Here. I have your bowstring. Give me your map to the hunting grounds.").also { + 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++ } - 166 -> npcl(core.game.dialogue.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 } + 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 } - 170 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"No outerlander... it is hard to explain. That map makes my role as huntsman too easy. I fear my skills are becoming dulled.").also { stage++ } - 171 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Now I must track my prey once more. To begin again from scratch... I feel this may keep me sharp.").also { stage = 1000 } + 170 -> npcl(FacialExpression.HAPPY,"No outerlander... it is hard to explain. That map makes my role as huntsman too easy. I fear my skills are becoming dulled.").also { stage++ } + 171 -> npcl(FacialExpression.HAPPY,"Now I must track my prey once more. To begin again from scratch... I feel this may keep me sharp.").also { stage = 1000 } - 175 -> npcl(core.game.dialogue.FacialExpression.ANNOYED,"Not the one I want, outerlander.").also { stage = 1000 } + 175 -> npcl(FacialExpression.ANNOYED,"Not the one I want, outerlander.").also { stage = 1000 } - 180 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Hello again to you ${player.getAttribute("fremennikname","spinky")}. How goes the hunt?").also { stage++ } - 181 -> playerl(core.game.dialogue.FacialExpression.ASKING,"They hunt? I've already done your task! That's why I'm a Fremennik now!").also { stage++ } - 182 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"You think so do you? The Draugen is always out there, just as I and my hunters are always on its trail to keep it at bay. The hunt will never end until I do.").also { stage = 1000 } + 180 -> npcl(FacialExpression.HAPPY,"Hello again to you ${player.getAttribute("fremennikname","spinky")}. How goes the hunt?").also { stage++ } + 181 -> playerl(FacialExpression.ASKING,"They hunt? I've already done your task! That's why I'm a Fremennik now!").also { stage++ } + 182 -> npcl(FacialExpression.HAPPY,"You think so do you? The Draugen is always out there, just as I and my hunters are always on its trail to keep it at bay. The hunt will never end until I do.").also { stage = 1000 } 1000 -> end() } return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return SigliTheHuntsman(player) } 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 6e345f2c6..b4804f0d2 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,9 +9,10 @@ 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 -class SigmundDialogue (player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { +class SigmundDialogue (player: Player? = null) : DialoguePlugin(player) { private val QUEST_ITEMS = intArrayOf(3709,3707,3706,3710,3705,3704,3703,3702,3701,3708,3700,3699,3698) @@ -22,38 +24,38 @@ class SigmundDialogue (player: Player? = null) : core.game.dialogue.DialoguePlug override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - if(player.questRepository.isComplete("Fremennik Trials")){ - playerl(core.game.dialogue.FacialExpression.HAPPY,"Hello there!") + if(player.questRepository.isComplete(Quests.THE_FREMENNIK_TRIALS)){ + playerl(FacialExpression.HAPPY,"Hello there!") stage = 50 return true } - else if(!player.questRepository.hasStarted("Fremennik Trials")){ - playerl(core.game.dialogue.FacialExpression.HAPPY,"Hello there!") + else if (getQuestStage(player, Quests.THE_FREMENNIK_TRIALS) <= 0) { + playerl(FacialExpression.HAPPY,"Hello there!") stage = 60 return true } if(player?.inventory?.contains(3698,1) == true){ - playerl(core.game.dialogue.FacialExpression.HAPPY,"Here's that flower you wanted.") + playerl(FacialExpression.HAPPY,"Here's that flower you wanted.") stage = 30 return true } else if(player?.getAttribute("sigmundreturning",false) == true && !player.inventory.containsAtLeastOneItem(QUEST_ITEMS)){ - npcl(core.game.dialogue.FacialExpression.ASKING,"So... how goes it outerlander? Did you manage to obtain my flower for me yet? Or do you lack the necessary merchanting skills?") + npcl(FacialExpression.ASKING,"So... how goes it outerlander? Did you manage to obtain my flower for me yet? Or do you lack the necessary merchanting skills?") stage = 35 return true } if(player?.getAttribute("sigmund-started",false)!!){ - playerl(core.game.dialogue.FacialExpression.HAPPY,"Hello there!") + playerl(FacialExpression.HAPPY,"Hello there!") stage = 25 return true } - else if(!player?.getAttribute("fremtrials:sigmund-vote",false)!!){ - playerl(core.game.dialogue.FacialExpression.HAPPY,"Hello there!") + else if (getQuestStage(player, Quests.THE_FREMENNIK_TRIALS) > 0 && !player?.getAttribute("fremtrials:sigmund-vote",false)!!) { + playerl(FacialExpression.HAPPY,"Hello there!") stage = 1 return true } else if(player?.getAttribute("fremtrials:sigmund-vote",false) == true){ - playerl(core.game.dialogue.FacialExpression.HAPPY,"Hello there!") + playerl(FacialExpression.HAPPY,"Hello there!") stage = 40 return true } @@ -62,38 +64,38 @@ class SigmundDialogue (player: Player? = null) : core.game.dialogue.DialoguePlug override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 1 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL,"Hello outlander.").also { stage++ } - 2 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL,"Are you a member of the council?").also { stage++ } - 3 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL,"That I am outlander; it is a position that brings my family and I pride.").also { stage++ } - 4 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL,"I was wondering if I can count on your vote at the council of elders?").also { stage++ } - 5 -> npcl(core.game.dialogue.FacialExpression.THINKING,"You wish to become a Fremennik? I may be persuaded to swing my vote to your favor, but you will first need to do a little task for me.").also { stage++ } - 6 -> playerl(core.game.dialogue.FacialExpression.ANNOYED,"How did I know it wouldn't be that simple for your vote?").also { stage++ } - 7 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL,"Calm yourself outerlander. It is but a small task really... I simply require a flower.").also { stage++ } - 8 -> playerl(core.game.dialogue.FacialExpression.ASKING,"A flower? What's the catch?").also { stage++ } - 9 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL,"The catch? Well, it is not just any flower. Someone in this town has an extremely unique flower from a far off land that they picked up on their travels.").also { stage++ } - 10 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL,"I would like you to demonstrate your merchanting skills to me by persuading them to part with it, and then give it to me for my vote.").also { stage++ } - 11 -> playerl(core.game.dialogue.FacialExpression.THINKING,"Well... I guess that doesn't sound too hard.").also { stage++ } - 12 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Excellent! You will obtain this flower for me, then?").also { stage++ } + 1 -> npcl(FacialExpression.NEUTRAL,"Hello outlander.").also { stage++ } + 2 -> playerl(FacialExpression.NEUTRAL,"Are you a member of the council?").also { stage++ } + 3 -> npcl(FacialExpression.NEUTRAL,"That I am outlander; it is a position that brings my family and I pride.").also { stage++ } + 4 -> playerl(FacialExpression.NEUTRAL,"I was wondering if I can count on your vote at the council of elders?").also { stage++ } + 5 -> npcl(FacialExpression.THINKING,"You wish to become a Fremennik? I may be persuaded to swing my vote to your favor, but you will first need to do a little task for me.").also { stage++ } + 6 -> playerl(FacialExpression.ANNOYED,"How did I know it wouldn't be that simple for your vote?").also { stage++ } + 7 -> npcl(FacialExpression.NEUTRAL,"Calm yourself outerlander. It is but a small task really... I simply require a flower.").also { stage++ } + 8 -> playerl(FacialExpression.ASKING,"A flower? What's the catch?").also { stage++ } + 9 -> npcl(FacialExpression.NEUTRAL,"The catch? Well, it is not just any flower. Someone in this town has an extremely unique flower from a far off land that they picked up on their travels.").also { stage++ } + 10 -> npcl(FacialExpression.NEUTRAL,"I would like you to demonstrate your merchanting skills to me by persuading them to part with it, and then give it to me for my vote.").also { stage++ } + 11 -> playerl(FacialExpression.THINKING,"Well... I guess that doesn't sound too hard.").also { stage++ } + 12 -> npcl(FacialExpression.HAPPY,"Excellent! You will obtain this flower for me, then?").also { stage++ } 13 -> options("Yes","No").also { stage++ } 14 -> when(buttonId){ - 1 -> playerl(core.game.dialogue.FacialExpression.ASKING,"Okay. I don't think this will be too difficult. Any suggestions on where to start looking for this flower?").also { + 1 -> playerl(FacialExpression.ASKING,"Okay. I don't think this will be too difficult. Any suggestions on where to start looking for this flower?").also { player?.setAttribute("/save:sigmund-started",true) player?.setAttribute("/save:sigmund-steps",1) stage++ } - 2 -> playerl(core.game.dialogue.FacialExpression.ANNOYED,"You know what? No. This all sounds like a lot of hassle to me, and frankly I just can't be bothered with it right now. I'll go get someone else to vote for me.").also { stage = 20 } + 2 -> playerl(FacialExpression.ANNOYED,"You know what? No. This all sounds like a lot of hassle to me, and frankly I just can't be bothered with it right now. I'll go get someone else to vote for me.").also { stage = 20 } } - 15 -> npcl(core.game.dialogue.FacialExpression.THINKING,"Ah, well outerlander, if I knew where to start looking I would simply do it myself!").also { stage++ } - 16 -> playerl(core.game.dialogue.FacialExpression.ANNOYED,"No help at ALL?").also { stage++ } - 17 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL,"We are a very insular clan, so I would not expect you to have to leave this town to find whatever you need.").also { stage = 1000 } + 15 -> npcl(FacialExpression.THINKING,"Ah, well outerlander, if I knew where to start looking I would simply do it myself!").also { stage++ } + 16 -> playerl(FacialExpression.ANNOYED,"No help at ALL?").also { stage++ } + 17 -> npcl(FacialExpression.NEUTRAL,"We are a very insular clan, so I would not expect you to have to leave this town to find whatever you need.").also { stage = 1000 } //No - 20 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL,"As you wish outlander. If you change your mind, come and see me again; I am very interested in getting my hands on that flower").also { stage = 1000 } + 20 -> npcl(FacialExpression.NEUTRAL,"As you wish outlander. If you change your mind, come and see me again; I am very interested in getting my hands on that flower").also { stage = 1000 } - 25 -> npcl(core.game.dialogue.FacialExpression.ASKING,"So... how goes it outerlander? Did you manage to obtain my flower for me yet? Or do you lack the necessary merchanting skills?").also { stage++ } - 26 -> playerl(core.game.dialogue.FacialExpression.ASKING,"I'm still working on it... Do you have any suggestion where to start looking for it?").also { stage++ } - 27 -> npcl(core.game.dialogue.FacialExpression.THINKING," I suggest you ask around the other Fremennik in the town. A good merchant will find exactly what their customer needs somewhere.").also { stage = 1000 } + 25 -> npcl(FacialExpression.ASKING,"So... how goes it outerlander? Did you manage to obtain my flower for me yet? Or do you lack the necessary merchanting skills?").also { stage++ } + 26 -> playerl(FacialExpression.ASKING,"I'm still working on it... Do you have any suggestion where to start looking for it?").also { stage++ } + 27 -> npcl(FacialExpression.THINKING," I suggest you ask around the other Fremennik in the town. A good merchant will find exactly what their customer needs somewhere.").also { stage = 1000 } - 30 -> npcl(core.game.dialogue.FacialExpression.AMAZED,"Incredible! Your merchanting skills might even match my own! I have no choice but to recommend you to the council of elders!").also { + 30 -> npcl(FacialExpression.AMAZED,"Incredible! Your merchanting skills might even match my own! I have no choice but to recommend you to the council of elders!").also { removeItem(player,3698) player?.removeAttribute("sigmund-steps") player?.removeAttribute("sigmund-started") @@ -103,40 +105,40 @@ class SigmundDialogue (player: Player? = null) : core.game.dialogue.DialoguePlug stage = 1000 } - 35 -> playerl(core.game.dialogue.FacialExpression.ASKING,"I'm still working on it... Do you have any suggestion where to start looking for it?").also { stage++ } - 36 -> npcl(core.game.dialogue.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(core.game.dialogue.FacialExpression.ASKING,"I was making some trades, but then I lost the goods...").also { stage++ } - 38 -> npcl(core.game.dialogue.FacialExpression.THINKING,"Hmmm... well try and start again at the beginning. And try to be more careful of your wares in future.").also { + 35 -> playerl(FacialExpression.ASKING,"I'm still working on it... Do you have any suggestion where to start looking for it?").also { stage++ } + 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 } - 40 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Hello again outerlander! I am amazed once more at your apparent skill at merchanting!").also { stage++ } - 41 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"So I can count on your vote at the council of elders?").also { stage++ } - 42 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Absolutely, outerlander. Your merchanting skills will be a real boon to the Fremennik.").also { 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++ } + 42 -> npcl(FacialExpression.HAPPY,"Absolutely, outerlander. Your merchanting skills will be a real boon to the Fremennik.").also { stage = 1000 } - 50 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Greetings again $gender $fName! What can I do for you this day?").also { stage++ } + 50 -> npcl(FacialExpression.HAPPY,"Greetings again $gender $fName! What can I do for you this day?").also { stage++ } 51 -> options("Can I see your wares?","Nothing thanks").also { stage++ } 52 -> when(buttonId){ - 1 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"Can I see your wares?").also { stage++ } - 2 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"Nothing thanks").also { stage = 55 } + 1 -> playerl(FacialExpression.HAPPY,"Can I see your wares?").also { stage++ } + 2 -> playerl(FacialExpression.HAPPY,"Nothing thanks").also { stage = 55 } } - 53 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Certainly, $fName.").also { + 53 -> npcl(FacialExpression.HAPPY,"Certainly, $fName.").also { npc.openShop(player) stage = 1000 } - 55 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Well, feel free to stop by anytime you wish $fName. You are always welcome here!").also { stage = 1000 } + 55 -> npcl(FacialExpression.HAPPY,"Well, feel free to stop by anytime you wish $fName. You are always welcome here!").also { stage = 1000 } - 60 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Hello outerlander. By the laws of our tribe, I am afraid I may not speak to you without the express permission of the chieftain.").also { stage++ } - 61 -> playerl(core.game.dialogue.FacialExpression.ASKING,"Where would I find him?").also { stage++ } - 62 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"In the longhall, outerlander.").also { stage = 1000 } + 60 -> npcl(FacialExpression.HAPPY,"Hello outerlander. By the laws of our tribe, I am afraid I may not speak to you without the express permission of the chieftain.").also { stage++ } + 61 -> playerl(FacialExpression.ASKING,"Where would I find him?").also { stage++ } + 62 -> npcl(FacialExpression.HAPPY,"In the longhall, outerlander.").also { stage = 1000 } 1000 -> end() } return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return SigmundDialogue(player) } 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 6a09d38ce..e4ec8ecfb 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,44 +7,45 @@ 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 SkulgrimenDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { +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){ - playerl(core.game.dialogue.FacialExpression.HAPPY,"Hi there. I got your fish, so can I have that bowstring for Sigli now?") + 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){ - playerl(core.game.dialogue.FacialExpression.ASKING,"So about this bowstring... was it hard to make or something?") + playerl(FacialExpression.ASKING,"So about this bowstring... was it hard to make or something?") stage = 25 return true } else if(player?.getAttribute("sigmundreturning",false) == true){ - playerl(core.game.dialogue.FacialExpression.ASKING,"Is this trade item for you?") + playerl(FacialExpression.ASKING,"Is this trade item for you?") stage = 26 return true } if(player?.getAttribute("sigmund-steps", 0) == 7){ - playerl(core.game.dialogue.FacialExpression.ASKING,"I don't suppose you have any idea where I could find an exotic and extremely odd fish, do you?") + playerl(FacialExpression.ASKING,"I don't suppose you have any idea where I could find an exotic and extremely odd fish, do you?") stage = 15 return true } else if(player?.getAttribute("sigmund-steps", 0) == 6){ - playerl(core.game.dialogue.FacialExpression.ASKING,"I don't suppose you have any idea where I could find a finely balanced custom bowstring, do you?") + playerl(FacialExpression.ASKING,"I don't suppose you have any idea where I could find a finely balanced custom bowstring, do you?") stage = 1 return true } - else if(player.questRepository.isComplete("Fremennik Trials")){ - npcl(core.game.dialogue.FacialExpression.HAPPY,"Hello again, ${player.getAttribute("fremennikname","ringo")}. Come to see what's for sale?") + 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 } else{ - playerl(core.game.dialogue.FacialExpression.HAPPY,"Hello!") + playerl(FacialExpression.HAPPY,"Hello!") stage = 100 } return true @@ -52,36 +53,36 @@ class SkulgrimenDialogue(player: Player? = null) : core.game.dialogue.DialoguePl override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 1 -> npcl(core.game.dialogue.FacialExpression.THINKING,"Aye, I have a few in stock. What would an outerlander be wanting with equipment like that?").also { stage++ } - 2 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"It's for Sigli. It needs to be weighted precisely to suit his hunting bow.").also { stage++ } - 3 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"For Sigli eh? Well, I made his bow in the first place, so I'll be able to select the right string for you... just one small problem.").also { stage++ } - 4 -> playerl(core.game.dialogue.FacialExpression.THINKING,"What's that?").also { stage++ } - 5 -> npcl(core.game.dialogue.FacialExpression.THINKING,"This string you'll be wanting... Very special it is. Take a lot of time to recreate. Not sure you have the cash for it.").also { stage++ } - 6 -> playerl(core.game.dialogue.FacialExpression.THINKING,"Then maybe you'll accept something else...?").also { stage++ } - 7 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Heh. Good thinking outerlander. Well, it's true, there is more to life than just making money. Making weapons is good money, but it's not why I do it. I'll tell you what.").also { stage++ } - 8 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"I heard a rumour that one of the fishermen down by the docks caught some weird looking fish as they were fishing the other day. From what I hear this fish is unique.").also { stage++ } - 9 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"From what I hear this fish is unique. Nobody's ever seen its like before. This intrigues me. I'd like to have it for myself.").also { stage++ } - 10 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Make a good trophy. You get me that fish, I give you the bowstring. What do you say? We got a deal?").also { stage++ } - 11 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"Sounds good to me.").also { + 1 -> npcl(FacialExpression.THINKING,"Aye, I have a few in stock. What would an outerlander be wanting with equipment like that?").also { stage++ } + 2 -> playerl(FacialExpression.HAPPY,"It's for Sigli. It needs to be weighted precisely to suit his hunting bow.").also { stage++ } + 3 -> npcl(FacialExpression.HAPPY,"For Sigli eh? Well, I made his bow in the first place, so I'll be able to select the right string for you... just one small problem.").also { stage++ } + 4 -> playerl(FacialExpression.THINKING,"What's that?").also { stage++ } + 5 -> npcl(FacialExpression.THINKING,"This string you'll be wanting... Very special it is. Take a lot of time to recreate. Not sure you have the cash for it.").also { stage++ } + 6 -> playerl(FacialExpression.THINKING,"Then maybe you'll accept something else...?").also { stage++ } + 7 -> npcl(FacialExpression.HAPPY,"Heh. Good thinking outerlander. Well, it's true, there is more to life than just making money. Making weapons is good money, but it's not why I do it. I'll tell you what.").also { stage++ } + 8 -> npcl(FacialExpression.HAPPY,"I heard a rumour that one of the fishermen down by the docks caught some weird looking fish as they were fishing the other day. From what I hear this fish is unique.").also { stage++ } + 9 -> npcl(FacialExpression.HAPPY,"From what I hear this fish is unique. Nobody's ever seen its like before. This intrigues me. I'd like to have it for myself.").also { stage++ } + 10 -> npcl(FacialExpression.HAPPY,"Make a good trophy. You get me that fish, I give you the bowstring. What do you say? We got a deal?").also { stage++ } + 11 -> playerl(FacialExpression.HAPPY,"Sounds good to me.").also { player?.incrementAttribute("sigmund-steps",1) stage = 1000 } - 15 -> npcl(core.game.dialogue.FacialExpression.EXTREMELY_SHOCKED,"What? There's another one?").also { stage++ } - 16 -> playerl(core.game.dialogue.FacialExpression.ANNOYED,"Er... no, it's the one for you that I'm looking for...").also { stage++ } - 17 -> npcl(core.game.dialogue.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 } + 15 -> npcl(FacialExpression.EXTREMELY_SHOCKED,"What? There's another one?").also { stage++ } + 16 -> playerl(FacialExpression.ANNOYED,"Er... no, it's the one for you that I'm looking for...").also { stage++ } + 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(core.game.dialogue.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 { + 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++ } - 21 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"Thanks!").also { stage = 1000 } + 21 -> playerl(FacialExpression.HAPPY,"Thanks!").also { stage = 1000 } - 25 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Not hard. Just a trick to it. Takes skill to learn, but when learnt, easy. Sigli will be happy. Finest bowstring on continent. Will suit his needs perfectly.").also { stage = 1000 } + 25 -> npcl(FacialExpression.HAPPY,"Not hard. Just a trick to it. Takes skill to learn, but when learnt, easy. Sigli will be happy. Finest bowstring on continent. Will suit his needs perfectly.").also { stage = 1000 } - 26 -> npcl(core.game.dialogue.FacialExpression.ANNOYED,"Not for me, I'm afraid.").also { stage = 1000 } + 26 -> npcl(FacialExpression.ANNOYED,"Not for me, I'm afraid.").also { stage = 1000 } - 100 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Sorry. I can't sell weapons to outerlanders. Wouldn't be right. Against our beliefs.").also { stage = 1000 } + 100 -> npcl(FacialExpression.HAPPY,"Sorry. I can't sell weapons to outerlanders. Wouldn't be right. Against our beliefs.").also { stage = 1000 } 1000 -> end() 1001 ->{ @@ -92,7 +93,7 @@ class SkulgrimenDialogue(player: Player? = null) : core.game.dialogue.DialoguePl return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return SkulgrimenDialogue(player) } 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 e712b989c..b2f5aada9 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,40 +1,41 @@ 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 @Initializable -class SwensenTheNavigator(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +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){ - playerl(core.game.dialogue.FacialExpression.HAPPY,"I would like your map of fishing spots.") + playerl(FacialExpression.HAPPY,"I would like your map of fishing spots.") stage = 120 return true } else if(player?.inventory?.contains(3704,1) == true){ - playerl(core.game.dialogue.FacialExpression.ASKING,"If this map of fishing spots is so valuable, why did you give it away to me so easily?") + 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 } else if(player?.getAttribute("sigmundreturning",false) == true){ - playerl(core.game.dialogue.FacialExpression.ASKING,"I have a trade item here.") + playerl(FacialExpression.ASKING,"I have a trade item here.") stage = 130 return true } if(player?.getAttribute("sigmund-steps", 0) == 9){ - npcl(core.game.dialogue.FacialExpression.HAPPY,"Greetings outerlander.") + npcl(FacialExpression.HAPPY,"Greetings outerlander.") stage = 115 return true } else if(player?.getAttribute("sigmund-steps", 0) == 8){ - npcl(core.game.dialogue.FacialExpression.HAPPY,"Greetings outerlander.") + npcl(FacialExpression.HAPPY,"Greetings outerlander.") stage = 105 return true } @@ -47,18 +48,18 @@ class SwensenTheNavigator(player: Player? = null) : core.game.dialogue.DialogueP stage = 1000 return true } - else if(player.questRepository.isComplete("Fremennik Trials")){ - playerl(core.game.dialogue.FacialExpression.HAPPY,"Hello!") + 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 } else{ - playerl(core.game.dialogue.FacialExpression.HAPPY,"Hello!") + playerl(FacialExpression.HAPPY,"Hello!") stage = 145 return true } @@ -90,8 +91,8 @@ class SwensenTheNavigator(player: Player? = null) : core.game.dialogue.DialogueP 100 -> {player("So does that mean I can rely on your vote at the","council of elders to allow me into your village?");stage++} 101 -> {npc("Of course outerlander! I am nothing if not a man of","my word!");stage++} 102 -> { - player("Thanks!"); - player?.removeAttribute("fremtrials:maze-complete") + player("Thanks!") + player?.removeAttribute("fremtrials:maze-complete") player?.removeAttribute("fremtrials:swensen-accepted") player?.setAttribute("/save:fremtrials:swensen-vote",true) player?.setAttribute("/save:fremtrials:votes",player.getAttribute("fremtrials:votes",0) + 1) @@ -99,50 +100,50 @@ class SwensenTheNavigator(player: Player? = null) : core.game.dialogue.DialogueP } //Sigmund subquest - 105 -> playerl(core.game.dialogue.FacialExpression.ASKING,"I don't suppose you have any idea where I could find a map of deep sea fishing spots do you?").also { stage++ } - 106 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Hmmm? Why of course! As the navigator for the Fremennik I keep all of our maps secure right here.").also { stage++ } - 107 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"Great! Can I have it?").also { stage++ } - 108 -> npcl(core.game.dialogue.FacialExpression.ANNOYED,"Have it? Just like that? I think not outerlander. This map shows all of the prime fishing locations nearby. It is very valuable to our clan. I am afraid I can not just give it away.").also { stage++ } - 109 -> playerl(core.game.dialogue.FacialExpression.THINKING,"Perhaps I can trade you something for it?").also { stage++ } - 110 -> npcl(core.game.dialogue.FacialExpression.ANNOYED,"A trade? For a map of the best fishing spots in a hundred leagues? I will trade it for no less than a weather forecast from our Seer.").also { stage++ } - 111 -> npcl(core.game.dialogue.FacialExpression.THINKING,"As a navigator, the weather is extremely important for plotting the best course. Unfortunately the Seer is always too busy to help me with a forecast.").also { stage++ } - 112 -> playerl(core.game.dialogue.FacialExpression.ASKING,"Where could I get a weather forecast from then?").also { stage++ } - 113 -> npcl(core.game.dialogue.FacialExpression.ANNOYED,"I just told you: from the Seer. You will need to persuade him to take the time to make a forecast somehow.").also { + 105 -> playerl(FacialExpression.ASKING,"I don't suppose you have any idea where I could find a map of deep sea fishing spots do you?").also { stage++ } + 106 -> npcl(FacialExpression.HAPPY,"Hmmm? Why of course! As the navigator for the Fremennik I keep all of our maps secure right here.").also { stage++ } + 107 -> playerl(FacialExpression.HAPPY,"Great! Can I have it?").also { stage++ } + 108 -> npcl(FacialExpression.ANNOYED,"Have it? Just like that? I think not outerlander. This map shows all of the prime fishing locations nearby. It is very valuable to our clan. I am afraid I can not just give it away.").also { stage++ } + 109 -> playerl(FacialExpression.THINKING,"Perhaps I can trade you something for it?").also { stage++ } + 110 -> npcl(FacialExpression.ANNOYED,"A trade? For a map of the best fishing spots in a hundred leagues? I will trade it for no less than a weather forecast from our Seer.").also { stage++ } + 111 -> npcl(FacialExpression.THINKING,"As a navigator, the weather is extremely important for plotting the best course. Unfortunately the Seer is always too busy to help me with a forecast.").also { stage++ } + 112 -> playerl(FacialExpression.ASKING,"Where could I get a weather forecast from then?").also { stage++ } + 113 -> npcl(FacialExpression.ANNOYED,"I just told you: from the Seer. You will need to persuade him to take the time to make a forecast somehow.").also { player?.incrementAttribute("sigmund-steps",1) stage = 1000 } - 115 -> playerl(core.game.dialogue.FacialExpression.THINKING,"I don't suppose you have any idea where I could find a weather forecast from the Fremennik Seer do you?").also { stage++ } - 116 -> npcl(core.game.dialogue.FacialExpression.ANNOYED,"Uh... from the Seer perhaps?").also { stage = 1000 } + 115 -> playerl(FacialExpression.THINKING,"I don't suppose you have any idea where I could find a weather forecast from the Fremennik Seer do you?").also { stage++ } + 116 -> npcl(FacialExpression.ANNOYED,"Uh... from the Seer perhaps?").also { stage = 1000 } - 120 -> npcl(core.game.dialogue.FacialExpression.ANNOYED,"I have already told you outerlander; I will not exchange it for anything other than a divination on the weather from our seer himself!").also { stage++ } - 121 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"What, like this one I have here?").also { stage++ } - 122 -> npcl(core.game.dialogue.FacialExpression.AMAZED,"W-what...? I don't believe it! How did you...?").also { stage++ } - 123 -> npcl(core.game.dialogue.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 { + 120 -> npcl(FacialExpression.ANNOYED,"I have already told you outerlander; I will not exchange it for anything other than a divination on the weather from our seer himself!").also { stage++ } + 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++ } - 124 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Here, outerlander; you may take my map of local fishing patterns with my gratitude!").also { stage = 1000 } + 124 -> npcl(FacialExpression.HAPPY,"Here, outerlander; you may take my map of local fishing patterns with my gratitude!").also { stage = 1000 } - 125 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Hmmm? Well, firstly it will be of value to our entire clan, so I have lost nothing from giving it to you.").also { stage++ } - 126 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"The other reason is of course that I have already memorised it, so I can make myself another copy whenever I want!").also { stage = 1000 } + 125 -> npcl(FacialExpression.HAPPY,"Hmmm? Well, firstly it will be of value to our entire clan, so I have lost nothing from giving it to you.").also { stage++ } + 126 -> npcl(FacialExpression.HAPPY,"The other reason is of course that I have already memorised it, so I can make myself another copy whenever I want!").also { stage = 1000 } - 130 -> npcl(core.game.dialogue.FacialExpression.ANNOYED,"It isn't for me, I'm afraid.").also { stage = 1000 } + 130 -> npcl(FacialExpression.ANNOYED,"It isn't for me, I'm afraid.").also { stage = 1000 } - 140 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Greetings to you $gender $fName. How fare you today?").also { stage++ } - 141 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"I am fine thanks Swensen. How are you doing?").also { stage++ } - 142 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"I am fine too!").also { stage = 1000 } + 140 -> npcl(FacialExpression.HAPPY,"Greetings to you $gender $fName. How fare you today?").also { stage++ } + 141 -> playerl(FacialExpression.HAPPY,"I am fine thanks Swensen. How are you doing?").also { stage++ } + 142 -> npcl(FacialExpression.HAPPY,"I am fine too!").also { stage = 1000 } - 145 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Hello outerlander. This is my home, I would be grateful if you would leave.").also { stage++ } - 146 -> playerl(core.game.dialogue.FacialExpression.THINKING,"Oh. Okay.").also { stage++ } - 147 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"I am sorry outerlander, I will not offer you hospitality until my Chieftain has vouched for your honesty. This is our way.").also { stage = 1000 } + 145 -> npcl(FacialExpression.HAPPY,"Hello outerlander. This is my home, I would be grateful if you would leave.").also { stage++ } + 146 -> playerl(FacialExpression.THINKING,"Oh. Okay.").also { stage++ } + 147 -> npcl(FacialExpression.HAPPY,"I am sorry outerlander, I will not offer you hospitality until my Chieftain has vouched for your honesty. This is our way.").also { stage = 1000 } 1000 -> end() } return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return SwensenTheNavigator(player) } 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 9bc326924..51331c124 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 { @@ -154,7 +156,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,17 +168,17 @@ 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(questStage(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!") - } else { - if(removeItem(player,lyre.asItem())){ + } else if (player.isTeleBlocked) { + sendMessage(player, "A magical force has stopped you from teleporting.") + } else if(removeItem(player,lyre.asItem())){ addItem(player,LYRE_IDs.getNext(lyre.id)) Pulser.submit(TelePulse(player)) } - } return@on true } @@ -208,15 +210,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 +267,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.") @@ -320,6 +324,8 @@ class TFTInteractionListeners : InteractionListener { class SpiritPulse(val player: Player, val fish: Int) : Pulse(){ var counter = 0 val npc = NPC(1273, player.location) + val sea_boots = intArrayOf(Items.FREMENNIK_SEA_BOOTS_1_14571,Items.FREMENNIK_SEA_BOOTS_2_14572,Items.FREMENNIK_SEA_BOOTS_3_14573) + val hasboots = player.equipment.containsAtLeastOneItem(sea_boots) override fun pulse(): Boolean { when(counter++){ 0 -> { @@ -332,10 +338,14 @@ class TFTInteractionListeners : InteractionListener { 3 -> player.dialogueInterpreter?.sendDialogues(npc, core.game.dialogue.FacialExpression.HAPPY,"I will kindly accept this offering, and","bestow upon you a gift in return.") 4 -> if(!removeItem(player,Items.LYRE_3689)) { removeItem(player,Items.ENCHANTED_LYRE_3690) } - 5 -> when(fish){ - 383 -> addItem(player,Items.ENCHANTED_LYRE2_6125) - 389 -> addItem(player,Items.ENCHANTED_LYRE3_6126) - 395 -> addItem(player,Items.ENCHANTED_LYRE4_6127) + 5 -> if(hasboots) when(fish){ + 383 -> addItem(player, Items.ENCHANTED_LYRE3_6126) + 389 -> addItem(player, Items.ENCHANTED_LYRE5_14590) + 395 -> addItem(player, Items.ENCHANTED_LYRE6_14591) + } else when(fish){ + 383 -> addItem(player, Items.ENCHANTED_LYRE2_6125) + 389 -> addItem(player, Items.ENCHANTED_LYRE3_6126) + 395 -> addItem(player, Items.ENCHANTED_LYRE4_6127) } 6 -> player.unlock() 10 -> npc.clear().also { @@ -346,9 +356,9 @@ class TFTInteractionListeners : InteractionListener { } } - class LyreConcertPulse(val player: Player, val Lyre: Int) : Pulse(){ + fun playLyreConcert(player: Player, lyre: Int) { val GENERIC_LYRICS = arrayOf( - "${player.name?.capitalize()} is my name,", + "${player.username?.capitalize()} is my name,", "I haven't much to say", "But since I have to sing this song.", "I'll just go ahead and play." @@ -371,8 +381,7 @@ class TFTInteractionListeners : InteractionListener { "I will simply tell you this:", "I've joined the Legends' Guild!" ) - var counter = 0 - val questPoints = getQP(player) + val questPoints = getQuestPoints(player) val champGuild = player.achievementDiaryManager?.hasCompletedTask(DiaryType.VARROCK, 1, 1)?: false val legGuild = questPoints >= 111 val heroGuild = questPoints >= 5 @@ -394,15 +403,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) @@ -426,13 +437,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) } } @@ -481,4 +494,4 @@ class TFTInteractionListeners : InteractionListener { return false } } -} \ No newline at end of file +} 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 0b65880d0..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,40 +8,41 @@ 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) : core.game.dialogue.DialoguePlugin(player){ +class ThoraDialogue(player: Player? = null) : DialoguePlugin(player){ val fName = player?.getAttribute("fremennikname","name jeff") var curNPC: NPC? = NPC(0, Location(0,0,0)) override fun open(vararg args: Any?): Boolean { curNPC = args[0] as? NPC npc = args[0] as NPC if(player.inventory.contains(3707,1)){ - playerl(core.game.dialogue.FacialExpression.ASKING,"Thanks for making me this cocktail. Why don't you make them anymore normally?") + playerl(FacialExpression.ASKING,"Thanks for making me this cocktail. Why don't you make them anymore normally?") stage = 35 } else if(player.inventory.contains(3709,1)){ - playerl(core.game.dialogue.FacialExpression.HAPPY,"Hi! Can I please have one of your legendary cocktails now?") + playerl(FacialExpression.HAPPY,"Hi! Can I please have one of your legendary cocktails now?") stage = 25 } else if(player?.getAttribute("sigmundreturning",false) == true){ - playerl(core.game.dialogue.FacialExpression.ASKING,"I'm trying to remember who I was meant to give this trade item to.") + playerl(FacialExpression.ASKING,"I'm trying to remember who I was meant to give this trade item to.") stage = 30 } else if(player?.getAttribute("sigmund-steps", 0) == 13){ - playerl(core.game.dialogue.FacialExpression.ASKING,"I don't suppose you have any idea where I could find a written promise from Askeladden to stay out of the Longhall?") + playerl(FacialExpression.ASKING,"I don't suppose you have any idea where I could find a written promise from Askeladden to stay out of the Longhall?") stage = 20 } else if(player?.getAttribute("sigmund-steps", 0) == 12){ - playerl(core.game.dialogue.FacialExpression.ASKING,"I don't suppose you have any idea where I could find the longhall barkeeps' legendary cocktail, do you?") + 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")){ - npcl(core.game.dialogue.FacialExpression.HAPPY,"Hello again, $fName. I suppose you want a drink? Or are you going to try another scam with that terrible Askeladden again?") + 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 } else{ - playerl(core.game.dialogue.FacialExpression.HAPPY,"Hello there.") + playerl(FacialExpression.HAPPY,"Hello there.") stage = 60 } return true @@ -49,77 +50,77 @@ class ThoraDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin( override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 1 -> npcl(core.game.dialogue.FacialExpression.AMAZED,"How did you hear about that? I didn't think anybody knew about that.").also { stage++ } - 2 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Well, it is true that in my younger years as a barkeep, I wandered the lands trying various alcoholic delicacies.").also { stage++ } - 3 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Do know how many different types of alcohol there are here in Gielinor? Lots!").also { stage++ } - 4 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Well, anyway, I used a fusion of various drinks from all around the world to create the greatest cocktail ever made!").also { stage++ } - 5 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Of course, when my wander lust was gone, and I returned to Rellekka to serve as barkeep here, I gave all that up.").also { stage++ } - 6 -> playerl(core.game.dialogue.FacialExpression.ASKING,"But you still remember how to make it, right?").also { stage++ } - 7 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Of course.").also { stage++ } - 8 -> playerl(core.game.dialogue.FacialExpression.ASKING,"And you have all the ingredients here? I don't need to go chasing round the world for obscure ingredients to make it?").also { stage++ } - 9 -> npcl(core.game.dialogue.FacialExpression.ASKING,"No, I have them all here. Why?").also { stage++ } - 10 -> playerl(core.game.dialogue.FacialExpression.ASKING,"Can you make me your legendary cocktail then?").also { stage++ } - 11 -> npcl(core.game.dialogue.FacialExpression.SAD,"I would rather not; it is a reminder of a life I left behind when I came back.").also { stage++ } - 12 -> playerl(core.game.dialogue.FacialExpression.ASKING,"Any way I could change your mind?").also { stage++ } - 13 -> npcl(core.game.dialogue.FacialExpression.THINKING,"You need this to become a Fremennik, right? Well, you seem okay for an outerlander, it would be a shame to see you fail. You know Askeladden?").also { stage++ } - 14 -> playerl(core.game.dialogue.FacialExpression.THINKING,"That kid outside? Sure.").also { stage++ } - 15 -> npcl(core.game.dialogue.FacialExpression.ANGRY,"He is nothing but a pest. He keeps sneaking in and stealing beer.").also { stage++ } - 16 -> npcl(core.game.dialogue.FacialExpression.ANGRY,"I shudder to think what he will be like when he has passed his trial of manhood, and is allowed in here legitimately.").also { stage++ } - 17 -> npcl(core.game.dialogue.FacialExpression.ANGRY,"If you can get him to sign a contract promising that he will NEVER EVER EVER darken my doorway here again, you get the drink.").also { stage++ } - 18 -> playerl(core.game.dialogue.FacialExpression.ASKING,"Any idea how I can get him to do that?").also { stage++ } - 19 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Knowing that little horror, he'll probably be willing to in exchange for some cash. You should go ask him yourself though.").also { + 1 -> npcl(FacialExpression.AMAZED,"How did you hear about that? I didn't think anybody knew about that.").also { stage++ } + 2 -> npcl(FacialExpression.HAPPY,"Well, it is true that in my younger years as a barkeep, I wandered the lands trying various alcoholic delicacies.").also { stage++ } + 3 -> npcl(FacialExpression.HAPPY,"Do know how many different types of alcohol there are here in Gielinor? Lots!").also { stage++ } + 4 -> npcl(FacialExpression.HAPPY,"Well, anyway, I used a fusion of various drinks from all around the world to create the greatest cocktail ever made!").also { stage++ } + 5 -> npcl(FacialExpression.HAPPY,"Of course, when my wander lust was gone, and I returned to Rellekka to serve as barkeep here, I gave all that up.").also { stage++ } + 6 -> playerl(FacialExpression.ASKING,"But you still remember how to make it, right?").also { stage++ } + 7 -> npcl(FacialExpression.HAPPY,"Of course.").also { stage++ } + 8 -> playerl(FacialExpression.ASKING,"And you have all the ingredients here? I don't need to go chasing round the world for obscure ingredients to make it?").also { stage++ } + 9 -> npcl(FacialExpression.ASKING,"No, I have them all here. Why?").also { stage++ } + 10 -> playerl(FacialExpression.ASKING,"Can you make me your legendary cocktail then?").also { stage++ } + 11 -> npcl(FacialExpression.SAD,"I would rather not; it is a reminder of a life I left behind when I came back.").also { stage++ } + 12 -> playerl(FacialExpression.ASKING,"Any way I could change your mind?").also { stage++ } + 13 -> npcl(FacialExpression.THINKING,"You need this to become a Fremennik, right? Well, you seem okay for an outerlander, it would be a shame to see you fail. You know Askeladden?").also { stage++ } + 14 -> playerl(FacialExpression.THINKING,"That kid outside? Sure.").also { stage++ } + 15 -> npcl(FacialExpression.ANGRY,"He is nothing but a pest. He keeps sneaking in and stealing beer.").also { stage++ } + 16 -> npcl(FacialExpression.ANGRY,"I shudder to think what he will be like when he has passed his trial of manhood, and is allowed in here legitimately.").also { stage++ } + 17 -> npcl(FacialExpression.ANGRY,"If you can get him to sign a contract promising that he will NEVER EVER EVER darken my doorway here again, you get the drink.").also { stage++ } + 18 -> playerl(FacialExpression.ASKING,"Any idea how I can get him to do that?").also { stage++ } + 19 -> npcl(FacialExpression.HAPPY,"Knowing that little horror, he'll probably be willing to in exchange for some cash. You should go ask him yourself though.").also { player?.incrementAttribute("sigmund-steps",1) stage = 1000 } - 20 -> npcl(core.game.dialogue.FacialExpression.ANNOYED,"Well, as I say, you should talk to him about that. Knowing the little runt as I do though He'll probably do it for the cash.").also {stage++} + 20 -> npcl(FacialExpression.ANNOYED,"Well, as I say, you should talk to him about that. Knowing the little runt as I do though He'll probably do it for the cash.").also {stage++} - 25 -> npcl(core.game.dialogue.FacialExpression.AMAZED,"What?!?! I can't believe you... Let me look at that... Askeladden would NEVER... Gosh. It looks legitimate.").also { + 25 -> npcl(FacialExpression.AMAZED,"What?!?! I can't believe you... Let me look at that... Askeladden would NEVER... Gosh. It looks legitimate.").also { player?.inventory?.remove(Item(Items.PROMISSORY_NOTE_3709)) player?.inventory?.add(Item(Items.LEGENDARY_COCKTAIL_3707)) player?.setAttribute("/save:sigmundreturning",true) stage++ } - 26 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Here you go, on the house! You have made my life SO much easier! Knowing that little monster won't be bugging me in here all the time anymore!").also { stage++ } - 27 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"That little weasel will have to abide by this written promise that Askeladden can never ever enter the Longhall again! He can't get round this one!").also { stage++ } - 28 -> playerl(core.game.dialogue.FacialExpression.SUSPICIOUS,"Uh... yeah... yeah, you probably won't see someone called Askeladden coming in here...").also { stage = 1000 } + 26 -> npcl(FacialExpression.HAPPY,"Here you go, on the house! You have made my life SO much easier! Knowing that little monster won't be bugging me in here all the time anymore!").also { stage++ } + 27 -> npcl(FacialExpression.HAPPY,"That little weasel will have to abide by this written promise that Askeladden can never ever enter the Longhall again! He can't get round this one!").also { stage++ } + 28 -> playerl(FacialExpression.SUSPICIOUS,"Uh... yeah... yeah, you probably won't see someone called Askeladden coming in here...").also { stage = 1000 } - 30 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL,"If it's not the note from Askeladden it isn't me, I'm afraid.").also { stage = 1000 } + 30 -> npcl(FacialExpression.NEUTRAL,"If it's not the note from Askeladden it isn't me, I'm afraid.").also { stage = 1000 } - 35 -> npcl(core.game.dialogue.FacialExpression.SAD,"Ah... when I gave up my travels across the world many years back, to return to my expected role as longhall barkeep,").also { stage++ } - 36 -> npcl(core.game.dialogue.FacialExpression.SAD,"as my mother, and her mother, were before me, I gave up a lot of the freedom I had found in the outside world.").also { stage++ } - 37 -> npcl(core.game.dialogue.FacialExpression.SAD,"I know it is our custom to shun outerlanders and their ways, but I didn't find them as bad as the stories say.").also { stage++ } - 38 -> npcl(core.game.dialogue.FacialExpression.SAD,"Sometimes I feel as though we Fremennik live in a prison that we have constructed for ourselves,").also { stage++ } - 39 -> npcl(core.game.dialogue.FacialExpression.SAD,"and that WE are the outerlanders, out here on the edge of the world...").also { stage++ } - 40 -> npcl(core.game.dialogue.FacialExpression.SAD,"I'm sorry, I think it is part of the job of Longhall barkeep to get philosophical about things ocassionally.").also { stage++ } - 41 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"I wish you all the best of luck with your trials, outerlander.").also { stage++ } - 42 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"When you have finished, perhaps you will come back here, and we can share a drink over tales of the outside world?").also { stage++ } - 43 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"Thanks, I'd like that.").also { stage = 1000 } + 35 -> npcl(FacialExpression.SAD,"Ah... when I gave up my travels across the world many years back, to return to my expected role as longhall barkeep,").also { stage++ } + 36 -> npcl(FacialExpression.SAD,"as my mother, and her mother, were before me, I gave up a lot of the freedom I had found in the outside world.").also { stage++ } + 37 -> npcl(FacialExpression.SAD,"I know it is our custom to shun outerlanders and their ways, but I didn't find them as bad as the stories say.").also { stage++ } + 38 -> npcl(FacialExpression.SAD,"Sometimes I feel as though we Fremennik live in a prison that we have constructed for ourselves,").also { stage++ } + 39 -> npcl(FacialExpression.SAD,"and that WE are the outerlanders, out here on the edge of the world...").also { stage++ } + 40 -> npcl(FacialExpression.SAD,"I'm sorry, I think it is part of the job of Longhall barkeep to get philosophical about things ocassionally.").also { stage++ } + 41 -> npcl(FacialExpression.HAPPY,"I wish you all the best of luck with your trials, outerlander.").also { stage++ } + 42 -> npcl(FacialExpression.HAPPY,"When you have finished, perhaps you will come back here, and we can share a drink over tales of the outside world?").also { stage++ } + 43 -> playerl(FacialExpression.HAPPY,"Thanks, I'd like that.").also { stage = 1000 } - 50 -> playerl(core.game.dialogue.FacialExpression.THINKING,"Scam? Oh... You mean his promise to not come in here anymore?").also { stage++ } - 51 -> npcl(core.game.dialogue.FacialExpression.ANNOYED,"Yes. Yes I do. I should have never trusted him not to come in here, even with that written promise. Anyway, do you want a drink or not?").also { stage++ } + 50 -> playerl(FacialExpression.THINKING,"Scam? Oh... You mean his promise to not come in here anymore?").also { stage++ } + 51 -> npcl(FacialExpression.ANNOYED,"Yes. Yes I do. I should have never trusted him not to come in here, even with that written promise. Anyway, do you want a drink or not?").also { stage++ } 52 -> options("Yes please","No thanks").also { stage++ } 53 -> when(buttonId){ - 1 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"Yes please").also { + 1 -> playerl(FacialExpression.HAPPY,"Yes please").also { npc.openShop(player) stage = 1000 } - 2 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"No thanks.").also { stage++ } + 2 -> playerl(FacialExpression.HAPPY,"No thanks.").also { stage++ } } - 54 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Okay then.").also { stage = 1000 } + 54 -> npcl(FacialExpression.HAPPY,"Okay then.").also { stage = 1000 } - 60 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Hello yourself, outerlander. A little out of the way up here, aren't you?").also { stage++ } - 61 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"I would love to stop and chat with you, but unfortunately we have a custom that no Fremennik may speak with the outerlanders without the permission of our chieftain. Don't take it personally.").also { stage++ } - 62 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"The Chieftain is stood just over there, his name is Brundt. I suggest you go and introduce yourself.").also { stage++ } - 63 -> npcl(core.game.dialogue.FacialExpression.ASKING,"You wouldn't want to go making any enemies because you weren't aware of our customs now, would you?").also { stage++ } - 64 -> playerl(core.game.dialogue.FacialExpression.THINKING,"No, I guess not. Thanks anyway.").also { stage++ } - 65 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Not a problem, outerlander. It's always nice to see a new face in the long hall; it happens so rarely.").also { stage = 1000 } + 60 -> npcl(FacialExpression.HAPPY,"Hello yourself, outerlander. A little out of the way up here, aren't you?").also { stage++ } + 61 -> npcl(FacialExpression.HAPPY,"I would love to stop and chat with you, but unfortunately we have a custom that no Fremennik may speak with the outerlanders without the permission of our chieftain. Don't take it personally.").also { stage++ } + 62 -> npcl(FacialExpression.HAPPY,"The Chieftain is stood just over there, his name is Brundt. I suggest you go and introduce yourself.").also { stage++ } + 63 -> npcl(FacialExpression.ASKING,"You wouldn't want to go making any enemies because you weren't aware of our customs now, would you?").also { stage++ } + 64 -> playerl(FacialExpression.THINKING,"No, I guess not. Thanks anyway.").also { stage++ } + 65 -> npcl(FacialExpression.HAPPY,"Not a problem, outerlander. It's always nice to see a new face in the long hall; it happens so rarely.").also { stage = 1000 } 1000 -> end() } return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return ThoraDialogue(player) } 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..af0ed440f 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,10 +1,12 @@ 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.NPCs +import content.data.Quests @Initializable class ThorvaldDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ @@ -39,27 +41,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 +77,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 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 83e782706..a515d5dde 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,46 +7,47 @@ 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 YrsaDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { +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){ - playerl(core.game.dialogue.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.") + 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){ - playerl(core.game.dialogue.FacialExpression.ASKING,"Hey, these shoes look pretty comfy. Think you could make me a pair like them?") + playerl(FacialExpression.ASKING,"Hey, these shoes look pretty comfy. Think you could make me a pair like them?") stage = 20 return true } else if(player?.getAttribute("sigmundreturning",false) == true){ - playerl(core.game.dialogue.FacialExpression.ASKING,"I have this trade item but I can't remember who it's for.") + playerl(FacialExpression.ASKING,"I have this trade item but I can't remember who it's for.") stage = 25 return true } if(player?.getAttribute("sigmund-steps",0) == 4){ - playerl(core.game.dialogue.FacialExpression.ASKING,"I don't suppose you have any idea where I could find a guarantee of a reduction on sales taxes, do you?") + playerl(FacialExpression.ASKING,"I don't suppose you have any idea where I could find a guarantee of a reduction on sales taxes, do you?") stage = 10 return true } else if(player?.getAttribute("sigmund-steps",0) == 3){ - playerl(core.game.dialogue.FacialExpression.ASKING,"I don't suppose you have any idea where I could find some custom sturdy boots, do you?") + playerl(FacialExpression.ASKING,"I don't suppose you have any idea where I could find some custom sturdy boots, do you?") stage = 1 return true } - else if(player.questRepository.isComplete("Fremennik Trials")){ - npcl(core.game.dialogue.FacialExpression.HAPPY,"Welcome to my clothes shop. I can change your shoes, or I've got a fine selection of clothes for sale.") + 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 //npcl(FacialExpression.HAPPY,"Welcome to my clothes shop. I can change your shows, or I've got a fine selection of clothes for sale") return true } else { - playerl(core.game.dialogue.FacialExpression.HAPPY,"Hello!") + playerl(FacialExpression.HAPPY,"Hello!") stage = 35 } return true @@ -54,46 +55,46 @@ class YrsaDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(p override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 1 -> npcl(core.game.dialogue.FacialExpression.THINKING,"Well, I don't usually have many shoes in stock here in my little clothes shop... I will be able to make you up a pair if you are really desperate though?").also { stage++ } - 2 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL,"They're not for me... I need them for Olaf.").also { stage++ } - 3 -> npcl(core.game.dialogue.FacialExpression.ANGRY,"Oh, that foolish bard... Why didn't he just ask me to make him some? It is his stupid pride, I believe! I will tell you what I will do outlander.").also { stage++ } - 4 -> npcl(core.game.dialogue.FacialExpression.ASKING,"I know that you must have the ear of the chieftain for him to consider you as worthy of becoming a Fremennik by trial.").also { stage++ } - 5 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"I will make you a pair of sturdy boots for Olaf if you will persuade him to reduce the sales tax placed upon all Fremennik shopkeepers. It does nothing but hurt my business now.").also { stage++ } - 6 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL,"Okay, I will see what I can do").also { + 1 -> npcl(FacialExpression.THINKING,"Well, I don't usually have many shoes in stock here in my little clothes shop... I will be able to make you up a pair if you are really desperate though?").also { stage++ } + 2 -> playerl(FacialExpression.NEUTRAL,"They're not for me... I need them for Olaf.").also { stage++ } + 3 -> npcl(FacialExpression.ANGRY,"Oh, that foolish bard... Why didn't he just ask me to make him some? It is his stupid pride, I believe! I will tell you what I will do outlander.").also { stage++ } + 4 -> npcl(FacialExpression.ASKING,"I know that you must have the ear of the chieftain for him to consider you as worthy of becoming a Fremennik by trial.").also { stage++ } + 5 -> npcl(FacialExpression.HAPPY,"I will make you a pair of sturdy boots for Olaf if you will persuade him to reduce the sales tax placed upon all Fremennik shopkeepers. It does nothing but hurt my business now.").also { stage++ } + 6 -> playerl(FacialExpression.NEUTRAL,"Okay, I will see what I can do").also { player?.incrementAttribute("sigmund-steps",1) stage = 1000 } - 10 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL,"Yes I do outerlander. Only the Chieftain may permit such a thing. Talk to him.").also { stage = 1000 } + 10 -> npcl(FacialExpression.NEUTRAL,"Yes I do outerlander. Only the Chieftain may permit such a thing. Talk to him.").also { stage = 1000 } - 15 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Certainly! Let me have a look at what he has written here, just give me a moment...").also { + 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++ } - 16 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Yes, that all appears in order. Tell Olaf to come to me next time for shoes!").also { stage = 1000 } + 16 -> npcl(FacialExpression.HAPPY,"Yes, that all appears in order. Tell Olaf to come to me next time for shoes!").also { stage = 1000 } - 20 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Maybe if you pass your trial and become a full fledged member of the Fremennik...").also { stage = 1000 } + 20 -> npcl(FacialExpression.HAPPY,"Maybe if you pass your trial and become a full fledged member of the Fremennik...").also { stage = 1000 } - 25 -> npcl(core.game.dialogue.FacialExpression.ANNOYED,"Not me, I'm afraid.").also { stage = 1000 } + 25 -> npcl(FacialExpression.ANNOYED,"Not me, I'm afraid.").also { stage = 1000 } 30 -> options("I'd like to buy some clothes","Nothing, thanks.").also { stage++ } 31 -> when(buttonId){ - 1 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"I'd like to buy some clothes").also { + 1 -> playerl(FacialExpression.HAPPY,"I'd like to buy some clothes").also { npc.openShop(player) stage = 1000 } - 2 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"Nothing, thanks.").also { stage++ } + 2 -> playerl(FacialExpression.HAPPY,"Nothing, thanks.").also { stage++ } } - 32 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"As you wish.").also { stage = 1000 } + 32 -> npcl(FacialExpression.HAPPY,"As you wish.").also { stage = 1000 } - 35 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"I'm sorry outerlander, I cannot sell you any clothes. Our customs forbid it.").also { stage = 1000 } + 35 -> npcl(FacialExpression.HAPPY,"I'm sorry outerlander, I cannot sell you any clothes. Our customs forbid it.").also { stage = 1000 } 1000 -> end() } return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return YrsaDialogue(player) } diff --git a/Server/src/main/content/region/fremennik/waterbirth/handlers/SpinolypNPC.java b/Server/src/main/content/region/fremennik/waterbirth/handlers/SpinolypNPC.java index 9fe83d2e9..7836a1c2c 100644 --- a/Server/src/main/content/region/fremennik/waterbirth/handlers/SpinolypNPC.java +++ b/Server/src/main/content/region/fremennik/waterbirth/handlers/SpinolypNPC.java @@ -8,12 +8,13 @@ import core.game.node.entity.combat.InteractionType; import core.game.node.entity.combat.equipment.SwitchAttack; import core.game.node.entity.npc.AbstractNPC; import core.game.node.entity.player.link.SpellBookManager.SpellBook; -import core.game.node.entity.state.EntityState; import core.game.world.map.Location; import core.tools.RandomFunction; import core.game.node.entity.combat.CombatSwingHandler; import core.game.node.entity.combat.MultiSwingHandler; +import static core.api.ContentAPIKt.*; + /** * Represents a spinolyp npc. * @author Vexia @@ -147,7 +148,7 @@ public final class SpinolypNPC extends AbstractNPC { victim.getSkills().decrementPrayerPoints(1); } else { if (RandomFunction.random(20) == 5) { - victim.getStateManager().register(EntityState.POISONED, false, 68, entity); + applyPoison(victim, entity, 30); } } } diff --git a/Server/src/main/content/region/kandarin/ardougne/dialogue/ArdougneBaker.java b/Server/src/main/content/region/kandarin/ardougne/dialogue/ArdougneBaker.java deleted file mode 100644 index 1fce150d1..000000000 --- a/Server/src/main/content/region/kandarin/ardougne/dialogue/ArdougneBaker.java +++ /dev/null @@ -1,59 +0,0 @@ -package content.region.kandarin.ardougne.dialogue; - -import core.game.dialogue.DialoguePlugin; -import core.game.node.entity.npc.NPC; -import core.game.node.entity.player.Player; -import core.plugin.Initializable; - -/** - * Replacement for ArdougneBaker.asc - * @author ceik - */ - -@Initializable -public class ArdougneBaker extends DialoguePlugin { - public ArdougneBaker(){ - /** - * Empty on purpose - */ - } - public ArdougneBaker(Player player){super(player);} - - @Override - public DialoguePlugin newInstance(Player player){return new ArdougneBaker(player);} - - @Override - public boolean open(Object... args) { - npc = (NPC)args[0]; - npc("Good day, monsieur. Would you like ze nice", "freshly-baked bread? Or perhaps a nice piece of cake?"); - stage = 1; - return true; - } - - @Override - public final boolean handle(int interfaceId, int buttonId){ - switch(stage){ - case 1: - interpreter.sendOptions("Choose an option.", "Let's see what you have.", "No thank you."); - stage = 2; - break; - case 2: - switch(buttonId){ - case 1: - end(); - npc.openShop(player); - stage = 20; - break; - case 2: - end(); - break; - } - break; - } - return true; - } - @Override - public int[] getIds() { - return new int[] { 571 }; - } -} diff --git a/Server/src/main/content/region/kandarin/ardougne/dialogue/ArdougneBakerDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/dialogue/ArdougneBakerDialogue.kt new file mode 100644 index 000000000..b5a080511 --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/dialogue/ArdougneBakerDialogue.kt @@ -0,0 +1,49 @@ +package content.region.kandarin.ardougne.dialogue + +import core.api.openNpcShop +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 + +@Initializable +class ArdougneBakerDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun newInstance(player: Player?): DialoguePlugin { + return ArdougneBakerDialogue(player) + } + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + npcl( + FacialExpression.HAPPY, + "Good day, monsieur. Would you like ze nice freshly-baked bread? Or perhaps a nice piece of cake?" + ).also { stage = START_DIALOGUE } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage) { + 0 -> options("Let's see what you have.", "No thank you.").also { stage++ } + 1 -> when (buttonId) { + 1 -> { + openNpcShop(player, NPCs.BAKER_571) + end() + } + + 2 -> playerl( + FacialExpression.FRIENDLY, "No thank you." + ).also { stage = END_DIALOGUE } + } + } + return true + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.BAKER_571) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/ardougne/dialogue/ArdougneShopKeeperPlugin.java b/Server/src/main/content/region/kandarin/ardougne/dialogue/ArdougneShopKeeperPlugin.java deleted file mode 100644 index 7d56d0aaa..000000000 --- a/Server/src/main/content/region/kandarin/ardougne/dialogue/ArdougneShopKeeperPlugin.java +++ /dev/null @@ -1,85 +0,0 @@ -package content.region.kandarin.ardougne.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 arougne shop keepers. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class ArdougneShopKeeperPlugin extends DialoguePlugin { - - /** - * Constructs a new {@code ArdouShopKeeperPlugin} {@code Object}. - */ - public ArdougneShopKeeperPlugin() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code ArdouShopKeeperPlugin} {@code Object}. - * @param player the player. - */ - public ArdougneShopKeeperPlugin(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new ArdougneShopKeeperPlugin(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Hello, you look like a bold adventurer. You've come to the", "right place for adventurers' equipment."); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendOptions("Select an Option", "Oh, that sounds interesting.", "How should I use your shop?", "No, sorry, I've come to the wrong place."); - stage = 1; - break; - case 1: - switch (buttonId) { - case 1: - end(); - npc.openShop(player); - break; - case 2: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "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 = 20; - break; - case 3: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Hmph. Well, perhaps next time you'll need something", "from me?"); - stage = 30; - break; - - } - break; - case 20: - end(); - break; - case 30: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 590, 591 }; - } -} diff --git a/Server/src/main/content/region/kandarin/ardougne/dialogue/BrotherKojoDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/dialogue/BrotherKojoDialogue.kt new file mode 100644 index 000000000..c085837f8 --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/dialogue/BrotherKojoDialogue.kt @@ -0,0 +1,29 @@ +package content.region.kandarin.ardougne.dialogue + +import content.region.kandarin.ardougne.quest.clocktower.BrotherKojoDialogueFile +import core.api.* +import core.game.dialogue.DialoguePlugin +import core.game.node.entity.player.Player +import core.plugin.Initializable +import org.rs09.consts.NPCs + +/** + * Brother Kojo main dialogue. + * @author ovenbread + */ +@Initializable +class BrotherKojoDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + // Fallback to default. Always the start of Clocktower quest + openDialogue(player!!, BrotherKojoDialogueFile(), npc) + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return BrotherKojoDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.BROTHER_KOJO_223) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/ardougne/dialogue/CaptainBarnabyDialogue.java b/Server/src/main/content/region/kandarin/ardougne/dialogue/CaptainBarnabyDialogue.java deleted file mode 100644 index cea30040b..000000000 --- a/Server/src/main/content/region/kandarin/ardougne/dialogue/CaptainBarnabyDialogue.java +++ /dev/null @@ -1,99 +0,0 @@ -package content.region.kandarin.ardougne.dialogue; - -import content.global.travel.ship.Ships; -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.node.item.Item; - -/** - * Represents the captain barnaby dialogue plugin. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class CaptainBarnabyDialogue extends DialoguePlugin { - - /** - * Represents the coins item. - */ - private static final Item COINS = new Item(995, 30); - - /** - * Constructs a new {@code CaptainBarnabyDialogue} {@code Object}. - */ - public CaptainBarnabyDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code CaptainBarnabyDialogue} {@code Object}. - * @param player the player. - */ - public CaptainBarnabyDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new CaptainBarnabyDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Do you want to go on a trip to Brimhaven?"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "The trip will cost you 30 coins."); - stage = 1; - break; - case 1: - interpreter.sendOptions("Select an Option", "Yes please.", "No, thank you."); - stage = 2; - break; - case 2: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Yes please."); - stage = 10; - break; - case 2: - end(); - break; - } - break; - case 10: - if (!player.getInventory().containsItem(COINS)) { - interpreter.sendDialogues(player, null, "Sorry, I don't seem to have enough coins."); - stage = 220; - return true; - } - if (player.getInventory().remove(COINS)) { - end(); - player.getPacketDispatch().sendMessage("You pay 30 coins and board the ship."); - Ships.ARDOUGNE_TO_BRIMHAVEN.sail(player); - } - break; - case 220: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 381 }; - } -} diff --git a/Server/src/main/content/region/kandarin/ardougne/dialogue/CaptainBarnabyListener.kt b/Server/src/main/content/region/kandarin/ardougne/dialogue/CaptainBarnabyListener.kt new file mode 100644 index 000000000..066aeba0f --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/dialogue/CaptainBarnabyListener.kt @@ -0,0 +1,55 @@ +package content.region.kandarin.ardougne.dialogue + +import content.global.travel.ship.Ships +import core.api.* +import core.game.dialogue.DialogueFile +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.item.Item +import core.tools.END_DIALOGUE +import org.rs09.consts.Items +import org.rs09.consts.NPCs + + +class CaptainBarnabyListener : InteractionListener { + override fun defineListeners() { + on(NPCs.CAPTAIN_BARNABY_4974, IntType.NPC, "talk-to") { player, npc -> + openDialogue(player,CaptainBarnabyDialogue(), npc) + return@on true + } + on(NPCs.CAPTAIN_BARNABY_4974, IntType.NPC, "pay-fare") { player, _ -> + if (removeItem(player,Item(Items.COINS_995,30))) { + sendMessage(player,"You pay 30 coins and board the ship.") + playJingle(player, 171) + Ships.ARDOUGNE_TO_BRIMHAVEN.sail(player) + } else { + sendMessage(player,"You don't have enough coins.") + } + return@on true + } + } +} + +class CaptainBarnabyDialogue : DialogueFile(){ + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + 0 -> npcl("Do you want to go on a trip to Brimhaven?").also { stage += 1 } + 1 -> npcl( "The trip will cost you 30 coins.").also { stage += 1 } + 2 -> options("Yes please.", "No, thank you.").also { stage += 1 } + 3 -> when (buttonID) { + 1 -> playerl("Yes please.").also { stage = 10 } + 2 -> playerl("No, thank you.").also { stage = END_DIALOGUE } + } + 10 -> { + if (removeItem(player!!,Item(Items.COINS_995,30))) { + sendMessage(player!!,"You pay 30 coins and board the ship.") + playJingle(player!!, 171) + Ships.ARDOUGNE_TO_BRIMHAVEN.sail(player) + stage = END_DIALOGUE + } else{ + playerl("Sorry, I don't seem to have enough coins.").also{ stage = END_DIALOGUE } + } + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/ardougne/dialogue/ChadwellDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/dialogue/ChadwellDialogue.kt index 2fcad3fff..118e67f4b 100644 --- a/Server/src/main/content/region/kandarin/ardougne/dialogue/ChadwellDialogue.kt +++ b/Server/src/main/content/region/kandarin/ardougne/dialogue/ChadwellDialogue.kt @@ -12,11 +12,11 @@ import org.rs09.consts.NPCs */ @Initializable -class ChadwellDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class ChadwellDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - npc(core.game.dialogue.FacialExpression.FRIENDLY,"Good day. What can I get you?").also { stage = 0 } + npc(FacialExpression.FRIENDLY,"Good day. What can I get you?").also { stage = 0 } return true } @@ -24,20 +24,20 @@ class ChadwellDialogue(player: Player? = null) : core.game.dialogue.DialoguePlug when(stage){ 0 -> options("Let's see what you've got.", "Nothing thanks.").also { stage++ } 1 -> when (buttonId) { - 1 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "Let's see what you've got.").also { stage = 5 } - 2 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "Nothing thanks.").also { stage = 10 } + 1 -> player(FacialExpression.FRIENDLY, "Let's see what you've got.").also { stage = 5 } + 2 -> player(FacialExpression.FRIENDLY, "Nothing thanks.").also { stage = 10 } } 5 -> end().also { npc.openShop(player) } - 10 -> npc(core.game.dialogue.FacialExpression.FRIENDLY, "Okay then.").also { stage = 99 } + 10 -> npc(FacialExpression.FRIENDLY, "Okay then.").also { stage = 99 } 99 -> end() } return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return ChadwellDialogue(player) } diff --git a/Server/src/main/content/region/kandarin/ardougne/dialogue/FurTraderDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/dialogue/FurTraderDialogue.kt new file mode 100644 index 000000000..0eafd5c61 --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/dialogue/FurTraderDialogue.kt @@ -0,0 +1,50 @@ +package content.region.kandarin.ardougne.dialogue + +import core.api.openNpcShop +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 + +@Initializable +class FurTraderDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun newInstance(player: Player?): DialoguePlugin { + return FurTraderDialogue(player) + } + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + npcl( + FacialExpression.ASKING, "Would you like to trade in fur?" + ).also { stage = START_DIALOGUE } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage) { + 0 -> options("Yes.", "No.").also { stage++ } + 1 -> when (buttonId) { + 1 -> { + openNpcShop(player, NPCs.FUR_TRADER_573) + end() + } + + 2 -> playerl( + FacialExpression.HALF_GUILTY, "No, thanks." + ).also { stage = END_DIALOGUE } + + } + } + return true + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.FUR_TRADER_573) + } + +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/ardougne/dialogue/GemMerchantDialogue.java b/Server/src/main/content/region/kandarin/ardougne/dialogue/GemMerchantDialogue.java deleted file mode 100644 index a4474d0f0..000000000 --- a/Server/src/main/content/region/kandarin/ardougne/dialogue/GemMerchantDialogue.java +++ /dev/null @@ -1,62 +0,0 @@ -package content.region.kandarin.ardougne.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 gem merchant dialogue plugin. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class GemMerchantDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code GemMerchantDialogue} {@code Object}. - */ - public GemMerchantDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code GemMerchantDialogue} {@code Object}. - * @param player the player. - */ - public GemMerchantDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new GemMerchantDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Here, look at my lovely gems."); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - end(); - npc.openShop(player); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 570 }; - } -} diff --git a/Server/src/main/content/region/kandarin/ardougne/dialogue/GemMerchantDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/dialogue/GemMerchantDialogue.kt new file mode 100644 index 000000000..b7e2fd4d7 --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/dialogue/GemMerchantDialogue.kt @@ -0,0 +1,40 @@ +package content.region.kandarin.ardougne.dialogue + +import core.api.openNpcShop +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.START_DIALOGUE +import org.rs09.consts.NPCs + +@Initializable +class GemMerchantDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun newInstance(player: Player?): DialoguePlugin { + return GemMerchantDialogue(player) + } + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + npcl( + FacialExpression.HAPPY, "Here, look at my lovely gems." + ).also { stage = START_DIALOGUE } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage) { + 0 -> { + openNpcShop(player, NPCs.GEM_MERCHANT_570) + end() + } + } + return true + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.GEM_MERCHANT_570) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/ardougne/dialogue/IorwerthsMessageScroll.kt b/Server/src/main/content/region/kandarin/ardougne/dialogue/IorwerthsMessageScroll.kt new file mode 100644 index 000000000..c5990fe79 --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/dialogue/IorwerthsMessageScroll.kt @@ -0,0 +1,21 @@ +package content.region.kandarin.ardougne.dialogue + +import core.api.openInterface +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import org.rs09.consts.Components +import org.rs09.consts.Items + +class IorwerthsMessageScroll : InteractionListener { + // Receive from the Lord Iorwerth after killing King Tyras in Regicide quest. + override fun defineListeners() { + val kingsmessage = Components.KINGS_LETTER_V2_463 + val iorwerthsscroll = Items.IORWERTHS_MESSAGE_3207 + on(iorwerthsscroll, IntType.ITEM, "read") { player, _ -> + openInterface(player, kingsmessage) + player.packetDispatch.sendString("Your Majesty King Lathas
Your man did well. The path is now open for the dark lord to enter this realm. You will yet live to see Camelot crushed under foot.", kingsmessage, 1) + player.packetDispatch.sendString("Warmaster Iorwerth", kingsmessage, 2) + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/ardougne/dialogue/KingsMessageScroll.kt b/Server/src/main/content/region/kandarin/ardougne/dialogue/KingsMessageScroll.kt new file mode 100644 index 000000000..474d3faa3 --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/dialogue/KingsMessageScroll.kt @@ -0,0 +1,21 @@ +package content.region.kandarin.ardougne.dialogue + +import core.api.openInterface +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import org.rs09.consts.Components +import org.rs09.consts.Items + +class KingsMessageScroll : InteractionListener { + // Receive from the King's messenger some time after completing the Underground Pass quest. + override fun defineListeners() { + val kingsmessage = Components.KINGS_LETTER_V2_463 + val kingsscroll = Items.KINGS_MESSAGE_3206 + on(kingsscroll, IntType.ITEM, "read") { player, _ -> + openInterface(player, kingsmessage) + player.packetDispatch.sendString("Squire ${player.username}
You are needed to serve the Kingdom of Kandarin. At last our magi have reopened the well of voyage. We must discuss your next commission.", kingsmessage, 1) + player.packetDispatch.sendString("His Majesty King Lathas", kingsmessage, 2) + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/ardougne/dialogue/MonkeyChildThingZooDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/dialogue/MonkeyChildThingZooDialogue.kt index 72415b9eb..4aa655a47 100644 --- a/Server/src/main/content/region/kandarin/ardougne/dialogue/MonkeyChildThingZooDialogue.kt +++ b/Server/src/main/content/region/kandarin/ardougne/dialogue/MonkeyChildThingZooDialogue.kt @@ -13,21 +13,21 @@ import org.rs09.consts.NPCs */ @Initializable -class MonkeyChildThingZooDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class MonkeyChildThingZooDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - npc(core.game.dialogue.FacialExpression.FRIENDLY,"").also { stage = 0 } + npc(FacialExpression.FRIENDLY,"").also { stage = 0 } if (!player.equipment.contains(Items.MSPEAK_AMULET_4021, 1)) { - npc(core.game.dialogue.FacialExpression.OLD_LAUGH1, "Eeekeek ookeek!").also { stage = 99 } + npc(FacialExpression.OLD_LAUGH1, "Eeekeek ookeek!").also { stage = 99 } } else { var a = 1..5 when(a.random()) { - 1 -> npc(core.game.dialogue.FacialExpression.OLD_LAUGH1, "Arr!").also { stage = 10 } - 2 -> npcl(core.game.dialogue.FacialExpression.OLD_LAUGH1, "Let me go, can't ye hear them? Howlin' in the dark...").also { stage = 20 } - 3 -> npcl(core.game.dialogue.FacialExpression.OLD_DEFAULT, "I'm not goin' back in that brewery, not fer all the Bitternuts I can carry!").also { stage = 99 } - 4 -> npc(core.game.dialogue.FacialExpression.OLD_DEFAULT, "Are ye here for...the stuff?").also { stage = 30 } - 5 -> npc(core.game.dialogue.FacialExpression.OLD_DISTRESSED, "Arr! Yer messin with me monkey plunder!").also { stage = 40 } + 1 -> npc(FacialExpression.OLD_LAUGH1, "Arr!").also { stage = 10 } + 2 -> npcl(FacialExpression.OLD_LAUGH1, "Let me go, can't ye hear them? Howlin' in the dark...").also { stage = 20 } + 3 -> npcl(FacialExpression.OLD_DEFAULT, "I'm not goin' back in that brewery, not fer all the Bitternuts I can carry!").also { stage = 99 } + 4 -> npc(FacialExpression.OLD_DEFAULT, "Are ye here for...the stuff?").also { stage = 30 } + 5 -> npc(FacialExpression.OLD_DISTRESSED, "Arr! Yer messin with me monkey plunder!").also { stage = 40 } } } return true @@ -36,32 +36,32 @@ class MonkeyChildThingZooDialogue(player: Player? = null) : core.game.dialogue.D override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 10 -> player(core.game.dialogue.FacialExpression.JOLLY, "Arr!").also { stage++ } - 11 -> npc(core.game.dialogue.FacialExpression.OLD_LAUGH1, "Arr!").also { stage++ } - 12 -> player(core.game.dialogue.FacialExpression.JOLLY, "Arr!").also { stage++ } - 13 -> npc(core.game.dialogue.FacialExpression.OLD_LAUGH1, "Arr!").also { stage++ } - 14 -> player(core.game.dialogue.FacialExpression.JOLLY, "Arr!").also { stage++ } - 15 -> npc(core.game.dialogue.FacialExpression.OLD_LAUGH1, "Arr!").also { stage++ } - 16 -> player(core.game.dialogue.FacialExpression.JOLLY, "Arr!").also { stage++ } - 17 -> npc(core.game.dialogue.FacialExpression.OLD_LAUGH1, "Bored now...").also { stage = 99 } + 10 -> player(FacialExpression.JOLLY, "Arr!").also { stage++ } + 11 -> npc(FacialExpression.OLD_LAUGH1, "Arr!").also { stage++ } + 12 -> player(FacialExpression.JOLLY, "Arr!").also { stage++ } + 13 -> npc(FacialExpression.OLD_LAUGH1, "Arr!").also { stage++ } + 14 -> player(FacialExpression.JOLLY, "Arr!").also { stage++ } + 15 -> npc(FacialExpression.OLD_LAUGH1, "Arr!").also { stage++ } + 16 -> player(FacialExpression.JOLLY, "Arr!").also { stage++ } + 17 -> npc(FacialExpression.OLD_LAUGH1, "Bored now...").also { stage = 99 } - 20 -> player(core.game.dialogue.FacialExpression.ASKING, "What do you mean?").also { stage++ } - 21 -> npc(core.game.dialogue.FacialExpression.OLD_DISTRESSED, "I'm not hangin' around te be killed!").also { stage++ } - 22 -> npc(core.game.dialogue.FacialExpression.OLD_DISTRESSED, "The Horrors, the Horrors!").also { stage = 99 } + 20 -> player(FacialExpression.ASKING, "What do you mean?").also { stage++ } + 21 -> npc(FacialExpression.OLD_DISTRESSED, "I'm not hangin' around te be killed!").also { stage++ } + 22 -> npc(FacialExpression.OLD_DISTRESSED, "The Horrors, the Horrors!").also { stage = 99 } - 30 -> player(core.game.dialogue.FacialExpression.ASKING, "What?").also { stage++ } - 31 -> npc(core.game.dialogue.FacialExpression.OLD_DEFAULT, "You know...the 'special' bananas?").also { stage++ } - 32 -> player(core.game.dialogue.FacialExpression.ASKING, "No... why do you ask?").also { stage++ } - 33 -> npc(core.game.dialogue.FacialExpression.OLD_SAD, "No reason. Have a nice day.").also { stage = 99 } + 30 -> player(FacialExpression.ASKING, "What?").also { stage++ } + 31 -> npc(FacialExpression.OLD_DEFAULT, "You know...the 'special' bananas?").also { stage++ } + 32 -> player(FacialExpression.ASKING, "No... why do you ask?").also { stage++ } + 33 -> npc(FacialExpression.OLD_SAD, "No reason. Have a nice day.").also { stage = 99 } - 40 -> player(core.game.dialogue.FacialExpression.ASKING, "What?").also { stage = 99 } + 40 -> player(FacialExpression.ASKING, "What?").also { stage = 99 } 99 -> end() } return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return MonkeyChildThingZooDialogue(player) } 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/dialogue/ShopKeeperDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/dialogue/ShopKeeperDialogue.kt new file mode 100644 index 000000000..94051bec4 --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/dialogue/ShopKeeperDialogue.kt @@ -0,0 +1,62 @@ +package content.region.kandarin.ardougne.dialogue + +import content.region.kandarin.dialogue.ShopKeeperDialogue +import core.api.openNpcShop +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 + +@Initializable +class ShopKeeperDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun newInstance(player: Player?): DialoguePlugin { + return content.region.kandarin.ardougne.dialogue.ShopKeeperDialogue(player) + } + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + npcl( + FacialExpression.FRIENDLY, + "Hello, you look like a bold adventurer. You've come to the right place for adventurers' equipment." + ).also { stage = START_DIALOGUE } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage) { + 0 -> options( + "Oh, that sounds interesting.", + "How should I use your shop?", + "No, sorry, I've come to the wrong place." + ).also { stage++ } + + 1 -> when (buttonId) { + 1 -> { + openNpcShop(player, NPCs.AEMAD_590) + end() + } + + 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 -> npcl( + FacialExpression.HALF_GUILTY, "Hmph. Well, perhaps next time you'll need something from me?" + ).also { stage = END_DIALOGUE } + + } + } + return true + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.AEMAD_590, NPCs.KORTAN_591) + } + +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/ardougne/dialogue/SilkMerchantPlugin.java b/Server/src/main/content/region/kandarin/ardougne/dialogue/SilkMerchantPlugin.java index c8239247c..616a942cc 100644 --- a/Server/src/main/content/region/kandarin/ardougne/dialogue/SilkMerchantPlugin.java +++ b/Server/src/main/content/region/kandarin/ardougne/dialogue/SilkMerchantPlugin.java @@ -45,13 +45,13 @@ public class SilkMerchantPlugin extends DialoguePlugin { switch (stage) { case 0: if (player.getInventory().containsItem(SILK)) { - interpreter.sendDialogues(player, null, "Hello. I have some fine silk from Al-Kharid to sell to", "you."); + interpreter.sendDialogues(player, FacialExpression.FRIENDLY, "Hello. I have some fine silk from Al-Kharid to sell to", "you."); stage = 1; } else if (player.getInventory().containsItem(NOTED_SILK)) { - interpreter.sendDialogues(player, null, "I've got some silk here. Will you buy it?"); + interpreter.sendDialogues(player, FacialExpression.ASKING, "I've got some silk here. Will you buy it?"); stage = 100; } else { - interpreter.sendDialogues(player, null, "Sorry, I don't have any silk."); + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "Sorry, I don't have any silk."); stage = 200; } break; @@ -62,7 +62,7 @@ public class SilkMerchantPlugin extends DialoguePlugin { end(); break; case 1: - interpreter.sendDialogues(npc, null, "Ah, I may be interested in that. What sort of price were", "you looking at per piece of silk?"); + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Ah, I may be interested in that. What sort of price were", "you looking at per piece of silk?"); stage = 2; break; case 2: @@ -72,26 +72,26 @@ public class SilkMerchantPlugin extends DialoguePlugin { case 3: switch (buttonId) { case 1: - interpreter.sendDialogues(player, null, "20 coins."); + interpreter.sendDialogues(player, FacialExpression.FRIENDLY, "20 coins."); stage = 1000; break; case 2: - interpreter.sendDialogues(player, null, "30 coins."); + interpreter.sendDialogues(player, FacialExpression.FRIENDLY, "30 coins."); stage = 2000; break; case 3: - interpreter.sendDialogues(player, null, "120 coins."); + interpreter.sendDialogues(player, FacialExpression.FRIENDLY, "120 coins."); stage = 300; break; case 4: - interpreter.sendDialogues(player, null, "200 coins."); + interpreter.sendDialogues(player, FacialExpression.FRIENDLY, "200 coins."); stage = 400; break; } break; case 1000: case 2000: - interpreter.sendDialogues(npc, null, "That price is fine by me! Hand over your silk."); + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "That price is fine by me! Hand over your silk."); stage = stage == 1000 ? 1010 : 1011; break; case 1010: @@ -102,7 +102,7 @@ public class SilkMerchantPlugin extends DialoguePlugin { break; case 300: case 400: - interpreter.sendDialogues(npc, null, "You'll never get that much for it. I'll be generous and", "give you 50 for it."); + interpreter.sendDialogues(npc, FacialExpression.FRIENDLY, "You'll never get that much for it. I'll be generous and", "give you 50 for it."); stage = 500; break; case 500:// use this for selling. @@ -112,15 +112,15 @@ public class SilkMerchantPlugin extends DialoguePlugin { case 501: switch (buttonId) { case 1: - interpreter.sendDialogues(player, null, "Ok, I guess 50 will do."); + interpreter.sendDialogues(player, FacialExpression.FRIENDLY, "Ok, I guess 50 will do."); stage = 510; break; case 2: - interpreter.sendDialogues(player, null, "I'll give it to you for 60."); + interpreter.sendDialogues(player, FacialExpression.HAPPY, "I'll give it to you for 60."); stage = 520; break; case 3: - interpreter.sendDialogues(player, null, "No, that is not enough."); + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No, that is not enough."); stage = 530; break; } @@ -129,7 +129,7 @@ public class SilkMerchantPlugin extends DialoguePlugin { buy(50); break; case 520: - interpreter.sendDialogues(npc, null, "You drive a hard bargain, but", "I guess that will have to do."); + interpreter.sendDialogues(npc, FacialExpression.FRIENDLY, "You drive a hard bargain, but", "I guess that will have to do."); stage = 521; break; case 521: @@ -139,7 +139,7 @@ public class SilkMerchantPlugin extends DialoguePlugin { end(); break; case 100: - interpreter.sendDialogues(npc, null, "Silk? Yo're not carrying any silk."); + interpreter.sendDialogues(npc, null, "Silk? You're not carrying any silk."); stage = 101; break; case 101: @@ -213,7 +213,7 @@ public class SilkMerchantPlugin extends DialoguePlugin { }); return false; } - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "I buy silk. If you ever want to", "sell any silk, bring it here."); + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "I buy silk. If you ever want to", "sell any silk, bring it here."); stage = 0; return true; } diff --git a/Server/src/main/content/region/kandarin/ardougne/dialogue/SilkTradeDialogue.java b/Server/src/main/content/region/kandarin/ardougne/dialogue/SilkTradeDialogue.java index 3e9a8c4f7..dc2a5b1ca 100644 --- a/Server/src/main/content/region/kandarin/ardougne/dialogue/SilkTradeDialogue.java +++ b/Server/src/main/content/region/kandarin/ardougne/dialogue/SilkTradeDialogue.java @@ -31,13 +31,13 @@ public class SilkTradeDialogue extends DialoguePlugin { public boolean handle(int interfaceId, int buttonId) { switch (stage) { case 0: - interpreter.sendOptions("Select an Option", "How much are they?", "No, slik doesn't suit me."); + interpreter.sendOptions("Select an Option", "How much are they?", "No, silk doesn't suit me."); stage = 1; break; case 1: switch (buttonId) { case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "How much are they?"); + interpreter.sendDialogues(player, FacialExpression.HALF_ASKING, "How much are they?"); stage = 10; break; case 2: @@ -49,7 +49,7 @@ public class SilkTradeDialogue extends DialoguePlugin { break; case 10: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "3gp."); + interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "3gp."); stage = 11; break; case 11: @@ -74,7 +74,7 @@ public class SilkTradeDialogue extends DialoguePlugin { stage = 111; break; case 111: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "I'm not selling it for any less. You'll only go and sell it", "in Varrock for a profit."); + interpreter.sendDialogues(npc, FacialExpression.ANNOYED, "I'm not selling it for any less. You'll only go and sell it", "in Varrock for a profit."); stage = 113; break; case 113: @@ -84,7 +84,7 @@ public class SilkTradeDialogue extends DialoguePlugin { case 114: switch (buttonId) { case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "2gp sounds good."); + interpreter.sendDialogues(player, FacialExpression.HAPPY, "2gp sounds good."); stage = 1000; break; case 2: @@ -140,7 +140,7 @@ public class SilkTradeDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Do you want to buy any fine silks?"); + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Do you want to buy any fine silks?"); stage = 0; return true; } diff --git a/Server/src/main/content/region/kandarin/ardougne/dialogue/SilverMechantPlugin.java b/Server/src/main/content/region/kandarin/ardougne/dialogue/SilverMechantPlugin.java index 9e5bfb83a..045a51c3f 100644 --- a/Server/src/main/content/region/kandarin/ardougne/dialogue/SilverMechantPlugin.java +++ b/Server/src/main/content/region/kandarin/ardougne/dialogue/SilverMechantPlugin.java @@ -40,7 +40,7 @@ public class SilverMechantPlugin extends DialoguePlugin { npc.openShop(player); break; case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No, thank you."); + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "No, thank you."); stage = 20; break; } @@ -61,7 +61,7 @@ public class SilverMechantPlugin extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Silver! Silver! Best prices for buying and selling in all", "Kandarin!"); + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Silver! Silver! Best prices for buying and selling in all", "Kandarin!"); stage = 0; return true; } diff --git a/Server/src/main/content/region/kandarin/ardougne/dialogue/SpiceSellerPlugin.java b/Server/src/main/content/region/kandarin/ardougne/dialogue/SpiceSellerPlugin.java index b1f486cae..593d0ac04 100644 --- a/Server/src/main/content/region/kandarin/ardougne/dialogue/SpiceSellerPlugin.java +++ b/Server/src/main/content/region/kandarin/ardougne/dialogue/SpiceSellerPlugin.java @@ -62,7 +62,7 @@ public class SpiceSellerPlugin extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Are you interested in buying or selling spice?"); + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Are you interested in buying or selling spice?"); stage = 0; return true; } diff --git a/Server/src/main/content/region/kandarin/ardougne/dialogue/ZookeeperDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/dialogue/ZookeeperDialogue.kt index 6630c1225..c4ff1a89e 100644 --- a/Server/src/main/content/region/kandarin/ardougne/dialogue/ZookeeperDialogue.kt +++ b/Server/src/main/content/region/kandarin/ardougne/dialogue/ZookeeperDialogue.kt @@ -13,56 +13,56 @@ import org.rs09.consts.NPCs */ @Initializable -class ZookeeperDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class ZookeeperDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - player(core.game.dialogue.FacialExpression.FRIENDLY,"Hi!").also { stage = 0 } + player(FacialExpression.FRIENDLY,"Hi!").also { stage = 0 } return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ 0 -> { - npc(core.game.dialogue.FacialExpression.FRIENDLY, "Welcome to the Ardougne Zoo! How can I help you?").also { stage++ } + npc(FacialExpression.FRIENDLY, "Welcome to the Ardougne Zoo! How can I help you?").also { stage++ } } 1 -> { options("Do you have any quests for me?", "Where did you get the animals from?").also { stage++ } } 2 -> when (buttonId) { - 1 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "Do you have any quests for me?").also { stage = 10 } - 2 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "Where did you get the animals from?").also { stage = 20 } + 1 -> player(FacialExpression.FRIENDLY, "Do you have any quests for me?").also { stage = 10 } + 2 -> player(FacialExpression.FRIENDLY, "Where did you get the animals from?").also { stage = 20 } } 10 -> { - npc(core.game.dialogue.FacialExpression.FRIENDLY, "Not at the moment.", + npc(FacialExpression.FRIENDLY, "Not at the moment.", "The explorers that come back from far away lands tell", "such amazing tales. Make sure you keep eyes and ears", "open as you may find new places to explore!").also { stage++ } } 11 -> { - player(core.game.dialogue.FacialExpression.FRIENDLY, "Ooh, I will!").also { stage = 99 } + player(FacialExpression.FRIENDLY, "Ooh, I will!").also { stage = 99 } } 20 -> { - npc(core.game.dialogue.FacialExpression.FRIENDLY, "We get them from all over the place!", + npc(FacialExpression.FRIENDLY, "We get them from all over the place!", "The most exotic creatures have been brought", "back by explorers and sold to us.").also { stage++ } } 21 -> { - player(core.game.dialogue.FacialExpression.HALF_ASKING, "Where on Gielinor did you get that scary", + player(FacialExpression.HALF_ASKING, "Where on Gielinor did you get that scary", "looking Cyclops?!").also { stage++ } } 22 -> { - npc(core.game.dialogue.FacialExpression.LAUGH, "Yes he is scary looking!").also { stage++ } + npc(FacialExpression.LAUGH, "Yes he is scary looking!").also { stage++ } } 23 -> { - npc(core.game.dialogue.FacialExpression.FRIENDLY, "He's from a very far away land, we couldn't", + npc(FacialExpression.FRIENDLY, "He's from a very far away land, we couldn't", "find out more as the explorer who brought", "him back died shortly afterwards!").also { stage = 99 } } @@ -72,7 +72,7 @@ class ZookeeperDialogue(player: Player? = null) : core.game.dialogue.DialoguePlu return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return ZookeeperDialogue(player) } 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 e1399e051..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; @@ -17,10 +17,14 @@ import core.game.world.map.Location; import core.game.world.update.flag.context.Animation; import core.plugin.Plugin; 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 @@ -64,12 +68,16 @@ public final class WildernessLeverPlugin extends OptionHandler { private static void pullLever(final Player player, final LeverSets lever, final int index) { player.lock(2); player.animate(lever.getAnimation()); - player.getAudioManager().send(new Audio(2400)); + playAudio(player, Sounds.LEVER_2400); GameWorld.getPulser().submit(new Pulse(2, player) { @Override public boolean pulse() { - lever.message(player, index); - lever.teleport(player, lever.getLocation(index)); + if (player.timers.getTimer("teleblock") == null) { + lever.message(player, index); + lever.teleport(player, lever.getLocation(index)); + } else { + player.getPacketDispatch().sendMessage("A magical force has stopped you from teleporting."); + } return true; } }); @@ -272,7 +280,6 @@ public final class WildernessLeverPlugin extends OptionHandler { public Location[] getLocations() { return locations; } - } /** @@ -318,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/quest/arena/FightArena.kt b/Server/src/main/content/region/kandarin/ardougne/quest/arena/FightArena.kt new file mode 100644 index 000000000..9c97b7110 --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/quest/arena/FightArena.kt @@ -0,0 +1,103 @@ +package content.region.kandarin.ardougne.quest.arena + +import core.api.addItemOrDrop +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.COINS_995 +import content.data.Quests + +@Initializable +class FightArena : Quest(Quests.FIGHT_ARENA, 61, 60, 2, 17, 0, 1, 14) { + override fun newInstance(`object`: Any?): Quest { return this } + override fun drawJournal(player: Player?, stage: Int) { + super.drawJournal(player, stage) + var line = 11 + player ?: return + if (stage == 0) { + line(player, "I can start this quest by speaking to !!Lady Servil?? just", line++) + line(player, "!!North-West?? of the !!Khazard Port??.", line++) + line += 1 + line(player, "I must be able to defeat a !!level 137?? enemy.", line++, player.properties.combatLevel >= 89) + line++ + } + if (stage >= 10) { + line += 1 + line(player, "I encountered a distraught Lady Servil, who said that her son and", line++, stage > 10) + line(player, "husband had been kidnapped by the evil General Khazard,", line++, stage > 10) + line(player, "and were being forced to fight in his fight arena.", line++, stage > 10) + line += 1 + line(player, "I headed to the arena to try and find Lady Servil's son and husband.", line++, stage > 10) + line++ + } + if (stage >= 20) { + line(player, "I found some Khazard armour in the armoury, on the north-east edge of", line++, stage > 20) + line(player, "town. I used it to disguise myself as a guard so I could look around", line++, stage > 20) + line++ + } + if (stage >= 35) { + line(player, "I found Lady Servil's son, Jeremy Servil, in one of the prison cells.", line++, stage > 40) + line(player, "He told me that a bald, fat, lazy guard", line++, stage > 40) + line(player, "with a goatee was in charge of the keys.", line++, stage > 40) + line++ + } + if (stage >= 50) { + line(player, "I found the guard Jeremy mentioned. He said that he'd like a drink,", line++, stage > 55) + line(player, "but too much Khali brew would make him fall asleep.", line++, stage > 55) + line++ + } + if (stage >= 60) { + line(player, "I plied the lazy guard with some Khali brew and he passed out.", line++, stage > 67) + line(player, "I was able to get his keys.", line++, stage > 67) + line++ + } + if (stage >= 68) { + line(player, "I found and freed Jeremy, who told me that his father", line++, stage > 69) + line(player, "had been taken to fight in the Fight Arena. We went there to save him.", line++, stage > 69) + line++ + } + if (stage >= 71) { + line(player, "I had to fight a large ogre to stop it killing Sir Servil.", line++, stage > 72) + line(player, "When I'd defeated it, General Khazard had locked me up.", line++, stage > 72) + line++ + } + if (stage >= 88) { + line(player, "I was led to the fight arena and forced to fight a colossal scorpion,", line++, stage > 88) + line(player, "followed by Khazard's monstrous pet dog called Bouncer.", line++, stage > 90) + line++ + } + if (stage >= 91) { + line(player, "General Khazard released the Servils, but he was so angry", line++, stage >= 99) + line(player, "that I'd killed Bouncer that he came to fight me himself.", line++, stage >= 99) + line++ + } + if (stage >= 99) { + line(player, "I escaped from the arena and returned to Lady Servil", line++, stage == 100) + line(player, "She thanked me profusely and rewarded me for my help.", line++, stage == 100) + line++ + } + if (stage == 100) { + line++ + line(player, "%%QUEST COMPLETE!&&", line) + } + } + + override fun finish(player: Player?) { + super.finish(player) + player ?: return + var ln = 10 + + player.packetDispatch.sendItemZoomOnInterface(COINS_995, 230, 277, 5) + + drawReward(player, "2 Quest Point", ln++) + drawReward(player, "1000 gold coins", ln++) + drawReward(player, "12,175 Attack XP", ln++) + drawReward(player, "2,175 Thieving XP", ln++) + + rewardXP(player, Skills.ATTACK, 12175.0) + rewardXP(player, Skills.THIEVING, 2175.0) + addItemOrDrop(player, COINS_995, 1000) + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..8e4526390 --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/quest/arena/FightArenaListeners.kt @@ -0,0 +1,286 @@ +package content.region.kandarin.ardougne.quest.arena + +import content.region.kandarin.ardougne.quest.arena.dialogue.* +import content.region.kandarin.ardougne.quest.arena.npc.GeneralNPC +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.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 { + + const val GENERAL = NPCs.GENERAL_KHAZARD_258 + const val BARMAN = NPCs.KHAZARD_BARMAN_259 + const val KELVIN = NPCs.KELVIN_260 + const val JOE = NPCs.JOE_261 + const val FIGHTSLAVE = NPCs.FIGHTSLAVE_262 + const val HENGARD = NPCs.HENGRAD_263 + const val JEREMY_A = NPCs.JEREMY_SERVIL_265 + const val JEREMY_B = NPCs.JEREMY_SERVIL_266 + const val JUSTIN = NPCs.JUSTIN_SERVIL_267 + + const val CELL_DOOR_1 = Scenery.PRISON_DOOR_79 + const val CELL_DOOR_2 = Scenery.PRISON_DOOR_80 + const val MAIN_DOOR = Scenery.DOOR_81 + const val CENTER_DOOR = Scenery.DOOR_82 + + const val A_LAZY_GUARD_1 = Scenery.A_LAZY_KHAZARD_GUARD_41494 + const val A_LAZY_GUARD_2 = Scenery.A_LAZY_KHAZARD_GUARD_41496 + const val A_LAZY_GUARD_3 = Scenery.A_LAZY_KHAZARD_GUARD_41497 + + val FULL_ARMOR_STAND = Scenery.A_SUIT_OF_ARMOUR_41490 + val FULL_ARMOR_STAND_1 = getScenery(2619, 3196, 0) + val ONLY_ARMOR_STAND = Scenery.A_SUIT_OF_ARMOUR_41506 + val ONLY_HELM_STAND = Scenery.A_SUIT_OF_ARMOUR_41507 + val EMPTY_STAND = Scenery.A_SUIT_OF_ARMOUR_41508 + + private const val HELMET = Items.KHAZARD_HELMET_74 + private const val ARMOR = Items.KHAZARD_ARMOUR_75 + private const val CELL_KEY = Items.KHAZARD_CELL_KEYS_76 + + val Jeremy = NPC(NPCs.JEREMY_SERVIL_265, Location.create(2616,3167,0)) + val General = GeneralNPC(NPCs.GENERAL_KHAZARD_258, Location.create(2605,3156,0)) + } + + init { + Jeremy.init() + Jeremy.isWalks = true + + General.init() + General.isWalks = true + } + + override fun defineListeners() { + + on(GENERAL, IntType.NPC, "talk-to") { player, npc -> + openDialogue(player, GeneralKhazardDialogue(), npc) + return@on true + } + + on(BARMAN, IntType.NPC, "talk-to") { player, npc -> + openDialogue(player, KhazardBarmanDialogue(), npc) + return@on true + } + + on(JEREMY_A, IntType.NPC, "talk-to") { player, npc -> + openDialogue(player, JeremyServilADialogue(), npc) + return@on true + } + + on(JEREMY_B, IntType.NPC, "talk-to") { player, npc -> + openDialogue(player, JeremyServilBDialogue(), npc) + return@on true + } + + on(HENGARD, IntType.NPC, "talk-to") { player, npc -> + openDialogue(player, HengradDialogue(), npc) + return@on true + } + + on(A_LAZY_GUARD_1, SCENERY, "talk-to") { player, node -> + openDialogue(player, ALazyGuardDialogue(), node) + return@on true + } + on(A_LAZY_GUARD_3, SCENERY, "talk-to") { player, node -> + openDialogue(player, ALazyGuardDialogue(), node) + return@on true + } + + on(JUSTIN, IntType.NPC, "talk-to") { player, npc -> + openDialogue(player, JustinServilDialogue(), npc) + return@on true + } + + on(FULL_ARMOR_STAND, IntType.SCENERY, "borrow") { player, _ -> + 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) + addItem(player, HELMET, 1) + } else if (!inEquipmentOrInventory(player, ARMOR) && freeSlots(player) >= 1){ + replaceScenery(FULL_ARMOR_STAND_1!!.asScenery(), ONLY_HELM_STAND, 10,location(2619, 3196, 0)) + addItem(player, ARMOR, 1) + sendMessage(player, "You borrow the suit of helmet. It looks like it's just your size.") + } else if (!inEquipmentOrInventory(player, HELMET) && freeSlots(player) >= 1){ + replaceScenery(FULL_ARMOR_STAND_1!!.asScenery(), ONLY_ARMOR_STAND, 10,location(2619, 3196, 0)) + addItem(player, HELMET, 1) + sendMessage(player, "You could borrow this suit of armour if you had space in your inventory.") + } else if (freeSlots(player) == 0) { + sendMessage(player, "You could borrow this suit of armour if you had space in your inventory.") + } else { + sendMessage(player, "Nothing interesting happens.") + } + return@on true + } + + on(A_LAZY_GUARD_2, IntType.SCENERY, "steal-keys") { player, _ -> + player.faceLocation(location(2618, 3144, 0)) + setVarbit(player,5627,3, true) + animate(player, 2286) + addItemOrDrop(player, CELL_KEY) + sendMessage(player, "You pick up the keys from the table.") + return@on true + } + + on(MAIN_DOOR, IntType.SCENERY, "open") { player, maingate -> + when (player.location.y) { + 3171 -> DoorActionHandler.handleAutowalkDoor(player, maingate.asScenery()) + 3172 -> { + if (allInEquipment(player, HELMET, ARMOR)) { + openDialogue(player, EastDoorSupportDialogue()) + } else { + sendPlayerDialogue(player, "This door appears to be locked.") + } + return@on true + } + } + when (player.location.x) { + 2585 -> DoorActionHandler.handleAutowalkDoor(player, maingate.asScenery()) + 2584 -> { + if (allInEquipment(player, HELMET, ARMOR)) { + openDialogue(player, WestDoorSupportDialogue()) + } else { + sendPlayerDialogue(player, "This door appears to be locked.") + } + return@on true + } + } + return@on true + } + + onUseWith(IntType.SCENERY, CELL_KEY, CELL_DOOR_2) { player, _, _ -> + openDialogue(player, JeremyServilADialogue()) + return@onUseWith true + } + + onUseWith(IntType.SCENERY, CELL_KEY, CELL_DOOR_1) { player, _, _ -> + 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.") + } + return@onUseWith false + } + + on(CELL_DOOR_1, IntType.SCENERY, "open") { player, _ -> + sendMessage(player, "the cell gate is securely locked.") + return@on false + } + + on(CELL_DOOR_2, IntType.SCENERY, "open") { player, _ -> + sendMessage(player, "the cell gate is securely locked.") + return@on false + } + + on(CENTER_DOOR, IntType.SCENERY, "open") { player, node -> + if (player.questRepository.getStage(Quests.FIGHT_ARENA) >= 91) { + DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) + } 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.") + } + return@on true + } + } + + override fun defineDestinationOverrides() { + + setDest(IntType.NPC, intArrayOf(JEREMY_A), "talk-to") { _, _ -> + return@setDest Location.create(2617, 3167, 0) + } + + setDest(IntType.NPC, intArrayOf(BARMAN), "talk-to") { _, _ -> + return@setDest Location.create(2566, 3142, 0) + } + + setDest(IntType.NPC, intArrayOf(FIGHTSLAVE), "talk-to") { player, _ -> + if (inBorders(player, 2585, 3139, 2605, 3148)) { + return@setDest Location.create(2595, 3141, 0) + } else if (inBorders(player, 2616, 3162, 2617, 3165)) { + return@setDest Location.create(2617, 3163, 0) + } else { + return@setDest Location.create(2617, 3159, 0) + } + } + + setDest(IntType.NPC, intArrayOf(KELVIN), "talk-to") { _, _ -> + return@setDest Location.create(2589, 3141, 0) + } + + setDest(IntType.NPC, intArrayOf(JOE), "talk-to") { _, _ -> + return@setDest Location.create(2589, 3141, 0) + } + + setDest(IntType.NPC, intArrayOf(HENGARD), "talk-to") { _, _ -> + return@setDest Location.create(2600, 3142, 0) + } + + setDest(IntType.SCENERY, intArrayOf(CENTER_DOOR), "open") { player, _ -> + if (inBorders(player, 2604, 3152, 2607, 3155)) { + return@setDest Location.create(2605, 3153, 0) + } else { + return@setDest Location.create(2607, 3151, 0) + } + } + + setDest(IntType.SCENERY, intArrayOf(A_LAZY_GUARD_2), "steal-keys") { _, _ -> + return@setDest Location.create(2619, 3143, 0) + } + + } + + class EastDoorSupportDialogue : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + npc = NPC(NPCs.KHAZARD_GUARD_257) + when (stage) { + 0 -> { + face(player!!, location(2617, 3170, 0)) + playerl(FacialExpression.NEUTRAL, "This door appears to be locked.").also { stage++ } + } + + 1 -> { + face(player!!, location(2603, 3155, 0)) + npcl(FacialExpression.NEUTRAL, "Nice observation guard. You could have just asked to be let in like a normal person.").also { stage++ } + } + 2 -> { + end() + lock(player!!, 2) + setQuestStage(player!!, Quests.FIGHT_ARENA, 20) + DoorActionHandler.handleAutowalkDoor(player, getScenery(2617, 3172, 0)) + } + } + } + } + + class WestDoorSupportDialogue : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + npc = NPC(NPCs.KHAZARD_GUARD_257) + when (stage) { + 0 -> { + face(player!!, location(2585, 3141, 0)) + playerl(FacialExpression.NEUTRAL, "This door appears to be locked.").also { stage++ } + } + 1 -> { + face(player!!, location(2603, 3155, 0)) + npcl(FacialExpression.NEUTRAL, "Nice observation guard. You could have just asked to be let in like a normal person.").also { stage++ } + } + 2 -> { + end() + lock(player!!, 2) + setQuestStage(player!!, Quests.FIGHT_ARENA, 20) + DoorActionHandler.handleAutowalkDoor(player, getScenery(2584, 3141, 0)) + } + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/ardougne/quest/arena/cutscenes/EscapeCutscene.kt b/Server/src/main/content/region/kandarin/ardougne/quest/arena/cutscenes/EscapeCutscene.kt new file mode 100644 index 000000000..8c5321bc2 --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/quest/arena/cutscenes/EscapeCutscene.kt @@ -0,0 +1,143 @@ +package content.region.kandarin.ardougne.quest.arena.cutscenes + +import content.region.kandarin.ardougne.quest.arena.FightArenaListeners.Companion.Jeremy +import content.region.kandarin.ardougne.quest.arena.npc.OgreNPC.Companion.spawnOgre +import core.api.* +import core.game.activity.Cutscene +import core.game.dialogue.FacialExpression +import core.game.global.action.DoorActionHandler +import core.game.node.entity.player.Player +import core.game.world.map.Direction +import core.game.world.map.Location + +class EscapeCutscene(player: Player) : Cutscene(player) { + override fun setup() { + setExit(location(2603, 3155, 0)) + if (player.settings.isRunToggled) { + player.settings.toggleRun() + } + } + + override fun runStage(stage: Int) { + when (stage) { + + 0 -> { + move(Jeremy, 56, 31) + timedUpdate(3) + } + + 1 -> { + player.faceLocation(location(56, 31, 0)) + animate(player, 2098) + timedUpdate(4) + } + + 2 -> { + move(player, 57, 32) + timedUpdate(-1) + } + + 3 -> { + DoorActionHandler.handleAutowalkDoor(Jeremy, getObject(57, 31, 0)) + player.faceLocation(location(57, 28, 0)) + timedUpdate(3) + } + + 4 -> { + move(Jeremy, 57, 31) + timedUpdate(1) + } + + 5 -> { + sendChat(Jeremy, "I'll run ahead.") + move(Jeremy, 57, 20) + timedUpdate(5) + } + + 6 -> { + teleport(Jeremy, 56, 31) + loadRegion(10289) + addNPC(JEREMYRESCUE, 41, 17, Direction.NORTH) + addNPC(GENERAL, 45, 19, Direction.NORTH) + addNPC(OGRE, 48, 30, Direction.NORTH) + addNPC(JUSTIN, 41, 32, Direction.EAST) + timedUpdate(-1) + } + + 7 -> { + moveCamera(47, 20) + rotateCamera(45, 15) + teleport(player, 47, 15) + timedUpdate(2) + } + + 8 -> { + DoorActionHandler.handleAutowalkDoor(player, getObject(46, 16)) + moveCamera(41, 26, 300, 4) + rotateCamera(45, 15, 300, 4) + timedUpdate(-1) + } + + 9 -> { + move(player, 44, 17) + timedUpdate(2) + } + + 10 -> { + move(player, 43, 18) + timedUpdate(1) + } + + 11 -> { + move(player, 43, 19) + timedUpdate(3) + } + + 12 -> { + player.faceLocation(getNPC(JEREMYRESCUE)!!.location) + sendPlayerDialogue(player, "Jeremy, where's your father?",FacialExpression.HALF_ASKING) + timedUpdate(2) + } + + 13 -> { + getNPC(JEREMYRESCUE)!!.faceLocation(player.location) + move(getNPC(JUSTIN)!!, 42, 32) + sendNPCDialogue(player, JEREMYRESCUE, "Quick help him! That beast will kill him. He's too old to fight.") + teleport(getNPC(OGRE)!!, 45, 30) + timedUpdate(2) + } + + 14 -> { + moveCamera(42, 26) + rotateCamera(42, 26) + timedUpdate(-1) + } + + 15 -> { + move(getNPC(OGRE)!!, 43, 31) + timedUpdate(4) + } + + 16 -> { + getNPC(OGRE)!!.faceLocation(getNPC(JUSTIN)!!.location) + animate(getNPC(OGRE)!!, 359, true) + animate(getNPC(JUSTIN)!!, 404, true) + timedUpdate(2) + } + + 17 -> { + end { + spawnOgre(player) + } + } + } + } + + companion object { + private const val GENERAL = 258 + private const val JEREMYRESCUE = 266 + private const val JUSTIN = 267 + private const val OGRE = 270 + } +} + diff --git a/Server/src/main/content/region/kandarin/ardougne/quest/arena/cutscenes/JailCutscene.kt b/Server/src/main/content/region/kandarin/ardougne/quest/arena/cutscenes/JailCutscene.kt new file mode 100644 index 000000000..cbfca605a --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/quest/arena/cutscenes/JailCutscene.kt @@ -0,0 +1,76 @@ +package content.region.kandarin.ardougne.quest.arena.cutscenes + +import content.region.kandarin.ardougne.quest.arena.dialogue.HengradDialogue +import core.api.* +import core.game.activity.Cutscene +import core.game.global.action.DoorActionHandler +import core.game.node.entity.player.Player +import core.game.world.map.Direction + +class JailCutscene(player: Player) : Cutscene(player) { + override fun setup() { + setExit(location(2600, 3142, 0)) + if (player.settings.isRunToggled) { + player.settings.toggleRun() + } + loadRegion(10289) + } + + override fun runStage(stage: Int) { + when (stage) { + + 0 -> { + addNPC(KHAZARD_GUARD, 48, 5, Direction.WEST) + teleport(player, 46, 5) + timedUpdate(1) + } + + 1 -> { + moveCamera(43, 4) + rotateCamera(41, 5) + timedUpdate(1) + } + + 2 -> { + move(player, 45, 5) + timedUpdate(1) + } + + 3 -> { + move(player, 40, 5) + move(getNPC(KHAZARD_GUARD)!!, 41, 5) + timedUpdate(6) + } + + 4 -> { + animate(getNPC(KHAZARD_GUARD)!!, 2098) + DoorActionHandler.handleAutowalkDoor(player, getObject(40, 5)) + timedUpdate(5) + } + + 5 -> { + move(player, 40, 6) + face(player, getNPC(KHAZARD_GUARD)!!) + move(getNPC(KHAZARD_GUARD)!!, 40, 5) + timedUpdate(2) + } + + 6 -> { + face(getNPC(KHAZARD_GUARD)!!, player) + sendNPCDialogue(player, KHAZARD_GUARD, "The General seems to have taken a liking to you. He'd normally kill imposters like you without a second thought.") + timedUpdate(3) + } + + 7 -> { + end { + openDialogue(player, HengradDialogue()) + } + } + } + } + + companion object { + private const val KHAZARD_GUARD = 257 + } +} + diff --git a/Server/src/main/content/region/kandarin/ardougne/quest/arena/cutscenes/SecondFightCutscene.kt b/Server/src/main/content/region/kandarin/ardougne/quest/arena/cutscenes/SecondFightCutscene.kt new file mode 100644 index 000000000..85a41b07a --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/quest/arena/cutscenes/SecondFightCutscene.kt @@ -0,0 +1,148 @@ +package content.region.kandarin.ardougne.quest.arena.cutscenes + +import content.region.kandarin.ardougne.quest.arena.npc.ScorpionNPC.Companion.spawnScorpion +import core.api.animate +import core.api.location +import core.api.sendChat +import core.game.activity.Cutscene +import core.game.global.action.DoorActionHandler +import core.game.node.entity.player.Player +import core.game.world.map.Direction + +class SecondFightCutscene(player: Player) : Cutscene(player) { + override fun setup() { + setExit(location(2603, 3155, 0)) + if (player.settings.isRunToggled) { + player.settings.toggleRun() + } + addNPC(GUARD, 36, 5, Direction.EAST) + addNPC(SCORPION, 47, 23, Direction.WEST) + + } + + override fun runStage(stage: Int) { + when (stage) { + + 0 -> { + move(getNPC(GUARD)!!, 39, 5) + sendChat(getNPC(GUARD)!!, "Right you, move it.") + timedUpdate(4) + } + + 1 -> { + move(player, 40, 6) + timedUpdate(1) + } + + 2 -> { + animate(getNPC(GUARD)!!, 805) + timedUpdate(2) + } + + 3 -> { + DoorActionHandler.handleAutowalkDoor(player, getObject(40, 5)) + timedUpdate(2) + } + + 4 -> { + move(player, 42, 5) + timedUpdate(2) + } + + 5 -> { + move(player, 43, 6) + move(getNPC(GUARD)!!, 43, 6) + timedUpdate(-1) + } + + 6 -> { + move(player, 47, 15) + timedUpdate(1) + } + + 7 -> { + move(getNPC(GUARD)!!, 43, 6) + timedUpdate(2) + } + + 8 -> { + move(getNPC(GUARD)!!, 48, 7) + timedUpdate(6) + } + + 9 -> { + move(getNPC(GUARD)!!, 48, 14) + timedUpdate(8) + } + + 10 -> { + sendChat(getNPC(GUARD)!!, "Get out! there.") + DoorActionHandler.handleAutowalkDoor(player, getObject(46, 16)) + move(getNPC(GUARD)!!, 47, 15) + timedUpdate(1) + } + + 11 -> { + move(player, 43, 19) + timedUpdate(2) + } + + 12 -> { + move(getNPC(GUARD)!!, 48, 14) + timedUpdate(1) + } + + 13 -> { + dialogueUpdate("From above you hear a voice..... 'Ladies and gentlemen! Let today's first fight between the outsider and everyone's favourite scorpion commence.'") + timedUpdate(4) + } + + 14 -> { + loadRegion(10289) + addNPC(SCORPION, 47, 23, Direction.WEST) + teleport(player, 43, 19) + timedUpdate(1) + } + + 15 -> { + moveCamera(42, 23) + rotateCamera(46, 23) + timedUpdate(1) + } + + 16 -> { + DoorActionHandler.handleAutowalkDoor(getNPC(SCORPION)!!, getObject(46, 24)) + DoorActionHandler.handleAutowalkDoor(getNPC(SCORPION)!!, getObject(46, 23)) + timedUpdate(1) + } + + 17 -> { + move(getNPC(SCORPION)!!, 45, 20) + moveCamera(37, 23, 300, 5) + rotateCamera(43, 23, 300, 5) + timedUpdate(2) + } + + 18 -> { + move(getNPC(SCORPION)!!, 44, 20) + timedUpdate(1) + } + + 19 -> { + move(getNPC(SCORPION)!!, 42, 20) + timedUpdate(3) + } + + 20 -> { + end { + spawnScorpion(player) + } + } + } + } + + companion object { + const val GUARD = 257 + const val SCORPION = 271 + } +} diff --git a/Server/src/main/content/region/kandarin/ardougne/quest/arena/cutscenes/ThirdFightCutscene.kt b/Server/src/main/content/region/kandarin/ardougne/quest/arena/cutscenes/ThirdFightCutscene.kt new file mode 100644 index 000000000..f9781474c --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/quest/arena/cutscenes/ThirdFightCutscene.kt @@ -0,0 +1,68 @@ +package content.region.kandarin.ardougne.quest.arena.cutscenes + +import content.region.kandarin.ardougne.quest.arena.npc.BouncerNPC.Companion.spawnBouncer +import core.game.activity.Cutscene +import core.game.global.action.DoorActionHandler +import core.game.node.entity.player.Player +import core.game.world.map.Direction + +class ThirdFightCutscene(player: Player) : Cutscene(player) { + + override fun setup() { + setExit(player.location.transform(0, 0, 0)) + if (player.settings.isRunToggled) { + player.settings.toggleRun() + } + loadRegion(10289) + addNPC(BOUNCER, 47, 26, Direction.WEST) + } + + override fun runStage(stage: Int) { + when (stage) { + + 0 -> { + teleport(player, 43, 19) + timedUpdate(1) + } + + 1 -> { + moveCamera(43, 27, 300, 100) + moveCamera(42, 27, 300, 100) + rotateCamera(46, 27, 300, 100) + timedUpdate(2) + } + + 2 -> { + DoorActionHandler.handleAutowalkDoor(getNPC(BOUNCER)!!, getObject(46, 26)) + timedUpdate(2) + } + + 3 -> { + move(getNPC(BOUNCER)!!, 45, 26) + moveCamera(37, 27, 300, 5) + rotateCamera(44, 27, 300, 5) + timedUpdate(2) + } + + 4 -> { + move(getNPC(BOUNCER)!!, 44, 26) + timedUpdate(1) + } + + 5 -> { + move(getNPC(BOUNCER)!!, 42, 26) + timedUpdate(3) + } + + 6 -> { + end { + spawnBouncer(player) + } + } + } + } + + companion object { + const val BOUNCER = 269 + } +} 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 new file mode 100644 index 000000000..2a4ffa5a8 --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/quest/arena/dialogue/ALazyGuardDialogue.kt @@ -0,0 +1,114 @@ +package content.region.kandarin.ardougne.quest.arena.dialogue + +import content.data.Quests +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 org.rs09.consts.Items +import org.rs09.consts.Items.KHALI_BREW_77 +import org.rs09.consts.Items.KHAZARD_CELL_KEYS_76 +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!!, Quests.FIGHT_ARENA)) { + + in 40..49 -> when (stage) { + 0 -> { + player!!.faceLocation(location(2617, 3144, 0)) + playerl(FacialExpression.NEUTRAL, "Long live General Khazard!").also { stage++ } + } + 1 -> npcl(FacialExpression.FRIENDLY, "Erm.. yes.. quite right.").also { stage++ } + 2 -> npcl(FacialExpression.FRIENDLY, "Have you come to laugh at the fight slaves? I used to really enjoy it, but after a while they become quite boring. ").also { stage++ } + 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!!, Quests.FIGHT_ARENA, 50) + } + } + + + in 50..59 -> when (stage) { + 0 -> if (!allInEquipment(player!!, Items.KHAZARD_HELMET_74, Items.KHAZARD_ARMOUR_75)) { + player!!.faceLocation(location(2617, 3144, 0)) + npcl(FacialExpression.DRUNK, "Go away. Thish areash resh... restricted! We don't like strangersh, 'specially ones who sway. Oooh!").also { stage = END_DIALOGUE } + } else { + player!!.faceLocation(location(2617, 3144, 0)) + playerl(FacialExpression.NEUTRAL, "Hello, how's the job?").also { stage++ } + } + 1 -> npcl(FacialExpression.DRUNK, "Please, leave me alone. I'm sure the walls never used to sway that much.").also { stage = END_DIALOGUE } + } + + + in 60..67 -> when (stage) { + 0 -> { + setVarbit(player!!, 5627, 0) + player!!.faceLocation(location(2617, 3144, 0)) + playerl(FacialExpression.FRIENDLY, "Hello again.").also { stage++ } + } + 1 -> npcl(FacialExpression.NEUTRAL, "Bored, bored, bored. You'd think the slaves would be more entertaining? Selfish the lot of them.").also { stage++ } + 2 -> playerl(FacialExpression.FRIENDLY, "Do you still fancy a drink?").also { stage++ } + 3 -> npcl(FacialExpression.HAPPY, "I really shouldn't... oh... ok then just the one.").also { stage++ } + 4 -> if (player!!.inventory.remove(Item(KHALI_BREW_77))) { + sendItemDialogue(player!!, KHALI_BREW_77, "You hand a bottle of Khali brew to the guard. He takes a mouthful of the drink.").also { stage = 5 } + setVarbit(player!!, 5627, 1, true) // swap beer to Khali brew + } else { + npcl(FacialExpression.NEUTRAL, "Now I just want a decent drink. Mind you, too much Khali brew and I'll fall asleep.").also { stage = END_DIALOGUE } + } + 5 -> npcl(FacialExpression.DRUNK, "Blimey this stuff is pretty good. It's not too strong is it?").also { stage++ } + 6 -> playerl(FacialExpression.HALF_GUILTY, "No, not at all. You'll be fine.").also { stage++ } + 7 -> sendDialogue(player!!, "The guard quickly finishes the rest of the bottle and begins to sway slightly.").also { stage++ } + 8 -> npcl(FacialExpression.HAPPY, "That is some gooood stuff... yeah... wooh yeah!").also { stage++ } + 9 -> playerl(FacialExpression.HALF_WORRIED, "Are you alright?").also { stage++ } + 10 -> npcl(FacialExpression.DRUNK, "Yeesshh, hiccup! Oooh, maybe I sshould relax for a while.").also { stage++ } + 11 -> playerl(FacialExpression.NEUTRAL, "Good idea. I'll look after the prisoners.").also { stage++ } + 12 -> npcl(FacialExpression.DRUNK, "Yeesh, yes that shounds reasonable. Here, hiccup', take the keysch. Any trouble, you give... you givem a good beating.").also { stage++ } + 13 -> playerl(FacialExpression.NEUTRAL, "No problem. I'll keep them in line.").also { stage++ } + 14 -> { + end() + setVarbit(player!!, 5627, 2) + setQuestStage(player!!, Quests.FIGHT_ARENA, 68) + } + } + + + in 68..99 -> when (stage) { + 0 -> if (player!!.inventory.containItems(KHAZARD_CELL_KEYS_76)) { + setVarbit(player!!, 5627, 0) + player!!.faceLocation(location(2617, 3144, 0)) + playerl(FacialExpression.HALF_ASKING, "Hello, how's the job?").also { stage = 1 } + } else { + setVarbit(player!!, 5627, 0) + player!!.faceLocation(location(2617, 3144, 0)) + playerl(FacialExpression.HALF_GUILTY, "Hi, er.. I lost the keys.").also { stage = 2 } + } + 1 -> npcl(FacialExpression.DRUNK, "Please, leave me alone. I'm sure the walls never used to sway that much.").also { stage = END_DIALOGUE } + 2 -> npcl(FacialExpression.DRUNK, "What?! You're foolish...").also { stage++ } + 3 -> npcl(FacialExpression.DRUNK, "...and I'm drunk. Here, take my spare set.").also { stage++ } + 4 -> { + end() + setVarbit(player!!, 5627, 3) + player!!.inventory.add(Item(KHAZARD_CELL_KEYS_76)) + sendMessage(player!!, "You pick up the keys from the table.") + } + } + + 100 -> when(stage){ + 0 ->{ + setVarbit(player!!, 5627, 0) + player!!.faceLocation(location(2617, 3144, 0)) + playerl(FacialExpression.HALF_ASKING, "Hello, how's the job?").also { stage++ } + } + 1 -> npcl(FacialExpression.DRUNK, "Please, leave me alone. I'm sure the walls never used to sway that much.").also { stage++ } + 2 -> { + end() + setVarbit(player!!, 5627, 3) + } + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/ardougne/quest/arena/dialogue/FightslaveDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/quest/arena/dialogue/FightslaveDialogue.kt new file mode 100644 index 000000000..5bac22208 --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/quest/arena/dialogue/FightslaveDialogue.kt @@ -0,0 +1,40 @@ +package content.region.kandarin.ardougne.quest.arena.dialogue + +import core.api.allInEquipment +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 FightslaveDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + if (allInEquipment(player, Items.KHAZARD_HELMET_74, Items.KHAZARD_ARMOUR_75)) { + playerl(FacialExpression.FRIENDLY, "Do you know of a Justin or Jeremy in this arena?").also { stage = 0 } + } else { + playerl(FacialExpression.FRIENDLY, "Do you know of a Justin or Jeremy in this arena?").also { stage = 1 } + } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage) { + 0 -> npcl(FacialExpression.FRIENDLY, "Please leave me alone.").also { stage = END_DIALOGUE } + 1 -> npcl(FacialExpression.AFRAID, "I've not met anybody in here by that name.").also { stage = END_DIALOGUE } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return FightslaveDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.FIGHTSLAVE_262) + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..11b516bbc --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/quest/arena/dialogue/GeneralKhazardDialogue.kt @@ -0,0 +1,131 @@ +package content.region.kandarin.ardougne.quest.arena.dialogue + +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 +import core.api.* +import core.game.dialogue.DialogueFile +import core.game.dialogue.FacialExpression +import core.game.node.entity.npc.NPC +import core.game.world.map.RegionManager +import core.tools.END_DIALOGUE +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +class GeneralKhazardDialogue : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + npc = NPC(NPCs.GENERAL_KHAZARD_258) + 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 } + } + + 71 -> when (stage) { + 0 -> if (!anyInEquipment(player!!, Items.KHAZARD_HELMET_74, Items.KHAZARD_ARMOUR_75)) { + face(player!!, findNPC(NPCs.GENERAL_KHAZARD_258)!!, 1) + sendPlayerDialogue(player!!, "General Khazard?").also { stage = 2 } + } else { + face(player!!, findNPC(NPCs.GENERAL_KHAZARD_258)!!, 1) + face(findNPC(NPCs.GENERAL_KHAZARD_258)!!, player!!,1) + sendNPCDialogue(player!!, NPCs.GENERAL_KHAZARD_258, "Who dares enter my home? You? A feeble traveller?", FacialExpression.OLD_DEFAULT).also { stage = 6 } + } + 2 -> npcl(FacialExpression.OLD_DEFAULT, "What do you want guard? I'm a busy man.").also { stage++ } + 3 -> playerl(FacialExpression.FRIENDLY, "Of course sir.").also { stage++ } + 4 -> npcl(FacialExpression.OLD_DEFAULT, "In the last two hundred years, I have survived all horrors imaginable. Now it is my turn to cover this land in darkness. One day you shall see, all will quake on hearing my name.").also { stage++ } + 5 -> npcl(FacialExpression.OLD_DEFAULT, "Now leave me.").also { stage = END_DIALOGUE } + 6 -> playerl(FacialExpression.ASKING, "Feeble?!").also { stage++ } + 7 -> npcl(FacialExpression.OLD_DEFAULT, "Get out! Whoever let you in shall be severely punished for this.").also { stage = END_DIALOGUE } + } + + 72 -> when (stage) { + 0 -> { + lockInteractions(player!!, 2) + face(player!!, findNPC(NPCs.JUSTIN_SERVIL_267)!!, 1) + face(findNPC(NPCs.JUSTIN_SERVIL_267)!!, player!!, 1) + sendNPCDialogue(player!!, NPCs.JUSTIN_SERVIL_267, "You saved my life and my son's, I am eternally in your debt brave traveller.").also { stage++ } + } + 1 -> { + face(findNPC(NPCs.GENERAL_KHAZARD_258)!!, player!!, 1) + npcl(FacialExpression.OLD_DEFAULT, "Haha, well done, well done that was rather entertaining. I am the great General Khazard, and the two men you just 'saved' are my property.").also { stage++ } + } + 2 -> { + face(player!!, findNPC(NPCs.GENERAL_KHAZARD_258)!!, 1) + playerl(FacialExpression.ANGRY, "They belong to nobody.").also { stage++ } + } + 3 -> npcl(FacialExpression.OLD_DEFAULT, "Well, I suppose we could find some arrangement for their freedom... hmmm?").also { stage++ } + 4 -> playerl(FacialExpression.ASKING, "What do you mean?").also { stage++ } + 5 -> npcl(FacialExpression.OLD_DEFAULT, "I'll let them go, but you must stay and fight for me. You'll make me double the gold if you manage to last a few fights.").also { stage++ } + 6 -> npcl(FacialExpression.OLD_ANGRY1, "Guards! Take " + (if (player!!.isMale) "him" else "her") + " to the cells.").also { stage++ } + 7 -> { + end() + JailCutscene(player!!).start() + setAttribute(player!!, "spawn-scorpion", true) + sendMessage(player!!, "A guard grabs the keys to the cell doors.") + } + } + + 87 -> when (stage) { + 0 -> { + face(findNPC(NPCs.GENERAL_KHAZARD_258)!!, player!!) + face(player!!, findNPC(NPCs.GENERAL_KHAZARD_258)!!, 1) + npcl(FacialExpression.OLD_ANGRY1, "How dare you speak to me, you are a slave of this arena now.").also { stage = END_DIALOGUE } + } + } + + 88 -> when (stage) { + 0 -> { + end() + JailCutscene(player!!).start() + setAttribute(player!!, "spawn-scorpion", true) + } + } + + 89 -> when (stage) { + 0 -> { + lockInteractions(player!!, 2) + face(findNPC(NPCs.GENERAL_KHAZARD_258)!!, player!!, 1) + face(player!!, findNPC(NPCs.GENERAL_KHAZARD_258)!!, 1) + npcl(FacialExpression.OLD_ANGRY1, "Not bad, not bad at all. I think you need a tougher challenge. Time for my puppy. Guards! Guards! Bring on Bouncer!").also { stage++ } + } + 1 -> sendDialogue(player!!, "From above you hear a voice..... 'Ladies and gentlemen! Today's second round of battle is between the outsider and Bouncer.'").also { stage++ } + 2 -> { + end() + setAttribute(player!!, "spawn-bouncer", true) + ThirdFightCutscene(player!!).start() + } + } + + 91 -> when (stage) { + 0 -> { + lockInteractions(player!!, 4) + npcl(FacialExpression.OLD_NEARLY_CRYING, "Nooooo! Bouncer! How dare you? You've taken the life of my old friend. Now you'll suffer traveller, prepare to meet your maker.").also { stage++ } + face(player!!, findNPC(NPCs.GENERAL_KHAZARD_258)!!, 1) + } + 1 -> playerl(FacialExpression.ANNOYED, "You agreed to let the Servils go if I stayed to fight.").also { stage++ } + 2 -> npcl(FacialExpression.OLD_DEFAULT, "Indeed. I obviously underestimated you, although you still pale in comparsion to the might of General Khazard.").also { stage++ } + 3 -> npcl(FacialExpression.OLD_NEARLY_CRYING, "You shall see that I am not cowardlly enough to make false promises. They may go.").also { stage++ } + 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!!, Quests.FIGHT_ARENA, 97) + RegionManager.getNpc(player!!.location, NPCs.GENERAL_KHAZARD_258, 15) + General.attack(player!!) + } + } + + 100 -> when (stage) { + 0 -> { + face(player!!, findNPC(NPCs.GENERAL_KHAZARD_258)!!, 1) + playerl(FacialExpression.ANNOYED, "Didn't I kill you?").also { stage++ } + } + 1 -> { + face(findNPC(NPCs.GENERAL_KHAZARD_258)!!, player!!, 1) + npcl(FacialExpression.FRIENDLY,"You might not believe, it, young one, but you can't kill me.").also { stage = END_DIALOGUE } + } + + } + } + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..77fd6c123 --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/quest/arena/dialogue/GuardsDialogue.kt @@ -0,0 +1,159 @@ +package content.region.kandarin.ardougne.quest.arena.dialogue + +import core.api.allInEquipment +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 +import content.data.Quests + + +@Initializable +class GuardsDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + 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 } + } else { + playerl(FacialExpression.FRIENDLY, "Hi.").also { stage = 2 } + } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage) { + 0 -> npcl(FacialExpression.ASKING, "Can I help you stranger?").also { stage++ } + 1 -> npcl(FacialExpression.FRIENDLY, "Oh, you're a guard as well. That's ok then. We don't like strangers around here").also { stage = END_DIALOGUE } + } + when (stage) { + 2 -> npcl(FacialExpression.ANNOYED, "I don't know you stranger. Get off our land.").also { stage = END_DIALOGUE + } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return GuardsDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.KHAZARD_GUARD_253) + } +} + +@Initializable +class KhazardGuard254Dialogue(player: Player? = null) : DialoguePlugin(player) { + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + 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 } + } else { + playerl(FacialExpression.FRIENDLY, "Hi.").also { stage = 7 } + } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage) { + 0 -> npcl(FacialExpression.FRIENDLY, "I've never seen you around here before!").also { stage++ } + 1 -> playerl(FacialExpression.FRIENDLY, "Long live General Khazard!").also { stage++ } + 2 -> npcl(FacialExpression.FRIENDLY, "Erm.. yes.. soldier, I take it you're new around here?").also { stage++ } + 3 -> playerl(FacialExpression.FRIENDLY, "You could say that.").also { stage++ } + 4 -> npcl(FacialExpression.FRIENDLY, "Khazard died two hundred years ago. However his dark spirit remains in the form of the undead maniac General Khazard. Remember he is your master, always watching.").also { stage++ } + 5 -> npcl(FacialExpression.FRIENDLY, "Got that newbie?").also { stage++ } + 6 -> playerl(FacialExpression.FRIENDLY, "Undead, maniac, master. Got it, loud and clear.").also { stage = END_DIALOGUE } + 7 -> npcl(FacialExpression.ANNOYED, "This area is restricted. Leave now! OUT! And don't come back.").also { stage++ } + 8 -> options("I apologise, I stumbled in here by mistake.", "You have no right to tell me where I can and cannot go.").also { stage++ } + 9 -> when(buttonId){ + 1 -> playerl(FacialExpression.HALF_WORRIED, "I apologise, I stumbled in here by mistake.").also { stage = 10 } + 2 -> playerl(FacialExpression.HALF_WORRIED, "You have no right to tell me where I can and cannot go.").also { stage = 11 } + } + 10 -> npcl(FacialExpression.FRIENDLY, "Well, don't just stand there - get out of here!").also { stage = END_DIALOGUE } + 11 -> npcl(FacialExpression.FRIENDLY, "Fair enough. Let's do this the hard way.").also { stage = END_DIALOGUE } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return KhazardGuard254Dialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.KHAZARD_GUARD_254) + } +} + +@Initializable +class KhazardGuard255Dialogue(player: Player? = null) : DialoguePlugin(player) { + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + 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 } + } else { + playerl(FacialExpression.FRIENDLY, "Hi.").also { stage = 2 } + } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage) { + 0 -> npcl(FacialExpression.ASKING, "Can I help you stranger?").also { stage++ } + 1 -> npcl(FacialExpression.FRIENDLY, "Oh, you're a guard as well. That's ok then. We don't like strangers around here.").also { stage = END_DIALOGUE } + 2 -> npcl(FacialExpression.ANNOYED, "This area is restricted, leave now!").also { stage++ } + 3 -> npcl(FacialExpression.ANGRY, "OUT! And don't come back!").also { stage = END_DIALOGUE } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return KhazardGuard255Dialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.KHAZARD_GUARD_255) + } +} + +@Initializable +class KhazardGuard256Dialogue(player: Player? = null) : DialoguePlugin(player) { + override fun open(vararg args: Any?): Boolean { + 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(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 } + } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage) { + 0 -> npcl(FacialExpression.ANGRY, "Despicable thieving scum, that was good armour. Did you see anyone around here soldier?").also { stage++ } + 1 -> playerl(FacialExpression.SILENT, "Me? No, no one!").also { stage++ } + 2 -> npcl(FacialExpression.SUSPICIOUS, "Hmmmm").also { stage = END_DIALOGUE } + 3 -> npcl(FacialExpression.ANNOYED, "I don't know who you are. Get out of my house stranger.").also { stage = END_DIALOGUE } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return KhazardGuard256Dialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.KHAZARD_GUARD_256) + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..bd31acf9f --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/quest/arena/dialogue/HengradDialogue.kt @@ -0,0 +1,49 @@ +package content.region.kandarin.ardougne.quest.arena.dialogue + +import content.data.Quests +import content.region.kandarin.ardougne.quest.arena.cutscenes.SecondFightCutscene +import core.api.* +import core.game.dialogue.DialogueFile +import core.game.dialogue.FacialExpression +import core.game.node.entity.npc.NPC +import org.rs09.consts.NPCs + +class HengradDialogue : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + npc = NPC(NPCs.HENGRAD_263) + when (getQuestStage(player!!, Quests.FIGHT_ARENA)) { + + in 72..87 -> when (stage) { + 0 -> { + lockInteractions(player!!, 10) + face(findNPC(NPCs.HENGRAD_263)!!,player!!, 1) + npcl(FacialExpression.AFRAID, "Are you ok stranger?").also { stage++ } + } + 1 -> { + face(player!!,findNPC(NPCs.HENGRAD_263)!!, 1) + playerl(FacialExpression.FRIENDLY, "I'm fine thanks.").also { stage++ } + } + 2 -> npcl(FacialExpression.ASKING, " So Khazard got his hand on you too?").also { stage++ } + 3 -> playerl(FacialExpression.HALF_WORRIED, " I'm afraid so.").also { stage++ } + 4 -> npcl(FacialExpression.FRIENDLY, " If you're lucky you may last as long as me.").also { stage++ } + 5 -> playerl(FacialExpression.ASKING, " How long have you been here?").also { stage++ } + 6 -> npcl(FacialExpression.FRIENDLY, " I've been in Khazard's prisons ever since I can remember. I was a child when his men kidnapped me. My whole life as been spent killing and fighting, all in the hope that, one day, I might escape.").also { stage++ } + 7 -> playerl(FacialExpression.FRIENDLY, "Don't give up.").also { stage++ } + 8 -> npcl(FacialExpression.FRIENDLY, "Thanks friend.").also { stage++ } + 9 -> npcl(FacialExpression.SILENT, "Wait... Shhh, the guard is coming. Looks like you'll be going into the arena. Good luck, friend.").also { stage++ } + 10 -> { + end() + SecondFightCutscene(player!!).start() + setQuestStage(player!!, Quests.FIGHT_ARENA, 88) + } + } + + 88 -> when (stage) { + 0 -> { + end() + SecondFightCutscene(player!!).start() + } + } + } + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..e92454d6f --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/quest/arena/dialogue/JeremyServilADialogue.kt @@ -0,0 +1,61 @@ +package content.region.kandarin.ardougne.quest.arena.dialogue + +import content.data.Quests +import content.region.kandarin.ardougne.quest.arena.cutscenes.EscapeCutscene +import core.api.* +import core.game.dialogue.DialogueFile +import core.game.dialogue.FacialExpression +import core.game.node.entity.npc.NPC +import core.tools.END_DIALOGUE +import org.rs09.consts.NPCs + +class JeremyServilADialogue : DialogueFile() { + + override fun handle(componentID: Int, buttonID: Int) { + npc = NPC(NPCs.JEREMY_SERVIL_265) + when (getQuestStage(player!!, Quests.FIGHT_ARENA)) { + + 20 -> when (stage) { + 0 -> { + face(player!!, findNPC(NPCs.JEREMY_SERVIL_265)!!, 1) + playerl(FacialExpression.FRIENDLY, "Hello.").also { stage++ } + } + 1 -> { + face(findNPC(NPCs.JEREMY_SERVIL_265)!!, player!!, 1) + npcl(FacialExpression.FRIENDLY, "Please " + (if (player!!.isMale) "Sir" else "Madam") + ", don't hurt me.").also { stage++ } + } + 2 -> playerl(FacialExpression.FRIENDLY, "Sshh. This uniform is a disguise. I'm here to help. Where do they keep the keys?").also { stage++ } + 3 -> npcl(FacialExpression.FRIENDLY, "The guard always keeps hold of them.").also { stage++ } + 4 -> playerl(FacialExpression.FRIENDLY, "Don't lose heart, I'll be back.").also { stage++ } + 5 -> { + end() + setQuestStage(player!!, Quests.FIGHT_ARENA, 40) + } + } + + in 68..89 -> when (stage) { + 0 -> { + face(player!!, findNPC(NPCs.JEREMY_SERVIL_265)!!, 2) + playerl(FacialExpression.FRIENDLY, "Jeremy look, I have the keys.").also { stage++ } + } + 1 -> { + face(findNPC(NPCs.JEREMY_SERVIL_265)!!, player!!, 2) + npcl(FacialExpression.AMAZED, "Wow! Please set me free, then we can find my dad. I overheard a guard talking. I think they're taken him to the arena.").also { stage++ } + } + 2 -> playerl(FacialExpression.NEUTRAL, "Ok, we'd better hurry.").also { stage++ } + 3 -> { + end() + setAttribute(player!!, "spawn-ogre", true) + EscapeCutscene(player!!).start() + } + } + + in 90..100 -> when (stage) { + 0 -> { + face(player!!, findNPC(NPCs.JEREMY_SERVIL_265)!!, 1) + npcl(FacialExpression.FRIENDLY, "You need to kill the creatures in the arena").also { stage = END_DIALOGUE } + } + } + } + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..7f87687c2 --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/quest/arena/dialogue/JeremyServilBDialogue.kt @@ -0,0 +1,74 @@ +package content.region.kandarin.ardougne.quest.arena.dialogue + +import content.data.Quests +import core.api.* +import core.game.dialogue.DialogueFile +import core.game.dialogue.FacialExpression +import core.game.node.entity.npc.NPC +import core.tools.END_DIALOGUE +import org.rs09.consts.NPCs + +class JeremyServilBDialogue : DialogueFile() { + + override fun handle(componentID: Int, buttonID: Int) { + npc = NPC(NPCs.JEREMY_SERVIL_266) + when (getQuestStage(player!!, Quests.FIGHT_ARENA)) { + + in 1..84 -> when (stage) { + 0 -> { + face(player!!, findNPC(NPCs.JEREMY_SERVIL_266)!!, 1) + playerl(FacialExpression.FRIENDLY, "Don't worry, I'll get us out of here.").also { stage++ } + } + 1 -> npcl(FacialExpression.FRIENDLY, "Thanks, traveller. I'm sorry that you too are a subject of this arena.").also { stage = END_DIALOGUE } + } + + in 85..97 -> when (stage) { + 0 -> { + lockInteractions(player!!, 1) + face(player!!, findNPC(NPCs.JEREMY_SERVIL_266)!!, 1) + playerl(FacialExpression.FRIENDLY, "You and you father can return to Lady Servil.").also { stage++ } + } + 1 -> npcl(FacialExpression.FRIENDLY, "Thank you, we are truly indebted to you.").also { stage++ } + 2 -> { + end() + setQuestStage(player!!, Quests.FIGHT_ARENA, 99) + } + } + + + 98 -> when (stage) { + 0 -> { + face(player!!, findNPC(NPCs.JEREMY_SERVIL_266)!!, 1) + sendPlayerDialogue(player!!, "Khazard is dead, you and you father can return to Lady Servil.").also { stage++ } + } + 1 -> { + face(findNPC(NPCs.JEREMY_SERVIL_266)!!,player!!, 1) + npcl(FacialExpression.FRIENDLY, "Thank you, we are truly indebted to you.").also { stage++ } + } + 2 -> { + end() + setQuestStage(player!!, Quests.FIGHT_ARENA, 99) + } + } + + 100 -> when (stage) { + 0 -> { + face(player!!, findNPC(NPCs.JEREMY_SERVIL_266)!!, 1) + playerl(FacialExpression.FRIENDLY, "Hiya kiddo! You're looking a lot happier now.").also { stage++ } + } + 1 -> { + face(findNPC(NPCs.JEREMY_SERVIL_266)!!,player!!, 1) + npcl(FacialExpression.FRIENDLY,"Thank you " + (if (player!!.isMale) "Sir" else "Madam") + " I saw you fighting in the arena. You were brilliant! You were like POW! and then you got hit, but you were like too hard and didn't care and then you like hit him again and KERPLOW! Then the big scary dog came out, but you weren't scared and you like smacked him as well, and then you...").also { stage++ } + } + 2 -> playerl(FacialExpression.FRIENDLY, "Jeremy, calm down. Player doesn't need to hear everything " + (if (player!!.isMale) "He" else "She") + " did. " + (if (player!!.isMale) "He" else "She") + " was there when it happened, remember.").also { stage++ } + 3 -> sendNPCDialogue(player!!, NPCs.LADY_SERVIL_264, "I was rather enjoying it, actually.").also { stage++ } + 4 -> npcl(FacialExpression.FRIENDLY, "Yeah, but mum... ${player!!.username} was so cool. " + (if (player!!.isMale) "He" else "She") + " was like BAM! BASH! SPLAT! When I grow up, I wanna be a hero as well.").also { stage++ } + 5 -> playerl(FacialExpression.FRIENDLY, "I'm sure you will be. You showed a lot of bravery in the arena, young lad. If you work hard to complete your squire training, you'll be a worthy knight. Trust me, I can tell these things.").also { stage++ } + 6 -> npcl(FacialExpression.FRIENDLY, "Yeah! I'm gonna be the bravest knight in the world! After you, of course.").also { stage++ } + 7 -> sendNPCDialogue(player!!, NPCs.JUSTIN_SERVIL_267, "Thank you again, ${player!!.username}. You have saved my family and given young Jeremy here a new hero.").also { stage++ } + 8 -> playerl(FacialExpression.FRIENDLY, "I'm glad I could help. Have a good journey home and farewell.").also { stage++ } + 9 -> npcl(FacialExpression.HAPPY, "See ya!").also { stage = END_DIALOGUE } + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/ardougne/quest/arena/dialogue/JoeDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/quest/arena/dialogue/JoeDialogue.kt new file mode 100644 index 000000000..a220a48d6 --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/quest/arena/dialogue/JoeDialogue.kt @@ -0,0 +1,46 @@ +package content.region.kandarin.ardougne.quest.arena.dialogue + +import core.api.allInEquipment +import core.api.sendNPCDialogue +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 JoeDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + if (allInEquipment(player, Items.KHAZARD_HELMET_74, Items.KHAZARD_ARMOUR_75)) { + playerl(FacialExpression.FRIENDLY, "Do you know of a Justin or Jeremy in this arena?").also { stage = 0 } + } else { + playerl(FacialExpression.FRIENDLY, "Do you know of a Justin or Jeremy in this arena?").also { stage = 3 } + } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage) { + 0 -> sendNPCDialogue(player, NPCs.JOE_261, "If we did, why would we tell you guard?", FacialExpression.ANNOYED).also { stage++ } + 1 -> sendNPCDialogue(player, NPCs.KELVIN_260, "Until our last breath, our every action will be against Khazard. Never will we help you.", FacialExpression.ANNOYED).also { stage++ } + 2 -> sendNPCDialogue(player, NPCs.JOE_261, "I spit on Khazard's grave, and all who do his bidding.", FacialExpression.ANNOYED).also { stage = END_DIALOGUE } + 3 -> sendNPCDialogue(player, NPCs.KELVIN_260, "I have heard of those of whom you speak. But I fear it may be too late.", FacialExpression.HALF_GUILTY).also { stage++ } + 4 -> sendNPCDialogue(player, NPCs.JOE_261, "It is said that Khazard has a personal vendetta against those two, their time is therefore short.", FacialExpression.FRIENDLY).also { stage++ } + 5 -> sendNPCDialogue(player, NPCs.KELVIN_260, "You're a brave " + (if (player!!.isMale) "Sir" else "Madam") + ". If the guards get you, you'll be in here next.", FacialExpression.HALF_WORRIED).also { stage = END_DIALOGUE } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return JoeDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.KELVIN_260, NPCs.JOE_261) + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..3244d30f0 --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/quest/arena/dialogue/JustinServilDialogue.kt @@ -0,0 +1,72 @@ +package content.region.kandarin.ardougne.quest.arena.dialogue + +import content.data.Quests +import core.api.face +import core.api.findNPC +import core.api.getQuestStage +import core.api.sendNPCDialogue +import core.game.dialogue.DialogueFile +import core.game.dialogue.FacialExpression +import core.game.node.entity.npc.NPC +import core.tools.END_DIALOGUE +import org.rs09.consts.NPCs + +class JustinServilDialogue : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + npc = NPC(NPCs.JUSTIN_SERVIL_267) + when (getQuestStage(player!!, Quests.FIGHT_ARENA)) { + + in 1..68 -> when (stage) { + 0 -> playerl(FacialExpression.FRIENDLY, "Hello.").also { stage++ } + 1 -> { + face(findNPC(NPCs.JUSTIN_SERVIL_267)!!, player!!, 1) + npcl(FacialExpression.FRIENDLY, "You've incurred the anger of Khazard himself. He won't let any of us go easily.").also { stage = END_DIALOGUE } + } + + } + + in 69..71 -> when (stage) { + 0 -> { + face(findNPC(NPCs.JUSTIN_SERVIL_267)!!, player!!, 1) + playerl(FacialExpression.FRIENDLY, "Lady Servil sent me to rescue you and your son. Come on, we have to get out of here.").also { stage++ } + } + 1 -> npcl(FacialExpression.FRIENDLY, "I'm too old to fight. I'm afraid you'll have to kill that ogre by yourself.").also { stage = END_DIALOGUE } + } + + 72 -> when (stage) { + 0 -> { + face(findNPC(NPCs.JUSTIN_SERVIL_267)!!, player!!, 1) + playerl(FacialExpression.FRIENDLY, "Are you alright").also { stage++ } + } + 1 -> npcl(FacialExpression.FRIENDLY, "You saved my life and my son's. I am eternally in your debt brave traveller.").also { stage = END_DIALOGUE } + } + + in 73..89 -> when (stage) { + 0 -> { + face(findNPC(NPCs.JUSTIN_SERVIL_267)!!, player!!, 1) + playerl(FacialExpression.FRIENDLY, "Don't worry, I'll get us out of here.").also { stage++ } + } + 1 -> npcl(FacialExpression.NEUTRAL, "You've incurred the anger of Khazard yourself now. He won't let any of us go easily.").also { stage = END_DIALOGUE } + } + + in 90..99 -> when (stage) { + 0 -> { + face(findNPC(NPCs.JUSTIN_SERVIL_267)!!, player!!, 1) + npcl(FacialExpression.NEUTRAL, " You saved my life and my son's, I am eternally in your debt brave taveller.").also { stage = END_DIALOGUE } + } + } + + 100 -> when (stage) { + 0 -> { + face(findNPC(NPCs.JUSTIN_SERVIL_267)!!, player!!, 1) + npcl(FacialExpression.FRIENDLY, "Thank you again, ${player!!.username}. You have saved me and my boy from an awful fate.").also { stage++ } + } + 1 -> sendNPCDialogue(player!!, NPCs.JEREMY_SERVIL_266, "Yeah, you were ace. I wanna be jus' like you.").also { stage++ } + 2 -> npcl(FacialExpression.FRIENDLY, "If you do well in your studies and work hard, maybe you will be.").also { stage++ } + 3 -> sendNPCDialogue(player!!, NPCs.JEREMY_SERVIL_266, "Daaad, studying's so boooooooring! I wanna hit things.").also { stage++ } + 4 -> npcl(FacialExpression.FRIENDLY, "I'm sure ${player!!.username} studied hard when he was younger.").also { stage++ } + 5 -> playerl(FacialExpression.FRIENDLY, "That's right, son, you should do as your father says. Anyway, I should go now, have a safe journey home. And you, young lad, work hard and stay out of trouble - at least until you're a little older.").also { stage = END_DIALOGUE } + } + } + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..48ff869ac --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/quest/arena/dialogue/KhazardBarmanDialogue.kt @@ -0,0 +1,71 @@ +package content.region.kandarin.ardougne.quest.arena.dialogue + +import content.data.Quests +import core.api.addItem +import core.api.getQuestStage +import core.api.removeItem +import core.api.setQuestStage +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 org.rs09.consts.Items +import org.rs09.consts.Items.COINS_995 +import org.rs09.consts.NPCs + +class KhazardBarmanDialogue : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + npc = NPC(NPCs.KHAZARD_BARMAN_259) + 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) + stage = END_DIALOGUE + } else { + end() + playerl(FacialExpression.STRUGGLE, "Oh, I don't have enough money with me.").also { stage = END_DIALOGUE } + } + } + } + + in 50..100 -> { + when (stage) { + 0 -> playerl(FacialExpression.HAPPY, "Hello.").also { stage++ } + 1 -> npcl(FacialExpression.FRIENDLY, "Hi, what can I get you? We have a range of quality brews.").also { stage++ } + 2 -> options("I'll have a beer please.", "I'd like a Khali brew please.", "Got any news?").also { stage++ } + 3 -> when (buttonID) { + 1 -> playerl(FacialExpression.NEUTRAL, "I'll have a beer please.").also { stage = 4 } + 2 -> playerl(FacialExpression.NEUTRAL, "I'd like a Khali brew please.").also { stage = 7 } + 3 -> playerl(FacialExpression.ASKING, "Got any news?").also { stage = 5 } + } + 4 -> npcl(FacialExpression.FRIENDLY, "There you go, that's two gold coins.").also { stage = 8 } + 5 -> npcl(FacialExpression.NEUTRAL, "If you want to see the action around here you should visit the famous Khazard fight arena. I've seen some grand battles in my time. Ogres, goblins, even dragons have fought there.").also { stage++ } + 6 -> npcl(FacialExpression.WORRIED, "Although you have to feel sorry for some of the slaves sent in there.").also { stage = END_DIALOGUE } + 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) + stage = END_DIALOGUE + } else { + end() + playerl(FacialExpression.STRUGGLE, "Oh, I don't have enough money with me.").also { stage = END_DIALOGUE } + } + 9 -> if (removeItem(player!!, Item(COINS_995, 5))){ + end() + addItem(player!!, Items.KHALI_BREW_77, 1) + setQuestStage(player!!, Quests.FIGHT_ARENA, 60) + stage = END_DIALOGUE + } else { + end() + playerl(FacialExpression.STRUGGLE, "Oh, I don't have enough money with me.").also { stage = END_DIALOGUE } + } + } + } + } + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..fc30c1ca1 --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/quest/arena/dialogue/LadyServilDialogue.kt @@ -0,0 +1,100 @@ +package content.region.kandarin.ardougne.quest.arena.dialogue + +import core.api.getQuestStage +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 +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(Quests.FIGHT_ARENA) == 10) { + playerl(FacialExpression.FRIENDLY, "Hello Lady Servil.") + } 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(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.") + } + return true + } + + override fun handle(componentID: Int, buttonID: Int): Boolean { + 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++ } + 1 -> options("I hope you can. good luck.", "Can I help you?").also { stage++ } + 2 -> when (buttonID) { + 1 -> playerl(FacialExpression.NEUTRAL, "I hope you can, good luck.").also { stage = END_DIALOGUE } + 2 -> playerl(FacialExpression.FRIENDLY, "Can I help you?").also { stage++ } + } + 3 -> npcl(FacialExpression.SAD, "Would you? Please?").also { stage++ } + 4 -> npcl(FacialExpression.SAD, "I'm Lady Servil, and my husband is Sir Servil. We were travelling north together with our son Jeremy when we were ambushed by General Khazard's men.").also { stage++ } + 5 -> playerl(FacialExpression.HALF_ASKING, "General Khazard?").also { stage++ } + 6 -> npcl(FacialExpression.SAD, "He's been after me even since I declined his hand in marriage.").also { stage++ } + 7 -> npcl(FacialExpression.SAD, "Now he's kidnapped my husband and son to fight in his battle arena to the south of here. I hate to think what he'll do to them. He's a sick and twisted man.").also { stage++ } + 8 -> playerl(FacialExpression.FRIENDLY, "I'll try my best to return your family.").also { stage++ } + 9 -> { + end() + 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 } + } + } + + + in 1..10 -> when (stage) { + 0 -> npcl(FacialExpression.SAD, "Brave traveller, please... bring back my family.").also { stage = END_DIALOGUE } + } + + in 11..20 -> when (stage) { + 0 -> npcl(FacialExpression.WORRIED, "Have you had any luck with freeing my family?").also { stage++ } + 1 -> playerl(FacialExpression.HAPPY, "I've managed to get a guard's uniform, hopefully I can infiltrate the arena.").also { stage++ } + 2 -> npcl(FacialExpression.SAD, "Please hurry.").also { stage = END_DIALOGUE } + } + + + in 21..30 -> when (stage) { + 0 -> npcl(FacialExpression.ASKING, "And my family?").also { stage++ } + 1 -> playerl(FacialExpression.NEUTRAL, "I'm working on it.").also { stage++ } + 2 -> npcl(FacialExpression.SAD, "Please hurry.").also { stage = END_DIALOGUE } + } + + + in 31..98 -> when (stage) { + 0 -> npcl(FacialExpression.WORRIED, "Oh no, they won't stand a chance. Please go back and help.").also { stage = END_DIALOGUE } + } + + + 99 -> when (stage) { + 0 -> npcl(FacialExpression.AMAZED, "You're alive, I thought Khazard's men had taken you.").also { stage++ } + 1 -> npcl(FacialExpression.HAPPY, "My son and husband are safe and recovering at home. Without you they would certainly be dead. I am truly grateful for your service.").also { stage++ } + 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(Quests.FIGHT_ARENA).finish(player) + } + } + + 100 -> when (stage) { + 0 -> npcl(FacialExpression.FRIENDLY, "Oh hello my dear. My husband and son are resting while I wait for the cart fixer.").also { stage++ } + 1 -> playerl(FacialExpression.FRIENDLY, "I hope he's not too long.").also { stage++ } + 2 -> npcl(FacialExpression.FRIENDLY, "Thanks again for everything.").also { stage = END_DIALOGUE } + } + } + return true + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.LADY_SERVIL_264) + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..84382f980 --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/quest/arena/dialogue/LocalDialogue.kt @@ -0,0 +1,53 @@ +package content.region.kandarin.ardougne.quest.arena.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 +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(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(Quests.FIGHT_ARENA) in 91..99) { + playerl(FacialExpression.FRIENDLY, "Hello.").also { stage = 9 } + } else if (player.questRepository.getStage(Quests.FIGHT_ARENA) >= 10) { + playerl(FacialExpression.FRIENDLY, "Hello.").also { stage = 0 } + } else { + playerl(FacialExpression.FRIENDLY, "Hello.").also { stage = 7 } + } + return true + } + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage) { + 0 -> npcl(FacialExpression.FRIENDLY, "I heard the Servil family are fighting soon. Should be very entertaining.").also { stage++ } + 1 -> npcl(FacialExpression.ASKING, "Hello stranger, are you new to these parts?").also { stage++ } + 2 -> playerl(FacialExpression.ASKING, "I suppose I am.").also { stage++ } + 3 -> npcl(FacialExpression.ASKING, "What's your business?").also { stage++ } + 4 -> playerl(FacialExpression.ASKING, "Just visiting friends in the cells.").also { stage++ } + 5 -> npcl(FacialExpression.LAUGH, "Visiting, that's funny.").also { stage++ } + 6 -> npcl(FacialExpression.FRIENDLY, "Only Khazard guards are allowed to see prisoners. Unless you know where to get some Khazard armour, you won't be visiting anyone.").also { stage = END_DIALOGUE } + 7 -> npcl(FacialExpression.ASKING, "Hello stranger, are you new to these parts? You look lost.").also { stage++ } + 8 -> npcl(FacialExpression.HALF_THINKING, "I suppose you're here for the fight arena? There are some rich folk fighting tomorrow. Should be entertaining.").also { stage = END_DIALOGUE } + 9 -> npcl(FacialExpression.FRIENDLY, "You're the guy who beat Bouncer! Amazing! That makes you a champion to many, but you'd better get out of here while you still can. General Khazard liked that brute and will be after you.").also { stage++ } + 10 -> playerl(FacialExpression.FRIENDLY, "He tried, but I killed him. You are safe now.").also { stage++ } + 11 -> npcl(FacialExpression.FRIENDLY, "Sadly, you can't kill what is already dead. He's probably still out there, somewhere.").also { stage = END_DIALOGUE } + + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return LocalDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.LOCAL_268) + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..f79aefcb1 --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/quest/arena/npc/BouncerNPC.kt @@ -0,0 +1,64 @@ +package content.region.kandarin.ardougne.quest.arena.npc + +import content.region.kandarin.ardougne.quest.arena.dialogue.GeneralKhazardDialogue +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.system.task.Pulse +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) { + var clearTime = 0 + override fun construct(id: Int, location: Location, vararg objects: Any): AbstractNPC { + return BouncerNPC(id, location) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.BOUNCER_269) + } + + override fun handleTickActions() { + super.handleTickActions() + if (clearTime++ > 300) poofClear(this) + } + + companion object { + fun spawnBouncer(player: Player) { + val bouncer = BouncerNPC(NPCs.BOUNCER_269) + bouncer.location = location(2604, 3160, 0) + bouncer.isWalks = true + bouncer.isAggressive = true + bouncer.isActive = false + + if (bouncer.asNpc() != null && bouncer.isActive && getAttribute(player, "spawn-bouncer", false)) { + bouncer.properties.teleportLocation = bouncer.properties.spawnLocation + } + bouncer.isActive = true + GameWorld.Pulser.submit(object : Pulse(2, bouncer) { + override fun pulse(): Boolean { + bouncer.init() + bouncer.attack(player) + return true + } + }) + } + } + + override fun finalizeDeath(killer: Entity?) { + if (killer is Player) { + if (getQuestStage(killer, Quests.FIGHT_ARENA) >= 89) { + setQuestStage(killer, Quests.FIGHT_ARENA, 91) + } + removeAttribute(killer, "spawn-bouncer") + openDialogue(killer, GeneralKhazardDialogue()) + } + clear() + super.finalizeDeath(killer) + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..700876e14 --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/quest/arena/npc/GeneralNPC.kt @@ -0,0 +1,41 @@ +package content.region.kandarin.ardougne.quest.arena.npc + +import content.region.kandarin.ardougne.quest.arena.FightArenaListeners.Companion.General +import content.region.kandarin.ardougne.quest.arena.dialogue.JeremyServilBDialogue +import core.api.openDialogue +import core.api.getQuestStage +import core.api.setQuestStage +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 +import content.data.Quests + +@Initializable +class GeneralNPC(id: Int = 0, location: Location? = null) : AbstractNPC(id, location) { + override fun construct(id: Int, location: Location, vararg objects: Any): AbstractNPC { + return GeneralNPC(id, location) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.GENERAL_KHAZARD_258) + } + + override fun handleTickActions() { + super.handleTickActions() + General.asNpc().isRespawn = true + General.respawnTick = 10 + } + + override fun finalizeDeath(killer: Entity?) { + if (killer is Player) { + if (getQuestStage(killer, Quests.FIGHT_ARENA) == 97) { + setQuestStage(killer, Quests.FIGHT_ARENA, 98) + } + openDialogue(killer, JeremyServilBDialogue()) + } + super.finalizeDeath(killer) + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..67e2ae7fd --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/quest/arena/npc/OgreNPC.kt @@ -0,0 +1,66 @@ +package content.region.kandarin.ardougne.quest.arena.npc + +import content.region.kandarin.ardougne.quest.arena.dialogue.GeneralKhazardDialogue +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.system.task.Pulse +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) { + var clearTime = 0 + override fun construct(id: Int, location: Location, vararg objects: Any): AbstractNPC { + return OgreNPC(id, location) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.KHAZARD_OGRE_270) + } + + override fun handleTickActions() { + super.handleTickActions() + if (clearTime++ > 300) poofClear(this) + } + + companion object { + fun spawnOgre(player: Player) { + val ogre = OgreNPC(NPCs.KHAZARD_OGRE_270) + ogre.location = location(2603, 3166, 0) + ogre.isWalks = true + ogre.isAggressive = true + ogre.isActive = false + + if (ogre.asNpc() != null && ogre.isActive && getAttribute(player, "spawn-ogre", false)) { + ogre.properties.teleportLocation = ogre.properties.spawnLocation + } + ogre.isActive = true + GameWorld.Pulser.submit(object : Pulse(2, ogre) { + override fun pulse(): Boolean { + ogre.init() + ogre.attack(player) + registerHintIcon(player, ogre) + return true + } + }) + } + } + + override fun finalizeDeath(killer: Entity?) { + if (killer is Player) { + 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") + openDialogue(killer, GeneralKhazardDialogue()) + } + clear() + super.finalizeDeath(killer) + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..164464901 --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/quest/arena/npc/ScorpionNPC.kt @@ -0,0 +1,64 @@ +package content.region.kandarin.ardougne.quest.arena.npc + +import content.region.kandarin.ardougne.quest.arena.dialogue.GeneralKhazardDialogue +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.system.task.Pulse +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) { + var clearTime = 0 + override fun construct(id: Int, location: Location, vararg objects: Any): AbstractNPC { + return ScorpionNPC(id, location) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.KHAZARD_SCORPION_271) + } + + override fun handleTickActions() { + super.handleTickActions() + if (clearTime++ > 300) poofClear(this) + } + + companion object { + fun spawnScorpion(player: Player) { + val scorpion = ScorpionNPC(NPCs.KHAZARD_SCORPION_271) + scorpion.location = location(2604, 3159, 0) + scorpion.isWalks = true + scorpion.isAggressive = true + scorpion.isActive = false + + if (scorpion.asNpc() != null && scorpion.isActive && getAttribute(player, "spawn-scorpion", false)) { + scorpion.properties.teleportLocation = scorpion.properties.spawnLocation + } + scorpion.isActive = true + GameWorld.Pulser.submit(object : Pulse(2, scorpion) { + override fun pulse(): Boolean { + scorpion.init() + scorpion.attack(player) + return true + } + }) + } + } + + override fun finalizeDeath(killer: Entity?) { + if (killer is Player) { + if (getQuestStage(killer, Quests.FIGHT_ARENA) == 88) { + setQuestStage(killer, Quests.FIGHT_ARENA, 89) + } + removeAttribute(killer, "spawn-scorpion") + openDialogue(killer, GeneralKhazardDialogue()) + } + clear() + super.finalizeDeath(killer) + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..c06a15558 --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/quest/clocktower/BrotherKojoDialogueFile.kt @@ -0,0 +1,109 @@ +package content.region.kandarin.ardougne.quest.clocktower + +import content.data.Quests +import core.api.* +import core.game.dialogue.DialogueFile +import core.game.dialogue.FacialExpression +import core.game.dialogue.Topic +import core.tools.END_DIALOGUE +import core.tools.START_DIALOGUE + +class BrotherKojoDialogueFile : DialogueFile() { + + override fun handle(componentID: Int, buttonID: Int) { + if (getAttribute(player!!, ClockTower.attributeAskKojoAboutRats, false) && !getAttribute(player!!, ClockTower.attributeRatsPoisoned, false)) { + when (stage) { + START_DIALOGUE -> playerl(FacialExpression.FRIENDLY, "I've found the white cog! But it's locked behind a gate. There's rats everywhere!").also { stage++ } + 1 -> npcl(FacialExpression.FRIENDLY, "Rats again?! Kill them! Kill them all!").also { stage++ } + 2 -> playerl(FacialExpression.FRIENDLY, "But how do I open the gate? Do you have a key?").also { stage++ } + 3 -> npcl(FacialExpression.FRIENDLY, "Get rid of the rats! I'm sure I left some rat poison down there!").also { stage++ } + 4 -> playerl(FacialExpression.FRIENDLY, "I guess I better go and deal with the rat problem...").also { + stage = END_DIALOGUE + } + } + return + } + when (getQuestStage(player!!, Quests.CLOCK_TOWER)) { + 0 -> { + when (stage) { + START_DIALOGUE -> player(FacialExpression.FRIENDLY, "Hello monk.").also { stage++ } + 1 -> npcl(FacialExpression.FRIENDLY, "Hello adventurer. My name is Brother Kojo. Do you happen to know the time?").also { stage++ } + 2 -> player(FacialExpression.SAD, "No, sorry, I don't.").also { stage++ } + 3 -> npcl(FacialExpression.NEUTRAL, "Exactly! This clock tower has recently broken down, and without it nobody can tell the correct time. I must fix it before the town people become too angry!").also { stage++ } + 4 -> npcl(FacialExpression.ASKING, "I don't suppose you could assist me in the repairs? I'll pay you for your help.").also { stage++ } + 5 -> showTopics( + Topic(FacialExpression.FRIENDLY, "Ok old monk, what can I do?", 30), + Topic(FacialExpression.ASKING, "So... how much reward are we talking then?", 10), + Topic(FacialExpression.FRIENDLY, "Not now old monk.", 20), + ) + + 10 -> npcl(FacialExpression.STRUGGLE, "Well, I'm only a monk so I'm not exactly rich, but I assure you I will give you a fair reward for the time spent assisting me in repairing the clock.").also { stage = 4 } + + 20 -> npcl(FacialExpression.FRIENDLY, "OK then. Come back and let me know if you change your mind.").also { stage = END_DIALOGUE } + + 30 -> npc(FacialExpression.HAPPY, "Oh, thank you kind ${if (player!!.isMale) "sir" else "madam"}!", + "In the cellar below, you'll find four cogs.", + "They're too heavy for me, but you should be able to", + "carry them one at a time.").also { stage++ } + + 31 -> npcl(FacialExpression.THINKING, "I know one goes on each floor... but I can't exactly remember which goes where specifically. Oh well, I'm sure you can figure it out fairly easily.").also { stage++ } + 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!!, Quests.CLOCK_TOWER, 1) + } + } + } + 1 -> { + when (stage) { + START_DIALOGUE -> npcl(FacialExpression.ASKING, "Oh hello, are you having trouble? The cogs are in four rooms below us. Place one cog on a pole on each of the four tower levels.").also { stage++ } + 1 -> showTopics( + Topic(FacialExpression.FRIENDLY, "Can I have a hint?", 2), + Topic(FacialExpression.ASKING, "I'll carry on looking.", END_DIALOGUE), + ) + 2 -> showTopics( + Topic(FacialExpression.FRIENDLY, "Have you any idea where I might find the red cog?", 3), + Topic(FacialExpression.FRIENDLY, "Have you any idea where I might find the blue cog?", 3), + Topic(FacialExpression.FRIENDLY, "Have you any idea where I might find the black cog?", 3), + Topic(FacialExpression.FRIENDLY, "Have you any idea where I might find the white cog?", 3), + ) + 3 -> npcl(FacialExpression.FRIENDLY, "If I knew, I wouldn't ask you to find it. It's in the basement somewhere.").also { stage = 1 } + } + } + 2 -> { + when (stage) { + START_DIALOGUE -> playerl(FacialExpression.FRIENDLY, "I've placed a cog!").also { stage++ } + 1 -> npcl(FacialExpression.FRIENDLY, "That's great. Come see me when you've done the other three.").also { stage = END_DIALOGUE } + } + } + 3 -> { + when (stage) { + START_DIALOGUE -> playerl(FacialExpression.FRIENDLY, "Two down!").also { stage++ } + 1 -> npcl(FacialExpression.FRIENDLY, "Two to go.").also { stage = END_DIALOGUE } + } + } + 4 -> { + when (stage) { + START_DIALOGUE -> npcl(FacialExpression.FRIENDLY, "One left.").also { stage = END_DIALOGUE } + } + } + in 5 .. 10 -> { + when (stage) { + START_DIALOGUE -> playerl(FacialExpression.FRIENDLY, "I have replaced all the cogs!").also { stage++ } + 1 -> npcl(FacialExpression.FRIENDLY, "Really..? Wait, listen! Well done, well done! Yes yes yes, you've done it! You ARE clever!").also { stage++ } + 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!!, Quests.CLOCK_TOWER) + } + } + } + 100 -> { + when (stage) { + START_DIALOGUE -> npcl(FacialExpression.FRIENDLY, "Oh hello there traveller. You've done a grand job with the clock. It's just like new.").also { stage = END_DIALOGUE } + } + } + } + } + +} \ No newline at end of file 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 new file mode 100644 index 000000000..2be89b1be --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/quest/clocktower/ClockTower.kt @@ -0,0 +1,74 @@ +package content.region.kandarin.ardougne.quest.clocktower + +import core.api.addItemOrDrop +import core.api.getAttribute +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(Quests.CLOCK_TOWER,38, 37, 1, 10, 0, 1, 8) { + companion object { + const val attributeBlueCog = "/save:quest:clocktower-bluecogplaced" + const val attributeBlackCog = "/save:quest:clocktower-blackcogplaced" + const val attributeWhiteCog = "/save:quest:clocktower-whitecogplaced" + const val attributeRedCog = "/save:quest:clocktower-redcogplaced" + const val attributeBlackCogCooled = "/save:quest:clocktower-blackcogcooled" + const val attributeRatsPoisoned = "/save:quest:clocktower-poisonplaced" + const val attributeAskKojoAboutRats = "quest:clocktower-askkojoaboutrats" + } + + override fun drawJournal(player: Player?, stage: Int) { + super.drawJournal(player, stage) + + var line = 12 + var stage = getStage(player) + + when (stage) { + 0 -> { + line(player, "I can start this quest by speaking to !!Brother Kojo?? at the", line++) + line(player, "!!Clock Tower?? which is located !!South?? of !!Ardougne??", line++) + } + + in 1 .. 10 -> { + line(player, "I spoke to Brother Kojo at the Clock Tower South of", line++, true) + line(player, "Ardougne and agreed to help him repair the clock.", line++, true) + line(player,"To repair the clock I need to find the four coloured cogs", line++, false) + line(player,"and place them on the four correctly coloured spindles.", line++, false) + line(player, if (getAttribute(player!!, attributeBlueCog, false)) "I placed the !!Blue Cog?? on it's !!spindle??." else "I haven't placed the !!Blue Cog?? on it's !!spindle?? yet.", line++, getAttribute(player!!, attributeBlueCog, false)) + line(player, if (getAttribute(player!!, attributeBlackCog, false)) "I placed the !!Black Cog?? on it's !!spindle??." else "I haven't placed the !!Black Cog?? on it's !!spindle?? yet.", line++, getAttribute(player!!, attributeBlackCog, false)) + line(player, if (getAttribute(player!!, attributeWhiteCog, false)) "I placed the !!White Cog?? on it's !!spindle??." else "I haven't placed the !!White Cog?? on it's !!spindle?? yet.", line++, getAttribute(player!!, attributeWhiteCog, false)) + line(player, if (getAttribute(player!!, attributeRedCog, false)) "I placed the !!Red Cog?? on it's !!spindle??." else "I haven't placed the !!Red Cog?? on it's !!spindle?? yet.", line++, getAttribute(player!!, attributeRedCog, false)) + } + + 100 -> { + line(player, "I spoke to Brother Kojo at the Clock Tower South of", line++, true) + line(player, "I have placed all four cogs successfully on the spindles.", line++, true) + line(player,"Brother Kojo was grateful for all my help and rewarded me.", line++, true) + line++ + line(player,"QUEST COMPLETE!", line) + } + } + } + + override fun finish(player: Player) { + var ln = 10 + super.finish(player) + player.packetDispatch.sendString("You have completed the Clock Tower Quest!", 277, 4) + player.packetDispatch.sendItemZoomOnInterface(Items.COINS_995, 240, 277, 5) + + drawReward(player,"1 Quest Point", ln++) + drawReward(player,"500 coins", ln++) + + addItemOrDrop(player, Items.COINS_995, 500) + } + + override fun newInstance(`object`: Any?): Quest { + return this + } +} 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 new file mode 100644 index 000000000..a769bac13 --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/quest/clocktower/ClockTowerListeners.kt @@ -0,0 +1,294 @@ +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 +import core.game.interaction.InteractionListener +import core.game.node.item.GroundItem +import core.game.node.item.Item +import core.game.world.map.Direction +import core.game.world.map.Location +import core.game.world.map.path.Pathfinder +import core.game.world.update.flag.context.Animation +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import org.rs09.consts.Scenery + +class ClockTowerListener : InteractionListener { + + companion object { + val DOWN_ANIMATION = Animation(2140) + val UP_ANIMATION = Animation(2139) + } + + override fun defineListeners() { + onUseWith(IntType.SCENERY, intArrayOf(Items.WHITE_COG_20, Items.BLACK_COG_21, Items.BLUE_COG_22, Items.RED_COG_23), Scenery.CLOCK_SPINDLE_25) { player, _, _ -> + sendMessage(player, "The cog doesn't seem to fit.") + return@onUseWith true + } + + onUseWith(IntType.SCENERY, intArrayOf(Items.WHITE_COG_20, Items.BLACK_COG_21, Items.BLUE_COG_22, Items.RED_COG_23), Scenery.CLOCK_SPINDLE_26) { player, _, _ -> + sendMessage(player, "The cog doesn't seem to fit.") + return@onUseWith true + } + + onUseWith(IntType.SCENERY, intArrayOf(Items.WHITE_COG_20, Items.BLACK_COG_21, Items.BLUE_COG_22, Items.RED_COG_23), Scenery.CLOCK_SPINDLE_27) { player, _, _ -> + sendMessage(player, "The cog doesn't seem to fit.") + return@onUseWith true + } + + onUseWith(IntType.SCENERY, intArrayOf(Items.WHITE_COG_20, Items.BLACK_COG_21, Items.BLUE_COG_22, Items.RED_COG_23), Scenery.CLOCK_SPINDLE_28) { player, _, _ -> + sendMessage(player, "The cog doesn't seem to fit.") + return@onUseWith true + } + + onUseWith(IntType.SCENERY, Items.WHITE_COG_20, Scenery.CLOCK_SPINDLE_31) { player, _, _ -> + if (!getAttribute(player, ClockTower.attributeWhiteCog, false)) { + if (removeItem(player, Items.WHITE_COG_20)) { + sendMessage(player, "The cog fits perfectly.") + setAttribute(player, ClockTower.attributeWhiteCog, true) + if (!isQuestComplete(player, Quests.CLOCK_TOWER)) { + setQuestStage( + player, + Quests.CLOCK_TOWER, + getQuestStage(player, Quests.CLOCK_TOWER) + 1 + ) + } + } + } else { + sendMessage(player, "The cog has already been placed.") + } + return@onUseWith true + } + + onUseWith(IntType.SCENERY, Items.BLACK_COG_21, Scenery.CLOCK_SPINDLE_30) { player, _, _ -> + if (!getAttribute(player, ClockTower.attributeBlackCog, false)) { + if (removeItem(player, Items.BLACK_COG_21)) { + sendMessage(player, "The cog fits perfectly.") + setAttribute(player, ClockTower.attributeBlackCog, true) + if (!isQuestComplete(player, Quests.CLOCK_TOWER)) { + setQuestStage( + player, + Quests.CLOCK_TOWER, + getQuestStage(player, Quests.CLOCK_TOWER) + 1 + ) + } + } + } else { + sendMessage(player, "The cog has already been placed.") + } + return@onUseWith true + } + + onUseWith(IntType.SCENERY, Items.BLUE_COG_22, Scenery.CLOCK_SPINDLE_32) { player, _, _ -> + if (!getAttribute(player, ClockTower.attributeBlueCog, false)) { + if (removeItem(player, Items.BLUE_COG_22)) { + sendMessage(player, "The cog fits perfectly.") + setAttribute(player, ClockTower.attributeBlueCog, true) + if (!isQuestComplete(player, Quests.CLOCK_TOWER)) { + setQuestStage( + player, + Quests.CLOCK_TOWER, + getQuestStage(player, Quests.CLOCK_TOWER) + 1 + ) + } + } + } else { + sendMessage(player, "The cog has already been placed.") + } + return@onUseWith true + } + + onUseWith(IntType.SCENERY, Items.RED_COG_23, Scenery.CLOCK_SPINDLE_29) { player, _, _ -> + if (!getAttribute(player, ClockTower.attributeRedCog, false)) { + if (removeItem(player, Items.RED_COG_23)) { + sendMessage(player, "The cog fits perfectly.") + setAttribute(player, ClockTower.attributeRedCog, true) + if (!isQuestComplete(player, Quests.CLOCK_TOWER)) { + setQuestStage( + player, + Quests.CLOCK_TOWER, + getQuestStage(player, Quests.CLOCK_TOWER) + 1 + ) + } + } + } else { + sendMessage(player, "The cog has already been placed.") + } + return@onUseWith true + } + + // The lever to the rats + val levers = intArrayOf(Scenery.LEVER_33, Scenery.LEVER_34) + on(levers, IntType.SCENERY, "pull") { player, node -> + val leverScenery = node.asScenery() + // This one opens the first and only gate + if (leverScenery.location.equals(2591, 9661, 0)) { + if (leverScenery.id == Scenery.LEVER_33) { + val metalGate = getScenery(2595, 9657, 0)!! + val location = metalGate.location.transform(1, 0, 0) + face(player, Location.create(2591, 9660, 0)) + animate(player, UP_ANIMATION) + replaceScenery(metalGate, Scenery.GATE_37, -1, Direction.WEST, location) + replaceScenery(leverScenery, Scenery.LEVER_34, -1) + } else if (leverScenery.id == Scenery.LEVER_34) { + val metalGateOpen = getScenery(2596, 9657, 0)!! + val location = metalGateOpen.location.transform(-1, 0, 0) + face(player, Location.create(2591, 9660, 0)) + animate(player, DOWN_ANIMATION) + replaceScenery(metalGateOpen, Scenery.GATE_37, -1, Direction.SOUTH, location) + replaceScenery(leverScenery, Scenery.LEVER_33, -1) + } + } + if (leverScenery.location.equals(2593, 9661, 0)) { + // This one doesn't do anything https://www.youtube.com/watch?v=2FXiYftAX9A + // The second gate is fixed and is not a Scenery + if (leverScenery.id == Scenery.LEVER_34) { + face(player, Location.create(2593, 9660, 0)) + animate(player, DOWN_ANIMATION) + replaceScenery(leverScenery, Scenery.LEVER_33, -1) + } else if (leverScenery.id == Scenery.LEVER_33) { + face(player, Location.create(2593, 9660, 0)) + animate(player, UP_ANIMATION) + replaceScenery(leverScenery, Scenery.LEVER_34, -1) + } + } + return@on true + } + + // The gate to the rats + on(Scenery.GATE_37, IntType.SCENERY, "open") { player, _ -> + sendMessage(player, "The door doesn't seem to open from here...") + return@on true + } + + // Poison the rats + onUseWith(IntType.SCENERY, Items.RAT_POISON_24, Scenery.FOOD_TROUGH_40) { player, _, _ -> + if (getAttribute(player, ClockTower.attributeRatsPoisoned, false)) { + sendDialogue(player, "The gate has already been damaged, you see no reason to cause more suffering.") + return@onUseWith true + } + if (removeItem(player, Items.RAT_POISON_24)) { + val loc = location(2579, 9656, 0) // Location of the gate + sendMessage(player, "The rats swarm towards the poisoned food...") + sendMessage(player, "... and devour it hungrily.") + sendMessage(player, "You see them smashing against the gates in a panic.") + sendMessage(player, "They seem to be dying.") + setAttribute(player, ClockTower.attributeRatsPoisoned, true) + runTask(player, 1) { + val rats = findLocalNPCs(player, intArrayOf(NPCs.DUNGEON_RAT_224)) + rats.forEach { rat -> + if (rat.location.withinDistance(loc, 20)) { + var xDeath = 0 + var yDeath = -1 + for (rat in rats) { + + Pathfinder.find(rat.location, location(loc.x + xDeath, loc.y + yDeath, loc.z), true, Pathfinder.SMART).walk(rat) + xDeath++ + if (xDeath == 4) { + xDeath = 0 + yDeath++ + } + } + runTask(rat, 15) { + rat.startDeath(null) + } + } + } + } + } + return@onUseWith true + } + + // Gate to White Cog (after rats die) + on(Scenery.GATE_39, IntType.SCENERY, "go-through") { player, node -> + if (getAttribute(player, ClockTower.attributeRatsPoisoned, false)) { + sendDialogueLines(player, "The death throws of the rats seem to have shaken the door loose of", "its hinges. You pick it up and go through.") + DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) + } else { + sendPlayerDialogue(player, "It won't open. Maybe the monk knows how to open this door?") + setAttribute(player, ClockTower.attributeAskKojoAboutRats, true) + } + return@on true + } + + // Wall to Blue Cog + on(Scenery.WALL_1586, IntType.SCENERY, "push") { player, node -> + DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) + return@on true + } + + on(Items.WHITE_COG_20, IntType.GROUNDITEM, "take") { player, groundItem -> + if (inInventory(player, Items.WHITE_COG_20) || inInventory(player, Items.BLACK_COG_21) || inInventory(player, Items.BLUE_COG_22) || inInventory(player, Items.RED_COG_23)) { + sendDialogue(player, "The cogs are too heavy to carry more than one at a time.") + } else if (hasSpaceFor(player, Item(Items.WHITE_COG_20))) { + addItem(player, Items.WHITE_COG_20) + removeGroundItem(groundItem as GroundItem) + } + return@on true + } + + onUseWith(IntType.GROUNDITEM, Items.BUCKET_OF_WATER_1929, Items.BLACK_COG_21) { player, _, _ -> + if (removeItem(player, Items.BUCKET_OF_WATER_1929)) { + addItem(player, Items.BUCKET_1925) + sendDialogue(player, "You pour water over the cog. It quickly cools down enough to take.") + setAttribute(player, ClockTower.attributeBlackCogCooled, true) + } + return@onUseWith true + } + + on(Items.BLACK_COG_21, IntType.GROUNDITEM, "take") { player, groundItem -> + if (inInventory(player, Items.WHITE_COG_20) || inInventory(player, Items.BLACK_COG_21) || inInventory(player, Items.BLUE_COG_22) || inInventory(player, Items.RED_COG_23)) { + sendDialogue(player, "The cogs are too heavy to carry more than one at a time.") + return@on true + } + if (hasSpaceFor(player, Item(Items.BLACK_COG_21)) && getAttribute(player, ClockTower.attributeBlackCogCooled, false)) { + addItem(player, Items.BLACK_COG_21) + removeGroundItem(groundItem as GroundItem) + return@on true + } + if (!inInventory(player, Items.BUCKET_OF_WATER_1929, 1) && !inEquipment(player, Items.ICE_GLOVES_1580)) { + sendDialogue(player, "The cog is red hot from the flames. You cannot pick it up.") + return@on true + } + if (hasSpaceFor(player, Item(Items.BLACK_COG_21)) && (inInventory(player, Items.BUCKET_OF_WATER_1929, 1) || inEquipment(player, Items.ICE_GLOVES_1580)) && !getAttribute(player, ClockTower.attributeBlackCogCooled, false)) { + // Picking up the Black Cog requires dousing it in water or using ice gloves + if (!inEquipment(player, Items.ICE_GLOVES_1580) && removeItem(player, Items.BUCKET_OF_WATER_1929)) { + sendDialogue(player, "You pour water over the cog. It quickly cools down enough to take.") + addItem(player, Items.BUCKET_1925) + addItem(player, Items.BLACK_COG_21) + setAttribute(player, ClockTower.attributeBlackCogCooled, true) + removeGroundItem(groundItem as GroundItem) + } else if (inEquipment(player, Items.ICE_GLOVES_1580) && !inInventory(player, Items.BUCKET_OF_WATER_1929)) { + sendDialogue(player, "You grab the cog with your ice gloves. It quickly cools down enough to take.") + setAttribute(player, ClockTower.attributeBlackCogCooled, true) + removeGroundItem(groundItem as GroundItem) + addItem(player, Items.BLACK_COG_21) + } + } + return@on true + } + + on(Items.BLUE_COG_22, IntType.GROUNDITEM, "take") { player, groundItem -> + if (inInventory(player, Items.WHITE_COG_20) || inInventory(player, Items.BLACK_COG_21) || inInventory(player, Items.BLUE_COG_22) || inInventory(player, Items.RED_COG_23)) { + sendDialogue(player, "The cogs are too heavy to carry more than one at a time.") + } else if (hasSpaceFor(player, Item(Items.BLUE_COG_22))) { + addItem(player, Items.BLUE_COG_22) + removeGroundItem(groundItem as GroundItem) + } + return@on true + } + + on(Items.RED_COG_23, IntType.GROUNDITEM, "take") { player, groundItem -> + if (inInventory(player, Items.WHITE_COG_20) || inInventory(player, Items.BLACK_COG_21) || inInventory(player, Items.BLUE_COG_22) || inInventory(player, Items.RED_COG_23)) { + sendDialogue(player, "The cogs are too heavy to carry more than one at a time.") + } else if (hasSpaceFor(player, Item(Items.RED_COG_23))) { + addItem(player, Items.RED_COG_23) + removeGroundItem(groundItem as GroundItem) + } + return@on true + } + + } +} 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 0f2e6b52b..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 @@ -1,6 +1,6 @@ package content.region.kandarin.ardougne.quest.monksfriend -import core.api.questStage +import core.api.getQuestStage import core.api.sendItemDialogue import core.api.setQuestStage import core.game.node.entity.npc.NPC @@ -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 = questStage(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 868eae3e2..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 = questStage(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 8b20528b7..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 @@ -48,6 +49,8 @@ class MonksFriend: Quest("Monk's Friend", 89, 88, 1, 30, 0, 1, 80) { } if(stage == 100){ line(player, "I had a party with the Monks. There were party balloons and we danced the night away!", line++, true) + line++ + line(player, "%%QUEST COMPLETE!&&", line++) } } diff --git a/Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/PlagueCity.kt b/Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/PlagueCity.kt new file mode 100644 index 000000000..d34a8904d --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/PlagueCity.kt @@ -0,0 +1,114 @@ +package content.region.kandarin.ardougne.quest.plaguecity + +import content.data.Quests +import core.api.addItemOrDrop +import core.api.removeAttributes +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 + +@Initializable +class PlagueCity : Quest(Quests.PLAGUE_CITY, 98, 97, 1, 165, 0, 1, 29) { + override fun newInstance(`object`: Any?): Quest { return this } + override fun drawJournal(player: Player?, stage: Int) { + super.drawJournal(player, stage) + var line = 11 + player ?: return + + if (stage == 0) { + line++ + line(player, "I can start this quest by speaking to !!Edmond?? who is in !!East??", line++, false) + line(player, "!!Ardougne??",line++, false) + line++ + line(player, "There aren't any requirements for this quest.", line++, false) + } + + if (stage >= 1) { + line++ + line(player, "I've spoken to Edmond, he's asked me to", line++, stage >= 2) + line(player, "help find his daughter Elena.", line++, stage >= 2) + line++ + } + + if (stage >= 2) { + line++ + 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++ + } + + if (stage >= 3) { + line++ + line(player, "She's making a spare which will be in the wardrobe.", line++, stage >= 4) + line(player, "I've spoken to Edmond about getting into West Ardougne.", line++, stage >= 4) + } + + if (stage >= 4) { + line++ + line(player, "I've softened the ground in Edmond's garden enough to dig.", line++, stage >= 5) + } + + if (stage >= 5) { + line++ + line(player, "I've dug a tunnel into the sewers.", line++, stage >= 6) + } + + if (stage >= 6) { + line++ + line(player, "I've managed to clear the way into West Ardougne.", line++, stage >= 7) + } + + if (stage >= 9) { + line++ + line(player, "I've spoken to Jethick, he thinks Elena was staying with the", line++, stage >= 11) + line(player, "Rehnison Family, in a timber house to the north of the city.", line++, stage >= 11) + } + + if (stage >= 11) { + line++ + line(player, "I've spoken to Milli about Elena,", line++, stage >= 12) + line(player, "she says Elena was taken into one of the Plague Houses.", line++, stage >= 12) + } + + if (stage >= 15) { + line++ + line(player, "Bravek might give me clearance if I make his Hangover Cure.", line++, stage >= 16) + } + + if (stage >= 16) { + line++ + line(player, "I've given Bravek the Hangover Cure and he has", line++, stage >= 17) + line(player, "given me a warrant to enter the plague House.", line++, stage >= 17) + } + + if (stage >= 99) { + line++ + line(player, "I've freed Elena from the Plague House.", line++, stage == 100) + } + + if (stage == 100) { + line++ + line(player, "I've spoken to Edmond and he thanked me", line++, true) + line(player, "for rescuing his daughter Elena.", line++, true) + line++ + line(player, "QUEST COMPLETE!", line, false) + } + } + + override fun finish(player: Player?) { + super.finish(player) + player ?: return + var ln = 10 + + player.packetDispatch.sendItemZoomOnInterface(Items.GAS_MASK_1506, 230, 277, 5) + drawReward(player, "1 Quest Point", ln++) + drawReward(player, "2,425 Mining XP", ln++) + drawReward(player, "An Ardougne Teleport Scroll", ln) + rewardXP(player, Skills.MINING, 2425.0) + addItemOrDrop(player, Items.A_MAGIC_SCROLL_1505) + removeAttributes(player, "/save:elena:dig", "/save:elena:bucket") + } +} \ No newline at end of file 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/quest/plaguecity/UndergroundCutscene.kt b/Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/UndergroundCutscene.kt new file mode 100644 index 000000000..eea5f2ffa --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/UndergroundCutscene.kt @@ -0,0 +1,124 @@ +package content.region.kandarin.ardougne.quest.plaguecity + +import content.data.Quests +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 core.game.world.map.Location + +class UndergroundCutscene(player: Player) : Cutscene(player) { + + override fun setup() { + setExit(Location.create(2514, 9740,0)) + if (player.settings.isRunToggled) { + player.settings.toggleRun() + } + loadRegion(10136) + addNPC(EDMOND, 18, 13, Direction.SOUTH) + } + + + override fun runStage(stage: Int) { + when (stage) { + + 0 -> { + fadeToBlack() + timedUpdate(5) + } + + 1 -> { + 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) + timedUpdate(4) + } + + 3 -> { + 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 -> { + 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", hide=true) + sendChat(getNPC(EDMOND)!!, "Once you're in the city") + timedUpdate(4) + } + + 6 -> { + sendChat(getNPC(EDMOND)!!, "look for a man called") + timedUpdate(4) + } + + 7 -> { + sendChat(getNPC(EDMOND)!!, "Jethick, he's an old friend") + timedUpdate(4) + } + + 8 -> { + sendChat(getNPC(EDMOND)!!, "and should help you. Send") + timedUpdate(4) + } + + 9 -> { + 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) + } + + 10 -> { + sendChat(getNPC(EDMOND)!!, "seen him since before") + timedUpdate(4) + } + + 11 -> { + sendChat(getNPC(EDMOND)!!, "Elena was born.") + timedUpdate(4) + } + + 12 -> { + sendChat(player, "Alright, thanks I will.") + // 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 -> { + end { + setQuestStage(player, Quests.PLAGUE_CITY, 7) + } + } + } + } + + companion object { + private const val EDMOND = 714 + private const val ROPE_PULL = 3187 + } +} diff --git a/Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/dialogue/AlrenaDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/dialogue/AlrenaDialogue.kt new file mode 100644 index 000000000..56384c44b --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/dialogue/AlrenaDialogue.kt @@ -0,0 +1,155 @@ +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 +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 AlrenaDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + 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++ } + } + return true + } + + override fun handle(componentID: Int, buttonID: Int): Boolean { + 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 } + } + + 1 -> when (stage) { + 1 -> npcl(FacialExpression.NEUTRAL, "Yes he told me. I've begun making your special gas mask, but I need some dwellberries to finish it.").also { stage++ } + 2 -> if (inInventory(player!!, Items.DWELLBERRIES_2126)) { + playerl(FacialExpression.NEUTRAL, "Yes I've got some here.").also { stage++ } + } else { + npcl(FacialExpression.FRIENDLY, "The place to look is in McGrubor's Wood to the west of Seers village.").also { stage = 8 } + } + 3 -> { + sendItemDialogue(player!!, Items.DWELLBERRIES_2126, "You give the dwellberries to Alrena.").also { stage++ } + removeItem(player!!, Items.DWELLBERRIES_2126) + } + // 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) + 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 } + } + + 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) { + 1 -> if (player.getAttribute(PlagueCityListeners.BUCKET_USES_ATTRIBUTE, 0) == 1) { + npcl(FacialExpression.FRIENDLY, "Hello darling, how's that tunnel coming along?").also { stage = 5 } + } else { + npcl(FacialExpression.FRIENDLY, "How's the tunnel going?").also { stage++ } + } + 2 -> playerl(FacialExpression.FRIENDLY, "I'm getting there.").also { stage++ } + 3 -> npcl(FacialExpression.FRIENDLY, "One of the mourners has been sniffing around asking questions about you and Edmond, you should keep an eye out for him.").also { stage++ } + 4 -> playerl(FacialExpression.FRIENDLY, "Ok, thanks for the warning.").also { stage = END_DIALOGUE } + 5 -> playerl(FacialExpression.FRIENDLY, "I just need to soften the soil a little more and then we'll start digging.").also { stage = END_DIALOGUE } + } + + 4 -> when (stage) { + 1 -> npcl(FacialExpression.FRIENDLY, "Hi, have you managed to get through to West Ardougne?").also { stage++ } + 2 -> playerl(FacialExpression.FRIENDLY, "Not yet, but I should be going soon.").also { stage++ } + 3 -> npcl(FacialExpression.FRIENDLY, "Make sure you wear your mask while you're over there! I can't think of a worse way to die.").also { stage++ } + 4 -> playerl(FacialExpression.FRIENDLY, "Ok, thanks for the warning.").also { stage = END_DIALOGUE } + } + + 5 -> when (stage) { + 1 -> npcl(FacialExpression.FRIENDLY, "Hello, any word on Elena?").also { stage++ } + 2 -> playerl(FacialExpression.FRIENDLY, "Not yet I'm afraid.").also { stage++ } + 3 -> npcl(FacialExpression.FRIENDLY, "Is there anything else I can do to help?").also { stage++ } + 4 -> playerl(FacialExpression.FRIENDLY, "It's alright, I'll get her back soon.").also { stage++ } + 5 -> if(inEquipment(player, Items.GAS_MASK_1506)) { + npcl(FacialExpression.FRIENDLY, "That's the spirit, dear.").also { stage = END_DIALOGUE } + } else { + npcl(FacialExpression.FRIENDLY, "That's the spirit, dear. Don't forget that there's a spare gas mask in the wardrobe if you need one.").also { stage = END_DIALOGUE } + } + } + + in 6..7 -> when (stage) { + 1 -> npcl(FacialExpression.FRIENDLY, "Hi, have you managed to get through to West Ardougne?").also { stage++ } + 2 -> playerl(FacialExpression.FRIENDLY, "Not yet, but I should be going soon.").also { stage++ } + 3 -> npcl(FacialExpression.FRIENDLY, "Make sure you wear your mask while you're over there! I can't think of a worse way to die.").also { stage++ } + 4 -> if(inEquipment(player, Items.GAS_MASK_1506)) { + playerl(FacialExpression.FRIENDLY, "Okay, thanks for the warning.").also { stage = END_DIALOGUE } + } else { + npcl(FacialExpression.FRIENDLY, "Don't forget, I've got a spare one hidden in the wardrobe if you need it.").also { stage++ } + } + 5 -> playerl(FacialExpression.FRIENDLY, "Great, thanks Alrena!").also { stage = END_DIALOGUE } + } + + in 8..14 -> when (stage) { + 1 -> npcl(FacialExpression.FRIENDLY, "Hello, any word on Elena?").also { stage++ } + 2 -> playerl(FacialExpression.FRIENDLY, "Not yet I'm afraid.").also { stage++ } + 3 -> npcl(FacialExpression.FRIENDLY, "Is there anything else I can do to help?").also { stage++ } + 4 -> playerl(FacialExpression.FRIENDLY, "Do you have a picture of Elena?").also { stage++ } + 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 } + } + + 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 { + 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) { + 1 -> npcl(FacialExpression.NEUTRAL, "Thank you, thank you! Elena beat you back by minutes.").also { stage = END_DIALOGUE } + } + + 100 -> when (stage) { + 1 -> npcl(FacialExpression.FRIENDLY, "Thank you for rescuing my daughter! Elena has told me of your bravery in entering a house that could have been plague infected. I can't thank you enough!").also { stage = END_DIALOGUE } + } + } + return true + } + + override fun getIds(): IntArray = intArrayOf(NPCs.ALRENA_710) +} 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/quest/plaguecity/dialogue/JethickDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/dialogue/JethickDialogue.kt new file mode 100644 index 000000000..4ab027d70 --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/dialogue/JethickDialogue.kt @@ -0,0 +1,87 @@ +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.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 JethickDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + 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(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!!, 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.", "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 } + } + // 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 { + 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 = 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 } + } + 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 + } else { + end() + sendItemDialogue(player!!, Items.BOOK_1509, "Jethick gives you a book.") + addItem(player!!, Items.BOOK_1509) + stage = END_DIALOGUE + } + } + + in 10..11 -> when (stage) { + 1 -> playerl(FacialExpression.FRIENDLY, "I'm looking for a woman from East Ardougne called Elena.").also { stage++ } + 2 -> npcl(FacialExpression.FRIENDLY,"Ah yes. She came over here to help the plague victims. I think she is staying over with the Rehnison family.").also { stage++ } + 3 -> npcl(FacialExpression.FRIENDLY, "They live in the small timbered building at the far north side of town. I've not seen her around here in a while, mind.").also { stage = END_DIALOGUE } + } + } + return true + } + + override fun getIds(): IntArray = intArrayOf(NPCs.JETHICK_725) +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/dialogue/KidnappedElenaDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/dialogue/KidnappedElenaDialogue.kt new file mode 100644 index 000000000..2fe574b61 --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/dialogue/KidnappedElenaDialogue.kt @@ -0,0 +1,45 @@ +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 +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 KidnappedElenaDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + 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 } + } + return true + } + + override fun handle(componentID: Int, buttonID: Int): Boolean { + when (stage) { + 1 -> npcl(FacialExpression.FRIENDLY, "Thank you, being kidnapped was so inconvenient. I was on my way back to East Ardougne with some samples, I want to see if I can diagnose a cure for this plague.").also { stage++ } + 2 -> playerl(FacialExpression.FRIENDLY, "Well you can leave via the manhole near the gate.").also { stage++ } + 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!!, 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.KIDNAPPED_ELENA_715) +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/dialogue/MarthaRehnisonDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/dialogue/MarthaRehnisonDialogue.kt new file mode 100644 index 000000000..4b2aa051e --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/dialogue/MarthaRehnisonDialogue.kt @@ -0,0 +1,48 @@ +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 +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 MarthaRehnisonDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + 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++ } + } + return true + } + + override fun handle(componentID: Int, buttonID: Int): Boolean { + 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++ } + 2 -> npcl(FacialExpression.FRIENDLY, "However she never managed to leave. My daughter Milli was playing near the west wall when she saw some shadowy figures jump out and grab her. Milli is upstairs if you wish to speak to her.").also { stage = END_DIALOGUE } + } + + 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 } + } + + 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 } + } + } + return true + } + + override fun getIds(): IntArray = intArrayOf(NPCs.MARTHA_REHNISON_722) +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/dialogue/MilliRehnisonDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/dialogue/MilliRehnisonDialogue.kt new file mode 100644 index 000000000..3f5fe54a9 --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/dialogue/MilliRehnisonDialogue.kt @@ -0,0 +1,57 @@ +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.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 MilliRehnisonDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + 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.CHILD_FRIENDLY, "Any luck finding Elena yet?").also { stage++ } + } + return true + } + + override fun handle(componentID: Int, buttonID: Int): Boolean { + when (getQuestStage(player!!, Quests.PLAGUE_CITY)) { + + 9 -> when(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.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.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.CHILD_FRIENDLY, "That's good to hear, she helped us a lot.").also { stage = END_DIALOGUE } + } + } + return true + } + + override fun getIds(): IntArray = intArrayOf(NPCs.MILLI_REHNISON_724) +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/dialogue/TedRehnisonDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/dialogue/TedRehnisonDialogue.kt new file mode 100644 index 000000000..b26c60d26 --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/dialogue/TedRehnisonDialogue.kt @@ -0,0 +1,51 @@ +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 +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 TedRehnisonDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + 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(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 } + } + return true + } + + override fun handle(componentID: Int, buttonID: Int): Boolean { + 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 -> 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) { + 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 } + } + + 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 } + } + } + return true + } + + override fun getIds(): IntArray = intArrayOf(NPCs.TED_REHNISON_721) + +} \ No newline at end of file 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/quest/plaguecity/dialogue/mourners/MournerGuardDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/dialogue/mourners/MournerGuardDialogue.kt new file mode 100644 index 000000000..529554cdb --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/quest/plaguecity/dialogue/mourners/MournerGuardDialogue.kt @@ -0,0 +1,93 @@ +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 +import core.game.dialogue.FacialExpression +import core.game.node.entity.npc.NPC +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import org.rs09.consts.NPCs + +@Initializable +class MournerGuardDialogue : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + 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++ } + 1 -> options("So what's a mourner?", "I haven't got the plague though...", "I'm looking for a woman named Elena.").also { stage++ } + 2 -> when (buttonID) { + 1 -> playerl(FacialExpression.FRIENDLY, "So what's a mourner?").also { stage = 3 } + 2 -> playerl(FacialExpression.FRIENDLY, "I haven't got the plague though...").also { stage = 6 } + 3 -> playerl(FacialExpression.FRIENDLY, "I'm looking for a woman named Elena.").also { stage = 7 } + } + 3 -> npcl(FacialExpression.NEUTRAL, "We're working for King Lathas of East Ardougne. He has tasked us with containing the accursed plague sweeping West Ardougne.").also { stage++ } + 4 -> npcl(FacialExpression.NEUTRAL, "We also do our best to ease these people's suffering. We're nicknamed mourners because we spend a lot of time at plague victim funerals, no one else is allowed to risk attending.").also { stage++ } + 5 -> npcl(FacialExpression.NEUTRAL, "It's a demanding job, and we get little thanks from the people here.").also { stage = 1 } + 6 -> npcl(FacialExpression.NEUTRAL, "Can't risk you being a carrier. That protective clothing you have isn't regulation issue. It won't meet safety standards.").also { stage = 1 } + 7 -> npcl(FacialExpression.NEUTRAL, "Ah yes, I've heard of her. A healer I believe. She must be mad coming over here voluntarily.").also { stage++ } + 8 -> npcl(FacialExpression.NEUTRAL, "I hear rumours she has probably caught the plague now. Very tragic, a stupid waste of life.").also { stage = END_DIALOGUE } + } + + 11 -> when (stage) { + 0 -> player!!.dialogueInterpreter.sendDialogue("The door won't open.", "You notice a black cross on the door.").also { stage++ } + 1 -> npcl(FacialExpression.NEUTRAL, "I'd stand away from there. That black cross means that house has been touched by the plague.").also { stage++ } + 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 = 5 } + 3 -> playerl(FacialExpression.FRIENDLY, "Thanks for the warning.").also { stage = 4 } + + } + 4 -> playerl(FacialExpression.FRIENDLY, "Thanks for the warning.").also { stage = END_DIALOGUE } + 5 -> npcl(FacialExpression.FRIENDLY, "That's irrelevant. You don't have clearance to go in there.").also { stage++ } + 6 -> playerl(FacialExpression.NEUTRAL, "How do I get clearance?").also { stage++ } + 7 -> npcl(FacialExpression.NEUTRAL, "Well you'd need to apply to the head mourner, or I suppose Bravek the city warder.").also { stage++ } + 8 -> npcl(FacialExpression.NEUTRAL, "I wouldn't get your hopes up though.").also { stage++ } + 9 -> { + end() + setQuestStage(player!!, Quests.PLAGUE_CITY, 12) + stage = END_DIALOGUE + } + } + + in 13..15 -> 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++ } + 1 -> options("I need clearance to enter a plague house.", "So what's a mourner?", "I haven't got the plague though...", "I'm looking for a woman named Elena.").also { stage++ } + 2 -> when (buttonID) { + 1 -> playerl(FacialExpression.FRIENDLY, "I need clearance to enter a plague house. It's in the south east corner of West Ardougne.").also { stage = 9 } + 2 -> playerl(FacialExpression.FRIENDLY, "So what's a mourner?").also { stage = 3 } + 3 -> playerl(FacialExpression.FRIENDLY, "I haven't got the plague though...").also { stage = 6 } + 4 -> playerl(FacialExpression.FRIENDLY, "I'm looking for a woman named Elena.").also { stage = 7 } + } + 3 -> npcl(FacialExpression.NEUTRAL, "We're working for King Lathas of East Ardougne. He has tasked us with containing the accursed plague sweeping West Ardougne.").also { stage++ } + 4 -> npcl(FacialExpression.NEUTRAL, "We also do our best to ease these people's suffering. We're nicknamed mourners because we spend a lot of time at plague victim funerals, no one else is allowed to risk attending.").also { stage++ } + 5 -> npcl(FacialExpression.NEUTRAL, "It's a demanding job, and we get little thanks from the people here.").also { stage = 1 } + 6 -> npcl(FacialExpression.NEUTRAL, "Can't risk you being a carrier. That protective clothing you have isn't regulation issue. It won't meet safety standards.").also { stage = 1 } + 7 -> npcl(FacialExpression.NEUTRAL, "Ah yes, I've heard of her. A healer I believe. She must be mad coming over here voluntarily.").also { stage++ } + 8 -> npcl(FacialExpression.NEUTRAL, "I hear rumours she has probably caught the plague now. Very tragic, a stupid waste of life.").also { stage = END_DIALOGUE } + 9 -> npcl(FacialExpression.DISGUSTED, "You must be nuts, absolutely not!").also { stage++ } + 10 -> options("There's a kidnap victim inside!", "I've got a gas mask though...", "Yes, I'm utterly crazy.").also { stage++ } + 11 -> when (buttonID) { + 1 -> playerl(FacialExpression.FRIENDLY, "There's a kidnap victim inside!").also { stage = 12 } + 2 -> playerl(FacialExpression.FRIENDLY, "I've got a gas mask though...").also { stage = 13 } + 3 -> playerl(FacialExpression.FRIENDLY, "Yes, I'm utterly crazy.").also { stage = 17 } + } + 12 -> npcl(FacialExpression.FRIENDLY, "Well they're as good as dead then, no point in trying to save them.").also { stage = END_DIALOGUE } + 13 -> npcl(FacialExpression.FRIENDLY, "It's not regulation. Anyway you're not properly trained to deal with the plague.").also { stage++ } + 14 -> playerl(FacialExpression.FRIENDLY, "How do I get trained?").also { stage++ } + 15 -> npcl(FacialExpression.FRIENDLY, "It requires a strict 18 months of training.").also { stage++ } + 16 -> playerl(FacialExpression.FRIENDLY, "I don't have that sort of time.").also { stage = END_DIALOGUE } + 17 -> playerl(FacialExpression.FRIENDLY, "Yes, I'm utterly crazy.").also { stage++ } + 18 -> npcl(FacialExpression.FRIENDLY, "You're wasting my time, I have a lot of work to do!").also { stage = END_DIALOGUE } + } + + in 16..100 -> when (stage) { + 0 -> 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/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 4a9a09e86..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 @@ -3,6 +3,9 @@ package content.region.kandarin.ardougne.quest.sheepherder; import core.game.node.entity.player.Player; 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 { @@ -19,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; @@ -114,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++; @@ -141,6 +144,20 @@ public class HalgriveDialogue extends DialoguePlugin { break; case 201: npc("Well, please do hurry!"); + stage++; + break; + case 202: + if (ContentAPIKt.hasAnItem(player, Items.SHEEP_FEED_279).getContainer() != null){ + player("I'll do my best sir."); + stage = 108; + } else { + player("Some more sheep poison would be appreciated..."); + stage++; + } + break; + case 203: + ContentAPIKt.addItemOrDrop(player, Items.SHEEP_FEED_279, 1); + npc("Certainly adventurer. Please hurry!"); stage = 108; break; case 205: @@ -152,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; } @@ -163,4 +180,4 @@ public class HalgriveDialogue extends DialoguePlugin { public int[] getIds() { return new int[] {289} ; } -} +} \ No newline at end of file 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 e3941023f..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/westardougne/dialogue/CarlaDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/westardougne/dialogue/CarlaDialogue.kt new file mode 100644 index 000000000..a3431f603 --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/westardougne/dialogue/CarlaDialogue.kt @@ -0,0 +1,52 @@ +package content.region.kandarin.ardougne.westardougne.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 CarlaDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + playerl(FacialExpression.FRIENDLY, "Hello there.").also { stage = 0 } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage) { + 0 -> npcl(FacialExpression.SAD, "Oh, hello.").also { stage++ } + 1 -> playerl(FacialExpression.FRIENDLY, "You seem upset... What's wrong?").also { stage++ } + 2 -> npcl(FacialExpression.FRIENDLY, "That's awful, I'm sorry.").also { stage++ } + 3 -> npcl(FacialExpression.FRIENDLY, "It would be easier to cope with if I could have spent his last few days with him.").also { stage = 4 } + 4 -> playerl(FacialExpression.FRIENDLY, "Why didn't you?").also { stage++ } + 5 -> npcl(FacialExpression.SAD, "Those mourners came and whisked him away. He didn't even seem that ill, I thought it was a common cold. But the mourners said he was infected and had to be taken away. Two days later the mourners returned and told me he had died.").also { stage++ } + 6 -> playerl(FacialExpression.FRIENDLY, "Again, I'm sorry. Life can be harsh.").also { stage++ } + 7 -> options("Where did the plague come from?", "Have there been many victims of the plague?", "I hope things get easier for you.").also { stage++ } + 8 -> when(buttonId) { + 1 -> playerl(FacialExpression.ASKING, "Where did the plague come from?").also { stage = 9 } + 2 -> playerl(FacialExpression.ASKING, "Have there been many victims of the plague?").also { stage = 10 } + 3 -> playerl(FacialExpression.ASKING, "I hope things get easier for you.").also { stage = 13 } + } + 9 -> npcl(FacialExpression.SAD, "It's down to King Tyras. He and his men brought the plague here from the darklands, and then left us to suffer. One day he'll pay for what he's done!").also { stage = 8 } + 10 -> npcl(FacialExpression.SAD, "You could say that... I've heard reports that half of West Ardougne is infected! Many have lost friends and family...").also { stage++ } + 11 -> playerl(FacialExpression.FRIENDLY, "It sounds an awful way to live.").also { stage++ } + 12 -> npcl(FacialExpression.FRIENDLY, "People are very depressed and scared. I've never met anyone fully infected though. I suppose we should be grateful to the mourners for that.").also { stage = END_DIALOGUE } + 13 -> npcl(FacialExpression.SAD, "Me too...").also { stage = END_DIALOGUE } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return CarlaDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.CARLA_712) + } + +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/ardougne/westardougne/dialogue/ChildDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/westardougne/dialogue/ChildDialogue.kt new file mode 100644 index 000000000..210d0ee8d --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/westardougne/dialogue/ChildDialogue.kt @@ -0,0 +1,35 @@ +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 +import core.game.node.entity.player.Player +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import org.rs09.consts.NPCs + +@Initializable +class ChildDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + playerl(FacialExpression.FRIENDLY, "Hello there.").also { stage = 0 } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + 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 + } + + override fun newInstance(player: Player?): DialoguePlugin { + return ChildDialogue(player) + } + + override fun getIds(): IntArray { + 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/westardougne/dialogue/NurseSarahDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/westardougne/dialogue/NurseSarahDialogue.kt new file mode 100644 index 000000000..fd7fbcd9b --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/westardougne/dialogue/NurseSarahDialogue.kt @@ -0,0 +1,38 @@ +package content.region.kandarin.ardougne.westardougne.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 NurseSarahDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + playerl(FacialExpression.FRIENDLY, "Hello there.").also { stage = 0 } + return true + } + + 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, "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 } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return NurseSarahDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.NURSE_SARAH_373) + } + +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/ardougne/westardougne/dialogue/PriestDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/westardougne/dialogue/PriestDialogue.kt new file mode 100644 index 000000000..472884667 --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/westardougne/dialogue/PriestDialogue.kt @@ -0,0 +1,35 @@ +package content.region.kandarin.ardougne.westardougne.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 PriestDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + 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 wish there was more I could do for these people.").also { stage = END_DIALOGUE } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return PriestDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.PRIEST_358) + } + +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/ardougne/westardougne/dialogue/RecruiterDialogue.kt b/Server/src/main/content/region/kandarin/ardougne/westardougne/dialogue/RecruiterDialogue.kt new file mode 100644 index 000000000..3e53af8c2 --- /dev/null +++ b/Server/src/main/content/region/kandarin/ardougne/westardougne/dialogue/RecruiterDialogue.kt @@ -0,0 +1,38 @@ +package content.region.kandarin.ardougne.westardougne.dialogue + +import core.api.sendNPCDialogue +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 RecruiterDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + npcl(FacialExpression.FRIENDLY, "Citizens of West Ardougne. Who will join the Royal Army of Ardougne? It is a very noble cause. Fight alongside King Tyras, crusading in the darklands of the west!").also { stage = 0 } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage) { + 0 -> sendNPCDialogue(player, NPCs.MAN_726, "Plague bringer!").also { stage++ } + 1 -> sendNPCDialogue(player, NPCs.MAN_726, "King Tyras is scum!").also { stage++ } + 2 -> npcl(FacialExpression.FRIENDLY, "Tyras will be informed of these words of treason!").also { stage = END_DIALOGUE } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return RecruiterDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.RECRUITER_720) + } + +} \ No newline at end of file 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/catherby/dialogue/HarryDialogue.java b/Server/src/main/content/region/kandarin/catherby/dialogue/HarryDialogue.java index 84c57b098..1ecf54b21 100644 --- a/Server/src/main/content/region/kandarin/catherby/dialogue/HarryDialogue.java +++ b/Server/src/main/content/region/kandarin/catherby/dialogue/HarryDialogue.java @@ -61,7 +61,7 @@ public final class HarryDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Welcome! You can buy Fishing equipment at my store.", "We'll also give you a good price for any fish that you", "catch."); + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Welcome! You can buy Fishing equipment at my store.", "We'll also give you a good price for any fish that you", "catch."); if (needsFish() || needsSeaWeed()) { stage = 10; } else if (needsFood()) { diff --git a/Server/src/main/content/region/kandarin/catherby/dialogue/HicktonDialogue.java b/Server/src/main/content/region/kandarin/catherby/dialogue/HicktonDialogue.java index 2a291621e..b0c0103c4 100644 --- a/Server/src/main/content/region/kandarin/catherby/dialogue/HicktonDialogue.java +++ b/Server/src/main/content/region/kandarin/catherby/dialogue/HicktonDialogue.java @@ -41,7 +41,7 @@ public final class HicktonDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Welcome to Hickton's Archery Emporium. Do you", "want to see my wares?"); + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Welcome to Hickton's Archery Emporium. Do you", "want to see my wares?"); stage = 0; return true; } @@ -65,7 +65,7 @@ public final class HicktonDialogue extends DialoguePlugin { npc.openShop(player); break; case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No, I prefer to bash things close up."); + interpreter.sendDialogues(player, FacialExpression.EVIL_LAUGH, "No, I prefer to bash things close up."); stage = 20; break; } @@ -84,7 +84,7 @@ public final class HicktonDialogue extends DialoguePlugin { npc.openShop(player); break; case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No, I prefer to bash things close up."); + interpreter.sendDialogues(player, FacialExpression.EVIL_LAUGH, "No, I prefer to bash things close up."); stage = 20; break; } diff --git a/Server/src/main/content/region/kandarin/catherby/dialogue/VanessaDialogue.java b/Server/src/main/content/region/kandarin/catherby/dialogue/VanessaDialogue.java index 31472391b..f1ed25b7d 100644 --- a/Server/src/main/content/region/kandarin/catherby/dialogue/VanessaDialogue.java +++ b/Server/src/main/content/region/kandarin/catherby/dialogue/VanessaDialogue.java @@ -30,10 +30,6 @@ public class VanessaDialogue extends DialoguePlugin { public boolean handle(int interfaceId, int buttonId) { switch (stage) { case 0: - interpreter.sendOptions("Select an Option", "What are you selling?", "Can you give me any Farming advice?", "I'm okay, thank you."); - stage = 1; - break; - case 1: interpreter.sendOptions("Select an Option", "What are you selling?", "Can you give me any Farming advice?", "I'm okay, thank you."); stage = 10; break; @@ -44,11 +40,11 @@ public class VanessaDialogue extends DialoguePlugin { npc.openShop(player); break; case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Can you give me any Farming advice?"); + interpreter.sendDialogues(player, FacialExpression.HALF_ASKING, "Can you give me any Farming advice?"); stage = 20; break; case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I'm okay, thank you."); + interpreter.sendDialogues(player, FacialExpression.FRIENDLY, "I'm okay, thank you."); stage = 30; break; @@ -56,10 +52,7 @@ public class VanessaDialogue extends DialoguePlugin { break; case 20: interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Yes - ask a gardener."); - stage = 21; - break; - case 21: - end(); + stage = 30; break; case 30: end(); @@ -77,7 +70,7 @@ public class VanessaDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Hello. How can I help you?"); + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Hello. How can I help you?"); stage = 0; return true; } diff --git a/Server/src/main/content/region/kandarin/dialogue/AstronomyBook.kt b/Server/src/main/content/region/kandarin/dialogue/AstronomyBook.kt new file mode 100644 index 000000000..80df6aa8c --- /dev/null +++ b/Server/src/main/content/region/kandarin/dialogue/AstronomyBook.kt @@ -0,0 +1,130 @@ +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/BindingBook.kt b/Server/src/main/content/region/kandarin/dialogue/BindingBook.kt new file mode 100644 index 000000000..3318d9077 --- /dev/null +++ b/Server/src/main/content/region/kandarin/dialogue/BindingBook.kt @@ -0,0 +1,149 @@ +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.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 BindingBook : InteractionListener { + companion object { + private val TITLE = "Book of Binding" + private val CONTENTS = arrayOf( + PageSet( + Page( + BookLine(" Book of Binding:", 60), + BookLine(" A treatise on Demons", 61), + ), + Page( + BookLine("", 67), + BookLine(" -- Indexo --", 68), + BookLine(" Arcana: I", 71), + BookLine(" Instructo: II", 72), + BookLine(" Defeati: III", 73), + BookLine(" Enchanto: IIII", 74), + ) + ), + PageSet( + Page( + BookLine("Arcana I: Use holy water to", 55), + BookLine("determine possession. Slight", 56), + BookLine("appearance changes may be", 57), + BookLine("perceived when doused.", 58), + BookLine("", 59), + BookLine("Legendary Silverlight will help", 60), + BookLine("to defeat any demon by", 61), + BookLine("weakening it.", 62), + ), + Page( + BookLine("Arcana II: Be wary of any", 66), + BookLine("demon, it may have special", 67), + BookLine("forms of attack.", 68), + BookLine("", 69), + BookLine("Use an Octagram of fire", 70), + BookLine("to confine unearthly", 71), + BookLine("creatures of the underworld", 72), + BookLine("- the perfect geometry", 73), + BookLine("confuses them.", 74), + BookLine("", 75), + BookLine("Eximus", 76), + ) + ), + PageSet( + Page( + BookLine("Instructo: Creation of holy", 55), + BookLine("water must be undertaken", 56), + BookLine("with determination and urgency.", 57), + BookLine("", 58), + BookLine("Take to yourself empty vials", 59), + BookLine("free of all liquids.", 60), + BookLine("", 61), + BookLine("Read warily the enchantment", 62), + BookLine("contained here within in order", 63), + BookLine("to magick the vial", 64), + ), + Page( + BookLine("for the holding of holy or", 66), + BookLine("sacred water.", 67), + BookLine("", 68), + BookLine("Take utmost care as you", 69), + BookLine("enchant them. With great care", 70), + BookLine("and precision place the sacred", 71), + BookLine("water into the magicked vial", 73), + BookLine("and stopper it.", 74), + BookLine("", 75), + BookLine("Eximus", 76), + ) + ), + PageSet( + Page( + BookLine("Defeati... Ye dreaded", 55), + BookLine("demon will be of unholy", 56), + BookLine("power and abilities.", 57), + BookLine("", 58), + BookLine("Present thyself before", 59), + BookLine("the possessed with good", 60), + BookLine("intent and ready manner.", 61), + BookLine("", 62), + BookLine("With least obstruction", 63), + BookLine("and utmost solemnity hold", 64), + BookLine("open the pages of this", 65), + ), + Page( + BookLine("great tome in order that", 66), + BookLine("the goodlight falls upon", 67), + BookLine("the victim completely.", 68), + BookLine("Be thee prepared in every", 69), + BookLine("capacity, for the demon's", 70), + BookLine("tricks and wiles will outwit", 71), + BookLine("the unready man. Attack with", 72), + BookLine("vigour and zest if thee hopes", 73), + BookLine("to see another day.", 74), + BookLine("", 75), + BookLine("Eximus", 76), + ) + ), + PageSet( + Page( + BookLine("Enchanto...", 55), + BookLine("", 56), + BookLine("Possessus valius emptious,", 57), + BookLine("projectus spellicus avoir valius", 58), + BookLine("magicus.", 59), + BookLine("", 60), + BookLine("Castus enchant avoir createur", 61), + BookLine("valius magicus holious avour", 62), + BookLine("defeati Demonicus Absolutus.", 63), + BookLine("", 64), + BookLine("Demonicus Absolutus.", 65), + ), + Page( + BookLine("Extralias projectus Magicus", 66), + BookLine("Holarius Attackanie demonicus", 67), + BookLine("Absolutus distancie airus", 68), + BookLine("throwus armiues.", 69), + BookLine("", 70), + BookLine("Eximus", 71), + ) + ), + ) + } + + 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.BINDING_BOOK_730, 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/dialogue/EniolaDialogue.kt b/Server/src/main/content/region/kandarin/dialogue/EniolaDialogue.kt index e8af8d314..de5a3e3c8 100644 --- a/Server/src/main/content/region/kandarin/dialogue/EniolaDialogue.kt +++ b/Server/src/main/content/region/kandarin/dialogue/EniolaDialogue.kt @@ -13,19 +13,19 @@ import core.tools.END_DIALOGUE import core.tools.START_DIALOGUE @Initializable -class EniolaDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { +class EniolaDialogue(player: Player? = null) : DialoguePlugin(player) { override fun handle(interfaceId: Int, buttonId: Int): Boolean { when (stage) { START_DIALOGUE -> if (hasIronmanRestriction(player, IronmanMode.ULTIMATE)) { npcl( - core.game.dialogue.FacialExpression.NEUTRAL, + 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( - core.game.dialogue.FacialExpression.NEUTRAL, + FacialExpression.NEUTRAL, "Good day, how may I help you?" ).also { if (hasAwaitingGrandExchangeCollections(player)) { @@ -37,107 +37,107 @@ class EniolaDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin } 1 -> npcl( - core.game.dialogue.FacialExpression.NEUTRAL, + 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(core.game.dialogue.FacialExpression.ASKING, "Who are you?") + 2 -> playerl(FacialExpression.ASKING, "Who are you?") .also { stage++ } 3 -> npcl( - core.game.dialogue.FacialExpression.NEUTRAL, + FacialExpression.NEUTRAL, "How frightfully rude of me, my dear ${if (player.isMale) "sir" else "lady"}. " + "My name is Eniola and I work for that excellent enterprise, the Bank of Gielinor." ).also { stage++ } 4 -> showTopics( - Topic(core.game.dialogue.FacialExpression.HALF_THINKING, "If you work for the bank, what are you doing here?", 10), - Topic(core.game.dialogue.FacialExpression.NEUTRAL, "I'd like to access my bank account, please.", 30), - Topic(core.game.dialogue.FacialExpression.NEUTRAL, "I'd like to check my PIN settings.", 31), - Topic(core.game.dialogue.FacialExpression.NEUTRAL, "I'd like to see my collection box.", 32), - Topic(core.game.dialogue.FacialExpression.NEUTRAL, "Never mind.", END_DIALOGUE) + 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), + 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) ) 10 -> npcl( - core.game.dialogue.FacialExpression.NEUTRAL, + FacialExpression.NEUTRAL, "My presence here is the start of a new enterprise of travelling banks. " + "I, and others like me, will provide you with the convenience of having " + "bank facilities where they will be of optimum use to you." ).also { stage++ } 11 -> playerl( - core.game.dialogue.FacialExpression.ASKING, + FacialExpression.ASKING, "So... What are you doing here?" ).also { stage++ } 12 -> npcl( - core.game.dialogue.FacialExpression.HALF_GUILTY, + FacialExpression.HALF_GUILTY, "The Z.M.I. - that is - the Zamorakian Magical Institute, required my services " + "upon discovery of this altar." ).also { stage++ } 13 -> npcl( - core.game.dialogue.FacialExpression.HALF_GUILTY, + FacialExpression.HALF_GUILTY, "We at the Bank of Gielinor are a neutral party and are willing to offer our " + "services regardless of affiliation. So that is why I am here." ).also { stage++ } 14 -> playerl( - core.game.dialogue.FacialExpression.ASKING, + FacialExpression.ASKING, "Can I access my bank account by speaking to you?" ).also { stage++ } 15 -> npcl( - core.game.dialogue.FacialExpression.NEUTRAL, + FacialExpression.NEUTRAL, "Of course, dear ${if (player.isMale) "sir" else "lady"}. However, I must inform you " + "that because the Z.M.I. are paying for my services, they require anyone not part of " + "the Institute to pay an access fee to open their bank account." ).also { stage++ } 16 -> npcl( - core.game.dialogue.FacialExpression.NEUTRAL, + FacialExpression.NEUTRAL, "But, as our goal as travelling bankers is to make our customers' lives more convenient, " + "we have accomodated to your needs." ).also { stage++ } 17 -> npcl( - core.game.dialogue.FacialExpression.NEUTRAL, + FacialExpression.NEUTRAL, "We know you will be busy creating runes and do not wish " + "to carry money with you." ).also { stage++ } 18 -> npcl( - core.game.dialogue.FacialExpression.NEUTRAL, + FacialExpression.NEUTRAL, "The charge to open your account is the small amount of twenty of one type of rune. " + "The type of rune is up to you." ).also { stage++ } 19 -> npcl( - core.game.dialogue.FacialExpression.HALF_ASKING, + FacialExpression.HALF_ASKING, "Would you like to pay the price of twenty runes to open " + "your bank account?" ).also { stage++ } 20 -> showTopics( - Topic(core.game.dialogue.FacialExpression.NEUTRAL, "Yes, please.", 30), - Topic(core.game.dialogue.FacialExpression.SUSPICIOUS, "Let me open my account and then I'll give you the runes.", 21), - Topic(core.game.dialogue.FacialExpression.ANNOYED, "No way! I'm not paying to withdraw my own stuff.", 22) + Topic(FacialExpression.NEUTRAL, "Yes, please.", 30), + Topic(FacialExpression.SUSPICIOUS, "Let me open my account and then I'll give you the runes.", 21), + Topic(FacialExpression.ANNOYED, "No way! I'm not paying to withdraw my own stuff.", 22) ) 21 -> npcl( - core.game.dialogue.FacialExpression.NEUTRAL, + FacialExpression.NEUTRAL, "It's not that I don't trust you, old ${if (player.isMale) "chap" else "girl"}, " + "but as the old adage goes: 'Payment comes before friends'." ).also { stage = END_DIALOGUE } 22 -> npcl( - core.game.dialogue.FacialExpression.NEUTRAL, + FacialExpression.NEUTRAL, "I'm sorry to hear that, dear ${if (player.isMale) "sir" else "madam"}. " ).also { stage++ } 23 -> npcl( - core.game.dialogue.FacialExpression.NEUTRAL, + FacialExpression.NEUTRAL, "Should you reconsider, because I believe this service offers excellent " + "value for the price, do not hesitate to contact me." ).also { stage = END_DIALOGUE } diff --git a/Server/src/main/content/region/kandarin/dialogue/GiannesCookBook.kt b/Server/src/main/content/region/kandarin/dialogue/GiannesCookBook.kt new file mode 100644 index 000000000..e663c99ba --- /dev/null +++ b/Server/src/main/content/region/kandarin/dialogue/GiannesCookBook.kt @@ -0,0 +1,338 @@ +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.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 GiannesCookBook : InteractionListener { + companion object { + private val TITLE = "Gianne's cook book" + private val CONTENTS = arrayOf( + PageSet( + Page( + BookLine("Chocolate Bomb", 55), + BookLine("", 56), + BookLine("Knead a ball of Gianne", 57), + BookLine("dough into a gnomebowl mould.", 58), + BookLine("Bake this briefly. Decadently", 59), + BookLine("add four bars of chocolate", 60), + BookLine("to the bowl and top with", 61), + BookLine("one sprig of equa leaves.", 62), + BookLine("Bake the bowl in the oven", 63), + BookLine("to melt the chocolate.", 64), + BookLine("Then mix in two big dollops", 65), + ), + Page( + BookLine("of cream and finally sprinkle", 66), + BookLine("chocolate dust all over.", 67), + BookLine("Chocolate is a relatively", 68), + BookLine("recent cooking ingredient", 69), + BookLine("for gnomes, having been", 70), + BookLine("imported from human lands.", 71), + ) + ), + PageSet( + Page( + BookLine("Tangled Toad's Legs", 55), + BookLine("", 56), + BookLine("Shape a portion of fresh Gianne", 57), + BookLine("dough into a gnomebowl mould.", 58), + BookLine("Bake this until it is", 59), + BookLine("slightly springy.", 60), + BookLine("Add to the bowl...", 61), + BookLine("", 62), + BookLine("4 pairs of toad's legs", 63), + BookLine("2 portions of cheese", 64), + BookLine("2 sprigs of equa leaves", 65), + ), + Page( + BookLine("2 dashes of gnome spice", 66), + BookLine("1 bunch of dwellberries.", 67), + BookLine("", 68), + BookLine("Bake the dish in the oven", 69), + BookLine("once more prior to serving.", 70), + BookLine("Tangled Toads Legs was", 71), + BookLine("a special dish created", 72), + BookLine("by gnome chef Deelie to", 73), + BookLine("celebrate the first ", 74), + BookLine("Healthorg the Great Day.", 75), + ) + ), + PageSet( + Page( + BookLine("Worm Hole", 55), + BookLine("", 56), + BookLine("Starting with a gnomebowl", 57), + BookLine("mould, shape a portion of", 58), + BookLine("fresh Gianne dough into a", 59), + BookLine("rough bowl. Bake this", 60), + BookLine("until it is firm to the", 61), + BookLine("touch. Add to the bowl", 62), + BookLine("four king worms, two onions", 63), + BookLine("and a dash of gnome spices.", 64), + BookLine("Bake the bowl in the", 65), + ), + Page( + BookLine("oven once more.", 66), + BookLine("To finish the dish simply", 67), + BookLine("add a topping of equa leaves.", 68), + BookLine("Worms are specially flavoured", 69), + BookLine("by gnomes as they", 70), + BookLine("purportedly add virility.", 71), + ) + ), + PageSet( + Page( + BookLine("Veg Ball", 55), + BookLine("", 56), + BookLine("As with all gnomebowl dishes,", 57), + BookLine("throw a ball of fresh Gianne", 58), + BookLine("dough into a mould. Bake this", 59), + BookLine("as usual. Bake this briefly.", 60), + BookLine("Add to the bowl two onions,", 61), + BookLine("two potatoes and a dash", 62), + BookLine("of gnome spices. Bake the bowl", 63), + BookLine("in the oven once more.", 64), + BookLine("To finish simply top with equa", 65), + ), + Page( + BookLine("leaves. Vegetable dishes are", 66), + BookLine("seen as luxurious food, since", 67), + BookLine("for most of gnome history", 68), + BookLine("growing vegetables was harder", 69), + BookLine("than finding toads and worms.", 70), + ) + ), + PageSet( + Page( + BookLine("Worm Crunchies", 55), + BookLine("", 56), + BookLine("Using a crunchy tray,", 57), + BookLine("form a portion of Gianne", 58), + BookLine("dough into small evenly", 59), + BookLine("sized balls. Heat these briefly", 60), + BookLine("in an oven. Mix into the", 61), + BookLine("dough balls two king worms,", 62), + BookLine("one sprig of equa leaves", 63), + BookLine("and a shake of gnome spices.", 64), + BookLine("Bake the crunchies for a", 65), + ), + Page( + BookLine("short time in the oven.", 66), + BookLine("Sprinkle generously with", 67), + BookLine("gnome spices to finish.", 68), + BookLine("Crunchies were invented", 69), + BookLine("accidentally by Pukkamay,", 70), + BookLine("who was Dellie's assistant", 71), + BookLine("before his sacking. He started", 72), + BookLine("a successful crunch making", 73), + BookLine("business before dying in a", 74), + BookLine("bizarre Terrorbird accident.", 75), + ) + ), + PageSet( + Page( + BookLine("Choc Chip Crunchies", 55), + BookLine("", 56), + BookLine("Fill up a crunchy tray with", 57), + BookLine("balls of Gianne dough. Heat", 58), + BookLine("these briefly in a warm oven.", 59), + BookLine("Break up two bars of chocolate", 60), + BookLine("and mix these with the balls", 61), + BookLine("of dough. Next add a little", 62), + BookLine("gnome spice to each ball.", 63), + BookLine("Bake the crunchies for a", 64), + BookLine("short time in the oven.", 65), + ), + Page( + BookLine("Top the crunchies with a", 66), + BookLine("sprinkling of chocolate", 67), + BookLine("dust to finish.", 68), + BookLine("(A large chocolate stain", 69), + BookLine("covers the rest of the page.)", 70), + ) + ), + PageSet( + Page( + BookLine("Spicy Crunchies", 55), + BookLine("", 56), + BookLine("Using a crunchy tray,", 57), + BookLine("form a portion of Gianne", 58), + BookLine("dough into small evenly", 59), + BookLine("sized balls. Heat these briefly", 60), + BookLine("in a warm oven.", 61), + BookLine("Add a generous shake", 62), + BookLine("of spice and two sprigs of", 63), + BookLine("equa leaves to the dough balls.", 64), + BookLine("Bake the crunchies for a", 65), + ), + Page( + BookLine("short time in the oven.", 66), + BookLine("Sprinkle a load more gnome", 67), + BookLine("spice over the cookies to finish.", 68), + BookLine("", 69), + BookLine("The special mix of herbs", 70), + BookLine("and spices in gnome spice", 71), + BookLine("is a closely guarded secret.", 72), + BookLine("It is rumoured to contain", 73), + BookLine("(the rest is scribbled out)", 74), + ) + ), + PageSet( + Page( + BookLine("Toad Crunchies", 55), + BookLine("", 56), + BookLine("Fill a crunchy tray with", 57), + BookLine("Gianne dough as normal.", 58), + BookLine("Heat these briefly in a", 59), + BookLine("warm oven. Mix into", 60), + BookLine("the dough balls two pairs", 61), + BookLine("of toad's legs and a", 62), + BookLine("shake of gnome spices.", 63), + BookLine("Bake the crunchies for a short", 64), + BookLine("time in the oven.", 65), + ), + Page( + BookLine("Finish the crunchies with", 66), + BookLine("a sprinkling of equa leaves.", 67), + BookLine("", 68), + BookLine("When Pukkamay first made", 69), + BookLine("toad crunchies, everyone", 70), + BookLine("thought he was mad.", 71), + BookLine("'Chewy toads, in crunchies?", 72), + BookLine("It'll never work' they said", 73), + BookLine("- how wrong they were...", 74), + ) + ), + PageSet( + Page( + BookLine("Worm Batta", 55), + BookLine("", 56), + BookLine("First take some fresh", 57), + BookLine("Gianne dough and place it", 58), + BookLine("in a batta tin. Bake the", 59), + BookLine("dough until it is lightly", 60), + BookLine("browned. Take one king worm,", 61), + BookLine("some gnome spice and", 62), + BookLine("a little cheese. Add these", 63), + BookLine("to the batta before briefly", 64), + BookLine("baking it in the oven once more.", 65), + ), + Page( + BookLine("Finish the batta with a", 66), + BookLine("topping of equa leaves.", 67), + BookLine("Battas are usually cooked by", 68), + BookLine("gnome mother during the", 69), + BookLine("cold winter months.", 70), + ) + ), + PageSet( + Page( + BookLine("Toad's Legs Batta", 55), + BookLine("", 56), + BookLine("Mould some Gianne dough", 57), + BookLine("into a batta tin.", 58), + BookLine("Bake the tin until it", 59), + BookLine("is almost cooked. Next add", 60), + BookLine("some prime toad's legs,", 61), + BookLine("a sprig of equa leaves", 62), + BookLine("and some spice along with", 63), + BookLine("some cheese to the batta.", 64), + BookLine("Bake the batta in the oven", 65), + ), + Page( + BookLine("once more and serve hot.", 66), + BookLine("Toads legs battas are", 67), + BookLine("sometimes called Toad in", 68), + BookLine("the Hole. Apparently there", 69), + BookLine("is a similar human dish", 70), + BookLine("that uses sausages. How odd.", 71), + ) + ), + PageSet( + Page( + BookLine("Cheese & Tomato Batta", 55), + BookLine("", 56), + BookLine("Place some Gianne dough in a", 57), + BookLine("batta tin. Bake it as normal.", 58), + BookLine("Top the plain batta with equal", 59), + BookLine("quantities of cheese and tomato.", 60), + BookLine("Place the batta in the oven", 61), + BookLine("once more until all the cheese", 62), + BookLine("has melted. Finish the dish with", 63), + BookLine("a sprinkling of equa leaves.", 64), + BookLine("The combination of cheese", 65), + ), + Page( + BookLine("and tomato was discovered", 66), + BookLine("by the explorer Wingstone", 67), + BookLine("while was visiting the human", 68), + BookLine("lands. Apparently it's used", 69), + BookLine("a lot in a strange flat", 70), + BookLine("human dish called a pizza.", 71), + ) + ), + PageSet( + Page( + BookLine("Fruit Batta", 55), + BookLine("", 56), + BookLine("Prepare Gianne dough in", 57), + BookLine("a batta tin as normal. Bake", 58), + BookLine("the dough for a short while.", 59), + BookLine("Top the batta with chunks of", 60), + BookLine("pineapple, orange and lime.", 61), + BookLine("Lay four sprigs of", 62), + BookLine("equa leaves on top of the", 63), + BookLine("batta before baking it in", 64), + BookLine("the oven once more. Finish", 65), + ), + Page( + BookLine("the batta with a sprinkling", 66), + BookLine("of gnome spices. Battas are", 67), + BookLine("normally savoury dish,", 68), + BookLine("and the fruit batta is definitely", 69), + BookLine("an acquired taste.", 70), + ) + ), + PageSet( + Page( + BookLine("Vegetable Batta", 55), + BookLine("", 56), + BookLine("Place some Gianne dough in a", 57), + BookLine("batta tin and bake as normal.", 58), + BookLine("Add to the plain batta two", 59), + BookLine("tomatoes, one onion, one", 60), + BookLine("cabbage and some dwellberries.", 61), + BookLine("Top the batta with cheese and", 62), + BookLine("briefly place it in the oven", 63), + BookLine("once more. Finish the dish with", 64), + BookLine("with a sprinkling of equa leaves.", 65), + ), + Page( + BookLine("There's no better batta", 66), + BookLine("than a vegetable batta.", 67), + ) + ), + ) + } + + 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.GIANNES_COOK_BOOK_2167, 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/dialogue/OttoGodblessedDialogue.kt b/Server/src/main/content/region/kandarin/dialogue/OttoGodblessedDialogue.kt index aa20a9618..dd0865501 100644 --- a/Server/src/main/content/region/kandarin/dialogue/OttoGodblessedDialogue.kt +++ b/Server/src/main/content/region/kandarin/dialogue/OttoGodblessedDialogue.kt @@ -15,9 +15,9 @@ import core.tools.START_DIALOGUE * @author vddCore */ @Initializable -class OttoGodblessedDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { +class OttoGodblessedDialogue(player: Player? = null) : DialoguePlugin(player) { - override fun newInstance(player: Player): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player): DialoguePlugin { return OttoGodblessedDialogue(player) } @@ -25,7 +25,7 @@ class OttoGodblessedDialogue(player: Player? = null) : core.game.dialogue.Dialog npc = args[0] as NPC npcl( - core.game.dialogue.FacialExpression.HAPPY, + FacialExpression.HAPPY, "Good day, you seem a hearty warrior. " + "Maybe even some barbarian blood in that body of yours?" ) @@ -36,51 +36,51 @@ class OttoGodblessedDialogue(player: Player? = null) : core.game.dialogue.Dialog override fun handle(interfaceId: Int, buttonId: Int): Boolean { when (stage) { START_DIALOGUE -> showTopics( - Topic(core.game.dialogue.FacialExpression.ASKING, "Is there anything you can teach me?", 1), - Topic(core.game.dialogue.FacialExpression.NEUTRAL, "Nevermind.", END_DIALOGUE) + Topic(FacialExpression.ASKING, "Is there anything you can teach me?", 1), + Topic(FacialExpression.NEUTRAL, "Nevermind.", END_DIALOGUE) ) 1 -> npcl( - core.game.dialogue.FacialExpression.FRIENDLY, + FacialExpression.FRIENDLY, "I can teach you how to fish." ).also { stage++ } 2 -> playerl( - core.game.dialogue.FacialExpression.HALF_ROLLING_EYES, + FacialExpression.HALF_ROLLING_EYES, "Oh, that's pretty underwhelming. But uhhh, okay!" ).also { stage++ } 3 -> npcl( - core.game.dialogue.FacialExpression.FRIENDLY, + FacialExpression.FRIENDLY, "Alright, so here's what you gotta do: you need to grab a pole and some bait, and then... " + "Fling it into the water!" ).also { stage++ } 4 -> playerl( - core.game.dialogue.FacialExpression.HALF_ASKING, + FacialExpression.HALF_ASKING, "The whole... Pole?" ).also { stage++ } 5 -> npcl( - core.game.dialogue.FacialExpression.ANNOYED, + FacialExpression.ANNOYED, "No, not the whole pole!" ).also { stage++ } 6 -> npcl( - core.game.dialogue.FacialExpression.ANNOYED, + FacialExpression.ANNOYED, "Look. Just... Grab the pole from under my bed and go click on that fishing spot." ).also { stage++ } 7 -> playerl( - core.game.dialogue.FacialExpression.STRUGGLE, + FacialExpression.STRUGGLE, "...click?" ).also { stage++ } 8 -> npcl( - core.game.dialogue.FacialExpression.FURIOUS, + FacialExpression.FURIOUS, "JUST GO DO IT!" ).also { - stage = END_DIALOGUE; + stage = END_DIALOGUE player.setAttribute("/save:barbtraining:fishing", true) } } diff --git a/Server/src/main/content/region/kandarin/dialogue/ShamansTomeBook.kt b/Server/src/main/content/region/kandarin/dialogue/ShamansTomeBook.kt new file mode 100644 index 000000000..9559fa9fa --- /dev/null +++ b/Server/src/main/content/region/kandarin/dialogue/ShamansTomeBook.kt @@ -0,0 +1,55 @@ +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.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 ShamansTomeBook : InteractionListener { + companion object { + private val TITLE = "Shaman's Tome" + private val CONTENTS = arrayOf( + PageSet( + Page( + BookLine("You read the ancient", 55), + BookLine("shaman's tome.", 56), + BookLine("It is written in a strange sort", 57), + BookLine("of language but you manage", 58), + BookLine("a rough translation.", 59), + BookLine("", 60), + BookLine("...scattered are my hopes that", 61), + BookLine("I will ever be released from", 62), + BookLine("this flaming Octagram, it is", 63), + BookLine("the only thing which will", 64), + BookLine("contain this beast within.", 65), + ), + Page( + BookLine("Although its grip over me is", 66), + BookLine("weakened with magic, it is", 67), + BookLine("hopeless to know if a", 68), + BookLine("saviour would guess this.", 69), + BookLine("I am doomed...", 70), + ) + ), + ) + } + + 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.SHAMANS_TOME_729, 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/dialogue/ShopKeeperDialogue.kt b/Server/src/main/content/region/kandarin/dialogue/ShopKeeperDialogue.kt new file mode 100644 index 000000000..cc0713e5f --- /dev/null +++ b/Server/src/main/content/region/kandarin/dialogue/ShopKeeperDialogue.kt @@ -0,0 +1,55 @@ +package content.region.kandarin.dialogue + +import core.api.openNpcShop +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 + +@Initializable +class ShopKeeperDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun newInstance(player: Player?): DialoguePlugin { + return ShopKeeperDialogue(player) + } + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + npcl( + FacialExpression.HAPPY, "Can I help you at all?" + ).also { stage = START_DIALOGUE } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage) { + 0 -> options( + "Yes, please. I'd like to see your stock.", "No thanks, I must be going now." + ).also { stage++ } + + 1 -> when (buttonId) { + 1 -> playerl( + FacialExpression.FRIENDLY, "Yes, please. I'd like to see your stock." + ).also { stage++ } + + 2 -> playerl( + FacialExpression.FRIENDLY, "No thanks, I must be going now." + ).also { stage = END_DIALOGUE } + } + + 2 -> { + openNpcShop(player, NPCs.SHOPKEEPER_555) + end() + } + } + return true + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.SHOPKEEPER_555) + } +} \ 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..f21493f59 --- /dev/null +++ b/Server/src/main/content/region/kandarin/dialogue/ThormacDialogue.kt @@ -0,0 +1,59 @@ +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.Items +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 -> { + val cost = if (player.equipment.contains(Items.SEERS_HEADBAND_14631, 1)) 27 else 40 + npcl(FacialExpression.HAPPY, "Yes, it'll cost you $cost,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/gutanoth/handlers/BogrogPlugin.java b/Server/src/main/content/region/kandarin/feldip/gutanoth/handlers/BogrogPlugin.java index 691ee67f3..c66f8e3ae 100644 --- a/Server/src/main/content/region/kandarin/feldip/gutanoth/handlers/BogrogPlugin.java +++ b/Server/src/main/content/region/kandarin/feldip/gutanoth/handlers/BogrogPlugin.java @@ -12,6 +12,9 @@ import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.plugin.Plugin; import core.plugin.ClassScanner; +import kotlin.Unit; + +import static core.api.ContentAPIKt.*; /** * Handles the bogrog npc. @@ -46,8 +49,10 @@ public final class BogrogPlugin extends OptionHandler { player.sendMessage("You need a Summoning level of at least 21 in order to do that."); return; } else { - InterfaceContainer.generateItems(player,player.getInventory().toArray(),new String[] {"Swap X","Swap 10","Swap 5","Swap 1","Value"}, 644,0,7,4,200); - player.getInterfaceManager().openSingleTab(new Component(644)); + sendItemSelect(player, new String[] { "Value", "Swap 1", "Swap 5", "Swap 10", "Swap X" }, true, (slot, index) -> { + BogrogPouchSwapper.handle(player, index, slot); + return Unit.INSTANCE; + }); } } diff --git a/Server/src/main/content/region/kandarin/feldip/gutanoth/handlers/BogrogPouchSwapper.kt b/Server/src/main/content/region/kandarin/feldip/gutanoth/handlers/BogrogPouchSwapper.kt index 8af239407..90dbfbea5 100644 --- a/Server/src/main/content/region/kandarin/feldip/gutanoth/handlers/BogrogPouchSwapper.kt +++ b/Server/src/main/content/region/kandarin/feldip/gutanoth/handlers/BogrogPouchSwapper.kt @@ -7,6 +7,7 @@ import content.global.skill.summoning.SummoningPouch import content.global.skill.summoning.SummoningScroll import core.game.node.item.Item import core.game.world.map.zone.ZoneBorders +import kotlin.math.ceil import kotlin.math.floor /** @@ -17,11 +18,11 @@ import kotlin.math.floor */ object BogrogPouchSwapper { //Opcodes for item options - private const val OP_VALUE = 155 - private const val OP_SWAP_1 = 196 - private const val OP_SWAP_5 = 124 - private const val OP_SWAP_10 = 199 - private const val OP_SWAP_X = 234 + private const val OP_VALUE = 0 + private const val OP_SWAP_1 = 1 + private const val OP_SWAP_5 = 2 + private const val OP_SWAP_10 = 3 + private const val OP_SWAP_X = 4 private const val SPIRIT_SHARD = 12183 @@ -29,16 +30,16 @@ object BogrogPouchSwapper { private val GEBorders = ZoneBorders(3151,3501,3175,3477) @JvmStatic - fun handle(player: Player, opcode: Int, slot: Int, itemID: Int): Boolean{ - if(GEBorders.insideBorder(player)) return false - return when(opcode){ - OP_VALUE -> sendValue(player.inventory.get(slot).id,player) - OP_SWAP_1 -> swap(player, 1, player.inventory.get(slot).id) - OP_SWAP_5 -> swap(player, 5, player.inventory.get(slot).id) - OP_SWAP_10 -> swap(player,10,player.inventory.get(slot).id) + fun handle(player: Player, optionIndex: Int, slot: Int): Boolean{ + val item = player.inventory.get(slot) ?: return false + return when(optionIndex){ + OP_VALUE -> sendValue(item.id,player) + OP_SWAP_1 -> swap(player, 1, item.id) + OP_SWAP_5 -> swap(player, 5, item.id) + OP_SWAP_10 -> swap(player, 10, item.id) OP_SWAP_X -> true.also{ - sendInputDialogue(player, true, "Enter the amount:"){value -> - swap(player, value as Int, player.inventory.get(slot).id) + sendInputDialogue(player, InputType.AMOUNT, "Enter the amount:"){value -> + swap(player, value as Int, item.id) } } else -> false @@ -56,7 +57,6 @@ object BogrogPouchSwapper { amt = inInventory player.inventory.remove(Item(itemID,amt)) player.inventory.add(Item(SPIRIT_SHARD, floor(value * amt).toInt())) - player.interfaceManager.close(Component(644)) return true } @@ -66,7 +66,7 @@ object BogrogPouchSwapper { return false } - player.sendMessage("Bogrog will give you $value shards for that.") + player.sendMessage("Bogrog will give you ${floor(value).toInt()} shards for that.") return true } @@ -77,7 +77,6 @@ object BogrogPouchSwapper { if(item == null) item = SummoningPouch.get(SummoningScroll.forItemId(itemID)?.pouch ?: -1).also { isScroll = true } item ?: return 0.0 var shardQuantity = item.items[item.items.size - 1].amount * 0.7 - if(isScroll) shardQuantity /= 20.0 - return shardQuantity + return if(isScroll) shardQuantity / 20.0 else ceil(shardQuantity) } -} \ 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 4fb34c407..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,41 +10,41 @@ 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 */ @Initializable -class BalneaDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { +class BalneaDialogue(player: Player? = null) : DialoguePlugin(player) { override fun handle(interfaceId: Int, buttonId: Int): Boolean { when (stage) { START_DIALOGUE -> playerl( - core.game.dialogue.FacialExpression.FRIENDLY, + FacialExpression.FRIENDLY, "Hi there!" ).also { stage++ } 1 -> npcl( - core.game.dialogue.FacialExpression.NEUTRAL, + FacialExpression.NEUTRAL, "I'm ever so busy at the moment; please come back after the grand opening." ).also { stage++ } 2 -> playerl( - core.game.dialogue.FacialExpression.HALF_ASKING, + FacialExpression.HALF_ASKING, "What grand reopening?" ).also { stage++ } 3 -> npcl( - core.game.dialogue.FacialExpression.ANNOYED, + FacialExpression.ANNOYED, "I'm sorry, I really can't spare the time to talk to you." ).also { stage++ } 4 -> playerl( - core.game.dialogue.FacialExpression.THINKING, + FacialExpression.THINKING, "Uh, sure." ).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/ooglog/dialogue/OgressBankerDialogue.kt b/Server/src/main/content/region/kandarin/feldip/ooglog/dialogue/OgressBankerDialogue.kt index 21b5b8d7b..045e01862 100644 --- a/Server/src/main/content/region/kandarin/feldip/ooglog/dialogue/OgressBankerDialogue.kt +++ b/Server/src/main/content/region/kandarin/feldip/ooglog/dialogue/OgressBankerDialogue.kt @@ -15,7 +15,7 @@ import core.tools.START_DIALOGUE * @author vddCore */ @Initializable -class OgressBankerDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { +class OgressBankerDialogue(player: Player? = null) : DialoguePlugin(player) { override fun getIds(): IntArray = intArrayOf( NPCs.OGRESS_BANKER_7049, NPCs.OGRESS_BANKER_7050 @@ -24,17 +24,17 @@ class OgressBankerDialogue(player: Player? = null) : core.game.dialogue.Dialogue override fun handle(interfaceId: Int, buttonId: Int): Boolean { when (stage) { START_DIALOGUE -> npcl( - core.game.dialogue.FacialExpression.ANNOYED, + FacialExpression.ANNOYED, "..." ).also { stage++ } 1 -> playerl( - core.game.dialogue.FacialExpression.ANNOYED, + FacialExpression.ANNOYED, "Excuse me, can I get some service here, please?" ).also { stage++ } 2 -> npcl( - core.game.dialogue.FacialExpression.ANGRY, + FacialExpression.ANGRY, "GRAAAAAH! You go away, human! Me too busy with training to talk to puny thing like you." ).also { stage++ } @@ -42,7 +42,7 @@ class OgressBankerDialogue(player: Player? = null) : core.game.dialogue.Dialogue player, NPCs.BALNEA_7047, "I do apologise, sir. We're temporarily unable to meet your banking needs.", - core.game.dialogue.FacialExpression.NEUTRAL, + FacialExpression.NEUTRAL, ).also { stage++ } 4 -> sendNPCDialogue( @@ -51,11 +51,11 @@ class OgressBankerDialogue(player: Player? = null) : core.game.dialogue.Dialogue "We'll be open as soon as we realize our customer experience goals " + "and can guarantee the high standards of service that you expect from all " + "branches of the Bank of Gielinor.", - core.game.dialogue.FacialExpression.NEUTRAL + FacialExpression.NEUTRAL ).also { stage++ } 5 -> playerl( - core.game.dialogue.FacialExpression.THINKING, + FacialExpression.THINKING, "What did you just say to me?" ).also { stage++ } @@ -63,11 +63,11 @@ class OgressBankerDialogue(player: Player? = null) : core.game.dialogue.Dialogue player, NPCs.BALNEA_7047, "We're closed until I can teach these wretched creatures some manners.", - core.game.dialogue.FacialExpression.ANNOYED + FacialExpression.ANNOYED ).also { stage++ } 7 -> playerl( - core.game.dialogue.FacialExpression.NEUTRAL, + FacialExpression.NEUTRAL, "Ah, right. Good luck with that." ).also { stage = END_DIALOGUE } } 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 8b48ff43e..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) @@ -103,7 +104,7 @@ class ChompyBird : Quest("Big Chompy Bird Hunting", 35, 34, 2, Vars.VARP_QUEST_C line(player, "- ${if(getAttribute(player, ATTR_FYCIE_ASKED, false)) "Fycie wants: !!${getItemName(getAttribute(player, ATTR_ING_FYCIE, -1))}??" else "I still need to ask !!Fycie??."}", ln++, false) } else if (stage > 70) { line(player, "I seasoned and cooked the chompy bird for Rantz and his kids.", ln++, true) - line(player, "!!QUEST COMPLETE!??", ln++, false) + line(player, "%%QUEST COMPLETE!&&", ln++, false) } } } @@ -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 e54cf011a..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,21 +18,22 @@ 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) : core.game.dialogue.DialoguePlugin(player) { +class RantzDialogue(player: Player? = null) : DialoguePlugin(player) { override fun getIds() : IntArray { return intArrayOf(NPCs.RANTZ_1010) } - override fun newInstance(player: Player?) : core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?) : DialoguePlugin { return RantzDialogue(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) @@ -54,37 +55,37 @@ class RantzDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin( override fun handle(componentId: Int, buttonId: Int) : Boolean { when(stage) { - 0 -> npcl(core.game.dialogue.FacialExpression.OLD_NORMAL, "Creature done good, cooking da chompy. Maybe you wants a free hatsie? Rantz got lots of hatsies for chompy shooters.").also { stage++ } + 0 -> npcl(FacialExpression.OLD_NORMAL, "Creature done good, cooking da chompy. Maybe you wants a free hatsie? Rantz got lots of hatsies for chompy shooters.").also { stage++ } 1 -> options("What are these 'hatsies'?", "Okay, show me your 'hatsies'.", "No thanks.").also { stage++ } 2 -> when(buttonId) { - 1 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY, "What are these 'hatsies'?").also { stage = 3 } - 2 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY, "Okay, show me your 'hatsies'.").also { stage = 100 } - 3 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY, "No thanks.").also { stage = END_DIALOGUE } + 1 -> playerl(FacialExpression.FRIENDLY, "What are these 'hatsies'?").also { stage = 3 } + 2 -> playerl(FacialExpression.FRIENDLY, "Okay, show me your 'hatsies'.").also { stage = 100 } + 3 -> playerl(FacialExpression.FRIENDLY, "No thanks.").also { stage = END_DIALOGUE } } - 3 -> npcl(core.game.dialogue.FacialExpression.OLD_NORMAL, "Creature stupid? Hatsies to wear on head, make you look good. Huh huh huh.").also { stage++ } - 4 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY, "Ah, I see, you're offering me hats.").also { stage++ } - 5 -> npcl(core.game.dialogue.FacialExpression.OLD_NORMAL, "Dat's what Rantz said. You want hatsies or not?").also { stage = 1 } + 3 -> npcl(FacialExpression.OLD_NORMAL, "Creature stupid? Hatsies to wear on head, make you look good. Huh huh huh.").also { stage++ } + 4 -> playerl(FacialExpression.FRIENDLY, "Ah, I see, you're offering me hats.").also { stage++ } + 5 -> npcl(FacialExpression.OLD_NORMAL, "Dat's what Rantz said. You want hatsies or not?").also { stage = 1 } 100 -> { val hasBow = inInventory(player, Items.OGRE_BOW_2883) || inEquipment(player, Items.OGRE_BOW_2883) if (!hasBow) { - npcl(core.game.dialogue.FacialExpression.OLD_NORMAL, "Stupid creature. Me need bow to see how many chompy creature kill. Bring bow.") + npcl(FacialExpression.OLD_NORMAL, "Stupid creature. Me need bow to see how many chompy creature kill. Bring bow.") stage = 200 return true } val hats = ChompyHat.getApplicableHats(player) if (hats.isEmpty()) { - npcl(core.game.dialogue.FacialExpression.OLD_NORMAL, "Sorry, creature, no hatsies for you. Come back when kill more chompy.") + npcl(FacialExpression.OLD_NORMAL, "Sorry, creature, no hatsies for you. Come back when kill more chompy.") } else { val spaces = freeSlots(player) if (spaces < hats.size) { for (i in 0 until spaces) addItem(player, hats[i]) - npcl(core.game.dialogue.FacialExpression.OLD_NORMAL, "There all hats you can fit, creature. Come back when have more room for hatsies.") + npcl(FacialExpression.OLD_NORMAL, "There all hats you can fit, creature. Come back when have more room for hatsies.") } else { for (hat in hats) addItem(player, hat) - npcl(core.game.dialogue.FacialExpression.OLD_NORMAL, "There all hats, creature. Come back when kill more chompy. Unless you kill all chompy already.") + npcl(FacialExpression.OLD_NORMAL, "There all hats, creature. Come back when kill more chompy. Unless you kill all chompy already.") } } stage = END_DIALOGUE @@ -95,15 +96,15 @@ class RantzDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin( Topic("Okay.", END_DIALOGUE) ) - 201 -> npcl(core.game.dialogue.FacialExpression.OLD_NORMAL, "Yes, creature?").also { stage++ } - 202 -> playerl(core.game.dialogue.FacialExpression.HALF_GUILTY, "I lost my bow.").also { stage++ } - 203 -> npcl(core.game.dialogue.FacialExpression.OLD_NORMAL, "Haha! Stupid creature! That okay, me can sell new one.").also { stage++ } - 204 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY, "Really? How much?").also { stage++ } - 205 -> npcl(core.game.dialogue.FacialExpression.OLD_NORMAL, "Hmm.. let me think...").also { stage++ } + 201 -> npcl(FacialExpression.OLD_NORMAL, "Yes, creature?").also { stage++ } + 202 -> playerl(FacialExpression.HALF_GUILTY, "I lost my bow.").also { stage++ } + 203 -> npcl(FacialExpression.OLD_NORMAL, "Haha! Stupid creature! That okay, me can sell new one.").also { stage++ } + 204 -> playerl(FacialExpression.FRIENDLY, "Really? How much?").also { stage++ } + 205 -> npcl(FacialExpression.OLD_NORMAL, "Hmm.. let me think...").also { stage++ } 206 -> { val cost = RandomFunction.random(500,550) setAttribute(player, "/save:chompybird:new-bow-cost", cost) - npcl(core.game.dialogue.FacialExpression.OLD_NORMAL, "Me think $cost shiny coins be ok.") + npcl(FacialExpression.OLD_NORMAL, "Me think $cost shiny coins be ok.") stage++ } @@ -118,9 +119,9 @@ class RantzDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin( 208 -> { if (removeItem(player, Item(Items.COINS_995, getAttribute(player, "chompybird:new-bow-cost", 500)))) { addItemOrDrop(player, Items.OGRE_BOW_2883) - npcl(core.game.dialogue.FacialExpression.OLD_NORMAL, "There you go, creature, now hunt chompy.") + npcl(FacialExpression.OLD_NORMAL, "There you go, creature, now hunt chompy.") } else { - playerl(core.game.dialogue.FacialExpression.FRIENDLY, "Actually, I can't afford it right now.") + playerl(FacialExpression.FRIENDLY, "Actually, I can't afford it right now.") } stage = END_DIALOGUE } @@ -130,19 +131,19 @@ class RantzDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin( } @Initializable -class BugsDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { +class BugsDialogue(player: Player? = null) : DialoguePlugin(player) { override fun getIds() : IntArray { return intArrayOf(NPCs.BUGS_1012) } - override fun newInstance(player: Player?) : core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?) : DialoguePlugin { return BugsDialogue(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) { @@ -160,19 +161,19 @@ class BugsDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(p } @Initializable -class FycieDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { +class FycieDialogue(player: Player? = null) : DialoguePlugin(player) { override fun getIds() : IntArray { return intArrayOf(NPCs.FYCIE_1011) } - override fun newInstance(player: Player?) : core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?) : DialoguePlugin { return FycieDialogue(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) { @@ -192,7 +193,7 @@ class FycieDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin( class BugsChompyBirdDialogue(val quest: Quest) : DialogueFile() { override fun handle(componentId: Int, buttonId: Int) { when (quest.getStage(player)) { - in 0 until 20 -> npcl(core.game.dialogue.FacialExpression.OLD_NORMAL, "You's better talk to Dad, him chasey sneaky da chompy.").also { stage = END_DIALOGUE } + in 0 until 20 -> npcl(FacialExpression.OLD_NORMAL, "You's better talk to Dad, him chasey sneaky da chompy.").also { stage = END_DIALOGUE } in 20 until 70 -> handleBellowsDialogue(player, buttonId) in 70 until 90 -> handleIngredientDialogue(player, buttonId) } @@ -201,14 +202,14 @@ class BugsChompyBirdDialogue(val quest: Quest) : DialogueFile() { private fun handleBellowsDialogue(player: Player?, buttonId: Int) { when(stage) { 0 -> playerl("Rantz said that you play with the fatsy toadies, what are they?").also { stage++ } - 1 -> npcl(core.game.dialogue.FacialExpression.OLD_NORMAL, "Oh, we sometimes use da blower on da toadies but Dad don't let us get in da locked box no more. He he it was good fun making da toadies fat on da swamp gas.").also { stage = END_DIALOGUE } + 1 -> npcl(FacialExpression.OLD_NORMAL, "Oh, we sometimes use da blower on da toadies but Dad don't let us get in da locked box no more. He he it was good fun making da toadies fat on da swamp gas.").also { stage = END_DIALOGUE } } } private fun handleIngredientDialogue(player: Player?, buttonId: Int) { val bugsIngredient = getAttribute(player!!, ChompyBird.ATTR_ING_BUGS, -1) when(stage) { - 0 -> npcl(core.game.dialogue.FacialExpression.OLD_NORMAL, "Dad say's you's making da chompy for us! Slurp! Me's has to have ${getItemName(bugsIngredient)} wiv mine! Chompy is our favourite yummms!").also { stage = END_DIALOGUE } + 0 -> npcl(FacialExpression.OLD_NORMAL, "Dad say's you's making da chompy for us! Slurp! Me's has to have ${getItemName(bugsIngredient)} wiv mine! Chompy is our favourite yummms!").also { stage = END_DIALOGUE } } setAttribute(player!!, ChompyBird.ATTR_BUGS_ASKED, true) } @@ -217,7 +218,7 @@ class BugsChompyBirdDialogue(val quest: Quest) : DialogueFile() { class FycieChompyBirdDialogue(val quest: Quest) : DialogueFile() { override fun handle(componentId: Int, buttonId: Int) { when (quest.getStage(player)) { - in 0 until 70 -> npcl(core.game.dialogue.FacialExpression.OLD_NORMAL, "You's better talk to Dad, We not talk to wierdly 'umans.").also { stage = END_DIALOGUE } + in 0 until 70 -> npcl(FacialExpression.OLD_NORMAL, "You's better talk to Dad, We not talk to wierdly 'umans.").also { stage = END_DIALOGUE } in 70 until 90 -> handleIngredientDialogue(player, buttonId) } } @@ -225,7 +226,7 @@ class FycieChompyBirdDialogue(val quest: Quest) : DialogueFile() { private fun handleIngredientDialogue(player: Player?, buttonId: Int) { val fycieIngredient = getAttribute(player!!, ChompyBird.ATTR_ING_FYCIE, -1) when(stage) { - 0 -> npcl(core.game.dialogue.FacialExpression.OLD_NORMAL, "Dad say's you's roasting da chompy for us! Slurp! Me's wants ${getItemName(fycieIngredient)} wiv mine! Yummy can't wait to eats it.").also { stage = END_DIALOGUE } + 0 -> npcl(FacialExpression.OLD_NORMAL, "Dad say's you's roasting da chompy for us! Slurp! Me's wants ${getItemName(fycieIngredient)} wiv mine! Yummy can't wait to eats it.").also { stage = END_DIALOGUE } } setAttribute(player!!, ChompyBird.ATTR_FYCIE_ASKED, true) } @@ -247,22 +248,22 @@ class RantzChompyBirdDialogue(val quest: Quest) : DialogueFile() { private fun handleQuestStartDialogue(player: Player?, buttonId: Int) { when(stage) { - 0 -> npcl(core.game.dialogue.FacialExpression.OLD_NORMAL, "Hey you creature! Make some stabbers! I wanna hunt da chompy?").also { stage++ } - 1 -> options("What are 'stabbers'?", "What's a 'chompy'?", "Ok, I’ll make you some 'stabbers'.", "Er, make your own 'stabbers'!").also { stage++ } + 0 -> npcl(FacialExpression.OLD_NORMAL, "Hey you creature! Make some stabbers! I wanna hunt da chompy?").also { stage++ } + 1 -> options("What are 'stabbers'?", "What's a 'chompy'?", "Ok, I'll make you some 'stabbers'.", "Er, make your own 'stabbers'!").also { stage++ } 2 -> when(buttonId) { 1 -> playerl("What are 'stabbers'?").also { stage = 3 } 2 -> playerl("What's a 'chompy'?").also { stage = 7 } 3 -> playerl("Ok, I'll make you some 'stabbers'.").also { stage = 10 } 4 -> playerl("Er, make your own 'stabbers'!").also { stage = END_DIALOGUE } } - 3 -> npcl(core.game.dialogue.FacialExpression.OLD_NORMAL, "For da stabbie chucker, I's wanna hunt da chompy! Creature knows what Rantz wants... ...flyin' to stabbie da chompy!").also { stage++ } - 4 -> npcl(core.game.dialogue.FacialExpression.OLD_NORMAL, "The ogre shows you a huge but crude bow and then starts to nod energetically in an effort to help you understand.").also { stage++ } + 3 -> npcl(FacialExpression.OLD_NORMAL, "For da stabbie chucker, I's wanna hunt da chompy! Creature knows what Rantz wants... ...flyin' to stabbie da chompy!").also { stage++ } + 4 -> npcl(FacialExpression.OLD_NORMAL, "The ogre shows you a huge but crude bow and then starts to nod energetically in an effort to help you understand.").also { stage++ } 5 -> playerl("I think I understand. You want me to make some arrows for you?").also { stage++ } - 6 -> npcl(core.game.dialogue.FacialExpression.OLD_NORMAL, "Yeah, is what Rantz sayed, make da stabbers for da stabby chucker!").also { stage = 1 } - 7 -> npcl(core.game.dialogue.FacialExpression.OLD_NORMAL, "Da chompy is der bestest yummies for Rantz, Fycie and Bugs! We's looking for da yummies all da time. Da chompy is a big flapper, Rantz want's stabbers to sneaky, sneaky, stick da chompy.").also { stage++ } + 6 -> npcl(FacialExpression.OLD_NORMAL, "Yeah, is what Rantz sayed, make da stabbers for da stabby chucker!").also { stage = 1 } + 7 -> npcl(FacialExpression.OLD_NORMAL, "Da chompy is der bestest yummies for Rantz, Fycie and Bugs! We's looking for da yummies all da time. Da chompy is a big flapper, Rantz want's stabbers to sneaky, sneaky, stick da chompy.").also { stage++ } 8 -> playerl("Ah, so 'da chompy' is some kind of bird?").also { stage++ } - 9 -> npcl(core.game.dialogue.FacialExpression.OLD_NORMAL, "Yeah, is what Rantz sayed, Da chompy is da big flapper and is bestest yummies. But Rantz needs stabbers to stick da chompy... Will creatures make dem stabbers for us?").also {stage = 1 } - 10 -> npcl(core.game.dialogue.FacialExpression.OLD_NORMAL, "Good you creature, you need sticksies from achey tree and stabbies from dog bones.").also { stage = END_DIALOGUE; quest.start(player) } + 9 -> npcl(FacialExpression.OLD_NORMAL, "Yeah, is what Rantz sayed, Da chompy is da big flapper and is bestest yummies. But Rantz needs stabbers to stick da chompy... Will creatures make dem stabbers for us?").also {stage = 1 } + 10 -> npcl(FacialExpression.OLD_NORMAL, "Good you creature, you need sticksies from achey tree and stabbies from dog bones.").also { stage = END_DIALOGUE; quest.start(player) } } } @@ -270,27 +271,27 @@ class RantzChompyBirdDialogue(val quest: Quest) : DialogueFile() { val hasArrows = amountInInventory(player!!, Items.OGRE_ARROW_2866) > 0 when(stage) { - 0 -> npcl(core.game.dialogue.FacialExpression.OLD_NORMAL, "Hey you creature... Have you made me da stabbers? I wanna stick da chompy?").also { stage++ } + 0 -> npcl(FacialExpression.OLD_NORMAL, "Hey you creature... Have you made me da stabbers? I wanna stick da chompy?").also { stage++ } 1 -> { if (!hasArrows) { playerl("Er not exactly?").also { stage++ } } else { - playerl(core.game.dialogue.FacialExpression.ANNOYED, "Well, yes actually, as you asked so nicely. Here you go! Here's your 'stabbers'.") + playerl(FacialExpression.ANNOYED, "Well, yes actually, as you asked so nicely. Here you go! Here's your 'stabbers'.") stage = 100 } } - 2 -> npcl(core.game.dialogue.FacialExpression.OLD_NORMAL, "You do stabbers quick!...quick!.. Or Rantz make stabbers for Rantz and then practice for chompy sticking on creature!").also { stage++ } + 2 -> npcl(FacialExpression.OLD_NORMAL, "You do stabbers quick!...quick!.. Or Rantz make stabbers for Rantz and then practice for chompy sticking on creature!").also { stage++ } 3 -> options("How do I make the 'stabbers' again?", "Ok, I'll make the 'stabbers' for you.").also { stage++ } 4 -> when(buttonId) { 1 -> playerl("How do I make the 'stabbers' again?").also { stage = 5 } 2 -> playerl("Ok, I'll make the 'stabbers' for you.").also { stage = END_DIALOGUE } } - 5 -> npcl(core.game.dialogue.FacialExpression.OLD_NORMAL, "Grrr creature... you's no good stabber maker! You's make da stabbie bit from da dog bones, and get da sticksies from da Achey tree... simple see! Oh and da flufgies from da flappers as well!").also { stage++ } + 5 -> npcl(FacialExpression.OLD_NORMAL, "Grrr creature... you's no good stabber maker! You's make da stabbie bit from da dog bones, and get da sticksies from da Achey tree... simple see! Oh and da flufgies from da flappers as well!").also { stage++ } 6 -> playerl("Oh, so I need logs from the achey tree, bones from a canine... and feather?").also { stage++ } - 7 -> npcl(core.game.dialogue.FacialExpression.OLD_NORMAL, "Is just what Rantz sayed!").also { stage++ } - 8 -> npcl(core.game.dialogue.FacialExpression.OLD_NORMAL, "The hulking ogre nods excitedly.").also { stage = END_DIALOGUE } + 7 -> npcl(FacialExpression.OLD_NORMAL, "Is just what Rantz sayed!").also { stage++ } + 8 -> npcl(FacialExpression.OLD_NORMAL, "The hulking ogre nods excitedly.").also { stage = END_DIALOGUE } 100 -> { sendItemDialogue(player!!, Item(Items.OGRE_ARROW_2866, 20), "Rantz takes six ogre arrows off you.") @@ -299,7 +300,7 @@ class RantzChompyBirdDialogue(val quest: Quest) : DialogueFile() { } 101 -> { - npcl(core.game.dialogue.FacialExpression.OLD_NORMAL, "Ahh, der creature has dem...goodly, goodly. Now us can stick der chompy bird...") + npcl(FacialExpression.OLD_NORMAL, "Ahh, der creature has dem...goodly, goodly. Now us can stick der chompy bird...") stage = 4 //Skips to "How do we make the chompsys come?" in the dialogue below quest.setStage(player, 20) } @@ -310,8 +311,8 @@ class RantzChompyBirdDialogue(val quest: Quest) : DialogueFile() { val hasToads = amountInInventory(player!!, Items.BLOATED_TOAD_2875) > 0 when(stage) { - 0 -> npcl(core.game.dialogue.FacialExpression.OLD_NORMAL, "Hey you creature you still here?").also { stage++ } - 1 -> npcl(core.game.dialogue.FacialExpression.OLD_NORMAL, "Da chompy still not coming! We need da fatsy toady to get da chompy, do you got it? Do you got da fatsy toady? Then we can sneaky, sneaky stick da chompy.").also { stage++ } + 0 -> npcl(FacialExpression.OLD_NORMAL, "Hey you creature you still here?").also { stage++ } + 1 -> npcl(FacialExpression.OLD_NORMAL, "Da chompy still not coming! We need da fatsy toady to get da chompy, do you got it? Do you got da fatsy toady? Then we can sneaky, sneaky stick da chompy.").also { stage++ } 2 -> { if (hasToads) { playerl("Yes, I have a 'fatsy toady' for you, here look!") @@ -320,7 +321,7 @@ class RantzChompyBirdDialogue(val quest: Quest) : DialogueFile() { playerl("No I haven't got the 'fatsy toady' yet!").also { stage++ } } } - 3 -> npcl(core.game.dialogue.FacialExpression.OLD_NORMAL, "Dat's a pidy... but maybe Rantz can help da creature?").also { stage++ } + 3 -> npcl(FacialExpression.OLD_NORMAL, "Dat's a pidy... but maybe Rantz can help da creature?").also { stage++ } 4 -> options("How do we make the chompys come?", "What are 'fatsy toadies'?", "Where do we put the fatsy toadies?", "What do you mean 'sneaky..sneaky.. stick da chompy?'", "Ok, thanks.").also { stage++ } 5 -> when(buttonId) { 1 -> playerl("How do we make the chompys come?").also { stage = 6 } @@ -329,16 +330,16 @@ class RantzChompyBirdDialogue(val quest: Quest) : DialogueFile() { 4 -> playerl("What do you mean 'sneaky..sneaky.. stick da chompy?'").also { stage = 12 } 5 -> playerl("Ok, thanks.").also { stage = END_DIALOGUE } } - 6 -> npcl(core.game.dialogue.FacialExpression.OLD_NORMAL, "Chompys love da fatsy toadies. Toadies get big on der swamp gas and der chomys are licking der lips for em as me is licking lips for da chompy. Da chompys don't like da smaller toadies from nearby swampy.").also { stage++ } - 7 -> npcl(core.game.dialogue.FacialExpression.OLD_NORMAL, "Dey's fussie eaters like Rantz. Fycie an Bugs play with toadies and blower dey's all times making fatsy toadies.").also { stage = 4 } - 8 -> npcl(core.game.dialogue.FacialExpression.OLD_NORMAL, "Fatsy toadies are da chompy burds bestest yumms. But da toadies here are too small for da chompy. You've godda make da toadies big and round!").also { stage = 4 } - 9 -> npcl(core.game.dialogue.FacialExpression.OLD_NORMAL, "Over der!").also { stage++ } - 10 -> npcl(core.game.dialogue.FacialExpression.OLD_NORMAL, "The ogre points to a small clearing to da south.").also { stage++ } - 11 -> npcl(core.game.dialogue.FacialExpression.OLD_NORMAL, "Ok creature? You got dat? Over here by der no tree's place.").also { stage = 4 } - 12 -> npcl(core.game.dialogue.FacialExpression.OLD_NORMAL, "Duh! You creature is a bit stoopid yes? Us needs to sneaky, sneaky.. and stick da chompy! Den we can eat da chompy!").also { stage = 4 } + 6 -> npcl(FacialExpression.OLD_NORMAL, "Chompys love da fatsy toadies. Toadies get big on der swamp gas and der chomys are licking der lips for em as me is licking lips for da chompy. Da chompys don't like da smaller toadies from nearby swampy.").also { stage++ } + 7 -> npcl(FacialExpression.OLD_NORMAL, "Dey's fussie eaters like Rantz. Fycie an Bugs play with toadies and blower dey's all times making fatsy toadies.").also { stage = 4 } + 8 -> npcl(FacialExpression.OLD_NORMAL, "Fatsy toadies are da chompy burds bestest yumms. But da toadies here are too small for da chompy. You've godda make da toadies big and round!").also { stage = 4 } + 9 -> npcl(FacialExpression.OLD_NORMAL, "Over der!").also { stage++ } + 10 -> npcl(FacialExpression.OLD_NORMAL, "The ogre points to a small clearing to da south.").also { stage++ } + 11 -> npcl(FacialExpression.OLD_NORMAL, "Ok creature? You got dat? Over here by der no tree's place.").also { stage = 4 } + 12 -> npcl(FacialExpression.OLD_NORMAL, "Duh! You creature is a bit stoopid yes? Us needs to sneaky, sneaky.. and stick da chompy! Den we can eat da chompy!").also { stage = 4 } 100 -> sendItemDialogue(player!!, Items.BLOATED_TOAD_2875, "You show the bloated toad to Rantz. He nods with approval.").also { stage++ } - 101 -> npcl(core.game.dialogue.FacialExpression.OLD_NORMAL, "Dat's a good fatsy toady, now we's need to put it for da chompy to come.").also { stage++ } + 101 -> npcl(FacialExpression.OLD_NORMAL, "Dat's a good fatsy toady, now we's need to put it for da chompy to come.").also { stage++ } 102 -> { playerl("Where do I put the 'fatsy toadies'?").also { stage++ } quest.setStage(player, 30) @@ -348,7 +349,7 @@ class RantzChompyBirdDialogue(val quest: Quest) : DialogueFile() { } private fun handlePlaceToadsiesDialogue(player: Player?, buttonId: Int) { - npcl(core.game.dialogue.FacialExpression.OLD_NORMAL, "Over 'dere creature, put da toadies over der! ~ The ogre points to a clearing to the south. ~") + npcl(FacialExpression.OLD_NORMAL, "Over 'dere creature, put da toadies over der! ~ The ogre points to a clearing to the south. ~") clearHintIcon(player!!) registerHintIcon(player!!, ChompyBird.TOAD_LOCATION, 5) //TODO: ADD OFFSETS WHEN WE UNFUCK ARIOS'S MISTAKES, YEET stage = END_DIALOGUE @@ -357,9 +358,9 @@ class RantzChompyBirdDialogue(val quest: Quest) : DialogueFile() { private fun handlePlacedBaitDialogue(player: Player?, buttonId: Int) { when(stage) { 0 -> playerl("There you go, I've placed the bait.").also { stage++ } - 1 -> npcl(core.game.dialogue.FacialExpression.OLD_NORMAL, "Goodz me now waits for da chompy! It shouldn't be long now. Sneaky...sneaky...").also { stage++ } + 1 -> npcl(FacialExpression.OLD_NORMAL, "Goodz me now waits for da chompy! It shouldn't be long now. Sneaky...sneaky...").also { stage++ } 2 -> playerl("Yes, I know...stick da chompy!").also { stage++ } - 3 -> npcl(core.game.dialogue.FacialExpression.OLD_NORMAL, "Hey.. you's creature, is da fatsy toady still dere? Go get more fatsy toadies if dey all gone!").also { stage++ } + 3 -> npcl(FacialExpression.OLD_NORMAL, "Hey.. you's creature, is da fatsy toady still dere? Go get more fatsy toadies if dey all gone!").also { stage++ } 4 -> playerl("What? I have to get more bait if there's none there? Does this Chompy Bird even exist I wonder?").also { stage = END_DIALOGUE } } } @@ -367,19 +368,19 @@ class RantzChompyBirdDialogue(val quest: Quest) : DialogueFile() { private fun handleRantzSucksDialogue(player: Player?, buttonId: Int) { when(stage) { 0 -> playerl("Hey there, you keep missing the chompy bird.").also { stage++ } - 1 -> npcl(core.game.dialogue.FacialExpression.OLD_NORMAL, "I knows, I keeps missing... because your stabbers are worserer at flying than a dead dog.").also { stage++ } + 1 -> npcl(FacialExpression.OLD_NORMAL, "I knows, I keeps missing... because your stabbers are worserer at flying than a dead dog.").also { stage++ } 2 -> options("Oh, keep trying then... you might hit one through pure luck.", "Come on, let me have a go...").also { stage++ } 3 -> when(buttonId) { 1 -> playerl("Oh, keep trying then... you might hit one through pure luck.").also { stage = END_DIALOGUE } 2 -> playerl("Come on, let me have a go...").also { stage = 4 } } - 4 -> npcl(core.game.dialogue.FacialExpression.OLD_NORMAL, "No, is Rantz stabby thrower... you too weedy...").also { stage++ } + 4 -> npcl(FacialExpression.OLD_NORMAL, "No, is Rantz stabby thrower... you too weedy...").also { stage++ } 5 -> options("I'm actually quite strong... please let me try.", "Oh suit yourself, you'll just have to go hungry.").also { stage++ } 6 -> when(buttonId) { 1 -> playerl("I'm actually quite strong... please let me try.").also { stage = 7 } 2 -> playerl("Oh suit yourself, you'll just have to go hungry.").also { stage = END_DIALOGUE } } - 7 -> npcl(core.game.dialogue.FacialExpression.OLD_NORMAL, "Oh, ok... I lend you other stabby thrower... but creature don't better cry when it hurts itself.").also { stage++ } + 7 -> npcl(FacialExpression.OLD_NORMAL, "Oh, ok... I lend you other stabby thrower... but creature don't better cry when it hurts itself.").also { stage++ } 8 -> { sendItemDialogue(player!!, Items.OGRE_BOW_2883, "Rantz hands over an ogre bow. It's huge! You can barely drawn back the string!") addItemOrDrop(player!!, Items.OGRE_BOW_2883) @@ -396,7 +397,7 @@ class RantzChompyBirdDialogue(val quest: Quest) : DialogueFile() { val rantzIngredient = getAttribute(player, ChompyBird.ATTR_ING_RANTZ, -1) when(stage) { - 0 -> npcl(core.game.dialogue.FacialExpression.OLD_NORMAL, "Hey You! Got da chompy yet?").also { stage++ } + 0 -> npcl(FacialExpression.OLD_NORMAL, "Hey You! Got da chompy yet?").also { stage++ } 1 -> { if (hasChompyBird) { playerl("Yep, here's your chompy Bird!") @@ -405,14 +406,14 @@ class RantzChompyBirdDialogue(val quest: Quest) : DialogueFile() { playerl("Not yet!").also { stage++ } } } - 2 -> npcl(core.game.dialogue.FacialExpression.OLD_NORMAL, "Well hurry up and get some, we is hungry!").also { stage = END_DIALOGUE } + 2 -> npcl(FacialExpression.OLD_NORMAL, "Well hurry up and get some, we is hungry!").also { stage = END_DIALOGUE } 100 -> sendItemDialogue(player!!, Items.RAW_CHOMPY_2876, "You show Rantz the freshly plucked chompy carcass.").also { stage++ } - 101 -> npcl(core.game.dialogue.FacialExpression.OLD_NORMAL, "Dat's a great chompy, you musta got a lucky shot wiv da stabbie chucker.").also { stage++ } - 102 -> npcl(core.game.dialogue.FacialExpression.OLD_NORMAL, "Okay's now you's needs to cook da chompy! Slurp! You's can cook it's over der! ~Rantz points to a nearby spit roast.~").also { stage++ } - 103 -> npcl(core.game.dialogue.FacialExpression.OLD_NORMAL, "But's we's particular about our chompy yumms. Me's wants ${getItemName(rantzIngredient)} wiv mine! Fycie and Bugs want something wiv der's as well, go and ask 'em wat dey want.").also { stage++ } + 101 -> npcl(FacialExpression.OLD_NORMAL, "Dat's a great chompy, you musta got a lucky shot wiv da stabbie chucker.").also { stage++ } + 102 -> npcl(FacialExpression.OLD_NORMAL, "Okay's now you's needs to cook da chompy! Slurp! You's can cook it's over der! ~Rantz points to a nearby spit roast.~").also { stage++ } + 103 -> npcl(FacialExpression.OLD_NORMAL, "But's we's particular about our chompy yumms. Me's wants ${getItemName(rantzIngredient)} wiv mine! Fycie and Bugs want something wiv der's as well, go and ask 'em wat dey want.").also { stage++ } 104 -> playerl("What! Now I've got the chompy bird, you expect me to cook it as well?").also { stage++ } - 105 -> npcl(core.game.dialogue.FacialExpression.OLD_NORMAL, "Yep, da spit's over der! Last time Rantz did yummies, got very bad, did bad things to food.. and belly.").also { stage++ } + 105 -> npcl(FacialExpression.OLD_NORMAL, "Yep, da spit's over der! Last time Rantz did yummies, got very bad, did bad things to food.. and belly.").also { stage++ } 106 -> { playerl("HUH!").also { stage = END_DIALOGUE } quest.setStage(player, 70) @@ -424,7 +425,7 @@ class RantzChompyBirdDialogue(val quest: Quest) : DialogueFile() { val hasCookedChompy = amountInInventory(player!!, Items.SEASONED_CHOMPY_2882) > 0 when (stage) { - 0 -> npcl(core.game.dialogue.FacialExpression.OLD_NORMAL, "Hey You! Cook da chompy yet?").also { stage++ } + 0 -> npcl(FacialExpression.OLD_NORMAL, "Hey You! Cook da chompy yet?").also { stage++ } 1 -> { if (hasCookedChompy) { playerl("Yep, here you go, here's your cooked chompy bird.") @@ -433,11 +434,11 @@ class RantzChompyBirdDialogue(val quest: Quest) : DialogueFile() { playerl("Not yet!").also { stage++ } } } - 2 -> npcl(core.game.dialogue.FacialExpression.OLD_NORMAL, "Well hurry up, we is hungry!").also { stage = END_DIALOGUE } + 2 -> npcl(FacialExpression.OLD_NORMAL, "Well hurry up, we is hungry!").also { stage = END_DIALOGUE } 100 -> sendItemDialogue(player!!, Items.SEASONED_CHOMPY_2882, "You hand over the cooked chompy bird to Rantz.").also { stage++ } - 101 -> npcl(core.game.dialogue.FacialExpression.OLD_NORMAL, "Hey hey! We got da delicious chompy bird Yay! This looks really tasty as well!").also { stage++ } - 102 -> npcl(core.game.dialogue.FacialExpression.OLD_NORMAL, "Tank's very much for da chompy... Fycie an Bugs like very much da chompy yumms!").also { stage++ } + 101 -> npcl(FacialExpression.OLD_NORMAL, "Hey hey! We got da delicious chompy bird Yay! This looks really tasty as well!").also { stage++ } + 102 -> npcl(FacialExpression.OLD_NORMAL, "Tank's very much for da chompy... Fycie an Bugs like very much da chompy yumms!").also { stage++ } 103 -> sendDialogue(player, "~The family of ogres sit down together~").also { stage++ } 104 -> sendDialogue(player, "~and enjoy your well cooked chompy bird.~").also { stage++ } 105 -> playerl("It's my pleasure!").also { stage = END_DIALOGUE; quest.finish(player) } 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..337f30ac2 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 @@ -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..4b309bbc9 --- /dev/null +++ b/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/SkogreBehavior.kt @@ -0,0 +1,39 @@ +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.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 { + private 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 + } + 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..169984ce0 --- /dev/null +++ b/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/SlashBashBehavior.kt @@ -0,0 +1,92 @@ +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.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 + } + 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..1e00271d5 --- /dev/null +++ b/Server/src/main/content/region/kandarin/feldip/quest/zogreflesheaters/ZogreBehavior.kt @@ -0,0 +1,47 @@ +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.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 { + private 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 + } + 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..ca14c0f1c --- /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_BALM4_4842) + 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/AluftGianneJrDialogue.kt b/Server/src/main/content/region/kandarin/gnomestronghold/dialogue/AluftGianneJrDialogue.kt index d9accfee9..685abce87 100644 --- a/Server/src/main/content/region/kandarin/gnomestronghold/dialogue/AluftGianneJrDialogue.kt +++ b/Server/src/main/content/region/kandarin/gnomestronghold/dialogue/AluftGianneJrDialogue.kt @@ -9,15 +9,15 @@ import core.game.node.entity.player.Player import core.plugin.Initializable @Initializable -class AluftGianneJrDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { +class AluftGianneJrDialogue(player: Player? = null) : DialoguePlugin(player) { var tutorialStage = -1 - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return AluftGianneJrDialogue(player) } override fun npc(vararg messages: String?): Component { - return super.npc(core.game.dialogue.FacialExpression.OLD_NORMAL,*messages) + return super.npc(FacialExpression.OLD_NORMAL,*messages) } override fun open(vararg args: Any?): Boolean { @@ -33,7 +33,7 @@ class AluftGianneJrDialogue(player: Player? = null) : core.game.dialogue.Dialogu override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ 0 -> npc("Sure, go talk to my dad. I'll put","a good word in!").also { stage++ } - 1 -> player(core.game.dialogue.FacialExpression.THINKING,"Th-thanks...?").also { stage++ } + 1 -> player(FacialExpression.THINKING,"Th-thanks...?").also { stage++ } 2 -> { end() player.setAttribute("/save:$GC_BASE_ATTRIBUTE:$GC_TUT_PROG",0) 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/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/handlers/StoneCircleZone.kt b/Server/src/main/content/region/kandarin/handlers/StoneCircleZone.kt index f1a3ae2c8..c276acc83 100644 --- a/Server/src/main/content/region/kandarin/handlers/StoneCircleZone.kt +++ b/Server/src/main/content/region/kandarin/handlers/StoneCircleZone.kt @@ -7,6 +7,7 @@ import core.game.world.map.zone.ZoneBorders import core.game.world.map.zone.ZoneBuilder import core.plugin.Initializable import core.plugin.Plugin +import core.api.* /** * Handles the stone circle zone south of Ardougne. @@ -33,15 +34,15 @@ class StoneCircleZone : MapZone("stone circle", true), Plugin { override fun enter(e: Entity): Boolean { if (e is Player) { - e.varpManager.get(1184).setVarbit(28, 1).send(e) + setVarbit(e, 4833, 1) } return super.enter(e) } override fun leave(e: Entity, logout: Boolean): Boolean { if (!logout && e is Player) { - e.varpManager.get(1184).setVarbit(28, 0).send(e) + setVarbit(e, 4833, 0) } return super.leave(e, logout) } -} \ No newline at end of file +} 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 cb048284c..1fd390535 100644 --- a/Server/src/main/content/region/kandarin/pisc/dialogue/ArnoldLydsporDialogue.kt +++ b/Server/src/main/content/region/kandarin/pisc/dialogue/ArnoldLydsporDialogue.kt @@ -20,36 +20,36 @@ import core.tools.START_DIALOGUE * @author vddCore */ @Initializable -class ArnoldLydsporDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { +class ArnoldLydsporDialogue(player: Player? = null) : DialoguePlugin(player) { override fun handle(interfaceId: Int, buttonId: Int): Boolean { when (stage) { START_DIALOGUE -> npcl( - core.game.dialogue.FacialExpression.FRIENDLY, + FacialExpression.FRIENDLY, "Ah, you come back! What you want from Arnold, heh?" ).also { stage++ } 1 -> showTopics( IfTopic( - core.game.dialogue.FacialExpression.ASKING, + FacialExpression.ASKING, "Can you open my bank account, please?", 2, !hasIronmanRestriction(player, IronmanMode.ULTIMATE) ), IfTopic( - core.game.dialogue.FacialExpression.NEUTRAL, + FacialExpression.NEUTRAL, "I'd like to check my bank PIN settings.", 3, !hasIronmanRestriction(player, IronmanMode.ULTIMATE) ), IfTopic( - core.game.dialogue.FacialExpression.NEUTRAL, + FacialExpression.NEUTRAL, "I'd like to collect items.", 4, !hasIronmanRestriction(player, IronmanMode.ULTIMATE) ), - Topic(core.game.dialogue.FacialExpression.ASKING, "Would you like to trade?", 5), - Topic(core.game.dialogue.FacialExpression.FRIENDLY, "Nothing, I just came to chat.", 7) + Topic(FacialExpression.ASKING, "Would you like to trade?", 5), + Topic(FacialExpression.FRIENDLY, "Nothing, I just came to chat.", 7) ) 2 -> { @@ -68,7 +68,7 @@ class ArnoldLydsporDialogue(player: Player? = null) : core.game.dialogue.Dialogu } 5 -> npcl( - core.game.dialogue.FacialExpression.FRIENDLY, + FacialExpression.FRIENDLY, "Ja, I have wide range of stock..." ).also { stage++ } @@ -77,7 +77,7 @@ class ArnoldLydsporDialogue(player: Player? = null) : core.game.dialogue.Dialogu end() } - 7 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, + 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." @@ -93,17 +93,17 @@ class ArnoldLydsporDialogue(player: Player? = null) : core.game.dialogue.Dialogu } 9 -> playerl( - core.game.dialogue.FacialExpression.HALF_THINKING, + FacialExpression.HALF_THINKING, "A cabbage?" ).also { stage++ } 10 -> npcl( - core.game.dialogue.FacialExpression.HAPPY, + FacialExpression.HAPPY, "Ja, cabbage is good for you!" ).also { stage++ } 11 -> playerl( - core.game.dialogue.FacialExpression.NEUTRAL, + FacialExpression.NEUTRAL, "Um... Thanks!" ).also { stage = END_DIALOGUE } } diff --git a/Server/src/main/content/region/kandarin/pisc/dialogue/FranklinCaranosDialogue.kt b/Server/src/main/content/region/kandarin/pisc/dialogue/FranklinCaranosDialogue.kt new file mode 100644 index 000000000..6ae06da7a --- /dev/null +++ b/Server/src/main/content/region/kandarin/pisc/dialogue/FranklinCaranosDialogue.kt @@ -0,0 +1,41 @@ +package content.region.kandarin.pisc.dialogue + +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 + +@Initializable +class FranklinCaranosDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage) { + START_DIALOGUE -> npcl( + FacialExpression.FRIENDLY, + "Hello again, " + player.username + "." + ).also { stage++ } + + 1 -> playerl( + FacialExpression.ASKING, + "Hello. How's the repair work going?" + ).also { stage++ } + + 2 -> npcl( + FacialExpression.NEUTRAL, + "I'm working on it. I can always do with more iron sheets, so if you've got any more, I'll give you 20 gp per sheet." + ).also { stage++ } + + 3 -> playerl( + FacialExpression.NEUTRAL, + "Thanks, I'll remember that." + ).also { stage = END_DIALOGUE } + } + + return true + } + + override fun getIds(): IntArray = intArrayOf((NPCs.FRANKLIN_CARANOS_3823)) +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/pisc/dialogue/HermanCaranosDialogue.kt b/Server/src/main/content/region/kandarin/pisc/dialogue/HermanCaranosDialogue.kt new file mode 100644 index 000000000..4e1171881 --- /dev/null +++ b/Server/src/main/content/region/kandarin/pisc/dialogue/HermanCaranosDialogue.kt @@ -0,0 +1,57 @@ +package content.region.kandarin.pisc.dialogue + +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 + +@Initializable +class HermanCaranosDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage) { + START_DIALOGUE -> playerl( + FacialExpression.FRIENDLY, + "Hiya Hermie!" + ).also { stage++ } + + 1 -> npcl( + FacialExpression.FRIENDLY, + "Ah, " + player.username + "! How's the fishing going?" + ).also { stage++ } + + 2 -> playerl( + FacialExpression.NEUTRAL, + "Still working on it. How's the Colony?" + ).also { stage++ } + + 3 -> npcl( + FacialExpression.HAPPY, + "Flourishing, now that the colonists are coming back! Soon all the finest restaurants will be serving monkfish!" + ).also { stage++ } + + 4 -> npcl( + FacialExpression.ANNOYED, + "It's a pity those skeletons you brought here haven't gone away again, though. They're in the dormitories, which is rather annoying! So please take a little time to kick 'em around a bit." + ).also { stage++ } + + 5 -> npcl( + FacialExpression.ANNOYED, + "Oh, and please don't call me Hermie." + ).also { stage++ } + + 6 -> playerl( + FacialExpression.LAUGH, + "Whatever you like, Hermie!" + ).also { stage = END_DIALOGUE } + } + + return true + } + + override fun getIds(): IntArray = intArrayOf(NPCs.HERMAN_CARANOS_3822) + +} \ No newline at end of file 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 664a727e5..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,10 +1,13 @@ 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; import core.game.node.item.Item; +import static core.api.ContentAPIKt.*; + /** * Handles captains lawgof dialogue. * @author Vexia @@ -42,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."); @@ -95,7 +98,7 @@ public class CaptainLawgofDialogue extends DialoguePlugin { case 10: switch (stage) { case 0: - if (player.getConfigManager().get(1) == 2016) { + if (DwarfCannon.allRailsFixed(player)) { npc("Well done, trooper! The goblins seems to have stopped", "getting in. I think you've done the job!"); stage = 105; break; @@ -182,7 +185,7 @@ public class CaptainLawgofDialogue extends DialoguePlugin { stage++; break; case 112: - player.getConfigManager().set(0, 3, true); + setVarp(player, 0, 3, true); quest.setStage(player, 20); end(); break; @@ -236,7 +239,7 @@ public class CaptainLawgofDialogue extends DialoguePlugin { break; case 10: end(); - player.getConfigManager().set(0, 5, true); + setVarp(player, 0, 5, true); player.getInventory().remove(DwarfCannon.DWARF_REMAINS); quest.setStage(player, 30); break; @@ -293,7 +296,7 @@ public class CaptainLawgofDialogue extends DialoguePlugin { break; case 8: player.getInventory().add(DwarfCannon.TOOL_KIT, player); - player.getConfigManager().set(0, 7); + setVarp(player, 0, 7); quest.setStage(player, 50); end(); break; @@ -392,8 +395,8 @@ public class CaptainLawgofDialogue extends DialoguePlugin { break; case 13: player.getInventory().remove(DwarfCannon.TOOL_KIT); - player.sendMessage("You giv the toolkit back to Captain Lawgof."); - player.getConfigManager().set(0, 9, true); + player.sendMessage("You give the toolkit back to Captain Lawgof."); + setVarp(player, 0, 9, true); quest.setStage(player, 70); end(); break; 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 6e52ee494..8268ced31 100644 --- a/Server/src/main/content/region/kandarin/quest/dwarfcannon/DwarfCannon.java +++ b/Server/src/main/content/region/kandarin/quest/dwarfcannon/DwarfCannon.java @@ -7,18 +7,15 @@ import core.plugin.Initializable; 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. * @author Vexia */ @Initializable public class DwarfCannon extends Quest { - - /** - * The name of this quest. - */ - public static final String NAME = "Dwarf Cannon"; - /** * The dwarf remain item. */ @@ -38,12 +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 }; /** * Constructs a new {@Code DwarfCannon} {@Code Object} */ public DwarfCannon() { - super(NAME, 49, 48, 1); + super(Quests.DWARF_CANNON, 49, 48, 1); } @Override @@ -64,7 +62,7 @@ public class DwarfCannon extends Quest { line(player, "I can start this quest by speaking to Lawgof the DwarvenCaptain of the Black Watch , he is defending an areaNorth-west of the Fishing Guild against goblin attack.", 11); break; case 10: - line(player, "I have spoken to Captain Lawgof, he recruited me into theBlack Guard and asked me to help the dwarves." + (player.getConfigManager().get(1) == 2016 ? "I have repaired all the broken railings,I should report back to Captain Lawgof." : "My first task is to fix the broken railingsin the dwarves defensive perimeter."), 11); + line(player, "I have spoken to Captain Lawgof, he recruited me into theBlack Guard and asked me to help the dwarves." + (allRailsFixed(player) ? "I have repaired all the broken railings,I should report back to Captain Lawgof." : "My first task is to fix the broken railingsin the dwarves defensive perimeter."), 11); break; case 20: line(player, "I have spoken to Captain Lawgof, he recruited me into theBlack Guard and asked me to help the dwarves." + (player.hasItem(DWARF_REMAINS) ? "I went to the watchtower where I found the remains ofGilob.I should take them back to Captain Lawgof." : "I have repaired all the broken railings,Captain Lawgof has asked me to check up on his guards atthe watchtower to the South of this camp."), 11); @@ -96,7 +94,7 @@ public class DwarfCannon extends Quest { @Override public void start(Player player) { super.start(player); - player.getConfigManager().set(0, 1); + setVarp(player, 0, 1); } @Override @@ -117,9 +115,16 @@ public class DwarfCannon extends Quest { if (stage >= 100) { val = 11; } else if (stage > 0 && stage < 100) { - val = player.getConfigManager().get(0); + val = getVarp(player, 0); } return new int[] { 0, val }; } + public static boolean allRailsFixed (Player player) { + for (int i = 0; i < 6; i++) { + if (getVarbit(player, railVarbits[i]) != 1) + return false; + } + return true; + } } 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 1f0f52ffb..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; @@ -16,13 +17,16 @@ 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 core.tools.SystemLogger; +import core.tools.Log; 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.Plugin; import core.tools.RandomFunction; +import core.game.node.scenery.Scenery; + +import static core.api.ContentAPIKt.*; /** * Handles the dwarf cannon quest interactions. @@ -57,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; } - player.getConfigManager().set(1, 2041, true); - player.getConfigManager().set(0, 8, true); - player.getQuestRepository().getQuest(DwarfCannon.NAME).setStage(player, 60); + //setVarp(player, 1, 2041, true); + setVarp(player, 0, 8, true); + 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); @@ -81,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))) { @@ -120,6 +124,7 @@ public class DwarfCannonPlugin extends OptionHandler { case 18: case 19: case 20: + int index = 15 - node.getId(); final int shift = 10 - (20 - node.getId()); player.faceLocation(node.getLocation()); player.getDialogueInterpreter().sendDialogue("The railing is broken and needs to be replaced."); @@ -164,8 +169,8 @@ public class DwarfCannonPlugin extends OptionHandler { } player.getInventory().remove(new Item(14)); player.sendMessage("The railing is now fixed."); - player.getConfigManager().set(1, player.getConfigManager().get(1) + (1 << shift), true); - if (player.getConfigManager().get(1) == 2016) { + setVarbit(player, ((Scenery) node).getDefinition().getVarbitID(), 1, true); + if (DwarfCannon.allRailsFixed(player)) { player.getDialogueInterpreter().sendDialogues(player, null, "I've fixed all these railings now."); } return true; @@ -190,7 +195,7 @@ public class DwarfCannonPlugin extends OptionHandler { if (!player.getInventory().hasSpaceFor(DwarfCannon.DWARF_REMAINS)) { return; } - player.getConfigManager().set(0, 4); + setVarp(player, 0, 4, true); player.getInventory().add(DwarfCannon.DWARF_REMAINS); } @@ -202,13 +207,14 @@ public class DwarfCannonPlugin extends OptionHandler { return true; } quest.setStage(player, 40); - player.getConfigManager().set(0, 6, true); + setVarp(player, 0, 6, true); spawnLollk(player); break; } return true; } + /** * Spawns the lollk npc. * @param player the player. @@ -289,7 +295,7 @@ public class DwarfCannonPlugin extends OptionHandler { } Part part = Part.forId(button); if (part == null) { - SystemLogger.logErr(this.getClass(), "Unhandled part id - " + button + "!"); + log(this.getClass(), Log.ERR, "Unhandled part id - " + button + "!"); return true; } if (this.tool.getPart() != part) { @@ -300,9 +306,10 @@ public class DwarfCannonPlugin extends OptionHandler { part.interact(player, this.tool, toggled[part.ordinal()]); if (toggled[0] && toggled[1] && toggled[2]) { player.lock(5); - player.getConfigManager().set(1, 2041, true); - player.getConfigManager().set(0, 8, true); - player.getQuestRepository().getQuest(DwarfCannon.NAME).setStage(player, 60); + + //setVarp(player, 1, 2041, true); + //setVarp(player, 0, 8, true); + 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 @@ -312,7 +319,7 @@ public class DwarfCannonPlugin extends OptionHandler { } }); } - player.sendMessage("Interacting with a part. Toggled=" + toggled[part.ordinal()] + ", configVal=" + player.getConfigManager().get(1) + "."); + //player.sendMessage("Interacting with a part. Toggled=" + toggled[part.ordinal()] + ", configVal=" + player.getConfigManager().get(1) + "."); return true; } @@ -355,7 +362,7 @@ public class DwarfCannonPlugin extends OptionHandler { * @param player the player. */ public Tool select(Player player) { - player.getConfigManager().set(1, configValue); + //setVarp(player, 1, configValue); return this; } @@ -433,7 +440,7 @@ public class DwarfCannonPlugin extends OptionHandler { * @param toggled if toggled on. */ public void interact(Player player, Tool tool, boolean toggled) { - player.getConfigManager().set(1, toggled ? getConfigValue() : tool.getConfigValue()); + //setVarp(player, 1, toggled ? getConfigValue() : tool.getConfigValue()); } /** 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 b38a3004d..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,11 +1,14 @@ 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; import core.game.node.entity.player.link.quest.Quest; import core.game.node.item.Item; +import static core.api.ContentAPIKt.*; + /** * Handles captains lawgof dialogue. * @author Vexia @@ -47,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."); @@ -93,7 +96,7 @@ public class NulodionDialogue extends DialoguePlugin { stage++; break; case 7: - player.getConfigManager().set(0, 10); + setVarp(player, 0, 10); quest.setStage(player, 80); player.sendMessage("The Cannon Engineer gives you some notes and a mould."); player.getInventory().add(DwarfCannon.NULODION_NOTES, player); diff --git a/Server/src/main/content/region/kandarin/quest/dwarfcannon/NulodionsNotes.kt b/Server/src/main/content/region/kandarin/quest/dwarfcannon/NulodionsNotes.kt new file mode 100644 index 000000000..9d9aea830 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/dwarfcannon/NulodionsNotes.kt @@ -0,0 +1,42 @@ +package content.region.kandarin.quest.dwarfcannon + +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 + +@Initializable +class NulodionsNotes : InteractionListener { + companion object { + private val TITLE = "Nulodion's notes" + private val CONTENTS = arrayOf( + PageSet( + Page( + BookLine("Ammo for the Dwarf Multi", 55), + BookLine("Cannon must be made from", 56), + BookLine("steel bars. The bars must be", 57), + BookLine("heated in a furnace and used", 58), + BookLine("with the ammo mould.", 59), + ) + ), + ) + } + + 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.NULODIONS_NOTES_3, 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/dwarfcannon/dmc/CannonTimer.kt b/Server/src/main/content/region/kandarin/quest/dwarfcannon/dmc/CannonTimer.kt new file mode 100644 index 000000000..064c341af --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/dwarfcannon/dmc/CannonTimer.kt @@ -0,0 +1,34 @@ +package content.region.kandarin.quest.dwarfcannon.dmc + +import core.api.* +import core.tools.* +import core.game.system.timer.* +import core.game.node.entity.Entity +import core.game.node.entity.player.Player + +class CannonTimer : RSTimer (1, "dmc:timer") { + lateinit var dmcHandler: DMCHandler + var ticksUntilDecay = 2500 + var isFiring = false + + override fun run (entity: Entity) : Boolean { + if (entity !is Player) + return false + if (!dmcHandler.cannon.isActive()) + return false + if (isFiring) + isFiring = dmcHandler.rotate() + if (--ticksUntilDecay == 500) { + sendMessage (entity, colorize("%RYour cannon is about to decay.")) + } else if (ticksUntilDecay == 0) { + dmcHandler.explode(true) + } + return ticksUntilDecay > 0 && dmcHandler.cannon.isActive() + } + + override fun getTimer (vararg args: Any) : RSTimer { + val t = retrieveInstance() as CannonTimer + t.dmcHandler = args[0] as DMCHandler + return t + } +} diff --git a/Server/src/main/content/region/kandarin/quest/dwarfcannon/dmc/DMCHandler.java b/Server/src/main/content/region/kandarin/quest/dwarfcannon/dmc/DMCHandler.java index 5dd8a9439..9b7d2363c 100644 --- a/Server/src/main/content/region/kandarin/quest/dwarfcannon/dmc/DMCHandler.java +++ b/Server/src/main/content/region/kandarin/quest/dwarfcannon/dmc/DMCHandler.java @@ -21,6 +21,9 @@ import core.tools.RandomFunction; import org.jetbrains.annotations.NotNull; import core.game.node.entity.combat.CombatSwingHandler; import core.game.world.GameWorld; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.*; /** * Handles a player's Dwarf Multi-cannon. @@ -43,23 +46,15 @@ public final class DMCHandler implements LogoutListener { */ private int cannonballs; - /** - * The firing pulse. - */ - private Pulse firingPulse; - /** * The current direction. */ private DMCRevolution direction = DMCRevolution.NORTH; - /** - * The decaying pulse. - */ - private Pulse decayPulse; + private CannonTimer timer; public DMCHandler() { - this.player = null; + this.player = null; } /** @@ -68,54 +63,26 @@ public final class DMCHandler implements LogoutListener { */ public DMCHandler(final Player player) { this.player = player; - this.firingPulse = new Pulse(1, player) { - @Override - public boolean pulse() { - if (!cannon.isActive()) { - return true; - } - return rotate(); - } - - }; - firingPulse.stop(); - this.decayPulse = new Pulse(2000, player) { - @Override - public boolean pulse() { - if (!cannon.isActive()) { - return true; - } - if (getDelay() == 2000) { - setDelay(500); - player.sendMessage("Your cannon is about to decay!"); - return false; - } - explode(true); - return true; - } - - }; - decayPulse.stop(); } /** * Rotates the cannon. * @return {@code True} if the cannon should stop rotating. */ - private boolean rotate() { + public boolean rotate() { if (cannonballs < 1) { player.getPacketDispatch().sendMessage("Your cannon has run out of ammo!"); - return true; + return false; } player.getPacketDispatch().sendSceneryAnimation(cannon, Animation.create(direction.getAnimationId())); Location l = cannon.getLocation().transform(1, 1, 0); - player.getAudioManager().send(new Audio(2877), true, l); + playGlobalAudio(l, Sounds.MCANNON_TURN_2877); direction = DMCRevolution.values()[(direction.ordinal() + 1) % DMCRevolution.values().length]; for (NPC npc : RegionManager.getLocalNpcs(l, 10)) { if (direction.isInSight(npc.getLocation().getX() - l.getX(), npc.getLocation().getY() - l.getY()) && npc.isAttackable(player, CombatStyle.RANGE, false) && CombatSwingHandler.isProjectileClipped(npc, l, false)) { int speed = (int) (25 + (l.getDistance(npc.getLocation()) * 10)); Projectile.create(l, npc.getLocation(), 53, 40, 36, 20, speed, 0, 128).send(); - player.getAudioManager().send(new Audio(1667), true, l); + playGlobalAudio(l, Sounds.MCANNON_FIRE_1667); cannonballs--; int hit = 0; if (player.getSwingHandler(false).isAccurateImpact(player, npc, CombatStyle.RANGE, 1.2, 1.0)) { @@ -127,7 +94,7 @@ public final class DMCHandler implements LogoutListener { break; } } - return false; + return true; } /** @@ -138,9 +105,9 @@ public final class DMCHandler implements LogoutListener { player.getPacketDispatch().sendMessage("You don't have a cannon active."); return; } - if (firingPulse.isRunning()) { - firingPulse.stop(); - return; + if (timer.isFiring()) { + timer.setFiring(false); + return; } if (cannonballs < 1) { int amount = player.getInventory().getAmount(new Item(2)); @@ -160,9 +127,7 @@ public final class DMCHandler implements LogoutListener { player.sendMessage("Your cannon is already fully loaded."); } } - firingPulse.restart(); - firingPulse.start(); - GameWorld.getPulser().submit(firingPulse); + timer.setFiring(true); } /** @@ -193,10 +158,6 @@ public final class DMCHandler implements LogoutListener { return; } final DMCHandler handler = new DMCHandler(player); - if (handler.decayPulse.isRunning()) { - handler.decayPulse.stop(); - return; - } player.setAttribute("dmc", handler); player.getPulseManager().clear(); player.getWalkingQueue().reset(); @@ -233,9 +194,11 @@ public final class DMCHandler implements LogoutListener { player.getPacketDispatch().sendMessage("You add the furnace."); SceneryBuilder.remove(object); handler.configure(SceneryBuilder.add(object = object.transform(6))); + handler.timer = (CannonTimer) spawnTimer ("dmc:timer", handler); + registerTimer (player, handler.timer); return true; } - player.getAudioManager().send(new Audio(2876), true); + playGlobalAudio(player.getLocation(), Sounds.MCANNON_SETUP_2876); if(count != 0) { SceneryBuilder.remove(object); SceneryBuilder.add(object = object.transform(object.getId() + 1)); @@ -267,9 +230,6 @@ public final class DMCHandler implements LogoutListener { * @param pickup If the cannon is getting picked up. */ public void clear(boolean pickup) { - if (decayPulse.isRunning()) { - decayPulse.stop(); - } SceneryBuilder.remove(cannon); player.removeAttribute("dmc"); if (!pickup) { diff --git a/Server/src/main/content/region/kandarin/quest/dwarfcannon/dmc/DMCManual.kt b/Server/src/main/content/region/kandarin/quest/dwarfcannon/dmc/DMCManual.kt new file mode 100644 index 000000000..fa22f2bed --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/dwarfcannon/dmc/DMCManual.kt @@ -0,0 +1,95 @@ +package content.region.kandarin.quest.dwarfcannon.dmc + +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 + +@Initializable +class DMCManual : InteractionListener { + companion object { + private val TITLE = "Dwarf multicannon manual" + private val CONTENTS = arrayOf( + PageSet( + Page( + BookLine("Constructing the cannon", 55), + BookLine("", 56), + BookLine("To construct the cannon, firstly", 57), + BookLine("set down the base of the cannon", 58), + BookLine("firmly onto the ground, next add", 59), + BookLine("the Dwarf stand to the cannon", 60), + BookLine("base then add the Barrels. Lastly", 61), + BookLine("add the Furnace, which powers", 62), + BookLine("the cannon. You should now have", 63), + BookLine("a fully set up Multi Cannon to", 64), + BookLine("splat nasty creatures!", 65), + ), + Page( + BookLine("Making ammo", 66), + BookLine("", 67), + BookLine("The ammo for the cannon", 68), + BookLine("is made from steel bars.", 69), + BookLine("Firstly you must heat up a", 70), + BookLine("steel bar in a furnace.", 71), + BookLine("Now pour the molten steel", 72), + BookLine("into a cannon ammo mould.", 73), + BookLine("You should now have a ready", 74), + BookLine("to fire Multi cannon ball.", 75), + ) + ), + PageSet( + Page( + BookLine("Firing the Cannon", 55), + BookLine("", 56), + BookLine("The cannon will only fire", 57), + BookLine("when monsters are available", 58), + BookLine("to target. If you are carrying", 59), + BookLine("enough ammo the cannon will", 60), + BookLine("fire up to 30 rounds before it", 61), + BookLine("runs out and stops. The", 62), + BookLine("cannon will automatically target", 63), + BookLine("non friendly creatures.", 64), + ), + Page( + BookLine("Dwarf Cannon Warranty", 66), + BookLine("", 67), + BookLine("If your cannon is stolen or", 68), + BookLine("has been lost, after or during", 69), + BookLine("being being set up, the Dwarf", 70), + BookLine("engineer will replace the parts,", 71), + BookLine("however cannon parts that", 72), + BookLine("were given away or dropped", 73), + BookLine("will not be replaced for free.", 74), + BookLine("It is only possible to operate", 75), + BookLine("one cannon at a time.", 76), + ) + ), + PageSet( + Page( + BookLine("By order", 55), + BookLine("of the members of the noble", 56), + BookLine("Dwarven Black Guard.", 57), + ) + ), + ) + } + + 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.INSTRUCTION_MANUAL_5, 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/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 62a62682d..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 27ad753db..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 { @@ -26,12 +27,12 @@ public class GarlicPipeInteraction extends PluginInteraction { @Override public boolean handle(Player player, NodeUsageEvent event) { - System.out.println("Trying to handle it"); + if(event.getUsed() instanceof Item && event.getUsedWith() instanceof Scenery){ 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 60b103359..97daa72b1 100644 --- a/Server/src/main/content/region/kandarin/quest/fishingcontest/StairInteraction.java +++ b/Server/src/main/content/region/kandarin/quest/fishingcontest/StairInteraction.java @@ -10,12 +10,14 @@ import core.plugin.Initializable; 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: @@ -33,7 +35,12 @@ public class StairInteraction extends PluginInteraction { player.getPulseManager().run(new MovementPulse(player,object.getLocation().transform(0,2,0)) { @Override public boolean pulse() { - player.getDialogueInterpreter().open(npc_id,new NPC(npc_id)); + NPC n = Repository.findNPC(npc_id); + if (n == null) { + player.sendMessage("Are you in a world without NPCs? What did you do?"); + return true; + } + player.getDialogueInterpreter().open(npc_id, n); return true; } }, PulseType.STANDARD); 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 new file mode 100644 index 000000000..4d9c58b1c --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/grandtree/AnitaDialogue.kt @@ -0,0 +1,49 @@ +package content.region.kandarin.quest.grandtree + +import content.data.Quests +import core.api.addItemOrDrop +import core.api.getQuestStage +import core.api.sendDialogue +import core.game.dialogue.DialogueFile +import core.game.node.item.Item +import core.tools.END_DIALOGUE +import org.rs09.consts.Items + +class AnitaDialogue : DialogueFile(){ + override fun handle(componentID: Int, buttonID: Int) { + when(getQuestStage(player!!, Quests.THE_GRAND_TREE)){ + 60 -> { + if(player!!.hasItem(Item(Items.GLOUGHS_KEY_788)) && stage < 12){ + when(stage){ + 0 -> npcl("Have you taken that key to Glough yet?").also { stage++ } + 1 -> playerl("No, I'm still carrying it around.").also { stage++ } + 2 -> npcl("Oh. Please take it to Glough!").also { stage = END_DIALOGUE } + } + } else { + when(stage){ + 0 -> playerl("Hello there.").also { stage++ } + 1 -> npcl("Oh hello, I've seen you with the King.").also { stage++ } + 2 -> playerl("Yes, I'm helping him with a problem.").also { stage++ } + 3 -> npcl("You must know my boyfriend Glough then?").also { stage++ } + 4 -> playerl("Indeed!").also { stage++ } + 5 -> npcl("Could you do me a favour?").also { stage++ } + 6 -> options("I suppose so.","No, I'm busy.").also { stage++ } + 7 -> when(buttonID){ + 1 -> playerl("I suppose so.").also { stage = 10 } + 2 -> playerl("No, I'm busy.").also { stage = END_DIALOGUE } + } + 10 -> playerl("I suppose so.").also { stage++ } + 11 -> npcl("Please give this key to Glough, he left it here last night.").also { stage++ } + 12 -> sendDialogue(player!!, "Anita gives you a key.").also { + addItemOrDrop(player!!, Items.GLOUGHS_KEY_788) + stage++ + } + 13 -> npcl("Thanks a lot.").also { stage++ } + 14 -> playerl("No... thank you!").also { stage = END_DIALOGUE } + } + } + } + } + } + +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/grandtree/BlackDemonCutscene.kt b/Server/src/main/content/region/kandarin/quest/grandtree/BlackDemonCutscene.kt new file mode 100644 index 000000000..2afe9f1cf --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/grandtree/BlackDemonCutscene.kt @@ -0,0 +1,88 @@ +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 org.rs09.consts.NPCs + +class BlackDemonCutscene(player: Player) : Cutscene(player) { + override fun setup() { + setExit(Location.create(2491, 9864, 0)) + if(player.settings.isRunToggled){ + player.settings.toggleRun() + } + loadRegion(9882) + addNPC(NPCs.GLOUGH_671, 48, 8, Direction.WEST) + addNPC(NPCs.BLACK_DEMON_677, 43, 9, Direction.EAST) + } + + override fun runStage(stage: Int) { + when(stage) + { + 0 -> { + fadeToBlack() + timedUpdate(6) + } + 1 -> { + fadeFromBlack() + teleport(player, 59, 8) + moveCamera(55, 8) + rotateCamera(0, 0) + sendChat(player, "Hello?") + player.face(getNPC(NPCs.GLOUGH_671)!!) + timedUpdate(3) + } + 2 -> { + moveCamera(48, 8) + sendChat(player, "Anybody?") + timedUpdate(3) + } + 3 -> { + sendChat(player, "Glough?") + move(getNPC(NPCs.GLOUGH_671)!!, 52, 8) + timedUpdate(10) + } + 4 -> { + moveCamera(55, 4,2000) + rotateCamera(55, 6) + timedUpdate(4) + } + 5 -> { + move(player, 53, 8) + 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 + "!") + } + 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!") + } + 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!") + } + 11 -> { + end{ + BlackDemonNPC(NPCs.BLACK_DEMON_677, Location.create(2485, 9864, 0)).init() + } + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/grandtree/BlackDemonNPC.kt b/Server/src/main/content/region/kandarin/quest/grandtree/BlackDemonNPC.kt new file mode 100644 index 000000000..112321405 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/grandtree/BlackDemonNPC.kt @@ -0,0 +1,34 @@ +package content.region.kandarin.quest.grandtree + +import content.data.Quests +import core.api.* +import core.game.node.entity.Entity +import core.game.node.entity.npc.AbstractNPC +import core.game.world.map.Location +import core.plugin.Initializable +import org.rs09.consts.NPCs +import core.game.interaction.InteractionListener + +@Initializable +class BlackDemonNPC(id: Int = 0, location: Location? = null) : AbstractNPC(id,location), InteractionListener { + + override fun construct(id: Int, location: Location?, vararg objects: Any?): AbstractNPC { + return BlackDemonNPC(id, location) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.BLACK_DEMON_677) + } + + override fun defineListeners() { + } + + override fun finalizeDeath(killer: Entity?) { + // In the event that this npcID is used somewhere else... + if(killer!!.asPlayer().location.regionId == 9882) { + 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 new file mode 100644 index 000000000..2b225bb47 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/grandtree/CaptainErrdoDialogue.kt @@ -0,0 +1,78 @@ +package content.region.kandarin.quest.grandtree + +import content.data.Quests +import content.global.travel.glider.Gliders +import core.api.getQuestStage +import core.api.teleport +import core.game.component.Component +import core.game.dialogue.DialogueFile +import core.game.world.map.Location +import core.tools.END_DIALOGUE + +class CaptainErrdoDialogue: DialogueFile(){ + override fun handle(componentID: Int, buttonID: Int) { + when(getQuestStage(player!!, Quests.THE_GRAND_TREE)){ + 55 -> { + if(player!!.location.regionId == 11567){ + when(stage){ + 0 -> npcl("Sorry about that.").also { stage++ } + 1 -> npcl("That turbulence over the Karamja Volcano was a bit unexpected, and the area round here isn't well suited for emergency landing.").also { stage++ } + 2 -> npcl("Still! we're still alive that's the main thing. Are you okay?").also { stage++ } + 3 -> playerl("I'm fine, I can't say the same for your glider!").also { stage++ } + 4 -> npcl("I don't think I can fix this. Looks like we'll be heading back by foot. I might see if I can find Penwie while I'm here, I believe he's charting the area.").also { stage++ } + 5 -> playerl("Where's the shipyard from here?").also { stage++ } + 6 -> npcl("I think I saw some buildings on the coast east of here while we were crashing. I'd have a look there.").also { stage++ } + 7 -> npcl("Take care adventurer!").also { stage++ } + 8 -> playerl("Take care little man.").also { stage = END_DIALOGUE } + } + } else if (player!!.location.regionId == 9782){ + when(stage){ + 0 -> npcl("Hi. The king said that you need to leave?").also { stage++ } + 1 -> playerl("Apparently, humans are invading!").also { stage++ } + 2 -> npcl("I find that hard to believe. I have lots of human friends.").also { stage++ } + 3 -> playerl("I don't understand it either!").also { stage++ } + 4 -> npcl("So, where to?").also { stage++ } + 5 -> options("Take me to Karamja please!", "Not anywhere for now!").also { stage++ } + 6 -> when(buttonID){ + 1 -> playerl("Take me to Karamja, please.").also { stage++ } + 2 -> playerl("Not anywhere for now.").also { stage = 10 } + } + 7 -> npcl("Okay, you're the boss! Hold on tight, it'll be a rough ride.").also { + teleport(player!!, Location.create(2917, 3054, 0)) + stage = END_DIALOGUE + } + 10 -> npcl("Okay. I'll be here for when you're ready.").also { stage = END_DIALOGUE } + } + } else { + // We are talking to another gnome outside the stronghold during the quest. + when(stage) { + 0 -> playerl("May you fly me somewhere on your glider?").also { stage++ } + 1 -> npcl("I only fly friends of the gnomes!").also { + stage = END_DIALOGUE + } + } + } + } + 100 -> { + when(stage){ + 0 -> playerl("May you fly me somewhere on your glider?").also { stage++ } + 1 -> npcl("If you wish.").also { + stage = END_DIALOGUE + player!!.interfaceManager.open(Component(138)) + Gliders.sendConfig(npc, player) + + } + } + } + else -> { + when(stage){ + 0 -> playerl("May you fly me somewhere on your glider?").also { stage++ } + 1 -> npcl("I only fly friends of the gnomes!").also { + stage = END_DIALOGUE + } + } + } + } + } + +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/grandtree/CharlieDialogue.kt b/Server/src/main/content/region/kandarin/quest/grandtree/CharlieDialogue.kt new file mode 100644 index 000000000..d4af65825 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/grandtree/CharlieDialogue.kt @@ -0,0 +1,124 @@ +package content.region.kandarin.quest.grandtree + +import content.data.Quests +import core.ServerConstants +import core.api.* +import core.game.dialogue.DialogueFile +import core.game.global.action.DoorActionHandler +import core.game.node.entity.npc.NPC +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 core.tools.END_DIALOGUE +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +class CharlieDialogue : DialogueFile() { + + override fun handle(componentID: Int, buttonID: Int) { + 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++ } + 1 -> npcl("What do you mean?").also { stage++ } + 2 -> playerl("Don't tell me, you just happened to be caught carrying Daconia rocks!").also { stage++ } + 3 -> npcl("All I know is that I did what I was asked.").also { stage++ } + 4 -> playerl("I don't understand.").also { stage++ } + 5 -> npcl("Glough paid me to go to this gnome on a hill. I gave the gnome a seal and he gave me some Daconia rocks to give to Glough.").also { stage++ } + 6 -> npcl("I've been doing it for weeks, this time though Glough locked me up here! I just don't understand it.").also { stage++ } + 7 -> playerl("Sounds like Glough is hiding something.").also { stage++ } + 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!!, Quests.THE_GRAND_TREE, 47) + stage = END_DIALOGUE + } + } + } + 47 -> { + when (stage) { + 0 -> npcl("Hello adventurer. Have you figured out what's going on?").also { stage++ } + 1 -> playerl("No idea.").also { stage++ } + 2 -> npcl("To get to the bottom of this you'll need to search Glough's home.").also { stage = END_DIALOGUE } + } + } + 50 -> { + when(stage) { + 0 -> npcl("So they got you as well?").also { stage++ } + 1 -> playerl("It's Glough! He's trying to cover something up.").also { stage++ } + 2 -> npcl("I shouldn't tell you this adventurer. But if you want to get to the bottom of this you should go and talk to the Karamja Shipyard foreman.").also { stage++ } + 3 -> playerl("Why?").also { stage++ } + 4 -> npcl("Glough sent me to Karamja to meet him. I delivered a large amount of gold. For what? I don't know. He may be able to tell you what Glough's up to. That's if you can get out of here. You'll find him").also { stage++ } + 5 -> npcl("in the Karamja Shipyard, east of Shilo Village. Be careful! If he discovers you're not working for Glough, there'll be trouble! The seamen use the password Ka-Lu-Min.").also { stage++ } + 6 -> playerl("Thanks, Charlie!").also { + stage = END_DIALOGUE + GameWorld.Pulser.submit(object : Pulse(0) { + var count = 0 + val npc = NPC.create(NPCs.KING_NARNODE_SHAREEN_670, Location.create(2467, 3496, 3), null) + override fun pulse(): Boolean { + when (count) { + 0 -> { + // Spawn in narnode + npc.init() + lock(player!!,10) + forceWalk(npc, player!!.location.transform(Direction.EAST, 2), "SMART") + } + + 2 -> { + sendNPCDialogue(player!!, npc.id,"Traveller, please accept my apologies! Glough had no right to arrest you! I just think he's scared of humans. Let me get you out of there.") + } + + 4 -> { + DoorActionHandler.handleAutowalkDoor(player!!, + getScenery(2465,3496,3) + ) + openDialogue(player!!,KingNarnodeUpstairsDialogue(), npc) + } + 8 -> { + unlock(player!!) + npc.clear() + setQuestStage(player!!, Quests.THE_GRAND_TREE, 55) + return true + } + } + count++ + return false + } + }) + } + } + } + 55 -> { + if(player!!.hasItem(Item(Items.LUMBER_ORDER_787))){ + when(stage) { + 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++ } + 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!!, Quests.THE_GRAND_TREE, 60) + stage = END_DIALOGUE + } + } + } else { + when(stage) { + 0 -> playerl("I can't figure this out Charlie!").also { stage++ } + 1 -> npcl("Go and see the foreman in the Karamja jungle, there's a shipyard there, you might find some clues. Don't forget the password is Ka-Lu-Min;").also { stage++ } + 2 -> npcl("Tf they realise that you're not working for Glough there'll be trouble!").also { stage = END_DIALOGUE } + + } + } + } else -> { + when(stage){ + 0 -> sendDialogue(player!!, "The prisoner is in no mood to talk.").also { 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 new file mode 100644 index 000000000..1977a47b8 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/grandtree/ForemanNPC.kt @@ -0,0 +1,140 @@ +package content.region.kandarin.quest.grandtree + +import content.data.Quests +import core.ServerConstants +import core.api.* +import core.game.component.Component +import core.game.dialogue.DialogueFile +import core.game.interaction.IntType +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 +import core.game.system.task.Pulse +import core.game.world.GameWorld +import core.game.world.map.RegionManager +import core.tools.END_DIALOGUE +import org.rs09.consts.Components + +@Initializable +class ForemanNPC(id: Int = 0, location: Location? = null) : AbstractNPC(id,location), InteractionListener { + + override fun construct(id: Int, location: Location?, vararg objects: Any?): AbstractNPC { + return ForemanNPC(id, location) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.FOREMAN_674) + } + + override fun defineListeners() { + on(this.ids, IntType.NPC, "talk-to"){ player, npc -> + openDialogue(player, ForemanDialogue(), npc) + return@on true + } + } + + override fun finalizeDeath(killer: Entity?) { + 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) + } + super.finalizeDeath(killer) + } +} + +class ForemanDialogue: DialogueFile(){ + private fun attackPlayer(){ + val foreman = RegionManager.getNpc(player!!.location, NPCs.FOREMAN_674, 6) + foreman!!.attack(player!!) + } + override fun handle(componentID: Int, buttonID: Int) { + when(stage){ + 0 -> playerl("Hello, are you in charge?").also { stage++ } + 1 -> npcl("That's right, and you are...?").also { stage++ } + 2 -> playerl("Glough sent me to check on how you are doing.").also { stage++ } + 3 -> npcl("Right. Glough sent a human?").also { stage++ } + 4 -> playerl("His gnomes are busy.").also { stage++ } + 5 -> npcl("Hmm... in that case we'd better go to my office. Follow me.").also { + val foremanOffice = Location.create(2954, 3024, 0) + GameWorld.Pulser.submit(object : Pulse(0) { + var count = 0 + override fun pulse(): Boolean { + when (count) { + 0 -> { + player!!.interfaceManager.closeOverlay() + player!!.interfaceManager.openOverlay(Component(Components.FADE_TO_BLACK_120)) + } + 2 -> { + teleport(player!!,foremanOffice) + player!!.interfaceManager.closeOverlay() + player!!.interfaceManager.openOverlay(Component(Components.FADE_FROM_BLACK_170)) + return true + } + } + count++ + return false + } + }) + stage++ + } + 6 -> npcl("Tell me again why you're here.").also { stage++ } + 7 -> playerl("Er... Glough sent me?").also { stage++ } + 8 -> npcl("By the way how is Glough? Still with his wife?").also { stage++ } + 9 -> options("Yes, they're getting on great.","Always arguing as usual!","Sadly his wife is no longer with us!").also { stage++ } + 10 -> when(buttonID){ + 1 -> playerl("Yes, they're getting on great.").also { stage++ } + 2 -> playerl("Always arguing as usual!").also { stage++ } + 3 -> playerl("Sadly his wife is no longer with us!").also { stage = 20 } + } + 11 -> npcl("Really? That's odd, considering she died last year. Die, imposter!").also { + attackPlayer() + stage = END_DIALOGUE + } + 20 -> npcl("Right answer. I have to watch for imposters. What's Glough's favourite dish?").also { stage++ } + 21 -> options("He loves tangled toads legs.","He loves worm holes.","He loves choc bombs.").also { stage++ } + 22 -> when(buttonID){ + 1 -> playerl("He loves tangled toads legs.").also { stage++ } + 2 -> playerl("He loves worm holes.").also { stage = 30 } + 3 -> playerl("He loves choc bombs.").also { stage++ } + } + 23 -> npcl("Our survey said.... Bzzzzzz! Wrong answer!").also { + attackPlayer() + stage = END_DIALOGUE + } + 30 -> npcl("OK. Just one more. What's the name of his new girlfriend?").also { stage++ } + 31 -> options("Anita.","Alia.","Elena.").also { stage++ } + 32 -> when(buttonID){ + 1 -> playerl("Anita.").also { stage = 35 } + 2 -> playerl("Alia.").also { stage++ } + 3 -> playerl("Elena.").also { stage++ } + } + 33 -> npcl("You almost had me fooled! Die, imposter!").also { + attackPlayer() + stage = END_DIALOGUE + } + 35 -> npcl("Well, well, you do know Glough. Sorry for the interrogation but I'm sure you understand.").also { stage++ } + 36 -> playerl("Of course, security is paramount.").also { stage++ } + 37 -> npcl("As you can see things are going well.").also { stage++ } + 38 -> playerl("Indeed.").also { stage++ } + 39 -> npcl("When I was asked to build a fleet large enough to invade Port Sarim and carry 300 gnome troops I said: 'If anyone can, I can.'").also { stage++ } + 40 -> playerl("That's a lot of troops!").also { stage++ } + 41 -> npcl("True, but if the gnomes are really going to take over " + ServerConstants.SERVER_NAME + " they'll need at least that.").also { stage++ } + 42 -> playerl("Take over?").also { stage++ } + 43 -> npcl("Of course, why else would Glough want 30 battleships? Between you and me I don't think he stands a chance.").also { stage++ } + 44 -> playerl("No?").also { stage++ } + 45 -> npcl("I mean, for the kind of battleships Glough's ordered I'll need tons and tons of lumber! Still, if he says he can supply the wood I'm sure he can! Anyway, here's the order for the lumber.").also { stage++ } + 46 -> sendDialogue(player!!, "The Foreman gives you the Lumber order.").also { + addItemOrDrop(player!!,Items.LUMBER_ORDER_787) + stage++ + } + 47 -> playerl("OK. I'll head off and give this order to Glough.").also { + stage = END_DIALOGUE + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/grandtree/GloughDialogue.kt b/Server/src/main/content/region/kandarin/quest/grandtree/GloughDialogue.kt new file mode 100644 index 000000000..496184c70 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/grandtree/GloughDialogue.kt @@ -0,0 +1,117 @@ +package content.region.kandarin.quest.grandtree + +import content.data.Quests +import core.api.* +import core.game.dialogue.DialogueFile +import core.game.dialogue.FacialExpression +import core.game.node.entity.npc.NPC +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.tools.END_DIALOGUE + +class GloughDialogue : DialogueFile() { + + override fun handle(componentID: Int, buttonID: Int) { + when (getQuestStage(player!!, Quests.THE_GRAND_TREE)) { + 40 -> { + when (stage) { + 0 -> playerl("Hello.").also { stage++ } + 1 -> sendDialogue(player!!,"The gnome is munching on a worm hole.").also { stage++ } + 2 -> npcl("Can I help human? Can't you see I'm eating?!").also { stage++ } + 3 -> sendDialogue(player!!,"The gnome continues to eat.").also { stage++ } + 4 -> playerl("The King asked me to inform you that the Daconia rocks have been taken!").also { stage++ } + 5 -> npcl("Surely not!").also { stage++ } + 6 -> playerl("Apparently a human took them from Hazelmere. Hazelmere believed him; he had the King's seal!").also { stage++ } + 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!!, Quests.THE_GRAND_TREE, 45) + stage = END_DIALOGUE + } + } + } + 47 -> { + when(stage){ + 0 -> playerl("Glough! I don't know what you're up to but I know you paid Charlie to get those rocks!").also { stage++ } + 1 -> npcl("You're a fool human! You have no idea what's going on.").also { stage++ } + 2 -> playerl("I know the Grand Tree's dying! And I think you're part of the reason.").also { stage++ } + 3 -> npcl("How dare you accuse me! I'm the head tree guardian! Guards! Guards!").also { stage++ } + 4 -> { + GameWorld.Pulser.submit(object : Pulse(0) { + var count = 0 + val npc = NPC.create(163,Location.create(2477, 3462, 1), null) + val ladderClimbAnimation = Animation(828) + val cell = Location.create(2464, 3496, 3) + override fun pulse(): Boolean { + when (count) { + 0 -> { + // Spawn in the gnome guard + lock(player!!, 10) + npc.init() + forceWalk(npc, player!!.location.transform(Direction.WEST,2), "SMART") + player!!.dialogueInterpreter.sendDialogues(npc, FacialExpression.ANNOYED, "Come with me!") + } + 2 -> { + // Walk to the ladder + forceWalk(npc, Location.create(2476, 3462, 1), "SMART") + } + 4 -> { + unlock(player!!) + forceWalk(player!!, Location.create(2477, 3463, 1), "SMART") + } + 6 -> { + player!!.faceLocation(Location.create(2476, 3463, 1)) + } + 7 -> { + player!!.animator.animate(ladderClimbAnimation) + } + 8 -> { + npc.clear() + setQuestStage(player!!, Quests.THE_GRAND_TREE, 50) + teleport(player!!, cell) + player!!.unlock() + return true + } + } + count++ + return false + } + }).also { stage = END_DIALOGUE } + } + } + } + 55 -> { + when(stage){ + 0 -> playerl("I know what you're up to Glough!").also { stage++ } + 1 -> npcl("You have no idea human!").also { stage++ } + 2 -> playerl("You may be able to make a fleet but the tree gnomes will never follow you into battle against humans.").also { stage++ } + 3 -> npcl("So, you know more than I thought! The gnomes fear humanity more than any other race. I just need to give them a push in the right direction. There's nothing you can do traveller! Leave before it's too late!").also { stage++ } + 4 -> playerl("King Narnode won't allow it!").also { stage++ } + 5 -> npcl("The King's a fool and a coward! He'll bow to me! You'll soon be back in that cage!").also { stage = END_DIALOGUE } + } + } + 60 -> { + when(stage){ + 0 -> playerl("I'm going to stop you, Glough!").also { stage++ } + 1 -> npcl("You're becoming quite annoying traveller!").also { stage++ } + 2 -> npcl("Glough is searching his pockets.").also { stage++ } + 3 -> npcl("Where are that darn key?").also { stage++ } + 4 -> npcl("Leave human, before I have you put in the cage!").also { stage = END_DIALOGUE } + } + } + else -> { + when (stage) { + 0 -> playerl("Hello there!").also { stage++ } + 1 -> npcl("You shouldn't be here human!").also { stage++ } + 2 -> playerl("What do you mean?").also { stage++ } + 3 -> npcl("The Gnome Stronghold is for gnomes alone!").also { stage++ } + 4 -> playerl("Surely not!").also { stage++ } + 5 -> npcl("We don't need your sort round here!").also { stage = END_DIALOGUE } + } + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/grandtree/GloughsJournal.kt b/Server/src/main/content/region/kandarin/quest/grandtree/GloughsJournal.kt new file mode 100644 index 000000000..e6c57f0cc --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/grandtree/GloughsJournal.kt @@ -0,0 +1,133 @@ +package content.region.kandarin.seers.quest.elementalworkshop + +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 + +/** + * Gloughs Journal book handler for The Grand Tree quest + * + * @author crop & szu & ovenbreado + */ +class GloughsJournal : InteractionListener { + companion object { + private val TITLE = "Glough's Journal" + private val CONTENTS = arrayOf( + PageSet( + Page( + BookLine("The migration failed!", 55), + BookLine("After spending half", 56), + BookLine("a century hiding", 57), + BookLine("underground you", 58), + BookLine("you would think", 59), + BookLine("that the great", 60), + BookLine("migration would have", 61), + BookLine("improved life on", 62), + BookLine("Gielinor for tree gnomes.", 63), + BookLine("However, rather", 64), + BookLine("than the great liberation", 65), + ), + Page( + BookLine("promised to us by", 65), + BookLine("King Healthorg at the", 66), + BookLine("end of the last age,", 67), + BookLine("we have been forced", 68), + BookLine("to live in hiding,", 69), + BookLine("up trees or in the", 70), + BookLine("gnome maze, laughed", 71), + BookLine("at and mocked by man.", 72), + BookLine("Living in constant", 73), + BookLine("fear of human aggression", 74), + BookLine("we are in a no better", 75), + ) + ), + PageSet( + Page( + BookLine("we are in a no better", 55), + BookLine("situation now than", 56), + BookLine("when we lived", 57), + BookLine("in the caves!", 58), + BookLine("Change must come soon!", 59), + BookLine("They must be stopped!", 60), + BookLine("Today I heard of three", 61), + BookLine("more gnomes slain", 62), + BookLine("by Khazard's human", 63), + BookLine("troops for fun,", 64), + BookLine("I can't control", 65), + ), + Page( + BookLine("my anger!", 66), + BookLine("Humanity seems to", 67), + BookLine("have acquired a", 68), + BookLine("level of arrogance", 69), + BookLine("comparable to that", 70), + BookLine("of Zamorak, killing", 71), + BookLine("and pillaging at will!", 72), + BookLine("We are small and at", 73), + BookLine("heart not warriors", 74), + BookLine("but something must", 75), + ) + ), + PageSet( + Page( + BookLine("be done! We will", 55), + BookLine("pick up arms and", 56), + BookLine("go forth into the", 57), + BookLine("human world! We will", 58), + BookLine("defend ourselves and", 59), + BookLine("we will pursue justice", 60), + BookLine("for all gnomes who", 61), + BookLine("fell at the hands", 62), + BookLine("of humans!", 63), + BookLine("Gaining support.", 64), + BookLine("", 65), + ), + Page( + BookLine("Some of the local", 66), + BookLine("gnomes seem strangely", 67), + BookLine("deluded about humans,", 68), + BookLine("many actually believe", 69), + BookLine("that humans are not", 70), + BookLine("all naturally evil", 71), + BookLine("but instead vary", 72), + BookLine("from person to person.", 73), + BookLine("This sort of talk", 74), + BookLine("could be the end", 75), + ) + ), + PageSet( + Page( + BookLine("for the tree gnomes", 55), + BookLine("and I must continue", 56), + BookLine("to convince my fellow", 57), + BookLine("gnome folk the cold", 58), + BookLine("truth about these", 59), + BookLine("human creatures!", 60), + BookLine("How they will not", 61), + BookLine("stop until all gnome", 62), + BookLine("life is destroyed!", 63), + BookLine("Unless we can ", 64), + BookLine("destroy them first!", 65), + ) + ) + ) + + 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.GLOUGHS_JOURNAL_785, 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/grandtree/GrandTreeListeners.kt b/Server/src/main/content/region/kandarin/quest/grandtree/GrandTreeListeners.kt new file mode 100644 index 000000000..8548efe96 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/grandtree/GrandTreeListeners.kt @@ -0,0 +1,222 @@ +package content.region.kandarin.quest.grandtree + +import core.api.* +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.GroundItemManager +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 org.rs09.consts.Items +import org.rs09.consts.NPCs +import org.rs09.consts.Sounds +import content.data.Quests + +class GrandTreeListeners: InteractionListener { + + val roots = arrayOf( + Location(2467,9896,0), + Location(2468,9890,0), + Location(2465,9891,0), + Location(2465,9891,0), + Location(2473,9897,0), + ) + + val hazelmerescroll = Items.HAZELMERES_SCROLL_786 + + val hazelmerescrollText = arrayOf( + "Es lemanto meso pro eis prit ta Cinqo mond.", + "Mi lovos ta lemanto Daconia arpos", + "et Daconia arpos eto meriz ta priw!", + ) + + fun unlockTUZODoor(player: Player) { + if (getAttribute(player, "/save:grandtree:twig1", false) && + getAttribute(player, "/save:grandtree:twig2", false) && + getAttribute(player, "/save:grandtree:twig3", false) && + getAttribute(player, "/save:grandtree:twig4", false) + ){ + sendDialogue(player,"With a grinding of machinery, a trapdoor snaps open!") + //SceneryBuilder.replace(Scenery(2444, Location(2487,3464,2), 22 , 2), Scenery(2445, Location(2487,3464,2), 22 , 2),20) + } + } + + override fun defineDestinationOverrides() { + setDest(IntType.NPC, intArrayOf(NPCs.CHARLIE_673),"talk-to"){ player, _ -> + return@setDest player.location + } + } + + override fun defineListeners() { + on(NPCs.KING_NARNODE_SHAREEN_670, IntType.NPC, "talk-to"){ player, npc -> + val aboveground = 9782 + if(player.location.regionId == aboveground) + openDialogue(player, KingNarnodeDialogue(), npc) + else + openDialogue(player, KingNarnodeUnderGroundDialogue(), npc) + return@on true + } + on(NPCs.HAZELMERE_669, IntType.NPC, "talk-to"){ player, npc -> + openDialogue(player, HazelmereDialogue(), npc) + return@on true + } + on(NPCs.GLOUGH_671, IntType.NPC, "talk-to"){ player, npc -> + openDialogue(player, GloughDialogue(), npc) + return@on true + } + on(intArrayOf(NPCs.SHIPYARD_WORKER_675, NPCs.SHIPYARD_WORKER_38, NPCs.SHIPYARD_WORKER_39), IntType.NPC, "talk-to"){ player, npc -> + openDialogue(player, ShipyardWorkerGenericDialogue(), npc) + return@on true + } + on(NPCs.CHARLIE_673, IntType.NPC, "talk-to"){ player, npc -> + openDialogue(player, CharlieDialogue(), npc) + return@on true + } + on(NPCs.CAPTAIN_ERRDO_3811, IntType.NPC, "talk-to"){ player, npc -> + openDialogue(player, CaptainErrdoDialogue(), npc) + return@on true + } + on(NPCs.ANITA_672, IntType.NPC, "talk-to"){ player, npc -> + openDialogue(player, AnitaDialogue(), npc) + return@on true + } + on(hazelmerescroll, IntType.ITEM, "read") { player, node -> + hazelmereScroll(player, node.asItem()) + return@on true + } + + on(2444, IntType.SCENERY, "open"){ player, node -> + 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) && + getAttribute(player, "/save:grandtree:twig4", false)){ + player.animator.animate(Animation(828)) + BlackDemonCutscene(player).start() + } + } + return@on true + } + + on(2446, IntType.SCENERY, "open"){ player, node -> + 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)) + } + return@on true + } + + onUseWith(IntType.SCENERY, 788, 2436){ player, used, with -> + 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!!, 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 -> + setAttribute(player, "/save:grandtree:twig1", true) + removeItem(player, used.asItem()) + GroundItemManager.create(used.asItem(), with.location, player) + unlockTUZODoor(player) + return@onUseWith true + } + onUseWith(IntType.SCENERY, Items.TWIGS_790, 2441){ player, used, with -> + setAttribute(player, "/save:grandtree:twig2", true) + removeItem(player, used.asItem()) + GroundItemManager.create(used.asItem(), with.location, player) + unlockTUZODoor(player) + return@onUseWith true + } + onUseWith(IntType.SCENERY, Items.TWIGS_791, 2442){ player, used, with -> + setAttribute(player, "/save:grandtree:twig3", true) + removeItem(player, used.asItem()) + GroundItemManager.create(used.asItem(), with.location, player) + unlockTUZODoor(player) + return@onUseWith true + } + onUseWith(IntType.SCENERY, Items.TWIGS_792, 2443){ player, used, with -> + setAttribute(player, "/save:grandtree:twig4", true) + removeItem(player, used.asItem()) + GroundItemManager.create(used.asItem(), with.location, player) + unlockTUZODoor(player) + return@onUseWith true + } + // Removing twigs + on(Items.TWIGS_789, IntType.GROUNDITEM, "take"){ player, node -> + setAttribute(player, "/save:grandtree:twig1", false) + return@on true + } + on(Items.TWIGS_790, IntType.GROUNDITEM, "take"){ player, node -> + setAttribute(player, "/save:grandtree:twig2", false) + return@on true + } + on(Items.TWIGS_791, IntType.GROUNDITEM, "take"){ player, node -> + setAttribute(player, "/save:grandtree:twig3", false) + return@on true + } + on(Items.TWIGS_792, IntType.GROUNDITEM, "take"){ player, node -> + setAttribute(player, "/save:grandtree:twig4", false) + return@on true + } + on(2435, IntType.SCENERY, "search"){ player, _ -> + 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) + } + return@on true + } + + // Roots for Daconia rock + on(32319, IntType.SCENERY, "search"){ player, node -> + 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!") + addItemOrDrop(player,Items.DACONIA_ROCK_793) + } + return@on true + } + // Gate Karamja + on(2439, IntType.SCENERY, "open"){ player, _ -> + openDialogue(player, ShipyardWorkerDialogue(), NPC(NPCs.SHIPYARD_WORKER_675)) + return@on true + } + on(2438, IntType.SCENERY, "open"){ player, _ -> + openDialogue(player, ShipyardWorkerDialogue(), NPC(NPCs.SHIPYARD_WORKER_675)) + return@on true + } + on(2451, IntType.SCENERY, "push"){ player, roots -> + 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) + } else { + forceMove(player, player.location, player.location.transform(0, -2, 0), 25, 60, null, 819) + } + animate(player, 2572, false) + animateScenery(roots.asScenery(), 452) + playAudio(player, Sounds.TANGLEVINE_APPEAR_2316) + } + return@on true + } + } + + private fun hazelmereScroll(player: Player, item: Item) { + val id = item.id + openInterface(player, 222).also { + when (id) { + hazelmerescroll -> setInterfaceText(player, hazelmerescrollText.joinToString("
"), 222, 6) + } + } + } +} + diff --git a/Server/src/main/content/region/kandarin/quest/grandtree/HazelmereDialogue.kt b/Server/src/main/content/region/kandarin/quest/grandtree/HazelmereDialogue.kt new file mode 100644 index 000000000..ab59a6ba6 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/grandtree/HazelmereDialogue.kt @@ -0,0 +1,47 @@ +package content.region.kandarin.quest.grandtree + +import content.data.Quests +import core.api.* +import core.game.dialogue.DialogueFile +import core.game.node.item.Item +import core.tools.END_DIALOGUE +import org.rs09.consts.Items + +class HazelmereDialogue : DialogueFile() { + + override fun handle(componentID: Int, buttonID: Int) { + when (getQuestStage(player!!, Quests.THE_GRAND_TREE)) { + 10 -> { + if(player!!.hasItem(Item(Items.BARK_SAMPLE_783))){ + when (stage) { + 0 -> sendDialogue(player!!,"The mage starts to speak but all you hear is").also { stage++ } + 1 -> npcl("Blah. Blah, blah, blah, blah...blah!").also { stage++ } + 2 -> sendDialogue(player!!,"You give the bark sample to Hazelmere. The mage carefully examines the sample.").also { stage++ } + 3 -> npcl("Blah, blah...Daconia...blah, blah.").also { stage++ } + 4 -> playerl("Can you write this down and I'll try and translate it?").also { stage++ } + 5 -> npcl("Blah, blah?").also { stage++ } + 6 -> sendDialogue(player!!,"You make a writing motion. The mages scribbles something down on a scroll. Hazelmere has given you the scroll.").also { + if(removeItem(player!!, Items.BARK_SAMPLE_783)){ + addItemOrDrop(player!!, Items.HAZELMERES_SCROLL_786) + } + setQuestStage(player!!, Quests.THE_GRAND_TREE, 20) + stage = END_DIALOGUE + } + } + } + } + 20 -> { + when (stage) { + 0 -> npcl("Blah, blah....Daconia...blah, blah.").also { stage++ } + 1 -> sendDialogue(player!!,"You still can't understand Hazelmere. The mage wrote it down for you on a scroll.").also { + if(!player!!.hasItem(Item(Items.HAZELMERES_SCROLL_786))){ + addItemOrDrop(player!!, Items.HAZELMERES_SCROLL_786) + } + stage = END_DIALOGUE + } + } + } + else -> npcl("Blah, blah...blah, blah.").also { stage = END_DIALOGUE } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/grandtree/KingNarnodeDialogue.kt b/Server/src/main/content/region/kandarin/quest/grandtree/KingNarnodeDialogue.kt new file mode 100644 index 000000000..5cbf67861 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/grandtree/KingNarnodeDialogue.kt @@ -0,0 +1,507 @@ +package content.region.kandarin.quest.grandtree + +import content.data.Quests +import core.api.* +import core.game.dialogue.DialogueFile +import core.game.interaction.MovementPulse +import core.game.node.entity.impl.PulseType +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.Direction +import core.game.world.map.Location +import core.game.world.map.RegionManager +import core.game.world.map.path.Pathfinder +import core.game.world.update.flag.context.Animation +import core.tools.END_DIALOGUE +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +class KingNarnodeDialogue : DialogueFile() { + + val trapdoorLocation = Location(2464,3497,0) + val closedTrapDoor = Scenery(2446, Location(2463,3497,0), 22, 0) + val openedTrapDoor = Scenery(2445, Location(2463,3497,0), 22, 0) + val tunnels = Location(2464, 9897, 0) + val ladderClimbAnimation = Animation(828) + + fun leadDownLadder(){ + // Animate Narnode to walk to the trapdoor and climb the ladder + GameWorld.Pulser.submit(object : Pulse(0) { + var count = 0 + override fun pulse(): Boolean { + when (count) { + 0 -> { + forceWalk(player!!, trapdoorLocation, "SMART") + lock(player!!,10) + npc!!.pulseManager.run(object : MovementPulse(npc, player, Pathfinder.SMART) { + override fun pulse(): Boolean { + return false + } + }, PulseType.STANDARD) + npc!!.sendChat("Down here!") + SceneryBuilder.replace(closedTrapDoor,openedTrapDoor,10) + } + 4 -> { + player!!.faceLocation(trapdoorLocation.transform(Direction.WEST, 1)) + player!!.animator.animate(ladderClimbAnimation) + } + 5 -> { + teleport(player!!, tunnels) + npc!!.pulseManager.clear() + } + 7 -> { + // We are now underground + val undergroundNarnode = RegionManager.getNpc(player!!.location, NPCs.KING_NARNODE_SHAREEN_670, 16) + forceWalk(undergroundNarnode!!,player!!.location.transform(Direction.EAST,1), "SMART") + unlock(player!!) + openDialogue(player!!, KingNarnodeUnderGroundDialogue(), undergroundNarnode) + } + 10 -> return true + } + count++ + return false + } + }) + } + override fun handle(componentID: Int, buttonID: Int) { + 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++ } + 1 -> playerl("Hi! It seems to be a very busy settlement.").also{ stage++ } + 2 -> npcl("For now.").also { stage++ } + 3 -> options("You seem worried, what's up?","I'll be off now.").also { stage++ } + 4 -> when (buttonID) { + 1 -> playerl("You seem worried, what's up?").also { stage = 5 } + 2 -> playerl("I'll be off now.").also { stage = 25 } + } + 5 -> npcl("Traveller, can I speak to you in strictest confidence?").also { stage++ } + 6 -> playerl("Of course sire.").also { stage++ } + 7 -> npcl("Not here, follow me.").also { + stage = END_DIALOGUE + // Animate Narnode to walk to the trapdoor and climb the ladder + leadDownLadder() + } + 25 -> npcl("Enjoy your stay with us. There are many things to see in my kingdom.").also { stage = END_DIALOGUE } + } + } + 10 -> { + when (stage) { + 0 -> { + // Did the player destroy/drop the quest items + if(!player!!.hasItem(Item(Items.BARK_SAMPLE_783)) || !player!!.hasItem(Item(Items.TRANSLATION_BOOK_784))) { + npcl("You dropped something I gave you earlier. Please take better care of my thing!") + if(!player!!.hasItem(Item(Items.BARK_SAMPLE_783))) { + addItemOrDrop(player!!,Items.BARK_SAMPLE_783) + } + if(!player!!.hasItem(Item(Items.TRANSLATION_BOOK_784))) { + addItemOrDrop(player!!,Items.TRANSLATION_BOOK_784) + } + stage = END_DIALOGUE + } else { + npcl("Traveller, any word from Hazelmere?") + stage++ + } + } + 1 -> playerl("Not yet.").also { stage = END_DIALOGUE } + } + } + 20 -> { + when (stage) { + 0 -> npcl("Welcome Traveller. I am King Narnode. It's nice to see an outsider.").also { + // Reset correct answered options + setAttribute(player!!, "/save:grandtree:opt1", false) + setAttribute(player!!, "/save:grandtree:opt2", false) + setAttribute(player!!, "/save:grandtree:opt3", false) + stage++ + } + 1 -> playerl("Hello again, Your Highness.").also{ stage++ } + 2 -> npcl("Hello Traveller, did you speak to Hazelmere?").also { stage++ } + 3 -> playerl("Yes! I managed to find him.").also{ stage++ } + 4 -> npcl("Do you understand what he said?").also { stage++ } + 5 -> options("I think so!","No, I need to go back.").also { stage++ } + 6 -> when (buttonID) { + 1 -> playerl("I think so!").also { stage = 10 } + 2 -> playerl("No, I need to go back.").also { stage = END_DIALOGUE } + } + 10 -> npcl("So what did he say?").also { stage++ } + 11 -> options("King Narnode must be stopped, he is a madman!","Praise be to the great Zamorak!", "Do you have any bread? I do like bread.", "The time has come to attack!","None of the above.").also { stage++ } + 12 -> when (buttonID) { + 1 -> playerl("King Narnode must be stopped, he is a madman!").also { stage++ } + 2 -> playerl("Praise be to the great Zamorak!").also { stage++ } + 3 -> playerl("Do you have any bread? I do like bread.").also { stage++ } + 4 -> playerl("The time has come to attack!").also { stage++ } + 5 -> options("The tree is fine, you have nothing to fear.","You must come and see me!", "The tree needs watering as there has been drought.","Grave danger lies ahead, only the bravest will linger.","None of the above.").also { stage = 20 } + } + 13 -> options("The tree is fine, you have nothing to fear.","You must come and see me!", "The tree needs watering as there has been drought.","Grave danger lies ahead, only the bravest will linger.","None of the above.").also { stage = 20 } + 20 -> when (buttonID) { + 1 -> playerl("The tree is fine, you have nothing to fear.").also { stage++ } + 2 -> playerl("You must come and see me!").also { stage++ } + 3 -> playerl("The tree needs watering as there has been drought.").also { stage++ } + 4 -> playerl("Grave danger lies ahead, only the bravest will linger.").also { stage++ } + 5 -> options("Time is of the essence! We must move quickly.","There is no need for haste, just send a runner.", "A man came to me with the King's seal.","You must act now, or we will all die!","None of the above.").also { stage = 30 } + } + 21 -> options("Time is of the essence! We must move quickly.","There is no need for haste, just send a runner.", "Time passes us by.","You must act now, or we will all die!","None of the above.").also { stage = 31 } + // Right + 30 -> when (buttonID) { + 1 -> playerl("Time is of the essence! We must move quickly.").also { stage = 33 } + 2 -> playerl("There is no need for haste, just send a runner.").also { stage = 33 } + 3 -> playerl("A man came to me with the King's seal.").also { + setAttribute(player!!, "/save:grandtree:opt1", true) + stage = 32 + } + 4 -> playerl("You must act now, or we will all die!").also { stage = 33 } + 5 -> options("Only Adamantite is any use.","You must use force!", "Use a bucket of milk from a scared cow.","Take this banana to him, he will understand.","None of the above.").also { stage = 41 } + } + // Wrong + 31 -> when (buttonID) { + 1 -> playerl("Time is of the essence! We must move quickly.").also { stage = 33 } + 2 -> playerl("There is no need for haste, just send a runner.").also { stage = 33 } + 3 -> playerl("Time passes us by.").also { stage = 33 } + 4 -> playerl("You must act now, or we will all die!").also { stage = 33 } + 5 -> options("Only Adamantite is any use.","You must use force!", "Use a bucket of milk from a scared cow.","Take this banana to him, he will understand.","None of the above.").also { stage = 41 } + } + 32 -> options("I gave the man Daconia rocks.","You must use force!", "Use a bucket of milk from a scared cow.","Take this banana to him, he will understand.","None of the above.").also { stage = 40 } + 33 -> options("Only Adamantite is any use.","You must use force!", "Use a bucket of milk from a scared cow.","Take this banana to him, he will understand.","None of the above.").also { stage = 41 } + // Right + 40 -> when (buttonID) { + 1 -> playerl("I gave the man Daconia rocks.").also { + setAttribute(player!!, "/save:grandtree:opt2", true) + stage = 42 + } + 2 -> playerl("You must use force!").also { stage = 43 } + 3 -> playerl("Use a bucket of milk from a scared cow.").also { stage = 43 } + 4 -> playerl("Take this banana to him, he will understand.").also { stage = 43 } + 5 -> options("All with be fine on the third night.","You must wait till the second night.", "Nothing will help us now!","The tree will die in five days!").also { stage = 51 } + } + // Wrong + 41 -> when (buttonID) { + 1 -> playerl("Only Adamantite is any use.").also { stage = 43 } + 2 -> playerl("You must use force!").also { stage = 43 } + 3 -> playerl("Use a bucket of milk from a scared cow.").also { stage = 43 } + 4 -> playerl("Take this banana to him, he will understand.").also { stage = 43 } + 5 -> options("All with be fine on the third night.","You must wait till the second night.", "Nothing will help us now!","The tree will die in five days!").also { stage = 51 } + } + 42 -> options("All with be fine on the third night.","You must wait till the second night.", "Nothing will help us now!","And Daconia rocks will kill the tree!").also { stage = 50 } + 43 -> options("All with be fine on the third night.","You must wait till the second night.", "Nothing will help us now!","The tree will die in five days!").also { stage = 51 } + // Right + 50 -> when (buttonID) { + 1 -> playerl("All with be fine on the third night.").also { stage = 52 } + 2 -> playerl("You must wait till the second night.").also { stage = 52 } + 3 -> playerl("Nothing will help us now!").also { stage = 52 } + 4 -> playerl("And Daconia rocks will kill the tree!").also { + setAttribute(player!!, "/save:grandtree:opt3", true) + stage = 52 + } + } + // Wrong + 51 -> when (buttonID) { + 1 -> playerl("All with be fine on the third night.").also { stage++ } + 2 -> playerl("You must wait till the second night.").also { stage++ } + 3 -> playerl("Nothing will help us now!").also { stage++ } + 4 -> playerl("The tree will die in five days!").also { stage++ } + } + 52 -> { + if(getAttribute(player!!, "/save:grandtree:opt1", false) + && getAttribute(player!!, "/save:grandtree:opt2", false) + && getAttribute(player!!, "/save:grandtree:opt3", false)){ + npcl("Of course! I should've known! Someone must've forged my royal seal. Hazelmere thought I sent him for the Daconia stones!").also { stage = 60 } + } else { + npcl("Wait a minute! That doesn't sound like Hazelmere! Are you sure you translated correctly?").also { stage++ } + } + } + 53 -> playerl("Erm...I think so.").also { stage++ } + 54 -> npcl(" I'm sorry Traveller but this is no good. The translation must be perfect or the information's no use. Please come back when you know exactly what Hazelmere said.").also { stage = END_DIALOGUE } + 60 -> playerl("What are Daconia stones?").also { stage++ } + 61 -> npcl("Hazelmere created the Daconia stones. They are a safety measure, in case the tree grew out of control. They're the only thing that can kill the tree.").also { stage++ } + 62 -> npcl("This is terrible! The stones must be recovered!").also { stage++ } + 63 -> playerl("Can I help?").also { stage++ } + 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!!, Quests.THE_GRAND_TREE, 40) + stage = END_DIALOGUE + } + } + } + 40 -> { + 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 } + } + } + 45 -> { + when(stage) { + 0 -> playerl("Hello, Your Highness. Have you any news on the Daconia stones?").also { stage++ } + 1 -> npcl("It's OK Traveller, thanks to Glough! He found a human sneaking around! He had three Daconia rocks on him!").also { stage++ } + 2 -> playerl("Wow! That was quick!").also { stage++ } + 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!!, Quests.THE_GRAND_TREE, 46) + stage = END_DIALOGUE + } + } + } + 46 -> { + when(stage){ + 0 -> npcl("Hello Traveller. If you wish to talk to the prisoner go to the top of the tree, you'll find him there.").also { stage++ } + 1 -> playerl("Thanks.").also { stage = END_DIALOGUE } + } + } + 55 -> { + when(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++ } + 4 -> playerl("His hatred for humanity is stronger than you know!").also { stage++ } + 5 -> npcl("That's enough Traveller, you sound as paranoid as him! Traveller please leave! It's bad enough having one human locked up.").also { stage = END_DIALOGUE } + } + } + 60 -> { + if(player!!.hasItem(Item(Items.INVASION_PLANS_794))){ + when(stage){ + 0 -> playerl("Hi, Your Highness, did you think about what I said?").also { stage++ } + 1 -> npcl("Look, if you're right about Glough I would have him arrested but there's no reason for me to think he's lying.").also { stage++ } + 2 -> playerl("Look, I found this at Glough's home!").also { stage++ } + 3 -> sendDialogue(player!!, "You give the King the invasion plans.").also { stage++ } + 4 -> npcl("If these are to be believed then this is terrible! But it's not proof, anyone could have made these. Traveller, I understand your concern.").also { stage++ } + 5 -> npcl("I had guards search Glough's house but they found nothing suspicious, just these odd twigs.").also { stage++ } + 6 -> sendDialogue(player!!, "The King has given you some twigs lashed together.").also{ + addItemOrDrop(player!!, Items.TWIGS_789) + addItemOrDrop(player!!, Items.TWIGS_790) + addItemOrDrop(player!!, Items.TWIGS_791) + addItemOrDrop(player!!, Items.TWIGS_792) + stage++ + } + 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!!, Quests.THE_GRAND_TREE, 70) + removeItem(player!!, Item(Items.INVASION_PLANS_794), Container.INVENTORY) + stage = END_DIALOGUE + } + } + } else { + when(stage){ + 0 -> playerl("Hello, Your Highness.").also { stage++ } + 1 -> npcl("Please Traveller, if the gnomes see me talking to you they'll revolt against me.").also { stage++ } + 2 -> playerl("That's crazy!").also { stage++ } + 3 -> npcl("Glough's scared the whole town, he expects the humans to attack any day. He's even begun to recruit hundreds of gnome soldiers.").also { stage++ } + 4 -> playerl("Don't you understand he's creating his own army?!").also { stage++ } + 5 -> npcl("Please Traveller, leave before it's too late!").also { stage = END_DIALOGUE } + } + } + } + 70 -> { + when(stage){ + 0 -> npcl("Please Traveller, take my advice and leave!").also { + if(!player!!.hasItem(Item(Items.TWIGS_789)) || !player!!.hasItem(Item(Items.TWIGS_790)) || !player!!.hasItem(Item(Items.TWIGS_791)) || !player!!.hasItem(Item(Items.TWIGS_792))) { + stage++ + } else { + stage = END_DIALOGUE + } + } + 1 -> playerl("I've lost those twigs you gave me.").also{ stage++ } + 2 -> { + if(player!!.inventory.freeSlots() < 4) { + npcl("Hmm. Looks like you can't carry anymore. Put 4 things down and come back to me.").also { stage = END_DIALOGUE } + } else { + npcl("King Narnode gives you 4 twigs.").also { + addItemOrDrop(player!!, Items.TWIGS_789) + addItemOrDrop(player!!, Items.TWIGS_790) + addItemOrDrop(player!!, Items.TWIGS_791) + addItemOrDrop(player!!, Items.TWIGS_792) + stage = END_DIALOGUE + } + } + } + } + } + 98,99 -> { + leadDownLadder() + } + 100 -> { + when (stage) { + 0 -> npcl("Thanks again for your help!").also { stage = END_DIALOGUE } + } + } + } + } +} + +class KingNarnodeUpstairsDialogue : DialogueFile(){ + override fun handle(componentID: Int, buttonID: Int) { + when(stage){ + 0 -> playerl("I don't think you can trust Glough, Your Highness. He seems to have an unnatural hatred for humans.").also { stage++ } + 1 -> npcl("I know he can be a bit extreme at times. But he's the best tree guardian I have, he has made the gnomes paranoid about humans though.").also { stage++ } + 2 -> npcl("I'm afraid Glough has placed guards on the front gate to stop you escaping! Let my glider pilot fly you away until things calm down around here.").also { stage++ } + 3 -> playerl("Well, OK.").also { stage++ } + 4 -> npcl("I'm sorry again Traveller!").also { stage = END_DIALOGUE } + } + } + +} + +class KingNarnodeUnderGroundDialogue : DialogueFile() { + private fun leadUpLadder(){ + val ladderLoc = Location.create(2464, 9897, 0) + val ladderClimbAnimation = Animation(828) + val ladderExit = Location.create(2464, 3497, 0) + GameWorld.Pulser.submit(object : Pulse(0) { + var count = 0 + override fun pulse(): Boolean { + when (count) { + 0 -> { + forceWalk(player!!, ladderLoc, "SMART") + lock(player!!,10) + npc!!.pulseManager.run(object : MovementPulse(npc, player, Pathfinder.SMART) { + override fun pulse(): Boolean { + return false + } + }, PulseType.STANDARD) + npc!!.sendChat("Up here!") + } + 4 -> { + player!!.faceLocation(ladderLoc.transform(Direction.WEST, 1)) + player!!.animator.animate(ladderClimbAnimation) + } + 5 -> { + teleport(player!!, ladderExit) + npc!!.pulseManager.clear() + unlock(player!!) + } + 10 -> return true + } + count++ + return false + } + }) + } + override fun handle(componentID: Int, buttonID: Int) { + 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++ } + 2 -> npcl("What?! Glough?! With a demon?!").also { stage++ } + 3 -> playerl("Glough has a store of Daconia rocks further up the passage! He's been accessing the roots from a secret passage at his home.").also { stage++ } + 4 -> npcl("Never! Not Glough! He's a good gnome at heart! Guard!").also { stage++ } + 5 -> sendNPCDialogue(player!!, NPCs.GNOME_GUARD_163,"Sire!").also{ stage++ } + 6 -> npcl("Go and check out that passage!").also { stage++ } + 7 -> sendNPCDialogue(player!!, NPCs.GNOME_GUARD_163,"We found Glough hiding under a horde of Daconia rocks!").also{ stage++ } + 8 -> playerl("That's what I've been trying to tell you! Glough's been fooling you!").also { stage++ } + 9 -> npcl("I... I don't know what to say! How could I have been so blind?! Guard! Call off the military training! The humans are not attacking!").also { stage++ } + 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!!, Quests.THE_GRAND_TREE, 99) + // position of the daconia rock + if(getAttribute(player!!,"treegnome:rock",0) == 0){ + val answer = (1..5).random() + setAttribute(player!!,"/save:treegnome:rock",answer) + } + stage = END_DIALOGUE + } + } + + 99 -> { + if(player!!.hasItem(Item(Items.DACONIA_ROCK_793))){ + when(stage){ + 0 -> npcl("Traveller, have you managed to find the Daconia?").also { stage++ } + 1 -> playerl("Is this it?").also { stage++ } + 2 -> npcl("Yes! Excellent, well done!").also { stage++ } + 3 -> sendDialogue(player!!,"You give the King the Daconia rock.").also { stage++ } + 4 -> npcl("It's incredible, the tree's health is improving already! I don't know what to say, we owe you so much! To think Glough had me fooled all along!").also { stage++ } + 5 -> playerl("All that matters now is that humans and gnomes can live together in peace!").also { stage++ } + 6 -> npcl("I'll drink to that! From now on I vow to make this stronghold a welcoming place for all! I'll grant you access to all our facilities.").also { stage++ } + 7 -> playerl("Thanks! I think!").also { stage++ } + 8 -> npcl("It should make your stay here easier. You can use the spirit tree to transport yourself, as well as the gnome glider. I also give you access to our mine.").also { stage++ } + 9 -> playerl("Mine?").also { stage++ } + 10 -> npcl("Very few know of the secret mine under the Grand Tree. If you push on the roots just to my north they will separate and let you pass.").also { stage++ } + 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!!, Quests.THE_GRAND_TREE) + removeItem(player!!, Items.DACONIA_ROCK_793) + stage = END_DIALOGUE + } + } + } else { + when (stage) { + 0 -> npcl("Traveller, have you managed to find the Daconia?").also { stage++ } + 1 -> playerl("No sign of it so far.").also { stage++ } + 2 -> npcl("The tree will still die if we don't find it! It could be anywhere!").also { stage++ } + 3 -> playerl("Don't worry, Your Highness! We'll find it!").also { stage = END_DIALOGUE } + } + } + } + 100 -> { + when (stage) { + 0 -> npcl("Thanks again for your help!").also { stage = END_DIALOGUE } + } + } + + else -> when (stage) { + 0 -> npcl("Down here.").also { stage++ } + 1 -> playerl("So what is this place?").also { stage++ } + 2 -> npcl("These, my friend, are the foundations of the stronghold.").also { stage++ } + 3 -> playerl("They look like roots to me.").also { stage++ } + 4 -> npcl("Not just any roots Traveller! These were created by gnome mages eons ago, since then they have grown to become a mighty stronghold!").also { stage++ } + 5 -> playerl("Impressive. What exactly is the problem?").also { stage++ } + 6 -> npcl("In the last two months our tree guardians have reported continuing deterioration of the Grand Tree's health. I've never seen this before! It could be the end for us all!").also { stage++ } + 7 -> playerl("You mean the tree is ill?").also { stage++ } + 8 -> npcl(" In effect yes. Would you be willing to help us discover what is happening to the tree?").also { stage++ } + 9 -> options("I'm sorry, I don't want to get involved.", "I'd be happy to help!").also { stage++ } + 10 -> when (buttonID) { + 1 -> playerl("I'm sorry, I don't want to get involved.").also { stage = 15 } + 2 -> playerl("I'd be happy to help!").also { stage = 20 } + } + + 15 -> npcl("I understand Traveller. Please keep this to yourself.").also { stage++ } + 16 -> playerl("Of course.").also { stage++ } + 17 -> npcl("I'll show you the way back up.").also { stage++ } + 18 -> { + npcl("Up here.") + leadUpLadder() + stage = END_DIALOGUE + } + + 20 -> npcl("Thank Guthix for your arrival! The first task is to find out what's killing the tree.").also { stage++ } + 21 -> playerl("Do you have an idea?").also { stage++ } + 22 -> npcl("My top tree guardian, Glough, believes it's human sabotage. I'm not so sure! The only way to know for sure is to talk to Hazelmere.").also { stage++ } + 23 -> playerl("Who's Hazelmere?").also { stage++ } + 24 -> npcl("Hazelmere is one of the mages that created the Grand Tree. He is the only one that has survived from that time. Take this bark sample to him, he will be able to help!").also { stage++ } + 25 -> sendItemDialogue( + player!!, + Items.BARK_SAMPLE_783, + "The king shows you a sample of bark." + ).also { stage++ } + + 26 -> npcl("The mage only talks in the old tongue, you'll need this.").also { stage++ } + 27 -> sendItemDialogue( + player!!, + Items.TRANSLATION_BOOK_784, + "The king shows you a translation book." + ).also { stage++ } + + 28 -> playerl("What is it?").also { stage++ } + 29 -> npcl("It's a translation book, you'll need it to translate what Hazelmere says. Do that carefully! His words are our only hope!").also { stage++ } + 30 -> npcl("You'll find his dwellings high upon a towering hill, on an island east of Yanille. I'll show you the way back up.").also { stage++ } + 31 -> if (player!!.inventory.freeSlots() >= 2) { + npcl("Up here.") + stage = END_DIALOGUE + setQuestStage(player!!, Quests.THE_GRAND_TREE, 10) + addItemOrDrop(player!!, Items.BARK_SAMPLE_783) + addItemOrDrop(player!!, Items.TRANSLATION_BOOK_784) + leadUpLadder() + } else { + npcl("You don't have inventory space for my book or the bark! Clear some space and speak to me again") + }.also { stage = END_DIALOGUE } + } + } + } +} diff --git a/Server/src/main/content/region/kandarin/quest/grandtree/ShipyardWorkerDialogue.kt b/Server/src/main/content/region/kandarin/quest/grandtree/ShipyardWorkerDialogue.kt new file mode 100644 index 000000000..1487b306c --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/grandtree/ShipyardWorkerDialogue.kt @@ -0,0 +1,118 @@ +package content.region.kandarin.quest.grandtree + +import content.data.Quests +import core.api.getAttribute +import core.api.getQuestStage +import core.api.setAttribute +import core.game.dialogue.DialogueFile +import core.game.global.action.DoorActionHandler +import core.game.node.scenery.Scenery +import core.game.world.map.Location +import core.tools.END_DIALOGUE + +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!!, Quests.THE_GRAND_TREE) == 55) { + setAttribute(player!!, "/save:grandtree:opt1", false) + setAttribute(player!!, "/save:grandtree:opt2", false) + setAttribute(player!!, "/save:grandtree:opt3", false) + } + stage++ + } + 1 -> playerl("I'm trying to open the gate!").also { stage++ } + 2 -> npcl("I can see that! Why?").also { stage++ } + 3 -> options("I'm from the Ministry of Health and Safety.","Glough sent me.","I'm just looking around.").also { stage++ } + 4 -> when(buttonID){ + 1 -> playerl("I'm from the Ministry of Health and Safety.").also { stage = 40} + 2 -> playerl("Glough sent me.").also{ stage = 50} + 3 -> playerl("I'm just looking around.").also { stage = 10 } + } + 10 -> playerl("I'm just looking around.").also { stage++ } + 11 -> npcl("This ain't a museum! Leave now!").also { stage++ } + 12 -> playerl("I'll leave when I choose!").also { stage++ } + 13 -> npcl("Well you're not on the list so you're not coming in. Go away.").also { stage++ } + 14 -> playerl("Well I'll just stand here then until you let me in.").also { stage++ } + 15 -> npcl("You do that!").also { stage++ } + 16 -> playerl("I will!").also { stage++ } + 17 -> npcl("Yeah?").also { stage++ } + 18 -> playerl("Yeah!").also { stage++ } + 19 -> npcl("...").also { stage++ } + 20 -> playerl("...").also { stage++ } + 21 -> playerl("So are you going to let me in then?").also { stage++ } + 22 -> npcl("No.").also { stage++ } + 23 -> playerl("...").also { stage++ } + 24 -> npcl("...").also { stage++ } + 25 -> playerl("You bored yet?").also { stage++ } + 26 -> npcl("No, I can stand here all day.").also { stage++ } + 27 -> playerl("...").also { stage++ } + 28 -> npcl("...").also { stage++ } + 29 -> playerl("Alright you win. I'll find another way in.").also { stage++ } + 30 -> npcl("No you won't.").also { stage++ } + 31 -> playerl("Yes I will.").also { stage++ } + 32 -> npcl("I'm not starting that again. Maybe if I ignore you you'll go away...").also { stage = + END_DIALOGUE + } + 40 -> npcl("Never 'erd of 'em.").also { stage++ } + 41 -> playerl("You will respect my authority!").also { stage++ } + 42 -> npcl("Get out of here before I give you a beating!").also { stage = END_DIALOGUE } + 50 -> npcl("Hmm... really? What for?").also { stage++ } + 51 -> playerl("You're wasting my time! Take me to your superior!").also { stage++ } + 52 -> npcl("OK. Password.").also { stage++ } + 53 -> options("Ka.","Ko.","Ke.").also { stage++ } + 54 -> when(buttonID){ + 1 -> playerl("Ka.").also { + setAttribute(player!!, "/save:grandtree:opt1", true) + stage++ + } + 2 -> playerl("Ko.").also { stage++ } + 3 -> playerl("Ke.").also { stage++ } + } + 55 -> options("Lo.","Lu.","Le.").also { stage++ } + 56 -> when(buttonID){ + 1 -> playerl("Lo.").also { stage++ } + 2 -> playerl("Lu.").also { + setAttribute(player!!, "/save:grandtree:opt2", true) + stage++ + } + 3 -> playerl("Le.").also { stage++ } + } + 57 -> options("Mon.","Min.","Men.").also { stage++ } + 58 -> when(buttonID){ + 1 -> playerl("Mon.").also { stage++ } + 2 -> playerl("Min.").also { + setAttribute(player!!, "/save:grandtree:opt3", true) + stage++ + } + 3 -> playerl("Men.").also { stage++ } + } + // Correct answer Ka-Lu-Min: + 59 -> { + if(getAttribute(player!!, "/save:grandtree:opt1", false) + && getAttribute(player!!, "/save:grandtree:opt2", false) + && getAttribute(player!!, "/save:grandtree:opt3", false) + ) { + DoorActionHandler.autowalkFence(player!!, Scenery(2438, Location(2945, 3041, 0)), 3727, 3728) + npcl("Sorry to have kept you.").also { stage = END_DIALOGUE } + } else { + npcl("You have no idea!").also { stage = END_DIALOGUE } + } + } + } + } +} + +class ShipyardWorkerGenericDialogue: DialogueFile(){ + override fun handle(componentID: Int, buttonID: Int) { + when(stage){ + 0 -> playerl("Hello.").also { stage++ } + 1 -> npcl("Hello matey!").also { stage++ } + 2 -> playerl("How are you?").also { stage++ } + 3 -> npcl("Tired!").also { stage++ } + 4 -> playerl("You shouldn't work so hard!").also { stage = END_DIALOGUE } + } + } + +} + diff --git a/Server/src/main/content/region/kandarin/quest/grandtree/TheGrandTree.kt b/Server/src/main/content/region/kandarin/quest/grandtree/TheGrandTree.kt new file mode 100644 index 000000000..c1b74008f --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/grandtree/TheGrandTree.kt @@ -0,0 +1,99 @@ +package content.region.kandarin.quest.grandtree + +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(Quests.THE_GRAND_TREE, 71, 70, 5, 150, 0, 1, 160) { + override fun newInstance(`object`: Any?): Quest { + return this + } + + override fun finish(player: Player?) { + super.finish(player) + player ?: return + var ln = 10 + player.packetDispatch.sendItemZoomOnInterface(Items.DACONIA_ROCK_793, 230, 277, 5) + drawReward(player,"5 Quest Points", ln++) + drawReward(player,"7900 Agility XP", ln++) + drawReward(player,"18,400 Attack XP", ln++) + drawReward(player,"2150 Magic XP", ln++) + player.skills.addExperience(Skills.AGILITY, 7900.0) + player.skills.addExperience(Skills.ATTACK, 18400.0) + player.skills.addExperience(Skills.MAGIC, 2150.0) + player.questRepository.syncronizeTab(player) + } + + override fun drawJournal(player: Player?, stage: Int) { + super.drawJournal(player, stage) + var line = 12 + player?: return + line(player, "I can start this quest at the !!Grand Tree?? in the !!Gnome??", line++, stage > 0) + line(player, "!!Stronghold?? by speaking to !!King Narnode Shareen.??", line++, stage > 0) + line++ + if(stage == 0) { + line(player, "I must have:", line++) + line(player, "!!Level 25 Agility.??", line++) + line(player, "!!High enough combat to defeat a level 172 demon.??", line++) + } + if(stage >= 10){ + line(player, "King Narnode Shareen suspects sabotage on the Grand Tree and", line++, stage > 10) + line(player, "wants to confirm it by consulting with Hazelmere.", line++, stage > 10) + line++ + line(player, "Hazelmere's dwelling is located on a towering hill, on an island ", line++, stage > 10) + line(player, "east of Yanille.", line++, stage > 10) + line++ + } + if(stage >= 20){ + line(player, "Report back to King Narnode Shareen.", line++, stage > 20) + line++ + } + if(stage >= 40){ + line(player, "King Narnode suspects that someone has forged his royal seal.", line++, stage > 40) + line(player, "Find Glough and notify him about the situation.", line++, stage > 40) + line++ + } + if(stage >= 45){ + line(player, "Glough says the humans are going to invade! Report back to the king.", line++, stage > 45) + line++ + } + if(stage >= 46){ + line(player, "Talk to the prisoner at the top of the tree", line++, stage > 46) + line++ + } + if(stage >= 47){ + line(player, "Search Glough's home for clues.", line++, stage > 47) + line++ + } + if(stage >= 55){ + line(player, "Glough has placed guards on the front gate to stop you escaping!", line++, stage > 55) + line(player, "Use King Narnode's glider pilot fly you away until things calm down.", line++, stage > 55) + if(player.hasItem(Item(Items.LUMBER_ORDER_787))){ + line(player, "Bring the lumber order to Charlie", line++, stage > 55) + } + line++ + + } + if(stage >= 60){ + line(player, "Search Glough's girlfriend's house for his chest keys.", line++, stage > 60) + } + if(stage >= 60){ + if(player.hasItem(Item(Items.GLOUGHS_KEY_788))){ + line(player, "Find a use for Gloughs key and report back to the king.", line++, stage > 60) + } + } + if(stage >= 70){ + line(player, "Find a use for the strange twigs from King Narnode Shareen.", line++, stage > 70) + } + if(stage == 100){ + line++ + line(player,"QUEST COMPLETE!", line) + } + } + +} diff --git a/Server/src/main/content/region/kandarin/quest/grandtree/TranslationBook.kt b/Server/src/main/content/region/kandarin/quest/grandtree/TranslationBook.kt new file mode 100644 index 000000000..a4c5c7bd9 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/grandtree/TranslationBook.kt @@ -0,0 +1,139 @@ +package content.region.kandarin.quest.grandtree + +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 + +/** + * Translation Book handler for The Grand Tree quest + * + * @author crop & szu & ovenbreado + */ +class TranslationBook : InteractionListener { + companion object { + private val TITLE = "Translation Book" + private val CONTENTS = arrayOf( + PageSet( + Page( + BookLine("Gnome", 55), + BookLine("English translation", 56), + BookLine("written by Anita", 57), + BookLine("", 58), + BookLine("This text contains", 59), + BookLine("the ancient Gnome words", 60), + BookLine("I have managed to", 61), + BookLine("translate thus far.", 62), + BookLine("", 63), + ), + Page( + BookLine("-A-", 66), + BookLine("arpos: rocks", 67), + BookLine("ando: gate", 68), + BookLine("andra: city", 69), + BookLine("ataris: cow", 70), + BookLine("-C-", 71), + BookLine("cef: threat", 72), + BookLine("cheray: lazy", 73), + BookLine("Cinqo: King", 74), + BookLine("cretor: bucket", 75), + ) + ), + PageSet( + Page( + BookLine("-E-", 55), + BookLine("eis: me", 56), + BookLine("es: a", 57), + BookLine("et: and", 58), + BookLine("eto: will", 59), + BookLine("-G-", 60), + BookLine("gandius: jungle", 61), + BookLine("Gal: All", 62), + BookLine("gentis: leaf", 63), + BookLine("gutus: banana", 64), + BookLine("gomondo: branch", 65), + ), + Page( + BookLine("-H-", 66), + BookLine("har: old", 67), + BookLine("harij: harpoon", 68), + BookLine("hewo: grass", 69), + BookLine("", 70), + BookLine("-I-", 71), + BookLine("ip: you", 72), + BookLine("imindus: quest", 73), + BookLine("irno: translate", 74), + BookLine("", 75), + ) + ), + PageSet( + Page( + BookLine("-K-", 55), + BookLine("kar: no", 56), + BookLine("kai: boat", 57), + BookLine("ko: sail", 58), + BookLine("", 59), + BookLine("-L-", 60), + BookLine("lauf: eye", 61), + BookLine("laquinay: common sense", 62), + BookLine("lemanto: man", 63), + BookLine("lemantolly: stupid man", 64), + BookLine("lovos: gave", 65), + ), + Page( + BookLine("-M-", 66), + BookLine("meriz: kill", 67), + BookLine("mina: time(s)", 68), + BookLine("mos: coin", 69), + BookLine("mi: I", 70), + BookLine("mond: seal", 71), + BookLine("", 72), + ) + ), + PageSet( + Page( + BookLine("-P-", 55), + BookLine("por: long", 56), + BookLine("prit: with", 57), + BookLine("priw: tree", 58), + BookLine("pro: to", 59), + BookLine("", 60), + BookLine("-Q-", 61), + BookLine("Qui: guard", 62), + BookLine("Quir: guardian", 63), + BookLine("-R-", 64), + BookLine("rentos: agility", 65), + ), + Page( + BookLine("-S-", 66), + BookLine("sarko: Begone", 67), + BookLine("sind: big", 68), + BookLine("", 69), + BookLine("-T-", 70), + BookLine("ta: the", 71), + BookLine("tuzo: open", 72), + BookLine("-U-", 73), + BookLine("Undri: lands", 74), + BookLine("Umesco: Soul", 75), + ) + ) + ) + + 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.TRANSLATION_BOOK_784, 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/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..bd4b91b78 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/scorpioncatcher/SCThormacDialogue.kt @@ -0,0 +1,133 @@ +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) + addItem(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..ba9cc4046 --- /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_TAVERLY = "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_TAVERLY, 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..8759e2459 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/scorpioncatcher/ScorpionCatcherUseListener.kt @@ -0,0 +1,342 @@ +package content.region.kandarin.quest.scorpioncatcher + +import content.region.kandarin.quest.scorpioncatcher.ScorpionCatcher.Companion.ATTRIBUTE_TAVERLY +import content.region.kandarin.quest.scorpioncatcher.ScorpionCatcher.Companion.ATTRIBUTE_BARB +import content.region.kandarin.quest.scorpioncatcher.ScorpionCatcher.Companion.ATTRIBUTE_MONK +import core.api.addItem +import core.game.node.item.Item +import core.api.removeItem +import core.api.runTask +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.system.config.NPCConfigParser +import core.game.world.GameWorld +import org.rs09.consts.Items +import org.rs09.consts.NPCs + + +class ScorpionCatcherUseListener : InteractionListener { + + override fun defineListeners() { + /** + * List of cages + * Talvery Barbarian Monk + * TBM + * 456 --- + * 457 O-- + * 458 00- + * 459 -0- + * 460 -00 + * 461 --0 + * 462 0-0 + * 463 000 + * + * Scorpions + * 385 - Barbarian + * 386 - Taverly + * 387 - Monastery + */ + + /** + * Good captures + */ + + /** + * Empty cage on Taverly scorpion + */ + + // todo check this message + val haveAlready = "You already have this scorpion in this cage." + val catchMessage = "You catch a scorpion!" + + onUseWith(IntType.NPC, Items.SCORPION_CAGE_456, NPCs.KHARID_SCORPION_386){ player, used, with -> + removeItem(player, Item(used.id, 1)) + addItem(player, Items.SCORPION_CAGE_457) + player.sendMessage(catchMessage) + // This is the first time taverly has been caught + if (!player.getAttribute(ATTRIBUTE_TAVERLY, false)){ + player.setAttribute("/save:$ATTRIBUTE_TAVERLY", true) + } + runTask(player, 2) { + with.asNpc().respawnTick = + GameWorld.ticks + with.asNpc().definition.getConfiguration(NPCConfigParser.RESPAWN_DELAY, 34) + } + return@onUseWith true + } + /** + * Barbarian cage on Taverly scorpion + */ + onUseWith(IntType.NPC, Items.SCORPION_CAGE_459, NPCs.KHARID_SCORPION_386){ player, used, with -> + removeItem(player, Item(used.id, 1)) + addItem(player, Items.SCORPION_CAGE_458) + player.sendMessage(catchMessage) + // This is the first time taverly has been caught + if (!player.getAttribute(ATTRIBUTE_TAVERLY, false)){ + player.setAttribute("/save:$ATTRIBUTE_TAVERLY", true) + } + runTask(player, 2) { + with.asNpc().respawnTick = + GameWorld.ticks + with.asNpc().definition.getConfiguration(NPCConfigParser.RESPAWN_DELAY, 34) + } + return@onUseWith true + } + /** + * Monk cage on Taverly scorpion + */ + onUseWith(IntType.NPC, Items.SCORPION_CAGE_461, NPCs.KHARID_SCORPION_386){ player, used, with -> + removeItem(player, Item(used.id, 1)) + addItem(player, Items.SCORPION_CAGE_462) + player.sendMessage(catchMessage) + // This is the first time taverly has been caught + if (!player.getAttribute(ATTRIBUTE_TAVERLY, false)){ + player.setAttribute("/save:$ATTRIBUTE_TAVERLY", true) + } + runTask(player, 2) { + with.asNpc().respawnTick = + GameWorld.ticks + with.asNpc().definition.getConfiguration(NPCConfigParser.RESPAWN_DELAY, 34) + } + return@onUseWith true + } + /** + * Others on Taverly scorpion + */ + onUseWith(IntType.NPC, Items.SCORPION_CAGE_460, NPCs.KHARID_SCORPION_386){ player, used, with -> + removeItem(player, Item(used.id, 1)) + addItem(player, Items.SCORPION_CAGE_463) + player.sendMessage(catchMessage) + // This is the first time taverly has been caught + if (!player.getAttribute(ATTRIBUTE_TAVERLY, false)){ + player.setAttribute("/save:$ATTRIBUTE_TAVERLY", true) + } + runTask(player, 2) { + with.asNpc().respawnTick = + GameWorld.ticks + with.asNpc().definition.getConfiguration(NPCConfigParser.RESPAWN_DELAY, 34) + } + return@onUseWith true + } + + /** + * Empty cage on barbarian agility course scorpion + */ + onUseWith(IntType.NPC, Items.SCORPION_CAGE_456, NPCs.KHARID_SCORPION_385){ player, used, with -> + removeItem(player, Item(used.id, 1)) + addItem(player, Items.SCORPION_CAGE_459) + player.sendMessage(catchMessage) + // This is the first time barbarian has been caught + if (!player.getAttribute(ATTRIBUTE_BARB, false)){ + player.setAttribute("/save:$ATTRIBUTE_BARB", true) + } + runTask(player, 2) { + with.asNpc().respawnTick = + GameWorld.ticks + with.asNpc().definition.getConfiguration(NPCConfigParser.RESPAWN_DELAY, 34) + } + return@onUseWith true + } + /** + * Cage with Taverly scorpion on barbarian scorpion + */ + onUseWith(IntType.NPC, Items.SCORPION_CAGE_457, NPCs.KHARID_SCORPION_385) { player, used, with -> + removeItem(player, Item(used.id, 1)) + addItem(player, Items.SCORPION_CAGE_458) + player.sendMessage(catchMessage) + // This is the first time barbarian has been caught + if (!player.getAttribute(ATTRIBUTE_BARB, false)){ + player.setAttribute("/save:$ATTRIBUTE_BARB", true) + } + runTask(player, 2) { + with.asNpc().respawnTick = + GameWorld.ticks + with.asNpc().definition.getConfiguration(NPCConfigParser.RESPAWN_DELAY, 34) + } + return@onUseWith true + } + /** + * Cage with Monk scorpion on barbarian scorpion + */ + onUseWith(IntType.NPC, Items.SCORPION_CAGE_461, NPCs.KHARID_SCORPION_385) { player, used, with -> + removeItem(player, Item(used.id, 1)) + addItem(player, Items.SCORPION_CAGE_460) + player.sendMessage(catchMessage) + // This is the first time barbarian has been caught + if (!player.getAttribute(ATTRIBUTE_BARB, false)){ + player.setAttribute("/save:$ATTRIBUTE_BARB", true) + } + runTask(player, 2) { + with.asNpc().respawnTick = + GameWorld.ticks + with.asNpc().definition.getConfiguration(NPCConfigParser.RESPAWN_DELAY, 34) + } + return@onUseWith true + } + + /** + * Others on barbarian scorpion + */ + onUseWith(IntType.NPC, Items.SCORPION_CAGE_462, NPCs.KHARID_SCORPION_385) { player, used, with -> + removeItem(player, Item(used.id, 1)) + addItem(player, Items.SCORPION_CAGE_463) + player.sendMessage(catchMessage) + // This is the first time barbarian has been caught + if (!player.getAttribute(ATTRIBUTE_BARB, false)){ + player.setAttribute("/save:$ATTRIBUTE_BARB", true) + } + runTask(player, 2) { + with.asNpc().respawnTick = + GameWorld.ticks + with.asNpc().definition.getConfiguration(NPCConfigParser.RESPAWN_DELAY, 34) + } + return@onUseWith true + } + + + /** + * Empty on Monk scorpion + */ + onUseWith(IntType.NPC, Items.SCORPION_CAGE_456, NPCs.KHARID_SCORPION_387) { player, used, with -> + removeItem(player, Item(used.id, 1)) + addItem(player, Items.SCORPION_CAGE_461) + player.sendMessage(catchMessage) + // This is the first time the monastery has been caught + if (!player.getAttribute(ATTRIBUTE_MONK, false)){ + player.setAttribute("/save:$ATTRIBUTE_MONK", true) + } + runTask(player, 2) { + with.asNpc().respawnTick = + GameWorld.ticks + with.asNpc().definition.getConfiguration(NPCConfigParser.RESPAWN_DELAY, 34) + } + return@onUseWith true + } + + /** + * Taverly cage on Monk scorpion + */ + onUseWith(IntType.NPC, Items.SCORPION_CAGE_457, NPCs.KHARID_SCORPION_387) { player, used, with -> + removeItem(player, Item(used.id, 1)) + addItem(player, Items.SCORPION_CAGE_462) + player.sendMessage(catchMessage) + // This is the first time the monastery has been caught + if (!player.getAttribute(ATTRIBUTE_MONK, false)){ + player.setAttribute("/save:$ATTRIBUTE_MONK", true) + } + runTask(player, 2) { + with.asNpc().respawnTick = + GameWorld.ticks + with.asNpc().definition.getConfiguration(NPCConfigParser.RESPAWN_DELAY, 34) + } + return@onUseWith true + } + + /** + * Barbarian cage on Monk scorpion + */ + onUseWith(IntType.NPC, Items.SCORPION_CAGE_459, NPCs.KHARID_SCORPION_387) { player, used, with -> + removeItem(player, Item(used.id, 1)) + addItem(player, Items.SCORPION_CAGE_460) + player.sendMessage(catchMessage) + // This is the first time the monastery has been caught + if (!player.getAttribute(ATTRIBUTE_MONK, false)){ + player.setAttribute("/save:$ATTRIBUTE_MONK", true) + } + runTask(player, 2) { + with.asNpc().respawnTick = + GameWorld.ticks + with.asNpc().definition.getConfiguration(NPCConfigParser.RESPAWN_DELAY, 34) + } + return@onUseWith true + } + + /** + * Others on Monk scorpion + */ + onUseWith(IntType.NPC, Items.SCORPION_CAGE_458, NPCs.KHARID_SCORPION_387) { player, used, with -> + removeItem(player, Item(used.id, 1)) + addItem(player, Items.SCORPION_CAGE_463) + player.sendMessage(catchMessage) + // This is the first time the monastery has been caught + if (!player.getAttribute(ATTRIBUTE_MONK, false)){ + player.setAttribute("/save:$ATTRIBUTE_MONK", true) + } + runTask(player, 2) { + with.asNpc().respawnTick = + GameWorld.ticks + with.asNpc().definition.getConfiguration(NPCConfigParser.RESPAWN_DELAY, 34) + } + return@onUseWith true + } + + + /** + * Player being stupid and trying to recatch one they have already + */ + + /** + * Taverly + */ + // Just Taverly + onUseWith(IntType.NPC, Items.SCORPION_CAGE_457, NPCs.KHARID_SCORPION_386){ player, _, _ -> + player.sendMessage(haveAlready) + return@onUseWith true + } + // Taverly and Barb + onUseWith(IntType.NPC, Items.SCORPION_CAGE_458, NPCs.KHARID_SCORPION_386){ player, _, _ -> + player.sendMessage(haveAlready) + return@onUseWith true + } + // Taverly and Monk + onUseWith(IntType.NPC, Items.SCORPION_CAGE_462, NPCs.KHARID_SCORPION_386){ player, _, _ -> + player.sendMessage(haveAlready) + return@onUseWith true + } + // All + onUseWith(IntType.NPC, Items.SCORPION_CAGE_463, NPCs.KHARID_SCORPION_386){ player, _, _ -> + player.sendMessage(haveAlready) + return@onUseWith true + } + + + /** + * Barbarian + */ + // Just Barb + onUseWith(IntType.NPC, Items.SCORPION_CAGE_459, NPCs.KHARID_SCORPION_385){ player, _, _ -> + player.sendMessage(haveAlready) + return@onUseWith true + } + // Barb and Taverly + onUseWith(IntType.NPC, Items.SCORPION_CAGE_458, NPCs.KHARID_SCORPION_385){ player, _, _ -> + player.sendMessage(haveAlready) + return@onUseWith true + } + // Barb and Monk + onUseWith(IntType.NPC, Items.SCORPION_CAGE_460, NPCs.KHARID_SCORPION_385){ player, _, _ -> + player.sendMessage(haveAlready) + return@onUseWith true + } + // All + onUseWith(IntType.NPC, Items.SCORPION_CAGE_463, NPCs.KHARID_SCORPION_385){ player, _, _ -> + player.sendMessage(haveAlready) + return@onUseWith true + } + + /** + * Monastery + */ + // Just Monk + onUseWith(IntType.NPC, Items.SCORPION_CAGE_461, NPCs.KHARID_SCORPION_387){ player, _, _ -> + player.sendMessage(haveAlready) + return@onUseWith true + } + // Monk and Taverly + onUseWith(IntType.NPC, Items.SCORPION_CAGE_462, NPCs.KHARID_SCORPION_387){ player, _, _ -> + player.sendMessage(haveAlready) + return@onUseWith true + } + // Monk and Barb + onUseWith(IntType.NPC, Items.SCORPION_CAGE_460, NPCs.KHARID_SCORPION_387){ player, _, _ -> + player.sendMessage(haveAlready) + return@onUseWith true + } + // All + onUseWith(IntType.NPC, Items.SCORPION_CAGE_463, NPCs.KHARID_SCORPION_387){ player, _, _ -> + player.sendMessage(haveAlready) + return@onUseWith true + } + + } + + +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/templeofikov/BridgeAgility.kt b/Server/src/main/content/region/kandarin/quest/templeofikov/BridgeAgility.kt new file mode 100644 index 000000000..f2b9e8341 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/templeofikov/BridgeAgility.kt @@ -0,0 +1,37 @@ +package content.region.kandarin.quest.templeofikov + +import core.api.* +import core.game.node.entity.Entity +import core.game.node.entity.combat.ImpactHandler +import core.game.node.entity.player.Player +import core.game.world.map.Location +import core.game.world.map.zone.ZoneBorders + +class BridgeAgility: MapArea { + companion object { + private fun checkWeightOrFailBridge(entity: Entity) { + // If player weighs more than 0kg at any point on the bridge, the bridge breaks. + // The boots of lightness may be needed here as it makes the player -4kg lighter. + if (entity is Player && entity.settings.weight > 0) { + entity.impactHandler.manualHit(entity, 20, ImpactHandler.HitsplatType.NORMAL) + sendMessage(entity, "The bridge gives way under your weight!") + sendChat(entity, "Ow! Hot! Hot!") + sendMessage(entity, "Good thing that lava was shallow!") + entity.teleport(Location.create(2647, 9826, 0)) + } + } + } + + override fun defineAreaBorders(): Array { + return arrayOf(ZoneBorders(2648, 9828, 2649, 9829)) + } + override fun areaEnter(entity: Entity) { + checkWeightOrFailBridge(entity) + } + override fun areaLeave(entity: Entity, logout: Boolean) { + checkWeightOrFailBridge(entity) + } + override fun entityStep(entity: Entity, location: Location, lastLocation: Location) { + checkWeightOrFailBridge(entity) + } +} diff --git a/Server/src/main/content/region/kandarin/quest/templeofikov/FireWarriorOfLesarkusDialogue.kt b/Server/src/main/content/region/kandarin/quest/templeofikov/FireWarriorOfLesarkusDialogue.kt new file mode 100644 index 000000000..2272e2efb --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/templeofikov/FireWarriorOfLesarkusDialogue.kt @@ -0,0 +1,37 @@ +package content.region.kandarin.quest.templeofikov + +import core.game.dialogue.* +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 +class FireWarriorOfLesarkusDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player): DialoguePlugin { + return FireWarriorOfLesarkusDialogue(player) + } + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage) { + START_DIALOGUE -> npcl(FacialExpression.ANGRY, "Who dares to enter the Temple of Ikov!").also { stage++ } + 1 -> showTopics( + Topic(FacialExpression.ANGRY, "A mighty hero!", 2), + Topic(FacialExpression.FRIENDLY, "A humble pilgrim.", 4), + ) + 2 -> npcl(FacialExpression.ANGRY, "Pathetic fool! Prepare to die!").also { stage++ } + 3 -> { + npc!!.attack(player) + end() + } + 4 -> npcl(FacialExpression.ANGRY, "I haven't seen a pilgrim for thousands of years!").also { stage++ } + 5 -> npcl(FacialExpression.ANGRY, "Temple is closed!").also { + stage = END_DIALOGUE + } + } + return false + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.FIRE_WARRIOR_OF_LESARKUS_277) + } +} diff --git a/Server/src/main/content/region/kandarin/quest/templeofikov/FireWarriorOfLesarkusNPC.kt b/Server/src/main/content/region/kandarin/quest/templeofikov/FireWarriorOfLesarkusNPC.kt new file mode 100644 index 000000000..5dbcbb4e5 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/templeofikov/FireWarriorOfLesarkusNPC.kt @@ -0,0 +1,73 @@ +package content.region.kandarin.quest.templeofikov + +import content.data.Quests +import core.api.* +import core.game.dialogue.FacialExpression +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.AbstractNPC +import core.game.node.entity.player.Player +import core.game.world.map.Location +import org.rs09.consts.NPCs + +class FireWarriorOfLesarkusNPC(id: Int = 0, val player: Player?, location: Location? = null) : AbstractNPC(id, location) { + var clearTime = 0 + // Technically not constructed this way since this is invoked on load. + override fun construct(id: Int, location: Location, vararg objects: Any): AbstractNPC { + return FireWarriorOfLesarkusNPC(id, null, location) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.FIRE_WARRIOR_OF_LESARKUS_277) + } + + override fun handleTickActions() { + super.handleTickActions() + // You have 240 ticks to kill this guy or touch the door again. + if (clearTime++ > 240) { + removeAttribute(player!!, TempleOfIkov.attributeWarriorInstance) + poofClear(this) + } + } + + override fun isAttackable(entity: Entity, style: CombatStyle, message: Boolean): Boolean { + val attackable = super.isAttackable(entity, style, message) + val player = entity.asPlayer() + //attributeWarriorInstance + return attackable + } + + override fun checkImpact(state: BattleState) { + super.checkImpact(state) + val player = state.attacker.asPlayer() + val opponent = this + println(state.ammunition) + if (state.ammunition != null && state.ammunition.itemId == 78) { + // Only ice arrows will damage warrior. + } else { + // If it is the wrong combat or ammunition, deny all damage. + player.properties.combatPulse.stop() + if (state.estimatedHit > -1) { + state.estimatedHit = 0 + } + if (state.secondaryHit > -1) { + state.secondaryHit = 0 + } + runTask(player, 0) { + sendNPCDialogue(player, opponent.id, "Your puny weapons do nothing against me human! Come back when you can give me a real fight!", FacialExpression.ANGRY) + } + } + } + + override fun finalizeDeath(entity: Entity) { + if (entity is Player) { + val player = entity.asPlayer() + removeAttribute(player, TempleOfIkov.attributeWarriorInstance) + if(getQuestStage(player, Quests.TEMPLE_OF_IKOV) == 3) { + setQuestStage(player, Quests.TEMPLE_OF_IKOV, 4) + } + super.finalizeDeath(player) + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/templeofikov/GuardianOfArmadylBehavior.kt b/Server/src/main/content/region/kandarin/quest/templeofikov/GuardianOfArmadylBehavior.kt new file mode 100644 index 000000000..846c5204f --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/templeofikov/GuardianOfArmadylBehavior.kt @@ -0,0 +1,31 @@ +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 +import core.game.node.entity.npc.NPCBehavior +import core.game.node.entity.player.Player +import core.game.node.item.Item +import core.tools.RandomFunction +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +class GuardianOfArmadylBehavior : NPCBehavior(*guardianOfArmadylIds) { + companion object { + private val guardianOfArmadylIds = intArrayOf( + NPCs.GUARDIAN_OF_ARMADYL_274, + NPCs.GUARDIAN_OF_ARMADYL_275 + ) + } + + 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, Quests.TEMPLE_OF_IKOV)) { + if(RandomFunction.roll(4)) { + drops.add(Item(Items.ARMADYL_PENDANT_87)) + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/templeofikov/GuardianOfArmadylDialogue.kt b/Server/src/main/content/region/kandarin/quest/templeofikov/GuardianOfArmadylDialogue.kt new file mode 100644 index 000000000..c8900621f --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/templeofikov/GuardianOfArmadylDialogue.kt @@ -0,0 +1,164 @@ +package content.region.kandarin.quest.templeofikov + +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 GuardianOfArmadylDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player): DialoguePlugin { + return GuardianOfArmadylDialogue(player) + } + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, GuardianOfArmadylDialogueFile(), npc) + return false + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.GUARDIAN_OF_ARMADYL_274, NPCs.GUARDIAN_OF_ARMADYL_275) + } +} + +class GuardianOfArmadylDialogueFile : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + b.onPredicate { _ -> true } + .branch { player -> + return@branch if (inEquipment(player, Items.PENDANT_OF_LUCIEN_86)) { 1 } else { 0 } + }.let{ branch -> + // Failure branch + branch.onValue(1) + .npcl("Thou is a foul agent of Lucien! Such an agent must die!") + .endWith { _, player -> + npc!!.attack(player) + } + return@let branch // Return DialogueBranchBuilder instead of DialogueBuilder to forward the success branch. + }.onValue(0) // Success branch + .npcl("Thou hast ventured deep into the tunnels, you have reached the temple of our master. It is many ages since a pilgrim has come here.") + .options() + .let { optionBuilder -> + val returnJoin = b.placeholder() + + optionBuilder.option("I seek the Staff of Armadyl.") + .playerl(FacialExpression.FRIENDLY, "I seek the Staff of Armadyl.") + .npcl(FacialExpression.FRIENDLY, "We are the guardians of the staff, our fathers were guardians and our fathers' fathers before that. Why dost thou seek it?") + .options() + .let { optionBuilder2 -> + optionBuilder2.option_playerl("Lucien will give me a grand reward for it!") + .npcl(FacialExpression.FRIENDLY, "Thou art working for that spawn of evil?! Fool! You must be cleansed to save your soul!") + .goto(returnJoin) + optionBuilder2.option_playerl("Give it to me!") + .npcl(FacialExpression.FRIENDLY, "The staff is sacred! You will not have it!") + .endWith { _, player -> + npc!!.attack(player) + } + optionBuilder2.option_playerl("I collect rare and powerful artefacts.") + .npcl(FacialExpression.FRIENDLY, "Your worldly greed has darkened your soul!") + .endWith { _, player -> + npc!!.attack(player) + } + } + + optionBuilder.option_playerl("Out of my way fool!") + .npcl(FacialExpression.FRIENDLY, "I may be a fool but I will not step aside!") + .options() + .let { optionBuilder2 -> + optionBuilder2.option_playerl("Why not?!") + .npcl(FacialExpression.FRIENDLY, "Only members of our order are allowed to handle the staff.") + .end() + optionBuilder2.option_playerl("Then you must die!") + .endWith { _, player -> + npc!!.attack(player) + } + optionBuilder2.option_playerl("You're right, I will go now.") + .npcl(FacialExpression.FRIENDLY, "That is a wise decision. Stay a while and let your soul be cleansed!") + .end() + } + + optionBuilder.option_playerl("What are your kind and what are you doing here?") + .npcl(FacialExpression.FRIENDLY, "We are the Guardians of Armadyl. We have kept the temple safe for many ages. The evil in the dungeons seek what lies here. The Mahjarrat are the worst.") + .options() + .let { optionBuilder2 -> + optionBuilder2.option_playerl("What is the Armadyl?") + .npcl(FacialExpression.FRIENDLY, "Armadyl is the god we serve. We have been charged with guarding his sacred artifacts until he requires them.") + .options() + .let { optionBuilder3 -> + optionBuilder3.option_playerl("Ah ok, thanks.") + .npcl(FacialExpression.FRIENDLY, "Go in peace.") + .end() + optionBuilder3.option("Someone told me there were only three gods.") + .playerl(FacialExpression.FRIENDLY, "Someone told me there were only three gods. Saradomin, Zamorak and Guthix.") + .npcl(FacialExpression.FRIENDLY, "Saradominists. Bleh. They only acknowledge those three. There are at least twenty gods!") + .end() + } + + optionBuilder2.option_playerl("Who are the Mahjarrat?") + .npcl(FacialExpression.FRIENDLY, "They are ancient and powerful beings! They are very evil! It is said that they once dominated this plane of existence, Zamorak was supposedly of their blood. They are far fewer in number now.") + .npcl(FacialExpression.FRIENDLY, "Some still have presence in this world in their liche forms. Mahjarrat such as Lucien and Azzanadra would become very powerful if they came into possession of the Staff of Armadyl.") + .options() + .let { optionBuilder3 -> + optionBuilder3.option("Did you say Lucien?") + .playerl(FacialExpression.FRIENDLY, "Did you say Lucien? It was Lucien that asked me to get the staff!") + .npcl(FacialExpression.FRIENDLY, "You are a fool to be working for Lucien! Your soul must be cleansed to save you!") + .goto(returnJoin) + optionBuilder3.option_playerl("I hope you're doing a good job then!") + .npcl(FacialExpression.FRIENDLY, "Do not fear! We are devoted to our charge!") + .end() + } + + optionBuilder2.option_playerl("Wow! You must be really old!") + .npcl(FacialExpression.ANGRY, "No! I am not old! My family has guarded the staff for many generations.") + .end() + } + return@let returnJoin.builder() + } // continue the path here for Lucien.options() + .options() + .let { optionBuilder -> + optionBuilder.option("How dare you call me a fool!") + .playerl(FacialExpression.ANGRY, "How dare you call me a fool! I will work for whom I want!") + .npcl(FacialExpression.ANGRY, "We must cleanse the temple!") + .endWith { _, player -> + npc!!.attack(player) + } + optionBuilder.option_playerl("I just thought of something I must do!") + .npcl(FacialExpression.ANGRY, "An agent of evil cannot be allowed to leave!") + .endWith { _, player -> + npc!!.attack(player) + } + optionBuilder.option_playerl("You're right, it's time for my yearly bath.") + .line("The guardian splashes holy water over you.") + .npcl(FacialExpression.FRIENDLY, "You have been cleansed!") + .npcl(FacialExpression.FRIENDLY, "Lucien must not get hold of the staff! He would become too powerful!") + .npcl(FacialExpression.FRIENDLY, "Hast thou come across the undead necromancer? It was he that raised an army of the undead against Varrock a generation ago. If you know where he is you can help us defeat him.") + .options() + .let { optionBuilder2 -> + optionBuilder2.option_playerl("Ok! I'll help!") + .npcl(FacialExpression.FRIENDLY, "So he is close by?") + .playerl(FacialExpression.FRIENDLY, "Yes!") + .npcl(FacialExpression.FRIENDLY, "He must be gaining in power again. If you can defeat him he will be banished from this plane for a while. You will need this pendant to attack him.") + .item(Items.ARMADYL_PENDANT_87, "The guardian has given you a pendant.") + .endWith { _, player -> + setAttribute(player, TempleOfIkov.attributeChosenEnding, 1) + if (getQuestStage(player, Quests.TEMPLE_OF_IKOV) == 5) { + setQuestStage(player, Quests.TEMPLE_OF_IKOV, 6) + } + addItemOrDrop(player, Items.ARMADYL_PENDANT_87) + } + optionBuilder2.option_playerl("No! I shall not turn against my employer!") + .npcl(FacialExpression.ANGRY, "Fool! You will die for your sins!") + .endWith { _, player -> + npc!!.attack(player) + } + optionBuilder2.option_playerl("I need time to think.") + .npcl(FacialExpression.FRIENDLY, "Linger a while and be at peace.") + .end() + } + } + } + +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/templeofikov/LucienDialogue.kt b/Server/src/main/content/region/kandarin/quest/templeofikov/LucienDialogue.kt new file mode 100644 index 000000000..c093d134e --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/templeofikov/LucienDialogue.kt @@ -0,0 +1,121 @@ +package content.region.kandarin.quest.templeofikov + +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 LucienDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player): DialoguePlugin { + return LucienDialogue(player) + } + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, LucienDialogueFile(), npc) + return false + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.LUCIEN_273) + } +} + +class LucienDialogueFile : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + 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(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 } + }.let { branch -> + branch.onValue(1) + .playerl("Sorry! Can you remind me of my mission?") + .npcl("My patience grows thin hero!") + .npcl("I need the Staff of Armadyl. It's in the Temple of Ikov, near Hemenster, north east of here.") + .playerl("I'm up for it!") + .end() + branch.onValue(0) + .playerl("I've lost the pendant you gave me.") + .npcl("Imbecile!") + .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(Quests.TEMPLE_OF_IKOV, 0) + .npcl("I seek a hero to go on an important mission!") + .options().let { optionBuilder -> + val returnJoin = b.placeholder() + optionBuilder.option("I'm a mighty hero!") + .playerl(FacialExpression.ANGRY, "I'm a mighty hero!") + .goto(returnJoin) + optionBuilder.option_playerl("Yep, lots of heros about these days.") + .npcl("Well, if you see any be sure to point them in my direction.") + .end() + return@let returnJoin.builder() + } + .npcl("I require the Staff of Armadyl. It is in the deserted Temple of Ikov, near Hemenster, north east of here.") + .npcl("Take care hero! There is a dangerous monster somewhere in the temple!") + .let{ builder -> + val returnJoin = b.placeholder() + returnJoin.builder() + .options() + .let { optionBuilder -> + optionBuilder.option_playerl("Why can't you get it yourself?") + .npcl("The guardians of the Staff of Armadyl fear me!") + .npcl("They have set up a magical barrier which even my power cannot overcome!") + .goto(returnJoin) + optionBuilder.option("That sounds like a laugh!") + .playerl(FacialExpression.FRIENDLY, "That sounds like a laugh!") + .npcl("It's not as easy as it sounds. The monster can only be killed with a weapon of ice. There are many other dangers.") + .playerl(FacialExpression.FRIENDLY, "I'm up for it!") + .npcl("Take this pendant. Without it you will not be enter the Chamber of Fear.") + .betweenStage { df, player, _, _ -> + addItemOrDrop(player, Items.PENDANT_OF_LUCIEN_86) + } + .item(Items.PENDANT_OF_LUCIEN_86, "Lucien has given you a pendant!") + .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, Quests.TEMPLE_OF_IKOV) == 0) { + setQuestStage(player, Quests.TEMPLE_OF_IKOV, 1) + } + } + optionBuilder.option_playerl("Oh no! Sounds far too dangerous!") + .npcl("Wimp! Call yourself a hero?! My daughter is more a hero than you!") + .end() + optionBuilder.option_playerl("What's the reward?!") + .npcl("I see you are the mercenary type.") + .playerl("It's a living.") + .npcl("I will reward you well if you bring me the staff.") + .goto(returnJoin) + return@let optionBuilder + } + return@let builder.goto(returnJoin) + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/templeofikov/LucienEndingDialogue.kt b/Server/src/main/content/region/kandarin/quest/templeofikov/LucienEndingDialogue.kt new file mode 100644 index 000000000..2ba1dd6b7 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/templeofikov/LucienEndingDialogue.kt @@ -0,0 +1,65 @@ +package content.region.kandarin.quest.templeofikov + +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 LucienEndingDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player): DialoguePlugin { + return LucienEndingDialogue(player) + } + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, LucienEndingDialogueFile(), npc) + return false + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.LUCIEN_272) + } +} + +class LucienEndingDialogueFile : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + 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(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 } + }.let { branch -> + branch.onValue(1) + .options() + .let { optionBuilder -> + optionBuilder.option_playerl("Yes! Here it is.") + .betweenStage { _, player, _, _ -> + removeItem(player, Items.STAFF_OF_ARMADYL_84) + } + .iteml(Items.STAFF_OF_ARMADYL_84, "You give Lucien the Staff of Armadyl.") + .npcl(FacialExpression.FRIENDLY, "Muhahahhahahaha!") + .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, Quests.TEMPLE_OF_IKOV) == 6) { + finishQuest(player, Quests.TEMPLE_OF_IKOV) + } + } + optionBuilder.option_playerl("No, not yet.") + .end() + } + branch.onValue(0) + .playerl(FacialExpression.FRIENDLY, "No, not yet.") + .end() + } + b.onPredicate { _ -> true } + .npcl("Not here. Meet me at the Flying Horse Inn in East Ardougne.") + .end() + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/templeofikov/LucienEndingNPC.kt b/Server/src/main/content/region/kandarin/quest/templeofikov/LucienEndingNPC.kt new file mode 100644 index 000000000..18e8e429b --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/templeofikov/LucienEndingNPC.kt @@ -0,0 +1,55 @@ +package content.region.kandarin.quest.templeofikov + +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.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 org.rs09.consts.Items +import org.rs09.consts.NPCs + +@Initializable +class LucienEndingNPC(id: Int = 0, location: Location? = null) : AbstractNPC(id, location) { + override fun construct(id: Int, location: Location, vararg objects: Any): AbstractNPC { + return LucienEndingNPC(id, location) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.LUCIEN_272) + } + + override fun isAttackable(entity: Entity, style: CombatStyle, message: Boolean): Boolean { + val attackable = super.isAttackable(entity, style, message) + val player = entity.asPlayer() + if (inEquipment(player, Items.ARMADYL_PENDANT_87)) { + return attackable + } + sendNPCDialogue(player, this.ids[0], "You don't want to attack me. I am your friend.") + return false + } + + override fun finalizeDeath(entity: Entity) { + if (entity is Player) { + val player = entity.asPlayer() + openDialogue(player, object : DialogueFile(){ + override fun handle(componentID: Int, buttonID: Int) { + when(stage){ + 0 -> npcl("You have defeated me for now! I shall reappear in the North!").also { stage++ } + 1 -> end().also { + if(getQuestStage(player, Quests.TEMPLE_OF_IKOV) == 6) { + finishQuest(player, Quests.TEMPLE_OF_IKOV) + } + } + } + } + }, NPC(NPCs.LUCIEN_272)) + + super.finalizeDeath(player) + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/templeofikov/TempleOfIkov.kt b/Server/src/main/content/region/kandarin/quest/templeofikov/TempleOfIkov.kt new file mode 100644 index 000000000..ebfc4773b --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/templeofikov/TempleOfIkov.kt @@ -0,0 +1,203 @@ +package content.region.kandarin.quest.templeofikov + +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 + +/** + * Temple of Ikov Quest + * + * This is the quest where the journal after completion is a barren wasteland of a useless log. + * One of the worst logs I've ever had the pleasure/pain of doing. + * + * 1 - Talked to Lucien (Lucien pendant) + * 2 - Entered chamber of fear (North Fence) + * 3 - Toggled lever(with disabling trap) + * 4 - Killed fire warrior (comes with trying to open the back door) + * 5 - Winelda given 20 fukkin Limpwurt + * 6 - Good (Armadyl pendant), Bad (Took the Staff of Armadyl) + * 7,100 - Good (Killed Lucien), Bad (Gave Lucien staff) + * + * In parallel (all before 4, log disappears after 4) + * A - Took boots + * B - Cross bridge and found lever + * C - Attached lever to switch near entrance lever + * D - Found ice arrows + */ +@Initializable +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 attributeChosenEnding = "/save:quest:templeofikov-chosenending" + + const val attributeDisabledTrap = "/save:quest:templeofikov-disabledtrap" + const val attributeTalkedToWinelda = "/save:quest:templeofikov-talkedtowinelda" + + const val attributeCrossedBridge = "/save:quest:templeofikov-crossedbridge" + const val attributeIceChamberAccess = "/save:quest:templeofikov-icechamberaccess" + const val attributeIceArrows = "/save:quest:templeofikov-icearrows" + + const val attributeRandomChest = "quest:templeofikov-randomChest" + const val attributeWarriorInstance = "quest:templeofikov-warriorInstance" + } + override fun drawJournal(player: Player, stage: Int) { + super.drawJournal(player, stage) + var line = 12 + var stage = getStage(player) + + 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) + line(player, "by speaking to !!Lucien??", line++, false) + line++ + line(player, "To start this quest I will need:", line++, false) + 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) + line(player, "from the !!Temple of Ikov??. The entrance is near !!Hemenster??. He has", line++, true) + line(player, "given me a !!pendant?? so I can enter the !!chamber of fear??.", line++, true) + } else if (stage >= 1) { + line(player, "Lucien has asked me to retrieve the !!Staff of Armadyl?? from", line++, false) + line(player, "from the !!Temple of Ikov??. The entrance is near !!Hemenster??. He has", line++, false) + line(player, "given me a !!pendant?? so I can enter the !!chamber of fear??.", line++, false) + } + + if (stage == 2) { + line(player, "I have entered the chamber of fear.", line++, true) + + } + if (stage == 3) { + line(player, "I have entered the chamber of fear. I found a trap on a", line++, true) + line(player, "lever and have disabled it. I pulled the lever.", line++, true) + } + + // This is a whole side part of retrieving ice arrows that would disappear once you reach stage 4. + if (stage < 4) { + // This is questionable. It's seen in 2014, but seems to be a "side thing". It tracks that you've obtained ice arrows. Derived from RS3. + if (getAttribute(player, attributeIceArrows, false)) { + line(player, "I have found some boots that make me lighter. I made it", line++, true) + line(player, "across the lava bridge and found a lever. I fit the lever", line++, true) + line(player, "into the bracket and pulled the lever. I found arrows", line++, true) + line(player, "made of ice in a chest.", line++, true) + } else if (getAttribute(player, attributeIceChamberAccess, false)) { + line(player, "I have found some boots that make me lighter. I made it", line++, true) + line(player, "across the lava bridge and found a lever. I fit the lever", line++, true) + line(player, "into the bracket and pulled the lever.", line++, true) + } else if (getAttribute(player, attributeCrossedBridge, false)) { + line(player, "I have found some boots that make me lighter. I made it", line++, true) + line(player, "across the lava bridge and found a lever.", line++, true) + } + } + + // Derived. Need sources. + if (stage in 2..3) { + line++ + line(player, "I need to find the entrance to the !!Temple of Ikov??", line++, false) + } + + if (stage == 4) { + line(player, "I have entered the chamber of fear. I found a trap on a", line++, true) + line(player, "lever and have disabled it. I pulled the lever. I went into", line++, true) + line(player, "another chamber and was attacked by a Fire Warrior! I", line++, true) + line(player, "killed it using arrows made of ice and my trusty bow.", line++, true) + } + if (stage == 4 && getAttribute(player, attributeTalkedToWinelda, false) ) { + line++ + // This will never show up crossed. https://www.youtube.com/watch?v=OKYM2oFOUtk 5:18 + line(player, "My path is blocked by lava. !!Winelda?? will teleport me across", line++, false) + line(player, "if I get her !!twenty limpwurt roots??.", line++, false) + } + if (stage == 5) { + line(player, "I have entered the chamber of fear. I found a trap on a", line++, true) + line(player, "lever and have disabled it. I pulled the lever. I went into", line++, true) + line(player, "another chamber and was attacked by a Fire Warrior! I", line++, true) + line(player, "killed it using arrows made of ice and my trusty bow.", line++, true) + } + + if (stage == 6 && getAttribute(player, attributeChosenEnding, 0) == 1 ) { + line(player, "I have entered the chamber of fear. I found a trap on a", line++, true) + line(player, "lever and have disabled it. I pulled the lever. I went into", line++, true) + line(player, "another chamber and was attacked by a Fire Warrior! I", line++, true) + line(player, "killed it using arrows made of ice and my trusty bow.", line++, true) + line++ + line(player, "I agreed to help the !!Guardians of Armadyl??, I will kill !!Lucien??.", line++, false) + line(player, "The guardians gave me a !!pendant?? that I will need to enable", line++, false) + line(player, "me to attack him.", line++, false) + } + + if (stage == 6 && getAttribute(player, attributeChosenEnding, 0) == 2 ) { + line(player, "I have entered the chamber of fear. I found a trap on a", line++, true) + line(player, "lever and have disabled it. I pulled the lever. I went into", line++, true) + line(player, "another chamber and was attacked by a Fire Warrior! I", line++, true) + line(player, "killed it using arrows made of ice and my trusty bow.", line++, true) + line++ + // Derived. Need sources. + line(player, "I recovered the !!Staff of Armadyl?? from the !!Temple of Ikov??.", line++, false) + line(player, "!!Lucien?? is staying at his house west of the !!Grand Exchange??", line++, false) + line(player, "in !!Varrock??.", line++, false) + } + + if (stage >= 100) { + if (getAttribute(player, attributeChosenEnding, 0) == 1) { + //end quest kill lucien https://www.youtube.com/watch?v=cePHhIOqsqg 19:40 + line++ + line(player, "I agreed to help the Guardians of Armadyl, I killed Lucien", line++, true) + line(player, "and banished him from this plane!", line++, true) + } + + if (getAttribute(player, attributeChosenEnding, 0) == 2) { + //end quest helped lucien + line++ + line(player, "I recovered the Staff of Armadyl from the Temple of Ikov.", line++, true) + line(player, "Lucien was staying at his house west of the Grand Exchange", line++, true) + line(player, "in Varrock. He said that the staff had made him more", line++, true) + line(player, "powerful!", line++, true) + } + + line++ + line(player,"QUEST COMPLETE!", line) + } + limitScrolling(player, line, false) + } + } + + override fun reset(player: Player) { + removeAttribute(player, attributeChosenEnding) + removeAttribute(player, attributeDisabledTrap) + removeAttribute(player, attributeTalkedToWinelda) + removeAttribute(player, attributeCrossedBridge) + removeAttribute(player, attributeIceArrows) + removeAttribute(player, attributeIceChamberAccess) + } + + override fun finish(player: Player) { + var ln = 10 + super.finish(player) + when (getAttribute(player, attributeChosenEnding, 0)){ + 1 -> player.packetDispatch.sendString("Temple of Ikov Quest completed for Armadyl!", 277, 4) + 2 -> player.packetDispatch.sendString("Temple of Ikov Quest completed for Lucien!", 277, 4) + else -> player.packetDispatch.sendString("Temple of Ikov Quest completed!!", 277, 4) + } + player.packetDispatch.sendItemZoomOnInterface(Items.YEW_LONGBOW_855,230,277,5) + + drawReward(player, "1 Quest Point", ln++) + drawReward(player, "10,500 Ranged XP", ln++) + drawReward(player, "8,000 Fletching XP", ln++) + + rewardXP(player, Skills.RANGE, 10500.0) + rewardXP(player, Skills.FLETCHING, 8000.0) + } + + override fun newInstance(`object`: Any?): Quest { + return this + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/templeofikov/TempleOfIkovListeners.kt b/Server/src/main/content/region/kandarin/quest/templeofikov/TempleOfIkovListeners.kt new file mode 100644 index 000000000..f513997e3 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/templeofikov/TempleOfIkovListeners.kt @@ -0,0 +1,261 @@ +package content.region.kandarin.quest.templeofikov + +import content.data.Quests +import content.global.skill.agility.AgilityHandler +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.skill.Skills +import core.game.node.item.GroundItem +import core.game.node.item.Item +import core.game.system.task.Pulse +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 TempleOfIkovListeners : InteractionListener { + + companion object { + val chestLocations = + arrayOf(Location(2710, 9850, 0), + Location(2719, 9838, 0), + Location(2729, 9850, 0), + Location(2747, 9848, 0), + Location(2738, 9835, 0), + Location(2745, 9821, 0)) + } + + override fun defineDestinationOverrides() { + // B - C (fucking lever is on an angle) + setDest(IntType.SCENERY, intArrayOf(Scenery.LEVER_87), "pull") { _, _ -> + return@setDest Location.create(2671, 9805, 0) + } + } + + override fun defineListeners() { + + // This is the trap door falling area. + addClimbDest(Location.create(2682, 9849, 0), Location.create(2665, 9849, 0)) + + // Shiny key to back of McGrubor's Wood. THIS MUST BE LOCKED UP + on(Scenery.DOOR_99, SCENERY, "open") { player, node -> + if (inInventory(player, Items.SHINY_KEY_85)){ + DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) + } else { + sendMessage(player, "The door is locked.") + } + return@on true + } + onUseWith(SCENERY, Items.SHINY_KEY_85, Scenery.DOOR_99) { player, used, with -> + DoorActionHandler.handleAutowalkDoor(player, with.asScenery()) + return@onUseWith true + } + + // 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, Quests.TEMPLE_OF_IKOV) == 1) { + setQuestStage(player, Quests.TEMPLE_OF_IKOV, 2) + } + DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) + } else { + sendMessage(player, "As you reach to open the door a great terror overcomes you!") + } + return@on true + } + + // 2: Trapdoor exit + on(Scenery.TRAPDOOR_100, SCENERY, "open") { player, node -> + sendDialogueLines(player, "You try to open the trapdoor but it won't budge! It looks like the", "trapdoor can only be opened from the other side.") + sendMessage(player, "You try to open the trapdoor but it won't budge!") + sendMessage(player, "It looks like the trapdoor can only be opened from the other side.") + return@on true + } + + + // 2 - 3: Lever with trapdoor pull. + on(Scenery.LEVER_91, SCENERY, "pull") { player, node -> + replaceScenery(node.asScenery(), 88, 3) + animate(player, Animation(2140)) + if (getAttribute(player, TempleOfIkov.attributeDisabledTrap, false)) { + 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.") + } + return@on true + } + + // 2: Lever with trapdoor search. If you do not have level 42 thieving, you cannot proceed. + on(Scenery.LEVER_91, SCENERY, "Search for traps") { player, node -> + if (getDynLevel(player, Skills.THIEVING) >= 42) { + sendDialogueLines(player, "You find a trap on the lever! You disable the trap.") + setAttribute(player, TempleOfIkov.attributeDisabledTrap, true) + } else { + // This is the best video. It shows that you can start the quest even if you didn't meet reqs. + // It also shows what happens if you tried to disarm without level 42 Thieving. + // https://www.youtube.com/watch?v=I28nZxZAd58 + sendDialogueLines(player, "You find nothing.") + } + return@on true + } + + // A: Obtain boots. + // Note that the room is "dark". There are two areas. + // You switch between them if you have light or not. + // The light lets you see further into the room + // https://www.youtube.com/watch?v=6ZGpJNeGLJ0 + // sendDialogue("Hmm...bit dark down here! I'm not going to venture far!") + + + // 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 -> + removeItem(player, used) + replaceScenery(with.asScenery(), Scenery.LEVER_87, 20) + sendDialogue(player, "You fit the lever into the bracket.") + return@onUseWith true + } + + // B to C: Pull down on lever + on(Scenery.LEVER_87, SCENERY, "pull") { player, node -> + face(player, Location.create(2671, 9803, 0)) + animate(player, Animation(2140)) + replaceScenery(node.asScenery(), 88, 3) // Scenery.88 is the downward lever. + setAttribute(player, TempleOfIkov.attributeIceChamberAccess, true) + queueScript(player, 6, QueueStrength.NORMAL) { + replaceScenery(node.asScenery(), Scenery.LEVER_BRACKET_86, -1) + return@queueScript stopExecuting(player) + } + sendDialogue(player, "You hear the clunking of some hidden machinery.") + return@on true + } + + // 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, + 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()) + DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) + } else { + sendDialogue(player, "The door won't open!") + } + return@on true + } + + // D: Chest open + on(Scenery.CLOSED_CHEST_35122, SCENERY, "open") { player, node -> + animate(player, 536) + replaceScenery(node as core.game.node.scenery.Scenery, Scenery.OPEN_CHEST_35123, -1) + return@on true + } + + // D: Chest shut + on(Scenery.OPEN_CHEST_35123, SCENERY, "shut") { player, node -> + animate(player, 536) + replaceScenery(node as core.game.node.scenery.Scenery, Scenery.CLOSED_CHEST_35122, -1) + return@on true + } + + // D: Chest search. I assure you this is the worst feature of this goddamn quest. + // 1/6 of the chests are randomly chosen to have 1-5 ice arrows, and it REMEMBERS... + // So clicking on the same one doesn't randomize it. Good luck and fuck you. + on(Scenery.OPEN_CHEST_35123, SCENERY, "search") { player, node -> + if (getAttribute(player, TempleOfIkov.attributeRandomChest, null) == null) { + setAttribute(player, TempleOfIkov.attributeRandomChest, chestLocations.random()) + } + if (getAttribute(player, TempleOfIkov.attributeRandomChest, null) == node.location){ + removeAttribute(player, TempleOfIkov.attributeRandomChest) + val randomAmount = (1..5).random() + addItemOrDrop(player, Items.ICE_ARROWS_78, randomAmount) + sendItemDialogue(player, Item(Items.ICE_ARROWS_78, randomAmount), "You found some ice arrows!") + setAttribute(player, TempleOfIkov.attributeIceArrows, true) + } else { + sendMessage(player, "You search the chest, but find nothing.") + } + return@on true + } + + // 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, Quests.TEMPLE_OF_IKOV) >= 3){ + DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) + } else { + sendMessage(player, "The door won't open.") + } + return@on true + } + + + // 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, Quests.TEMPLE_OF_IKOV) >= 4){ + DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) + } else { + if (getAttribute(player, TempleOfIkov.attributeWarriorInstance, null) == null) { + val npc = FireWarriorOfLesarkusNPC(NPCs.FIRE_WARRIOR_OF_LESARKUS_277, player, Location(2646, 9866, 0)) + npc.init() + npc.isRespawn = false + npc.isWalks = false + npc.location = Location(2646, 9866, 0) + npc.direction = Direction.NORTH + submitIndividualPulse(player, object : Pulse() { + var counter = 0 + override fun pulse(): Boolean { + when (counter++) { + 0 -> { + sendChat(npc, "You will not pass!") + } + 2 -> { + sendChat(npc, "Amitus! Setitii!") + spawnProjectile(npc, player, 127) // Launch fire projectile + } + 4 -> { + npc.isWalks = true + return true + } + } + return false + } + }) + + setAttribute(player, TempleOfIkov.attributeWarriorInstance, npc) + // This npc doesn't attack you immediately nor is aggressive at the start. + } else { + sendMessage(player, "The door won't open.") + } + } + return@on true + } + + on(Items.STAFF_OF_ARMADYL_84, IntType.GROUNDITEM,"take") { player, node -> + 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) + if (npcs.isNotEmpty()) { + sendChat(npcs[0], "That is not thine to take!") + npcs[0].attack(player) + } else { + 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) + } + return@on true + } + + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/quest/templeofikov/WineldaDialogue.kt b/Server/src/main/content/region/kandarin/quest/templeofikov/WineldaDialogue.kt new file mode 100644 index 000000000..aa34d7d76 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/templeofikov/WineldaDialogue.kt @@ -0,0 +1,111 @@ +package content.region.kandarin.quest.templeofikov + +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.game.node.item.Item +import core.game.world.map.Location +import core.plugin.Initializable +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +@Initializable +class WineldaDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player): DialoguePlugin { + return WineldaDialogue(player) + } + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, WineldaDialogueFile(), npc) + return false + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.WINELDA_276) + } +} + +class WineldaDialogueFile : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + 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 -> + // There's a cutscene, but I'm lazy man. + teleport(player, Location(2664, 9876, 0)) + } + + b.onPredicate { player -> getAttribute(player, TempleOfIkov.attributeTalkedToWinelda, false) } + .npcl("Have you got roots for poor Winelda?") + .branch { player -> + if (inInventory(player, Items.LIMPWURT_ROOT_225)) { + if (amountInInventory(player, Items.LIMPWURT_ROOT_225) >= 20) { + return@branch 2 + } + return@branch 1 + } + return@branch 0 + }.let{ branch -> + branch.onValue(2) + .playerl(FacialExpression.FRIENDLY, "Yes, I've got them.") + .betweenStage { _, player, _, _ -> + removeItem(player, Item(Items.LIMPWURT_ROOT_225, 20)) + } + .iteml(Items.LIMPWURT_ROOT_225, "You give Winelda the limpwurt roots.") + .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, 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)) + } + branch.onValue(1) + .playerl(FacialExpression.FRIENDLY, "I've got some limpwurt roots!") + .npcl(FacialExpression.FRIENDLY, "We needs 20 rooteses!") + .end() + branch.onValue(0) + .playerl(FacialExpression.FRIENDLY, "How many did you need again?") + .npcl(FacialExpression.FRIENDLY, "We needs 20 Limpwurt roots for pot.") + .end() + } + + b.onPredicate { _ -> true } + .npcl(FacialExpression.FRIENDLY, "Hehe! We see you're in a pickle!") + .npcl("Wants to be getting over the nasty lava do we?") + .options() + .let { optionBuilder -> + optionBuilder.option_playerl("Nah, not bothered!") + .npcl(FacialExpression.FRIENDLY, "Hehe! Ye'll come back! They always come back!") + .end() + optionBuilder.option_playerl("Yes we do!") + .npcl(FacialExpression.FRIENDLY, "Mocking us are we? Clever one aren't we?") + .npcl(FacialExpression.FRIENDLY, "I'm knowing some magic trickesses! I could get over easy as that!") + .npcl(FacialExpression.FRIENDLY, "Don't tell them! They always come! They pester poor Winelda!") + .playerl(FacialExpression.FRIENDLY, "If you're such a great witch, get me over!") + .npcl(FacialExpression.FRIENDLY, "See! They pester Winelda!") + .playerl(FacialExpression.FRIENDLY, "I can do something for you!") + .npcl(FacialExpression.FRIENDLY, "Good! Don't pester! Help!") + .npcl(FacialExpression.FRIENDLY, "Get Winelda 20 limpwurt roots for my pot.") + .npcl(FacialExpression.FRIENDLY, "Then we shows them some magic!") + .endWith { _, player -> + setAttribute(player, TempleOfIkov.attributeTalkedToWinelda, true) + } + optionBuilder.option_playerl("Yes I do!") + .npcl(FacialExpression.FRIENDLY, "I'm knowing some magic trickesses! I could get over easy as that!") + .npcl(FacialExpression.FRIENDLY, "Don't tell them! They always come! They pester poor Winelda!") + .playerl(FacialExpression.FRIENDLY, "If you're such a great witch, get me over!") + .npcl(FacialExpression.FRIENDLY, "See! They pester Winelda!") + .playerl(FacialExpression.FRIENDLY, "I can do something for you!") + .npcl(FacialExpression.FRIENDLY, "Good! Don't pester! Help!") + .npcl(FacialExpression.FRIENDLY, "Get Winelda 20 limpwurt roots for my pot.") + .npcl(FacialExpression.FRIENDLY, "Then we shows them some magic!") + .endWith { _, player -> + setAttribute(player, TempleOfIkov.attributeTalkedToWinelda, true) + } + } + } +} \ No newline at end of file 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 f4f477d60..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 = questStage(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 7a4541651..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 = questStage(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 135a7e179..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 = questStage(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 thread 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 1e1f3c715..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 core.api.questStage +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 = questStage(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 526acfc06..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,7 +1,8 @@ package content.region.kandarin.quest.tree +import content.data.Quests import core.api.addItemOrDrop -import core.api.questStage +import core.api.getQuestStage import core.api.sendDialogue import core.game.node.entity.Entity import core.game.node.entity.npc.AbstractNPC @@ -27,7 +28,7 @@ class KhazardWarlordNPC(id: Int = 0, location: Location? = null) : AbstractNPC(i } override fun finalizeDeath(killer: Entity?) { - if(questStage(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 f0cd48ed2..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 = questStage(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 dc1ac4c7e..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.questStage +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 = questStage(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 fb84466a1..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 = questStage(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 8c1776707..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 = questStage(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 88dfce52b..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 = questStage(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 cccce11d8..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 } @@ -85,12 +86,11 @@ class TreeGnomeVillage: Quest("Tree Gnome Village", 125, 124, 2, 111, 0, 1, 9) line++ } if (stage == 100) { - line(player,"QUEST COMPLETE", line, stage > 99) + line(player,"%%QUEST COMPLETE!&&", line, stage > 99) } } 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 f3d6254d6..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,7 +1,7 @@ package content.region.kandarin.quest.tree +import content.data.Quests import core.api.* -import core.game.node.entity.impl.ForceMovement import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player import content.global.skill.agility.AgilityHandler @@ -85,22 +85,7 @@ class TreeGnomeVillageListeners : InteractionListener { return@on true } on(looseRailing, IntType.SCENERY, "squeeze-through"){ player, _ -> - if(player.location != location(2516,3161,0)) { - squeezeThrough(player) - } else { - player.pulseManager.run(object : Pulse(0) { - var count = 0 - override fun pulse(): Boolean { - when (count) { - 0 -> ForceMovement.run(player,player.location,player.location.transform(Direction.WEST,1)) - 2 -> squeezeThrough(player) - 3 -> return true - } - count++ - return false - } - }) - } + squeezeThrough(player) return@on true } on(crumbledWall, IntType.SCENERY, "climb-over"){ player, _ -> @@ -108,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 -> @@ -132,71 +125,53 @@ class TreeGnomeVillageListeners : InteractionListener { return@on true } } -} -fun squeezeThrough(player: Player){ - val squeezeAnim = Animation.create(3844) - if(player.location.y >= 3161) { - AgilityHandler.forceWalk( - player, - -1, - player.location, - player.location.transform(Direction.SOUTH, 1), - squeezeAnim, - 5, - 0.0, - null - ) - } else { - AgilityHandler.forceWalk( - player, - -1, - player.location, - player.location.transform(Direction.NORTH, 1), - squeezeAnim, - 5, - 0.0, - null - ) + fun squeezeThrough(player: Player){ + val dest = if (player.location.y >= 3161) + player.location.transform(Direction.SOUTH, 1) + else + player.location.transform(Direction.NORTH, 1) + + forceMove(player, player.location, dest, 0, 80, anim = 3844) } -} -private class ClimbWall : DialogueFile() { - val climbAnimation = Animation(839) - val wallLoc = Location(2509,3253,0) - override fun handle(componentID: Int, buttonID: Int) { - if(questStage(player!!, TreeGnomeVillage.questName) > 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++ } - 1 -> AgilityHandler.forceWalk(player!!, -1, player!!.location, player!!.location.transform(northSouth, 2), climbAnimation, 20, 0.0, null).also { - end() - if(northSouth == Direction.SOUTH) return - val lowerGuard: NPC = RegionManager.getNpc(player!!.location, NPCs.KHAZARD_COMMANDER_478, 6) ?: return - GameWorld.Pulser.submit(object : Pulse(0) { - var count = 0 - override fun pulse(): Boolean { - when (count) { - 0 -> { - player!!.lock(4) - lowerGuard.sendChat("What? How did you manage to get in here.") - } - 2 -> { - player!!.sendChat("I've come for the orb.") - } - 3 -> { - lowerGuard.sendChat("I'll never let you take it.") - lowerGuard.attack(player) - player!!.unlock() - return true + private class ClimbWall : DialogueFile() { + val climbAnimation = Animation(839) + val wallLoc = Location(2509,3253,0) + override fun handle(componentID: Int, buttonID: Int) { + 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++ } + 1 -> AgilityHandler.forceWalk(player!!, -1, player!!.location, player!!.location.transform(northSouth, 2), climbAnimation, 20, 0.0, null).also { + end() + if(northSouth == Direction.SOUTH) return + val lowerGuard: NPC = RegionManager.getNpc(player!!.location, NPCs.KHAZARD_COMMANDER_478, 6) ?: return + GameWorld.Pulser.submit(object : Pulse(0) { + var count = 0 + override fun pulse(): Boolean { + when (count) { + 0 -> { + player!!.lock(4) + lowerGuard.sendChat("What? How did you manage to get in here.") + } + 2 -> { + player!!.sendChat("I've come for the orb.") + } + 3 -> { + lowerGuard.sendChat("I'll never let you take it.") + lowerGuard.attack(player) + player!!.unlock() + return true + } } + count++ + return false } - count++ - return false - } - }) + }) + } } } } } -} \ No newline at end of file +} 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 new file mode 100644 index 000000000..fe92e9136 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/waterfall/BaxtorianBook.kt @@ -0,0 +1,160 @@ +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 +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 + +/** + * Baxtorian Book + * @author ovenbreado + * @author Splinter + */ +class BaxtorianBook : InteractionListener { + + companion object { + private val TITLE = "Book on Baxtorian" + private val CONTENTS = arrayOf( + PageSet( + Page( + BookLine("The missing relics", 55), + BookLine("", 56), + BookLine(" Many artefacts of", 57), + BookLine("elven history were lost", 58), + BookLine("after the fourth age. The", 59), + BookLine("greatest loss to our", 60), + BookLine("collections of elf history", 61), + BookLine("were the hidden treasures", 62), + BookLine("of Baxtorian.", 63), + BookLine(" Some believe these", 64), + BookLine("treasures are still", 65) + ), + Page( + BookLine("unclaimed, but it is more", 66), + BookLine("commonly believed that", 67), + BookLine("dwarf miners recovered", 68), + BookLine("the treasure at the", 69), + BookLine("beginning of the third", 70), + BookLine("age. Another great loss", 71), + BookLine("was Glarial's pebble, a key", 72), + BookLine("which allowed her family", 73), + BookLine("to visit her tomb.", 74), + BookLine(" The stone was taken", 75), + BookLine("by a gnome family over a", 76) + ) + ), + PageSet( + Page( + BookLine("century ago. It is", 55), + BookLine("believed that the gnomes'", 56), + BookLine("descendant Golrie still has", 57), + BookLine("the stone hidden in the", 58), + BookLine("caves under the gnome", 59), + BookLine("tree village.", 60), + BookLine("", 61), + BookLine("The sonnet of Baxtorian", 62), + BookLine("", 63), + BookLine("The love between", 64), + BookLine("Baxtorian and Glarial was", 65) + ), Page( + BookLine("said to have lasted over a", 66), + BookLine("century. They lived a", 67), + BookLine("peaceful life learning and", 68), + BookLine("teaching the laws of", 69), + BookLine("nature. When Baxtorian's", 70), + BookLine("kingdom was invaded by", 71), + BookLine("the dark forces he left on", 72), + BookLine("a five year campaign. He", 73), + BookLine("returned to find his", 74), + BookLine("people slaughtered and his", 75), + BookLine("wife taken by the enemy.", 76) + ) + ), + PageSet( + Page( + BookLine(" After years of", 55), + BookLine("searching for his love he", 56), + BookLine("finally gave up and", 57), + BookLine("returned to the home he", 58), + BookLine("made for Glarial under", 59), + BookLine("the Baxtorian Waterfall.", 60), + BookLine("Once he entered he", 61), + BookLine("never returned. Only", 62), + BookLine("Glarial had the power to", 63), + BookLine("also enter the waterfall.", 64), + BookLine(" Since Baxtorian", 65) + ), + Page( + BookLine("entered no one but her", 66), + BookLine("can follow him in, it's as if", 67), + BookLine("the powers of nature still", 68), + BookLine("work to protect him.", 69), + BookLine("", 70), + BookLine("The power of nature", 71), + BookLine("", 72), + BookLine(" Glarial and Baxtorian", 73), + BookLine("were masters of nature.", 74), + BookLine("Trees would grow, hills", 75), + BookLine("form and rivers flood on", 76) + ) + ), + PageSet( + Page( + BookLine("their command. Baxtorian", 55), + BookLine("in particular had", 56), + BookLine("perfected rune lore. It", 57), + BookLine("was said that he could", 58), + BookLine("uses the stones to control", 59), + BookLine("water, earth, and air.", 60), + BookLine("", 61), + BookLine("Ode to eternity", 62), + BookLine("", 63), + BookLine("A short piece written by", 64), + BookLine("Baxtorian himself.", 65) + ), + Page( + BookLine("", 66), + BookLine("What care I for this", 67), + BookLine("mortal coil,", 68), + BookLine("where treasures are yet", 69), + BookLine("so frail,", 70), + BookLine("for it is you that is my", 71), + BookLine("life blood,", 72), + BookLine("the wine to my holy grail", 73), + BookLine("and if I see the", 74), + BookLine("judgement day,", 75), + BookLine("when the gods fill the air", 76) + ) + ), + PageSet( + Page( + BookLine("with dust,", 55), + BookLine("I'll happily choke on your", 56), + BookLine("memory,", 57), + BookLine("as my kingdom turns to", 58), + BookLine("rust", 59) + ) + ) + ) + private fun display(player: Player, pageNum: Int, buttonID: Int) : Boolean { + BookInterface.pageSetup(player, BookInterface.FANCY_BOOK_3_49, TITLE, CONTENTS) + if (player.questRepository.getQuest(Quests.WATERFALL_QUEST).getStage(player) == 20) { + player.questRepository.getQuest(Quests.WATERFALL_QUEST).setStage(player, 30) + } + return true + } + } + + override fun defineListeners() { + on(Items.BOOK_ON_BAXTORIAN_292, 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/waterfall/BookOnBaxtorianPlugin.java b/Server/src/main/content/region/kandarin/quest/waterfall/BookOnBaxtorianPlugin.java deleted file mode 100644 index 635425717..000000000 --- a/Server/src/main/content/region/kandarin/quest/waterfall/BookOnBaxtorianPlugin.java +++ /dev/null @@ -1,122 +0,0 @@ -package content.region.kandarin.quest.waterfall; - -import core.game.dialogue.DialoguePlugin; -import core.game.dialogue.book.Book; -import core.game.dialogue.book.BookLine; -import core.game.dialogue.book.Page; -import core.game.dialogue.book.PageSet; -import core.game.node.entity.player.Player; - -/** - * Handles the book on Baxtorian that nobody will read. - * @author Splinter - */ -public final class BookOnBaxtorianPlugin extends Book { - - /** - * Represents the book id - */ - public static int ID = 183764; - - /** - * Represents the array of pages for this book. - */ - private static final PageSet[] PAGES = new PageSet[] { - new PageSet( - new Page( - new BookLine("The missing relics", 55), new BookLine("", 56), new BookLine(" Many artefacts of", 57), new BookLine("elven history were lost", 58), - new BookLine("after the fourth age. The", 59), new BookLine("greatest loss to our", 60), new BookLine("collections of elf history", 61), - new BookLine("were the hidden treasures", 62), new BookLine("of Baxtorian.", 63), new BookLine(" Some believe these", 64), new BookLine("treasures are still", 65)), - new Page( - new BookLine("unclaimed, but it is more", 66), new BookLine("commonly believed that", 67), new BookLine("dwarf miners recovered", 68), - new BookLine("the treasure at the", 69), new BookLine("beginning of the third", 70), new BookLine("age. Another great loss", 71), new BookLine("was Glarial's pebble, a key", 72), - new BookLine("which allowed her family", 73), new BookLine("to visit her tomb.", 74), new BookLine(" The stone was taken", 75), new BookLine("by a gnome family over a", 76))), - new PageSet( - new Page( - new BookLine("century ago. It is", 55), new BookLine("believed that the gnomes'", 56), new BookLine("descendant Golrie still has", 57), new BookLine("the stone hidden in the", 58), - new BookLine("caves under the gnome", 59), new BookLine("tree village.", 60), new BookLine("", 61), new BookLine("The sonnet of Baxtorian", 62), new BookLine("", 63), - new BookLine("The love between", 64), new BookLine("Baxtorian and Glarial was", 65)), new Page(new BookLine("said to have lasted over a", 66), new BookLine("century. They lived a", 67), - new BookLine("peaceful life learning and", 68), new BookLine("teaching the laws of", 69), new BookLine("nature. When Baxtorian's", 70), new BookLine("kingdom was invaded by", 71), - new BookLine("the dark forces he left on", 72), new BookLine("a five year campaign. He", 73), new BookLine("returned to find his", 74), new BookLine("people slaughtered and his", 75), - new BookLine("wife taken by the enemy.", 76))), - new PageSet( - new Page( - new BookLine(" After years of", 55), new BookLine("searching for his love he", 56), - new BookLine("finally gave up and", 57), new BookLine("returned to the home he", 58), new BookLine("made for Glarial under", 59), new BookLine("the Baxtorian Waterfall.", 60), - new BookLine("Once he entered he", 61), new BookLine("never returned. Only", 62), new BookLine("Glarial had the power to", 63), new BookLine("also enter the waterfall.", 64), - new BookLine(" Since Baxtorian", 65)), - new Page(new BookLine("entered no one but her", 66), new BookLine("can follow him in, it's as if", 67), new BookLine("the powers of nature still", 68), new BookLine("work to protect him.", 69), - new BookLine("", 70), new BookLine("The power of nature", 71), new BookLine("", 72), new BookLine(" Glarial and Baxtorian", 73), new BookLine("were masters of nature.", 74), - new BookLine("Trees would grow, hills", 75), new BookLine("form and rivers flood on", 76))), - new PageSet( - new Page( - new BookLine("their command. Baxtorian", 55), new BookLine("in particular had", 56), new BookLine("perfected rune lore. It", 57), new BookLine("was said that he could", 58), - new BookLine("uses the stones to control", 59), new BookLine("water, earth, and air.", 60), new BookLine("", 61), new BookLine("Ode to eternity", 62), new BookLine("", 63), - new BookLine("A short piece written by", 64), new BookLine("Baxtorian himself.", 65)), - new Page(new BookLine("", 66), new BookLine("What care I for this", 67), new BookLine("mortal coil,", 68), new BookLine("where treasures are yet", 69), - new BookLine("so frail,", 70), new BookLine("for it is you that is my", 71), new BookLine("life blood,", 72), new BookLine("the wine to my holy grail", 73), - new BookLine("and if I see the", 74), new BookLine("judgement day,", 75), new BookLine("when the gods fill the air", 76))), - new PageSet( - new Page( - new BookLine("with dust,", 55), new BookLine("I'll happily choke on your", 56), new BookLine("memory,", 57), new BookLine("as my kingdom turns to", 58), new BookLine("rust", 59))), - - }; - - /** - * Constructs a new {@code ShieldofArravBook} {@code Object}. - */ - public BookOnBaxtorianPlugin(final Player player) { - super(player, "Book On Baxtorian", 260, PAGES); - } - - /** - * Constructs a new {@code ShieldofArravBook} {@code Object}. - */ - public BookOnBaxtorianPlugin() { - /** - * empty. - */ - } - - @Override - public void finish() { - if (player.getQuestRepository().getQuest(WaterFall.NAME).getStage(player) == 20) { - player.getQuestRepository().getQuest(WaterFall.NAME).setStage(player, 30); - } - } - - @Override - public void display(Page[] set) { - player.lock(); - player.getInterfaceManager().open(getInterface()); - player.getPacketDispatch().sendString("Previous", getInterface().getId(), 77); - player.getPacketDispatch().sendString("Next", getInterface().getId(), 78); - if (player.getQuestRepository().getQuest(WaterFall.NAME).getStage(player) == 20) { - player.getQuestRepository().getQuest(WaterFall.NAME).setStage(player, 30); - } - for (int i = 55; i < 77; i++) { - player.getPacketDispatch().sendString("", getInterface().getId(), i); - } - player.getPacketDispatch().sendString(getName(), getInterface().getId(), 6); - for (Page page : set) { - for (BookLine line : page.getLines()) { - player.getPacketDispatch().sendString(line.getMessage(), getInterface().getId(), line.getChild()); - } - } - boolean lastPage = index == sets.length - 1; - if (lastPage) { - finish(); - } - player.unlock(); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new BookOnBaxtorianPlugin(player); - } - - @Override - public int[] getIds() { - return new int[] { ID }; - } -} diff --git a/Server/src/main/content/region/kandarin/quest/waterfall/HadleyDialogue.java b/Server/src/main/content/region/kandarin/quest/waterfall/HadleyDialogue.java index 919a031dc..6ac9e47f9 100644 --- a/Server/src/main/content/region/kandarin/quest/waterfall/HadleyDialogue.java +++ b/Server/src/main/content/region/kandarin/quest/waterfall/HadleyDialogue.java @@ -51,7 +51,7 @@ public class HadleyDialogue extends DialoguePlugin { stage = 3; break; case 3: - interpreter.sendDialogues(302, FacialExpression.HALF_GUILTY, "Surely pretty is an understatement kind Sir. Beautiful,", "amazing or possibly life-changing would be more suitable", "wording. Have you seen the Baxtorian waterfall?", "Named after the elf king who was buried underneath."); + interpreter.sendDialogues(302, FacialExpression.HALF_GUILTY, "Surely pretty is an understatement kind " + (player.isMale() ? "Sir. " : "Lady. ") + "Beautiful,", "amazing or possibly life-changing would be more suitable", "wording. Have you seen the Baxtorian waterfall?", "Named after the elf king who was buried underneath."); stage = 4; break; case 4: 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/MithrilSeedsDialogue.java b/Server/src/main/content/region/kandarin/quest/waterfall/MithrilSeedsDialogue.java index 509b3cfb3..4a6c28e70 100644 --- a/Server/src/main/content/region/kandarin/quest/waterfall/MithrilSeedsDialogue.java +++ b/Server/src/main/content/region/kandarin/quest/waterfall/MithrilSeedsDialogue.java @@ -9,7 +9,6 @@ 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.game.world.update.flag.player.FaceLocationFlag; /** * Represents the dialogue plugin used for mithril seeds. @@ -64,7 +63,7 @@ public final class MithrilSeedsDialogue extends DialoguePlugin { switch (buttonId) { case 1: // First option player.lock(2); - player.faceLocation(FaceLocationFlag.getFaceLocation(player, flower)); + player.faceLocation(flower.getFaceLocation(player.getLocation())); player.animate(ANIMATION); GameWorld.getPulser().submit(new Pulse(2, player, flower) { @Override diff --git a/Server/src/main/content/region/kandarin/quest/waterfall/MithrilSeedsPlugin.java b/Server/src/main/content/region/kandarin/quest/waterfall/MithrilSeedsPlugin.java index 565e45593..b085e42e2 100644 --- a/Server/src/main/content/region/kandarin/quest/waterfall/MithrilSeedsPlugin.java +++ b/Server/src/main/content/region/kandarin/quest/waterfall/MithrilSeedsPlugin.java @@ -11,7 +11,6 @@ import core.game.system.task.Pulse; import core.game.world.GameWorld; import core.game.world.map.RegionManager; import core.game.world.update.flag.context.Animation; -import core.game.world.update.flag.player.FaceLocationFlag; import core.plugin.Initializable; import core.plugin.Plugin; import core.tools.RandomFunction; @@ -67,7 +66,7 @@ public final class MithrilSeedsPlugin extends OptionHandler { player.getPulseManager().run(new Pulse(1, player) { @Override public boolean pulse() { - player.faceLocation(FaceLocationFlag.getFaceLocation(player, object)); + player.faceLocation(object.getFaceLocation(player.getLocation())); player.getDialogueInterpreter().open(1 << 16 | 1, object); return true; } 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 7fcc50d38..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 @@ -62,7 +57,6 @@ public class WaterFall extends Quest { public void finish(Player player) { super.finish(player); player.getQuestRepository().syncronizeTab(player); - player.getConfigManager().set(101, player.getQuestRepository().getPoints()); player.getPacketDispatch().sendString("1 Quest Point", 277, 8+ 2); player.getPacketDispatch().sendString("13,750 Strength XP", 277, 9+ 2); @@ -84,7 +78,7 @@ public class WaterFall extends Quest { @Override public Quest newInstance(Object object) { - ClassScanner.definePlugins(new AlmeraDialogue(), new BookOnBaxtorianPlugin(), new GolrieDialogue(), new HadleyDialogue(), new HudonDialogue(), new WaterfallPlugin(), new WaterfallTreeDialogue()); + ClassScanner.definePlugins(new AlmeraDialogue(), new GolrieDialogue(), new HadleyDialogue(), new HudonDialogue(), new WaterfallPlugin(), new WaterfallTreeDialogue()); return this; } 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 9f31ed13e..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; @@ -28,6 +29,8 @@ import core.plugin.Plugin; import kotlin.Unit; import core.plugin.ClassScanner; +import static core.api.ContentAPIKt.*; + /** * Master plugin file for the Waterfall quest. * @@ -128,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; @@ -139,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 { @@ -177,11 +180,11 @@ public final class WaterfallPlugin extends OptionHandler { } break; case 1999:// Second key (in the actual waterfall dungeon) - if (player.getInventory().contains(298, 1) || quest.getStage(player) < 30) { - player.getPacketDispatch().sendMessage("You search the crate and find nothing."); - } else if (quest.getStage(player) >= 30 && !player.getInventory().contains(298, 1)) { - player.getPacketDispatch().sendMessage("You find a large key."); - player.getInventory().add(new Item(298, 1)); + if (inInventory(player, 298,1) || quest.getStage(player) < 30) { + sendMessage(player, "You search the crate and find nothing."); + } else if (quest.getStage(player) >= 30 && !inInventory(player,298, 1)) { + sendMessage(player, "You find a large key."); + addItemOrDrop(player, 298, 1); } break; case 2002:// @@ -247,11 +250,11 @@ public final class WaterfallPlugin extends OptionHandler { player.getDialogueInterpreter().open(305, ((NPC) node)); break; case 1990: - if (player.getInventory().contains(293, 1) || quest.getStage(player) < 30) { - player.getPacketDispatch().sendMessage("You search the crate and find nothing."); - } else if (quest.getStage(player) >= 30 && !player.getInventory().contains(293, 1)) { - player.getPacketDispatch().sendMessage("You find a large key."); - player.getInventory().add(new Item(293, 1)); + if (inInventory(player,293, 1) || quest.getStage(player) < 30) { + sendMessage(player,"You search the crate and find nothing."); + } else if (quest.getStage(player) >= 30 && !inInventory(player,293, 1)) { + sendMessage(player, "You find a large key."); + addItemOrDrop(player,293, 1); } break; case 1991: @@ -423,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) { @@ -482,6 +485,10 @@ public final class WaterfallPlugin extends OptionHandler { } if (useditem.getId() == PEBBLE.getId() && object.getId() == 1992 && ItemDefinition.canEnterEntrana(player)) { + if (player.getFamiliarManager().hasFamiliar()) { + sendMessage(player, "You can't take a follower into the tomb."); + return false; + } player.getPacketDispatch().sendMessage("You place the pebble in the gravestone's small indent."); player.getPacketDispatch().sendMessage("It fits perfectly."); player.getPacketDispatch().sendMessage("You hear a loud creak.", 4); diff --git a/Server/src/main/content/region/kandarin/quest/whileguthixsleeps/WhileGuthixSleeps.kt b/Server/src/main/content/region/kandarin/quest/whileguthixsleeps/WhileGuthixSleeps.kt new file mode 100644 index 000000000..10ea45b99 --- /dev/null +++ b/Server/src/main/content/region/kandarin/quest/whileguthixsleeps/WhileGuthixSleeps.kt @@ -0,0 +1,106 @@ +package content.region.kandarin.quest.whileguthixsleeps + +import core.api.getQuestStage +import core.api.hasLevelStat +import core.api.isQuestComplete +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.quest.Quest +import core.game.node.entity.skill.Skills +import org.rs09.consts.Items +import content.data.Quests + +/** + * While Guthix Sleeps Quest + * + * Not completed. Here is the quest journal in color. https://www.youtube.com/watch?v=lOwoy45ywwA + * Uncomment the @Initializable when it's done. + * + */ +//@Initializable +class WhileGuthixSleeps : Quest(Quests.WHILE_GUTHIX_SLEEPS, 161, 160, 5, 5491, 0, 1, 900) { + + override fun drawJournal(player: Player, stage: Int) { + super.drawJournal(player, stage) + var line = 12 + var stage = getStage(player) + + 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) + line(player, "!!Legends' Guild??, which is located in the !!north-east?? of", line++, false) + line(player, "!!Ardougne??", line++, false) + line++ + line(player, "To complete this quest, I need:", line++, false) + line(player, "!!Level 65 Defence??", line++, hasLevelStat(player, Skills.DEFENCE, 65)) + line(player, "!!Level 75 Magic??", line++, hasLevelStat(player, Skills.MAGIC, 75)) + line(player, "!!Level 65 Herblore??", line++, hasLevelStat(player, Skills.HERBLORE, 65)) + line(player, "!!Level 60 Thieving??", line++, hasLevelStat(player, Skills.THIEVING, 60)) + line(player, "!!Level 55 Hunter??", line++, hasLevelStat(player, Skills.HUNTER, 55)) + 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, 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++) + line(player, "I have all the !!stats?? and !!requirements?? needed to start and", line++) + line(player, "complete this quest.", line++) + } + } + + override fun finish(player: Player) { + 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) + + drawReward(player, "5 Quest Points", ln++) + drawReward(player, "Lump of dragon metal.", ln++) + drawReward(player, "5000 coins.", ln++) + drawReward(player, "Talk to Idria to receive 4 x", ln++) + drawReward(player, "100,000 XP.", ln++) + drawReward(player, "Opportunity to loot Movario's", ln++) + drawReward(player, "base", ln++) + } + + override fun newInstance(`object`: Any?): Quest { + return this + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/seers/dialogue/FionellaDialogue.kt b/Server/src/main/content/region/kandarin/seers/dialogue/FionellaDialogue.kt index fbdc2faa7..ea78bf714 100644 --- a/Server/src/main/content/region/kandarin/seers/dialogue/FionellaDialogue.kt +++ b/Server/src/main/content/region/kandarin/seers/dialogue/FionellaDialogue.kt @@ -13,11 +13,11 @@ import org.rs09.consts.NPCs */ @Initializable -class FionellaDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class FionellaDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - npc(core.game.dialogue.FacialExpression.FRIENDLY,"Can I help you at all?").also { stage = 0 } + npc(FacialExpression.FRIENDLY,"Can I help you at all?").also { stage = 0 } return true } @@ -30,15 +30,15 @@ class FionellaDialogue(player: Player? = null) : core.game.dialogue.DialoguePlug 1 -> when(buttonId){ 1 -> { - player(core.game.dialogue.FacialExpression.FRIENDLY, "Yes please. What are you selling?").also { stage = 10 } + player(FacialExpression.FRIENDLY, "Yes please. What are you selling?").also { stage = 10 } } 2 -> { - player(core.game.dialogue.FacialExpression.FRIENDLY, "No thanks.").also { stage = 99 } + player(FacialExpression.FRIENDLY, "No thanks.").also { stage = 99 } } } - 10 -> npc(core.game.dialogue.FacialExpression.FRIENDLY, "Take a look.").also { stage = 100 } + 10 -> npc(FacialExpression.FRIENDLY, "Take a look.").also { stage = 100 } 99 -> end() 100 -> end().also { npc.openShop(player) } @@ -46,7 +46,7 @@ class FionellaDialogue(player: Player? = null) : core.game.dialogue.DialoguePlug return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return FionellaDialogue(player) } 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 944e3c7df..87eac97a9 100644 --- a/Server/src/main/content/region/kandarin/seers/dialogue/GeoffreyDialogue.kt +++ b/Server/src/main/content/region/kandarin/seers/dialogue/GeoffreyDialogue.kt @@ -9,7 +9,7 @@ import core.plugin.Initializable import org.rs09.consts.Items @Initializable -class GeoffreyDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { +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()) { @@ -38,7 +38,7 @@ class GeoffreyDialogue(player: Player? = null) : core.game.dialogue.DialoguePlug return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return GeoffreyDialogue(player) } diff --git a/Server/src/main/content/region/kandarin/seers/dialogue/LegendsGuardDialogue.kt b/Server/src/main/content/region/kandarin/seers/dialogue/LegendsGuardDialogue.kt index c12725198..8cacba296 100644 --- a/Server/src/main/content/region/kandarin/seers/dialogue/LegendsGuardDialogue.kt +++ b/Server/src/main/content/region/kandarin/seers/dialogue/LegendsGuardDialogue.kt @@ -11,33 +11,33 @@ import core.plugin.Initializable * @author qmqz */ @Initializable -class LegendsGuardDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class LegendsGuardDialogue(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 - npc(core.game.dialogue.FacialExpression.FRIENDLY,"! ! ! Attention ! ! !") + npc(FacialExpression.FRIENDLY,"! ! ! Attention ! ! !") stage = 0 return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 0 -> npc(core.game.dialogue.FacialExpression.FRIENDLY,"Legends Guild Member Approaching").also { stage++ } - 1 -> npc(core.game.dialogue.FacialExpression.FRIENDLY,"Welcome "+ gender() + "!", "I hope you enjoy your time in the Legends Guild.").also { stage = 99 } + 0 -> npc(FacialExpression.FRIENDLY,"Legends Guild Member Approaching").also { stage++ } + 1 -> npc(FacialExpression.FRIENDLY,"Welcome "+ gender() + "!", "I hope you enjoy your time in the Legends Guild.").also { stage = 99 } //while doing legend's quest - 10 -> npc(core.game.dialogue.FacialExpression.FRIENDLY,"I hope the quest is going well " + gender() + ".").also { stage = 99 } + 10 -> npc(FacialExpression.FRIENDLY,"I hope the quest is going well " + gender() + ".").also { stage = 99 } //after the legend's quest is done - 20 -> npc(core.game.dialogue.FacialExpression.FRIENDLY,"Legends Guild Member Approaching!").also { stage = 99 } + 20 -> npc(FacialExpression.FRIENDLY,"Legends Guild Member Approaching!").also { stage = 99 } 99 -> end() } return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return LegendsGuardDialogue(player) } diff --git a/Server/src/main/content/region/kandarin/seers/dialogue/PhantuwtiFantstuwiFarSight.java b/Server/src/main/content/region/kandarin/seers/dialogue/PhantuwtiFantstuwiFarSight.java index 95155b718..aaaa62e66 100644 --- a/Server/src/main/content/region/kandarin/seers/dialogue/PhantuwtiFantstuwiFarSight.java +++ b/Server/src/main/content/region/kandarin/seers/dialogue/PhantuwtiFantstuwiFarSight.java @@ -55,26 +55,26 @@ public final class PhantuwtiFantstuwiFarSight extends DialoguePlugin { case 0: switch (buttonId) { case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Hello, what is this place?"); + interpreter.sendDialogues(player, FacialExpression.ASKING, "Hello, what is this place?"); stage = 10; break; case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "What do you do here?"); + interpreter.sendDialogues(player, FacialExpression.HALF_ASKING, "What do you do here?"); stage = 20; break; case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Do you have any quests?"); + interpreter.sendDialogues(player, FacialExpression.HALF_ASKING, "Do you have any quests?"); stage = 30; break; case 4: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Ok, thanks."); + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "Ok, thanks."); stage = 40; break; } break; case 10: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "This is Seers Village! We're an organisation of mystically", "gifted people with the power of foresight...we see things", "that have yet to come to pass."); + interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "This is Seers Village! We're an organisation of mystically", "gifted people with the power of foresight...we see things", "that have yet to come to pass."); stage = 11; break; case 11: @@ -82,7 +82,7 @@ public final class PhantuwtiFantstuwiFarSight extends DialoguePlugin { stage = 0; break; case 20: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "A lot of our time is spent adressing everyday sorts of", "things, plus we meditate a lot and ehhance our", "powers of mystical foresight."); + interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "A lot of our time is spent addressing everyday sorts of", "things, plus we meditate a lot and enhance our", "powers of mystical foresight."); stage = 31; break; case 30: 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..c4d8a4e93 --- /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_TAVERLY, 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/SeersVillageAchivementDiary.kt b/Server/src/main/content/region/kandarin/seers/diary/SeersVillageAchievementDiary.kt similarity index 79% rename from Server/src/main/content/region/kandarin/seers/diary/SeersVillageAchivementDiary.kt rename to Server/src/main/content/region/kandarin/seers/diary/SeersVillageAchievementDiary.kt index b2e7eb072..b42c7643a 100644 --- a/Server/src/main/content/region/kandarin/seers/diary/SeersVillageAchivementDiary.kt +++ b/Server/src/main/content/region/kandarin/seers/diary/SeersVillageAchievementDiary.kt @@ -1,6 +1,12 @@ package content.region.kandarin.seers.diary -import core.api.* +import content.global.handlers.iface.FairyRing +import content.global.handlers.item.withnpc.PoisonChaliceOnKingArthurDialogue +import core.api.inBorders +import core.api.inEquipment +import core.game.diary.DiaryEventHookBase +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.item.Item @@ -8,13 +14,9 @@ import core.game.world.map.Location import core.game.world.map.zone.ZoneBorders import org.rs09.consts.Items import org.rs09.consts.NPCs -import content.global.handlers.iface.FairyRing -import content.global.handlers.item.withnpc.PoisonChaliceOnKingArthurDialogue -import core.game.diary.DiaryEventHookBase -import core.game.diary.DiaryLevel -import core.game.event.* +import org.rs09.consts.Scenery -class SeersVillageAchivementDiary : DiaryEventHookBase(DiaryType.SEERS_VILLAGE) { +class SeersVillageAchievementDiary : DiaryEventHookBase(DiaryType.SEERS_VILLAGE) { companion object { private const val ATTRIBUTE_CUT_YEW_COUNT = "diary:seers:cut-yew" private const val ATTRIBUTE_BASS_CAUGHT = "diary:seers:bass-caught" @@ -23,6 +25,7 @@ class SeersVillageAchivementDiary : DiaryEventHookBase(DiaryType.SEERS_VILLAGE) private const val ATTRIBUTE_ELEMENTAL_KILL_FLAGS = "diary:seers:elemental-kills" private const val ATTRIBUTE_ARCHER_KILL_FLAGS = "diary:seers:archer-kills" private const val ATTRIBUTE_COAL_TRUCK_FULL = "diary:seers:coal-truck-full" + private const val ATTRIBUTE_FLAX_PICKED = "diary:seers:flax-picked" private val SEERS_VILLAGE_AREA = ZoneBorders(2687, 3455, 2742, 3507) private val SEERS_BANK_AREA = ZoneBorders(2721, 3490, 2730, 3493) @@ -46,6 +49,10 @@ class SeersVillageAchivementDiary : DiaryEventHookBase(DiaryType.SEERS_VILLAGE) NPCs.AIR_ELEMENTAL_1021, NPCs.WATER_ELEMENTAL_1022 ) + private val CHURN_PRODUCT = arrayOf( + Items.CHEESE_1985, Items.POT_OF_CREAM_2130, Items.PAT_OF_BUTTER_6697 + ) + object EasyTasks { const val PICK_5_FLAX = 0 const val WALK_CLOCKWISE_AROUND_MYSTERIOUS_STATUE = 1 @@ -92,7 +99,25 @@ class SeersVillageAchivementDiary : 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( + player, + DiaryLevel.EASY, + EasyTasks.PICK_5_FLAX, + ATTRIBUTE_FLAX_PICKED, + 5 + ) + } + 10806 -> if (event.itemId == Items.YEW_LOGS_1515) { progressIncrementalTask( player, @@ -103,6 +128,14 @@ class SeersVillageAchivementDiary : DiaryEventHookBase(DiaryType.SEERS_VILLAGE) ) } + 10807 -> if (event.itemId in CHURN_PRODUCT) { + finishTask( + player, + DiaryLevel.EASY, + EasyTasks.SINCLAIR_MANSION_USE_CHURN + ) + } + 11317 -> when (event.itemId) { Items.RAW_MACKEREL_353 -> { finishTask( @@ -142,7 +175,7 @@ class SeersVillageAchivementDiary : DiaryEventHookBase(DiaryType.SEERS_VILLAGE) } Items.SHARK_385 -> { - if (isEquipped(player, Items.COOKING_GAUNTLETS_775)) { + if (inEquipment(player, Items.COOKING_GAUNTLETS_775)) { progressIncrementalTask( player, DiaryLevel.HARD, @@ -262,9 +295,9 @@ class SeersVillageAchivementDiary : DiaryEventHookBase(DiaryType.SEERS_VILLAGE) if (inBorders(player, SEERS_BANK_AREA)) { if (event.itemId == Items.MAGIC_SHORTBOW_861 && event.isHigh) { finishTask( - player, - DiaryLevel.HARD, - HardTasks.HIGH_ALCH_MAGIC_SHORTBOW_INSIDE_BANK + player, + DiaryLevel.HARD, + HardTasks.HIGH_ALCH_MAGIC_SHORTBOW_INSIDE_BANK ) } } @@ -273,10 +306,50 @@ class SeersVillageAchivementDiary : DiaryEventHookBase(DiaryType.SEERS_VILLAGE) override fun onFairyRingDialed(player: Player, event: FairyRingDialEvent) { if (event.fairyRing == FairyRing.ALS) { finishTask( - player, - DiaryLevel.HARD, - HardTasks.DIAL_FAIRY_RING_MCGRUBORS_WOOD + player, + DiaryLevel.HARD, + HardTasks.DIAL_FAIRY_RING_MCGRUBORS_WOOD ) } } + + override fun onItemPurchasedFromShop(player: Player, event: ItemShopPurchaseEvent) { + if (event.itemId == Items.CANDLE_36 && player.viewport.region.id == 11061) { + finishTask( + player, + DiaryLevel.EASY, + 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 + ) + } + } + } } \ No newline at end of file diff --git a/Server/src/main/content/region/kandarin/seers/handlers/RangingGuildPlugin.java b/Server/src/main/content/region/kandarin/seers/handlers/RangingGuildPlugin.java index 5db42eeb0..5589a4d69 100644 --- a/Server/src/main/content/region/kandarin/seers/handlers/RangingGuildPlugin.java +++ b/Server/src/main/content/region/kandarin/seers/handlers/RangingGuildPlugin.java @@ -34,6 +34,8 @@ import core.game.world.map.RegionManager; import core.game.world.update.flag.context.Animation; import core.plugin.Plugin; +import static core.api.ContentAPIKt.*; + /** * Represents the plugin used for the ranging guild. * @@ -1115,9 +1117,9 @@ public final class RangingGuildPlugin extends OptionHandler { private void showInterface(int points, int arrowsLeft, int target, String msg) { player.getInterfaceManager().openComponent(325); - player.getConfigManager().set(156, 11 - arrowsLeft); - player.getConfigManager().set(157, points); - player.getConfigManager().set(158, target); + setVarp(player, 156, 11 - arrowsLeft); + setVarp(player, 157, points); + setVarp(player, 158, target); player.getPacketDispatch().sendString(msg, 325, 32); } 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 d005f60e8..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,64 +1,39 @@ package content.region.kandarin.seers.quest.elementalworkshop +import content.global.handlers.iface.BookInterface import core.api.setQuestStage -import core.game.dialogue.DialoguePlugin -import core.game.dialogue.book.Book -import core.game.dialogue.book.Page +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 content.data.Quests /** * Battered book handler for the Elemental Workshop I quest * * @author Woah, with love */ -@Initializable -class BatteredBookHandler : core.game.dialogue.book.Book { +class BatteredBookHandler : InteractionListener { - constructor(player: Player?) : super(player, "Book of the elemental shield", Items.BATTERED_BOOK_2886, *EWUtils.PAGES) {} + companion object { + private val TITLE = "Book of the elemental shield" + private val CONTENTS = EWUtils.PAGES - constructor() { - /** - * empty. - */ - } - - override fun finish() { - if (EWUtils.currentStage(player) == 0) { - setQuestStage(player, "Elemental Workshop I", 1) - } - } - - override fun display(set: Array) { - player.lock() - player.interfaceManager.open(getInterface()) - - for (i in 55..76) { - player.packetDispatch.sendString("", getInterface().id, i) - } - player.packetDispatch.sendString("", getInterface().id, 77) - player.packetDispatch.sendString("", getInterface().id, 78) - player.packetDispatch.sendString(getName(), getInterface().id, 6) - for (page in set) { - for (line in page.lines) { - player.packetDispatch.sendString(line.message, getInterface().id, line.child) + 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 (EWUtils.currentStage(player) == 0) { + setQuestStage(player, Quests.ELEMENTAL_WORKSHOP_I, 1) + } } + return true } - player.packetDispatch.sendInterfaceConfig(getInterface().id, 51, index < 1) - val lastPage = index == sets.size - 1 - player.packetDispatch.sendInterfaceConfig(getInterface().id, 53, lastPage) - if (lastPage) { - finish() - } - player.unlock() } - override fun newInstance(player: Player): core.game.dialogue.DialoguePlugin { - return BatteredBookHandler(player) - } - - override fun getIds(): IntArray { - return intArrayOf(EWUtils.BATTERED_BOOK_ID) + override fun defineListeners() { + on(Items.BATTERED_BOOK_2886, 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/seers/quest/elementalworkshop/EWListeners.kt b/Server/src/main/content/region/kandarin/seers/quest/elementalworkshop/EWListeners.kt index fab55b910..f992cb72e 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 @@ -1,7 +1,6 @@ package content.region.kandarin.seers.quest.elementalworkshop import core.api.* -import core.game.node.entity.player.link.audio.Audio import core.game.node.entity.skill.Skills import core.game.node.item.Item import core.game.system.task.Pulse @@ -16,7 +15,8 @@ import content.region.kandarin.seers.quest.elementalworkshop.EWUtils.WATER_WHEEL import content.region.kandarin.seers.quest.elementalworkshop.EWUtils.currentStage import core.game.interaction.InteractionListener import core.game.interaction.IntType -import core.tools.SystemLogger +import core.tools.Log +import content.data.Quests /** * Listeners for the Elemental Workshop I quest @@ -65,14 +65,14 @@ class EWListeners : InteractionListener { private val smithElementalShield = Animation(Animations.HUMAN_COOKING_RANGE_896) /* Sound effects */ - private val fillStoneBowlSFX = Audio(Sounds.FILL_STONE_BOWL_1537, 1) - private val fillFurnaceWithLavaSFX = Audio(Sounds.FILL_FURNACE_WITH_LAVA_1538) - private val pullLeverResetGatesSFX = Audio(Sounds.PULL_LEVER_RESET_GATE_1540) - private val turnWaterControlsSFX = Audio(Sounds.TURN_METAL_WATER_VALVE) - private val pullLeverEnabledSFX = Audio(Sounds.PULL_LEVER_ENABLED_1547) - private val pullLeverDisabledSFX = Audio(Sounds.PULL_LEVER_DISABLED_1548) - private val bellowLeverSFX = Audio(Sounds.PULL_LEVER_GENERAL_2400) - private val smithElementalShieldSFX = Audio(2721) + private val fillStoneBowlSFX = Sounds.FILL_STONE_BOWL_1537 + private val fillFurnaceWithLavaSFX = Sounds.FILL_FURNACE_WITH_LAVA_1538 + private val pullLeverResetGatesSFX = Sounds.PULL_LEVER_RESET_GATE_1540 + private val turnWaterControlsSFX = Sounds.TURN_METAL_WATER_VALVE + private val pullLeverEnabledSFX = Sounds.PULL_LEVER_ENABLED_1547 + private val pullLeverDisabledSFX = Sounds.PULL_LEVER_DISABLED_1548 + private val bellowLeverSFX = Sounds.PULL_LEVER_GENERAL_2400 + private val smithElementalShieldSFX = Sounds.ANVIL_4_2721 /* In-game locations */ private val leftWaterControlsLocation = Location.create(2713, 9907, 0) @@ -141,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 } } @@ -178,8 +178,8 @@ class EWListeners : InteractionListener { return@on true } // Increment quest stage - if (questStage(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.") @@ -201,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 } @@ -276,7 +276,7 @@ class EWListeners : InteractionListener { } // Sanity error check (Should never get thrown) if (!player.inventory.containsAll(*elementalShieldReqItems)) { - SystemLogger.logErr(this::class.java, "${player.username} tried to forge an elemental shield without all the required items.") + log(this::class.java, Log.ERR, "${player.username} tried to forge an elemental shield without all the required items.") return@onUseWith false } // Successfully smith the elemental shield @@ -290,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 } @@ -333,7 +333,7 @@ class EWListeners : InteractionListener { sendMessage(player, "You empty the lava into the furnace.") } 2 -> { - setVarbit(player, Vars.VARP_QUEST_ELEMENTAL_WORKSHOP, FURNACE_STATE, ENABLED, true) + setVarbit(player, FURNACE_STATE, ENABLED, true) sendMessage(player, "The furnace bursts to life.") return true } @@ -451,7 +451,7 @@ class EWListeners : InteractionListener { else -> { offset = -1 enabled = 0 - SystemLogger.logErr(this::class.java, "Unhandled location when determining enabled water controls! ${player.location}") + log(this::class.java, Log.ERR, "Unhandled location when determining enabled water controls! ${player.location}") return@on false } } @@ -468,7 +468,7 @@ class EWListeners : InteractionListener { sendMessage(player, "You turn the handle.") } 3 -> { - setVarbit(player, Vars.VARP_QUEST_ELEMENTAL_WORKSHOP, offset, enabled, true) + setVarbit(player, offset, enabled, true) return true } } @@ -490,8 +490,8 @@ class EWListeners : InteractionListener { if (EWUtils.waterWheelEnabled(player)) { playAudio(player, pullLeverDisabledSFX) sendMessage(player, "You hear the sound of a water wheel coming to a standstill.") - setVarbit(player, Vars.VARP_QUEST_ELEMENTAL_WORKSHOP, WATER_WHEEL_STATE, DISABLED, true) - setVarbit(player, Vars.VARP_QUEST_ELEMENTAL_WORKSHOP, BELLOWS_STATE, DISABLED, true) + setVarbit(player, WATER_WHEEL_STATE, DISABLED, true) + setVarbit(player, BELLOWS_STATE, DISABLED, true) return@on true } @@ -499,15 +499,15 @@ class EWListeners : InteractionListener { if (!EWUtils.leftWaterControlEnabled(player) || !EWUtils.rightWaterControlEnabled(player)) { playAudio(player, pullLeverResetGatesSFX) sendMessage(player, "You hear the sound of the flow gates resetting.") - setVarbit(player, Vars.VARP_QUEST_ELEMENTAL_WORKSHOP, LEFT_WATER_CONTROL_STATE, DISABLED, true) - setVarbit(player, Vars.VARP_QUEST_ELEMENTAL_WORKSHOP, RIGHT_WATER_CONTROL_STATE, DISABLED, true) + setVarbit(player, LEFT_WATER_CONTROL_STATE, DISABLED, true) + setVarbit(player, RIGHT_WATER_CONTROL_STATE, DISABLED, true) return@on true } // If both of the above are false, the water wheel can start running playAudio(player, pullLeverEnabledSFX) sendMessage(player, "You hear the sound of a water wheel starting up.") - setVarbit(player, Vars.VARP_QUEST_ELEMENTAL_WORKSHOP, WATER_WHEEL_STATE, ENABLED, true) + setVarbit(player, WATER_WHEEL_STATE, ENABLED, true) return@on true } @@ -572,13 +572,13 @@ class EWListeners : InteractionListener { // Bellows lever "OFF" state if (EWUtils.bellowsEnabled(player)) { sendMessage(player, "The bellows stop pumping air.") - setVarbit(player, Vars.VARP_QUEST_ELEMENTAL_WORKSHOP, BELLOWS_STATE, DISABLED, true) + setVarbit(player, BELLOWS_STATE, DISABLED, true) return@on true } // Bellows lever "ON" state sendMessage(player, "The bellows pump air down the pipe.") - setVarbit(player, Vars.VARP_QUEST_ELEMENTAL_WORKSHOP, BELLOWS_STATE, ENABLED, true) + setVarbit(player, BELLOWS_STATE, ENABLED, true) return@on true } } @@ -604,4 +604,4 @@ class EWListeners : InteractionListener { } } } -} \ No newline at end of file +} 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 b065c4040..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 @@ -1,11 +1,11 @@ package content.region.kandarin.seers.quest.elementalworkshop -import core.api.getVarbitValue -import core.game.dialogue.book.BookLine -import core.game.dialogue.book.Page -import core.game.dialogue.book.PageSet +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 @@ -14,102 +14,94 @@ import org.rs09.consts.Vars * @author Woah, with love */ object EWUtils { - - // Helpers for the battered/slashed book /** - * Represents the book id - */ - const val BATTERED_BOOK_ID = 49610760 - const val SLASHED_BOOK_ID = 49610761 - - /** - * Represents the array of pages for this book. + * Represents the array of pages for both the BATTERED_BOOK and SLASHED_BOOK. */ val PAGES = arrayOf( - core.game.dialogue.book.PageSet( - core.game.dialogue.book.Page( - core.game.dialogue.book.BookLine("Within the pages of this", 55), - core.game.dialogue.book.BookLine("book you will find the", 56), - core.game.dialogue.book.BookLine("secret to working the", 57), - core.game.dialogue.book.BookLine("very elements themselves.", 58), - core.game.dialogue.book.BookLine("Early in the fifth age, a", 59), - core.game.dialogue.book.BookLine("new ore was discovered.", 60), - core.game.dialogue.book.BookLine("This ore has a unique", 61), - core.game.dialogue.book.BookLine("property of absorbing,", 62), - core.game.dialogue.book.BookLine("transforming or focusing", 63), - core.game.dialogue.book.BookLine("elemental energy. A", 64), - core.game.dialogue.book.BookLine("workshop was erected", 65), + PageSet( + Page( + BookLine("Within the pages of this", 55), + BookLine("book you will find the", 56), + BookLine("secret to working the", 57), + BookLine("very elements themselves.", 58), + BookLine("Early in the fifth age, a", 59), + BookLine("new ore was discovered.", 60), + BookLine("This ore has a unique", 61), + BookLine("property of absorbing,", 62), + BookLine("transforming or focusing", 63), + BookLine("elemental energy. A", 64), + BookLine("workshop was erected", 65), ), - core.game.dialogue.book.Page( - core.game.dialogue.book.BookLine("close by to work this new", 66), - core.game.dialogue.book.BookLine("material. The workshop", 67), - core.game.dialogue.book.BookLine("was set up for artisans", 68), - core.game.dialogue.book.BookLine("and inventors to be able", 69), - core.game.dialogue.book.BookLine("to come and create", 70), - core.game.dialogue.book.BookLine("devices made from the", 71), - core.game.dialogue.book.BookLine("unique ore, found only in", 72), - core.game.dialogue.book.BookLine("the village of the Seers.", 73) + Page( + BookLine("close by to work this new", 66), + BookLine("material. The workshop", 67), + BookLine("was set up for artisans", 68), + BookLine("and inventors to be able", 69), + BookLine("to come and create", 70), + BookLine("devices made from the", 71), + BookLine("unique ore, found only in", 72), + BookLine("the village of the Seers.", 73) ) ), - core.game.dialogue.book.PageSet( - core.game.dialogue.book.Page( - core.game.dialogue.book.BookLine("After some time of", 55), - core.game.dialogue.book.BookLine("successful industry the", 56), - core.game.dialogue.book.BookLine("true power of this ore", 57), - core.game.dialogue.book.BookLine("became apparent, as", 58), - core.game.dialogue.book.BookLine("greater and more", 59), - core.game.dialogue.book.BookLine("powerful weapons were", 60), - core.game.dialogue.book.BookLine("created. Realising the", 61), - core.game.dialogue.book.BookLine("threat this posed, the magi", 62), - core.game.dialogue.book.BookLine("of the time closed down", 63), - core.game.dialogue.book.BookLine("the workshop and bound", 64), - core.game.dialogue.book.BookLine("it under lock and key,", 65) + PageSet( + Page( + BookLine("After some time of", 55), + BookLine("successful industry the", 56), + BookLine("true power of this ore", 57), + BookLine("became apparent, as", 58), + BookLine("greater and more", 59), + BookLine("powerful weapons were", 60), + BookLine("created. Realising the", 61), + BookLine("threat this posed, the magi", 62), + BookLine("of the time closed down", 63), + BookLine("the workshop and bound", 64), + BookLine("it under lock and key,", 65) ), - core.game.dialogue.book.Page( - core.game.dialogue.book.BookLine("also trying to destroy all", 66), - core.game.dialogue.book.BookLine("knowledge of ", 67), - core.game.dialogue.book.BookLine("manufacturing processes.", 68), - core.game.dialogue.book.BookLine("Yet this book remains and", 69), - core.game.dialogue.book.BookLine("you may still find a way", 70), - core.game.dialogue.book.BookLine("to enter the workshop", 71), - core.game.dialogue.book.BookLine("within this leather bound", 72), - core.game.dialogue.book.BookLine("volume.", 73), + Page( + BookLine("also trying to destroy all", 66), + BookLine("knowledge of ", 67), + BookLine("manufacturing processes.", 68), + BookLine("Yet this book remains and", 69), + BookLine("you may still find a way", 70), + BookLine("to enter the workshop", 71), + BookLine("within this leather bound", 72), + BookLine("volume.", 73), ) ) ) /* OFFSETS */ - const val LEFT_WATER_CONTROL_STATE = 3 - const val RIGHT_WATER_CONTROL_STATE = 4 - const val WATER_WHEEL_STATE = 5 - const val FURNACE_STATE = 7 - const val BELLOWS_STATE = 9 + const val LEFT_WATER_CONTROL_STATE = 2058 + const val RIGHT_WATER_CONTROL_STATE = 2059 + const val WATER_WHEEL_STATE = 2060 + const val FURNACE_STATE = 2062 + const val BELLOWS_STATE = 2063 fun leftWaterControlBit(player: Player): Int { - return getVarbitValue(player, Vars.VARP_QUEST_ELEMENTAL_WORKSHOP, LEFT_WATER_CONTROL_STATE) + return getVarbit(player, LEFT_WATER_CONTROL_STATE) } fun rightWaterControlBit(player: Player): Int { - return getVarbitValue(player, Vars.VARP_QUEST_ELEMENTAL_WORKSHOP, RIGHT_WATER_CONTROL_STATE) + return getVarbit(player, RIGHT_WATER_CONTROL_STATE) } fun leftWaterControlEnabled(player: Player): Boolean { - return getVarbitValue(player, Vars.VARP_QUEST_ELEMENTAL_WORKSHOP, LEFT_WATER_CONTROL_STATE) == 1 + return getVarbit(player, LEFT_WATER_CONTROL_STATE) == 1 } fun rightWaterControlEnabled(player: Player): Boolean { - return getVarbitValue(player, Vars.VARP_QUEST_ELEMENTAL_WORKSHOP, RIGHT_WATER_CONTROL_STATE) == 1 + return getVarbit(player, RIGHT_WATER_CONTROL_STATE) == 1 } fun waterWheelEnabled(player: Player): Boolean { - return getVarbitValue(player, Vars.VARP_QUEST_ELEMENTAL_WORKSHOP, WATER_WHEEL_STATE) == 1 + return getVarbit(player, WATER_WHEEL_STATE) == 1 } fun bellowsEnabled(player: Player): Boolean { - return getVarbitValue(player, Vars.VARP_QUEST_ELEMENTAL_WORKSHOP, BELLOWS_STATE) == 1 + return getVarbit(player, BELLOWS_STATE) == 1 } fun currentStage(player: Player): Int { - return player.questRepository.getStage("Elemental Workshop I") + return player.questRepository.getStage(Quests.ELEMENTAL_WORKSHOP_I) } -} \ No newline at end of file +} 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 bfa7fdfe3..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 @@ -1,9 +1,6 @@ package content.region.kandarin.seers.quest.elementalworkshop -import core.api.Commands -import core.api.addItem -import core.api.setAttribute -import core.api.setVarbit +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 @@ -12,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 @@ -28,11 +26,11 @@ import core.game.system.command.Privilege * Quality Assurance: Andrew C * QuestHelp: Rob M * - * 2009scape adaptation: + * 2009Scape adaptation: * @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 @@ -89,7 +87,7 @@ class ElementalWorkshopQuest : Quest("Elemental Workshop I", 52, 51, 1), Command line(player, "---After fixing up the old workshop machinery, collecting ore", line++) line(player, "---and smelting it I was able to create an Elemental Shield./--", line++) line++ - line(player, "!!QUEST COMPLETE!??", line++) + line(player, "%%QUEST COMPLETE!&&", line++) } } } @@ -120,8 +118,7 @@ class ElementalWorkshopQuest : Quest("Elemental Workshop I", 52, 51, 1), Command setAttribute(player, "/save:ew1:got_leather", false) setAttribute(player, "/save:ew1:bellows_fixed", false) player.questRepository.setStageNonmonotonic(player.questRepository.forIndex(52), 0) - player.varpManager.get(Vars.VARP_QUEST_ELEMENTAL_WORKSHOP).clearBitRange(0, 31) - player.varpManager.get(Vars.VARP_QUEST_ELEMENTAL_WORKSHOP).send(player) + setVarp(player, Vars.VARP_QUEST_ELEMENTAL_WORKSHOP, 0) player.teleport(Location.create(2715, 3481, 0)) player.inventory.clear() addItem(player, Items.KNIFE_946) @@ -138,11 +135,11 @@ class ElementalWorkshopQuest : Quest("Elemental Workshop I", 52, 51, 1), Command setAttribute(player, "/save:ew1:got_leather", true) setAttribute(player, "/save:ew1:bellows_fixed", true) player.questRepository.setStageNonmonotonic(player.questRepository.forIndex(52), 95) - setVarbit(player, Vars.VARP_QUEST_ELEMENTAL_WORKSHOP, EWUtils.BELLOWS_STATE, enabled, true) - setVarbit(player, Vars.VARP_QUEST_ELEMENTAL_WORKSHOP, EWUtils.FURNACE_STATE, enabled, true) - setVarbit(player, Vars.VARP_QUEST_ELEMENTAL_WORKSHOP, EWUtils.WATER_WHEEL_STATE, enabled, true) - setVarbit(player, Vars.VARP_QUEST_ELEMENTAL_WORKSHOP, EWUtils.RIGHT_WATER_CONTROL_STATE, enabled, true) - setVarbit(player, Vars.VARP_QUEST_ELEMENTAL_WORKSHOP, EWUtils.LEFT_WATER_CONTROL_STATE, enabled, true) + setVarbit(player, EWUtils.BELLOWS_STATE, enabled, true) + setVarbit(player, EWUtils.FURNACE_STATE, enabled, true) + setVarbit(player, EWUtils.WATER_WHEEL_STATE, enabled, true) + setVarbit(player, EWUtils.RIGHT_WATER_CONTROL_STATE, enabled, true) + setVarbit(player, EWUtils.LEFT_WATER_CONTROL_STATE, enabled, true) } } -} \ No newline at end of file +} diff --git a/Server/src/main/content/region/kandarin/seers/quest/elementalworkshop/SlashedBookHandler.kt b/Server/src/main/content/region/kandarin/seers/quest/elementalworkshop/SlashedBookHandler.kt index afa260d27..01f24b0e6 100644 --- a/Server/src/main/content/region/kandarin/seers/quest/elementalworkshop/SlashedBookHandler.kt +++ b/Server/src/main/content/region/kandarin/seers/quest/elementalworkshop/SlashedBookHandler.kt @@ -1,10 +1,9 @@ package content.region.kandarin.seers.quest.elementalworkshop -import core.game.dialogue.DialoguePlugin -import core.game.dialogue.book.Book -import core.game.dialogue.book.Page +import content.global.handlers.iface.BookInterface +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 /** @@ -12,47 +11,21 @@ import org.rs09.consts.Items * * @author Woah, with love */ -@Initializable -class SlashedBookHandler : core.game.dialogue.book.Book { - constructor(player: Player?) : super(player, "Book of the elemental shield", Items.SLASHED_BOOK_9715, *EWUtils.PAGES) {} +class SlashedBookHandler : InteractionListener { + companion object { + private val TITLE = "Book of the elemental shield" + private val CONTENTS = EWUtils.PAGES - constructor() { - /** - * empty. - */ + private fun display(player:Player, pageNum: Int, buttonID: Int) : Boolean { + BookInterface.pageSetup(player, BookInterface.FANCY_BOOK_3_49, TITLE, CONTENTS) + return true + } } - override fun finish() {} - - override fun display(set: Array) { - player.lock() - player.interfaceManager.open(getInterface()) - - for (i in 55..76) { - player.packetDispatch.sendString("", getInterface().id, i) + override fun defineListeners() { + on(Items.SLASHED_BOOK_9715, IntType.ITEM, "read") { player, _ -> + BookInterface.openBook(player, BookInterface.FANCY_BOOK_3_49, ::display) + return@on true } - player.packetDispatch.sendString("", getInterface().id, 77) - player.packetDispatch.sendString("", getInterface().id, 78) - player.packetDispatch.sendString(getName(), getInterface().id, 6) - for (page in set) { - for (line in page.lines) { - player.packetDispatch.sendString(line.message, getInterface().id, line.child) - } - } - player.packetDispatch.sendInterfaceConfig(getInterface().id, 51, index < 1) - val lastPage = index == sets.size - 1 - player.packetDispatch.sendInterfaceConfig(getInterface().id, 53, lastPage) - if (lastPage) { - finish() - } - player.unlock() - } - - override fun newInstance(player: Player): core.game.dialogue.DialoguePlugin { - return BatteredBookHandler(player) - } - - override fun getIds(): IntArray { - return intArrayOf(EWUtils.SLASHED_BOOK_ID) } } \ No newline at end of file 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 a4d4cf715..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 @@ -16,10 +17,10 @@ import core.tools.START_DIALOGUE class ArheinMCDialogue (val questStage: Int) : DialogueFile() { override fun handle(componentID: Int, buttonID: Int) { when (stage) { - START_DIALOGUE -> playerl(core.game.dialogue.FacialExpression.NEUTRAL, "Can you drop me off on the way down please?").also { stage++ } + START_DIALOGUE -> playerl(FacialExpression.NEUTRAL, "Can you drop me off on the way down please?").also { stage++ } 1 -> { - npcl(core.game.dialogue.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") + 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(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 a9d3369cf..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,9 +57,9 @@ 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, 198); + Shops.openId(player, 56); } else { npc.openShop(player); } @@ -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 @@ -175,7 +176,7 @@ public final class CandleMakerDialogue extends DialoguePlugin { case 30: end(); if (quest.getStage(player) > 60) { - Shops.openId(player, 198); + Shops.openId(player, 56); } else { npc.openShop(player); } 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 d6a07c71a..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.DropItemHandler; 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. @@ -67,14 +63,13 @@ public final class MerlinCrystalPlugin extends OptionHandler { SceneryDefinition.forId(71).getHandlers().put("option:open", this); SceneryDefinition.forId(71).getHandlers().put("option:knock-at", this); SceneryDefinition.forId(72).getHandlers().put("option:knock-at", this); - ItemDefinition.forId(530).getHandlers().put("option:drop", this); SceneryDefinition.forId(62).getHandlers().put("option:smash", this); return this; } @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: @@ -128,24 +123,6 @@ public final class MerlinCrystalPlugin extends OptionHandler { DoorActionHandler.handleAutowalkDoor(player, node.asScenery()); } return true; - case 530: - if (player.getLocation().equals(new Location(2780, 3515, 0)) && quest.getStage(player) == 80) { - if (player.getInventory().contains(32, 1) && player.getAttribute("thrantax_npc") == null) { - ForceMovement.run(player, player.getLocation().transform(-2, 0, 0)).setDirection(Direction.EAST); - NPC npc = new ThrantaxNPC(player, Location.create(2780, 3515, 0)); - npc.init(); - npc.setRespawn(false); - player.setAttribute("thrantax_npc", npc); - npc.setAttribute("thrantax_owner", player.getUsername()); - player.getDialogueInterpreter().open("thrantax_dialogue", 34, 248); - } else { - player.getDialogueInterpreter().sendDialogue("You'll need to light the black candle before the spirit", "will appear."); - } - return true; - } else { - DropItemHandler.drop(player, node.asItem()); - } - return true; case 40026: if (player.getLocation().withinDistance(Location.create(3013, 3245, 0)) && quest.getStage(player) == 60 && player.getAttribute("beggar_npc") == null) { NPC npc = new BeggarNPC(player, Location.create(3012, 3248, 1)); @@ -197,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!"); @@ -347,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)); @@ -468,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; @@ -493,68 +470,6 @@ public final class MerlinCrystalPlugin extends OptionHandler { } } - /** - * Handles the thrantax npc. - * @author Vexia - */ - public final class ThrantaxNPC extends NPC { - - /** - * The player. - */ - private final Player player; - - /** - * Constructs a new {@code AnimatedArmour} {@code Object}. - * @param player The player. - * @param location The location to spawn. - */ - protected ThrantaxNPC(Player player, Location location) { - super(238, location); - this.player = player; - } - - @Override - public boolean isHidden(final Player player) { - if (player.getQuestRepository().getQuest("Merlin's Crystal").getStage(player) == 80 && this.getAttribute("thrantax_owner", "").equals(player.getUsername())) { - return false; - } - return true; - } - - @Override - public void init() { - super.init(); - } - - @Override - public void handleTickActions() { - if (player != null && !player.isActive() || player != null && !player.getLocation().withinDistance(getLocation(), 20)) { - clear(); - } - super.handleTickActions(); - } - - @Override - public void finalizeDeath(Entity entity) { - if (entity instanceof Player) { - Player p = entity.asPlayer(); - super.finalizeDeath(p); - } - } - - @Override - public boolean canAttack(Entity entity) { - return getAttribute("thrantax_owner", entity) == entity; - } - - @Override - public void clear() { - super.clear(); - player.getHintIconManager().clear(); - } - } - /** * Used for item handling during the quest. * @author Adam Rodrigues 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 new file mode 100644 index 000000000..3a24ee325 --- /dev/null +++ b/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/MerlinListeners.kt @@ -0,0 +1,41 @@ +package content.region.kandarin.seers.quest.merlinsquest + +import core.api.* +import core.game.interaction.* +import core.game.world.map.Location +import core.game.world.map.Direction +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, 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 + + if (doingQuest && hasAuxiliaryRequirements) { + ForceMovement.run(player, player.location.transform(-2, 0, 0)).direction = Direction.EAST + var npc = ThrantaxNPC(player, Location(2780, 3515, 0)) + npc.player = player + npc.init() + npc.isRespawn = false + setAttribute(player, "thrantax_npc", npc) + setAttribute(npc, "thrantax_owner", player.username) + player.dialogueInterpreter.open("thrantax_dialogue", 34, 248) + return@on true + } else if (doingQuest) { + sendDialogue(player, "You'll need to light the black candle before the spirit will appear.") + return@on true + } + + DropListener.drop(player, node.asItem()) + return@on true + } + } +} 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 2489694e0..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. @@ -15,7 +16,7 @@ import org.rs09.consts.Items * @author Splinter * @author pain */ -class TheLadyOfTheLake(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { +class TheLadyOfTheLake(player: Player? = null) : DialoguePlugin(player) { override fun open(vararg args: Any): Boolean { npc = args[0] as NPC @@ -24,7 +25,7 @@ class TheLadyOfTheLake(player: Player? = null) : core.game.dialogue.DialoguePlug 0 } else if(player.achievementDiaryManager.getDiary(DiaryType.SEERS_VILLAGE).isComplete(2) && player.equipment.contains(14631,1) && player.equipment.contains(35,1)){ - npcl(core.game.dialogue.FacialExpression.HAPPY,"I am the Lady of the Lake.") + npcl(FacialExpression.HAPPY,"I am the Lady of the Lake.") 110 } else { @@ -35,7 +36,7 @@ class TheLadyOfTheLake(player: Player? = null) : core.game.dialogue.DialoguePlug } 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) { @@ -49,10 +50,10 @@ class TheLadyOfTheLake(player: Player? = null) : core.game.dialogue.DialoguePlug } 3 -> player("Good day.").also { stage = 10000 } } - 100 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"I am the Lady of the Lake.").also { stage = 145 } + 100 -> npcl(FacialExpression.HAPPY,"I am the Lady of the Lake.").also { stage = 145 } 110 -> player("And I'm-").also { stage++ } 111 -> npc( - "You're " + player.name + ". And I see from the sign you", + "You're " + player.username + ". And I see from the sign you", "wear that you have earned the trust of Sir Kay." ).also { stage++ } 112 -> player("It was nothing.. really...").also { stage++ } @@ -82,7 +83,14 @@ class TheLadyOfTheLake(player: Player? = null) : core.game.dialogue.DialoguePlug 117 -> player("Thanks!").also { stage = 10000 } 145 -> options("I seek the sword Excalibur.", "Good day.").also { stage = 150 } 150 -> when (buttonId) { - 1 -> player("I seek the sword Excalibur.").also { stage = 161 } + 1 -> { + player("I seek the sword Excalibur.") + if (quest.getStage(player) < 50) { + stage = 250 + } else { + stage = 161 + } + } 2 -> player("Good day.").also { stage = 10000 } } 161 -> if (quest.getStage(player) == 50 || quest.getStage(player) == 60) { // they haven't proven themselves yet @@ -143,11 +151,11 @@ class TheLadyOfTheLake(player: Player? = null) : core.game.dialogue.DialoguePlug return true } - override fun newInstance(player: Player): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player): DialoguePlugin { return TheLadyOfTheLake(player) } override fun getIds(): IntArray { return intArrayOf(250) } -} \ No newline at end of file +} 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 new file mode 100644 index 000000000..16efc79b6 --- /dev/null +++ b/Server/src/main/content/region/kandarin/seers/quest/merlinsquest/ThrantaxNPC.java @@ -0,0 +1,69 @@ +package content.region.kandarin.seers.quest.merlinsquest; + +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. + * @author Vexia + */ +public class ThrantaxNPC extends NPC { + + /** + * The player. + */ + public Player player; + + /** + * Constructs a new {@code AnimatedArmour} {@code Object}. + * @param player The player. + * @param location The location to spawn. + */ + protected ThrantaxNPC(Player player, Location location) { + super(238, location); + this.player = player; + } + + @Override + public boolean isHidden(final Player player) { + if (player.getQuestRepository().getQuest(Quests.MERLINS_CRYSTAL).getStage(player) == 80 && this.getAttribute("thrantax_owner", "").equals(player.getUsername())) { + return false; + } + return true; + } + + @Override + public void init() { + super.init(); + } + + @Override + public void handleTickActions() { + if (player != null && !player.isActive() || player != null && !player.getLocation().withinDistance(getLocation(), 20)) { + clear(); + } + super.handleTickActions(); + } + + @Override + public void finalizeDeath(Entity entity) { + if (entity instanceof Player) { + Player p = entity.asPlayer(); + super.finalizeDeath(p); + } + } + + @Override + public boolean canAttack(Entity entity) { + return getAttribute("thrantax_owner", entity) == entity; + } + + @Override + public void clear() { + super.clear(); + player.getHintIconManager().clear(); + } +} 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/BrotherMaledictDialogue.kt b/Server/src/main/content/region/kandarin/witchhaven/dialogue/BrotherMaledictDialogue.kt index 17c70f0bd..72e26afca 100644 --- a/Server/src/main/content/region/kandarin/witchhaven/dialogue/BrotherMaledictDialogue.kt +++ b/Server/src/main/content/region/kandarin/witchhaven/dialogue/BrotherMaledictDialogue.kt @@ -13,11 +13,11 @@ import org.rs09.consts.NPCs */ @Initializable -class BrotherMaledictDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class BrotherMaledictDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - npc(core.game.dialogue.FacialExpression.FRIENDLY,"The blessings of Saradomin be with you child.").also { stage = 99 } + npc(FacialExpression.FRIENDLY,"The blessings of Saradomin be with you child.").also { stage = 99 } return true } @@ -28,7 +28,7 @@ class BrotherMaledictDialogue(player: Player? = null) : core.game.dialogue.Dialo return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return BrotherMaledictDialogue(player) } 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 86ca9527d..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) : core.game.dialogue.DialoguePlugin(player){ - - override fun open(vararg args: Any?): Boolean { - npc = args[0] as NPC - player(core.game.dialogue.FacialExpression.FRIENDLY,"Hello again.").also { stage = 0 } - return true - } - - override fun handle(interfaceId: Int, buttonId: Int): Boolean { - when(stage){ - 0 -> npc(core.game.dialogue.FacialExpression.FRIENDLY, "Hello traveller, how are you?").also { stage++ } - 1 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "Not bad thanks, yourself?").also { stage++ } - 2 -> npcl(core.game.dialogue.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?): core.game.dialogue.DialoguePlugin { +class CarolineDialogue(player: Player? = null) : DialoguePlugin(player){ + 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/ColONiallDialogue.kt b/Server/src/main/content/region/kandarin/witchhaven/dialogue/ColONiallDialogue.kt index 9ed6feb21..5dddfe275 100644 --- a/Server/src/main/content/region/kandarin/witchhaven/dialogue/ColONiallDialogue.kt +++ b/Server/src/main/content/region/kandarin/witchhaven/dialogue/ColONiallDialogue.kt @@ -13,40 +13,40 @@ import org.rs09.consts.NPCs */ @Initializable -class ColONiallDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class ColONiallDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - player(core.game.dialogue.FacialExpression.FRIENDLY,"Hello there.").also { stage = 0 } + player(FacialExpression.FRIENDLY,"Hello there.").also { stage = 0 } return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 0 -> npc(core.game.dialogue.FacialExpression.FRIENDLY, "Hello. What can I do for you?").also { stage++ } - 1 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "Oh, I'm just wondering what you're doing.").also { stage++ } - 2 -> npc(core.game.dialogue.FacialExpression.FRIENDLY, "A spot of fishing.").also { stage++ } - 3 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "That doesn't look much like a fishing rod.").also { stage++ } - 4 -> npc(core.game.dialogue.FacialExpression.FRIENDLY, "That my friend, depends on what you're fishing for.").also { stage++ } - 5 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "And what would that be?").also { stage++ } - 6 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "A little of this, a little of that; the usual things.").also { stage++ } - 7 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "Have you caught much?").also { stage++ } - 8 -> npc(core.game.dialogue.FacialExpression.FRIENDLY, "The odd bite here and there. Hmm.").also { stage++ } - 9 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "What?").also { stage++ } - 10 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "You look like a capable lad. Tell you what, when you've got a bit more experience under your belt, get yourself over to Falador.").also { stage++ } - 11 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "What's in Falador?").also { stage++ } - 12 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "An old friend of mine. You'll find him sitting on a bench in Falador Park. See what he can do for you.").also { stage++ } - 13 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "What's his name? Who should I say sent me?").also { stage++ } - 14 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "None of that matters if you can find him and if you're ready my name isn't necessary.").also { stage++ } - 15 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "Oh right. I'll get going then. Goodbye.").also { stage++ } - 16 -> npc(core.game.dialogue.FacialExpression.FRIENDLY, "Goodbye and good luck.").also { stage = 99 } + 0 -> npc(FacialExpression.FRIENDLY, "Hello. What can I do for you?").also { stage++ } + 1 -> player(FacialExpression.FRIENDLY, "Oh, I'm just wondering what you're doing.").also { stage++ } + 2 -> npc(FacialExpression.FRIENDLY, "A spot of fishing.").also { stage++ } + 3 -> player(FacialExpression.FRIENDLY, "That doesn't look much like a fishing rod.").also { stage++ } + 4 -> npc(FacialExpression.FRIENDLY, "That my friend, depends on what you're fishing for.").also { stage++ } + 5 -> player(FacialExpression.FRIENDLY, "And what would that be?").also { stage++ } + 6 -> npcl(FacialExpression.FRIENDLY, "A little of this, a little of that; the usual things.").also { stage++ } + 7 -> player(FacialExpression.FRIENDLY, "Have you caught much?").also { stage++ } + 8 -> npc(FacialExpression.FRIENDLY, "The odd bite here and there. Hmm.").also { stage++ } + 9 -> player(FacialExpression.FRIENDLY, "What?").also { stage++ } + 10 -> npcl(FacialExpression.FRIENDLY, "You look like a capable lad. Tell you what, when you've got a bit more experience under your belt, get yourself over to Falador.").also { stage++ } + 11 -> player(FacialExpression.FRIENDLY, "What's in Falador?").also { stage++ } + 12 -> npcl(FacialExpression.FRIENDLY, "An old friend of mine. You'll find him sitting on a bench in Falador Park. See what he can do for you.").also { stage++ } + 13 -> player(FacialExpression.FRIENDLY, "What's his name? Who should I say sent me?").also { stage++ } + 14 -> npcl(FacialExpression.FRIENDLY, "None of that matters if you can find him and if you're ready my name isn't necessary.").also { stage++ } + 15 -> player(FacialExpression.FRIENDLY, "Oh right. I'll get going then. Goodbye.").also { stage++ } + 16 -> npc(FacialExpression.FRIENDLY, "Goodbye and good luck.").also { stage = 99 } 99 -> end() } return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return ColONiallDialogue(player) } diff --git a/Server/src/main/content/region/kandarin/witchhaven/dialogue/EzekialLovecraftDialogue.kt b/Server/src/main/content/region/kandarin/witchhaven/dialogue/EzekialLovecraftDialogue.kt index 4d3ab3772..4f9c68fbe 100644 --- a/Server/src/main/content/region/kandarin/witchhaven/dialogue/EzekialLovecraftDialogue.kt +++ b/Server/src/main/content/region/kandarin/witchhaven/dialogue/EzekialLovecraftDialogue.kt @@ -13,11 +13,11 @@ import org.rs09.consts.NPCs */ @Initializable -class EzekialLovecraftDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class EzekialLovecraftDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - npcl(core.game.dialogue.FacialExpression.FRIENDLY,"Well hello there; welcome to our little village. Pray, stay awhile.").also { stage = 0 } + npcl(FacialExpression.FRIENDLY,"Well hello there; welcome to our little village. Pray, stay awhile.").also { stage = 0 } return true } @@ -26,26 +26,26 @@ class EzekialLovecraftDialogue(player: Player? = null) : core.game.dialogue.Dial 0 -> options("What do you do here?", "Can I buy some fishing supplies please?", "You are a bit too strange for me. Bye.").also { stage++ } 1 -> when (buttonId) { - 1 -> player(core.game.dialogue.FacialExpression.ASKING, "What do you do here?").also { stage = 10 } - 2 -> player(core.game.dialogue.FacialExpression.HALF_ASKING, "Can I buy some fishing supplies please?").also { stage = 25 } - 3 -> player(core.game.dialogue.FacialExpression.ANNOYED, "You are a bit too strange for me. Bye.").also { stage = 30 } + 1 -> player(FacialExpression.ASKING, "What do you do here?").also { stage = 10 } + 2 -> player(FacialExpression.HALF_ASKING, "Can I buy some fishing supplies please?").also { stage = 25 } + 3 -> player(FacialExpression.ANNOYED, "You are a bit too strange for me. Bye.").also { stage = 30 } } - 10 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "I supply the local fishermen with the tools and bait they need to do their job.").also { stage++ } - 11 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "Interesting. Have you been doing it long?").also { stage++ } - 12 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Why yes, all of my life. I took over from my father, who inherited the business from his father and so on. In fact, there have been Lovecraft's selling bait for over ten generations.").also { stage++ } - 13 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "Wow, that's some lineage.").also { stage++ } - 14 -> npcl(core.game.dialogue.FacialExpression.LAUGH, "Oh yes, we have a long and interesting family history. For one reason or another the Lovecraft's have always been bait sellers or writers.").also { stage++ } - 15 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY, "Hmm, yes well. I'm sure it's all fascinating, but I...").also { stage++ } - 16 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Oh very, very fascinating, for instance my great grandfather Howard...").also { stage++ } - 17 -> playerl(core.game.dialogue.FacialExpression.HALF_ROLLING_EYES, "Yawn. Oh, I'm sorry but I really must be getting on. I think my giraffe needs feeding.").also { stage++ } - 18 -> npc(core.game.dialogue.FacialExpression.HALF_ASKING, "Your?").also { stage++ } - 19 -> playerl(core.game.dialogue.FacialExpression.ANNOYED, "Giraffe. Sorry, but he gets cranky without enough sugar. Bye now!").also { stage++ } - 20 -> npc(core.game.dialogue.FacialExpression.FRIENDLY, "Oh right, goodbye.").also { stage = 99 } + 10 -> npcl(FacialExpression.FRIENDLY, "I supply the local fishermen with the tools and bait they need to do their job.").also { stage++ } + 11 -> player(FacialExpression.FRIENDLY, "Interesting. Have you been doing it long?").also { stage++ } + 12 -> npcl(FacialExpression.FRIENDLY, "Why yes, all of my life. I took over from my father, who inherited the business from his father and so on. In fact, there have been Lovecraft's selling bait for over ten generations.").also { stage++ } + 13 -> player(FacialExpression.FRIENDLY, "Wow, that's some lineage.").also { stage++ } + 14 -> npcl(FacialExpression.LAUGH, "Oh yes, we have a long and interesting family history. For one reason or another the Lovecraft's have always been bait sellers or writers.").also { stage++ } + 15 -> playerl(FacialExpression.FRIENDLY, "Hmm, yes well. I'm sure it's all fascinating, but I...").also { stage++ } + 16 -> npcl(FacialExpression.FRIENDLY, "Oh very, very fascinating, for instance my great grandfather Howard...").also { stage++ } + 17 -> playerl(FacialExpression.HALF_ROLLING_EYES, "Yawn. Oh, I'm sorry but I really must be getting on. I think my giraffe needs feeding.").also { stage++ } + 18 -> npc(FacialExpression.HALF_ASKING, "Your?").also { stage++ } + 19 -> playerl(FacialExpression.ANNOYED, "Giraffe. Sorry, but he gets cranky without enough sugar. Bye now!").also { stage++ } + 20 -> npc(FacialExpression.FRIENDLY, "Oh right, goodbye.").also { stage = 99 } 25 -> end().also { npc.openShop(player) } - 30 -> npc(core.game.dialogue.FacialExpression.FRIENDLY, "Sniff. Yes, everyone says that.").also { stage = 99 } + 30 -> npc(FacialExpression.FRIENDLY, "Sniff. Yes, everyone says that.").also { stage = 99 } @@ -54,7 +54,7 @@ class EzekialLovecraftDialogue(player: Player? = null) : core.game.dialogue.Dial return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return EzekialLovecraftDialogue(player) } 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 83c827b9f..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) : core.game.dialogue.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(core.game.dialogue.FacialExpression.FRIENDLY,"Hello there.").also { stage = 0 } - return true - } - - override fun handle(interfaceId: Int, buttonId: Int): Boolean { - when(stage){ - 0 -> npc(core.game.dialogue.FacialExpression.FRIENDLY, "Well hello " + gender() + ", beautiful day isn't it?").also { stage++ } - 1 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "Not bad I suppose.").also { stage++ } - 2 -> npc(core.game.dialogue.FacialExpression.FRIENDLY, "Just smell that sea air... beautiful.").also { stage++ } - 3 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "Hmm... lovely...").also { stage = 99 } - - 99 -> end() - } - return true - } - - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { +class HolgartDialogue(player: Player? = null) : DialoguePlugin(player){ + 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/JebDialogue.kt b/Server/src/main/content/region/kandarin/witchhaven/dialogue/JebDialogue.kt index c750ebf71..970d61f4d 100644 --- a/Server/src/main/content/region/kandarin/witchhaven/dialogue/JebDialogue.kt +++ b/Server/src/main/content/region/kandarin/witchhaven/dialogue/JebDialogue.kt @@ -13,11 +13,11 @@ import org.rs09.consts.NPCs */ @Initializable -class JebDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class JebDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - npc(core.game.dialogue.FacialExpression.FRIENDLY,"Hello there.").also { stage = 99 } + npc(FacialExpression.FRIENDLY,"Hello there.").also { stage = 99 } return true } @@ -28,7 +28,7 @@ class JebDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(pl return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return JebDialogue(player) } 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/MayorHobbDialogue.kt b/Server/src/main/content/region/kandarin/witchhaven/dialogue/MayorHobbDialogue.kt index d1367e81e..dafb2f88f 100644 --- a/Server/src/main/content/region/kandarin/witchhaven/dialogue/MayorHobbDialogue.kt +++ b/Server/src/main/content/region/kandarin/witchhaven/dialogue/MayorHobbDialogue.kt @@ -13,11 +13,11 @@ import org.rs09.consts.NPCs */ @Initializable -class MayorHobbDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class MayorHobbDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - npcl(core.game.dialogue.FacialExpression.FRIENDLY,"Well hello there; welcome to our little village. Pray, stay awhile.").also { stage = 99 } + npcl(FacialExpression.FRIENDLY,"Well hello there; welcome to our little village. Pray, stay awhile.").also { stage = 99 } return true } @@ -28,7 +28,7 @@ class MayorHobbDialogue(player: Player? = null) : core.game.dialogue.DialoguePlu return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return MayorHobbDialogue(player) } 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 68f9b6d42..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) : core.game.dialogue.DialoguePlugin(player){ - - private val conversations = arrayOf (0, 7, 11, 19, 24) - - override fun open(vararg args: Any?): Boolean { - npc = args[0] as NPC - player(core.game.dialogue.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(core.game.dialogue.FacialExpression.NEUTRAL, "Ye mariners all, as ye pass by,").also { stage++ } - 2 -> npc(core.game.dialogue.FacialExpression.NEUTRAL, "Come in and drink if you are dry,").also { stage++ } - 3 -> npc(core.game.dialogue.FacialExpression.NEUTRAL, "Come spend, me lads, your money brisk,").also { stage++ } - 4 -> npc(core.game.dialogue.FacialExpression.NEUTRAL, "And pop your nose in a jug of this.").also { stage++ } - 5 -> player(core.game.dialogue.FacialExpression.NEUTRAL, "You're not fooling anyone you know.").also { stage++ } - 6 -> npc(core.game.dialogue.FacialExpression.NEUTRAL, "We fooled you easily enough.").also { stage = 99 } - - 7 -> sendDialogue("Their eyes are staring vacantly into space.").also { stage++ } - 8 -> npc(core.game.dialogue.FacialExpression.NEUTRAL, "Free. She is free...").also { stage++ } - 9 -> player(core.game.dialogue.FacialExpression.NEUTRAL, "What?").also { stage++ } - 10 -> npc(core.game.dialogue.FacialExpression.NEUTRAL, "The mother is free.").also { stage = 99 } - - 11 -> sendDialogue("Their eyes are staring vacantly into space.").also { stage++ } - 12 -> npc(core.game.dialogue.FacialExpression.NEUTRAL, "You! You did it!").also { stage++ } - 13 -> player(core.game.dialogue.FacialExpression.NEUTRAL, "I didn't mean to!").also { stage++ } - 14 -> npc(core.game.dialogue.FacialExpression.NEUTRAL, "You killed him!").also { stage++ } - 15 -> player(core.game.dialogue.FacialExpression.NEUTRAL, "It was an accide... Killed who?").also { stage++ } - 16 -> npc(core.game.dialogue.FacialExpression.NEUTRAL, "Our Prince, you killed our Prince.").also { stage++ } - 17 -> player(core.game.dialogue.FacialExpression.NEUTRAL, "Oh that, yes I did.").also { stage++ } - 18 -> npc(core.game.dialogue.FacialExpression.NEUTRAL, "Leave us alone.").also { stage = 99 } - - 19 -> sendDialogue("Their eyes are staring vacantly into space.").also { stage++ } - 20 -> npc(core.game.dialogue.FacialExpression.NEUTRAL, "Soon now... So soon...").also { stage++ } - 21 -> npc(core.game.dialogue.FacialExpression.NEUTRAL, "The stars are almost right.").also { stage++ } - 22 -> player(core.game.dialogue.FacialExpression.NEUTRAL, "For what?").also { stage++ } - 23 -> npc(core.game.dialogue.FacialExpression.NEUTRAL, ". . .").also { stage = 99 } - - 24 -> sendDialogue("Their eyes are staring vacantly into space.").also { stage++ } - 25 -> npc(core.game.dialogue.FacialExpression.NEUTRAL, "Ahh, our saviour.").also { stage++ } - 26 -> player(core.game.dialogue.FacialExpression.NEUTRAL, "Please don't remind me.").also { stage++ } - 27 -> npc(core.game.dialogue.FacialExpression.NEUTRAL, "Do not worry, soon your regret will be gone.").also { stage++ } - 28 -> player(core.game.dialogue.FacialExpression.NEUTRAL, "If you think you will get to me...").also { stage++ } - 29 -> npc(core.game.dialogue.FacialExpression.NEUTRAL, "All in good time.").also { stage = 99 } - - - 99 -> end() - } - return true - } - - override fun newInstance(player: Player?): core.game.dialogue.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/FenitasDialogue.java b/Server/src/main/content/region/kandarin/yanille/dialogue/FenitasDialogue.java index c3f117ace..a562fe43c 100644 --- a/Server/src/main/content/region/kandarin/yanille/dialogue/FenitasDialogue.java +++ b/Server/src/main/content/region/kandarin/yanille/dialogue/FenitasDialogue.java @@ -39,7 +39,7 @@ public final class FenitasDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Would you like to buy some cooking equipment?"); + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Would you like to buy some cooking equipment?"); stage = 0; return true; } @@ -48,7 +48,7 @@ public final class FenitasDialogue extends DialoguePlugin { public boolean handle(int interfaceId, int buttonId) { switch (stage) { case 0: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Yes please."); + interpreter.sendDialogues(player, FacialExpression.HAPPY, "Yes please."); stage = 1; break; case 1: 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 836b9280a..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.HALF_GUILTY, "What is this place?"); - stage = 10; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Can I have a go with your crossbow?"); - stage = 20; - break; - case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "What are you holding there?"); - stage = 30; - break; - - } - break; - case 10: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "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.HALF_GUILTY, "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/kandarin/yanille/handlers/YanilleAgilityDungeon.kt b/Server/src/main/content/region/kandarin/yanille/handlers/YanilleAgilityDungeon.kt index 1c26cee2c..509ea4e58 100644 --- a/Server/src/main/content/region/kandarin/yanille/handlers/YanilleAgilityDungeon.kt +++ b/Server/src/main/content/region/kandarin/yanille/handlers/YanilleAgilityDungeon.kt @@ -10,7 +10,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.node.entity.state.EntityState import core.game.node.item.Item import core.game.node.scenery.Scenery import core.game.node.scenery.SceneryBuilder @@ -32,7 +31,7 @@ val SINISTER_CHEST_HERBS = arrayOf(Item(205, 2), Item(207, 3), Item(209), Item(2 public class YanilleAgilityDungeonListeners : InteractionListener { override fun defineListeners() { - ZoneBuilder.configure(YanilleAgilityDungeon()); + ZoneBuilder.configure(YanilleAgilityDungeon()) on(1728, IntType.SCENERY, "climb-down") { player, _ -> player.teleport(Location(2620, 9565, 0)) @@ -66,17 +65,17 @@ public class YanilleAgilityDungeonListeners : InteractionListener { player.sendMessage("The chest is locked.") } else { if (player.getInventory().freeSlots() == 0) { - player.sendMessage("You don't have enough inventory space."); + player.sendMessage("You don't have enough inventory space.") return@on true } player.lock(1) if(player.inventory.remove(Item(Items.SINISTER_KEY_993, 1))) { - player.sendMessages("You unlock the chest with your key...", "A foul gas seeps from the chest"); - player.getStateManager().register(EntityState.POISONED, true, 28, player); + player.sendMessages("You unlock the chest with your key...", "A foul gas seeps from the chest") + applyPoison (player, player, 2) for(item in SINISTER_CHEST_HERBS) { addItemOrDrop(player, item.id, item.amount) } - SceneryBuilder.replace(target.asScenery(), target.asScenery().transform(target.id + 1), 5); + SceneryBuilder.replace(target.asScenery(), target.asScenery().transform(target.id + 1), 5) } } return@on true @@ -98,14 +97,14 @@ public class YanilleAgilityDungeonListeners : InteractionListener { */ fun handleBalancingLedge(player: Player, scenery: Scenery) { if (player.skills.getLevel(Skills.AGILITY) < 40) { - player.getDialogueInterpreter().sendDialogue("You need an Agility level of at least 40 in order to do this."); - return; - } + player.getDialogueInterpreter().sendDialogue("You need an Agility level of at least 40 in order to do this.") + return + } val dir = Direction.getLogicalDirection(player.location, scenery.location) val diff = if(player.location.y == 9512) { 0 } else { 1 } - var end = scenery.location; - var xp = 0.0; - if (AgilityHandler.hasFailed(player, 40, 0.01)) { + var end = scenery.location + var xp = 0.0 + if (AgilityHandler.hasFailed(player, 40, 0.01)) { player.lock(3) GameWorld.Pulser.submit(object : Pulse(2, player) { override public fun pulse(): Boolean { @@ -114,11 +113,11 @@ public class YanilleAgilityDungeonListeners : InteractionListener { } }) } else { - xp = 22.5; - end = scenery.location.transform(dir.getStepX() * 7, dir.getStepY() * 7, 0); - } - AgilityHandler.walk(player, -1, player.location, end, Animation.create(157 - diff), xp, null); - } + xp = 22.5 + end = scenery.location.transform(dir.getStepX() * 7, dir.getStepY() * 7, 0) + } + AgilityHandler.walk(player, -1, player.location, end, Animation.create(157 - diff), xp, null) + } } @@ -174,7 +173,7 @@ public class SalarinTwistedNPC : AbstractNPC { } override public fun getIds(): IntArray { - return intArrayOf(205); + return intArrayOf(205) } } 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/BrimhavenBartenderDialogue.java b/Server/src/main/content/region/karamja/brimhaven/dialogue/BrimhavenBartenderDialogue.java index 4499c5f39..231610533 100644 --- a/Server/src/main/content/region/karamja/brimhaven/dialogue/BrimhavenBartenderDialogue.java +++ b/Server/src/main/content/region/karamja/brimhaven/dialogue/BrimhavenBartenderDialogue.java @@ -60,7 +60,7 @@ public final class BrimhavenBartenderDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Yohoho me hearty what would you like to drink?"); + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Yohoho me hearty what would you like to drink?"); stage = 99; return true; } @@ -75,15 +75,15 @@ public final class BrimhavenBartenderDialogue extends DialoguePlugin { case 0: switch (buttonId) { case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Nothing, thank you."); + interpreter.sendDialogues(player, FacialExpression.FRIENDLY, "Nothing, thank you."); stage = 10; break; case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "A pint of Grog please.."); + interpreter.sendDialogues(player, FacialExpression.HAPPY, "A pint of Grog please.."); stage = 20; break; case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "A bottle of rum please."); + interpreter.sendDialogues(player, FacialExpression.HAPPY, "A bottle of rum please."); stage = 30; break; } @@ -92,7 +92,7 @@ public final class BrimhavenBartenderDialogue extends DialoguePlugin { end(); break; case 20: - interpreter.sendDialogues(npc, null, "One grog coming right up, that'll be three coins."); + interpreter.sendDialogues(npc, FacialExpression.FRIENDLY, "One grog coming right up, that'll be three coins."); stage = 21; break; case 21: @@ -115,7 +115,7 @@ public final class BrimhavenBartenderDialogue extends DialoguePlugin { end(); break; case 30: - interpreter.sendDialogues(npc, null, "That'll be 27 coins."); + interpreter.sendDialogues(npc, FacialExpression.FRIENDLY, "That'll be 27 coins."); stage = 31; break; case 31: diff --git a/Server/src/main/content/region/karamja/brimhaven/dialogue/CapnIzzyDialogue.java b/Server/src/main/content/region/karamja/brimhaven/dialogue/CapnIzzyDialogue.java deleted file mode 100644 index 88155d7c6..000000000 --- a/Server/src/main/content/region/karamja/brimhaven/dialogue/CapnIzzyDialogue.java +++ /dev/null @@ -1,264 +0,0 @@ -package content.region.karamja.brimhaven.dialogue; - -import core.game.dialogue.DialoguePlugin; -import core.game.dialogue.FacialExpression; -import core.game.global.Skillcape; -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.item.Item; - -/** - * Represents the dialogue plugin used for the capn izzy dialogue. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class CapnIzzyDialogue extends DialoguePlugin { - - /** - * Represents the parrot npc id. - */ - private static final int PARROT = 4535; - - /** - * Constructs a new {@code CapnIzzyDialogue.java} {@code Object}. - */ - public CapnIzzyDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code CapnIzzyDialogue.java} {@code Object}. - * @param player the player. - */ - public CapnIzzyDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new CapnIzzyDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - if (args.length > 1) { - interpreter.sendDialogues(PARROT, null, "Clap him in irons!"); - stage = 200; - return true; - } - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Ahoy Cap'n!"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Ahoy there!"); - stage = 90; - break; - case 90: - interpreter.sendDialogues(PARROT, null, "Avast ye scurvy swabs!"); - stage = 91; - break; - case 91: - interpreter.sendDialogues(player, null, "Huh?"); - stage = 92; - break; - case 92: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Don't mind me parrot, he's Cracked Jenny's Tea Cup!"); - stage = 1; - break; - case 1: - interpreter.sendOptions("Select an Option", "What is this place?", "What do I do in the arena?", "I'd like to use the Agility Arena, please.", "Can you tell me a bit about the Skillcape of Agility, please.", "See you later."); - stage = 2; - break; - case 2: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "What is this place?"); - stage = 10; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "What do I do in the arena?"); - stage = 20; - break; - case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I'd like to use the Agility Arena, please."); - stage = 30; - break; - case 4: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Can you tell me a bit about the Skillcape of Agility,", "please?"); - stage = 40; - break; - case 5: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "See you later."); - stage = 50; - break; - } - break; - case 10: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "This, me hearty, is the entrance to the Brimhaven", "Agility Arena!"); - stage = 11; - break; - case 11: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "I were diffin for buried treasure when I found it!", "Amazed I was! It was a sight to behold!"); - stage = 12; - break; - case 12: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "It were the biggest thing I'd ever seen! it must've been", "at least a league from side to side!"); - stage = 13; - break; - case 13: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "It made me list, I were that excited!"); - stage = 14; - break; - case 14: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "I'd found a huge cave with all these platforms. I reckon", "it be an ancient civilisation that made it. I had to be", "mighty careful as there was these traps everywehre!", "Dangerous it was!"); - stage = 15; - break; - case 15: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Entrance is only 200 coins!"); - stage = 1; - break; - case 20: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Well, me hearty, it's simple. Ye can cross between two", "platforms by using the traps or obstacles strung across", "'em. Try and make your way to the pillar that is", "indicated by the flashing arrow."); - stage = 21; - break; - case 21: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Ye receive tickets for tagging more than one pillar in a", "row. So ye won't get a ticket from the first pillar but", "ye will for every platform ye tag in a row after that."); - stage = 22; - break; - case 22: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "If ye miss a platform ye will miss out on the next ticket", "so try and get every platform you can! When ye be", "done, take the tickets to Jackie over there and she'll", "exchange them for more stuff!"); - stage = 23; - break; - case 23: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Thanks me hearty!"); - stage = 24; - break; - case 24: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Thanks!"); - stage = 1; - break; - case 30: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Aye, entrance be 200 coins."); - stage = 31; - break; - case 31: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "A word of warning me hearty! There are dangerous", "traps down there!"); - stage = 32; - break; - case 32: - interpreter.sendOptions("Select an Option", "Ok, here's 200 coins.", "Never mind."); - stage = 33; - break; - case 33: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Ok, here's 200 coins."); - stage = 35; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Never mind."); - stage = 34; - break; - } - break; - case 34: - end(); - break; - case 35: - if (!player.getAttribute("capn_izzy", false)) { - if (player.getInventory().contains(995, 200) && player.getInventory().remove(new Item(995, 200))) { - end(); - player.getDialogueInterpreter().sendItemMessage(new Item(995, 200), "You give Cap'n Izzy the 200 coin entrance fee."); - player.getPacketDispatch().sendMessage("You give Cap'n Izzy the 200 coin entrance fee."); - player.setAttribute("/save:capn_izzy", true); - return true; - } else { - player.getPacketDispatch().sendMessage("You don't have the 200 coin entrance fee."); - end(); - } - } else { - player.getDialogueInterpreter().sendDialogues(npc, null, "Avast there, ye've already paid!"); - stage = 36; - } - break; - case 36: - end(); - break; - case 40: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Aye, to be sure! The Skillcape of Agility be the symbol", "of the master of dexterity! One who wears it can climb", "like a cat, run like the wind and jump like...err, well", "jump like a jumping thing! Now, be there anything else"); - stage = 41; - break; - case 41: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "ye'd like to know?"); - if (!Skillcape.isMaster(player, Skills.AGILITY)) { - stage = 1; - } else { - stage = 99; - } - break; - case 99: - interpreter.sendDialogues(player, null, "I'd like to buy an Agility Skillcape."); - stage = 100; - break; - case 100: - interpreter.sendDialogues(npc, null, "That will cost you 99,000 gold coins."); - stage = 101; - break; - case 101: - interpreter.sendOptions("Select an Option", "Okay, I'll pay it.", "Nevermind."); - stage = 102; - break; - case 102: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, null, "Okay, I'll pay it."); - stage = 104; - break; - case 2: - interpreter.sendDialogues(player, null, "Nevermind."); - stage = 103; - break; - } - break; - case 104: - if (Skillcape.purchase(player, Skills.AGILITY)) { - interpreter.sendDialogues(npc, null, "There you go hearty! You're truly a master of Agility."); - } - stage = 105; - break; - case 105: - end(); - break; - case 103: - end(); - break; - case 50: - end(); - break; - case 200: - interpreter.sendDialogues(npc, null, "Ahoy there! Pay up first!"); - stage = 201; - break; - case 201: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 437 }; - } -} diff --git a/Server/src/main/content/region/karamja/brimhaven/dialogue/CapnIzzyDialogue.kt b/Server/src/main/content/region/karamja/brimhaven/dialogue/CapnIzzyDialogue.kt new file mode 100644 index 000000000..c74885eb5 --- /dev/null +++ b/Server/src/main/content/region/karamja/brimhaven/dialogue/CapnIzzyDialogue.kt @@ -0,0 +1,159 @@ +package content.region.karamja.brimhaven.dialogue + +import core.api.* +import core.game.dialogue.DialogueFile +import core.game.dialogue.FacialExpression +import core.game.global.Skillcape +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 org.rs09.consts.Items +import org.rs09.consts.NPCs + +class CapnIzzyDialogue(private val it: Int) : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (it) { + 0 -> when (stage) { + 0 -> playerl(FacialExpression.HAPPY, "Ahoy Cap'n!").also { stage++ } + 1 -> npcl(FacialExpression.HAPPY, "Ahoy there!").also { stage++ } + 2 -> npcl(NPCs.PARROT_4535, FacialExpression.ANGRY, "Avast ye scurvy swabs!").also { stage++ } + 3 -> playerl(FacialExpression.THINKING, "Huh?").also { stage++ } + 4 -> npcl(FacialExpression.HAPPY, "Don't mind me parrot, he's Cracked Jenny's Tea Cup!").also { stage++ } + + 5 -> { + if (Skillcape.isMaster(player!!, Skills.AGILITY)) { + options( + "What is this place?", + "What do I do in the arena?", + "I'd like to use the Agility Arena, please.", + "Is it true you sell the Skillcape of Agility?", + "See you later." + ).also { stage++ } + } else { + options( + "What is this place?", + "What do I do in the arena?", + "I'd like to use the Agility Arena, please.", + "Can you tell me a bit about the Skillcape of Agility, please?", + "See you later." + ).also { stage = 7 } + } + } + + 6 -> when (buttonID) { + 1 -> playerl(FacialExpression.NEUTRAL, "What is this place?").also { stage = 10 } + 2 -> playerl(FacialExpression.NEUTRAL, "What do I do in the arena?").also { stage = 20 } + 3 -> playerl(FacialExpression.NEUTRAL, "I'd like to use the Agility Arena, please.").also { stage = 30 } + 4 -> playerl(FacialExpression.HALF_ASKING, "May I buy a Skillcape of Agility, please?").also { stage = 40 } + 5 -> playerl(FacialExpression.NEUTRAL, "See you later.").also { stage = END_DIALOGUE } + } + + 7 -> when (buttonID) { + 1 -> playerl(FacialExpression.NEUTRAL, "What is this place?").also { stage = 10 } + 2 -> playerl(FacialExpression.NEUTRAL, "What do I do in the arena?").also { stage = 20 } + 3 -> playerl(FacialExpression.NEUTRAL, "I'd like to use the Agility Arena, please.").also { stage = 30 } + 4 -> playerl(FacialExpression.NEUTRAL, "Can you tell me a bit about the Skillcape of Agility, please?").also { stage = 50 } + 5 -> playerl(FacialExpression.NEUTRAL, "See you later.").also { stage = END_DIALOGUE } + } + + 10 -> npcl(FacialExpression.NEUTRAL, "This, me hearty, is the entrance to the Brimhaven, Agility Arena!").also { stage++ } + 11 -> npcl(FacialExpression.NEUTRAL, "I were diggin for buried treasure when I found it! Amazed I was! It was a sight to behold!").also { stage++ } + 12 -> npcl(FacialExpression.NEUTRAL, "It were the biggest thing I'd ever seen! it must've been at least a league from side to side!").also { stage++ } + 13 -> npcl(FacialExpression.NEUTRAL, "It made me list, I were that excited!").also { stage++ } + 14 -> npcl(FacialExpression.NEUTRAL, "I'd found a huge cave with all these platforms. I reckon it be an ancient civilisation that made it. I had " + + "to be mighty careful as there was these traps everywhere! Dangerous it was!").also { stage++ } + + 15 -> npcl(FacialExpression.NEUTRAL, "Entrance is only 200 coins!").also { stage = 5 } + 20 -> npcl(FacialExpression.NEUTRAL, "Well, me hearty, it's simple. Ye can cross between two platforms by using the traps or obstacles " + + "strung across 'em. Try and make your way to the pillar that is indicated by the flashing arrow.").also { stage++ } + 21 -> npcl(FacialExpression.NEUTRAL, "Ye receive tickets for tagging more than one pillar in a row. So ye won't get a ticket from the " + + "first pillar but ye will for every platform ye tag in a row after that.").also { stage++ } + 22 -> npcl(FacialExpression.NEUTRAL, "If ye miss a platform ye will miss out on the next ticket so try and get every platform you can! " + + "When ye be done, take the tickets to Jackie over there and she'll exchange them for more stuff!").also { stage++ } + 23 -> playerl(FacialExpression.NEUTRAL, "Thanks!").also { stage = 5 } + 30 -> npcl(FacialExpression.NEUTRAL, "Aye, entrance be 200 coins.").also { stage++ } + 31 -> npcl(NPCs.PARROT_4535, FacialExpression.ANGRY, "Pieces of eight!").also { stage++ } + 32 -> npcl(FacialExpression.AMAZED, "A word of warning me hearty! There are dangerous traps down there!").also { stage++ } + 33 -> options("Ok, here's 200 coins.", "Never mind.").also { stage++ } + 34 -> when (buttonID) { + 1 -> playerl(FacialExpression.NEUTRAL, "Ok, here's 200 coins.").also { stage++ } + 2 -> playerl(FacialExpression.NEUTRAL, "Never mind.").also { stage = END_DIALOGUE } + } + + 35 -> { + if (!getAttribute(player!!, "capn_izzy", false)) { + if (inInventory(player!!, Items.COINS_995, 200) && removeItem(player!!, Item(Items.COINS_995, 200))) { + sendItemDialogue(player!!, Items.COINS_6964, "You give Cap'n Izzy 200 coins.").also { stage++ } + npcl(FacialExpression.HAPPY, "May the wind be in ye sails!").also { stage = END_DIALOGUE } + sendMessage(player!!, "You give Cap'n Izzy 200 coins.") + setAttribute(player!!, "/save:capn_izzy", true) + } else { + end() + sendMessage(player!!, "You don't have the 200 coin entrance fee.") + } + } else { + npcl(FacialExpression.NEUTRAL, "Avast there, ye've already paid!").also { stage = END_DIALOGUE } + } + } + + 40 -> npcl(FacialExpression.NEUTRAL, "Most certainly, I think it'll really suit you. All that remains to be done is pay me 99000 gold!").also { stage++ } + 41 -> options("I'm afraid I can't afford that.", "Certainly, here you go.").also { stage++ } + 42 -> when (buttonID) { + 1 -> playerl(FacialExpression.NEUTRAL, "I'm afraid I can't afford that.").also { stage++ } + 2 -> playerl(FacialExpression.HAPPY, "Certainly, here you go.").also { stage = 44 } + } + + 43 -> npcl(FacialExpression.NEUTRAL, "So sad, too bad. Go and pickpocket some wealthy people and come back here once you're richer.").also { stage = END_DIALOGUE } + 44 -> { + when { + (!inInventory(player!!, Items.COINS_995, 99000)) -> { + playerl(FacialExpression.NEUTRAL, "But, unfortunately, I don't have enough money with me.").also { stage = 45 } + } + + (freeSlots(player!!)) < 2 -> { + npcl(FacialExpression.NEUTRAL, "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.AGILITY) + npcl(FacialExpression.HAPPY, "Excellent! That cape really does suit you.").also { stage = END_DIALOGUE } + } + } + } + + 45 -> npcl(FacialExpression.NEUTRAL, "Well, come back and see me when you do.").also { stage = END_DIALOGUE } + // The below text was too long when using npcl's automatic line splitting. + 50 -> sendNormalDialogue( + NPC(NPCs.CAPN_IZZY_NO_BEARD_437), + FacialExpression.NEUTRAL, + "Aye, to be sure! The Skillcape of Agility be the symbol", + "of the master of dexterity! One who wears it can climb", + "like a cat, run like the wind and jump like...err, well", + "jump like a jumping thing!" + ).also { stage++ } + 51 -> npcl(FacialExpression.NEUTRAL, "Now, be there anything else ye'd like to know?").also { stage = 5 } + } + + 1 -> when (stage) { + 0 -> npcl(NPCs.PARROT_4535, FacialExpression.ANGRY, "Clap him in irons!").also { stage++ } + 1 -> npcl(NPCs.CAPN_IZZY_NO_BEARD_437, FacialExpression.NEUTRAL, "Ahoy there! Pay up first!").also { stage = END_DIALOGUE } + } + + 2 -> { + if (!getAttribute(player!!, "capn_izzy", false)) { + if (inInventory(player!!, Items.COINS_995, 200) && removeItem(player!!, Item(Items.COINS_995, 200))) { + sendItemDialogue(player!!, Items.COINS_6964, "You give Cap'n Izzy the 200 coin entrance fee.").also { stage = END_DIALOGUE } + sendMessage(player!!, "You give Cap'n Izzy the 200 coin entrance fee.") + setAttribute(player!!, "/save:capn_izzy", true) + } else { + sendMessage(player!!, "You don't have the 200 coin entrance fee.") + } + } else { + npcl(NPCs.CAPN_IZZY_NO_BEARD_437, FacialExpression.NEUTRAL, "Avast there, ye've already paid!").also { stage = END_DIALOGUE } + } + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/karamja/brimhaven/dialogue/DavonDialogue.java b/Server/src/main/content/region/karamja/brimhaven/dialogue/DavonDialogue.java index 65599fbee..e202dd5e9 100644 --- a/Server/src/main/content/region/karamja/brimhaven/dialogue/DavonDialogue.java +++ b/Server/src/main/content/region/karamja/brimhaven/dialogue/DavonDialogue.java @@ -39,7 +39,7 @@ public final class DavonDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Pssst! Come here if you want to do some amulet", "trading."); + interpreter.sendDialogues(npc, FacialExpression.SUSPICIOUS, "Pssst! Come here if you want to do some amulet", "trading."); stage = 0; return true; } @@ -54,15 +54,15 @@ public final class DavonDialogue extends DialoguePlugin { case 1: switch (buttonId) { case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "What are you selling?"); + interpreter.sendDialogues(player, FacialExpression.HAPPY, "What are you selling?"); stage = 10; break; case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "What do you mean pssst?"); + interpreter.sendDialogues(player, FacialExpression.SUSPICIOUS, "What do you mean pssst?"); stage = 20; break; case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Why don'y you ever restock some types of amulets?"); + interpreter.sendDialogues(player, FacialExpression.HALF_ASKING, "Why don't you ever restock some types of amulets?"); stage = 30; break; } @@ -72,7 +72,7 @@ public final class DavonDialogue extends DialoguePlugin { npc.openShop(player); break; case 20: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Errr, I was...I was clearing my throat."); + interpreter.sendDialogues(npc, FacialExpression.SUSPICIOUS, "Errr, I was...I was clearing my throat."); stage = 21; break; case 21: 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/dialogue/PirateJackieDialogue.java b/Server/src/main/content/region/karamja/brimhaven/dialogue/PirateJackieDialogue.java deleted file mode 100644 index 96aa03693..000000000 --- a/Server/src/main/content/region/karamja/brimhaven/dialogue/PirateJackieDialogue.java +++ /dev/null @@ -1,217 +0,0 @@ -package content.region.karamja.brimhaven.dialogue; - -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.game.node.entity.player.link.diary.AchievementDiary; -import core.game.node.entity.player.link.diary.DiaryType; -import core.plugin.Initializable; - -/** - * Represents the dialogue plugin used for pirate jackie. - * @author Vexia - */ -@Initializable -public final class PirateJackieDialogue extends DialoguePlugin { - - /** - * Represents the interface component. - */ - private static final Component COMPONENT = new Component(6); - - /** - * Constructs a new {@code PirateJackieDialogue} {@code Object}. - */ - public PirateJackieDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code PirateJackieDialogue} {@code Object}. - * @param player the player. - */ - public PirateJackieDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new PirateJackieDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Ahoy there!"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Ahoy!"); - stage = 1; - break; - case 1: - interpreter.sendOptions("Select an Option", "What is this place?", "What do you do?", "I'd like to trade in my tickets, please.", "I have a question about my Achievement Diary.", "See you later."); - stage = 2; - break; - case 2: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "What is this place?"); - stage = 10; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "What do you do?"); - stage = 20; - break; - case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I'd like to trade in my tickets, please."); - stage = 30; - break; - case 4: - player("I have a question about my Achievement Diary."); - stage = 41; - break; - case 5: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "See you later."); - stage = 50; - break; - } - break; - case 10: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Welcome to the Brimhaven Agility Arena!"); - stage = 11; - break; - case 11: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "If ye want to know more talk to Cap'n Izzy, he found", "it!"); - stage = 12; - break; - case 12: - end(); - break; - case 20: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "I be the Jack o' tickets. I exchange the tickets ye", "collect in the Agility Arena for more stuff. Ye can", "obtain more agility experience or some items ye won't", "find anywhere else!"); - stage = 21; - break; - case 21: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Sounds good!"); - stage = 22; - break; - case 22: - end(); - break; - case 30: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Aye, ye be on the right track."); - stage = 31; - break; - case 31: - end(); - player.getInterfaceManager().open(COMPONENT); - break; - case 41: - if (AchievementDiary.canClaimLevelRewards(player, DiaryType.KARAMJA, 0)) { - player("I've done all the easy tasks in my Karamja Achievement", "Diary."); - stage = 440; - break; - } - if (AchievementDiary.canReplaceReward(player, DiaryType.KARAMJA, 0)) { - player("I've seemed to have lost my gloves.."); - stage = 450; - break; - } - options("What is the Achievement Diary?", "What are the rewards?", "How do I claim the rewards?", "See you later."); - stage++; - break; - case 42: - switch (buttonId) { - case 1: - player("What is the Achievement Diary?"); - stage = 410; - break; - case 2: - player("What are the rewards?"); - stage = 420; - break; - case 3: - player("How do I claim the rewards?"); - stage = 430; - break; - case 4: - end(); - break; - } - break; - case 440: - npc("Arr, ye have that, I see yer list. I s'pose ye'll be wanting", "yer reward then!"); - stage++; - break; - case 441: - player("Yes please."); - stage++; - break; - case 442: - AchievementDiary.flagRewarded(player, DiaryType.KARAMJA, 0); - npc("These 'ere Karamja gloves be a symbol of yer explorin'", "on the island. All the merchants will recognise 'em when", "yer wear 'em and mabe give ye a little discount. I'll", "ave a word with some of the seafarin' folk who sail to"); - stage++; - break; - case 443: - npc("Port Sarim and Ardougne, so they'll take ye on board", "half price if year wearin' them. Arrr, take this lamp I", "found washed ashore too."); - stage++; - break; - case 444: - player("Wow, thanks!"); - stage = 41; - break; - case 450: - AchievementDiary.grantReplacement(player, DiaryType.KARAMJA, 0); - npc("Arr matey, have another pair. Ye better be more", "careful this time."); - stage = 41; - break; - case 410: - npc("It's a diary that helps you keep track of particular", "achievements. Here on Karamja it can help you", "discover some quite useful things. Eventually, with", "enough exploration, the people of Karamja will reward"); - stage++; - break; - case 411: - npc("you."); - stage++; - break; - case 412: - npc("You can see what tasks you have listed by clicking on", "the green button in the Quest List."); - stage = 41; - break; - case 420: - npc("Well, there's three different pairs of Karamja gloves,", "which match up with the three levels of difficulty. Each", "has the same rewards as the previous level, and an", "additional one too... but I won't spoil your surprise."); - stage++; - break; - case 421: - npc("Rest assured, the people of Karamja are happy to see", "you visiting the island."); - stage = 41; - break; - case 430: - npc("Just complete the tasks so they're all ticked off, then", "you can claim yer reward. Most of them are", "straightforward; you might find some require quests to", "be started, if not finished."); - stage++; - break; - case 431: - npc("To claim the different Karamja gloves, speak to Kaleb", "Paramaya in Shilo Village, one of the jungle foresters", "near the Kharazi Jungle, or me."); - stage = 41; - break; - case 50: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 1055 }; - } -} diff --git a/Server/src/main/content/region/karamja/brimhaven/dialogue/PirateJackieDialogue.kt b/Server/src/main/content/region/karamja/brimhaven/dialogue/PirateJackieDialogue.kt new file mode 100644 index 000000000..e9ecfd200 --- /dev/null +++ b/Server/src/main/content/region/karamja/brimhaven/dialogue/PirateJackieDialogue.kt @@ -0,0 +1,120 @@ +package content.region.karamja.brimhaven.dialogue + +import core.api.openInterface +import core.game.dialogue.DialogueFile +import core.game.dialogue.FacialExpression +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.tools.END_DIALOGUE +import org.rs09.consts.Components +import org.rs09.consts.NPCs + +class PirateJackieDialogue : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + 0 -> playerl(FacialExpression.NEUTRAL, "Ahoy there!").also { stage++ } + 1 -> npcl(FacialExpression.NEUTRAL, "Ahoy!").also { stage++ } + 2 -> options( + "What is this place?", + "What do you do?", + "I'd like to trade in my tickets, please.", + "I have a question about my Achievement Diary.", + "See you later." + ).also { stage++ } + + 3 -> when (buttonID) { + 1 -> playerl(FacialExpression.NEUTRAL, "What is this place?").also { stage = 10 } + 2 -> playerl(FacialExpression.NEUTRAL, "What do you do?").also { stage = 20 } + 3 -> playerl(FacialExpression.NEUTRAL, "I'd like to trade in my tickets, please.").also { stage = 30 } + 4 -> playerl(FacialExpression.NEUTRAL, "I have a question about my Achievement Diary.").also { + stage = 40 + } + + 5 -> playerl(FacialExpression.NEUTRAL, "See you later.").also { stage = END_DIALOGUE } + } + + 10 -> npcl(FacialExpression.NEUTRAL, "Welcome to the Brimhaven Agility Arena!").also { stage++ } + 11 -> npcl( + FacialExpression.NEUTRAL, + "If ye want to know more talk to Cap'n Izzy, he found it!" + ).also { stage = END_DIALOGUE } + + 20 -> npcl( + FacialExpression.NEUTRAL, + "I be the Jack o' tickets. I exchange the tickets ye collect in the Agility Arena for " + + "more stuff. Ye can obtain more agility experience or some items ye won't find anywhere else!" + ).also { stage++ } + + 21 -> playerl(FacialExpression.NEUTRAL, "Sounds good!").also { stage = END_DIALOGUE } + 30 -> { + npcl(FacialExpression.NEUTRAL, "Aye, ye be on the right track.").also { stage = END_DIALOGUE } + end() + openInterface(player!!, Components.AGILITYARENA_TRADE_6) + } + + 40 -> when { + AchievementDiary.canClaimLevelRewards(player!!, DiaryType.KARAMJA, 0) -> + playerl(FacialExpression.NEUTRAL, "I've done all the easy tasks in my Karamja Achievement Diary.").also { stage = 410 } + + AchievementDiary.canReplaceReward(player, DiaryType.KARAMJA, 0) -> + playerl(FacialExpression.NEUTRAL, "I've seemed to have lost my gloves..").also { stage = 420 } + + else -> options( + "What is the Achievement Diary?", + "What are the rewards?", + "How do I claim the rewards?", + "See you later." + ).also { stage++ } + } + + 41 -> when (buttonID) { + 1 -> playerl(FacialExpression.NEUTRAL, "What is the Achievement Diary?").also { stage = 430 } + 2 -> playerl(FacialExpression.NEUTRAL, "What are the rewards?").also { stage = 440 } + 3 -> playerl(FacialExpression.NEUTRAL, "How do I claim the rewards?").also { stage = 450 } + 4 -> playerl(FacialExpression.NEUTRAL, "See you later.").also { stage = END_DIALOGUE } + } + + 410 -> npcl(FacialExpression.NEUTRAL, "Arr, ye have that, I see yer list. I s'pose ye'll be wanting yer reward then!").also { stage++ } + + 411 -> playerl(FacialExpression.NEUTRAL, "Yes please.").also { stage++ } + 412 -> { + AchievementDiary.flagRewarded(player, DiaryType.KARAMJA, 0) + npcl(FacialExpression.NEUTRAL, + "These 'ere Karamja gloves be a symbol of yer explorin' on the island. All the merchants will recognise" + + " 'em when yer wear 'em and mabe give ye a little discount. I'll ave a word with some of the seafarin' folk who ").also { stage++ } + } + + 413 -> npcl(FacialExpression.NEUTRAL, "sail to Port Sarim and Ardougne, so they'll take ye on board half price if year wearin' them. Arrr, take this" + + " lamp I found washed ashore too.").also { stage++ } + + 414 -> playerl(FacialExpression.NEUTRAL, "Wow, thanks!").also { stage = 40 } + 420 -> { + AchievementDiary.grantReplacement(player, DiaryType.KARAMJA, 0) + npcl(FacialExpression.NEUTRAL, "Arr matey, have another pair. Ye better be more careful this time.").also { stage = 40 } + } + + 430 -> npcl( + FacialExpression.NEUTRAL, + "It's a diary that helps you keep track of particular achievements. Here on Karamja it can help " + + "you discover some quite useful things. Eventually, with enough exploration, the people of Karamja will reward you.").also { stage++ } + + 431 -> npcl(FacialExpression.NEUTRAL, "You can see what tasks you have listed by clicking on the green button in the Quest List.").also { stage = 40 } + // The below text was too long when using npcl's automatic line splitting. + 440 -> sendNormalDialogue( + NPC(NPCs.PIRATE_JACKIE_THE_FRUIT_1055), + FacialExpression.NEUTRAL, + "Well, there's three different pairs of Karamja gloves,", + "which match up with the three levels of difficulty. Each", + "has the same rewards as the previous level, and an", + "additional one too... but I won't spoil your surprise." + ).also { stage++ } + + 441 -> npcl(FacialExpression.NEUTRAL, + "Rest assured, the people of Karamja are happy to see you visiting the island.").also { stage = 40 } + + 450 -> npcl(FacialExpression.NEUTRAL, "To claim the different Karamja gloves, speak to Kaleb Paramaya in Shilo Village, one of the jungle " + + "foresters near the Kharazi Jungle, or me.").also { stage = 40 } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/karamja/brimhaven/dialogue/SanibochDialogue.java b/Server/src/main/content/region/karamja/brimhaven/dialogue/SanibochDialogue.java index 141d3d467..0ed5006ad 100644 --- a/Server/src/main/content/region/karamja/brimhaven/dialogue/SanibochDialogue.java +++ b/Server/src/main/content/region/karamja/brimhaven/dialogue/SanibochDialogue.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 org.rs09.consts.Items; /** * Handles the SanibochDialogue dialogue. @@ -129,7 +130,7 @@ public class SanibochDialogue extends DialoguePlugin { if (player.getInventory().remove(item)) { player.getPacketDispatch().sendMessage("You pay Saniboch 875 coins."); player.setAttribute("saniboch:paid", true); - interpreter.sendItemMessage(item, "You give Saniboch 875 coins."); + interpreter.sendItemMessage(Items.COINS_6964, "You give Saniboch 875 coins."); stage = 33; break; } diff --git a/Server/src/main/content/region/karamja/brimhaven/handlers/BrimhavenDungeonListeners.kt b/Server/src/main/content/region/karamja/brimhaven/handlers/BrimhavenDungeonListeners.kt index ff2067d84..2e39db2ad 100644 --- a/Server/src/main/content/region/karamja/brimhaven/handlers/BrimhavenDungeonListeners.kt +++ b/Server/src/main/content/region/karamja/brimhaven/handlers/BrimhavenDungeonListeners.kt @@ -1,16 +1,22 @@ package content.region.karamja.brimhaven.handlers -import core.game.node.entity.skill.Skills import content.global.skill.agility.AgilityHandler +import core.api.* +import core.game.dialogue.FacialExpression +import core.game.global.action.ClimbActionHandler +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +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 org.rs09.consts.NPCs -import core.game.interaction.InteractionListener -import core.game.interaction.IntType +import org.rs09.consts.Scenery class BrimhavenDungeonListeners : InteractionListener { - val EXIT = 5084 + val ENTRANCE = Scenery.DUNGEON_ENTRANCE_5083 + val EXIT = Scenery.EXIT_5084 val SANIBOCH = NPCs.SANIBOCH_1595 val VINES = intArrayOf(5103,5104,5105,5106,5107) val STEPPING_STONES = intArrayOf(5110,5111) @@ -18,6 +24,16 @@ class BrimhavenDungeonListeners : InteractionListener { val LOGS = intArrayOf(5088,5090) override fun defineListeners() { + on(ENTRANCE, IntType.SCENERY, "enter"){ player, node -> + if (getAttribute(player, "saniboch:paid", false) || player.achievementDiaryManager.getDiary(DiaryType.KARAMJA).isComplete()) { + ClimbActionHandler.climb(player, ClimbActionHandler.CLIMB_UP, location(2713, 9564, 0)) + removeAttribute(player, "saniboch:paid") + } else { + sendNPCDialogue(player, SANIBOCH, "You can't go in there without paying!", FacialExpression.NEUTRAL) + } + return@on true + } + on(EXIT, IntType.SCENERY, "leave"){ player, _ -> player.properties.teleportLocation = Location.create(2745, 3152, 0) return@on true diff --git a/Server/src/main/content/region/karamja/brimhaven/handlers/BrimhavenListeners.kt b/Server/src/main/content/region/karamja/brimhaven/handlers/BrimhavenListeners.kt new file mode 100644 index 000000000..e2b43bc28 --- /dev/null +++ b/Server/src/main/content/region/karamja/brimhaven/handlers/BrimhavenListeners.kt @@ -0,0 +1,106 @@ +package content.region.karamja.brimhaven.handlers + +import content.region.karamja.brimhaven.dialogue.CapnIzzyDialogue +import content.region.karamja.brimhaven.dialogue.PirateJackieDialogue +import core.api.* +import core.game.dialogue.FacialExpression +import core.game.global.action.ClimbActionHandler +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.world.repository.Repository +import org.rs09.consts.Components +import org.rs09.consts.NPCs +import org.rs09.consts.Scenery + +class BrimhavenListeners : InteractionListener { + companion object { + /** + * Represents the ladder that exits the Agility Arena. + */ + private const val AGILITY_ARENA_EXIT_LADDER = Scenery.LADDER_3618 + + /** + * Represents the ladder that enters the Agility Arena. + */ + private const val AGILITY_ARENA_ENTRANCE_LADDER = Scenery.LADDER_3617 + + /** + * Represents the area inside Brimhaven Agility Arena. + */ + private val AGILITY_ARENA = location(2805, 9589, 3) + + /** + * Represents the area within the hut outside the Brimhaven Agility Arena. + */ + private val AGILITY_ARENA_HUT = location(2809, 3193, 0) + + /** + * Represents the agility ticket exchange interface/component. + */ + private const val TICKET_EXCHANGE_IFACE = Components.AGILITYARENA_TRADE_6 + + /** + * Represents the back door of the Shrimp and Parrot restaurant used in the Heroes' Quest. + */ + private const val RESTAURANT_REAR_DOOR = Scenery.DOOR_1591 + + /** + * Represents Lubufu's karambwan fishing spot unlocked in Tai Bwo Wannai Trio. + */ + private const val KARAMBWAN_FISHING_SPOT = NPCs.FISHING_SPOT_1178 + } + + override fun defineListeners() { + on(AGILITY_ARENA_EXIT_LADDER, IntType.SCENERY, "climb-up") { player, _ -> + ClimbActionHandler.climb(player, ClimbActionHandler.CLIMB_UP, AGILITY_ARENA_HUT) + return@on true + } + + on(AGILITY_ARENA_ENTRANCE_LADDER, IntType.SCENERY, "climb-down") { player, _ -> + if (!getAttribute(player, "capn_izzy", false)) { + openDialogue(player, CapnIzzyDialogue(1)) + return@on true + } else { + ClimbActionHandler.climb(player, ClimbActionHandler.CLIMB_DOWN, AGILITY_ARENA) + removeAttribute(player, "capn_izzy") + } + return@on true + } + + on(NPCs.CAPN_IZZY_NO_BEARD_437, IntType.NPC, "talk-to") { player, node -> + openDialogue(player, CapnIzzyDialogue(0), node) + return@on true + } + + on(NPCs.CAPN_IZZY_NO_BEARD_437, IntType.NPC, "pay") { player, node -> + openDialogue(player, CapnIzzyDialogue(2), node) + return@on true + } + + on(NPCs.PIRATE_JACKIE_THE_FRUIT_1055, IntType.NPC, "talk-to") { player, node -> + openDialogue(player, PirateJackieDialogue(), node) + return@on true + } + + on(NPCs.PIRATE_JACKIE_THE_FRUIT_1055, IntType.NPC, "trade") { player, _ -> + openInterface(player, TICKET_EXCHANGE_IFACE) + return@on true + } + + on(RESTAURANT_REAR_DOOR, IntType.SCENERY, "open") { player, _ -> + sendMessage(player, "You try and open the door...") + sendMessage(player, "The door is locked tight, I can't open it.") + return@on true + } + + on(KARAMBWAN_FISHING_SPOT, IntType.NPC, "fish") { player, _ -> + sendNPCDialogue( + player, + NPCs.LUBUFU_1171, + "Keep off my fishing spot, whippersnapper!", + FacialExpression.FURIOUS + ) + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/karamja/dialogue/CaptainShanksDialogue.java b/Server/src/main/content/region/karamja/dialogue/CaptainShanksDialogue.java index 3bcabdfc7..ddf3f18e5 100644 --- a/Server/src/main/content/region/karamja/dialogue/CaptainShanksDialogue.java +++ b/Server/src/main/content/region/karamja/dialogue/CaptainShanksDialogue.java @@ -21,7 +21,7 @@ public final class CaptainShanksDialogue extends DialoguePlugin { private static final Item TICKET = new Item(Items.SHIP_TICKET_621); /** - * Constructs a new {@code CaptainBarnabyDialogue} {@code Object}. + * Constructs a new {@code CaptainShanksDialogue} {@code Object}. */ public CaptainShanksDialogue() { /** @@ -30,7 +30,7 @@ public final class CaptainShanksDialogue extends DialoguePlugin { } /** - * Constructs a new {@code CaptainBarnabyDialogue} {@code Object}. + * Constructs a new {@code CaptainShanksDialogue} {@code Object}. * @param player the player. */ public CaptainShanksDialogue(Player player) { diff --git a/Server/src/main/content/region/karamja/dialogue/CustomsOfficerDialogue.java b/Server/src/main/content/region/karamja/dialogue/CustomsOfficerDialogue.java index fbb999f17..b32c1bfa0 100644 --- a/Server/src/main/content/region/karamja/dialogue/CustomsOfficerDialogue.java +++ b/Server/src/main/content/region/karamja/dialogue/CustomsOfficerDialogue.java @@ -9,6 +9,9 @@ import core.game.node.item.Item; 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. * @author Vexia @@ -52,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; @@ -184,6 +187,11 @@ public final class CustomsOfficerDialogue extends DialoguePlugin { } player.getPacketDispatch().sendMessage("You pay " + getPrice() + " coins and board the ship."); ship.sail(player); + if(ship == Ships.BRIMHAVEN_TO_ARDOUGNE) { + playJingle(player, 171); + } else { + playJingle(player, 172); + } break; case 220: end(); diff --git a/Server/src/main/content/region/karamja/dialogue/KaramajaManDialogue.java b/Server/src/main/content/region/karamja/dialogue/KaramajaManDialogue.java deleted file mode 100644 index ffe40ec31..000000000 --- a/Server/src/main/content/region/karamja/dialogue/KaramajaManDialogue.java +++ /dev/null @@ -1,69 +0,0 @@ -package content.region.karamja.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 karamja man dialogue plugin. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class KaramajaManDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code KaramajaManDialogue} {@code Object}. - */ - public KaramajaManDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code KaramajaManDialogue} {@code Object}. - * @param player the player. - */ - public KaramajaManDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new KaramajaManDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Hello, how's it going?"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Not too bad, but I'm a little worried about the increase", "of goblins these days."); - stage = 1; - break; - case 1: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Don't worry, I'll kill them."); - stage = 2; - break; - case 2: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 3915 }; - } -} diff --git a/Server/src/main/content/region/karamja/dialogue/KaramjaManDialogue.kt b/Server/src/main/content/region/karamja/dialogue/KaramjaManDialogue.kt new file mode 100644 index 000000000..80d89ada1 --- /dev/null +++ b/Server/src/main/content/region/karamja/dialogue/KaramjaManDialogue.kt @@ -0,0 +1,46 @@ +package content.region.karamja.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 core.tools.START_DIALOGUE +import org.rs09.consts.NPCs + +@Initializable +class KaramjaManDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun newInstance(player: Player?): DialoguePlugin { + return KaramjaManDialogue(player) + } + + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + playerl( + FacialExpression.FRIENDLY, "Hello, how's it going?" + ).also { stage = START_DIALOGUE } + return true + } + + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage) { + 0 -> npcl( + FacialExpression.HALF_GUILTY, + "Not too bad, but I'm a little worried about the increase of goblins these days." + ).also { stage++ } + + 1 -> playerl( + FacialExpression.HAPPY, "Don't worry, I'll kill them." + ).also { stage = END_DIALOGUE } + } + return true + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.MAN_3915) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/karamja/dialogue/LuthasDialogue.java b/Server/src/main/content/region/karamja/dialogue/LuthasDialogue.java index eb002ac8f..8692e3a63 100644 --- a/Server/src/main/content/region/karamja/dialogue/LuthasDialogue.java +++ b/Server/src/main/content/region/karamja/dialogue/LuthasDialogue.java @@ -50,15 +50,15 @@ public final class LuthasDialogue extends DialoguePlugin { if (player.getSavedData().getGlobalData().isLuthasTask()) { final int current = player.getSavedData().getGlobalData().getKaramjaBananas(); if (current >= 10) { - interpreter.sendDialogues(player, null, "I've filled a crate with bananas."); + interpreter.sendDialogues(player, FacialExpression.FRIENDLY, "I've filled a crate with bananas."); stage = 905; return true; } - interpreter.sendDialogues(npc, null, "Have you completed your task yet?"); + interpreter.sendDialogues(npc, FacialExpression.HALF_ASKING, "Have you completed your task yet?"); stage = 900; return true; } - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Hello I'm Luthas, I run the banana plantation here."); + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Hello I'm Luthas, I run the banana plantation here."); stage = 0; return true; } @@ -73,25 +73,25 @@ public final class LuthasDialogue extends DialoguePlugin { case 1: switch (buttonId) { case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Could you offer me employment on your plantation?"); + interpreter.sendDialogues(player, FacialExpression.ASKING, "Could you offer me employment on your plantation?"); stage = 10; break; case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "That customs officer is annoying isn't she?"); + interpreter.sendDialogues(player, FacialExpression.SUSPICIOUS, "That customs officer is annoying isn't she?"); stage = 20; break; } break; case 10: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Yes, I can sort something out. There's a crate ready to", "be loaded onto the ship."); + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Yes, I can sort something out. There's a crate ready to", "be loaded onto the ship."); stage = 11; break; case 11: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "You wouldn't believe the demand for bananas from", "Wydin's shop over in Port Sarim. I think this is the", "third crate I've shipped him this month.."); + interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "You wouldn't believe the demand for bananas from", "Wydin's shop over in Port Sarim. I think this is the", "third crate I've shipped him this month.."); stage = 12; break; case 12: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "If you could go fill it up with bananas, I'll pay you 30", "gold."); + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "If you could go fill it up with bananas, I'll pay you 30", "gold."); stage = 13; player.getSavedData().getGlobalData().setLuthasTask(true); break; @@ -99,27 +99,39 @@ public final class LuthasDialogue extends DialoguePlugin { end(); break; case 20: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "I don't know about that."); + interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "Well I know her pretty well. She doesn't cause me any", "trouble any more"); stage = 21; break; case 21: + interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "She doesn't even search my export crates anymore.", "She knows they only contain bananas."); + stage = 22; + break; + case 22: + interpreter.sendDialogues(player, FacialExpression.SUSPICIOUS, "Really? How interesting. Whereabouts do you send", "those to?"); + stage = 23; + break; + case 23: + interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "There is a little shop over in Port Sarim that buys", "them up by the crate. I believe it is run by a man", "called Wydin."); + stage = 24; + break; + case 24: end(); break; case 900: int amt = player.getSavedData().getGlobalData().getKaramjaBananas(); if (amt < 30) { - interpreter.sendDialogues(player, null, "No, the crate isn't full yet."); + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No, the crate isn't full yet."); stage = 901; } else { } break; case 901: - interpreter.sendDialogues(npc, null, "Well come back when it is."); + interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "Well come back when it is."); stage = 21; break; case 905: - interpreter.sendDialogues(npc, null, "Well done, here's your payment."); + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Well done, here's your payment."); stage = 906; break; case 906: diff --git a/Server/src/main/content/region/karamja/dialogue/MonkeyDialogue.java b/Server/src/main/content/region/karamja/dialogue/MonkeyDialogue.java deleted file mode 100644 index a2680da4d..000000000 --- a/Server/src/main/content/region/karamja/dialogue/MonkeyDialogue.java +++ /dev/null @@ -1,65 +0,0 @@ -package content.region.karamja.dialogue; - -import core.game.dialogue.DialoguePlugin; -import core.game.dialogue.FacialExpression; -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * The dialogue for the desert monkey. - * @author 'Vexia - */ -@Initializable -public final class MonkeyDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code MonkeyDialogue} {@code Object}. - */ - public MonkeyDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code MonkeyDialogue} {@code Object}. - * @param player the player. - */ - public MonkeyDialogue(final Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new MonkeyDialogue(player); - } - - @Override - public boolean open(Object... args) { - player("Hey little man, how's it goin'?"); - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - npc("Ukkuk oook! Eeek aka, ahh aka gonk."); - stage++; - break; - case 1: - player(FacialExpression.HALF_GUILTY,"Yeah."); - stage = 5; - break; - case 5: - end(); - break; - } - return true; - } - @Override - public int[] getIds() { - return new int[] { 2301 }; - } - -} \ No newline at end of file diff --git a/Server/src/main/content/region/karamja/dialogue/MonkeyDialogue.kt b/Server/src/main/content/region/karamja/dialogue/MonkeyDialogue.kt new file mode 100644 index 000000000..8e911788d --- /dev/null +++ b/Server/src/main/content/region/karamja/dialogue/MonkeyDialogue.kt @@ -0,0 +1,41 @@ +package content.region.karamja.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 core.tools.START_DIALOGUE +import org.rs09.consts.NPCs + +@Initializable +class MonkeyDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun newInstance(player: Player?): DialoguePlugin { + return MonkeyDialogue(player) + } + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + playerl( + FacialExpression.ASKING, "Hey little man, how's it goin'?" + ).also { stage = START_DIALOGUE } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage) { + 0 -> npc("Ukkuk oook! Eeek aka, ahh aka gonk.").also { stage++ } + + 1 -> playerl( + FacialExpression.HALF_GUILTY, "Yeah." + ).also { stage = END_DIALOGUE } + } + return true + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.MONKEY_2301) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/karamja/dialogue/MusaPointShopDialogue.kt b/Server/src/main/content/region/karamja/dialogue/MusaPointShopDialogue.kt new file mode 100644 index 000000000..d84d4bc96 --- /dev/null +++ b/Server/src/main/content/region/karamja/dialogue/MusaPointShopDialogue.kt @@ -0,0 +1,49 @@ +package content.region.karamja.dialogue + +import core.api.openNpcShop +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 + +@Initializable +class MusaPointShopDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun newInstance(player: Player?): DialoguePlugin { + return MusaPointShopDialogue(player) + } + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + npcl( + FacialExpression.FRIENDLY, "Can I help you at all?" + ).also { stage = START_DIALOGUE } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage) { + 0 -> options("Yes please. What are you selling?", "No thanks.").also { stage++ } + + 1 -> when (buttonId) { + 1 -> { + openNpcShop(player, NPCs.SHOPKEEPER_532) + end() + } + + 2 -> playerl( + FacialExpression.FRIENDLY, "No thanks." + ).also { stage = END_DIALOGUE } + } + } + return true + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.SHOPKEEPER_532, NPCs.SHOP_ASSISTANT_533) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/karamja/dialogue/ZamboDialogue.java b/Server/src/main/content/region/karamja/dialogue/ZamboDialogue.java deleted file mode 100644 index 14031211a..000000000 --- a/Server/src/main/content/region/karamja/dialogue/ZamboDialogue.java +++ /dev/null @@ -1,77 +0,0 @@ -package content.region.karamja.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 zambo. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class ZamboDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code ZamboDialogue} {@code Object}. - */ - public ZamboDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code ZamboDialogue} {@code Object}. - * @param player the player. - */ - public ZamboDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new ZamboDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Hey, are you wanting to try some of my fine wines", "and spririts? All brewed locally on Karamja island."); - 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"); - stage = 1; - break; - case 1: - switch (buttonId) { - case 1: - end(); - npc.openShop(player); - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No, thank you."); - stage = 20; - break; - } - break; - case 20: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 568 }; - } -} diff --git a/Server/src/main/content/region/karamja/dialogue/ZamboDialogue.kt b/Server/src/main/content/region/karamja/dialogue/ZamboDialogue.kt new file mode 100644 index 000000000..23323d0c0 --- /dev/null +++ b/Server/src/main/content/region/karamja/dialogue/ZamboDialogue.kt @@ -0,0 +1,54 @@ +package content.region.karamja.dialogue + +import core.api.openNpcShop +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 + +@Initializable +class ZamboDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun newInstance(player: Player?): DialoguePlugin { + return ZamboDialogue(player) + } + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + npcl( + FacialExpression.ASKING, + " Hey, are you wanting to try some of my fine wines and spirits? All brewed locally on Karamja." + ).also { stage = START_DIALOGUE } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage) { + 0 -> options("Yes, please.", "No, thank you.").also { stage++ } + + 1 -> when (buttonId) { + 1 -> playerl( + FacialExpression.FRIENDLY, "Yes, please." + ).also { stage++ } + + 2 -> playerl( + FacialExpression.FRIENDLY, "No, thank you." + ).also { stage = END_DIALOGUE } + } + + 2 -> { + openNpcShop(player, NPCs.ZAMBO_568) + end() + } + } + return true + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.ZAMBO_568) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/karamja/diary/KaramjaAchievementDiary.kt b/Server/src/main/content/region/karamja/diary/KaramjaAchievementDiary.kt index eb7ed8589..cda31648c 100644 --- a/Server/src/main/content/region/karamja/diary/KaramjaAchievementDiary.kt +++ b/Server/src/main/content/region/karamja/diary/KaramjaAchievementDiary.kt @@ -16,6 +16,7 @@ class KaramjaAchievementDiary : DiaryEventHookBase(DiaryType.KARAMJA) { companion object { private const val ATTRIBUTE_SEAWEED_PICKED = "diary:karamja:seaweed-picked" private const val ATTRIBUTE_PALM_LEAF_PICKED = "diary:karamja:palm-leaf-picked" + private const val ATTRIBUTE_BANANA_PICKED = "diary:karamja:banana-picked" private val MAIN_ISLAND_AREA = ZoneBorders(2749, 2886, 2972, 3132) private val BANANA_FISHING_SPOT_AREA = ZoneBorders(2923, 3173, 2928, 3182) @@ -62,8 +63,9 @@ class KaramjaAchievementDiary : DiaryEventHookBase(DiaryType.KARAMJA) { const val TRAP_HORNED_GRAAHK = 13 const val BRIMHAVEN_DUNGEON_CHOP_VINES = 14 const val BRIMHAVEN_DUNGEON_CROSS_LAVA_STEPPING_STONES = 15 - const val CHARTER_SHIP_EAST = 16 - const val MINE_RED_TOPAZ = 17 + const val BRIMHAVEN_DUNGEON_CLIMB_STAIRS = 16 + const val CHARTER_SHIP_EAST = 17 + const val MINE_RED_TOPAZ = 18 } object HardTasks { @@ -82,6 +84,14 @@ class KaramjaAchievementDiary : DiaryEventHookBase(DiaryType.KARAMJA) { override fun onResourceProduced(player: Player, event: ResourceProducedEvent) { when (player.viewport.region.id) { + 10802 -> if (event.itemId == Items.GOLD_ORE_444) { + finishTask( + player, + DiaryLevel.EASY, + EasyTasks.BRIMHAVEN_MINE_GOLD + ) + } + 11310, 11410 -> if (event.itemId == Items.UNCUT_RED_TOPAZ_1629) { finishTask( player, @@ -90,11 +100,13 @@ class KaramjaAchievementDiary : DiaryEventHookBase(DiaryType.KARAMJA) { ) } - 10802 -> if (event.itemId == Items.GOLD_ORE_444) { - finishTask( + 11569 -> if (event.itemId == Items.BANANA_1963) { + progressIncrementalTask( player, DiaryLevel.EASY, - EasyTasks.BRIMHAVEN_MINE_GOLD + EasyTasks.PICK_5_BANANAS, + ATTRIBUTE_BANANA_PICKED, + 5 ) } } 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/JungleBushHandler.kt b/Server/src/main/content/region/karamja/handlers/JungleBushHandler.kt index f5da3dc61..8e4462110 100644 --- a/Server/src/main/content/region/karamja/handlers/JungleBushHandler.kt +++ b/Server/src/main/content/region/karamja/handlers/JungleBushHandler.kt @@ -1,5 +1,7 @@ package content.region.karamja.handlers +import core.api.inEquipmentOrInventory +import core.api.inInventory import core.game.node.scenery.Scenery import core.game.node.scenery.SceneryBuilder import core.game.node.entity.player.Player @@ -47,11 +49,11 @@ class JungleBushHandler : InteractionListener { } - fun checkRequirement(player: Player): Boolean{ + private fun checkRequirement(player: Player): Boolean{ val machete = Item(Items.MACHETE_975) val jade_machete = Item(Items.JADE_MACHETE_6315) val opal_machete = Item(Items.OPAL_MACHETE_6313) val red_topaz_machete = Item(Items.RED_TOPAZ_MACHETE_6317) - return player.inventory.containsItem(machete) || player.inventory.containsItem(jade_machete) || player.inventory.containsItem(opal_machete) || player.inventory.containsItem(red_topaz_machete) + return inEquipmentOrInventory(player, machete.id) || inEquipmentOrInventory(player, jade_machete.id) || inEquipmentOrInventory(player, opal_machete.id) || inEquipmentOrInventory(player, red_topaz_machete.id) } } diff --git a/Server/src/main/content/region/karamja/handlers/KaramajaOptionPlugin.java b/Server/src/main/content/region/karamja/handlers/KaramajaOptionPlugin.java deleted file mode 100644 index 3b45719af..000000000 --- a/Server/src/main/content/region/karamja/handlers/KaramajaOptionPlugin.java +++ /dev/null @@ -1,254 +0,0 @@ -package content.region.karamja.handlers; - -import core.cache.def.impl.NPCDefinition; -import core.cache.def.impl.SceneryDefinition; -import core.game.component.Component; -import core.plugin.Initializable; -import org.rs09.consts.Items; -import core.game.system.task.Pulse; -import core.game.world.GameWorld; -import core.game.dialogue.FacialExpression; -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.npc.NPC; -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.world.map.Location; -import core.game.world.repository.Repository; -import core.plugin.Plugin; - -/** - * Represents the option plugin used to handle karamaja options. - * - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class KaramajaOptionPlugin extends OptionHandler { - - /** - * Represents the pineapple item. - */ - private static final Item PINEAPPLE = new Item(2114); - - /** - * Represents the pine apple objects. - */ - private static final int[] PINEAPPLE_OBJECTS = new int[]{ 1408, 1409, 1410, 1411, 1412, 1413 }; - - /** - * Represents the musa point dungeon. - */ - private static final Location MUSA_POINT_DUNGEON = Location.create(2856, 9567, 0); - - /** - * Represents the volacno rim location. - */ - private static final Location VOLCANO_RIM = new Location(2856, 3167, 0); - - /** - * Represents the agility brimhaven location. - */ - private static final Location AGILITY_LOCATION = new Location(2805, 9589, 3); - - /** - * Represents the main base asgility location (ground floor) - */ - private static final Location AGILITY_MAIN = new Location(2809, 3193, 0); - - /** - * Represents the component. - */ - private static final Component TICKET_EXCHANGE = new Component(6); - - @Override - public Plugin newInstance(Object arg) throws Throwable { - SceneryDefinition.forId(2078).getHandlers().put("option:search", this);// random crate. - SceneryDefinition.forId(492).getHandlers().put("option:climb-down", this);// musa point rock - SceneryDefinition.forId(1764).getHandlers().put("option:climb", this);// musa dungeon rope - SceneryDefinition.forId(3617).getHandlers().put("option:climb-down", this);// agility ladder - SceneryDefinition.forId(3618).getHandlers().put("option:climb-up", this);// agility ladder - NPCDefinition.forId(437).getHandlers().put("option:pay", this);// capn izzy - NPCDefinition.forId(1055).getHandlers().put("option:trade", this);// capn izzy trader (tickets) - SceneryDefinition.forId(2626).getHandlers().put("option:open", this);// grubors locked door - SceneryDefinition.forId(2628).getHandlers().put("option:open", this);// the shrimp and parrot door (chef) - SceneryDefinition.forId(2627).getHandlers().put("option:open", this);// garv door - SceneryDefinition.forId(1591).getHandlers().put("option:open", this);// garv door - NPCDefinition.forId(1178).getHandlers().put("option:fish", this);// lubufu fishing spot - SceneryDefinition.forId(5083).getHandlers().put("option:enter", this);// sanibock dungeon entrance - SceneryDefinition.forId(2439).getHandlers().put("option:open", this); - for (int pineapple : PINEAPPLE_OBJECTS) { - SceneryDefinition.forId(pineapple).getHandlers().put("option:pick", this);// pineapple picking - } - SceneryDefinition.forId(2975).getHandlers().put("option:shake", this); // leafy palm tree - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - switch (option) { - case "pick":// pineapples. - if (player.getInventory().freeSlots() == 0) { - player.getPacketDispatch().sendMessage("Not enough inventory space."); - return true; - } - final Scenery pineApple = ((Scenery) node); - if (pineApple.getId() == 1413) { - player.getPacketDispatch().sendMessage("There are no pineapples left on this plant."); - return true; - } - boolean last = pineApple.getId() == 14012; - if (player.getInventory().add(PINEAPPLE)) { - SceneryBuilder.replace(pineApple, pineApple.transform(pineApple.getId() + 1), last ? 270 : 40); - player.getPacketDispatch().sendMessage("You pick a pineapple."); - } - break; - case "enter": - switch (((Scenery) node).getId()) { - case 5083: - if (player.getAttribute("saniboch:paid", false) || player.getAchievementDiaryManager().getDiary(DiaryType.KARAMJA).isComplete()) { - ClimbActionHandler.climb(player, ClimbActionHandler.CLIMB_UP, Location.create(2713, 9564, 0)); - player.removeAttribute("saniboch:paid"); - break; - } - player.getDialogueInterpreter().sendDialogues(1595, null, "You can't go in there without paying!"); - break; - } - break; - case "trade": - switch (((NPC) node).getId()) { - case 1055: - player.getInterfaceManager().open(TICKET_EXCHANGE); - break; - } - break; - case "fish": - player.getDialogueInterpreter().sendDialogues(1171, FacialExpression.FURIOUS, "Keep off my fishing spot, whippersnapper!"); - break; - case "pay": - if (!player.getAttribute("capn_izzy", false)) { - if (player.getInventory().contains(995, 200) && player.getInventory().remove(new Item(995, 200))) { - player.getDialogueInterpreter().sendItemMessage(new Item(995, 200), "You give Cap'n Izzy the 200 coin entrance fee."); - player.getPacketDispatch().sendMessage("You give Cap'n Izzy the 200 coin entrance fee."); - player.setAttribute("/save:capn_izzy", true); - return true; - } else { - player.getPacketDispatch().sendMessage("You don't have the 200 coin entrance fee."); - } - } else { - player.getDialogueInterpreter().sendDialogues(437, null, "Avast there, ye've already paid!"); - } - break; - case "pay-fare": - switch (((Scenery) node).getId()) { - case 2230: - player.getPacketDispatch().sendMessage("This cart appears to have been out of reward for some time."); - break; - } - break; - case "climb-up": - switch (((Scenery) node).getId()) { - case 3618: - player.getProperties().setTeleportLocation(AGILITY_MAIN); - break; - } - break; - case "climb-down": - switch (((Scenery) node).getId()) { - case 492: - player.getPacketDispatch().sendMessage("You climb down through the pot hole."); - player.getProperties().setTeleportLocation(MUSA_POINT_DUNGEON); - break; - case 3617: - if (!player.getAttribute("capn_izzy", false)) { - player.getDialogueInterpreter().open(437, Repository.findNPC(437), true); - return true; - } - player.removeAttribute("capn_izzy"); - player.getProperties().setTeleportLocation(AGILITY_LOCATION); - break; - } - break; - case "climb": - switch (((Scenery) node).getId()) { - case 1764: - player.getProperties().setTeleportLocation(VOLCANO_RIM); - player.getPacketDispatch().sendMessage("You climb up the hanging rope..."); - player.getPacketDispatch().sendMessage("You appear on the volcano rim."); - break; - } - break; - case "open": - switch (((Scenery) node).getId()) { - case 2626: - player.getDialogueInterpreter().open(789, Repository.findNPC(789)); - break; - case 2628: - player.getPacketDispatch().sendMessage("The door is securely closed."); - break; - case 2627: - player.getDialogueInterpreter().open(788, Repository.findNPC(788), true); - break; - case 1591: - player.getPacketDispatch().sendMessage("You try and open the door..."); - player.getPacketDispatch().sendMessage("The door is locked tight, I can't open it."); - break; - case 2439: - player.getPacketDispatch().sendMessage("The gate doesn't open."); - break; - } - break; - case "search": - switch (((Scenery) node).getId()) { - case 2078: - player.getPacketDispatch().sendMessage("There are no bananas left on the tree."); - break; - } - break; - case "shake": - if (player.getInventory().hasSpaceFor(new Item(Items.PALM_LEAF_2339))) { - player.getPacketDispatch().sendMessage("You shake the tree..."); - SceneryBuilder.replace(node.asScenery(), node.asScenery().transform(2976), 60); // 35 second cool-down - player.lock(); - final Pulse palmPulse = new Pulse(2) { - @Override - public boolean pulse() { - player.getPacketDispatch().sendMessage("You pick up a fallen palm leaf off of the ground."); - player.getInventory().add(new Item(Items.PALM_LEAF_2339)); - // Collect five palm leaves - if (!player.getAchievementDiaryManager().hasCompletedTask(DiaryType.KARAMJA, 2, 7)) { - int palms = player.getAttribute("palms", 0); - palms++; - player.setAttribute("palms", palms); - player.getAchievementDiaryManager().updateTask(player, DiaryType.KARAMJA, 2, 7, palms >= 5); - } - player.unlock(); - return true; - } - }; - GameWorld.getPulser().submit(palmPulse); - } else { - player.getPacketDispatch().sendMessage("You don't have enough inventory space."); - } - break; - } - - return true; - } - - @Override - public Location getDestination(Node node, Node n) { - if (n instanceof Scenery) { - if (((Scenery) n).getDefinition().hasAction("open")) { - return DoorActionHandler.getDestination((Player) node, (Scenery) n); - } - } - return null; - } - -} diff --git a/Server/src/main/content/region/karamja/handlers/KaramjaListeners.kt b/Server/src/main/content/region/karamja/handlers/KaramjaListeners.kt new file mode 100644 index 000000000..3ef1178e5 --- /dev/null +++ b/Server/src/main/content/region/karamja/handlers/KaramjaListeners.kt @@ -0,0 +1,117 @@ +package content.region.karamja.handlers + +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.item.Item +import org.rs09.consts.* + +class KaramjaListeners : InteractionListener { + companion object { + /** + * Represents the Musa Point dungeon location. + */ + private val MUSA_POINT_DUNGEON = location(2856, 9567, 0) + + /** + * Represents the volcano rim location. + */ + private val VOLCANO_RIM = location(2856, 3167, 0) + + /** + * Represents the rocks used to enter the Musa Point dungeon. + */ + private const val MUSA_POINT_DUNGEON_ENTRANCE = Scenery.ROCKS_492 + + /** + * Represents the rope used to exit the Musa Point dungeon. + */ + private const val MUSA_POINT_DUNGEON_EXIT = Scenery.CLIMBING_ROPE_1764 + + /** + * Represents the pineapple plant objects. + */ + private val PINEAPPLE_PLANT = intArrayOf( + Scenery.PINEAPPLE_PLANT_1408, Scenery.PINEAPPLE_PLANT_1409, + Scenery.PINEAPPLE_PLANT_1410, Scenery.PINEAPPLE_PLANT_1411, + Scenery.PINEAPPLE_PLANT_1412, Scenery.PINEAPPLE_PLANT_1413 + ) + private const val PICK_PINEAPPLE_ANIMATION = 2282 + private const val PINEAPPLE = Items.PINEAPPLE_2114 + + private const val SHAKE_TREE_ANIMATION = 2572 + private const val PALM_LEAF = Items.PALM_LEAF_2339 + private const val PALM_TREE_FULL = Scenery.LEAFY_PALM_TREE_2975 + private const val PALM_TREE_EMPTY = Scenery.LEAFY_PALM_TREE_2976 + } + + override fun defineListeners() { + on(MUSA_POINT_DUNGEON_ENTRANCE, IntType.SCENERY, "climb-down") { player, _ -> + ClimbActionHandler.climb(player, ClimbActionHandler.CLIMB_DOWN, MUSA_POINT_DUNGEON) + sendMessage(player, "You climb down through the pot hole.") + return@on true + } + + on(MUSA_POINT_DUNGEON_EXIT, IntType.SCENERY, "climb-up") { player, _ -> + ClimbActionHandler.climb(player, ClimbActionHandler.CLIMB_UP, VOLCANO_RIM) + sendMessage(player, "You climb up the hanging rope...") + sendMessage(player, "You appear on the volcano rim.") + return@on true + } + + on(PINEAPPLE_PLANT, IntType.SCENERY, "pick") { player, node -> + // TODO: Convert FieldPickingPlugin to Kotlin & include this. + if (!hasSpaceFor(player, Item(PINEAPPLE))) { + sendMessage(player, "You don't have enough space in your inventory.") + return@on true + } + if (node.id == Scenery.PINEAPPLE_PLANT_1413) { + sendMessage(player, "There are no pineapples left on this plant.") + return@on true + } + val last: Boolean = node.id == Scenery.PINEAPPLE_PLANT_1412 + if (addItem(player, PINEAPPLE)) { + animate(player, PICK_PINEAPPLE_ANIMATION) + playAudio(player, Sounds.PICK_2581, 30) + replaceScenery(node.asScenery(), node.id + 1, if (last) 270 else 40) + sendMessage(player, "You pick a pineapple.") + } + return@on true + } + + on(Scenery.BANANA_TREE_2078, IntType.SCENERY, "search") { player, _ -> + // TODO: Convert FieldPickingPlugin to Kotlin & include this. + sendMessage(player, "There are no bananas left on the tree.") + return@on true + } + + on(PALM_TREE_FULL, IntType.SCENERY, "Shake") { player, node -> + queueScript(player, 0, QueueStrength.WEAK) { stage: Int -> + when (stage) { + 0 -> { + lock(player, 2) + face(player, node) + animate(player, SHAKE_TREE_ANIMATION) + sendMessage(player, "You give the tree a good shake.") + replaceScenery(node.asScenery(), PALM_TREE_EMPTY, 60) + return@queueScript delayScript(player, 2) + } + 1 -> { + produceGroundItem( + player, + PALM_LEAF, + 1, + getPathableRandomLocalCoordinate(player, 1, node.location) + ) + sendMessage(player, "A palm leaf falls to the ground.") + return@queueScript stopExecuting(player) + } + else -> return@queueScript stopExecuting(player) + } + } + return@on true + } + } +} \ No newline at end of file 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 8b7027c9c..5639c3b20 100644 --- a/Server/src/main/content/region/karamja/quest/junglepotion/JunglePotion.java +++ b/Server/src/main/content/region/karamja/quest/junglepotion/JunglePotion.java @@ -13,6 +13,9 @@ import core.plugin.ClassScanner; 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. * @author Vexia @@ -21,16 +24,11 @@ import core.tools.StringUtils; @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 @@ -77,8 +75,11 @@ public final class JunglePotion extends Quest { player.getPacketDispatch().sendItemZoomOnInterface(Herbs.VOLENCIA_MOSS.getProduct().getId(), 235, 277, 3 + 2); player.getSkills().addExperience(Skills.HERBLORE, 775); player.getQuestRepository().syncronizeTab(player); - player.varpManager.get(534).setVarbit(15,2).setVarbit(18,2).setVarbit(21,2).setVarbit(24,2).setVarbit(12,2).send(player); - player.varpManager.flagSave(534, false); + setVarbit(player, 897, 2); + setVarbit(player, 898, 2); + setVarbit(player, 899, 2); + setVarbit(player, 900, 2); + setVarbit(player, 896, 2, true); } @Override @@ -114,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/ArdougneGuideBook.kt b/Server/src/main/content/region/karamja/quest/tribaltotem/ArdougneGuideBook.kt index 36fa06a8b..74ce79ed9 100644 --- a/Server/src/main/content/region/karamja/quest/tribaltotem/ArdougneGuideBook.kt +++ b/Server/src/main/content/region/karamja/quest/tribaltotem/ArdougneGuideBook.kt @@ -1,163 +1,124 @@ package content.region.karamja.quest.tribaltotem -import core.game.dialogue.book.BookLine -import core.game.dialogue.DialoguePlugin -import core.game.dialogue.book.Book -import core.game.dialogue.book.Page -import core.game.dialogue.book.PageSet +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 /** - * This was some code for a post-tutorial RSPS guide book a long ass - * time ago, but it was using the ID for the guide book for Tribal Totem - * and I kind of need that, bro - * + * Ardougne Guide Book + * https://www.youtube.com/watch?v=-Qburz3T00o + * @author ovenbreado * @author Splinter * @author Phil */ -@Initializable -class ArdougneGuideBook : core.game.dialogue.book.Book { - /** - * Constructs a new `ShieldofArravBook` `Object`. - */ - constructor(player: Player?) : super(player, "Ardougne Guide Book", 1856, *PAGES) {} - - /** - * Constructs a new `ShieldofArravBook` `Object`. - */ - constructor() { - /** - * empty. - */ - } - - override fun finish() {} - override fun display(set: Array) { - player.lock() - player.interfaceManager.open(getInterface()) - player.packetDispatch.sendString("Previous", getInterface().id, 77) - player.packetDispatch.sendString("Next", getInterface().id, 78) - for (i in 55..76) { - player.packetDispatch.sendString("", getInterface().id, i) - } - player.packetDispatch.sendString(getName(), getInterface().id, 6) - for (page in set) { - for (line in page.lines) { - player.packetDispatch.sendString(line.message, getInterface().id, line.child) - } - } - val lastPage = index == sets.size - 1 - if (lastPage) { - finish() - } - player.unlock() - } - - override fun newInstance(player: Player): core.game.dialogue.DialoguePlugin { - return ArdougneGuideBook(player) - } - - override fun getIds(): IntArray { - return intArrayOf(ID) - } - +class ArdougneGuideBook : InteractionListener { companion object { - /** - * Represents the book id - */ - var ID = 387454 - - /** - * Represents the array of pages for this book. - */ - private val PAGES = arrayOf( - core.game.dialogue.book.PageSet( - core.game.dialogue.book.Page( - core.game.dialogue.book.BookLine("Introduction", 57), - core.game.dialogue.book.BookLine("This book is your guide to", 59), - core.game.dialogue.book.BookLine("the vibrant city of Ardougne.", 60), - core.game.dialogue.book.BookLine("Ardougne is known as an", 61), - core.game.dialogue.book.BookLine("exciting modern city located", 62), - core.game.dialogue.book.BookLine("on the sun drenched southern", 63), - core.game.dialogue.book.BookLine("coast of Kandarin", 64), - ), - core.game.dialogue.book.Page( - core.game.dialogue.book.BookLine("Ardougne: City of Shopping!", 66), - core.game.dialogue.book.BookLine("Come sample the delights of", 67), - core.game.dialogue.book.BookLine("the Ardougne market - the", 68), - core.game.dialogue.book.BookLine("biggest in the known world!", 69), - core.game.dialogue.book.BookLine("From spices to silk, there", 70), - core.game.dialogue.book.BookLine("is something here for", 71), - core.game.dialogue.book.BookLine("everybody! Other popular", 72), - core.game.dialogue.book.BookLine("shopping destinations in", 73), - core.game.dialogue.book.BookLine("the area include the Armoury", 74), - core.game.dialogue.book.BookLine("and the ever popular", 75), - core.game.dialogue.book.BookLine("Adventurers' supply store.", 76) - ) + private val TITLE = "Tourist Guide to Ardougne" + private val CONTENTS = arrayOf( + PageSet( + Page( + BookLine("Introduction", 57), + BookLine("This book is your guide to", 59), + BookLine("the vibrant city of Ardougne.", 60), + BookLine("Ardougne is known as an", 61), + BookLine("exciting modern city located", 62), + BookLine("on the sun drenched southern", 63), + BookLine("coast of Kandarin", 64), ), - core.game.dialogue.book.PageSet( - core.game.dialogue.book.Page( - core.game.dialogue.book.BookLine("Ardougne: City of Fun!", 55), - core.game.dialogue.book.BookLine("If you're looking for", 56), - core.game.dialogue.book.BookLine("entertainment in Ardougne,", 57), - core.game.dialogue.book.BookLine("why not pay a visit to the", 58), - core.game.dialogue.book.BookLine("Ardougne City zoo? Or relax", 59), - core.game.dialogue.book.BookLine("with a drink in the ever", 60), - core.game.dialogue.book.BookLine("popular Flying Horse Inn?", 61), - core.game.dialogue.book.BookLine("And for the adventurous,", 62), - core.game.dialogue.book.BookLine("there are always rats to be", 63), - core.game.dialogue.book.BookLine("slaughtered in the expansive", 64), - core.game.dialogue.book.BookLine("and vermin-ridden sewers", 65) - ), - core.game.dialogue.book.Page( - core.game.dialogue.book.BookLine("There is something truly", 66), - core.game.dialogue.book.BookLine("for everybody here!", 67) - ) - ), - core.game.dialogue.book.PageSet( - core.game.dialogue.book.Page( - core.game.dialogue.book.BookLine("Ardougne: City of History!", 55), - core.game.dialogue.book.BookLine("Ardougne is renowned as an", 56), - core.game.dialogue.book.BookLine("important city of historical", 57), - core.game.dialogue.book.BookLine("interest. One historic building", 58), - core.game.dialogue.book.BookLine("is the magnificent Handelmort", 59), - core.game.dialogue.book.BookLine("Mansion, currently owned by", 60), - core.game.dialogue.book.BookLine("Lord Francis Kurt Handelmort.", 61), - core.game.dialogue.book.BookLine("Also of historical interest is", 62), - core.game.dialogue.book.BookLine("Ardougne Castle in the east of", 63), - core.game.dialogue.book.BookLine("the city recently opened to the", 64), - core.game.dialogue.book.BookLine("public. And of course the Holy", 65) - ), - core.game.dialogue.book.Page( - core.game.dialogue.book.BookLine("Order of Paladins still wander", 66), - core.game.dialogue.book.BookLine("the streets of Ardougne, and", 67), - core.game.dialogue.book.BookLine("are often of interest to", 68), - core.game.dialogue.book.BookLine("tourists.", 69) - ) - ), - core.game.dialogue.book.PageSet( - core.game.dialogue.book.Page( - core.game.dialogue.book.BookLine("Further Fields", 55), - core.game.dialogue.book.BookLine("", 56), - core.game.dialogue.book.BookLine("The area surrounding Ardougne", 57), - core.game.dialogue.book.BookLine("is also of great interest to", 58), - core.game.dialogue.book.BookLine("the cultural tourist. If you", 59), - core.game.dialogue.book.BookLine("want to go further afield, why", 60), - core.game.dialogue.book.BookLine("not have a look at the ominous", 61), - core.game.dialogue.book.BookLine("Pillars of Zanash, the", 62), - core.game.dialogue.book.BookLine("mysterious marble pillars", 63), - core.game.dialogue.book.BookLine("located just West of the city?", 64), - core.game.dialogue.book.BookLine("Or perhaps the town of Brimhaven,", 65) - ), - core.game.dialogue.book.Page( - core.game.dialogue.book.BookLine("on the exotic island of Karamja?", 66), - core.game.dialogue.book.BookLine("It's only a short boat trip with", 67), - core.game.dialogue.book.BookLine("regular transport leaving from", 68), - core.game.dialogue.book.BookLine("Ardougne harbor at all times", 69), - core.game.dialogue.book.BookLine("of the day, all year round.", 70) - ) + Page( + BookLine("Ardougne: City of Shopping!", 66), + BookLine("Come sample the delights of", 67), + BookLine("the Ardougne market - the", 68), + BookLine("biggest in the known world!", 69), + BookLine("From spices to silk, there", 70), + BookLine("is something here for", 71), + BookLine("everybody! Other popular", 72), + BookLine("shopping destinations in", 73), + BookLine("the area include the Armoury", 74), + BookLine("and the ever popular", 75), + BookLine("Adventurers' supply store.", 76) ) + ), + PageSet( + Page( + BookLine("Ardougne: City of Fun!", 55), + BookLine("If you're looking for", 56), + BookLine("entertainment in Ardougne,", 57), + BookLine("why not pay a visit to the", 58), + BookLine("Ardougne City zoo? Or relax", 59), + BookLine("with a drink in the ever", 60), + BookLine("popular Flying Horse Inn?", 61), + BookLine("And for the adventurous,", 62), + BookLine("there are always rats to be", 63), + BookLine("slaughtered in the expansive", 64), + BookLine("and vermin-ridden sewers", 65) + ), + Page( + BookLine("There is something truly", 66), + BookLine("for everybody here!", 67) + ) + ), + PageSet( + Page( + BookLine("Ardougne: City of History!", 55), + BookLine("Ardougne is renowned as an", 56), + BookLine("important city of historical", 57), + BookLine("interest. One historic building", 58), + BookLine("is the magnificent Handelmort", 59), + BookLine("Mansion, currently owned by", 60), + BookLine("Lord Francis Kurt Handelmort.", 61), + BookLine("Also of historical interest is", 62), + BookLine("Ardougne Castle in the east of", 63), + BookLine("the city recently opened to the", 64), + BookLine("public. And of course the Holy", 65) + ), + Page( + BookLine("Order of Paladins still wander", 66), + BookLine("the streets of Ardougne, and", 67), + BookLine("are often of interest to", 68), + BookLine("tourists.", 69) + ) + ), + PageSet( + Page( + BookLine("Further Fields", 55), + BookLine("", 56), + BookLine("The area surrounding Ardougne", 57), + BookLine("is also of great interest to", 58), + BookLine("the cultural tourist. If you", 59), + BookLine("want to go further afield, why", 60), + BookLine("not have a look at the ominous", 61), + BookLine("Pillars of Zanash, the", 62), + BookLine("mysterious marble pillars", 63), + BookLine("located just West of the city?", 64), + BookLine("Or perhaps the town of Brimhaven,", 65) + ), + Page( + BookLine("on the exotic island of Karamja?", 66), + BookLine("It's only a short boat trip with", 67), + BookLine("regular transport leaving from", 68), + BookLine("Ardougne harbor at all times", 69), + BookLine("of the day, all year round.", 70) + ) + ) ) + + 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.GUIDE_BOOK_1856, 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/karamja/quest/tribaltotem/CrompertyDialogue.kt b/Server/src/main/content/region/karamja/quest/tribaltotem/CrompertyDialogue.kt index d1ce9ee2f..54ec527f3 100644 --- a/Server/src/main/content/region/karamja/quest/tribaltotem/CrompertyDialogue.kt +++ b/Server/src/main/content/region/karamja/quest/tribaltotem/CrompertyDialogue.kt @@ -10,14 +10,18 @@ import core.game.world.update.flag.context.Graphics import core.plugin.Initializable import org.rs09.consts.NPCs import content.global.travel.EssenceTeleport +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) { override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - npcl(core.game.dialogue.FacialExpression.HAPPY,"Hello ${player.name}, I'm Cromperty. Sedridor has told me about you. As a wizard and an inventor he has aided me in my great invention!") + npcl(core.game.dialogue.FacialExpression.HAPPY,"Hello, ${player.username}, I'm Cromperty. Sedridor has told me about you. As a wizard and an inventor he has aided me in my great invention!") stage = 0 return true } @@ -25,11 +29,11 @@ class CrompertyDialogue(player: Player? = null) : core.game.dialogue.DialoguePlu override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ 0 -> options("Two jobs? that's got to be tough","So what have you invented?","Can you teleport me to the Rune Essence?").also { stage++ } - 1 ->when(buttonId){ + 1 -> when(buttonId){ 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() } @@ -54,11 +58,11 @@ class CrompertyDialogue(player: Player? = null) : core.game.dialogue.DialoguePlu 2 -> playerl(core.game.dialogue.FacialExpression.ASKING,"Can I be teleported please?").also { stage = 25 } } - 15 -> npcl(core.game.dialogue.FacialExpression.THINKING,"Well...Hmm. I would guess somewhere between here and the Wizards' Tower in Misthalin.").also { stage++ } + 15 -> npcl(core.game.dialogue.FacialExpression.THINKING,"Well... Hmm. I would guess somewhere between here and the Wizards' Tower in Misthalin.").also { stage++ } 16 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"All I know is that it hasn't got there yet as the wizards there would have contacted me.").also { stage++ } - 17 -> npcl(core.game.dialogue.FacialExpression.THINKING,"I'm using the GPDT for delivery. They assured me it would be delivered promptly.").also { stage++ } - 18 -> playerl(core.game.dialogue.FacialExpression.ASKING,"Who are the GPDT?").also { stage++ } - 19 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"The Gielinor Parcel Delivery Team. They come very highly recommended.").also { stage++ } + 17 -> npcl(core.game.dialogue.FacialExpression.THINKING,"I'm using the RPDT for delivery. They assured me it would be delivered promptly.").also { stage++ } + 18 -> playerl(core.game.dialogue.FacialExpression.ASKING,"Who are the RPDT?").also { stage++ } + 19 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"The ${ServerConstants.SERVER_NAME} Parcel Delivery Team. They come very highly recommended.").also { stage++ } 20 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Their motto is: \"We aim to deliver your stuff at some point after you have paid us!\"").also { stage = 1000 } 25 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"By all means! I'm afraid I can't give you any specifics as to where you will come out however. Presumably wherever the other block is located.").also { stage++ } @@ -70,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 @@ -102,17 +106,17 @@ class CrompertyDialogue(player: Player? = null) : core.game.dialogue.DialoguePlu npc.faceTemporary(player, 1) npc.graphics(Graphics(108)) player.lock() - player.audioManager.send(125) + playAudio(player, Sounds.CURSE_ALL_125, 0, 1) Projectile.create(npc, player, 109).send() 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/GPDTEmployeeDialogue.kt b/Server/src/main/content/region/karamja/quest/tribaltotem/GPDTEmployeeDialogue.kt deleted file mode 100644 index f7ed2539e..000000000 --- a/Server/src/main/content/region/karamja/quest/tribaltotem/GPDTEmployeeDialogue.kt +++ /dev/null @@ -1,41 +0,0 @@ -package content.region.karamja.quest.tribaltotem - -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 GPDTEmployeeDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { - override fun open(vararg args: Any?): Boolean { - npcl(core.game.dialogue.FacialExpression.HAPPY,"Welcome to G.P.D.T.!") - stage = if(player.questRepository.getStage("Tribal Totem") == 20){ - 5 - }else 0 - return true - } - - override fun handle(interfaceId: Int, buttonId: Int): Boolean { - when(stage){ - 0 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"Thank you very much.").also { stage = 1000 } - - 5 -> playerl(core.game.dialogue.FacialExpression.ASKING,"So, when are you going to deliver this crate?").also { stage++ } - 6 -> npcl(core.game.dialogue.FacialExpression.THINKING,"Well... I guess we could do it now...").also { - player.questRepository.getQuest("Tribal Totem").setStage(player,25) - stage = 1000 - } - - 1000 -> end() - } - return true - } - - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { - return GPDTEmployeeDialogue(player) - } - - override fun getIds(): IntArray { - return intArrayOf(NPCs.RPDT_EMPLOYEE_843) - } -} \ No newline at end of file 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 c6f2b8a16..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,17 +4,18 @@ 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) : core.game.dialogue.DialoguePlugin(player) { +class HoracioDialogue(player: Player? = null) : DialoguePlugin(player) { override fun open(vararg args: Any?): Boolean { - if(player.questRepository.hasStarted("Tribal Totem")){ - npcl(core.game.dialogue.FacialExpression.HAPPY,"It's a fine day to be out in a garden, isn't it? ") + 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 } else{ - npcl(core.game.dialogue.FacialExpression.HAPPY,"It's a fine day to be out in a garden, isn't it? ") + npcl(FacialExpression.HAPPY,"It's a fine day to be out in a garden, isn't it? ") stage = 0 } return true @@ -22,33 +23,33 @@ class HoracioDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugi override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 0 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"Yes it's very nice.").also { stage++ } - 1 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Days like these make me glad to be alive!").also { stage = 1000 } + 0 -> playerl(FacialExpression.HAPPY,"Yes it's very nice.").also { stage++ } + 1 -> npcl(FacialExpression.HAPPY,"Days like these make me glad to be alive!").also { stage = 1000 } - 5 -> playerl(core.game.dialogue.FacialExpression.ASKING,"So... who are you?").also { stage++ } - 6 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"My name is Horacio Dobson. I'm a gardener to Lord Handlemort.").also { stage++ } - 7 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Take a look around this beautiful garden, all of this is my handiwork.").also { stage++ } + 5 -> playerl(FacialExpression.ASKING,"So... who are you?").also { stage++ } + 6 -> npcl(FacialExpression.HAPPY,"My name is Horacio Dobson. I'm a gardener to Lord Handlemort.").also { stage++ } + 7 -> npcl(FacialExpression.HAPPY,"Take a look around this beautiful garden, all of this is my handiwork.").also { stage++ } 8 -> options("So... do you garden round the back too?","Do you need any help?").also { stage++ } 9 -> when(buttonId){ - 1 -> playerl(core.game.dialogue.FacialExpression.THINKING,"So... do you garden round the back, too?").also { stage = 10 } - 2 -> playerl(core.game.dialogue.FacialExpression.ASKING,"Do you need any help?").also { stage = 20 } + 1 -> playerl(FacialExpression.THINKING,"So... do you garden round the back, too?").also { stage = 10 } + 2 -> playerl(FacialExpression.ASKING,"Do you need any help?").also { stage = 20 } } - 10 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"That I do!").also { stage++ } - 11 -> playerl(core.game.dialogue.FacialExpression.ASKING,"Doesn't all of the security around the house get in your way then?").also { stage++ } - 12 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Ah. I'm used to all that. I have my keys, the guard dogs know me, and I know the combination to the door lock.").also { stage++ } - 13 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"It's rather easy, it's his middle name.").also { stage++ } - 14 -> playerl(core.game.dialogue.FacialExpression.ASKING,"Whose middle name?").also { stage++ } - 15 -> npcl(core.game.dialogue.FacialExpression.ANNOYED,"Hum. I probably shouldn't have said that. Forget I mentioned it.").also { stage = 1000 } + 10 -> npcl(FacialExpression.HAPPY,"That I do!").also { stage++ } + 11 -> playerl(FacialExpression.ASKING,"Doesn't all of the security around the house get in your way then?").also { stage++ } + 12 -> npcl(FacialExpression.HAPPY,"Ah. I'm used to all that. I have my keys, the guard dogs know me, and I know the combination to the door lock.").also { stage++ } + 13 -> npcl(FacialExpression.HAPPY,"It's rather easy, it's his middle name.").also { stage++ } + 14 -> playerl(FacialExpression.ASKING,"Whose middle name?").also { stage++ } + 15 -> npcl(FacialExpression.ANNOYED,"Hum. I probably shouldn't have said that. Forget I mentioned it.").also { stage = 1000 } - 20 -> npcl(core.game.dialogue.FacialExpression.ANNOYED,"Trying to muscle in on my job, eh? I'm more than happy to do this all by myself!").also { stage = 1000 } + 20 -> npcl(FacialExpression.ANNOYED,"Trying to muscle in on my job, eh? I'm more than happy to do this all by myself!").also { stage = 1000 } 1000 -> end() } return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return HoracioDialogue(player) } 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 57968ab94..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,24 +7,25 @@ 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) : core.game.dialogue.DialoguePlugin(player) { +class KangaiMauDialogue(player: Player? = null) : DialoguePlugin(player) { override fun open(vararg args: Any?): Boolean { - if(!player.questRepository.hasStarted("Tribal Totem")){ - npcl(core.game.dialogue.FacialExpression.HAPPY,"Hello. I'm Kangai Mau of the Rantuki Tribe.") + 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")) { - npcl(core.game.dialogue.FacialExpression.HAPPY, "Many greetings esteemed thief.") + } else if(isQuestComplete(player, Quests.TRIBAL_TOTEM)) { + npcl(FacialExpression.HAPPY, "Many greetings esteemed thief.") stage = 40 } else if(player.inventory.containsAtLeastOneItem(Items.TOTEM_1857)){ - npcl(core.game.dialogue.FacialExpression.ASKING,"Have you got our totem back?") + npcl(FacialExpression.ASKING,"Have you got our totem back?") stage = 35 } - else if(player.questRepository.hasStarted("Tribal Totem")){ - npcl(core.game.dialogue.FacialExpression.ASKING,"Have you got our totem back?") + else if(player.questRepository.hasStarted(Quests.TRIBAL_TOTEM)){ + npcl(FacialExpression.ASKING,"Have you got our totem back?") stage = 30 } return true @@ -34,54 +35,54 @@ class KangaiMauDialogue(player: Player? = null) : core.game.dialogue.DialoguePlu when(stage){ 0 -> options("And what are you doing here in Brimhaven?","I'm in search of adventure!","Who are the Rantuki tribe?").also { stage++ } 1 -> when(buttonId){ - 1 -> playerl(core.game.dialogue.FacialExpression.ASKING,"And what are you doing here in Brimhaven?").also { stage = 5 } - 2 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"I'm in search of adventure!").also { stage = 15 } - 3 -> playerl(core.game.dialogue.FacialExpression.ASKING,"Who are the Rantuki Tribe?").also { stage = 10 } + 1 -> playerl(FacialExpression.ASKING,"And what are you doing here in Brimhaven?").also { stage = 5 } + 2 -> playerl(FacialExpression.HAPPY,"I'm in search of adventure!").also { stage = 15 } + 3 -> playerl(FacialExpression.ASKING,"Who are the Rantuki Tribe?").also { stage = 10 } } - 5 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"I'm looking for someone brave to go on important mission. Someone skilled in thievery and sneaking about.").also { stage++ } - 6 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"I am told I can find such people in Brimhaven.").also { stage++ } - 7 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"Yep. I have heard there are many of that type here.").also { stage++ } - 8 -> npcl(core.game.dialogue.FacialExpression.THINKING,"Let's hope I find them.").also { stage = 1000 } + 5 -> npcl(FacialExpression.HAPPY,"I'm looking for someone brave to go on important mission. Someone skilled in thievery and sneaking about.").also { stage++ } + 6 -> npcl(FacialExpression.HAPPY,"I am told I can find such people in Brimhaven.").also { stage++ } + 7 -> playerl(FacialExpression.HAPPY,"Yep. I have heard there are many of that type here.").also { stage++ } + 8 -> npcl(FacialExpression.THINKING,"Let's hope I find them.").also { stage = 1000 } - 10 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"A proud and noble tribe of Karamja.").also { stage++ } - 11 -> npcl(core.game.dialogue.FacialExpression.ANGRY,"But now we are few, as men come from across, steal our land, and settle on our hunting grounds").also { stage = 1000 } + 10 -> npcl(FacialExpression.HAPPY,"A proud and noble tribe of Karamja.").also { stage++ } + 11 -> npcl(FacialExpression.ANGRY,"But now we are few, as men come from across, steal our land, and settle on our hunting grounds").also { stage = 1000 } - 15 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Adventure is something I may be able to give. I need someone to go on a mission to the city of Ardougne.").also { stage++ } - 16 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"There you will find the house of Lord Handlemort. In his house he has our tribal totem. We need it back.").also { stage++ } - 17 -> playerl(core.game.dialogue.FacialExpression.ASKING,"Why does he have it?").also { stage++ } - 18 -> npcl(core.game.dialogue.FacialExpression.ANGRY,"Lord Handlemort is an Ardougnese explorer which means he think he have the right to come to my tribal home,").also { stage++ } - 19 -> npcl(core.game.dialogue.FacialExpression.ANGRY,"steal our stuff and put in his private museum.").also { stage++ } - 20 -> playerl(core.game.dialogue.FacialExpression.THINKING,"How can I find Handlemoret's house? Ardougne IS a big place...").also { stage++ } - 21 -> npcl(core.game.dialogue.FacialExpression.ANNOYED,"I don't know Ardougne. You tell me.").also { stage++ } - 22 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"Ok, I will get it back.").also { - player.questRepository.getQuest("Tribal Totem").start(player) - player.questRepository.getQuest("Tribal Totem").setStage(player, 10) + 15 -> npcl(FacialExpression.HAPPY,"Adventure is something I may be able to give. I need someone to go on a mission to the city of Ardougne.").also { stage++ } + 16 -> npcl(FacialExpression.HAPPY,"There you will find the house of Lord Handlemort. In his house he has our tribal totem. We need it back.").also { stage++ } + 17 -> playerl(FacialExpression.ASKING,"Why does he have it?").also { stage++ } + 18 -> npcl(FacialExpression.ANGRY,"Lord Handlemort is an Ardougnese explorer which means he think he have the right to come to my tribal home,").also { stage++ } + 19 -> npcl(FacialExpression.ANGRY,"steal our stuff and put in his private museum.").also { stage++ } + 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(Quests.TRIBAL_TOTEM).start(player) + player.questRepository.getQuest(Quests.TRIBAL_TOTEM).setStage(player, 10) stage++ } - 23 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"Best of luck with that adventurer").also { stage = 1000 } + 23 -> npcl(FacialExpression.HAPPY,"Best of luck with that adventurer").also { stage = 1000 } - 30 -> playerl(core.game.dialogue.FacialExpression.SAD,"Not yet, sorry.").also { stage = 1000 } + 30 -> playerl(FacialExpression.SAD,"Not yet, sorry.").also { stage = 1000 } - 35 -> playerl(core.game.dialogue.FacialExpression.HAPPY,"Yes I have.").also { stage++ } - 36 -> npcl(core.game.dialogue.FacialExpression.HAPPY,"You have??? Many thanks brave adventurer! Here, have some freshly cooked Karamjan fish, caught specially by my tribe.").also { stage++ } + 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 } } - 40 -> player(core.game.dialogue.FacialExpression.NEUTRAL, "Hey.").also { stage = 1000 } + 40 -> player(FacialExpression.NEUTRAL, "Hey.").also { stage = 1000 } 1000 -> end() } return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return KangaiMauDialogue(player) } diff --git a/Server/src/main/content/region/karamja/quest/tribaltotem/RPDTEmployeeDialogue.kt b/Server/src/main/content/region/karamja/quest/tribaltotem/RPDTEmployeeDialogue.kt new file mode 100644 index 000000000..2bd933011 --- /dev/null +++ b/Server/src/main/content/region/karamja/quest/tribaltotem/RPDTEmployeeDialogue.kt @@ -0,0 +1,42 @@ +package content.region.karamja.quest.tribaltotem + +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 +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(Quests.TRIBAL_TOTEM) == 20){ + 5 + }else 0 + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + 0 -> playerl(FacialExpression.HAPPY,"Thank you very much.").also { stage = 1000 } + + 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(Quests.TRIBAL_TOTEM).setStage(player,25) + stage = 1000 + } + + 1000 -> end() + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return RPDTEmployeeDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.RPDT_EMPLOYEE_843) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/karamja/quest/tribaltotem/TTDoorCodeInterfaceListener.kt b/Server/src/main/content/region/karamja/quest/tribaltotem/TTDoorCodeInterfaceListener.kt index e3e2fe633..372a51f0c 100644 --- a/Server/src/main/content/region/karamja/quest/tribaltotem/TTDoorCodeInterfaceListener.kt +++ b/Server/src/main/content/region/karamja/quest/tribaltotem/TTDoorCodeInterfaceListener.kt @@ -9,44 +9,18 @@ import core.game.interaction.InterfaceListener class TTDoorCodeInterfaceListener : InterfaceListener { override fun defineInterfaceListeners() { - val LETTERONEBACK = 10 - val LETTERONEFORWARD = 11 - val LETTERTWOBACK = 12 - val LETTERTWOFORWARD = 13 - val LETTERTHREEBACK = 14 - val LETTERTHREEFORWARD = 15 - val LETTERFOURBACK = 16 - val LETTERFOURFORWARD = 17 - val ENTER = 18 - val DOORLOCKINTERFACE = 285 - val LETTERS = arrayOf( - "A", - "B", - "C", - "D", - "E", - "F", - "G", - "H", - "I", - "J", - "K", - "L", - "M", - "N", - "O", - "P", - "Q", - "R", - "S", - "T", - "U", - "V", - "W", - "X", - "Y", - "Z" - ) + val LETTERONEBACK = 17 + val LETTERONEFORWARD = 18 + val LETTERTWOBACK = 19 + val LETTERTWOFORWARD = 20 + val LETTERTHREEBACK = 21 + val LETTERTHREEFORWARD = 22 + val LETTERFOURBACK = 23 + val LETTERFOURFORWARD = 24 + val ENTER = 27 + val EXIT = 28 + val DOORLOCKINTERFACE = 369 + val LETTERS = arrayOf("A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z") onOpen(DOORLOCKINTERFACE) { player, component -> player.setAttribute("tt-letter-one", 0) @@ -55,6 +29,7 @@ class TTDoorCodeInterfaceListener : InterfaceListener { player.setAttribute("tt-letter-four", 0) return@onOpen true } + onClose(DOORLOCKINTERFACE) { player, component -> player.removeAttribute("tt-letter-one") player.removeAttribute("tt-letter-two") @@ -62,125 +37,89 @@ class TTDoorCodeInterfaceListener : InterfaceListener { player.removeAttribute("tt-letter-four") return@onClose true } + on(DOORLOCKINTERFACE) { player, component, opcode, buttonID, slot, itemID -> when (buttonID) { LETTERONEBACK -> { if (player.getAttribute("tt-letter-one", 0) == 0) { player.setAttribute("tt-letter-one", 25) - setInterfaceText(player, LETTERS[player.getAttribute("tt-letter-one", 0)], DOORLOCKINTERFACE, 6) + setInterfaceText(player, LETTERS[player.getAttribute("tt-letter-one", 0)], DOORLOCKINTERFACE, 13) } else { (player.incrementAttribute("tt-letter-one", -1)) - setInterfaceText(player, LETTERS[player.getAttribute("tt-letter-one", 0)], DOORLOCKINTERFACE, 6) + setInterfaceText(player, LETTERS[player.getAttribute("tt-letter-one", 0)], DOORLOCKINTERFACE, 13) } } + LETTERONEFORWARD -> { if (player.getAttribute("tt-letter-one", 0) == 25) { player.setAttribute("tt-letter-one", 0) - setInterfaceText(player, LETTERS[player.getAttribute("tt-letter-one", 0)], DOORLOCKINTERFACE, 6) + setInterfaceText(player, LETTERS[player.getAttribute("tt-letter-one", 0)], DOORLOCKINTERFACE, 13) } else { (player.incrementAttribute("tt-letter-one", 1)) - setInterfaceText( - player, - LETTERS[player.getAttribute("tt-letter-one", 0)], - DOORLOCKINTERFACE, - 6 - ) + setInterfaceText(player, LETTERS[player.getAttribute("tt-letter-one", 0)], DOORLOCKINTERFACE, 13) } } + LETTERTWOBACK -> { if (player.getAttribute("tt-letter-two", 0) == 0) { player.setAttribute("tt-letter-two", 25) - setInterfaceText(player, LETTERS[player.getAttribute("tt-letter-two", 0)], DOORLOCKINTERFACE, 7) + setInterfaceText(player, LETTERS[player.getAttribute("tt-letter-two", 0)], DOORLOCKINTERFACE, 14) } else { (player.incrementAttribute("tt-letter-two", -1)) - setInterfaceText(player, LETTERS[player.getAttribute("tt-letter-two", 0)], DOORLOCKINTERFACE, 7) + setInterfaceText(player, LETTERS[player.getAttribute("tt-letter-two", 0)], DOORLOCKINTERFACE, 14) } } + LETTERTWOFORWARD -> { if (player.getAttribute("tt-letter-two", 0) == 25) { player.setAttribute("tt-letter-two", 0) - setInterfaceText(player, LETTERS[player.getAttribute("tt-letter-two", 0)], DOORLOCKINTERFACE, 7) + setInterfaceText(player, LETTERS[player.getAttribute("tt-letter-two", 0)], DOORLOCKINTERFACE, 14) } else { (player.incrementAttribute("tt-letter-two", 1)) - setInterfaceText(player, LETTERS[player.getAttribute("tt-letter-two", 0)], DOORLOCKINTERFACE, 7) + setInterfaceText(player, LETTERS[player.getAttribute("tt-letter-two", 0)], DOORLOCKINTERFACE, 14) } } + LETTERTHREEBACK -> { if (player.getAttribute("tt-letter-three", 0) == 0) { player.setAttribute("tt-letter-three", 25) - setInterfaceText( - player, - LETTERS[player.getAttribute("tt-letter-three", 0)], - DOORLOCKINTERFACE, - 8 - ) + setInterfaceText(player, LETTERS[player.getAttribute("tt-letter-three", 0)], DOORLOCKINTERFACE, 15) } else { (player.incrementAttribute("tt-letter-three", -1)) - setInterfaceText( - player, - LETTERS[player.getAttribute("tt-letter-three", 0)], - DOORLOCKINTERFACE, - 8 - ) + setInterfaceText(player, LETTERS[player.getAttribute("tt-letter-three", 0)], DOORLOCKINTERFACE, 15) } } + LETTERTHREEFORWARD -> { if (player.getAttribute("tt-letter-three", 0) == 25) { player.setAttribute("tt-letter-three", 0) - setInterfaceText( - player, - LETTERS[player.getAttribute("tt-letter-three", 0)], - DOORLOCKINTERFACE, - 8 - ) + setInterfaceText(player, LETTERS[player.getAttribute("tt-letter-three", 0)], DOORLOCKINTERFACE, 15) } else { (player.incrementAttribute("tt-letter-three", 1)) - setInterfaceText( - player, - LETTERS[player.getAttribute("tt-letter-three", 0)], - DOORLOCKINTERFACE, - 8 - ) + setInterfaceText(player, LETTERS[player.getAttribute("tt-letter-three", 0)], DOORLOCKINTERFACE, 15) } } + LETTERFOURBACK -> { if (player.getAttribute("tt-letter-four", 0) == 0) { player.setAttribute("tt-letter-four", 25) - setInterfaceText( - player, - LETTERS[player.getAttribute("tt-letter-four", 0)], - DOORLOCKINTERFACE, - 9 - ) + setInterfaceText(player, LETTERS[player.getAttribute("tt-letter-four", 0)], DOORLOCKINTERFACE, 16) } else { (player.incrementAttribute("tt-letter-four", -1)) - setInterfaceText( - player, - LETTERS[player.getAttribute("tt-letter-four", 0)], - DOORLOCKINTERFACE, - 9 - ) + setInterfaceText(player, LETTERS[player.getAttribute("tt-letter-four", 0)], DOORLOCKINTERFACE, 16) } } + LETTERFOURFORWARD -> { if (player.getAttribute("tt-letter-four", 0) == 25) { player.setAttribute("tt-letter-four", 0) - setInterfaceText( - player, - LETTERS[player.getAttribute("tt-letter-four", 0)], - DOORLOCKINTERFACE, - 9 - ) + setInterfaceText(player, LETTERS[player.getAttribute("tt-letter-four", 0)], DOORLOCKINTERFACE, 16) } else { (player.incrementAttribute("tt-letter-four", 1)) - setInterfaceText( - player, - LETTERS[player.getAttribute("tt-letter-four", 0)], - DOORLOCKINTERFACE, - 9 - ) + setInterfaceText(player, LETTERS[player.getAttribute("tt-letter-four", 0)], DOORLOCKINTERFACE, 16) } } + ENTER -> { val letterOne = LETTERS[player.getAttribute("tt-letter-one", 0)] val letterTwo = LETTERS[player.getAttribute("tt-letter-two", 0)] 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 4cfd74f3e..33ab1eb7e 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,7 +23,7 @@ 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.") @@ -30,11 +31,11 @@ class TribalTotemListeners : InteractionListener { } 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) } - 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 +49,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 } @@ -56,7 +57,7 @@ class TribalTotemListeners : InteractionListener { if(player.getAttribute("TT:DoorUnlocked",false) == true){ core.game.global.action.DoorActionHandler.handleAutowalkDoor(player,node.asScenery()) }else{ - openInterface(player,285) + openInterface(player,369) } return@on true } 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/dialogue/CrumpledScroll.kt b/Server/src/main/content/region/karamja/shilo/dialogue/CrumpledScroll.kt new file mode 100644 index 000000000..fc35f251d --- /dev/null +++ b/Server/src/main/content/region/karamja/shilo/dialogue/CrumpledScroll.kt @@ -0,0 +1,37 @@ +package content.region.karamja.shilo.dialogue + +import core.api.openInterface +import core.api.setInterfaceText +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import org.rs09.consts.Items + +class CrumpledScroll : InteractionListener { + // Receive during the Shilo Village quest. + companion object { + private const val MESSAGE_SCROLL = 222 + val CRUMPLED_SCROLL_TEXT = arrayOf( + "Rashiliyia's rage went unchecked. She killed", + "", + "without mercy for revenge of her son's life. Like", + "", + "a spectre through the night she entered houses", + "", + "and one by one quietly strangled life from the", + "", + "occupants. It is said that only a handful survived,", + "", + "protected by the necklace wards to keep the Witch", + "", + "Queen at bay." + ) + } + + override fun defineListeners() { + on(Items.CRUMPLED_SCROLL_608, IntType.ITEM, "read") { player, _ -> + openInterface(player, MESSAGE_SCROLL) + setInterfaceText(player, CRUMPLED_SCROLL_TEXT.joinToString("
"), MESSAGE_SCROLL, 3) + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/karamja/shilo/dialogue/FernaheiDialogue.kt b/Server/src/main/content/region/karamja/shilo/dialogue/FernaheiDialogue.kt index ba79870d7..3a2655ac4 100644 --- a/Server/src/main/content/region/karamja/shilo/dialogue/FernaheiDialogue.kt +++ b/Server/src/main/content/region/karamja/shilo/dialogue/FernaheiDialogue.kt @@ -12,11 +12,11 @@ import org.rs09.consts.NPCs */ @Initializable -class FernaheiDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class FernaheiDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - npc(core.game.dialogue.FacialExpression.FRIENDLY,"Welcome to Fernahei's Fishing Shop Bwana!", + npc(FacialExpression.FRIENDLY,"Welcome to Fernahei's Fishing Shop Bwana!", "Would you like to see my items?").also { stage = 0 } return true } @@ -28,8 +28,8 @@ class FernaheiDialogue(player: Player? = null) : core.game.dialogue.DialoguePlug } 1 -> when (buttonId) { - 1 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "Yes, please.").also { stage = 10 } - 2 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "No, but thanks for the offer.").also { stage = 20 } + 1 -> player(FacialExpression.FRIENDLY, "Yes, please.").also { stage = 10 } + 2 -> player(FacialExpression.FRIENDLY, "No, but thanks for the offer.").also { stage = 20 } } 10 -> { @@ -37,7 +37,7 @@ class FernaheiDialogue(player: Player? = null) : core.game.dialogue.DialoguePlug } 20 -> { - npc(core.game.dialogue.FacialExpression.FRIENDLY, "That's fine, and thanks for your interest.").also { stage = 99 } + npc(FacialExpression.FRIENDLY, "That's fine, and thanks for your interest.").also { stage = 99 } } 99 -> end() @@ -45,7 +45,7 @@ class FernaheiDialogue(player: Player? = null) : core.game.dialogue.DialoguePlug return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return FernaheiDialogue(player) } diff --git a/Server/src/main/content/region/karamja/shilo/dialogue/ObliDialogue.kt b/Server/src/main/content/region/karamja/shilo/dialogue/ObliDialogue.kt index 2f7b5d11a..6299c94ee 100644 --- a/Server/src/main/content/region/karamja/shilo/dialogue/ObliDialogue.kt +++ b/Server/src/main/content/region/karamja/shilo/dialogue/ObliDialogue.kt @@ -12,11 +12,11 @@ import org.rs09.consts.NPCs */ @Initializable -class ObliDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class ObliDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - npc(core.game.dialogue.FacialExpression.FRIENDLY,"Welcome to Obli's General Store Bwana!", + npc(FacialExpression.FRIENDLY,"Welcome to Obli's General Store Bwana!", "Would you like to see my items?").also { stage = 0 } return true } @@ -28,8 +28,8 @@ class ObliDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(p } 1 -> when (buttonId) { - 1 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "Yes, please.").also { stage = 10 } - 2 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "No, but thanks for the offer.").also { stage = 99 } + 1 -> player(FacialExpression.FRIENDLY, "Yes, please.").also { stage = 10 } + 2 -> player(FacialExpression.FRIENDLY, "No, but thanks for the offer.").also { stage = 99 } } 10 -> { @@ -41,7 +41,7 @@ class ObliDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(p return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return ObliDialogue(player) } diff --git a/Server/src/main/content/region/karamja/shilo/dialogue/TatteredScroll.kt b/Server/src/main/content/region/karamja/shilo/dialogue/TatteredScroll.kt new file mode 100644 index 000000000..2e4a1b880 --- /dev/null +++ b/Server/src/main/content/region/karamja/shilo/dialogue/TatteredScroll.kt @@ -0,0 +1,34 @@ +package content.region.karamja.shilo.dialogue + +import content.global.handlers.iface.ScrollInterface +import content.global.handlers.iface.ScrollLine +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import org.rs09.consts.Components +import org.rs09.consts.Items + +class TatteredScroll : InteractionListener { + // Receive during the Shilo Village quest. + companion object { + private const val SCROLL_INTERFACE = Components.MESSAGESCROLL_220 + val TATTERED_SCROLL_TEXT = arrayOf( + ScrollLine("Bervirius, son of King Danthalas, was killed in battle. His",3), + ScrollLine("devout Mother Rashiliyia was so heartbroken that she swore",4), + ScrollLine("fealty to Zamorak if he would return her son to her. Bervirius",5), + ScrollLine("returned as an undead creature and terrorized the King and",6), + ScrollLine("Queen. Many guards died fighting the Undead Bervirius.",7), + ScrollLine("Eventually the undead Bervirius was set on fire and soon only",8), + ScrollLine("the bones remained.His remains were taken far to the South,",9), + ScrollLine("and then towards the setting sun to a tomb that is surrounded",10), + ScrollLine("by and level with the sea. The only remedy for containing the",11), + ScrollLine("spirits of witches and undead.",12), + ) + } + + override fun defineListeners() { + on(Items.TATTERED_SCROLL_607, IntType.ITEM, "read") { player, _ -> + ScrollInterface.scrollSetup(player, SCROLL_INTERFACE, TATTERED_SCROLL_TEXT) + return@on true + } + } +} \ No newline at end of file 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 7011a44ca..e06e1ad09 100644 --- a/Server/src/main/content/region/karamja/shilo/handlers/BrokenCartBypass.java +++ b/Server/src/main/content/region/karamja/shilo/handlers/BrokenCartBypass.java @@ -11,6 +11,9 @@ import core.game.world.update.flag.context.Animation; import core.plugin.Initializable; import core.plugin.Plugin; +import static core.api.ContentAPIKt.hasRequirement; +import content.data.Quests; + /** * Temporarily fix to allow people into shilo village by foot until the Shilo Village quest is added @@ -39,6 +42,8 @@ public class BrokenCartBypass extends OptionHandler { }); } public final boolean handle(Player player, Node node, String options){ + if (!hasRequirement(player, Quests.SHILO_VILLAGE)) + return true; Location location = new Location(0,0); Location playerloc = new Location(player.getLocation().getX(),player.getLocation().getY()); if(options.equals("look-at")) { diff --git a/Server/src/main/content/region/karamja/shilo/handlers/ShiloCart.kt b/Server/src/main/content/region/karamja/shilo/handlers/ShiloCart.kt new file mode 100644 index 000000000..1b15bbcab --- /dev/null +++ b/Server/src/main/content/region/karamja/shilo/handlers/ShiloCart.kt @@ -0,0 +1,121 @@ +package content.region.karamja.shilo.handlers + +import core.api.* +import core.game.component.Component +import core.game.dialogue.DialogueFile +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.interaction.QueueStrength +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.link.diary.DiaryType +import core.game.node.item.Item +import core.game.world.map.Location +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 { + + override fun defineListeners() { + + val BRIMHAVEN_CART = 2230 + val SHILO_CART = 2265 + + // Cart IN Brimhaven + on(BRIMHAVEN_CART, IntType.SCENERY, "board") { player, _ -> + openDialogue(player, CartTravelDialogue(), NPC(NPCs.HAJEDY_510)) + return@on true + } + on(BRIMHAVEN_CART, IntType.SCENERY, "pay-fare") { player, _ -> + openDialogue(player, CartQuickPay(), NPC(NPCs.HAJEDY_510)) + return@on true + } + + // Hajedy + on(NPCs.HAJEDY_510, IntType.NPC, "talk-to") { player, _ -> + openDialogue(player, CartTravelDialogue(), NPC(NPCs.HAJEDY_510)) + return@on true + } + on(NPCs.HAJEDY_510, IntType.NPC, "pay-fare") { player, _ -> + openDialogue(player, CartQuickPay(), NPC(NPCs.HAJEDY_510)) + return@on true + } + + + // Cart IN Shilo + on(SHILO_CART, IntType.SCENERY, "board") { player, _ -> + openDialogue(player, CartTravelDialogue(), NPC(NPCs.VIGROY_511)) + return@on true + } + on(SHILO_CART, IntType.SCENERY, "pay-fare") { player, _ -> + openDialogue(player, CartQuickPay(), NPC(NPCs.VIGROY_511)) + return@on true + } + + // Vigroy + on(NPCs.VIGROY_511, IntType.NPC, "talk-to") { player, npc -> + openDialogue(player, CartTravelDialogue(), npc) + return@on true + } + on(NPCs.VIGROY_511, IntType.NPC, "pay-fare") { player, npc -> + openDialogue(player, CartQuickPay(), npc) + return@on true + } + } +} + +class CartQuickPay : DialogueFile(){ + override fun handle(interfaceId: Int, buttonId: Int) { + if (!hasRequirement(player!!, Quests.SHILO_VILLAGE)) return; + val shilo = npc?.id == 510; + when (stage) { + 0 -> if(inInventory(player!!,Items.COINS_995,10)){ + sendDialogue(player!!,"You pay the fare and hand 10 gold coins to "+ (npc?.name ?: "") +".").also { stage++ } + } else { + sendMessage(player!!,"You don't have enough coins.").also { stage = END_DIALOGUE } + } + 1 -> { + if(removeItem(player!!,Item(Items.COINS_995,10))){ + queueScript(player!!, 1, QueueStrength.SOFT) { Qstage: Int -> + when(Qstage){ + 0 -> { + player!!.interfaceManager.closeOverlay() + player!!.interfaceManager.openOverlay(Component(Components.FADE_TO_BLACK_120)) + return@queueScript keepRunning(player!!) + } + 1 -> { + teleport(player!!, if (shilo) Location.create(2834, 2951, 0) else Location.create(2780, 3212, 0)) + player!!.interfaceManager.closeOverlay() + player!!.interfaceManager.openOverlay(Component(Components.FADE_FROM_BLACK_170)) + player!!.achievementDiaryManager.finishTask(player, DiaryType.KARAMJA, 1, 3) + sendDialogue(player!!,"You feel tired from the journey, but at least you didn't have to walk all that distance.").also { stage = END_DIALOGUE } + } + } + return@queueScript stopExecuting(player!!) + } + } + stage = END_DIALOGUE; + } + } + } +} + +class CartTravelDialogue : DialogueFile(){ + override fun handle(componentID: Int, buttonID: Int) { + 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++ } + 1 -> if (inInventory(player!!,Items.COINS_995,10)) { + playerl("Yes please, I'd like to go to " + (if (shilo) "Shilo Village" else "Brimhaven") + ".").also { stage++ } + } else{ + playerl("Sorry, I don't seem to have enough coins.").also{ stage = END_DIALOGUE } + } + 2 -> npcl("Great! Just hop into the cart then and we'll go!").also { stage++ } + 3 -> sendDialogue(player!!,"You hop into the cart and the driver urges the horses on. You take a taxing journey through the jungle to " + (if (shilo) "Shilo Village" else "Brimhaven") + ".").also { stage++ } + 4 -> openDialogue(player!!,CartQuickPay(),npc!!) + } + } +} diff --git a/Server/src/main/content/region/karamja/shilo/handlers/ShiloVillagePlugin.java b/Server/src/main/content/region/karamja/shilo/handlers/ShiloVillagePlugin.java deleted file mode 100644 index afb1c65a4..000000000 --- a/Server/src/main/content/region/karamja/shilo/handlers/ShiloVillagePlugin.java +++ /dev/null @@ -1,149 +0,0 @@ -package content.region.karamja.shilo.handlers; - -import core.cache.def.impl.NPCDefinition; -import core.cache.def.impl.SceneryDefinition; -import core.plugin.Initializable; -import core.game.dialogue.DialoguePlugin; -import core.game.interaction.OptionHandler; -import core.game.node.Node; -import core.game.node.entity.npc.NPC; -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.world.map.Location; -import core.game.world.map.RegionManager; -import core.plugin.Plugin; -import core.plugin.ClassScanner; - -/** - * Handles shilo village interactions. - * @author Vexia - */ -@Initializable -public final class ShiloVillagePlugin extends OptionHandler { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - NPCDefinition.forId(511).getHandlers().put("option:pay-fare", this); - NPCDefinition.forId(510).getHandlers().put("option:pay-fare", this); - SceneryDefinition.forId(2230).getHandlers().put("option:board", this);// cart - // travel. - SceneryDefinition.forId(2230).getHandlers().put("option:pay-fare", this);// cart - // travel. - SceneryDefinition.forId(2265).getHandlers().put("option:board", this);// cart - // travel. - SceneryDefinition.forId(2265).getHandlers().put("option:pay-fare", this);// cart - // travel. - ClassScanner.definePlugin(new VillageCartDialogue()); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - switch (node.getId()) { - case 511: - case 510: - player.getDialogueInterpreter().open(510, node); - break; - case 2230: - case 2265: - player.getDialogueInterpreter().open(510, RegionManager.getNpc(player, node.getId() == 2230 ? 510 : 511)); - break; - } - return true; - } - - /** - * Handles the vigroy dialogue. - * @author Vexia - */ - public static final class VillageCartDialogue extends DialoguePlugin { - - /** - * If the ride is to shilo. - */ - private boolean shilo; - - /** - * Constructs a new {@Code VigroyDialogue} {@Code Object} - * @param player the player. - */ - public VillageCartDialogue(Player player) { - super(player); - } - - /** - * Constructs a new {@Code VigroyDialogue} {@Code Object} - */ - public VillageCartDialogue() { - /** - * empty. - */ - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new VillageCartDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - shilo = npc.getId() == 510; - npc("Hello Bwana!"); - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - npc("I am offering a cart ride to " + (shilo ? "Shilo Village" : "Brimhaven") + " if you're", "interested? It will cost 10 gold coins. Is that Ok?"); - stage++; - break; - case 1: - player("Yes please, I'd like to go to " + (shilo ? "Shilo Village" : "Brimhaven") + "."); - stage++; - break; - case 2: - npc("Great! Just hop into the cart then and we'll go!"); - stage++; - break; - case 3: - if (!player.getInventory().contains(995, 10)) { - player.sendMessage("Not enough coins."); - end(); - break; - } - interpreter.sendDialogue("You hop into the cart and the driver urges the horses on. You take", "a taxing journey through the jungle to " + (shilo ? "Shilo Village" : "Brimhaven") + "."); - stage++; - break; - case 4: - interpreter.sendDialogue("You pay the fare and hand 10 gold coins to " + npc.getName() + "."); - stage++; - break; - case 5: - if (player.getInventory().remove(new Item(995, 10))) { - interpreter.sendDialogue("You feel tired from the journey, but at least you didn't have to walk", "all that distance."); - stage++; - } else { - end(); - } - break; - case 6: - end(); - player.getProperties().setTeleportLocation(shilo ? Location.create(2834, 2951, 0) : Location.create(2780, 3212, 0)); - // Use Vigroy and Hajedy's cart service - player.getAchievementDiaryManager().finishTask(player, DiaryType.KARAMJA, 1, 3); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 511, 510 }; - } - - } -} diff --git a/Server/src/main/content/region/karamja/taibwo/dialogue/GabootyDialogue.kt b/Server/src/main/content/region/karamja/taibwo/dialogue/GabootyDialogue.kt index 1908b4c3f..a590492c6 100644 --- a/Server/src/main/content/region/karamja/taibwo/dialogue/GabootyDialogue.kt +++ b/Server/src/main/content/region/karamja/taibwo/dialogue/GabootyDialogue.kt @@ -12,24 +12,24 @@ import core.game.shops.Shops * @author qmqz */ @Initializable -class GabootyDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class GabootyDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - player(core.game.dialogue.FacialExpression.FRIENDLY, "What do you do here?").also { stage = 0 } + player(FacialExpression.FRIENDLY, "What do you do here?").also { stage = 0 } return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 0 -> npc(core.game.dialogue.FacialExpression.FRIENDLY,"Not much really... I run a local shop which earns", "me a few trading sticks, but that's about it.", "I keep myself to myself really.").also { stage++ } - 1 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "What are trading sticks?").also { stage++ } - 2 -> npc(core.game.dialogue.FacialExpression.FRIENDLY, + 0 -> npc(FacialExpression.FRIENDLY,"Not much really... I run a local shop which earns", "me a few trading sticks, but that's about it.", "I keep myself to myself really.").also { stage++ } + 1 -> player(FacialExpression.FRIENDLY, "What are trading sticks?").also { stage++ } + 2 -> npc(FacialExpression.FRIENDLY, "They're the local currency Bwana, ", "it's used in Tai Bwo Wannai, there's usually", "some odd jobs that need doing around the village", "which you could do to earn some trading sticks.").also { stage++ } - 3 -> npc(core.game.dialogue.FacialExpression.FRIENDLY, + 3 -> npc(FacialExpression.FRIENDLY, "Or, if you have something which the local villagers", "might like, you could sell it to me and I'll pay", "you for it in trading sticks.").also { stage++ } @@ -48,29 +48,29 @@ class GabootyDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugi 4 -> player("Ok, thanks.").also { stage = 99 } } - 10 -> npc(core.game.dialogue.FacialExpression.FRIENDLY, + 10 -> npc(FacialExpression.FRIENDLY, "Gnome cocktails! It's amazing but we all just love them!", "Luckily I've managed to get stocks of the gnome made", "cocktails and I supply those to my favourite customers.", "However, many of the customers really like the cocktails").also { stage++ } - 11 -> npc(core.game.dialogue.FacialExpression.FRIENDLY, + 11 -> npc(FacialExpression.FRIENDLY, "made by the adventurers passing through this way.", "If you ever happen to have any, bring them my way! ", "I'll give you a good deal on it that's for sure!").also { stage++ } - 12 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "How did the villagers ever get to try any gnome cocktails?").also { stage++ } - 13 -> npc(core.game.dialogue.FacialExpression.FRIENDLY, + 12 -> player(FacialExpression.FRIENDLY, "How did the villagers ever get to try any gnome cocktails?").also { stage++ } + 13 -> npc(FacialExpression.FRIENDLY, "I think it was that gnome pilot who crashed his glider,", "perhaps he had a little mini-bar on board?", "Come to think about it, you'd expect the little guy to get ", "his stuff together and move on out of here wouldn't you?").also { stage++ } - 14 -> npc(core.game.dialogue.FacialExpression.FRIENDLY, "He must love the jungle to have stayed here so long!").also { stage = 4 } + 14 -> npc(FacialExpression.FRIENDLY, "He must love the jungle to have stayed here so long!").also { stage = 4 } - 21 -> npc(core.game.dialogue.FacialExpression.FRIENDLY, + 21 -> npc(FacialExpression.FRIENDLY, "Yes, it's the Tai Bwo cooperative... catchy name huh!", "We sell a few local village trinkets and tools.", "Also, there are a few items that we're actually", "looking to stock for the locals' sake.").also { stage++ } - 22 -> npc(core.game.dialogue.FacialExpression.FRIENDLY, + 22 -> npc(FacialExpression.FRIENDLY, "If you happen across any of them please bring them to me,", "I'll pay a good price for them.", "I'm sure you'll find the prices very reasonable.").also { stage++ } @@ -90,7 +90,7 @@ class GabootyDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugi 5 -> player("I want to ask another question.").also { stage = 4 } } - 30 -> npc(core.game.dialogue.FacialExpression.FRIENDLY, "Sure you can...which shop would you like to see?", + 30 -> npc(FacialExpression.FRIENDLY, "Sure you can...which shop would you like to see?", "The Cooperative or Drinks store?").also { stage++ } 31 ->interpreter.sendOptions("Select an Option", @@ -105,13 +105,13 @@ class GabootyDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugi 3 -> player("None thanks...").also { stage = 99 } } - 40 -> npc(core.game.dialogue.FacialExpression.FRIENDLY, "It's funny that, actually. I've managed to get a ", + 40 -> npc(FacialExpression.FRIENDLY, "It's funny that, actually. I've managed to get a ", "good deal with the pilot of that gnome glider. He can supply", "me directly now. However, it's really interesting that the ", "local villagers really like the gnome cocktails made by the").also { stage++ } - 41 -> npc(core.game.dialogue.FacialExpression.FRIENDLY, "adventurers passing through this way.").also { stage++ } - 42 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "Why, what's the difference?").also { stage++ } - 43 -> npc(core.game.dialogue.FacialExpression.FRIENDLY, "I think it may just be that they're made fresher,", + 41 -> npc(FacialExpression.FRIENDLY, "adventurers passing through this way.").also { stage++ } + 42 -> player(FacialExpression.FRIENDLY, "Why, what's the difference?").also { stage++ } + 43 -> npc(FacialExpression.FRIENDLY, "I think it may just be that they're made fresher,", "or there is a slight twist in the flavour of the drink,", "you know, a little more of this, a little less of that, it all", "adds up and makes for an interesting tipple!").also { stage = 23 } @@ -124,7 +124,7 @@ class GabootyDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugi return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return GabootyDialogue(player) } diff --git a/Server/src/main/content/region/karamja/tzhaar/handlers/TzhaarCityPlugin.java b/Server/src/main/content/region/karamja/tzhaar/handlers/TzhaarCityPlugin.java index b4b23b029..669df926a 100644 --- a/Server/src/main/content/region/karamja/tzhaar/handlers/TzhaarCityPlugin.java +++ b/Server/src/main/content/region/karamja/tzhaar/handlers/TzhaarCityPlugin.java @@ -27,12 +27,11 @@ public final class TzhaarCityPlugin extends OptionHandler { @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(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); + SceneryDefinition.forId(31292).getHandlers().put("option:go-through", this); //unimplemented door near fairy ring new TzhaarDialogue().init(); return this; } @@ -65,6 +64,12 @@ public final class TzhaarCityPlugin extends OptionHandler { break; } break; + case "go-through": + switch (id) { + case 31292: + return false; + } + break; } return true; } 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 b6054f080..e1baca76a 100644 --- a/Server/src/main/content/region/karamja/tzhaar/handlers/TzhaarFightCavesPlugin.java +++ b/Server/src/main/content/region/karamja/tzhaar/handlers/TzhaarFightCavesPlugin.java @@ -148,6 +148,10 @@ public final class TzhaarFightCavesPlugin extends ActivityPlugin { activeNPCs.clear(); player.getProperties().setTeleportLocation(getSpawnLocation()); player.getSkills().restore(); + player.timers.removeTimer("poison"); + player.timers.removeTimer("disease"); + player.timers.removeTimer("teleblock"); + player.getSettings().updateRunEnergy(-100); boolean practice = player.getAttribute("fc_practice_jad", false); if (wave == 63) { if (!practice) { diff --git a/Server/src/main/content/region/karamja/tzhaar/handlers/TzhaarFightPitsPlugin.java b/Server/src/main/content/region/karamja/tzhaar/handlers/TzhaarFightPitsPlugin.java index 9abbe6e08..5070b76ef 100644 --- a/Server/src/main/content/region/karamja/tzhaar/handlers/TzhaarFightPitsPlugin.java +++ b/Server/src/main/content/region/karamja/tzhaar/handlers/TzhaarFightPitsPlugin.java @@ -29,9 +29,10 @@ import core.game.world.map.Location; import core.game.world.map.RegionManager; import core.game.world.map.zone.ZoneBorders; import core.game.world.map.zone.ZoneRestriction; -import core.game.world.update.flag.player.AppearanceFlag; import core.tools.RandomFunction; +import static core.api.ContentAPIKt.*; + /** * Handles the Tzhaar Fight pits. * @author Emperor @@ -140,7 +141,7 @@ public final class TzhaarFightPitsPlugin extends ActivityPlugin { lastVictor.getAchievementDiaryManager().finishTask(lastVictor, DiaryType.KARAMJA, 2, 0); addTokkul(lastVictor); lastVictor.getAppearance().setSkullIcon(SKULL_ID); - lastVictor.getUpdateMasks().register(new AppearanceFlag(lastVictor)); + lastVictor.updateAppearance(); lastVictor.getPacketDispatch().sendString("Current Champion: " + getChampionName(), INTERFACE_ID, 0); resetDamagePulse(lastVictor); } @@ -158,7 +159,7 @@ public final class TzhaarFightPitsPlugin extends ActivityPlugin { player.getInteraction().remove(Option._P_ATTACK); if (player.getAppearance().getSkullIcon() == SKULL_ID) { player.getAppearance().setSkullIcon(-1); - player.getUpdateMasks().register(new AppearanceFlag(player)); + player.updateAppearance(); } } } @@ -190,7 +191,7 @@ public final class TzhaarFightPitsPlugin extends ActivityPlugin { } if (lastVictor.getAppearance().getSkullIcon() == SKULL_ID) { player.getAppearance().setSkullIcon(-1); - player.getUpdateMasks().register(new AppearanceFlag(player)); + player.updateAppearance(); } } @@ -238,7 +239,7 @@ public final class TzhaarFightPitsPlugin extends ActivityPlugin { } if (e instanceof Player && (player = (Player) e).getAppearance().getSkullIcon() == SKULL_ID) { player.getAppearance().setSkullIcon(-1); - player.getUpdateMasks().register(new AppearanceFlag(player)); + player.updateAppearance(); } return super.leave(e, logout); } @@ -354,7 +355,7 @@ public final class TzhaarFightPitsPlugin extends ActivityPlugin { } int size = (LOBBY_PLAYERS.size() + WAR_PLAYERS.size()) - 1; if (!WAR_PLAYERS.isEmpty()) { - WAR_PLAYERS.get(0).getConfigManager().set(560, size); + setVarp(WAR_PLAYERS.get(0), 560, size); } for (Iterator it = LOBBY_PLAYERS.iterator(); it.hasNext();) { Player p = it.next(); @@ -366,7 +367,7 @@ public final class TzhaarFightPitsPlugin extends ActivityPlugin { } p.getSkullManager().setSkullCheckDisabled(true); p.getSkullManager().setWilderness(true); - p.getConfigManager().set(560, size); + setVarp(p, 560, size); p.getProperties().setTeleportLocation(getZoneDestination()); p.getInteraction().set(Option._P_ATTACK); tokkulAmount += p.getProperties().getCurrentCombatLevel(); @@ -448,7 +449,7 @@ public final class TzhaarFightPitsPlugin extends ActivityPlugin { */ public static void sendPlayersRemaining(int value) { for (Player p : WAR_PLAYERS) { - p.getConfigManager().set(560, value); + setVarp(p, 560, value); } } diff --git a/Server/src/main/content/region/misc/apeatoll/dialogue/bananaplantation/BonzaraDialogue.kt b/Server/src/main/content/region/misc/apeatoll/dialogue/bananaplantation/BonzaraDialogue.kt index e61eb228a..5eee207d0 100644 --- a/Server/src/main/content/region/misc/apeatoll/dialogue/bananaplantation/BonzaraDialogue.kt +++ b/Server/src/main/content/region/misc/apeatoll/dialogue/bananaplantation/BonzaraDialogue.kt @@ -12,11 +12,11 @@ import org.rs09.consts.NPCs */ @Initializable -class BonzaraDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class BonzaraDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - npcl(core.game.dialogue.FacialExpression.OLD_DEFAULT,"It looks like you're trying to escape. Would you like some help?").also { stage = 0 } + npcl(FacialExpression.OLD_DEFAULT,"It looks like you're trying to escape. Would you like some help?").also { stage = 0 } return true } @@ -24,27 +24,27 @@ class BonzaraDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugi when(stage){ 0 -> options("Yes", "No").also { stage++ } 1 -> when (buttonId) { - 1 -> player(core.game.dialogue.FacialExpression.WORRIED, "I ... uh ... yes.").also { stage = 10 } - 2 -> playerl(core.game.dialogue.FacialExpression.ASKING, "No thank you. Who are you by the way?").also { stage = 20 } + 1 -> player(FacialExpression.WORRIED, "I ... uh ... yes.").also { stage = 10 } + 2 -> playerl(FacialExpression.ASKING, "No thank you. Who are you by the way?").also { stage = 20 } } - 10 -> npc(core.game.dialogue.FacialExpression.OLD_DEFAULT, "Right you are.").also { stage++ } + 10 -> npc(FacialExpression.OLD_DEFAULT, "Right you are.").also { stage++ } 11 -> { end() //teleport to ape atoll } - 20 -> npcl(core.game.dialogue.FacialExpression.OLD_DEFAULT, "Never mind that child. You should worry more about who you are and the nature of the forces that have driven you here.").also { stage++ } - 21 -> player(core.game.dialogue.FacialExpression.THINKING, "I'll ... keep that in mind, thanks.").also { stage++ } - 22 -> npc(core.game.dialogue.FacialExpression.OLD_DEFAULT, "We WILL meet again, " + player.name + ".").also { stage++ } - 23 -> player(core.game.dialogue.FacialExpression.SUSPICIOUS, "Ok...").also { stage = 99 } + 20 -> npcl(FacialExpression.OLD_DEFAULT, "Never mind that child. You should worry more about who you are and the nature of the forces that have driven you here.").also { stage++ } + 21 -> player(FacialExpression.THINKING, "I'll ... keep that in mind, thanks.").also { stage++ } + 22 -> npc(FacialExpression.OLD_DEFAULT, "We WILL meet again, " + player.username + ".").also { stage++ } + 23 -> player(FacialExpression.SUSPICIOUS, "Ok...").also { stage = 99 } 99 -> end() } return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return BonzaraDialogue(player) } diff --git a/Server/src/main/content/region/misc/apeatoll/dialogue/dungeon/ZooknockDialogue.kt b/Server/src/main/content/region/misc/apeatoll/dialogue/dungeon/ZooknockDialogue.kt index 4c1ec3698..c84225bd8 100644 --- a/Server/src/main/content/region/misc/apeatoll/dialogue/dungeon/ZooknockDialogue.kt +++ b/Server/src/main/content/region/misc/apeatoll/dialogue/dungeon/ZooknockDialogue.kt @@ -13,15 +13,15 @@ import org.rs09.consts.NPCs */ @Initializable -class ZooknockDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class ZooknockDialogue(player: Player? = null) : DialoguePlugin(player){ - var fr = core.game.dialogue.FacialExpression.FRIENDLY - var neu = core.game.dialogue.FacialExpression.NEUTRAL - var ask = core.game.dialogue.FacialExpression.ASKING - var nor = core.game.dialogue.FacialExpression.OLD_NORMAL - var ntalk1 = core.game.dialogue.FacialExpression.OLD_CALM_TALK1 - var ntalk2 = core.game.dialogue.FacialExpression.OLD_CALM_TALK2 + var fr = FacialExpression.FRIENDLY + var neu = FacialExpression.NEUTRAL + var ask = FacialExpression.ASKING + var nor = FacialExpression.OLD_NORMAL + var ntalk1 = FacialExpression.OLD_CALM_TALK1 + var ntalk2 = FacialExpression.OLD_CALM_TALK2 var hasSpokenToZook = false @@ -52,7 +52,7 @@ class ZooknockDialogue(player: Player? = null) : core.game.dialogue.DialoguePlug 12 -> playerl(neu, "No, I have been accompanied by Flight Commander Waydar. We flew south on a special type of glider and landed on a small island to our east.").also { stage++ } 13 -> npcl(ntalk2, "The so called Crash Island. We left there one of our number, Lumdo, to guard our gliders until our return.").also { stage++ } 14 -> player(neu, "Yes, we have met. He ferried me across to the atoll.").also { stage++ } - 15 -> npcl(core.game.dialogue.FacialExpression.OLD_ANGRY1, "He did!? He was explicitly ordered to guard the gliders! How did this happen? Who is guarding the gliders now?").also { stage++ } + 15 -> npcl(FacialExpression.OLD_ANGRY1, "He did!? He was explicitly ordered to guard the gliders! How did this happen? Who is guarding the gliders now?").also { stage++ } 16 -> playerl(ntalk1, "Waydar ordered him to leave his post. He is guarding the gliders himself.").also { stage++ } 17 -> npcl(ntalk2, "Flight Commander Waydar you said? For some reason that name is familiar...").also { stage++ } 18 -> player(ask, "So why are you here?").also { stage++ } @@ -61,7 +61,7 @@ class ZooknockDialogue(player: Player? = null) : core.game.dialogue.DialoguePlug 21 -> npc(ntalk1, "Correct. I assume Lumdo told you this?").also { stage++ } 22 -> player(ask, "Yes. What happened when you landed here?").also { stage++ } 23 -> npcl(ntalk2, "We split up into several small groups to search the island for potential gnome glider launch sites. Whilst we knew the island to be inhabited, we did not expect its occupants to be quite so ... militant.").also { stage++ } - 24 -> player(core.game.dialogue.FacialExpression.THINKING, "...").also { stage++ } + 24 -> player(FacialExpression.THINKING, "...").also { stage++ } 25 -> npcl(ntalk1, "Monkeys. Lots of monkeys. They are unlike any other type of monkey we've come across. A far cry from the usual wild variety, these were armed with high quality weaponry and uncanny tactical ability.").also { stage++ } 26 -> npcl(ntalk2, "We were overwhelmed in numbers. Some of us managed to escape, but the rest were taken captive.").also { stage++ } 27 -> player(ask, "Who survived?").also { stage++ } @@ -135,7 +135,7 @@ class ZooknockDialogue(player: Player? = null) : core.game.dialogue.DialoguePlug return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return ZooknockDialogue(player) } diff --git a/Server/src/main/content/region/misc/apeatoll/dialogue/marim/AberabDialogue.kt b/Server/src/main/content/region/misc/apeatoll/dialogue/marim/AberabDialogue.kt index 8eee92aa5..5d84c1aae 100644 --- a/Server/src/main/content/region/misc/apeatoll/dialogue/marim/AberabDialogue.kt +++ b/Server/src/main/content/region/misc/apeatoll/dialogue/marim/AberabDialogue.kt @@ -12,11 +12,11 @@ import core.plugin.Initializable */ @Initializable -class AberabDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class AberabDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - npc(core.game.dialogue.FacialExpression.OLD_ANGRY1,"Grr ... Get out of my way...") + npc(FacialExpression.OLD_ANGRY1,"Grr ... Get out of my way...") stage = 99 return true } @@ -28,7 +28,7 @@ class AberabDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return AberabDialogue(player) } diff --git a/Server/src/main/content/region/misc/apeatoll/dialogue/marim/DagaDialogue.kt b/Server/src/main/content/region/misc/apeatoll/dialogue/marim/DagaDialogue.kt index 572138fcb..5be5285bf 100644 --- a/Server/src/main/content/region/misc/apeatoll/dialogue/marim/DagaDialogue.kt +++ b/Server/src/main/content/region/misc/apeatoll/dialogue/marim/DagaDialogue.kt @@ -17,11 +17,11 @@ import core.tools.END_DIALOGUE */ @Initializable -class DagaDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class DagaDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - npc(core.game.dialogue.FacialExpression.OLD_DEFAULT,"Would you like to buy or sell some scimitars?").also { stage = 0 } + npc(FacialExpression.OLD_DEFAULT,"Would you like to buy or sell some scimitars?").also { stage = 0 } return true } @@ -30,29 +30,29 @@ class DagaDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(p 0 -> options("Yes, please.", "No, thanks.", "Do you have any Dragon Scimitars in stock?").also { stage++ } 1 -> when (buttonId) { - 1 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "Yes, please.").also { stage = 10 } - 2 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "No, thanks.").also { stage = END_DIALOGUE } - 3 -> player(core.game.dialogue.FacialExpression.HALF_ASKING, "Do you have any Dragon Scimitars in stock?").also { stage = 30 } + 1 -> player(FacialExpression.FRIENDLY, "Yes, please.").also { stage = 10 } + 2 -> player(FacialExpression.FRIENDLY, "No, thanks.").also { stage = END_DIALOGUE } + 3 -> player(FacialExpression.HALF_ASKING, "Do you have any Dragon Scimitars in stock?").also { stage = 30 } } 10 -> end().also { npc.openShop(player) } - 30 -> npcl(core.game.dialogue.FacialExpression.OLD_DEFAULT, "It just so happens I recently got a fresh delivery, do you want to buy one?").also { stage++ } + 30 -> npcl(FacialExpression.OLD_DEFAULT, "It just so happens I recently got a fresh delivery, do you want to buy one?").also { stage++ } 31 -> options("Yes, please.", "No, thanks.").also { stage++ } 32 -> when (buttonId) { 1 -> if (inInventory(player, Items.COINS_995, 100000)) { end().also { return removeItem(player, Item(Items.COINS_995, 100000)) && addItem(player, Items.DRAGON_SCIMITAR_4587, 1) } } else { - npcl(core.game.dialogue.FacialExpression.OLD_NORMAL, "Sorry but you don't have enough to buy one, at the moment it costs 100,000 gold coins.").also { stage = END_DIALOGUE } + npcl(FacialExpression.OLD_NORMAL, "Sorry but you don't have enough to buy one, at the moment it costs 100,000 gold coins.").also { stage = END_DIALOGUE } } - 2 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "No thanks.").also { stage = END_DIALOGUE } + 2 -> player(FacialExpression.FRIENDLY, "No thanks.").also { stage = END_DIALOGUE } } } return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return DagaDialogue(player) } diff --git a/Server/src/main/content/region/misc/apeatoll/dialogue/marim/ElderGuardDialogue.kt b/Server/src/main/content/region/misc/apeatoll/dialogue/marim/ElderGuardDialogue.kt index 85a38feb8..6902cbaa6 100644 --- a/Server/src/main/content/region/misc/apeatoll/dialogue/marim/ElderGuardDialogue.kt +++ b/Server/src/main/content/region/misc/apeatoll/dialogue/marim/ElderGuardDialogue.kt @@ -13,7 +13,7 @@ import org.rs09.consts.NPCs */ @Initializable -class ElderGuardDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class ElderGuardDialogue(player: Player? = null) : DialoguePlugin(player){ val ids = 4025..4031 @@ -27,11 +27,11 @@ class ElderGuardDialogue(player: Player? = null) : core.game.dialogue.DialoguePl if (player.equipment.containsAtLeastOneItem(ids.toIntArray())) { if (outside) { - npc(core.game.dialogue.FacialExpression.OLD_ANGRY1, "Grrr ... What do you want?").also { stage = 10 } + npc(FacialExpression.OLD_ANGRY1, "Grrr ... What do you want?").also { stage = 10 } } else if(cornerGuard){ - npc(core.game.dialogue.FacialExpression.OLD_ANGRY1, "Grrr ... What do you want?").also { stage = 20 } + npc(FacialExpression.OLD_ANGRY1, "Grrr ... What do you want?").also { stage = 20 } } else { - npc(core.game.dialogue.FacialExpression.OLD_ANGRY1, "Move!").also { stage = 99 } + npc(FacialExpression.OLD_ANGRY1, "Move!").also { stage = 99 } } } else { //todo monke is gonna knock you out if you don't have a monkey gree gree thang, chicken wang @@ -42,18 +42,18 @@ class ElderGuardDialogue(player: Player? = null) : core.game.dialogue.DialoguePl override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 10 -> playerl(core.game.dialogue.FacialExpression.ASKING, "I must speak with Awowogei on a subject of great import.").also { stage ++ } - 11 -> npc(core.game.dialogue.FacialExpression.OLD_NORMAL, "As you wish.").also { stage = 99 } + 10 -> playerl(FacialExpression.ASKING, "I must speak with Awowogei on a subject of great import.").also { stage ++ } + 11 -> npc(FacialExpression.OLD_NORMAL, "As you wish.").also { stage = 99 } - 20 -> player(core.game.dialogue.FacialExpression.ASKING, "I would like to leave now.").also { stage ++ } - 21 -> npc(core.game.dialogue.FacialExpression.OLD_NORMAL, "As you wish.").also { stage = 99 } + 20 -> player(FacialExpression.ASKING, "I would like to leave now.").also { stage ++ } + 21 -> npc(FacialExpression.OLD_NORMAL, "As you wish.").also { stage = 99 } 99 -> end() } return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return ElderGuardDialogue(player) } diff --git a/Server/src/main/content/region/misc/apeatoll/dialogue/marim/MuruwoiDialogue.kt b/Server/src/main/content/region/misc/apeatoll/dialogue/marim/MuruwoiDialogue.kt index c9f5e9f57..fd621df95 100644 --- a/Server/src/main/content/region/misc/apeatoll/dialogue/marim/MuruwoiDialogue.kt +++ b/Server/src/main/content/region/misc/apeatoll/dialogue/marim/MuruwoiDialogue.kt @@ -13,11 +13,11 @@ import org.rs09.consts.NPCs */ @Initializable -class MuruwoiDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class MuruwoiDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - npc(core.game.dialogue.FacialExpression.OLD_ANGRY1,"Grr ... Get out of my way...") + npc(FacialExpression.OLD_ANGRY1,"Grr ... Get out of my way...") stage = 99 return true } @@ -29,7 +29,7 @@ class MuruwoiDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugi return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return MuruwoiDialogue(player) } 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/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 bee5903bd..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,68 +1,72 @@ 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) : core.game.dialogue.DialoguePlugin(player){ - - override fun open(vararg args: Any?): Boolean { - npc = args[0] as NPC - - if (!player.questRepository.isComplete("Heroes' Quest")) { - npc(core.game.dialogue.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 - } +class HighPriestEntranaDialogue(player: Player? = null) : DialoguePlugin(player){ override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 1 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "and devoted the island to those who wish peace for the world.").also { stage++ } - 2 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "The inhabitants of this island are mostly monks who spend their time meditating on Saradomin's ways.").also { stage++ } - 3 -> npcl(core.game.dialogue.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(core.game.dialogue.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(core.game.dialogue.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(core.game.dialogue.FacialExpression.FRIENDLY, "Enjoy our stay here. May it be spiritually uplifting!").also { stage = 99 } - - 20 -> player(core.game.dialogue.FacialExpression.SUSPICIOUS, "Er...").also { stage++ } - 21 -> npcl(core.game.dialogue.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(core.game.dialogue.FacialExpression.ANNOYED, "The ice gloves that, from context, I would assume you have lost, if not carelessly discarded?").also { stage++ } - - 23 -> player(core.game.dialogue.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(core.game.dialogue.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 } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return HighPriestEntranaDialogue(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/dialogue/MazionDialogue.kt b/Server/src/main/content/region/misc/entrana/dialogue/MazionDialogue.kt index 6c084daae..60368b2cc 100644 --- a/Server/src/main/content/region/misc/entrana/dialogue/MazionDialogue.kt +++ b/Server/src/main/content/region/misc/entrana/dialogue/MazionDialogue.kt @@ -12,14 +12,14 @@ import org.rs09.consts.NPCs */ @Initializable -class MazionDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class MazionDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC when ((1..3).random()) { - 1 -> npc(core.game.dialogue.FacialExpression.FRIENDLY, "Nice weather we're having today!").also { stage = 99 } - 2 -> npc(core.game.dialogue.FacialExpression.FRIENDLY, "Hello " + player.name + ", fine day today!").also { stage = 99 } - 3 -> npc(core.game.dialogue.FacialExpression.ANNOYED, "Please leave me alone, a parrot stole my banana.").also { stage = 99 } + 1 -> npc(FacialExpression.FRIENDLY, "Nice weather we're having today!").also { stage = 99 } + 2 -> npc(FacialExpression.FRIENDLY, "Hello, " + player.username + ", fine day today!").also { stage = 99 } + 3 -> npc(FacialExpression.ANNOYED, "Please leave me alone, a parrot stole my banana.").also { stage = 99 } } return true } @@ -31,7 +31,7 @@ class MazionDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return MazionDialogue(player) } 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/etceteria/dialogue/FishmongerEtcDialogue.kt b/Server/src/main/content/region/misc/etceteria/dialogue/FishmongerEtcDialogue.kt index 297e80eef..39f349294 100644 --- a/Server/src/main/content/region/misc/etceteria/dialogue/FishmongerEtcDialogue.kt +++ b/Server/src/main/content/region/misc/etceteria/dialogue/FishmongerEtcDialogue.kt @@ -12,11 +12,11 @@ import org.rs09.consts.NPCs */ @Initializable -class FishmongerEtcDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class FishmongerEtcDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - npcl(core.game.dialogue.FacialExpression.FRIENDLY,"Welcome, ${player.getAttribute("fremennikname","fremmyname")}. My fish is fresher than any in Miscellania.").also { stage = 0 } + npcl(FacialExpression.FRIENDLY,"Welcome, ${player.getAttribute("fremennikname","fremmyname")}. My fish is fresher than any in Miscellania.").also { stage = 0 } return true } @@ -27,7 +27,7 @@ class FishmongerEtcDialogue(player: Player? = null) : core.game.dialogue.Dialogu return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return FishmongerEtcDialogue(player) } diff --git a/Server/src/main/content/region/misc/keldagrim/dialogue/AgmundiDialogue.kt b/Server/src/main/content/region/misc/keldagrim/dialogue/AgmundiDialogue.kt index 6e097d97d..e67ea194b 100644 --- a/Server/src/main/content/region/misc/keldagrim/dialogue/AgmundiDialogue.kt +++ b/Server/src/main/content/region/misc/keldagrim/dialogue/AgmundiDialogue.kt @@ -1,67 +1,47 @@ package content.region.misc.keldagrim.dialogue +import core.api.openNpcShop import core.game.dialogue.DialoguePlugin import core.game.dialogue.FacialExpression -import core.game.node.entity.npc.NPC +import core.game.dialogue.Topic import core.game.node.entity.player.Player import core.plugin.Initializable - -/** - * @author qmqz - */ +import core.tools.END_DIALOGUE +import org.rs09.consts.NPCs @Initializable -class AgmundiDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ - - override fun open(vararg args: Any?): Boolean { - npc = args[0] as NPC - npc(core.game.dialogue.FacialExpression.CHILD_NORMAL,"Oh no, not another human... what do you want then?") - stage = 0 - return true - } - +class AgmundiDialogue(player: Player? = null) : DialoguePlugin(player) { override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 0 -> player(core.game.dialogue.FacialExpression.ASKING, "Oh, do you get humans here often?").also { stage++ } - 1 -> npc(core.game.dialogue.FacialExpression.OLD_NORMAL, "Not that often, no, but sometimes.").also { stage++ } - 2 -> npc(core.game.dialogue.FacialExpression.OLD_NORMAL, "Of course, since you people are too big for dwarven", - "clothes, they typically don't stay very long.").also { stage++ } - 3 -> player(core.game.dialogue.FacialExpression.SUSPICIOUS, "Why don't you make bigger clothes then?").also { stage++ } - 4 -> npc(core.game.dialogue.FacialExpression.OLD_NOT_INTERESTED, "What'd be the point? Besides, I don't make", "these clothes myself.").also { stage++ } - - 5 -> options ("Who makes these clothes then?", "I still want to buy your clothes.", "So do you have any quests for me?").also { stage++ } - 6 -> when(buttonId){ - 1 -> player(core.game.dialogue.FacialExpression.GUILTY, "Who makes the clothes then?").also { stage = 10 } - 2 -> npc.openShop(player) - 3 -> npc(core.game.dialogue.FacialExpression.OLD_NOT_INTERESTED,"Quests? Why would I have any quests?").also { stage = 20 } - - } - - 10 -> npc(core.game.dialogue.FacialExpression.OLD_DEFAULT,"Oh, my sister, she lives in Keldagrim-East.", - "Has a little stall on the other side of", - "the Kelda.").also { stage++ } - 11 -> npc(core.game.dialogue.FacialExpression.OLD_DEFAULT,"If she only worked a little harder, like me,", - "she wouldn't have to live in the sewers of the city.", - "Shame really.").also { stage++ } - 12 -> player("The sewers? Your sister lives in the sewers?").also { stage++ } - 13 -> npc(core.game.dialogue.FacialExpression.OLD_SAD,"Keldagrim-East, such a ghastly place.", - "Not civil, polite and clean like we are in the West.").also { stage++ } - 14 -> player(core.game.dialogue.FacialExpression.SUSPICIOUS, "Uh-huh.").also { stage=99 } - - 20 -> player("Oh, just anything to do would be fine.").also { stage++ } - 21 -> npc(core.game.dialogue.FacialExpression.OLD_NOT_INTERESTED,"No, not right now... maybe I'll have something", - "for you to do later, but nothing at the moment.").also { stage = 99 } - - 99 -> end() + 0 -> npc(FacialExpression.OLD_NOT_INTERESTED,"Oh no, not another human... what do you want then?").also { stage++ } + 1 -> player(FacialExpression.ASKING, "Oh, do you get humans here often?").also { stage++ } + 2 -> npc(FacialExpression.OLD_NORMAL, "Not that often, no, but sometimes.").also { stage++ } + 3 -> npc(FacialExpression.OLD_NORMAL, "Of course, since you people are too big for dwarven", "clothes, they typically don't stay very long.").also { stage++ } + 4 -> player(FacialExpression.SUSPICIOUS, "Why don't you make bigger clothes then?").also { stage++ } + 5 -> npc(FacialExpression.OLD_NOT_INTERESTED, "What'd be the point? Besides, I don't make", "these clothes myself.").also { stage++ } + 6 -> showTopics( + Topic(FacialExpression.FRIENDLY, "Who makes these clothes then?", 10), + Topic(FacialExpression.FRIENDLY, "I still want to buy your clothes.", 8, true), + Topic(FacialExpression.FRIENDLY, "So do you have any quests for me?", 20), + ) + 8 -> openNpcShop(player, NPCs.AGMUNDI_2161).also { stage = END_DIALOGUE } + 10 -> npc(FacialExpression.OLD_DEFAULT,"Oh, my sister, she lives in Keldagrim-East.", "Has a little stall on the other side of", "the Kelda.").also { stage++ } + 11 -> npc(FacialExpression.OLD_DEFAULT,"If she only worked a little harder, like me,", "she wouldn't have to live in the sewers of the city.", "Shame really.").also { stage++ } + 12 -> player(FacialExpression.FRIENDLY, "The sewers? Your sister lives in the sewers?").also { stage++ } + 13 -> npc(FacialExpression.OLD_SAD,"Keldagrim-East, such a ghastly place.", "Not civil, polite and clean like we are in the West.").also { stage++ } + 14 -> player(FacialExpression.SUSPICIOUS, "Uh-huh.").also { stage = END_DIALOGUE } + 20 -> npc(FacialExpression.OLD_NOT_INTERESTED,"Quests? Why would I have any quests?").also { stage++ } + 21 -> player(FacialExpression.FRIENDLY, "Oh, just anything to do would be fine.").also { stage++ } + 22 -> npc(FacialExpression.OLD_NOT_INTERESTED,"No, not right now... maybe I'll have something", "for you to do later, but nothing at the moment.").also { stage = END_DIALOGUE } } return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return AgmundiDialogue(player) } override fun getIds(): IntArray { - return intArrayOf(2161) + return intArrayOf(NPCs.AGMUNDI_2161) } } \ No newline at end of file diff --git a/Server/src/main/content/region/misc/keldagrim/dialogue/BarmaidDialogue.kt b/Server/src/main/content/region/misc/keldagrim/dialogue/BarmaidDialogue.kt new file mode 100644 index 000000000..1b23ffe61 --- /dev/null +++ b/Server/src/main/content/region/misc/keldagrim/dialogue/BarmaidDialogue.kt @@ -0,0 +1,81 @@ +package content.region.misc.keldagrim.dialogue + +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 + +@Initializable +class BarmaidDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + // TODO: Forgettable Tale Dialogue + when(stage){ + START_DIALOGUE -> npcl(FacialExpression.OLD_DEFAULT, "Welcome to the Laughing Miner pub, human traveller.").also { stage++ } + 1 -> showTopics( + Topic(FacialExpression.FRIENDLY, "Who is that man walking around outside?", 2), + Topic(FacialExpression.FRIENDLY, "I'd like a beer please.", 12), + Topic(FacialExpression.FRIENDLY, "I'd like some food please.", 16), + ) + 2 -> npcl(FacialExpression.OLD_DEFAULT, "What man?").also { stage++ } + 3 -> playerl(FacialExpression.FRIENDLY, "I mean the dwarf, the one with the sign.").also { stage++ } + 4 -> npcl(FacialExpression.OLD_DEFAULT, "Oh, him. Yes, we employ him to advertise our pub, he's the cheapest labour we could find. We don't have a lot of money to spare you know, we pay him in beer.").also { stage++ } + 5 -> playerl(FacialExpression.FRIENDLY, "But what's wrong with him?").also { stage++ } + 6 -> npcl(FacialExpression.OLD_DEFAULT, "Well, he's drunk isn't he?").also { stage++ } + 7 -> npcl(FacialExpression.OLD_DEFAULT, "I told you, he's cheap. He couldn't get any other work since the Red Axe fired him. Been drinking ever since.").also { stage++ } + 8 -> playerl(FacialExpression.FRIENDLY, "How did that happen?").also { stage++ } + 9 -> npcl(FacialExpression.OLD_DEFAULT, "I'm not quite sure, I think he kept wearing the wrong coloured cap all the time. They don't much like it if you don't wear your red uniform into work.").also { stage++ } + 10 -> playerl(FacialExpression.FRIENDLY, "They fired him for that??").also { stage++ } + 11 -> npcl(FacialExpression.OLD_DEFAULT, "The Red Axe will fire you for just about anything if they want to.").also { + stage = END_DIALOGUE + } + 12 -> npcl(FacialExpression.OLD_DEFAULT, "That'll be 2 gold coins.").also { stage++ } + 13 -> showTopics( + Topic(FacialExpression.FRIENDLY, "Pay.", 14, true), + Topic(FacialExpression.FRIENDLY, "Don't pay.", 15,true), + ) + 14 -> { + if (!inInventory(player, Items.COINS_995, 2)) { + playerl(FacialExpression.FRIENDLY, "Sorry, I don't have 2 coins on me.").also { stage = END_DIALOGUE } + } else { + if (removeItem(player, Item(Items.COINS_995, 2))) { + addItemOrDrop(player, Items.BEER_1917) + npcl(FacialExpression.OLD_DEFAULT, "Thanks for your custom.").also { stage = END_DIALOGUE } + } + } + } + 15 -> playerl(FacialExpression.FRIENDLY, "Sorry, I changed my mind.").also { stage = END_DIALOGUE } + 16 -> npcl(FacialExpression.OLD_DEFAULT, "I can make you a stew for 20 gold coins.").also { stage++ } + 17 -> showTopics( + Topic(FacialExpression.FRIENDLY, "Pay.", 18, true), + Topic(FacialExpression.FRIENDLY, "Don't pay.", 19,true), + ) + 18 -> { + if (!inInventory(player, Items.COINS_995, 20)) { + playerl(FacialExpression.FRIENDLY, "Sorry, I don't have 20 coins on me.").also { stage = END_DIALOGUE } + } else { + if (removeItem(player, Item(Items.COINS_995, 20))) { + addItemOrDrop(player, Items.STEW_2003) + npcl(FacialExpression.OLD_DEFAULT, "Thanks for your custom.").also { stage = END_DIALOGUE } + } + } + } + 19 -> playerl(FacialExpression.FRIENDLY, "Sorry, I changed my mind.").also { stage = END_DIALOGUE } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return BarmaidDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.BARMAID_2178) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misc/keldagrim/dialogue/BentamirDialogue.kt b/Server/src/main/content/region/misc/keldagrim/dialogue/BentamirDialogue.kt new file mode 100644 index 000000000..5396162e6 --- /dev/null +++ b/Server/src/main/content/region/misc/keldagrim/dialogue/BentamirDialogue.kt @@ -0,0 +1,36 @@ +package content.region.misc.keldagrim.dialogue + +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 + +@Initializable +class BentamirDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + START_DIALOGUE -> npcl(FacialExpression.OLD_ANGRY1, "Do you mind? You're in my home.").also { stage++ } + 1 -> playerl(FacialExpression.FRIENDLY, "I'm sorry, should I have knocked?").also { stage++ } + 2 -> npcl(FacialExpression.OLD_ANGRY1, "Very funny, human.").also { stage++ } + 3 -> npcl(FacialExpression.OLD_ANGRY1, "We can't all live in plush houses, you know. But that doesn't mean us mining dwarves don't work hard.").also { stage++ } + 4 -> playerl(FacialExpression.FRIENDLY, "Where do you do your mining?").also { stage++ } + 5 -> npcl(FacialExpression.OLD_NOT_INTERESTED, "Normally the coal mine to the north. We need a lot of coal to keep our steam engines going, you know.").also { stage++ } + 6 -> playerl(FacialExpression.FRIENDLY, "Can I do a bit of mining there as well?").also { stage++ } + 7 -> npcl(FacialExpression.OLD_NOT_INTERESTED, "I'm not sure, but as long as no one notices I don't think anyone is going to care.").also { + stage = END_DIALOGUE + } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return BentamirDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.BENTAMIR_2192) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misc/keldagrim/dialogue/BlackGuardDialogue.kt b/Server/src/main/content/region/misc/keldagrim/dialogue/BlackGuardDialogue.kt index d56b17611..22d229fc6 100644 --- a/Server/src/main/content/region/misc/keldagrim/dialogue/BlackGuardDialogue.kt +++ b/Server/src/main/content/region/misc/keldagrim/dialogue/BlackGuardDialogue.kt @@ -1,36 +1,37 @@ package content.region.misc.keldagrim.dialogue import core.game.dialogue.DialoguePlugin -import core.game.node.entity.npc.NPC +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 -/** - * @author qmqz - */ - @Initializable -class BlackGuardDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ - - override fun open(vararg args: Any?): Boolean { - npc = args[0] as NPC - sendDialogue("The guard ignores you.").also { stage = 99 } - return true - } +class BlackGuardDialogue(player: Player? = null) : DialoguePlugin(player){ override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 99 -> end() + START_DIALOGUE -> { + var random = arrayOf(1, 2, 3, 4, 5).random() + when (random) { + 1 -> npcl(FacialExpression.OLD_ANGRY3, "Obey the law!").also { stage = END_DIALOGUE } + 2 -> npcl(FacialExpression.OLD_ANGRY3, "Stay out of trouble!").also { stage = END_DIALOGUE } + 3 -> npcl(FacialExpression.OLD_ANGRY3, "Out of the way, human!").also { stage = END_DIALOGUE } + 4 -> npcl(FacialExpression.OLD_ANGRY3, "I'm keeping an eye on you!").also { stage = END_DIALOGUE } + 5 -> end().also{ sendDialogue("The guard ignores you.").also { stage = END_DIALOGUE } } + } + } } return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return BlackGuardDialogue(player) } override fun getIds(): IntArray { - return intArrayOf(NPCs.BLACK_GUARD_2130, NPCs.BLACK_GUARD_2131, NPCs.BLACK_GUARD_2132, NPCs.BLACK_GUARD_2133) + return intArrayOf(NPCs.BLACK_GUARD_2130, NPCs.BLACK_GUARD_2131, NPCs.BLACK_GUARD_2132, NPCs.BLACK_GUARD_2133, NPCs.BLACK_GUARD_BERSERKER_2134, NPCs.BLACK_GUARD_BERSERKER_2135, NPCs.BLACK_GUARD_BERSERKER_2136) } } diff --git a/Server/src/main/content/region/misc/keldagrim/dialogue/BlandebirDialogue.kt b/Server/src/main/content/region/misc/keldagrim/dialogue/BlandebirDialogue.kt new file mode 100644 index 000000000..974f22438 --- /dev/null +++ b/Server/src/main/content/region/misc/keldagrim/dialogue/BlandebirDialogue.kt @@ -0,0 +1,51 @@ +package content.region.misc.keldagrim.dialogue + +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.dialogue.Topic +import core.game.node.entity.player.Player +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import core.tools.START_DIALOGUE +import org.rs09.consts.NPCs + +@Initializable +class BlandebirDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + START_DIALOGUE -> playerl(FacialExpression.FRIENDLY, "Hello, I wonder if you could help me on this whole brewing thing...").also { stage++ } + 1 -> npcl(FacialExpression.OLD_DEFAULT, "I might be able to - what do you need to know?").also { stage++ } + 2 -> showTopics( + Topic(FacialExpression.FRIENDLY, "How do I brew ales?", 3), + Topic(FacialExpression.FRIENDLY, "How do I brew cider?", 6), + Topic(FacialExpression.FRIENDLY, "What do I do once my ale has matured?", 8), + Topic(FacialExpression.FRIENDLY, "Do you have any spare ale yeast?", 9), + Topic(FacialExpression.FRIENDLY, "That's all I need to know, thanks.", END_DIALOGUE), + ) + 3 -> npcl(FacialExpression.OLD_DEFAULT, "Well first off you need to fill the vat with water - two bucketfuls should do the trick. Then you'll need to put in two handfuls of barley malt - that's roasted barley by the way.").also { stage++ } + 4 -> npcl(FacialExpression.OLD_DEFAULT, "After that you'll be putting your main ingredient in - this will decide which ale it is you're brewing. There should be some good guides around with recipes in.").also { stage++ } + 5 -> npcl(FacialExpression.OLD_DEFAULT, "Lastly you pour a pot full of ale yeast in, which'll start it off fermenting. Then all you have to do is wait for the good stuff.").also { + stage = 2 + } + 6 -> npcl(FacialExpression.OLD_DEFAULT, "First you'll need some apples. You'll need to crush them using this cider-press - four apples should make a full bucket of apple-mush. Take four buckets of mush and fill up the vat.").also { stage++ } + 7 -> npcl(FacialExpression.OLD_DEFAULT, "After that you pour a pot full of ale yeast in, which'll start it off fermenting. Then all you have to do is wait for the good stuff.").also { + stage = 2 + } + 8 -> npcl(FacialExpression.OLD_DEFAULT, "Well, once you've got a full vat of the good stuff, just turn the valve and your barrel will fill up with eight pints of whatever your chosen tipple is. Mind it's an empty barrel, though.").also { + stage = 2 + } + 9 -> npcl(FacialExpression.OLD_DEFAULT, "Well, as a matter of fact I do, although I wouldn't describe it as spare. This ale yeast I've got is the best money can buy, but if you've got a pot I'll fill it for you for 25GP - very cheap as it happens.").also { + stage = 2 + } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return BlandebirDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.BLANDEBIR_2321) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misc/keldagrim/dialogue/BlastFurnaceForemanDialogue.kt b/Server/src/main/content/region/misc/keldagrim/dialogue/BlastFurnaceForemanDialogue.kt new file mode 100644 index 000000000..a6ed126a8 --- /dev/null +++ b/Server/src/main/content/region/misc/keldagrim/dialogue/BlastFurnaceForemanDialogue.kt @@ -0,0 +1,180 @@ +package content.region.misc.keldagrim.dialogue + +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.system.task.Pulse +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import core.tools.START_DIALOGUE +import org.rs09.consts.Components +import org.rs09.consts.NPCs + +@Initializable +class BlastFurnaceForemanDialogue (player: Player? = null) : DialoguePlugin(player) { + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + START_DIALOGUE -> npcl(FacialExpression.OLD_ANGRY1, "You! Get to work!").also { stage++ } + 1 -> showTopics( + Topic(FacialExpression.FRIENDLY, "What?", 2), + Topic(FacialExpression.FRIENDLY, "Okay.", END_DIALOGUE), + ) + 2 -> npcl(FacialExpression.OLD_DEFAULT, "You are here to help work the blast furnace, aren't you?").also { stage++ } + 3 -> showTopics( + Topic(FacialExpression.FRIENDLY, "What's the blast furnace?", 5), + Topic(FacialExpression.FRIENDLY, "How can I help work the blast furnace?", 10), + Topic(FacialExpression.FRIENDLY, "Can I use the furnace to smelt ore?", 40), + Topic(FacialExpression.FRIENDLY, "Maybe some other time.", 102), + ) + + 5 -> npcl(FacialExpression.OLD_DEFAULT, "The blast furnace is the pinnacle of dwarven metal- processing! Ore goes in the top and comes out as metal bars almost at once! And it's so efficient it only takes half as much coal to purify it as a regular furnace.").also { stage++ } + 6 -> npcl(FacialExpression.OLD_DEFAULT, "But we've got a bit of a labour shortage at the moment, which is why I have to rely on immigrant workers to keep the furnace going. So, are you here to start work?").also { stage++ } + 7 -> showTopics( + Topic(FacialExpression.FRIENDLY, "How can I help work the blast furnace?", 10), + Topic(FacialExpression.FRIENDLY, "Can I use the furnace to smelt ore?", 40), + Topic(FacialExpression.FRIENDLY, "Maybe some other time.", 102), + ) + + 10 -> npcl(FacialExpression.OLD_DEFAULT, "The blast furnace will only work if there is a group of people keeping it going. Let me explain...").also { stage++ } + 11 -> { + openInterface(player, Components.BLAST_FURNACE_PLAN_SCROLL_29) + submitIndividualPulse(player, object : Pulse() { + var flash = true + override fun pulse(): Boolean { + setComponentVisibility(player, Components.BLAST_FURNACE_PLAN_SCROLL_29, 2, flash) + flash = !flash + return false + } + }) + npcl(FacialExpression.OLD_DEFAULT, "Firstly, the stove needs to be kept filled with coal. If this runs out the furnace will not work.") + stage++ + } + 12 -> { + setComponentVisibility(player, Components.BLAST_FURNACE_PLAN_SCROLL_29, 2, false) + submitIndividualPulse(player, object : Pulse() { + var flash = true + override fun pulse(): Boolean { + setComponentVisibility(player, Components.BLAST_FURNACE_PLAN_SCROLL_29, 14, flash) + flash = !flash + return false + } + }) + npcl(FacialExpression.OLD_DEFAULT, "Secondly, someone needs to operate the pump that keeps the hot air blast circulating through the furnace.") + stage++ + } + 13 -> { + setComponentVisibility(player, Components.BLAST_FURNACE_PLAN_SCROLL_29, 14, false) + submitIndividualPulse(player, object : Pulse() { + var flash = true + override fun pulse(): Boolean { + setComponentVisibility(player, Components.BLAST_FURNACE_PLAN_SCROLL_29, 15, flash) + flash = !flash + return false + } + }) + npcl(FacialExpression.OLD_DEFAULT, "Thirdly, someone needs to keep an eye on the temperature gauge. They should tell the pumper to start or stop so that the temperature stays in the right range.") + stage++ + } + 14 -> { + setComponentVisibility(player, Components.BLAST_FURNACE_PLAN_SCROLL_29, 15, false) + submitIndividualPulse(player, object : Pulse() { + var flash = true + override fun pulse(): Boolean { + setComponentVisibility(player, Components.BLAST_FURNACE_PLAN_SCROLL_29, 13, flash) + flash = !flash + return false + } + }) + npcl(FacialExpression.OLD_DEFAULT, "Lastly, someone needs to be on the pedals to power the conveyor belt that puts ore into the furnace.") + stage++ + } + 15 -> npcl(FacialExpression.OLD_DEFAULT, "Someone will also need to be on standby with a hammer in case the machine breaks!").also { stage++ } + 16 -> npcl(FacialExpression.OLD_DEFAULT, "While that's going, anyone else can put their ore on the conveyor belt to have it smelted.").also { stage++ } + 17 -> { + setComponentVisibility(player, Components.BLAST_FURNACE_PLAN_SCROLL_29, 13, false) + closeInterface(player) + showTopics( + Topic(FacialExpression.FRIENDLY, "So what do I get out of it?", 20), + Topic(FacialExpression.FRIENDLY, "Okay I'll get to work.", 101), + Topic(FacialExpression.FRIENDLY, "Maybe some other time.", 102), + ) + } + + 20 -> npcl(FacialExpression.OLD_DEFAULT, "Keeping the furnace going is valuable experience in itself!").also { stage++ } + 21 -> npcl(FacialExpression.OLD_DEFAULT, "Keeping the stove alight will train your Firemaking, working the pump will train your Strength, pedalling the conveyor belt will train your Agility and repairing the furnace will train Crafting.").also { stage++ } + 22 -> npcl(FacialExpression.OLD_DEFAULT, "Plus, everyone working the furnace will get a bit of experience in Smithing whenever anyone smelts ore in it.").also { stage++ } + 23 -> showTopics( + Topic(FacialExpression.FRIENDLY, "So I don't actually get paid then?", 25), + Topic(FacialExpression.FRIENDLY, "Okay I'll get to work.", 101), + Topic(FacialExpression.FRIENDLY, "Maybe some other time.", 102), + ) + + 25 -> npcl(FacialExpression.OLD_ANGRY1, "I offer you the privilege of working on the greatest example of dwarven engineering and you want to be PAID?").also { stage++ } + 26 -> npcl(FacialExpression.OLD_ANGRY2, "If you co-operate with a group of people you can all get your smelting done pretty quickly. Or perhaps some humans who need to do some smelting will pay you, since you humans are all so obsessed with money.").also { stage++ } + 27 -> showTopics( + Topic(FacialExpression.FRIENDLY, "Okay I'll get to work.", 101), + Topic(FacialExpression.FRIENDLY, "Maybe some other time.", 102), + Topic(FacialExpression.FRIENDLY, "No wonder you have a labour shortage if you don't pay your workers!", 30, true), + ) + + 30 -> playerl(FacialExpression.FRIENDLY, "No wonder you have a labour shortage if you don't pay your workers!").also { stage++ } + 31 -> npcl(FacialExpression.OLD_DEFAULT, "What would be the point of making money if I gave it all away to menial workers?").also { stage++ } + 32 -> npcl(FacialExpression.OLD_DEFAULT, "I mean... Money isn't important! What about the greater glory of dwarven engineering? What about the satisfaction of helping others? What about the XP?").also { stage++ } + 33 -> showTopics( + Topic(FacialExpression.FRIENDLY, "Okay I'll get to work.", 101), + Topic(FacialExpression.FRIENDLY, "Maybe some other time.", 102), + ) + + 40 -> npcl(FacialExpression.OLD_DEFAULT, "You can, although you'll need to find some people to work it for you as well. But the furnace is very delicate so I charge a fee for anyone who doesn't have level 60 smithing.").also { stage++ } + 41 -> showTopics( + Topic(FacialExpression.FRIENDLY, "I have level 60!", 45), + Topic(FacialExpression.FRIENDLY, "I don't have level 60.", 51), + ) + + 45 -> npcl(FacialExpression.OLD_DEFAULT, "A human has level 60 smithing? How extraordinary!").also { stage++ } + 46 -> npcl(FacialExpression.OLD_DEFAULT, "Feel free to use the furnace. Remember, you only need half as much coal as with a regular furnace.").also { + stage = END_DIALOGUE + } + // This is inauthentic. Authentic dialogue below. + 51 -> npcl(FacialExpression.OLD_DEFAULT, "Hmm, well, I can't let you use the furnace. You need to have level 60 smithing.").also { + stage = END_DIALOGUE + } + + // These are unreachable until Blast Furnace implements <60 Smithing timeshare. + 52 -> npcl(FacialExpression.OLD_DEFAULT, "Hmm, well, I'll let you use the furnace if you want, but you must pay a fee of 2,500 coins.").also { stage++ } + 53 -> showTopics( + Topic(FacialExpression.FRIENDLY, "Okay.", 55, true), + Topic(FacialExpression.FRIENDLY, "Maybe some other time.", 102), + Topic(FacialExpression.FRIENDLY, "[Charm] How about I give you 1250 coins and I work extra hard?", 70, true), // Ring of Charos(a) + ) + 55 -> playerl(FacialExpression.FRIENDLY, "Okay, here you are.").also { stage++ } + 56 -> npcl(FacialExpression.OLD_DEFAULT, "Okay, you can use the furnace for ten minutes. Remember, you only need half as much coal as with a regular furnace.").also { + stage = END_DIALOGUE + } + + 70 -> playerl(FacialExpression.FRIENDLY, "How about I give you 1250 coins and I work extra hard?").also { stage++ } + 71 -> npcl(FacialExpression.OLD_DEFAULT, "You'll have to work hard enough for two humans if I agree...").also { stage++ } + 72 -> playerl(FacialExpression.FRIENDLY, "Oh, three humans, easily!").also { stage++ } + 73 -> npcl(FacialExpression.OLD_DEFAULT, "Well, okay then - 1250GP it is.").also { stage++ } + + 101 -> npcl(FacialExpression.OLD_DEFAULT, "Splendid!").also { + stage = END_DIALOGUE + } + 102 -> npcl(FacialExpression.OLD_ANGRY1, "Well if you're not here to work, go hang around somewhere else!").also { + stage = END_DIALOGUE + } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return BlastFurnaceForemanDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.BLAST_FURNACE_FOREMAN_2553) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misc/keldagrim/dialogue/BlastFurnaceForemanNPC.kt b/Server/src/main/content/region/misc/keldagrim/dialogue/BlastFurnaceForemanNPC.kt new file mode 100644 index 000000000..f74016a21 --- /dev/null +++ b/Server/src/main/content/region/misc/keldagrim/dialogue/BlastFurnaceForemanNPC.kt @@ -0,0 +1,42 @@ +package content.region.misc.keldagrim.dialogue + +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 BlastFurnaceForemanNPC(id: Int = 0, location: Location? = null) : AbstractNPC(id,location) { + companion object { + val MESSAGES = arrayOf( + // Mostly https://youtu.be/hZ_q441esf0 + "Somebody fix the conveyor belt!", // When the drive belt or cogs are broken -> conveyor belt. + "Somebody work the conveyor belt!", + "Somebody fix that pipe!", + "Somebody operate the pump!", + "Somebody refuel the stove!", + "Keep the temperature controlled.", // https://youtu.be/McMNWADwdF8&t=38 + "Work faster!", + "That's, it keep it going." + + ) + } + + override fun construct(id: Int, location: Location?, vararg objects: Any?): AbstractNPC { + return BlastFurnaceForemanNPC(id, location) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.BLAST_FURNACE_FOREMAN_2553) + } + + override fun handleTickActions() { + super.handleTickActions() + // TODO: Have chat messages pop up according to the state of the Blast Furnace and not on a dice roll. + if (RandomFunction.random(20) < 1) { + core.api.sendChat(this, MESSAGES.random()) + } + } + +} \ No newline at end of file diff --git a/Server/src/main/content/region/misc/keldagrim/dialogue/CartConductorDialogue.kt b/Server/src/main/content/region/misc/keldagrim/dialogue/CartConductorDialogue.kt index f47aca3ac..d88d0841c 100644 --- a/Server/src/main/content/region/misc/keldagrim/dialogue/CartConductorDialogue.kt +++ b/Server/src/main/content/region/misc/keldagrim/dialogue/CartConductorDialogue.kt @@ -15,7 +15,7 @@ private const val WHITE_WOLF_CONDUCTOR = 2181 private const val KELDAGRIM_CONDUCTOR = 2182 @Initializable -class CartConductorDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { +class CartConductorDialogue(player: Player? = null) : DialoguePlugin(player) { var visitedKeldagrim = false override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ @@ -61,7 +61,7 @@ class CartConductorDialogue(player: Player? = null) : core.game.dialogue.Dialogu } override fun npc(vararg messages: String?): Component { - return super.npc(core.game.dialogue.FacialExpression.OLD_NORMAL, *messages) + return super.npc(FacialExpression.OLD_NORMAL, *messages) } override fun open(vararg args: Any?): Boolean { @@ -77,7 +77,7 @@ class CartConductorDialogue(player: Player? = null) : core.game.dialogue.Dialogu return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return CartConductorDialogue(player) } diff --git a/Server/src/main/content/region/misc/keldagrim/dialogue/DrunkenDwarfDialogue.kt b/Server/src/main/content/region/misc/keldagrim/dialogue/DrunkenDwarfDialogue.kt new file mode 100644 index 000000000..a2cc6a9c1 --- /dev/null +++ b/Server/src/main/content/region/misc/keldagrim/dialogue/DrunkenDwarfDialogue.kt @@ -0,0 +1,43 @@ +package content.region.misc.keldagrim.dialogue + +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 + +@Initializable +class DrunkenDwarfDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + START_DIALOGUE -> playerl(FacialExpression.FRIENDLY, "Hello there! Are you alright?").also { stage++ } + 1 -> npcl(FacialExpression.OLD_DRUNK_RIGHT, "Of courshe! Why why why *hic* why shouldn't I be?").also { stage++ } + 2 -> playerl(FacialExpression.FRIENDLY, "Hey, you look vaguely familiar... haven't I seen you somewhere before?").also { stage++ } + 3 -> npcl(FacialExpression.OLD_DRUNK_RIGHT, "That'sh not likely... I've been here for eh...").also { stage++ } + 4 -> npcl(FacialExpression.OLD_DRUNK_RIGHT, "I've been here for...").also { stage++ } + 5 -> npcl(FacialExpression.OLD_DRUNK_RIGHT, "Yearsh!").also { stage++ } + 6 -> npcl(FacialExpression.OLD_DRUNK_RIGHT, "I've been looking after thish houshe for my... my coushin? Or wash it my brother? A family member anyway.").also { stage++ } + 7 -> playerl(FacialExpression.FRIENDLY, "And where is this... family member of yours?").also { stage++ } + 8 -> npcl(FacialExpression.OLD_DRUNK_RIGHT, "All over, all over... he shaish he went off to wander all over Gielinor. Don't remember why.").also { stage++ } + 9 -> playerl(FacialExpression.FRIENDLY, "Hmm... I wonder, I may have met him...").also { stage++ } + 10 -> npcl(FacialExpression.OLD_DRUNK_RIGHT, "He comesh back sometimesh... saysh he needsh more kebabsh.").also { stage++ } + 11 -> npcl(FacialExpression.OLD_DRUNK_RIGHT, "Or ish that jusht a dream? Sometimesh I dream kebabsh invade Keldi... Keldu... Keldashomething.").also { stage++ } + 12 -> npcl(FacialExpression.OLD_DRUNK_RIGHT, "When you eatsh them, they take over your mind, they do! An army of mindlesh kebab eating dwarvesh!").also { stage++ } + 13 -> playerl(FacialExpression.FRIENDLY, "Er, yes, well, I think I should be off now.").also { stage++ } + 14 -> npcl(FacialExpression.OLD_DRUNK_RIGHT, "THE KEBABSH WILL COME FOR YOU!").also { + stage = END_DIALOGUE + } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return DrunkenDwarfDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.DRUNKEN_DWARF_2203) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misc/keldagrim/dialogue/DwarfsDialogue.kt b/Server/src/main/content/region/misc/keldagrim/dialogue/DwarfsDialogue.kt new file mode 100644 index 000000000..a2b818a5a --- /dev/null +++ b/Server/src/main/content/region/misc/keldagrim/dialogue/DwarfsDialogue.kt @@ -0,0 +1,36 @@ +package content.region.misc.keldagrim.dialogue + +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 + +@Initializable +class DwarfsDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + START_DIALOGUE -> { + var random = arrayOf(1, 2, 3, 4, 5).random() + when (random) { + 1 -> npcl(FacialExpression.OLD_DEFAULT, "I hope the goblins are properly maintaining their stretch of track! I'd hate to see them spoil good Dwarven workmanship.").also { stage = END_DIALOGUE } + 2 -> npcl(FacialExpression.OLD_DEFAULT, "You're ${player.username}, aren't you? It's a good job you did helping to get this train link open.").also { stage = END_DIALOGUE } + 3 -> npcl(FacialExpression.OLD_DEFAULT, "Y'know, at first I didn't like the idea of visiting a goblin city, but these cave goblins are alright.").also { stage = END_DIALOGUE } + 4 -> npcl(FacialExpression.OLD_DEFAULT, "These goblins have so much more advanced technology than us. Take a look at their magical lights! We could never create something like them.").also { stage = END_DIALOGUE } + 5 -> npcl(FacialExpression.OLD_DEFAULT, "Dorgesh-Kaan's very nice to visit, but I don't think I'd want to live there.").also { stage = END_DIALOGUE } + } + } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return DwarfsDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.DWARF_5880, NPCs.DWARF_5881, NPCs.DWARF_5882, NPCs.DWARF_5883, NPCs.DWARF_5884, NPCs.DWARF_5885) + } +} diff --git a/Server/src/main/content/region/misc/keldagrim/dialogue/DwarvenBoatmanDialogue.kt b/Server/src/main/content/region/misc/keldagrim/dialogue/DwarvenBoatmanDialogue.kt index 85e4daa39..c25041dc4 100644 --- a/Server/src/main/content/region/misc/keldagrim/dialogue/DwarvenBoatmanDialogue.kt +++ b/Server/src/main/content/region/misc/keldagrim/dialogue/DwarvenBoatmanDialogue.kt @@ -1,67 +1,152 @@ package content.region.misc.keldagrim.dialogue -import core.game.component.Component +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.system.task.Pulse import core.game.world.map.Location import core.plugin.Initializable +import core.tools.END_DIALOGUE +import core.tools.START_DIALOGUE import org.rs09.consts.Components -import core.game.world.GameWorld +import org.rs09.consts.NPCs @Initializable -class DwarvenBoatmanDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { +class DwarvenBoatmanForthDialogue(player: Player? = null) : DialoguePlugin(player) { override fun handle(interfaceId: Int, buttonId: Int): Boolean { - when(stage++){ - 0 -> player("I'd like to go to Keldagrim please.") - 1 -> npc("How do you know about that?") - 2 -> player("Look, I just do, okay?") - 3 -> npc("uhhhh") - 4 -> player("I'm, errr, from the future!") - 5 -> npc("hmmm") - 6 -> player("Can I please just go?") - 7 -> npc("Hmmmmmmmmmmm") - 8 -> player("Ok, fine! I'll prove it! errrr....") - 9 -> npc("Hm?") - 10 -> player("In the future your big statue gets knocked over!") - 11 -> npc("How do you even know about-") - 12 -> npc("Gah, nevermind! Just climb","on board!") - 13 -> player("Thank you!") - 14 -> end().also { GameWorld.Pulser.submit(travelPulse(player)) } + // TODO: This should be replaced with the Giant Dwarf quest + if (!getAttribute(player, "/save:keldagrim-visited", false)) { + when (stage) { + START_DIALOGUE -> npcl(FacialExpression.OLD_HAPPY, "Ho there, human!").also { stage++ } + 1 -> playerl(FacialExpression.FRIENDLY, "${player.username}.").also { stage++ } + 2 -> npcl(FacialExpression.OLD_HAPPY, "Ho there, ${player.username}! Want to take a ride with me?").also { stage++ } + 3 -> playerl(FacialExpression.FRIENDLY, "Where are you going? Across the river?").also { stage++ } + 4 -> npcl(FacialExpression.OLD_HAPPY, "No no, that's what the ferryman is for! I'm going to Keldagrim, my home!").also { stage++ } + 5 -> playerl(FacialExpression.FRIENDLY, "How much will that cost me then?").also { stage++ } + 6 -> npcl(FacialExpression.OLD_HAPPY, "For a human like you, I can do it for free!").also { stage++ } + 7 -> showTopics( + Topic(FacialExpression.FRIENDLY, "That's a deal!", 8), + Topic(FacialExpression.FRIENDLY, "I prefer the mines to the city.", 15), + ) + 8 -> npcl(FacialExpression.OLD_HAPPY, "Excellent! I'm just waiting for my ship to arrive and then we can go.").also { stage++ } + 9 -> npcl(FacialExpression.OLD_HAPPY, "Mind, this trip could take a few minutes! Are you sure you're ready to go as well?").also { stage++ } + 10 -> { + setComponentVisibility(player, 228, 6, true) + setComponentVisibility(player, 228, 9, false) + sendDialogueOptions(player, "(Not Done) Start The Giant Dwarf quest?", "Yes", "No").also { stage++ } + } + 11 -> when (buttonId) { + 1 -> playerl(FacialExpression.FRIENDLY, "Yes, I'm ready and don't mind it taking a few minutes.").also { stage = 12 } + 2 -> playerl(FacialExpression.FRIENDLY, "No, I don't have time right now, I'll be back later.").also { stage = 14 } + } + 12 -> npcl(FacialExpression.OLD_HAPPY, "Well, let's not waste any more time then!").also { stage++ } + 13 -> end().also { + submitWorldPulse(TravelForthPulse(player)) + } + 14 -> npcl(FacialExpression.OLD_HAPPY, "Sure, sure! Come back when you need to!").also { + stage = END_DIALOGUE + } + 15 -> npcl(FacialExpression.OLD_HAPPY, "Well, suit yourself then!").also { + stage = END_DIALOGUE + } + } + } else { + when (stage) { + START_DIALOGUE -> npcl(FacialExpression.OLD_HAPPY, "Hello again, ${player.username}! Want to go back to Keldagrim?").also { stage++ } + 1 -> showTopics( + Topic(FacialExpression.FRIENDLY, "Yes, please take me.", 2), + Topic(FacialExpression.FRIENDLY, "What, on your ship? No way.", 3), + ) + 2 -> end().also { + submitWorldPulse(TravelForthPulse(player)) + } + 3 -> npcl(FacialExpression.OLD_NORMAL, "Hey, it was only a little accident! Calm down!").also { + stage = END_DIALOGUE + } + } } return true } - override fun open(vararg args: Any?): Boolean { - npc("Aye, what do you want?") - stage = 0 - return true - } - - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { - return DwarvenBoatmanDialogue(player) + override fun newInstance(player: Player?): DialoguePlugin { + return DwarvenBoatmanForthDialogue(player) } override fun getIds(): IntArray { - return intArrayOf(2205,1844,1843) - } - - override fun npc(vararg messages: String?): Component { - return super.npc(core.game.dialogue.FacialExpression.OLD_NORMAL, *messages) + return intArrayOf(NPCs.DWARVEN_BOATMAN_2205) //1844,1843 } } -class travelPulse(val player: Player): Pulse(1){ +class TravelForthPulse(val player: Player): Pulse(1){ var counter = 0 override fun pulse(): Boolean { when(counter++){ - 0 -> player.lock().also { player.interfaceManager.open(Component(Components.FADE_TO_BLACK_120)) } - 3 -> player.properties.teleportLocation = Location.create(2888, 10225, 0) - 4 -> { - player.interfaceManager.close(Component(Components.FADE_TO_BLACK_120)) - player.interfaceManager.open(Component(Components.FADE_FROM_BLACK_170)) + 0 -> lock(player, 10).also { + openInterface(player, Components.FADE_TO_BLACK_120) + } + 3 -> teleport(player, Location.create(2892, 10225, 0)) + 4 -> { + closeInterface(player) + openInterface(player, Components.FADE_FROM_BLACK_170) + } + 6 -> unlock(player).also { + closeInterface(player) + setAttribute(player, "/save:keldagrim-visited", true) + return true } - 6 -> player.unlock().also { player.interfaceManager.close(Component(Components.FADE_FROM_BLACK_170)); player.setAttribute("/save:keldagrim-visited",true); return true } } return false } -} \ No newline at end of file +} + + +@Initializable +class DwarvenBoatmanBackDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + START_DIALOGUE -> npcl(FacialExpression.OLD_HAPPY, "Want me to take you back to the mines?").also { stage++ } + 1 -> showTopics( + Topic(FacialExpression.FRIENDLY, "Yes, please take me.", 2), + Topic(FacialExpression.FRIENDLY, "What, on your ship! No thanks!", 3), + ) + 2 -> end().also { submitWorldPulse(TravelBackPulse(player)) } + 3 -> npcl(FacialExpression.OLD_NORMAL, "Hey now, it was only a slight accident!").also { stage++ } + 4 -> playerl(FacialExpression.FRIENDLY, "A slight accident?? Have you any idea how much time I spent rebuilding that statue?").also { stage++ } + 5 -> npcl(FacialExpression.OLD_NORMAL, "Calm down, calm down! You got what you paid for the trip, didn't you?").also { + stage = END_DIALOGUE + } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return DwarvenBoatmanBackDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.DWARVEN_BOATMAN_2206) + } +} + +class TravelBackPulse(val player: Player): Pulse(1){ + var counter = 0 + override fun pulse(): Boolean { + when(counter++){ + 0 -> lock(player, 10).also { + openInterface(player, Components.FADE_TO_BLACK_120) + } + 3 -> teleport(player, Location.create(2838, 10127, 0)) + 4 -> { + closeInterface(player) + openInterface(player, Components.FADE_FROM_BLACK_170) + } + 6 -> unlock(player).also { + closeInterface(player) + return true + } + } + return false + } +} diff --git a/Server/src/main/content/region/misc/keldagrim/dialogue/DwarvenFerrymanDialogue.kt b/Server/src/main/content/region/misc/keldagrim/dialogue/DwarvenFerrymanDialogue.kt new file mode 100644 index 000000000..37e462edb --- /dev/null +++ b/Server/src/main/content/region/misc/keldagrim/dialogue/DwarvenFerrymanDialogue.kt @@ -0,0 +1,113 @@ +package content.region.misc.keldagrim.dialogue + +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.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 + +@Initializable +class DwarvenFerrymanForthDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + START_DIALOGUE -> { + if (!getAttribute(player, "/save:keldagrim-river-forth-crossed", false)) { + npcl(FacialExpression.OLD_DEFAULT, "Hello there, want a ride?").also { stage++ } + } else { + npcl(FacialExpression.OLD_DEFAULT, "Want to cross the river? It's just 2 gold pieces!").also { stage = 6 } + } + } + 1 -> playerl(FacialExpression.FRIENDLY, "A ride, where to?").also { stage++ } + 2 -> npcl(FacialExpression.OLD_DEFAULT, "Across the river, across the river! It's just a short ride!").also { stage++ } + 3 -> playerl(FacialExpression.FRIENDLY, "How much will that cost me?").also { stage++ } + 4 -> npcl(FacialExpression.OLD_DEFAULT, "Haha, you understand us dwarves well, human!").also { stage++ } + 5 -> npcl(FacialExpression.OLD_DEFAULT, "It's just 2 gold pieces! Want to go?").also { stage++ } + 6 -> showTopics( + Topic(FacialExpression.FRIENDLY, "Yes please.", 7), + Topic(FacialExpression.FRIENDLY, "No thanks.", END_DIALOGUE), + ) + 7 -> { + if (!inInventory(player, Items.COINS_995, 2)) { + npcl(FacialExpression.OLD_DEFAULT, "You don't even have 2 gold coins, humans!").also { stage++ } + } else { + if (removeItem(player, Item(Items.COINS_995, 2))) { + setAttribute(player, "/save:keldagrim-river-forth-crossed", true) + end() + teleport(player, Location.create(2836, 10143, 0)) + } + } + } + 8 -> npcl(FacialExpression.OLD_DEFAULT, "Come back later.").also { + stage = END_DIALOGUE + } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return DwarvenFerrymanForthDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.DWARVEN_FERRYMAN_1843) + } +} + +// https://youtu.be/KI_f24g0Xl0 +@Initializable +class DwarvenFerrymanBackDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + START_DIALOGUE -> { + if (!getAttribute(player, "/save:keldagrim-river-back-crossed", false)) { + npcl(FacialExpression.OLD_DEFAULT, "Hello, want to cross the river?").also { stage++ } + } else { + npcl(FacialExpression.OLD_DEFAULT, "Want me to take you across? It's just 2 gold pieces!").also { stage = 4 } + } + } + 1 -> playerl(FacialExpression.FRIENDLY, "Are you going to charge me too?").also { stage++ } + 2 -> npcl(FacialExpression.OLD_DEFAULT, "Are you kidding?").also { stage++ } + 3 -> npc(FacialExpression.OLD_DEFAULT, "Of course I am! But it's just 2 gold coins, do you", "want to go?").also { stage++ } + 4 -> showTopics( + Topic(FacialExpression.FRIENDLY, "Yes please.", 5), + Topic(FacialExpression.FRIENDLY, "No thanks.", END_DIALOGUE), + ) + 5 -> { + if (!inInventory(player, Items.COINS_995, 2)) { + npcl(FacialExpression.OLD_DEFAULT, "You don't even have 2 gold coins, humans!").also { stage++ } + } else { + if (removeItem(player, Item(Items.COINS_995, 2))) { + end() + setAttribute(player, "/save:keldagrim-river-back-crossed", true) + teleport(player, Location.create(2864, 10133, 0)) + } + } + } + 6 -> npcl(FacialExpression.OLD_DEFAULT, "Come back later.").also { stage++ } + 7 -> playerl(FacialExpression.FRIENDLY, "But... that means I'm stuck here.").also { stage++ } + 8 -> npcl(FacialExpression.OLD_DEFAULT, "Hmm. I suppose I could make an exception for you this time.").also { stage++ } + 9 -> playerl(FacialExpression.FRIENDLY, "Thanks a lot!").also { stage++ } + 10 -> { + end() + setAttribute(player, "/save:keldagrim-river-back-crossed", true) + teleport(player, Location.create(2864, 10133, 0)) + } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return DwarvenFerrymanBackDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.DWARVEN_FERRYMAN_1844) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misc/keldagrim/dialogue/FactoryManagerDialogue.kt b/Server/src/main/content/region/misc/keldagrim/dialogue/FactoryManagerDialogue.kt new file mode 100644 index 000000000..bc6e16495 --- /dev/null +++ b/Server/src/main/content/region/misc/keldagrim/dialogue/FactoryManagerDialogue.kt @@ -0,0 +1,35 @@ +package content.region.misc.keldagrim.dialogue + +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 + +@Initializable +class FactoryManagerDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + START_DIALOGUE -> npcl(FacialExpression.OLD_ANGRY1, "Don't bother me, can't you see I'm busy?").also { stage++ } + 1 -> playerl(FacialExpression.FRIENDLY, "No, I can't... can I go into the factory?").also { stage++ } + 2 -> npcl(FacialExpression.OLD_NORMAL, "You can go in. The Consortium, in their infinite wisdom, has decided to open up the factory to all and sundry.").also { stage++ } + 3 -> playerl(FacialExpression.FRIENDLY, "How come?").also { stage++ } + 4 -> npcl(FacialExpression.OLD_NORMAL, "We've built a new blast furnace and always have a shortage of people to work on it.").also { stage++ } + 5 -> playerl(FacialExpression.FRIENDLY, "I'll have a look, thanks.").also { stage++ } + 6 -> npcl(FacialExpression.OLD_NORMAL, "Just don't go causing any trouble, I still run this factory!").also { + stage = END_DIALOGUE + } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return FactoryManagerDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.FACTORY_MANAGER_2171) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misc/keldagrim/dialogue/FactoryWorkersDialogue.kt b/Server/src/main/content/region/misc/keldagrim/dialogue/FactoryWorkersDialogue.kt new file mode 100644 index 000000000..3acf1ced7 --- /dev/null +++ b/Server/src/main/content/region/misc/keldagrim/dialogue/FactoryWorkersDialogue.kt @@ -0,0 +1,109 @@ +package content.region.misc.keldagrim.dialogue + +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 + +@Initializable +class FactoryWorkerDialogue1(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + START_DIALOGUE -> playerl(FacialExpression.FRIENDLY, "I'm sorry, I'm looking for the blast furnace?").also { stage++ } + 1 -> npcl(FacialExpression.OLD_ANGRY1, "Do I look like a guide to you?").also { stage++ } + 2 -> playerl(FacialExpression.FRIENDLY, "No, you look like a hard-working dwarf, but can you please tell me where the blast furnace is?").also { stage++ } + 3 -> npcl(FacialExpression.OLD_DEFAULT, "Alright, just head down the stairs, it's easy to find.").also { stage++ } + 4 -> playerl(FacialExpression.FRIENDLY, "Thanks.").also { + stage = END_DIALOGUE + } + } + return true + } + + override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + return FactoryWorkerDialogue1(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.FACTORY_WORKER_2172) + } +} + +@Initializable +class FactoryWorkerDialogue2(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + START_DIALOGUE -> playerl(FacialExpression.FRIENDLY, "Are you okay?").also { stage++ } + 1 -> npcl(FacialExpression.OLD_ANGRY1, "Don't I look okay?").also { stage++ } + 2 -> playerl(FacialExpression.FRIENDLY, "If you were any shorter you wouldn't exist.").also { stage++ } + 3 -> npcl(FacialExpression.OLD_ANGRY1, "Very funny, human.").also { + stage = END_DIALOGUE + } + } + return true + } + + override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + return FactoryWorkerDialogue2(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.FACTORY_WORKER_2173) + } +} + +@Initializable +class FactoryWorkerDialogue3(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + START_DIALOGUE -> playerl(FacialExpression.FRIENDLY, "What are you dwarves doing in this factory?").also { stage++ } + 1 -> npcl(FacialExpression.OLD_ANGRY1, "Working of course, can't you see that?").also { stage++ } + 2 -> playerl(FacialExpression.FRIENDLY, "But working on what?").also { stage++ } + 3 -> npcl(FacialExpression.OLD_DEFAULT, "Refining the ore that is being brought into the factory, of course.").also { stage++ } + 4 -> playerl(FacialExpression.FRIENDLY, "And what does that mean?").also { stage++ } + 5 -> npcl(FacialExpression.OLD_ANGRY1, "It means you should stop asking so many questions and get back to work!").also { + stage = END_DIALOGUE + } + } + return true + } + + override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + return FactoryWorkerDialogue3(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.FACTORY_WORKER_2174) + } +} + +@Initializable +class FactoryWorkerDialogue4(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + START_DIALOGUE -> playerl(FacialExpression.FRIENDLY, "Who owns this factory?").also { stage++ } + 1 -> npcl(FacialExpression.OLD_DEFAULT, "The Consortium does and that's all you need to know.").also { stage++ } + 2 -> playerl(FacialExpression.FRIENDLY, "But what company? I thought there were all these different companies?").also { stage++ } + 3 -> npcl(FacialExpression.OLD_DEFAULT, "Oh yes, all the major companies own this plant. It's too vital to be in the hands of one company alone.").also { stage++ } + 4 -> playerl(FacialExpression.FRIENDLY, "And what exactly are you doing here?").also { stage++ } + 5 -> npcl(FacialExpression.OLD_ANGRY1, "I tire of these questions. Let me get back to work!").also { + stage = END_DIALOGUE + } + } + return true + } + + override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + return FactoryWorkerDialogue4(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.FACTORY_WORKER_2175) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misc/keldagrim/dialogue/GunslikDialogue.kt b/Server/src/main/content/region/misc/keldagrim/dialogue/GunslikDialogue.kt new file mode 100644 index 000000000..169ab018e --- /dev/null +++ b/Server/src/main/content/region/misc/keldagrim/dialogue/GunslikDialogue.kt @@ -0,0 +1,34 @@ +package content.region.misc.keldagrim.dialogue + +import core.api.openNpcShop +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 +class GunslikDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + START_DIALOGUE -> npcl(FacialExpression.OLD_DEFAULT, "What can I interest you in? We have something of everything here!").also { stage++ } + 1 -> showTopics( + Topic(FacialExpression.FRIENDLY, "Oh good!", 2), + Topic(FacialExpression.FRIENDLY, "Nothing, thanks.", END_DIALOGUE), + ) + 2 -> end().also{ openNpcShop(player, NPCs.GUNSLIK_2154) } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return GunslikDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.GUNSLIK_2154) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misc/keldagrim/dialogue/InnKeeperDialogue.kt b/Server/src/main/content/region/misc/keldagrim/dialogue/InnKeeperDialogue.kt index 1dfaebdeb..f341e6d53 100644 --- a/Server/src/main/content/region/misc/keldagrim/dialogue/InnKeeperDialogue.kt +++ b/Server/src/main/content/region/misc/keldagrim/dialogue/InnKeeperDialogue.kt @@ -12,34 +12,34 @@ import org.rs09.consts.NPCs */ @Initializable -class InnKeeperDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class InnKeeperDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - player(core.game.dialogue.FacialExpression.FRIENDLY,"Hello.").also { stage = 0 } + player(FacialExpression.FRIENDLY,"Hello.").also { stage = 0 } return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ 0 -> { - npc(core.game.dialogue.FacialExpression.CHILD_NEUTRAL, "Welcome to the King's Axe inn!", "What can I help you with?").also { stage++ } + npc(FacialExpression.CHILD_NEUTRAL, "Welcome to the King's Axe inn!", "What can I help you with?").also { stage++ } } 1 -> { - player(core.game.dialogue.FacialExpression.ASKING, "Can I have some beer please?").also { stage++ } + player(FacialExpression.ASKING, "Can I have some beer please?").also { stage++ } } 2 -> { - npc(core.game.dialogue.FacialExpression.CHILD_NORMAL, "Go to the bar downstairs.", "I only deal with residents.").also { stage++ } + npc(FacialExpression.CHILD_NORMAL, "Go to the bar downstairs.", "I only deal with residents.").also { stage++ } } 3 -> { - player(core.game.dialogue.FacialExpression.THINKING, "Residents? People live here?").also { stage++ } + player(FacialExpression.THINKING, "Residents? People live here?").also { stage++ } } 4 -> { - npc(core.game.dialogue.FacialExpression.CHILD_LOUDLY_LAUGHING, "No, just guests that stay the night.").also { stage = 99 } + npc(FacialExpression.CHILD_LOUDLY_LAUGHING, "No, just guests that stay the night.").also { stage = 99 } } 99 -> end() @@ -47,7 +47,7 @@ class InnKeeperDialogue(player: Player? = null) : core.game.dialogue.DialoguePlu return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return InnKeeperDialogue(player) } diff --git a/Server/src/main/content/region/misc/keldagrim/dialogue/JorzikDialogue.kt b/Server/src/main/content/region/misc/keldagrim/dialogue/JorzikDialogue.kt index 0c0b33e7e..8844f3758 100644 --- a/Server/src/main/content/region/misc/keldagrim/dialogue/JorzikDialogue.kt +++ b/Server/src/main/content/region/misc/keldagrim/dialogue/JorzikDialogue.kt @@ -1,52 +1,46 @@ package content.region.misc.keldagrim.dialogue import core.game.dialogue.DialoguePlugin -import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player import core.plugin.Initializable -import core.api.* +import core.game.dialogue.FacialExpression +import core.game.dialogue.Topic import core.tools.END_DIALOGUE +import core.tools.START_DIALOGUE +import org.rs09.consts.NPCs -const val JORZIK = 2565 - -/**Just some silly little dialogue for Jorzik hehe - * @author phil lips*/ +/** + * Jorzik dialogue. + * @author phil lips + */ @Initializable -class JorzikDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { - - override fun open(vararg args: Any?): Boolean { - npc = args[0] as NPC - npc("Do you want to trade?") - stage = 0 - return true - } +class JorzikDialogue(player: Player? = null) : DialoguePlugin(player) { override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 0 -> options("What are you selling?","No, thanks").also { stage = 10 } - 10 -> when(buttonId){ - 1 -> sendPlayerDialogue(player,"What are you selling?").also { stage = 15 } - 2 -> sendPlayerDialogue(player,"No, thanks.").also {stage = 50} - } - 15 -> sendNPCDialogue(player, JORZIK,"The finest smiths from all over Gielinor come here to work, and I buy the fruit of their craft. Armour made from the strongest metals!").also { stage = 20 } - 20 -> options("Lets have a look, then.","No, thanks").also { stage = 30 } - 30 -> when(buttonId){ - 1 -> sendPlayerDialogue(player,"Let's have a look, then.").also { stage = 40 } - 2 -> sendPlayerDialogue(player,"No, thanks.").also {stage = 50} - } - 40 -> end().also { npc.openShop(player) } - 50 ->sendNPCDialogue(player, JORZIK,"You just don't appreciate the beauty of fine metalwork.").also { stage = END_DIALOGUE } + START_DIALOGUE -> npc(FacialExpression.OLD_DEFAULT,"Do you want to trade?").also { stage++ } + 1 -> showTopics( + Topic(FacialExpression.FRIENDLY,"What are you selling?",2), + Topic(FacialExpression.FRIENDLY,"No, thanks.", 5) + ) + 2 -> npcl(FacialExpression.OLD_DEFAULT,"The finest smiths from all over Gielinor come here to work, and I buy the fruit of their craft. Armour made from the strongest metals!").also { stage++ } + 3 -> showTopics( + Topic(FacialExpression.FRIENDLY,"Let's have a look, then.",4), + Topic(FacialExpression.FRIENDLY,"No, thanks.", 5) + ) + 4 -> end().also { npc.openShop(player) } + 5 -> npcl(FacialExpression.OLD_DEFAULT,"You just don't appreciate the beauty of fine metalwork.").also { stage = END_DIALOGUE } } return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return JorzikDialogue(player) } override fun getIds(): IntArray { - return intArrayOf(JORZIK) + return intArrayOf(NPCs.JORZIK_2565) } } \ No newline at end of file diff --git a/Server/src/main/content/region/misc/keldagrim/dialogue/KarlDialogue.kt b/Server/src/main/content/region/misc/keldagrim/dialogue/KarlDialogue.kt new file mode 100644 index 000000000..a00e2dc1f --- /dev/null +++ b/Server/src/main/content/region/misc/keldagrim/dialogue/KarlDialogue.kt @@ -0,0 +1,40 @@ +package content.region.misc.keldagrim.dialogue + +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 + +@Initializable +class KarlDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + START_DIALOGUE -> npcl(FacialExpression.OLD_DEFAULT, "Hello there, human! Come in, come in!").also { stage++ } + 1 -> playerl(FacialExpression.FRIENDLY, "Hi, nice to see you!").also { stage++ } + 2 -> npcl(FacialExpression.OLD_DEFAULT, "Tell me, what brings you here?").also { stage++ } + 3 -> playerl(FacialExpression.FRIENDLY, "Well, first I came to this city just for the adventure and excitement, but then I knocked over this statue, you see. Well, I didn't really knock it over myself, I was on this boat.").also { stage++ } + 4 -> npcl(FacialExpression.OLD_DEFAULT, "A boat! Fascinating, go on!").also { stage++ } + 5 -> playerl(FacialExpression.FRIENDLY, "Yes, well, this boatman was a bit rubbish, or there was something wrong with the ship, I don't know. And then I got arrested by the Black Guard.").also { stage++ } + 6 -> npcl(FacialExpression.OLD_DEFAULT, "Not the Black Guard!").also { stage++ } + 7 -> playerl(FacialExpression.FRIENDLY, "Yes, and they took me to this Veldaban person, who pretended to be really angry but actually he was really nice and he asked me for my help.").also { stage++ } + 8 -> playerl(FacialExpression.FRIENDLY, "And after a whole lot of running around, I finally managed to get the statue to be rebuilt.").also { stage++ } + 9 -> npcl(FacialExpression.OLD_DEFAULT, "Such an interesting tale!").also { stage++ } + 10 -> npcl(FacialExpression.OLD_DEFAULT, "Now how did you come to be in Keldagrim again?").also { stage++ } + 11 -> playerl(FacialExpression.FRIENDLY, "Never mind.").also { + stage = END_DIALOGUE + } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return KarlDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.KARL_2195) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misc/keldagrim/dialogue/KjutDialogue.kt b/Server/src/main/content/region/misc/keldagrim/dialogue/KjutDialogue.kt new file mode 100644 index 000000000..9cd9bdca0 --- /dev/null +++ b/Server/src/main/content/region/misc/keldagrim/dialogue/KjutDialogue.kt @@ -0,0 +1,79 @@ +package content.region.misc.keldagrim.dialogue + +import content.data.Quests +import core.api.addItemOrDrop +import core.api.inInventory +import core.api.isQuestComplete +import core.api.removeItem +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 + +@Initializable +class KjutDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + START_DIALOGUE -> npcl(FacialExpression.OLD_DEFAULT, "Kebabs! Get your kebabs here! One gold each!").also { stage++ } + 1 -> showTopics( + Topic(FacialExpression.FRIENDLY, "Sure, one kebab please.", 2, true), + Topic(FacialExpression.FRIENDLY, "I'm looking for a drunken dwarf.", 6), + Topic(FacialExpression.FRIENDLY, "Do you have any quests?", 17), + ) + 2 -> { + if (!inInventory(player, Items.COINS_995, 1)) { + playerl(FacialExpression.FRIENDLY, "Sure, one kebab please.").also { stage = 3 } + } else { + if (removeItem(player, Item(Items.COINS_995, 1))) { + addItemOrDrop(player, Items.KEBAB_1971) + playerl(FacialExpression.FRIENDLY, "Sure, one kebab please.").also { stage = END_DIALOGUE } + } + } + } + 3 -> npcl(FacialExpression.OLD_ANGRY1, "Yes? Are you going to give me that one gold coin then?").also { stage++ } + 4 -> playerl(FacialExpression.FRIENDLY, "Sorry, I don't seem to have any money on me.").also { stage++ } + 5 -> npcl(FacialExpression.OLD_DEFAULT, "The bank is Keldagrim-West. Hurry back!").also { + stage = END_DIALOGUE + } + 6 -> { + 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 } + } + } + 7 -> playerl(FacialExpression.FRIENDLY, "No, a specific drunken dwarf, he keeps harassing me.").also { stage++ } + 8 -> npcl(FacialExpression.OLD_DEFAULT, "That's still not very helpful. But let me see. There is one particularly drunken dwarf just to the northeast of here.").also { stage++ } + 9 -> npcl(FacialExpression.OLD_DEFAULT, "He's scaring away my customers, he says there's something wrong with my kebabs. Quite annoying fellow.").also { stage++ } + 10 -> playerl(FacialExpression.FRIENDLY, "I don't think that's who I mean.").also { stage++ } + 11 -> npcl(FacialExpression.OLD_DEFAULT, "Well, he does have a relative who wanders all over Gielinor. Comes back from time to time to buy more kebabs. Don't see him very often though.").also { stage++ } + 12 -> playerl(FacialExpression.FRIENDLY, "Do you think I'll have any chance of catching him here?").also { stage++ } + 13 -> npcl(FacialExpression.OLD_DEFAULT, "I think he was back here quite recently, so I doubt he'll be back any time soon.").also { + stage = END_DIALOGUE + } + 14 -> playerl(FacialExpression.FRIENDLY, "How do you mean?").also { stage++ } + 15 -> npcl(FacialExpression.OLD_DEFAULT, "Oh come on! You in the Laughing Miner? The whole city knows about you and your dwarven drinking buddies!").also { stage++ } + 16 -> playerl(FacialExpression.FRIENDLY, "Right, right.").also { + stage = END_DIALOGUE + } + 17 -> npcl(FacialExpression.OLD_DEFAULT, "Not really! I have a steady supply of meat coming in through the carts and I don't really need anything else!").also { + stage = END_DIALOGUE + } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return KjutDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.KJUT_2198) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misc/keldagrim/dialogue/NolarDialogue.kt b/Server/src/main/content/region/misc/keldagrim/dialogue/NolarDialogue.kt index 99a22d6cf..9b8eb4a32 100644 --- a/Server/src/main/content/region/misc/keldagrim/dialogue/NolarDialogue.kt +++ b/Server/src/main/content/region/misc/keldagrim/dialogue/NolarDialogue.kt @@ -1,51 +1,31 @@ package content.region.misc.keldagrim.dialogue +import core.api.openNpcShop import core.game.dialogue.DialoguePlugin import core.game.dialogue.FacialExpression -import core.game.node.entity.npc.NPC +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 -/** - * @author qmqz - */ - @Initializable -class NolarDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ - - override fun open(vararg args: Any?): Boolean { - npc = args[0] as NPC - npc(core.game.dialogue.FacialExpression.CHILD_NORMAL,"I have a wide variety of crafting tools on offer,", - "care to take a look?").also { stage = 0 } - return true - } +class NolarDialogue(player: Player? = null) : DialoguePlugin(player){ override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 0 -> { - options("Yes please!", "No thanks").also { stage++ } - } - - 1 -> when(buttonId){ - 1 -> { - player(core.game.dialogue.FacialExpression.FRIENDLY, "Yes please!").also { stage = 10 } - } - 2 -> { - player(core.game.dialogue.FacialExpression.FRIENDLY, "No thanks.").also { stage = 99 } - } - } - - 10 -> { - end().also { npc.openShop(player) } - } - - 99 -> end() + START_DIALOGUE -> npc(FacialExpression.OLD_DEFAULT, "I have a wide variety of crafting tools on offer,", "care to take a look?").also { stage++ } + 1 -> showTopics( + Topic(FacialExpression.FRIENDLY, "Yes please!", 2), + Topic(FacialExpression.FRIENDLY, "No thanks.", END_DIALOGUE), + ) + 2 -> end().also{ openNpcShop(player, NPCs.NOLAR_2158) } } return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return NolarDialogue(player) } diff --git a/Server/src/main/content/region/misc/keldagrim/dialogue/OrdanDialogue.kt b/Server/src/main/content/region/misc/keldagrim/dialogue/OrdanDialogue.kt index 567720bf3..701ed5aec 100644 --- a/Server/src/main/content/region/misc/keldagrim/dialogue/OrdanDialogue.kt +++ b/Server/src/main/content/region/misc/keldagrim/dialogue/OrdanDialogue.kt @@ -1,37 +1,44 @@ package content.region.misc.keldagrim.dialogue -import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player import core.plugin.Initializable import core.api.* -import core.game.node.item.Item -import org.rs09.consts.Items -import org.rs09.consts.NPCs -import core.game.interaction.InteractionListener +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.item.Item +import org.rs09.consts.NPCs import core.tools.END_DIALOGUE - -/**It's just some simple Ordan dialogue - * don't look at the unnoting code if you care - * about your braincells - * @author phil lips*/ - -const val ORDAN = 2564 +import core.tools.START_DIALOGUE +import org.rs09.consts.Items @Initializable -class OrdanDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { - - override fun open(vararg args: Any?): Boolean { - npc = args[0] as NPC - sendPlayerDialogue(player,"Can you un-note any of my items?") - stage = 0 - return true - } +class OrdanDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player), InteractionListener { override fun handle(interfaceId: Int, buttonId: Int): Boolean { + // Dialogue follows https://www.youtube.com/watch?v=mwoA01EYQks a 29 May 2010 capture when(stage){ - 0 -> sendNPCDialogue(player, ORDAN,"I can un-note Tin, Copper, Iron, Coal, and Mithril.").also { stage++ } - 1 -> sendNPCDialogue(player, ORDAN,"I can even un-note Adamantite and Runite, but you're gonna need deep pocktes for that.").also { stage = END_DIALOGUE } + START_DIALOGUE -> npcl(FacialExpression.OLD_HAPPY, "Are you here to smith? Do you want to buy some ore?").also { stage++ } + 1 -> showTopics( + Topic(FacialExpression.FRIENDLY, "Yes please.", 2), + Topic(FacialExpression.FRIENDLY, "Can you un-note any of my items?", 3), + Topic(FacialExpression.FRIENDLY, "No thanks.", 5), + ) + 2 -> openNpcShop(player, NPCs.ORDAN_2564).also { + end() + } + 3 -> npc(FacialExpression.OLD_HAPPY,"I suppose I can un-note any ores that I sell in my", "shop - for a price of course. Just give me the noted ore", "you wish to exchange.").also { + stage++ + } + // This following dialogue is not authentic, but was originally here. + 4 -> npcl(FacialExpression.OLD_HAPPY,"I can even un-note Adamantite and Runite, but you're gonna need deep pockets for that.").also { + stage = END_DIALOGUE + } + 5 -> npcl(FacialExpression.OLD_ANGRY1, "Well don't waste my time then!").also { + stage = END_DIALOGUE + } } return true } @@ -41,92 +48,92 @@ class OrdanDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin( } override fun getIds(): IntArray { - return intArrayOf(ORDAN) + return intArrayOf(NPCs.ORDAN_2564) } - /**This is for unnoting ores from Ordan, yeah I know being able to unnote Addy and Rune isn't authentic - * but QOL wins out in this one, however unnoting Addy and Runite is expensive as fuck and is essentially - * a big fat smelly gold sink, perhaps it will add some value to GP uh ha ha - * just comment those options out if you don't want em - * aka "WOW I CAN'T BELIEVE IT'S ALL CONDITIONALS*/ + override fun defineListeners() { + onUseWith(IntType.NPC, OrdanDialogueFile.ORES_TO_UNNOTE_PRICES.keys.toIntArray(), NPCs.ORDAN_2564) { player, noteType, npc -> + openDialogue(player, OrdanDialogueFile(noteType.id, noteType.name), npc.asNpc()) + return@onUseWith true + } + } +} - class OrdanUnnoteListener : InteractionListener { - val notedOre = intArrayOf( - Items.IRON_ORE_441, - Items.COPPER_ORE_437, - Items.TIN_ORE_439, - Items.COAL_454, - Items.MITHRIL_ORE_448, - Items.ADAMANTITE_ORE_450, - Items.SILVER_ORE_443, - Items.GOLD_ORE_445, - Items.RUNITE_ORE_452 +class OrdanDialogueFile(private val noteOreId: Int, private val noteOreName: String) : DialogueFile() { + + companion object { + val ORES_TO_UNNOTE_PRICES = hashMapOf( + Items.COPPER_ORE_437 to 10, + Items.TIN_ORE_439 to 10, + Items.IRON_ORE_441 to 8, + Items.SILVER_ORE_443 to 37, + Items.GOLD_ORE_445 to 75, + Items.MITHRIL_ORE_448 to 81, + Items.ADAMANTITE_ORE_450 to 330, + Items.RUNITE_ORE_452 to 1000, + Items.COAL_454 to 22, ) - val UNNOTE_PRICES = hashMapOf( - Items.IRON_ORE_441 to 8, - Items.COPPER_ORE_437 to 10, - Items.TIN_ORE_439 to 10, - Items.COAL_454 to 22, - Items.MITHRIL_ORE_448 to 81, - Items.ADAMANTITE_ORE_450 to 330, - Items.SILVER_ORE_443 to 37, - Items.GOLD_ORE_445 to 75, - Items.RUNITE_ORE_452 to 1000 - ) - override fun defineListeners() { - onUseWith(IntType.NPC, NPCs.ORDAN_2564,*notedOre){ player, _, noteType -> - val noteAmount = amountInInventory(player, noteType.id) - val noteName = noteType.name - var unNoteAmount = 0 - var inventorySlots = freeSlots(player) - var actualAmount = 0 - var proceed = false - var wait = false - player.dialogueInterpreter.sendOptions("$noteName un-note?","1","5","10","X") - player.dialogueInterpreter.addAction { player, button -> - when(button) { - 2 -> unNoteAmount = 1 - 3 -> unNoteAmount = 5 - 4 -> unNoteAmount = 10 - 5 ->{sendInputDialogue(player, true, "Enter amount:") { value -> - unNoteAmount = value as Int - actualAmount = if(unNoteAmount > inventorySlots){ - inventorySlots - }else{ - unNoteAmount - } - if(actualAmount > noteAmount){ - actualAmount = noteAmount - } - /**Yes this is a second when statement that does the exact same thing - * as the other one, and no im not removing this because if I do then - * custom input amounts don't work and im not spending more than - * a day making it work with just one when statement.*/ - val totalPrice = UNNOTE_PRICES[noteType.id]!! * (actualAmount) - if(amountInInventory(player,Items.COINS_995) >= totalPrice && actualAmount > 0 ){ - removeItem(player, Item(995,totalPrice),Container.INVENTORY) - removeItem(player, Item(noteType.id,actualAmount),Container.INVENTORY) - addItem(player,noteType.id - 1,actualAmount) - } - } - } - } - actualAmount = if(unNoteAmount > inventorySlots){ - inventorySlots - }else{ - unNoteAmount - } - if(actualAmount > noteAmount){ - actualAmount = noteAmount - } - val totalPrice = UNNOTE_PRICES[noteType.id]!! * (actualAmount) - if(amountInInventory(player,Items.COINS_995) >= totalPrice && actualAmount > 0 ){ - removeItem(player, Item(995,totalPrice),Container.INVENTORY) - removeItem(player, Item(noteType.id,actualAmount),Container.INVENTORY) - addItem(player,noteType.id - 1,actualAmount) - } + } + + // These are instance variables for un-noting dialogue. + var unnoteAmount = 0 + var unnotePrice = 0 + + // Dialogue follows https://www.youtube.com/watch?v=nqwzco2Es7o a 6 Dec 2009 capture + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + START_DIALOGUE -> { + // Swords position iface/child changes according to number of dialogue options. + setComponentVisibility(player!!, 232, 9, false) + setComponentVisibility(player!!, 232, 8, true) + sendDialogueOptions(player!!, "How much $noteOreName would you like to un-note?", "1", "5", "10", "X") + stage++ + } + 1 -> when (buttonID) { + 1 -> { + unnoteAmount = 1 + unnotePrice = 1 * ORES_TO_UNNOTE_PRICES[noteOreId]!! + npcl(FacialExpression.OLD_HAPPY, "I can un-note those for $unnotePrice gold pieces, is that okay?").also { stage++ } + } + 2 -> { + unnoteAmount = 5 + unnotePrice = 5 * ORES_TO_UNNOTE_PRICES[noteOreId]!! + npcl(FacialExpression.OLD_HAPPY, "I can un-note those for $unnotePrice gold pieces, is that okay?").also { stage++ } + } + 3 -> { + unnoteAmount = 10 + unnotePrice = 10 * ORES_TO_UNNOTE_PRICES[noteOreId]!! + npcl(FacialExpression.OLD_HAPPY, "I can un-note those for $unnotePrice gold pieces, is that okay?").also { stage++ } + } + 4 -> sendInputDialogue(player!!, true, "Enter amount:") { value -> + unnoteAmount = value as Int + // For QOL, reduce the amount to the number of free slots in player's inventory. + if (unnoteAmount > freeSlots(player!!)) { + unnoteAmount = freeSlots(player!!) + } + unnotePrice = unnoteAmount * ORES_TO_UNNOTE_PRICES[noteOreId]!! + // Dialogue is to cater to QOL + npcl(FacialExpression.OLD_HAPPY, "I can un-note $unnoteAmount $noteOreName for $unnotePrice gold pieces, is that okay?").also { stage++ } + return@sendInputDialogue + } + } + 2 -> showTopics( + Topic(FacialExpression.FRIENDLY, "It's a deal.", 3), + Topic(FacialExpression.FRIENDLY, "No, that's too expensive.", END_DIALOGUE), + ) + 3 -> { + if (freeSlots(player!!) < unnoteAmount) { + npc(FacialExpression.OLD_NORMAL, "You don't have enough room in your inventory for that", "number of un-noted items. Clear some space and try", "again.").also { stage = END_DIALOGUE } + } else if(amountInInventory(player!!, noteOreId) < unnoteAmount) { + sendDialogueLines(player!!, "You do not have enough notes to un-note", "$unnoteAmount $noteOreName.").also { stage = END_DIALOGUE } + } else if(amountInInventory(player!!, Items.COINS_995) < unnotePrice){ + sendDialogueLines(player!!, "You do not have enough coins to afford un-noting", "$unnoteAmount $noteOreName.").also{ stage = END_DIALOGUE } + } else { + if(removeItem(player!!, Item(Items.COINS_995, unnotePrice), Container.INVENTORY) && removeItem(player!!, Item(noteOreId, unnoteAmount), Container.INVENTORY)) { + addItem(player!!, noteOreId - 1, unnoteAmount) + } + end() } - return@onUseWith true } } } diff --git a/Server/src/main/content/region/misc/keldagrim/dialogue/Reinald.kt b/Server/src/main/content/region/misc/keldagrim/dialogue/ReinaldDialogue.kt similarity index 53% rename from Server/src/main/content/region/misc/keldagrim/dialogue/Reinald.kt rename to Server/src/main/content/region/misc/keldagrim/dialogue/ReinaldDialogue.kt index 96b102e62..6e746f33b 100644 --- a/Server/src/main/content/region/misc/keldagrim/dialogue/Reinald.kt +++ b/Server/src/main/content/region/misc/keldagrim/dialogue/ReinaldDialogue.kt @@ -1,45 +1,39 @@ package content.region.misc.keldagrim.dialogue +import core.api.openInterface import core.cache.def.impl.NPCDefinition -import core.game.component.Component import core.game.dialogue.DialoguePlugin import core.game.interaction.OptionHandler import core.game.node.Node import core.game.node.entity.player.Player import core.plugin.Initializable import core.plugin.Plugin - -const val REINALD = 2194 +import org.rs09.consts.NPCs /** * File for handling Reinald's dialogue and right-click option * @author Ceikry */ @Initializable -class ReinaldDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class ReinaldDialogue(player: Player? = null) : DialoguePlugin(player){ override fun handle(interfaceId: Int, buttonId: Int): Boolean { - when(stage++){ - 0 -> options("Yes, please!", "No, thanks.") - 1 -> when(buttonId){ - 1 -> end().also { player.interfaceManager.open(Component(593)) } + when(stage){ + 0 -> npc("Hello, human! Would you like to browse","my little shop of bracelets?").also { stage++ } + 1 -> options("Yes, please!", "No, thanks.").also { stage++ } + 2 -> when(buttonId){ + 1 -> end().also { openInterface(player,593) } 2 -> end() } } return true } - override fun open(vararg args: Any?): Boolean { - npc("Hello, human! Would you like to browse","my little shop of bracelets?") - stage = 0 - return true - } - - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return ReinaldDialogue(player) } override fun getIds(): IntArray { - return intArrayOf(REINALD) + return intArrayOf(NPCs.REINALD_2194) } } @@ -47,12 +41,12 @@ class ReinaldDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugi @Initializable class ReinaldOptionHandler : OptionHandler(){ override fun handle(player: Player?, node: Node?, option: String?): Boolean { - player?.interfaceManager?.open(Component(593)) + openInterface(player!!,593) return true } override fun newInstance(arg: Any?): Plugin { - NPCDefinition.forId(REINALD).handlers["option:change-armguards"] = this + NPCDefinition.forId(NPCs.REINALD_2194).handlers["option:change-armguards"] = this return this } diff --git a/Server/src/main/content/region/misc/keldagrim/dialogue/RowdyDwarfDialogue.kt b/Server/src/main/content/region/misc/keldagrim/dialogue/RowdyDwarfDialogue.kt new file mode 100644 index 000000000..40f789eda --- /dev/null +++ b/Server/src/main/content/region/misc/keldagrim/dialogue/RowdyDwarfDialogue.kt @@ -0,0 +1,30 @@ +package content.region.misc.keldagrim.dialogue + +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 + +@Initializable +class RowdyDwarfDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + START_DIALOGUE -> npcl(FacialExpression.OLD_DRUNK_LEFT, "Get OUT of my WAY!").also { + stage = END_DIALOGUE + } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return RowdyDwarfDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.ROWDY_DWARF_2187) + } +} + diff --git a/Server/src/main/content/region/misc/keldagrim/dialogue/RunvastrDialogue.kt b/Server/src/main/content/region/misc/keldagrim/dialogue/RunvastrDialogue.kt new file mode 100644 index 000000000..77e5d32e6 --- /dev/null +++ b/Server/src/main/content/region/misc/keldagrim/dialogue/RunvastrDialogue.kt @@ -0,0 +1,34 @@ +package content.region.misc.keldagrim.dialogue + +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 + +@Initializable +class RunvastrDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + START_DIALOGUE -> npcl(FacialExpression.OLD_DEFAULT, "Oh, leave an old dwarf in peace will you?").also { stage++ } + 1 -> playerl(FacialExpression.FRIENDLY, "Why, what's wrong?").also { stage++ } + 2 -> npcl(FacialExpression.OLD_DEFAULT, "Ninenty years down in the mines, and this is all I got out of it. All my life wasted away.").also { stage++ } + 3 -> playerl(FacialExpression.FRIENDLY, "You've still got a nice house, don't you?").also { stage++ } + 4 -> npcl(FacialExpression.OLD_DEFAULT, "A crummy old house in East, that's all my life amounts to.").also { stage++ } + 5 -> playerl(FacialExpression.FRIENDLY, "I'll leave you alone then.").also { + stage = END_DIALOGUE + } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return RunvastrDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.RUNVASTR_2190) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misc/keldagrim/dialogue/SuneDialogue.kt b/Server/src/main/content/region/misc/keldagrim/dialogue/SuneDialogue.kt new file mode 100644 index 000000000..efaa8e6e9 --- /dev/null +++ b/Server/src/main/content/region/misc/keldagrim/dialogue/SuneDialogue.kt @@ -0,0 +1,33 @@ +package content.region.misc.keldagrim.dialogue + +import core.api.openNpcShop +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 +class SuneDialogue(player: Player? = null) : DialoguePlugin(player){ + + // This npc travels between Tati's house and the city's marketplace. + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + START_DIALOGUE -> npcl(FacialExpression.OLD_ANGRY1, "Can you leave me alone please? I'm trying to get a bit of rest.").also { + stage = END_DIALOGUE + } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return SuneDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.SUNE_2191) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misc/keldagrim/dialogue/TatiDialogue.kt b/Server/src/main/content/region/misc/keldagrim/dialogue/TatiDialogue.kt new file mode 100644 index 000000000..df4ab14b2 --- /dev/null +++ b/Server/src/main/content/region/misc/keldagrim/dialogue/TatiDialogue.kt @@ -0,0 +1,54 @@ +package content.region.misc.keldagrim.dialogue + +import core.api.openNpcShop +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 +class TatiDialogue(player: Player? = null) : DialoguePlugin(player){ + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + START_DIALOGUE -> npcl(FacialExpression.OLD_ANGRY1, "What'you want?").also { stage++ } + 1 -> showTopics( + Topic(FacialExpression.FRIENDLY, "Do you have any pickaxes?", 2, true), + Topic(FacialExpression.FRIENDLY, "Do you have any quests?", 7, true), + Topic(FacialExpression.FRIENDLY, "Nothing really.", 14), + ) + 2 -> playerl(FacialExpression.FRIENDLY, "Do you-").also { stage++ } + 3 -> npcl(FacialExpression.OLD_ANGRY1, "What? Speak up, I can't hear you!").also { stage++ } + 4 -> playerl(FacialExpression.FRIENDLY, "Uhm... I'm just looking for some pickaxes! Do you have any?").also { stage++ } + 5 -> npcl(FacialExpression.OLD_ANGRY1, "Do I have any pickaxes? Do I? Of course I do, this is a pickaxe shop, isn't it!").also{ stage++ } + 6 -> openNpcShop(player, NPCs.TATI_2160).also { + stage = END_DIALOGUE + } + 7 -> playerl(FacialExpression.FRIENDLY, "Do you-").also { stage++ } + 8 -> npcl(FacialExpression.OLD_ANGRY1, "What? Stop mumbling!").also { stage++ } + 9 -> playerl(FacialExpression.FRIENDLY, "I want a quest!").also { stage++ } + 10 -> npcl(FacialExpression.OLD_ANGRY1, "I don't have any lousy quests... I've got someone who's helping me already!").also { stage++ } + 11 -> npcl(FacialExpression.OLD_ANGRY1, "Well, I say helping... he takes his merry time to do his chores, my son does.").also { stage++ } + 12 -> playerl(FacialExpression.FRIENDLY, "Then perhaps I can help in some way?").also { stage++ } + 13 -> npcl(FacialExpression.OLD_ANGRY1, "Pfft, I doubt it... maybe when my son fouls up again, but not now.").also { + stage = END_DIALOGUE + } + 14 -> npcl(FacialExpression.OLD_ANGRY1, "Then clear off!").also { + stage = END_DIALOGUE + } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return TatiDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.TATI_2160) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misc/keldagrim/dialogue/UlifedDialogue.kt b/Server/src/main/content/region/misc/keldagrim/dialogue/UlifedDialogue.kt new file mode 100644 index 000000000..8de60562b --- /dev/null +++ b/Server/src/main/content/region/misc/keldagrim/dialogue/UlifedDialogue.kt @@ -0,0 +1,31 @@ +package content.region.misc.keldagrim.dialogue + +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 + +@Initializable +class UlifedDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage){ + START_DIALOGUE -> npcl(FacialExpression.OLD_DEFAULT, "Say, aren't you the human who got arrested here the other day, by the boat?").also { stage++ } + 1 -> playerl(FacialExpression.FRIENDLY, "Yes, but we cleared up the whole situation.").also { stage++ } + 2 -> npcl(FacialExpression.OLD_DEFAULT, "Right, right.").also { + stage = END_DIALOGUE + } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return UlifedDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.ULIFED_2193) + } +} \ No newline at end of file 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 0fb6f4cf8..20d4d5771 100644 --- a/Server/src/main/content/region/misc/keldagrim/handlers/KeldagrimCartMethods.kt +++ b/Server/src/main/content/region/misc/keldagrim/handlers/KeldagrimCartMethods.kt @@ -8,15 +8,21 @@ import core.game.world.map.Direction 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, Quests.THE_GIANT_DWARF)) + return GameWorld.Pulser.submit(TravelToKeldagrimPulse(player)) } @JvmStatic fun leaveKeldagrimTo(player: Player, dest: Location){ + if (!hasRequirement(player, Quests.THE_GIANT_DWARF)) + return GameWorld.Pulser.submit(TravelFromKeldagrimPulse(player,dest)) } } @@ -63,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)) } @@ -90,4 +96,4 @@ class TravelToKeldagrimPulse(val player: Player) : Pulse(){ } return false } -} \ No newline at end of file +} diff --git a/Server/src/main/content/region/misc/keldagrim/handlers/KeldagrimListeners.kt b/Server/src/main/content/region/misc/keldagrim/handlers/KeldagrimListeners.kt new file mode 100644 index 000000000..2cf8da95e --- /dev/null +++ b/Server/src/main/content/region/misc/keldagrim/handlers/KeldagrimListeners.kt @@ -0,0 +1,19 @@ +package content.region.misc.keldagrim.handlers + +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.world.map.Location +import org.rs09.consts.Scenery + +class KeldagrimListeners : InteractionListener { + override fun defineListeners() { + on(Scenery.DOORWAY_23286, IntType.SCENERY, "enter") { player, _ -> + player.properties.teleportLocation = Location.create(2941, 10179, 0) + return@on true + } + on(Scenery.DOORWAY_23287, IntType.SCENERY, "enter") { player, _ -> + player.properties.teleportLocation = Location.create(2435, 5535, 0) + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misc/keldagrim/handlers/KeldagrimPlugin.kt b/Server/src/main/content/region/misc/keldagrim/handlers/KeldagrimPlugin.kt index 496e0aaca..186343d26 100644 --- a/Server/src/main/content/region/misc/keldagrim/handlers/KeldagrimPlugin.kt +++ b/Server/src/main/content/region/misc/keldagrim/handlers/KeldagrimPlugin.kt @@ -1,17 +1,19 @@ package content.region.misc.keldagrim.handlers +import content.minigame.blastfurnace.BlastFurnace import core.api.* import core.cache.def.impl.SceneryDefinition -import core.game.dialogue.DialoguePlugin import core.game.interaction.OptionHandler import core.game.node.Node import core.game.node.entity.player.Player import core.game.node.item.Item -import core.game.node.scenery.Scenery import core.game.world.map.Location import core.plugin.Initializable import core.plugin.Plugin import core.game.dialogue.DialogueFile +import core.game.dialogue.DialoguePlugin +import core.tools.END_DIALOGUE +import org.rs09.consts.Items /** * File that contains several plugins relating to Keldagrim, @@ -39,21 +41,17 @@ class KeldagrimOptionHandlers : OptionHandler() { 5998 -> player.properties.teleportLocation = Location.create(2780, 10161) } } - "climb-up" -> { - when(node.id){ - 9138 -> player.properties.teleportLocation = Location.create(2930, 10197, 0) - } - } - "climb-down" -> { - when(node.id){ - 9084 -> player.dialogueInterpreter.open(BlastFurnaceDoorDialogue(),Scenery(9084, location(2929,10196,0))) - } - } "open" -> { when(node.id){ 28094 -> player.dialogueInterpreter.open(GETrapdoorDialogueID) } } + "ride" -> { + when(node.id){ + 7029 -> player.dialogueInterpreter.open(GETrapdoorDialogueID) + 7030 -> player.dialogueInterpreter.open(GETrapdoorDialogueID) + } + } "enter" -> { when(node.id){ 5014 -> player.properties.teleportLocation = Location.create(2730, 3713, 0) @@ -70,6 +68,8 @@ class KeldagrimOptionHandlers : OptionHandler() { SceneryDefinition.forId(9138).handlers["option:climb-up"] = this SceneryDefinition.forId(28094).handlers["option:open"] = this SceneryDefinition.forId(5014).handlers["option:enter"] = this + SceneryDefinition.forId(7029).handlers["option:ride"] = this + SceneryDefinition.forId(7030).handlers["option:ride"] = this return this } } @@ -78,7 +78,7 @@ class KeldagrimOptionHandlers : OptionHandler() { * Dialogue used for the trapdoor in the grand exchange. */ @Initializable -class GETrapdoorDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class GETrapdoorDialogue(player: Player? = null) : DialoguePlugin(player){ override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ 0 -> end() @@ -102,7 +102,7 @@ class GETrapdoorDialogue(player: Player? = null) : core.game.dialogue.DialoguePl return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return GETrapdoorDialogue(player) } @@ -112,41 +112,23 @@ class GETrapdoorDialogue(player: Player? = null) : core.game.dialogue.DialoguePl } -class BlastFurnaceDoorDialogue : DialogueFile(){ +class BlastFurnaceDoorDialogue(val fee: Int) : DialogueFile(){ var init = true override fun handle(componentID: Int, buttonID: Int) { - if (init) { - //stage = if (player!!.getSkills().getLevel(Skills.SMITHING) >= 60) { - // 100 - //}else{ - // 5 - //} - //init = false - //uncomment the above out when BF gets fixed. - stage = 69 - } when(stage){ - 5 -> sendDialogue(player!!,"You must be Smithing Level 60 or higher in order to enter the Blast Furnace").also { stage = 10 } - 10 -> sendDialogue(player!!,"However, you may enter if you pay the entrance fee").also { stage = 20 } - 20 -> options("Yes","No").also { stage = 30 } - 30 -> when(buttonID){ + 0 -> sendDialogue(player!!,"You must be Smithing Level 60 or higher in order to enter the Blast Furnace").also { stage++ } + 1 -> sendDialogue(player!!,"However, you may enter for 10 minutes if you pay the entrance fee.
($fee gp)").also { stage++ } + 2 -> options("Yes","No").also { stage++ } + 3 -> when(buttonID){ 1 -> { - if(player!!.equipment.contains(6465,1) && player!!.inventory.contains(995, 1250)){ - removeItem(player!!, Item(995, 1250), Container.INVENTORY) - player!!.setAttribute("BlastTimer",1000) - player?.properties?.teleportLocation = Location.create(1940, 4958, 0).also { end() } - } - else if (player!!.inventory.contains(995, 2500)) { - removeItem(player!!, Item(995, 2500), Container.INVENTORY) - player!!.setAttribute("BlastTimer",1000) - player?.properties?.teleportLocation = Location.create(1940, 4958, 0).also { end() } - } else sendDialogue(player!!, "You don't have enough gold to pay the fee!").also { stage = 40 } + if (removeItem(player!!, Item(Items.COINS_995, fee))) + BlastFurnace.enter(player!!, true) + else + sendDialogue(player!!, "You don't have enough gold to cover the entrance fee!") + stage = END_DIALOGUE } - 20 -> sendDialogue(player!!,"Then get out of here!").also { stage = 40 } + 20 -> sendDialogue(player!!,"Then get out of here!").also { stage = END_DIALOGUE } } - 40 -> end() - 69 -> sendDialogue(player!!,"The Blast Furnace is temporarily closed down for maintenance.").also { end() } //Remove this line when BF gets fixed - 100 -> player?.properties?.teleportLocation = Location.create(1940, 4958, 0).also { stage = 40 } } } } diff --git a/Server/src/main/content/region/misc/miscellania/dialogue/AlvissDialogue.kt b/Server/src/main/content/region/misc/miscellania/dialogue/AlvissDialogue.kt index 354ab4f80..b3f4f7004 100644 --- a/Server/src/main/content/region/misc/miscellania/dialogue/AlvissDialogue.kt +++ b/Server/src/main/content/region/misc/miscellania/dialogue/AlvissDialogue.kt @@ -12,11 +12,11 @@ import org.rs09.consts.NPCs */ @Initializable -class AlvissDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class AlvissDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - npc(core.game.dialogue.FacialExpression.OLD_DEFAULT,"Good day, sir.").also { stage = 0 } + npc(FacialExpression.OLD_DEFAULT,"Good day, sir.").also { stage = 0 } return true } @@ -28,26 +28,26 @@ class AlvissDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin 1 -> when(buttonId){ 1 -> { - player(core.game.dialogue.FacialExpression.FRIENDLY, "What are you doing down here?").also { stage = 10 } + player(FacialExpression.FRIENDLY, "What are you doing down here?").also { stage = 10 } } 2 -> { - player(core.game.dialogue.FacialExpression.NEUTRAL, "Good day.").also { stage = 99 } + player(FacialExpression.NEUTRAL, "Good day.").also { stage = 99 } } } 10 -> { - npc(core.game.dialogue.FacialExpression.OLD_DEFAULT,"I'm waiting for my shift, of course.", + npc(FacialExpression.OLD_DEFAULT,"I'm waiting for my shift, of course.", "We can't dig all the time, you know.").also { stage++ } } 11 -> { - npc(core.game.dialogue.FacialExpression.OLD_DEFAULT," I'm also researching the links between the ", + npc(FacialExpression.OLD_DEFAULT," I'm also researching the links between the ", "Fremenniks and the Dwarves.").also { stage++ } } 12 -> { - npc(core.game.dialogue.FacialExpression.OLD_DEFAULT,"I've found that we have some mythology in common.").also { stage = 99 } + npc(FacialExpression.OLD_DEFAULT,"I've found that we have some mythology in common.").also { stage = 99 } } 99 -> end() @@ -55,7 +55,7 @@ class AlvissDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return AlvissDialogue(player) } diff --git a/Server/src/main/content/region/misc/miscellania/dialogue/DerrikDialogue.kt b/Server/src/main/content/region/misc/miscellania/dialogue/DerrikDialogue.kt index cefd28535..4e55e6f08 100644 --- a/Server/src/main/content/region/misc/miscellania/dialogue/DerrikDialogue.kt +++ b/Server/src/main/content/region/misc/miscellania/dialogue/DerrikDialogue.kt @@ -13,11 +13,11 @@ import org.rs09.consts.NPCs */ @Initializable -class DerrikDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class DerrikDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - npc(core.game.dialogue.FacialExpression.FRIENDLY,"Good day, Sir. Can I help you with anything?").also { stage = 0 } + npc(FacialExpression.FRIENDLY,"Good day, Sir. Can I help you with anything?").also { stage = 0 } return true } @@ -30,15 +30,15 @@ class DerrikDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin 1 -> when(buttonId){ 1 -> { - player(core.game.dialogue.FacialExpression.ASKING, "Can I use your anvil?").also { stage = 5 } + player(FacialExpression.ASKING, "Can I use your anvil?").also { stage = 5 } } 2 -> { - player(core.game.dialogue.FacialExpression.NEUTRAL, "Nothing, thanks.").also { stage = 99 } + player(FacialExpression.NEUTRAL, "Nothing, thanks.").also { stage = 99 } } } 5 -> { - npc(core.game.dialogue.FacialExpression.NEUTRAL, "You may.").also { stage = 99 } + npc(FacialExpression.NEUTRAL, "You may.").also { stage = 99 } } 99 -> end() @@ -46,7 +46,7 @@ class DerrikDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return DerrikDialogue(player) } diff --git a/Server/src/main/content/region/misc/miscellania/dialogue/DonalDialogue.kt b/Server/src/main/content/region/misc/miscellania/dialogue/DonalDialogue.kt index 77447aa88..a7aafe588 100644 --- a/Server/src/main/content/region/misc/miscellania/dialogue/DonalDialogue.kt +++ b/Server/src/main/content/region/misc/miscellania/dialogue/DonalDialogue.kt @@ -12,37 +12,37 @@ import org.rs09.consts.NPCs */ @Initializable -class DonalDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class DonalDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - npc(core.game.dialogue.FacialExpression.OLD_DEFAULT,"What do you want?").also { stage = 0 } + npc(FacialExpression.OLD_DEFAULT,"What do you want?").also { stage = 0 } return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ 0 -> { - player(core.game.dialogue.FacialExpression.THINKING, "Just wondering if you were still here.").also { stage++ } + player(FacialExpression.THINKING, "Just wondering if you were still here.").also { stage++ } } 1 -> { - npc(core.game.dialogue.FacialExpression.OLD_DEFAULT, "Of course I'm still here.").also { stage++ } + npc(FacialExpression.OLD_DEFAULT, "Of course I'm still here.").also { stage++ } } 2 -> { - npc(core.game.dialogue.FacialExpression.OLD_DISTRESSED, "I'm not going near that crack in the wall again.").also { stage++ } + npc(FacialExpression.OLD_DISTRESSED, "I'm not going near that crack in the wall again.").also { stage++ } } 3 -> { - npc(core.game.dialogue.FacialExpression.OLD_DISTRESSED, "Rock falls and so on are fine, ", "but sea monsters in caves - never!").also { stage = 99 } + npc(FacialExpression.OLD_DISTRESSED, "Rock falls and so on are fine, ", "but sea monsters in caves - never!").also { stage = 99 } } 99 -> end() } return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return DonalDialogue(player) } diff --git a/Server/src/main/content/region/misc/miscellania/dialogue/FerdDialogue.kt b/Server/src/main/content/region/misc/miscellania/dialogue/FerdDialogue.kt index 22c7cf8f0..92d018524 100644 --- a/Server/src/main/content/region/misc/miscellania/dialogue/FerdDialogue.kt +++ b/Server/src/main/content/region/misc/miscellania/dialogue/FerdDialogue.kt @@ -12,51 +12,51 @@ import org.rs09.consts.NPCs */ @Initializable -class FerdDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class FerdDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - npc(core.game.dialogue.FacialExpression.OLD_DEFAULT,"Good day, sir.").also { stage = 0 } + npc(FacialExpression.OLD_DEFAULT,"Good day, sir.").also { stage = 0 } return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ 0 -> { - player(core.game.dialogue.FacialExpression.THINKING, "What are you doing down here?.").also { stage++ } + player(FacialExpression.THINKING, "What are you doing down here?.").also { stage++ } } 1 -> { - npc(core.game.dialogue.FacialExpression.OLD_DEFAULT, "Shoring up the walls.").also { stage++ } + npc(FacialExpression.OLD_DEFAULT, "Shoring up the walls.").also { stage++ } } 2 -> { - player(core.game.dialogue.FacialExpression.ASKING, "What does that do?").also { stage++ } + player(FacialExpression.ASKING, "What does that do?").also { stage++ } } 3 -> { - npc(core.game.dialogue.FacialExpression.OLD_DEFAULT, "Stops them falling down.").also { stage = 99 } + npc(FacialExpression.OLD_DEFAULT, "Stops them falling down.").also { stage = 99 } } 4 -> { - player(core.game.dialogue.FacialExpression.ASKING, "Oh, I see.").also { stage++ } + player(FacialExpression.ASKING, "Oh, I see.").also { stage++ } } 5 -> { - npc(core.game.dialogue.FacialExpression.OLD_NOT_INTERESTED, "Aye.", + npc(FacialExpression.OLD_NOT_INTERESTED, "Aye.", "If you want to chatter, you'd better talk to ", "Thorodin over there. I'm working.").also { stage = 99 } } 6 -> { - player(core.game.dialogue.FacialExpression.ASKING, "Okay then.").also { stage++ } + player(FacialExpression.ASKING, "Okay then.").also { stage++ } } 99 -> end() } return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return FerdDialogue(player) } diff --git a/Server/src/main/content/region/misc/miscellania/dialogue/FinnDialogue.kt b/Server/src/main/content/region/misc/miscellania/dialogue/FinnDialogue.kt index df03a1062..f9c813a53 100644 --- a/Server/src/main/content/region/misc/miscellania/dialogue/FinnDialogue.kt +++ b/Server/src/main/content/region/misc/miscellania/dialogue/FinnDialogue.kt @@ -13,11 +13,11 @@ import org.rs09.consts.NPCs */ @Initializable -class FinnDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class FinnDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - npc(core.game.dialogue.FacialExpression.FRIENDLY,"Can I help you, your Royal Highness?").also { stage = 0 } + npc(FacialExpression.FRIENDLY,"Can I help you, your Royal Highness?").also { stage = 0 } stage = 0 return true } @@ -30,15 +30,15 @@ class FinnDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(p 1 -> when(buttonId){ 1 -> { - player(core.game.dialogue.FacialExpression.ASKING, "Yes please. What are you selling?").also { stage = 10 } + player(FacialExpression.ASKING, "Yes please. What are you selling?").also { stage = 10 } } 2 -> { - player(core.game.dialogue.FacialExpression.NEUTRAL, "No thanks.").also { stage = 99 } + player(FacialExpression.NEUTRAL, "No thanks.").also { stage = 99 } } 3 -> { - player(core.game.dialogue.FacialExpression.ASKING, "What's it like living down here?").also { stage = 20 } + player(FacialExpression.ASKING, "What's it like living down here?").also { stage = 20 } } } @@ -47,7 +47,7 @@ class FinnDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(p } 20 -> { - npc(core.game.dialogue.FacialExpression.HALF_WORRIED, "A lot drier in the winter than it is above ground.").also { stage = 99 } + npc(FacialExpression.HALF_WORRIED, "A lot drier in the winter than it is above ground.").also { stage = 99 } } 99 -> { @@ -58,7 +58,7 @@ class FinnDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(p return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return FinnDialogue(player) } 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 18d23fee0..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,20 +7,21 @@ 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 */ @Initializable -class FishmongerMiscDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +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")) { - npcl(core.game.dialogue.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 } + 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(core.game.dialogue.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 } + 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 } } return true } @@ -32,7 +33,7 @@ class FishmongerMiscDialogue(player: Player? = null) : core.game.dialogue.Dialog return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return FishmongerMiscDialogue(player) } 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 9b4fcfcb1..6320d5ad1 100644 --- a/Server/src/main/content/region/misc/miscellania/dialogue/FlowerGirlDialogue.kt +++ b/Server/src/main/content/region/misc/miscellania/dialogue/FlowerGirlDialogue.kt @@ -15,36 +15,36 @@ import org.rs09.consts.NPCs */ @Initializable -class FlowerGirlDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class FlowerGirlDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC //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 } } */ - npc(core.game.dialogue.FacialExpression.NEUTRAL, "Hello.").also { stage = 1 } + npc(FacialExpression.NEUTRAL, "Hello.").also { stage = 1 } return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ 1 -> { - player(core.game.dialogue.FacialExpression.ASKING, "Good day. What are you doing?").also { stage++ } + player(FacialExpression.ASKING, "Good day. What are you doing?").also { stage++ } } 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++ } } */ - npc(core.game.dialogue.FacialExpression.NEUTRAL, "I'm selling flowers, 15gp for three. Would you like some?").also { stage++ } + npc(FacialExpression.NEUTRAL, "I'm selling flowers, 15gp for three. Would you like some?").also { stage++ } } 3 -> { @@ -54,15 +54,15 @@ class FlowerGirlDialogue(player: Player? = null) : core.game.dialogue.DialoguePl 4 -> when(buttonId){ 1 -> { if (player.inventory.contains(995,15)) { - npc(core.game.dialogue.FacialExpression.HAPPY, "Thank you! Here you go.").also { stage = 99 } + npc(FacialExpression.HAPPY, "Thank you! Here you go.").also { stage = 99 } player.inventory.remove(Item(995, 15)) addItemOrDrop(player, 2460, 1) } else { - player(core.game.dialogue.FacialExpression.HALF_THINKING, "I'm sorry, but I don't have 15gp.").also { stage = 99 } + player(FacialExpression.HALF_THINKING, "I'm sorry, but I don't have 15gp.").also { stage = 99 } } } 2 -> { - player(core.game.dialogue.FacialExpression.NEUTRAL, "No, thank you.").also { stage = 99 } + player(FacialExpression.NEUTRAL, "No, thank you.").also { stage = 99 } } } @@ -71,7 +71,7 @@ class FlowerGirlDialogue(player: Player? = null) : core.game.dialogue.DialoguePl return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return FlowerGirlDialogue(player) } diff --git a/Server/src/main/content/region/misc/miscellania/dialogue/FullangrDialogue.kt b/Server/src/main/content/region/misc/miscellania/dialogue/FullangrDialogue.kt index 0ab58465e..c8e0530e8 100644 --- a/Server/src/main/content/region/misc/miscellania/dialogue/FullangrDialogue.kt +++ b/Server/src/main/content/region/misc/miscellania/dialogue/FullangrDialogue.kt @@ -12,11 +12,11 @@ import org.rs09.consts.NPCs */ @Initializable -class FullangrDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class FullangrDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - npc(core.game.dialogue.FacialExpression.OLD_DEFAULT,"Good day, sir.").also { stage = 0 } + npc(FacialExpression.OLD_DEFAULT,"Good day, sir.").also { stage = 0 } return true } @@ -28,16 +28,16 @@ class FullangrDialogue(player: Player? = null) : core.game.dialogue.DialoguePlug 1 -> when(buttonId){ 1 -> { - player(core.game.dialogue.FacialExpression.FRIENDLY, "What are you doing down here?").also { stage = 10 } + player(FacialExpression.FRIENDLY, "What are you doing down here?").also { stage = 10 } } 2 -> { - player(core.game.dialogue.FacialExpression.NEUTRAL, "Good day.").also { stage = 99 } + player(FacialExpression.NEUTRAL, "Good day.").also { stage = 99 } } } 10 -> { - npc(core.game.dialogue.FacialExpression.OLD_DEFAULT,"I'm working on the digging, of course.", + npc(FacialExpression.OLD_DEFAULT,"I'm working on the digging, of course.", "It's a small excavation, so only two of us ", "can work on it at a time.").also { stage = 99 } } @@ -47,7 +47,7 @@ class FullangrDialogue(player: Player? = null) : core.game.dialogue.DialoguePlug return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return FullangrDialogue(player) } diff --git a/Server/src/main/content/region/misc/miscellania/dialogue/GreengrocerMiscDialogue.kt b/Server/src/main/content/region/misc/miscellania/dialogue/GreengrocerMiscDialogue.kt index 74ff66ee0..789d09154 100644 --- a/Server/src/main/content/region/misc/miscellania/dialogue/GreengrocerMiscDialogue.kt +++ b/Server/src/main/content/region/misc/miscellania/dialogue/GreengrocerMiscDialogue.kt @@ -13,11 +13,11 @@ import org.rs09.consts.NPCs */ @Initializable -class GreengrocerMiscDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class GreengrocerMiscDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - npc(core.game.dialogue.FacialExpression.FRIENDLY,"Welcome, Sir.", + npc(FacialExpression.FRIENDLY,"Welcome, Sir.", "I sell only the finest and freshest vegetables!").also { stage = 0 } return true } @@ -31,7 +31,7 @@ class GreengrocerMiscDialogue(player: Player? = null) : core.game.dialogue.Dialo return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return GreengrocerMiscDialogue(player) } diff --git a/Server/src/main/content/region/misc/miscellania/dialogue/HallaDialogue.kt b/Server/src/main/content/region/misc/miscellania/dialogue/HallaDialogue.kt index 93acdbee3..ca55da312 100644 --- a/Server/src/main/content/region/misc/miscellania/dialogue/HallaDialogue.kt +++ b/Server/src/main/content/region/misc/miscellania/dialogue/HallaDialogue.kt @@ -12,18 +12,18 @@ import org.rs09.consts.NPCs */ @Initializable -class HallaDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class HallaDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - npc(core.game.dialogue.FacialExpression.FRIENDLY,"Welcome to Miscellania's first clothing store!", "We sell clothing made especially for Fremenniks", "and Dwarves.").also { stage = 0 } + npc(FacialExpression.FRIENDLY,"Welcome to Miscellania's first clothing store!", "We sell clothing made especially for Fremenniks", "and Dwarves.").also { stage = 0 } return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ 0 -> { - npc(core.game.dialogue.FacialExpression.ASKING, "Can I help you with anything, your Royal Highness?").also { stage++ } + npc(FacialExpression.ASKING, "Can I help you with anything, your Royal Highness?").also { stage++ } } 1 -> { @@ -32,15 +32,15 @@ class HallaDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin( 2 -> when(buttonId){ 1 -> { - player(core.game.dialogue.FacialExpression.FRIENDLY, "I'd like to look at what you have for sale.").also { stage = 10 } + player(FacialExpression.FRIENDLY, "I'd like to look at what you have for sale.").also { stage = 10 } } 2 -> { - player(core.game.dialogue.FacialExpression.NEUTRAL, "No thank you, I'm fine.").also { stage = 99 } + player(FacialExpression.NEUTRAL, "No thank you, I'm fine.").also { stage = 99 } } 3 -> { - player(core.game.dialogue.FacialExpression.ASKING, "What's it like living down here?").also { stage = 20 } + player(FacialExpression.ASKING, "What's it like living down here?").also { stage = 20 } } } @@ -49,10 +49,10 @@ class HallaDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin( } 20 -> { - npc(core.game.dialogue.FacialExpression.NEUTRAL, "It's very spacious down here.", "One of the dwarves said that the caves go on for miles!").also { stage++ } + npc(FacialExpression.NEUTRAL, "It's very spacious down here.", "One of the dwarves said that the caves go on for miles!").also { stage++ } } 21 -> { - npc(core.game.dialogue.FacialExpression.NEUTRAL, "The only problem I find is that the lighting's not very good,", "which means I make mistakes when cutting cloth.").also { stage = 99 } + npc(FacialExpression.NEUTRAL, "The only problem I find is that the lighting's not very good,", "which means I make mistakes when cutting cloth.").also { stage = 99 } } 99 -> end() @@ -60,7 +60,7 @@ class HallaDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin( return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return HallaDialogue(player) } diff --git a/Server/src/main/content/region/misc/miscellania/dialogue/JariDialogue.kt b/Server/src/main/content/region/misc/miscellania/dialogue/JariDialogue.kt index b765c7fff..94e6c2def 100644 --- a/Server/src/main/content/region/misc/miscellania/dialogue/JariDialogue.kt +++ b/Server/src/main/content/region/misc/miscellania/dialogue/JariDialogue.kt @@ -12,11 +12,11 @@ import org.rs09.consts.NPCs */ @Initializable -class JariDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class JariDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - npc(core.game.dialogue.FacialExpression.OLD_DEFAULT,"Good day, sir.").also { stage = 0 } + npc(FacialExpression.OLD_DEFAULT,"Good day, sir.").also { stage = 0 } return true } @@ -28,20 +28,20 @@ class JariDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(p 1 -> when(buttonId){ 1 -> { - player(core.game.dialogue.FacialExpression.FRIENDLY, "What are you doing down here?").also { stage = 10 } + player(FacialExpression.FRIENDLY, "What are you doing down here?").also { stage = 10 } } 2 -> { - player(core.game.dialogue.FacialExpression.NEUTRAL, "Good day.").also { stage = 99 } + player(FacialExpression.NEUTRAL, "Good day.").also { stage = 99 } } } 10 -> { - npc(core.game.dialogue.FacialExpression.OLD_DEFAULT,"I'm waiting to work on the digging.").also { stage++ } + npc(FacialExpression.OLD_DEFAULT,"I'm waiting to work on the digging.").also { stage++ } } 11 -> { - npc(core.game.dialogue.FacialExpression.OLD_HAPPY,"It's the first excavation I've worked on, ", + npc(FacialExpression.OLD_HAPPY,"It's the first excavation I've worked on, ", "and I'm looking forward to it.").also { stage = 99 } } @@ -50,7 +50,7 @@ class JariDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(p return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return JariDialogue(player) } diff --git a/Server/src/main/content/region/misc/miscellania/dialogue/MiscCitizenGoodDayDialogue.kt b/Server/src/main/content/region/misc/miscellania/dialogue/MiscCitizenGoodDayDialogue.kt index ea3f56294..72247144d 100644 --- a/Server/src/main/content/region/misc/miscellania/dialogue/MiscCitizenGoodDayDialogue.kt +++ b/Server/src/main/content/region/misc/miscellania/dialogue/MiscCitizenGoodDayDialogue.kt @@ -12,25 +12,25 @@ import org.rs09.consts.NPCs */ @Initializable -class MiscCitizenGoodDayDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class MiscCitizenGoodDayDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - player(core.game.dialogue.FacialExpression.FRIENDLY,"Hello.").also { stage = 0 } + player(FacialExpression.FRIENDLY,"Hello.").also { stage = 0 } return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ 0 -> { - npc(core.game.dialogue.FacialExpression.FRIENDLY, "Good day, Your Royal Highness.").also { stage = 99 } + npc(FacialExpression.FRIENDLY, "Good day, Your Royal Highness.").also { stage = 99 } } 99 -> end() } return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return MiscCitizenGoodDayDialogue(player) } diff --git a/Server/src/main/content/region/misc/miscellania/dialogue/OsvaldDialogue.kt b/Server/src/main/content/region/misc/miscellania/dialogue/OsvaldDialogue.kt index 82e271b32..ad21fb7ea 100644 --- a/Server/src/main/content/region/misc/miscellania/dialogue/OsvaldDialogue.kt +++ b/Server/src/main/content/region/misc/miscellania/dialogue/OsvaldDialogue.kt @@ -12,18 +12,18 @@ import org.rs09.consts.NPCs */ @Initializable -class OsvaldDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class OsvaldDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - npc(core.game.dialogue.FacialExpression.FRIENDLY,"Welcome to the Miscellania food store.","We've only opened recently.").also { stage = 0 } + npc(FacialExpression.FRIENDLY,"Welcome to the Miscellania food store.","We've only opened recently.").also { stage = 0 } return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ 0-> { - npc(core.game.dialogue.FacialExpression.NEUTRAL, "Would you like to buy anything,", + npc(FacialExpression.NEUTRAL, "Would you like to buy anything,", "your Royal Highness?").also { stage++ } } @@ -35,15 +35,15 @@ class OsvaldDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin 2 -> when(buttonId){ 1 -> { - player(core.game.dialogue.FacialExpression.ASKING, "Could you show me what you have for sale?").also { stage = 10 } + player(FacialExpression.ASKING, "Could you show me what you have for sale?").also { stage = 10 } } 2 -> { - player(core.game.dialogue.FacialExpression.NEUTRAL, "No thank you, I don't need food just now.").also { end() } + player(FacialExpression.NEUTRAL, "No thank you, I don't need food just now.").also { end() } } 3 -> { - player(core.game.dialogue.FacialExpression.ASKING, "What's it like living down here?").also { stage = 20 } + player(FacialExpression.ASKING, "What's it like living down here?").also { stage = 20 } } } @@ -52,7 +52,7 @@ class OsvaldDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin } 20 -> { - npc(core.game.dialogue.FacialExpression.FRIENDLY, "The town's thriving.", + npc(FacialExpression.FRIENDLY, "The town's thriving.", "I'm sure it'll soon be as busy as Rellekka!").also { stage = 99 } } @@ -63,7 +63,7 @@ class OsvaldDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return OsvaldDialogue(player) } diff --git a/Server/src/main/content/region/misc/miscellania/dialogue/RunaDialogue.kt b/Server/src/main/content/region/misc/miscellania/dialogue/RunaDialogue.kt index fb8a6b062..8a13bdabe 100644 --- a/Server/src/main/content/region/misc/miscellania/dialogue/RunaDialogue.kt +++ b/Server/src/main/content/region/misc/miscellania/dialogue/RunaDialogue.kt @@ -12,22 +12,22 @@ import org.rs09.consts.NPCs */ @Initializable -class RunaDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class RunaDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - npc(core.game.dialogue.FacialExpression.FRIENDLY,"Would you like to try some fine Miscellanian ale,", "your Royal Highness?").also { stage = 0 } + npc(FacialExpression.FRIENDLY,"Would you like to try some fine Miscellanian ale,", "your Royal Highness?").also { stage = 0 } return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ 0 -> { - npc(core.game.dialogue.FacialExpression.ASKING, "Well I say Miscellanian, but it's actually brewed", "on the mainland.").also { stage++ } + npc(FacialExpression.ASKING, "Well I say Miscellanian, but it's actually brewed", "on the mainland.").also { stage++ } } 1 -> { - npc(core.game.dialogue.FacialExpression.FRIENDLY, "Would you like to try some anyway?").also { stage++ } + npc(FacialExpression.FRIENDLY, "Would you like to try some anyway?").also { stage++ } } 2 -> { @@ -36,15 +36,15 @@ class RunaDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(p 3 -> when(buttonId){ 1 -> { - player(core.game.dialogue.FacialExpression.ASKING, "Yes please.").also { stage = 10 } + player(FacialExpression.ASKING, "Yes please.").also { stage = 10 } } 2 -> { - player(core.game.dialogue.FacialExpression.NEUTRAL, "No thank you.").also { stage = 99 } + player(FacialExpression.NEUTRAL, "No thank you.").also { stage = 99 } } 3 -> { - player(core.game.dialogue.FacialExpression.ASKING, "What's it like living down here?").also { stage = 20 } + player(FacialExpression.ASKING, "What's it like living down here?").also { stage = 20 } } } @@ -53,11 +53,11 @@ class RunaDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(p } 20 -> { - npc(core.game.dialogue.FacialExpression.HALF_WORRIED, "Business is booming!").also { stage++ } + npc(FacialExpression.HALF_WORRIED, "Business is booming!").also { stage++ } } 21 -> { - npc(core.game.dialogue.FacialExpression.HALF_WORRIED, "Now, if only I hadn't taken a loss to the beer I sold", "to those teenagers.").also { end() } + npc(FacialExpression.HALF_WORRIED, "Now, if only I hadn't taken a loss to the beer I sold", "to those teenagers.").also { end() } } 99 -> { @@ -67,7 +67,7 @@ class RunaDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(p return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return RunaDialogue(player) } diff --git a/Server/src/main/content/region/misc/miscellania/dialogue/ThorodinDialogue.kt b/Server/src/main/content/region/misc/miscellania/dialogue/ThorodinDialogue.kt index c34711819..6cf33d5d5 100644 --- a/Server/src/main/content/region/misc/miscellania/dialogue/ThorodinDialogue.kt +++ b/Server/src/main/content/region/misc/miscellania/dialogue/ThorodinDialogue.kt @@ -12,11 +12,11 @@ import org.rs09.consts.NPCs */ @Initializable -class ThorodinDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class ThorodinDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - npc(core.game.dialogue.FacialExpression.OLD_DEFAULT,"Good day, sir.").also { stage = 0 } + npc(FacialExpression.OLD_DEFAULT,"Good day, sir.").also { stage = 0 } return true } @@ -28,35 +28,35 @@ class ThorodinDialogue(player: Player? = null) : core.game.dialogue.DialoguePlug 1 -> when(buttonId){ 1 -> { - player(core.game.dialogue.FacialExpression.FRIENDLY, "What are you doing down here?").also { stage = 10 } + player(FacialExpression.FRIENDLY, "What are you doing down here?").also { stage = 10 } } 2 -> { - player(core.game.dialogue.FacialExpression.NEUTRAL, "Good day.").also { stage = 99 } + player(FacialExpression.NEUTRAL, "Good day.").also { stage = 99 } } } 10 -> { - npc(core.game.dialogue.FacialExpression.OLD_DEFAULT,"We're extending the cave so more people can live in it.", + npc(FacialExpression.OLD_DEFAULT,"We're extending the cave so more people can live in it.", "These Miscellanians aren't so bad.", "They appreciate the benefits of living underground.").also { stage++ } } 11 -> { - player(core.game.dialogue.FacialExpression.ASKING,"...such as?").also { stage++ } + player(FacialExpression.ASKING,"...such as?").also { stage++ } } 12 -> { - npc(core.game.dialogue.FacialExpression.OLD_DEFAULT,"Not getting rained on, for example.", + npc(FacialExpression.OLD_DEFAULT,"Not getting rained on, for example.", "Did you do anything about that monster Donal", "was talking about?").also { stage++ } } 13 -> { - player(core.game.dialogue.FacialExpression.FRIENDLY,"It's been taken care of.").also { stage++ } + player(FacialExpression.FRIENDLY,"It's been taken care of.").also { stage++ } } 14 -> { - npc(core.game.dialogue.FacialExpression.OLD_HAPPY,"Glad to hear it.", + npc(FacialExpression.OLD_HAPPY,"Glad to hear it.", "Now we can get on with excavating.").also { stage = 99 } } @@ -65,7 +65,7 @@ class ThorodinDialogue(player: Player? = null) : core.game.dialogue.DialoguePlug return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return ThorodinDialogue(player) } diff --git a/Server/src/main/content/region/misc/mosle/dialogue/CharleyDialogue.kt b/Server/src/main/content/region/misc/mosle/dialogue/CharleyDialogue.kt index ab1ee240f..9461b96a2 100644 --- a/Server/src/main/content/region/misc/mosle/dialogue/CharleyDialogue.kt +++ b/Server/src/main/content/region/misc/mosle/dialogue/CharleyDialogue.kt @@ -13,15 +13,15 @@ import org.rs09.consts.NPCs */ @Initializable -class CharleyDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class CharleyDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC if (player.inventory.contains(Items.BOOK_O_PIRACY_7144, 1)) { - npc(core.game.dialogue.FacialExpression.FRIENDLY, "I got fish, you got gold?").also { stage = 10 } + npc(FacialExpression.FRIENDLY, "I got fish, you got gold?").also { stage = 10 } } else { - player(core.game.dialogue.FacialExpression.FRIENDLY, "Hello!").also { stage = 0 } + player(FacialExpression.FRIENDLY, "Hello!").also { stage = 0 } } return true @@ -29,37 +29,37 @@ class CharleyDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugi override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 0 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Arr? Be ye wantin' te go on account with our gang o' fillibusters?").also { stage++ } - 1 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "The powder monkey be takin' a caulk after gettin' rowdy on bumboo, so there be plenty of room for ye.").also { stage++ } - 2 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "Riiiiight...").also { stage++ } - 3 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "I'll just be over here if you need me.").also { stage = 99 } + 0 -> npcl(FacialExpression.FRIENDLY, "Arr? Be ye wantin' te go on account with our gang o' fillibusters?").also { stage++ } + 1 -> npcl(FacialExpression.FRIENDLY, "The powder monkey be takin' a caulk after gettin' rowdy on bumboo, so there be plenty of room for ye.").also { stage++ } + 2 -> player(FacialExpression.FRIENDLY, "Riiiiight...").also { stage++ } + 3 -> player(FacialExpression.FRIENDLY, "I'll just be over here if you need me.").also { stage = 99 } 10 -> options("Yes.", "Yes, but I don't want your fish.", "What happened to your legs?").also { stage++ } 11 -> when(buttonId) { - 1 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "Yes.").also { stage = 80 } - 2 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "Yes, but I don't want your fish.").also { stage = 20 } - 3 -> player(core.game.dialogue.FacialExpression.HALF_ASKING, "What happened to your legs?").also { stage = 30 } + 1 -> player(FacialExpression.FRIENDLY, "Yes.").also { stage = 80 } + 2 -> player(FacialExpression.FRIENDLY, "Yes, but I don't want your fish.").also { stage = 20 } + 3 -> player(FacialExpression.HALF_ASKING, "What happened to your legs?").also { stage = 30 } } - 20 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Then what are ye doin' in a fish shop? Looking fer work?").also { stage++ } - 21 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "Possibly, do you have any quests?").also { stage++ } - 22 -> npcl(core.game.dialogue.FacialExpression.HALF_ASKING, "I dunno, I haven't gone through the last catch yet. What sort of a fish is it?").also { stage++ } - 23 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "A quest isn't a type of fish!").also { stage++ } - 24 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Then I don't have any, and yer wastin' my time!").also { stage = 99 } + 20 -> npcl(FacialExpression.FRIENDLY, "Then what are ye doin' in a fish shop? Looking fer work?").also { stage++ } + 21 -> player(FacialExpression.FRIENDLY, "Possibly, do you have any quests?").also { stage++ } + 22 -> npcl(FacialExpression.HALF_ASKING, "I dunno, I haven't gone through the last catch yet. What sort of a fish is it?").also { stage++ } + 23 -> player(FacialExpression.FRIENDLY, "A quest isn't a type of fish!").also { stage++ } + 24 -> npcl(FacialExpression.FRIENDLY, "Then I don't have any, and yer wastin' my time!").also { stage = 99 } - 30 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Ye wanna know what happened to my legs?").also { stage++ } - 31 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Yer too much of a lilly-livered, hat wearin' landlubber to know what happened to my legs!").also { stage++ } - 32 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY, "No I'm not! I've seen some freaky stuff! I can take it!").also { stage++ } - 33 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "All right, lad, since yer so insistent, I'll tell ye.").also { stage++ } - 34 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "See, I was clingin' onto a barrel, me ship havin' just had an encounter with this albatross.").also { stage++ } - 35 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "The sea was thrashin' and wild, but not so wild that I didn't see the fins of some sharks closin' in on me.").also { stage++ } - 36 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "I managed to yank a sliver of wood from the barrel just as one of them grabbed me from below, but I slipped down the things throat by about two feet before I managed te kill it.").also { stage++ } - 37 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "How did you survive?").also { stage++ } - 38 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "A passin' ship saw the sharks and knew there would be survivors in the water. They sent a longboat and picked me up, but not before the sharks had taken off my legs.").also { stage++ } - 39 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "And that lad is why they call me two feet Charley, because they found me jammed two feet down a shark's throat.").also { stage++ } - 40 -> player(core.game.dialogue.FacialExpression.DISGUSTED, "I think I'm gonna be sick...").also { stage++ } - 41 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "I knew ye couldn't handle the truth!").also { stage = 99 } + 30 -> npcl(FacialExpression.FRIENDLY, "Ye wanna know what happened to my legs?").also { stage++ } + 31 -> npcl(FacialExpression.FRIENDLY, "Yer too much of a lilly-livered, hat wearin' landlubber to know what happened to my legs!").also { stage++ } + 32 -> playerl(FacialExpression.FRIENDLY, "No I'm not! I've seen some freaky stuff! I can take it!").also { stage++ } + 33 -> npcl(FacialExpression.FRIENDLY, "All right, lad, since yer so insistent, I'll tell ye.").also { stage++ } + 34 -> npcl(FacialExpression.FRIENDLY, "See, I was clingin' onto a barrel, me ship havin' just had an encounter with this albatross.").also { stage++ } + 35 -> npcl(FacialExpression.FRIENDLY, "The sea was thrashin' and wild, but not so wild that I didn't see the fins of some sharks closin' in on me.").also { stage++ } + 36 -> npcl(FacialExpression.FRIENDLY, "I managed to yank a sliver of wood from the barrel just as one of them grabbed me from below, but I slipped down the things throat by about two feet before I managed te kill it.").also { stage++ } + 37 -> player(FacialExpression.FRIENDLY, "How did you survive?").also { stage++ } + 38 -> npcl(FacialExpression.FRIENDLY, "A passin' ship saw the sharks and knew there would be survivors in the water. They sent a longboat and picked me up, but not before the sharks had taken off my legs.").also { stage++ } + 39 -> npcl(FacialExpression.FRIENDLY, "And that lad is why they call me two feet Charley, because they found me jammed two feet down a shark's throat.").also { stage++ } + 40 -> player(FacialExpression.DISGUSTED, "I think I'm gonna be sick...").also { stage++ } + 41 -> npcl(FacialExpression.FRIENDLY, "I knew ye couldn't handle the truth!").also { stage = 99 } 80 -> end().also { npc.openShop(player) } 99 -> end() @@ -67,7 +67,7 @@ class CharleyDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugi return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return CharleyDialogue(player) } diff --git a/Server/src/main/content/region/misc/piratecove/dialogue/BeefyBurnsDialogue.kt b/Server/src/main/content/region/misc/piratecove/dialogue/BeefyBurnsDialogue.kt index 0744cdc74..9ee5b708c 100644 --- a/Server/src/main/content/region/misc/piratecove/dialogue/BeefyBurnsDialogue.kt +++ b/Server/src/main/content/region/misc/piratecove/dialogue/BeefyBurnsDialogue.kt @@ -12,28 +12,28 @@ import org.rs09.consts.NPCs */ @Initializable -class BeefyBurnsDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class BeefyBurnsDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - player(core.game.dialogue.FacialExpression.HALF_ASKING, "What are you cooking?").also { stage = 0 } + player(FacialExpression.HALF_ASKING, "What are you cooking?").also { stage = 0 } return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 0 -> npc(core.game.dialogue.FacialExpression.LAUGH, "My speciality! What else could I be cooking?").also { stage++ } - 1 -> player(core.game.dialogue.FacialExpression.THINKING, "Ok, and your speciality is...?").also { stage++ } - 2 -> npcl(core.game.dialogue.FacialExpression.LAUGH, "Boiled shark guts with a hint of rosemary and a dash of squid ink.").also { stage++ } - 3 -> player(core.game.dialogue.FacialExpression.FRIENDLY,"I think I'll stick to making my own food.").also { stage++ } - 4 -> npc(core.game.dialogue.FacialExpression.FRIENDLY, "Your loss!").also { stage = 99 } + 0 -> npc(FacialExpression.LAUGH, "My speciality! What else could I be cooking?").also { stage++ } + 1 -> player(FacialExpression.THINKING, "Ok, and your speciality is...?").also { stage++ } + 2 -> npcl(FacialExpression.LAUGH, "Boiled shark guts with a hint of rosemary and a dash of squid ink.").also { stage++ } + 3 -> player(FacialExpression.FRIENDLY,"I think I'll stick to making my own food.").also { stage++ } + 4 -> npc(FacialExpression.FRIENDLY, "Your loss!").also { stage = 99 } 99 -> end() } return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return BeefyBurnsDialogue(player) } diff --git a/Server/src/main/content/region/misc/piratecove/dialogue/DaveyBoyDialogue.kt b/Server/src/main/content/region/misc/piratecove/dialogue/DaveyBoyDialogue.kt index 70e35c311..756915ac2 100644 --- a/Server/src/main/content/region/misc/piratecove/dialogue/DaveyBoyDialogue.kt +++ b/Server/src/main/content/region/misc/piratecove/dialogue/DaveyBoyDialogue.kt @@ -12,13 +12,13 @@ import org.rs09.consts.NPCs */ @Initializable -class DaveyBoyDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class DaveyBoyDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC when ((1..2).random()) { - 1 -> playerl(core.game.dialogue.FacialExpression.HALF_ASKING, "What does it take to become first mate on a ship?").also { stage = 0 } - 2 -> npcl(core.game.dialogue.FacialExpression.ANNOYED, "It is customary when stowing away on a vessel to not introduce yourself to the Captains First Mate, oh foolish one.").also { stage = 10 } + 1 -> playerl(FacialExpression.HALF_ASKING, "What does it take to become first mate on a ship?").also { stage = 0 } + 2 -> npcl(FacialExpression.ANNOYED, "It is customary when stowing away on a vessel to not introduce yourself to the Captains First Mate, oh foolish one.").also { stage = 10 } } return true @@ -26,22 +26,22 @@ class DaveyBoyDialogue(player: Player? = null) : core.game.dialogue.DialoguePlug override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 0 -> npcl(core.game.dialogue.FacialExpression.LAUGH, "Good question. We have a diplomatic consession at the turn of the financial year. Said pirate is chosen should the existing mate be absent without leave.").also { stage++ } - 1 -> playerl(core.game.dialogue.FacialExpression.THINKING, "I had no idea. I always figured it was all about popularity.").also { stage++ } - 2 -> npc(core.game.dialogue.FacialExpression.FRIENDLY, "It is. I'm just pulling your leg.").also { stage++ } - 3 -> player(core.game.dialogue.FacialExpression.FRIENDLY,"Oh....").also { stage = 99 } + 0 -> npcl(FacialExpression.LAUGH, "Good question. We have a diplomatic consession at the turn of the financial year. Said pirate is chosen should the existing mate be absent without leave.").also { stage++ } + 1 -> playerl(FacialExpression.THINKING, "I had no idea. I always figured it was all about popularity.").also { stage++ } + 2 -> npc(FacialExpression.FRIENDLY, "It is. I'm just pulling your leg.").also { stage++ } + 3 -> player(FacialExpression.FRIENDLY,"Oh....").also { stage = 99 } - 10 -> player(core.game.dialogue.FacialExpression.ANNOYED, "Hey! I'm not a stowaway!").also { stage++ } - 11 -> player(core.game.dialogue.FacialExpression.ANNOYED, "That Lokar guy invited me aboard...").also { stage++ } - 12 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "I see. Well, don't distract me as I'm making preparations for departure.").also { stage++ } - 13 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Try not to distract any of the crew either, Zamorak knows it's hard enough to get them to do any work around here without strangers wandering round the ship asking them inane questions.").also { stage = 99 } + 10 -> player(FacialExpression.ANNOYED, "Hey! I'm not a stowaway!").also { stage++ } + 11 -> player(FacialExpression.ANNOYED, "That Lokar guy invited me aboard...").also { stage++ } + 12 -> npcl(FacialExpression.FRIENDLY, "I see. Well, don't distract me as I'm making preparations for departure.").also { stage++ } + 13 -> npcl(FacialExpression.FRIENDLY, "Try not to distract any of the crew either, Zamorak knows it's hard enough to get them to do any work around here without strangers wandering round the ship asking them inane questions.").also { stage = 99 } 99 -> end() } return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return DaveyBoyDialogue(player) } diff --git a/Server/src/main/content/region/misc/piratecove/dialogue/EagleEyeShultzDialogue.kt b/Server/src/main/content/region/misc/piratecove/dialogue/EagleEyeShultzDialogue.kt index 486498765..ab7083b0b 100644 --- a/Server/src/main/content/region/misc/piratecove/dialogue/EagleEyeShultzDialogue.kt +++ b/Server/src/main/content/region/misc/piratecove/dialogue/EagleEyeShultzDialogue.kt @@ -12,27 +12,27 @@ import org.rs09.consts.NPCs */ @Initializable -class EagleEyeShultzDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class EagleEyeShultzDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - playerl(core.game.dialogue.FacialExpression.HALF_ASKING, "What do you do for fun on this ship? You know, when you're not doing pirate stuff.").also { stage = 0 } + playerl(FacialExpression.HALF_ASKING, "What do you do for fun on this ship? You know, when you're not doing pirate stuff.").also { stage = 0 } return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 0 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "All sorts! Hide and seek, pin the patch on the pirate, walk the plank!").also { stage++ } - 1 -> playerl(core.game.dialogue.FacialExpression.WORRIED, "What a life! Wait a minute. 'Walk the plank'? Surely that's a bit dangerous?").also { stage++ } - 2 -> npcl(core.game.dialogue.FacialExpression.LAUGH, "Well of course, but where's the fun without a few deaths?").also { stage++ } - 3 -> player(core.game.dialogue.FacialExpression.HALF_THINKING,"I think I'll stick to Runelink.").also { stage = 99 } + 0 -> npcl(FacialExpression.FRIENDLY, "All sorts! Hide and seek, pin the patch on the pirate, walk the plank!").also { stage++ } + 1 -> playerl(FacialExpression.WORRIED, "What a life! Wait a minute. 'Walk the plank'? Surely that's a bit dangerous?").also { stage++ } + 2 -> npcl(FacialExpression.LAUGH, "Well of course, but where's the fun without a few deaths?").also { stage++ } + 3 -> player(FacialExpression.HALF_THINKING,"I think I'll stick to Runelink.").also { stage = 99 } 99 -> end() } return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return EagleEyeShultzDialogue(player) } diff --git a/Server/src/main/content/region/misc/piratecove/dialogue/LecherousLeeDialogue.kt b/Server/src/main/content/region/misc/piratecove/dialogue/LecherousLeeDialogue.kt index 63aa1c1e3..566450942 100644 --- a/Server/src/main/content/region/misc/piratecove/dialogue/LecherousLeeDialogue.kt +++ b/Server/src/main/content/region/misc/piratecove/dialogue/LecherousLeeDialogue.kt @@ -12,84 +12,84 @@ import org.rs09.consts.NPCs */ @Initializable -class LecherousLeeDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class LecherousLeeDialogue(player: Player? = null) : DialoguePlugin(player){ private val conversations = arrayOf (0, 10, 13, 17, 21, 27, 31, 36, 43, 52) override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - player(core.game.dialogue.FacialExpression.FRIENDLY, "Hello.").also { stage = conversations.random() } + player(FacialExpression.FRIENDLY, "Hello.").also { stage = conversations.random() } return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 0 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "So how's life as a pirate?").also { stage++ } - 1 -> npcl(core.game.dialogue.FacialExpression.LAUGH, "What kind of question is that? How's life as a... I dunno. Whatever it is that you do for a living.").also { stage++ } - 2 -> player(core.game.dialogue.FacialExpression.FRIENDLY,"I'm a freelance troubleshooter.").also { stage++ } - 3 -> npc(core.game.dialogue.FacialExpression.FRIENDLY, "What does that entail then?").also { stage++ } - 4 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY,"Mostly killing things for money and delivering items around the planet for people.").also { stage++ } - 5 -> player(core.game.dialogue.FacialExpression.FRIENDLY,"I collect stuff.").also { stage++ } - 6 -> npc(core.game.dialogue.FacialExpression.FRIENDLY, "So how's that life?").also { stage++ } - 7 -> player(core.game.dialogue.FacialExpression.FRIENDLY,"Can't complain, can't complain...").also { stage++ } - 8 -> npc(core.game.dialogue.FacialExpression.FRIENDLY, "Well, there you go.").also { stage = 99 } + 0 -> player(FacialExpression.FRIENDLY, "So how's life as a pirate?").also { stage++ } + 1 -> npcl(FacialExpression.LAUGH, "What kind of question is that? How's life as a... I dunno. Whatever it is that you do for a living.").also { stage++ } + 2 -> player(FacialExpression.FRIENDLY,"I'm a freelance troubleshooter.").also { stage++ } + 3 -> npc(FacialExpression.FRIENDLY, "What does that entail then?").also { stage++ } + 4 -> playerl(FacialExpression.FRIENDLY,"Mostly killing things for money and delivering items around the planet for people.").also { stage++ } + 5 -> player(FacialExpression.FRIENDLY,"I collect stuff.").also { stage++ } + 6 -> npc(FacialExpression.FRIENDLY, "So how's that life?").also { stage++ } + 7 -> player(FacialExpression.FRIENDLY,"Can't complain, can't complain...").also { stage++ } + 8 -> npc(FacialExpression.FRIENDLY, "Well, there you go.").also { stage = 99 } - 10 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "Aren't you a little short for a pirate?").also { stage++ } - 11 -> npcl(core.game.dialogue.FacialExpression.LAUGH, "My mother was a gnome. Apparently it was a very painful birth.").also { stage++ } - 12 -> player(core.game.dialogue.FacialExpression.FRIENDLY,"More info than I wanted, thanks!").also { stage = 99 } + 10 -> player(FacialExpression.FRIENDLY, "Aren't you a little short for a pirate?").also { stage++ } + 11 -> npcl(FacialExpression.LAUGH, "My mother was a gnome. Apparently it was a very painful birth.").also { stage++ } + 12 -> player(FacialExpression.FRIENDLY,"More info than I wanted, thanks!").also { stage = 99 } - 13 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Sorry, can't stop, the Captain will have my guts for garters if he catches me slacking off talking to the stowaway.").also { stage++ } - 14 -> player(core.game.dialogue.FacialExpression.FRIENDLY,"I'm not a stowaway! I was invited aboard!").also { stage++ } - 15 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Yeah, whatever guy, it doesn't really matter who you are I'll get in trouble!").also { stage = 99 } + 13 -> npcl(FacialExpression.FRIENDLY, "Sorry, can't stop, the Captain will have my guts for garters if he catches me slacking off talking to the stowaway.").also { stage++ } + 14 -> player(FacialExpression.FRIENDLY,"I'm not a stowaway! I was invited aboard!").also { stage++ } + 15 -> npcl(FacialExpression.FRIENDLY, "Yeah, whatever guy, it doesn't really matter who you are I'll get in trouble!").also { stage = 99 } - 17 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY, "You know, I've always wondered what life as a pirate actually entails.").also { stage++ } - 18 -> npcl(core.game.dialogue.FacialExpression.LAUGH, "Well, at the moment it mostly involves being asked random questions by a stowaway.").also { stage++ } - 19 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY,"I'm not a stowaway! I was invited aboard! By Lokar! Ask him!").also { stage++ } - 20 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Hey, whatever pal. Just make sure the captain doesn't catch you, pirates don't like stowaways much.").also { stage = 99 } + 17 -> playerl(FacialExpression.FRIENDLY, "You know, I've always wondered what life as a pirate actually entails.").also { stage++ } + 18 -> npcl(FacialExpression.LAUGH, "Well, at the moment it mostly involves being asked random questions by a stowaway.").also { stage++ } + 19 -> playerl(FacialExpression.FRIENDLY,"I'm not a stowaway! I was invited aboard! By Lokar! Ask him!").also { stage++ } + 20 -> npcl(FacialExpression.FRIENDLY, "Hey, whatever pal. Just make sure the captain doesn't catch you, pirates don't like stowaways much.").also { stage = 99 } - 21 -> npcl(core.game.dialogue.FacialExpression.LAUGH, "Ah, good day to you sirrah! Your face is unfamiliar, did you perhaps join us aboard the ship at Lunar Isle?").also { stage++ } - 22 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY,"No, Lokar offered me a lift in Rellekka actually.").also { stage++ } - 23 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Oh, really? You don't look like a Fremennik to me!").also { stage++ } - 24 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY,"Well... I kind of am, and I kind of aren't. It's a long story.").also { stage++ } - 25 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Sorry I don't have time to hear it then! See you around young fremennik-who-is-not-really-a- fremennik!").also { stage++ } - 26 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY,"'Bye.").also { stage = 99 } + 21 -> npcl(FacialExpression.LAUGH, "Ah, good day to you sirrah! Your face is unfamiliar, did you perhaps join us aboard the ship at Lunar Isle?").also { stage++ } + 22 -> playerl(FacialExpression.FRIENDLY,"No, Lokar offered me a lift in Rellekka actually.").also { stage++ } + 23 -> npcl(FacialExpression.FRIENDLY, "Oh, really? You don't look like a Fremennik to me!").also { stage++ } + 24 -> playerl(FacialExpression.FRIENDLY,"Well... I kind of am, and I kind of aren't. It's a long story.").also { stage++ } + 25 -> npcl(FacialExpression.FRIENDLY, "Sorry I don't have time to hear it then! See you around young fremennik-who-is-not-really-a- fremennik!").also { stage++ } + 26 -> playerl(FacialExpression.FRIENDLY,"'Bye.").also { stage = 99 } - 27 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "Brrrr! Its cold up here!").also { stage++ } - 28 -> npcl(core.game.dialogue.FacialExpression.LAUGH, "You think this is cold? Up by Acheron it gets so cold that when you talk you see the words freeze in the air in front of you!").also { stage++ } - 29 -> player(core.game.dialogue.FacialExpression.FRIENDLY,"REALLY?").also { stage++ } - 30 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Nah, not really. I was exaggerating for humourous effect. It is very very cold though!").also { stage = 99 } + 27 -> player(FacialExpression.FRIENDLY, "Brrrr! Its cold up here!").also { stage++ } + 28 -> npcl(FacialExpression.LAUGH, "You think this is cold? Up by Acheron it gets so cold that when you talk you see the words freeze in the air in front of you!").also { stage++ } + 29 -> player(FacialExpression.FRIENDLY,"REALLY?").also { stage++ } + 30 -> npcl(FacialExpression.FRIENDLY, "Nah, not really. I was exaggerating for humourous effect. It is very very cold though!").also { stage = 99 } - 31 -> npc(core.game.dialogue.FacialExpression.LAUGH, "Hello to you too.").also { stage++ } - 32 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY,"Yar! We be pirates, yar! Avast, ye scurvy land-lubbing lychee!").also { stage++ } - 33 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Please don't talk like that, it is extremely irritating.").also { stage++ } - 34 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Also, please don't call me a lychee, whatever that may be.").also { stage++ } - 35 -> player(core.game.dialogue.FacialExpression.FRIENDLY,"Oh. Okay. Sorry.").also { stage = 99} + 31 -> npc(FacialExpression.LAUGH, "Hello to you too.").also { stage++ } + 32 -> playerl(FacialExpression.FRIENDLY,"Yar! We be pirates, yar! Avast, ye scurvy land-lubbing lychee!").also { stage++ } + 33 -> npcl(FacialExpression.FRIENDLY, "Please don't talk like that, it is extremely irritating.").also { stage++ } + 34 -> npcl(FacialExpression.FRIENDLY, "Also, please don't call me a lychee, whatever that may be.").also { stage++ } + 35 -> player(FacialExpression.FRIENDLY,"Oh. Okay. Sorry.").also { stage = 99} - 36 -> npc(core.game.dialogue.FacialExpression.LAUGH, "ARGH!", "SOUND THE ALARM!", "STOWAWAY ON BOARD!", "STOWAWAY ON BOARD!").also { stage++ } - 37 -> player(core.game.dialogue.FacialExpression.FRIENDLY,"No! I'm not a stowaway! Honest! I was invited here!").also { stage++ } - 38 -> npc(core.game.dialogue.FacialExpression.FRIENDLY, "Oh, sorry, my mistake then.").also { stage++ } - 39 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "You must admit you do look a lot like a stowaway though.").also { stage++ } - 40 -> player(core.game.dialogue.FacialExpression.FRIENDLY,"Why, what do they usually look like?").also { stage++ } - 41 -> npc(core.game.dialogue.FacialExpression.FRIENDLY, "Erm... I've never actually met one...").also { stage++ } - 42 -> player(core.game.dialogue.FacialExpression.FRIENDLY,"Okay then...").also { stage = 99 } + 36 -> npc(FacialExpression.LAUGH, "ARGH!", "SOUND THE ALARM!", "STOWAWAY ON BOARD!", "STOWAWAY ON BOARD!").also { stage++ } + 37 -> player(FacialExpression.FRIENDLY,"No! I'm not a stowaway! Honest! I was invited here!").also { stage++ } + 38 -> npc(FacialExpression.FRIENDLY, "Oh, sorry, my mistake then.").also { stage++ } + 39 -> npcl(FacialExpression.FRIENDLY, "You must admit you do look a lot like a stowaway though.").also { stage++ } + 40 -> player(FacialExpression.FRIENDLY,"Why, what do they usually look like?").also { stage++ } + 41 -> npc(FacialExpression.FRIENDLY, "Erm... I've never actually met one...").also { stage++ } + 42 -> player(FacialExpression.FRIENDLY,"Okay then...").also { stage = 99 } - 43 -> npc(core.game.dialogue.FacialExpression.LAUGH, "Hello.").also { stage++ } - 44 -> player(core.game.dialogue.FacialExpression.FRIENDLY,"So... You're a pirate, huh?").also { stage++ } - 45 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "It's what it says on my pay-packet at the end of the month.").also { stage++ } - 46 -> player(core.game.dialogue.FacialExpression.FRIENDLY,"How's that working out for you?").also { stage++ } - 47 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Pretty good so far. All the grog and loot that we can plunder, plus full medical including dental.").also { stage++ } - 48 -> player(core.game.dialogue.FacialExpression.FRIENDLY,"You mean you have insurance?").also { stage++ } - 49 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Not as such. If any of us get sick we kidnap a doctor and don't let him go until we're better.").also { stage++ } - 50 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "You'd be surprised what an incentive for expert health care that is.").also { stage++ } - 51 -> player(core.game.dialogue.FacialExpression.FRIENDLY,"I can imagine.").also { stage = 99 } + 43 -> npc(FacialExpression.LAUGH, "Hello.").also { stage++ } + 44 -> player(FacialExpression.FRIENDLY,"So... You're a pirate, huh?").also { stage++ } + 45 -> npcl(FacialExpression.FRIENDLY, "It's what it says on my pay-packet at the end of the month.").also { stage++ } + 46 -> player(FacialExpression.FRIENDLY,"How's that working out for you?").also { stage++ } + 47 -> npcl(FacialExpression.FRIENDLY, "Pretty good so far. All the grog and loot that we can plunder, plus full medical including dental.").also { stage++ } + 48 -> player(FacialExpression.FRIENDLY,"You mean you have insurance?").also { stage++ } + 49 -> npcl(FacialExpression.FRIENDLY, "Not as such. If any of us get sick we kidnap a doctor and don't let him go until we're better.").also { stage++ } + 50 -> npcl(FacialExpression.FRIENDLY, "You'd be surprised what an incentive for expert health care that is.").also { stage++ } + 51 -> player(FacialExpression.FRIENDLY,"I can imagine.").also { stage = 99 } - 52 -> npc(core.game.dialogue.FacialExpression.LAUGH, "Hello there. So what brings you aboard the Lady Zay?").also { stage++ } - 53 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY,"Well, I was planning on visiting the Moon Clan, but I have to say your ship is very impressive.").also { stage++ } - 54 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Aye, she's a beauty alright! The Lady Zay has been my home for many hard months, through storm and sun, and she always gets us to here we were headed!").also { stage++ } - 55 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY,"Yes, she's certainly one of the finest boats I've seen on my travels!").also { stage++ } - 56 -> npc(core.game.dialogue.FacialExpression.FRIENDLY, "That she is lad, that she is.").also { stage = 99 } + 52 -> npc(FacialExpression.LAUGH, "Hello there. So what brings you aboard the Lady Zay?").also { stage++ } + 53 -> playerl(FacialExpression.FRIENDLY,"Well, I was planning on visiting the Moon Clan, but I have to say your ship is very impressive.").also { stage++ } + 54 -> npcl(FacialExpression.FRIENDLY, "Aye, she's a beauty alright! The Lady Zay has been my home for many hard months, through storm and sun, and she always gets us to here we were headed!").also { stage++ } + 55 -> playerl(FacialExpression.FRIENDLY,"Yes, she's certainly one of the finest boats I've seen on my travels!").also { stage++ } + 56 -> npc(FacialExpression.FRIENDLY, "That she is lad, that she is.").also { stage = 99 } 99 -> end() @@ -97,7 +97,7 @@ class LecherousLeeDialogue(player: Player? = null) : core.game.dialogue.Dialogue return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return LecherousLeeDialogue(player) } diff --git a/Server/src/main/content/region/misc/tutisland/dialogue/BankerTutorDialogue.java b/Server/src/main/content/region/misc/tutisland/dialogue/BankerTutorDialogue.java index 8e5640807..c6a121500 100644 --- a/Server/src/main/content/region/misc/tutisland/dialogue/BankerTutorDialogue.java +++ b/Server/src/main/content/region/misc/tutisland/dialogue/BankerTutorDialogue.java @@ -38,7 +38,7 @@ public final class BankerTutorDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = args[0] instanceof NPC ? (NPC) args[0] : null; - npc("Good day, how may I help you?"); + npc("Good day, would you like to access your bank account?"); stage = 0; return true; } 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 99503423a..3a16f44c9 100644 --- a/Server/src/main/content/region/misc/tutisland/dialogue/SkipTutorialDialogue.kt +++ b/Server/src/main/content/region/misc/tutisland/dialogue/SkipTutorialDialogue.kt @@ -15,13 +15,13 @@ import content.region.misc.tutisland.handlers.TutorialStage * @author Ceikry */ @Initializable -class SkipTutorialDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { +class SkipTutorialDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player?): DialoguePlugin { return SkipTutorialDialogue(player) } override fun open(vararg args: Any?): Boolean { - npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Hey, would you like to skip to the end? Choose wisely! This is the only time you get this choice.") + 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 } 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 01006fed9..21c914b98 100644 --- a/Server/src/main/content/region/misc/tutisland/dialogue/SurvivalExpertDialogue.kt +++ b/Server/src/main/content/region/misc/tutisland/dialogue/SurvivalExpertDialogue.kt @@ -18,8 +18,8 @@ import content.region.misc.tutisland.handlers.TutorialStage * @author Ceikry */ @Initializable -class SurvivalExpertDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { +class SurvivalExpertDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player?): DialoguePlugin { return SurvivalExpertDialogue(player) } @@ -32,10 +32,10 @@ class SurvivalExpertDialogue(player: Player? = null) : core.game.dialogue.Dialog player, interpreter.sendDialogues( npc, - core.game.dialogue.FacialExpression.FRIENDLY, + FacialExpression.FRIENDLY, "Hello there, newcomer. My name is Brynna. My job is", - "to teach you a few suvival tips and tricks. First off", - "we're going to start with the most basic skill of", + "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." ) ) @@ -44,10 +44,10 @@ class SurvivalExpertDialogue(player: Player? = null) : core.game.dialogue.Dialog player, interpreter.sendDialogues( npc, - core.game.dialogue.FacialExpression.FRIENDLY, + FacialExpression.FRIENDLY, "Well done! Next we need to get some food in our", - "bellies. We'd need something to cook. There are shrimp", - "in the pond there. So let's catch and cook some." + "bellies. We'll need something to cook. There are shrimp", + "in the pond there, so let's catch and cook some." ) ) @@ -79,7 +79,7 @@ class SurvivalExpertDialogue(player: Player? = null) : core.game.dialogue.Dialog interpreter.sendDoubleItemMessage( Items.TINDERBOX_590, Items.BRONZE_AXE_1351, - "The Surivival Guide gives you a tinderbox and a bronze axe!" + "The Survival Guide gives you a tinderbox and a bronze axe!" ) ) addItem(player, Items.TINDERBOX_590) 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 058eb4db2..4e73a799d 100644 --- a/Server/src/main/content/region/misc/tutisland/dialogue/TutorialCombatInstructorDialogue.kt +++ b/Server/src/main/content/region/misc/tutisland/dialogue/TutorialCombatInstructorDialogue.kt @@ -15,8 +15,8 @@ import content.region.misc.tutisland.handlers.TutorialStage * @author Ceikry */ @Initializable -class TutorialCombatInstructorDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { +class TutorialCombatInstructorDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player?): DialoguePlugin { return TutorialCombatInstructorDialogue(player) } @@ -25,9 +25,9 @@ class TutorialCombatInstructorDialogue(player: Player? = null) : core.game.dialo when(getAttribute(player, "tutorial:stage", 0)) { - 44 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY, "Hi! My name's ${player.username}.") - 47 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Very good, but that little butter knife isn't going to protect you much. Here, take these.") - 53 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY, "I did it! I killed a giant rat!") + 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)) @@ -43,9 +43,9 @@ class TutorialCombatInstructorDialogue(player: Player? = null) : core.game.dialo when(getAttribute(player, "tutorial:stage", 0)) { 44 -> when(stage){ - 0 -> npcl(core.game.dialogue.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(core.game.dialogue.FacialExpression.FRIENDLY, "I'm Vannaka, the greatest swordsman alive.").also { stage++ } - 2 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Let's get started by teaching you to wield a weapon.").also { 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) @@ -68,8 +68,8 @@ class TutorialCombatInstructorDialogue(player: Player? = null) : core.game.dialo } 53 -> when(stage){ - 0 -> npcl(core.game.dialogue.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(core.game.dialogue.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++ } + 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)) @@ -92,4 +92,4 @@ class TutorialCombatInstructorDialogue(player: Player? = null) : core.game.dialo return intArrayOf(NPCs.COMBAT_INSTRUCTOR_944) } -} \ 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 4bee2197f..540382e1f 100644 --- a/Server/src/main/content/region/misc/tutisland/dialogue/TutorialMagicTutorDialogue.kt +++ b/Server/src/main/content/region/misc/tutisland/dialogue/TutorialMagicTutorDialogue.kt @@ -1,22 +1,22 @@ 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.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.tools.END_DIALOGUE +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 @@ -93,41 +93,67 @@ class TutorialMagicTutorDialogue(player: Player? = null) : core.game.dialogue.Di } } 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++ } + 0 -> options("Set Ironman Mode (current: ${player.ironmanManager.mode.name})", "Change XP Rate (current: ${player.skills.experienceMultiplier}x)", "Toggle Boosted mode (current: ${if (getAttribute(player, "snowscape:boostedmode", false)) "Enabled" else "Disabled"})", "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 } + 1 -> options("None","Standard (No trading)","Ultimate (No trading and no bank)","Hardcore (No trading and PERMADEATH!)","Nevermind.").also { stage = 10 } + 2 -> options("1.0x","2.5x","5.0x","10.0x").also { stage = 20 } + 3 -> options("Enable", "Disable").also { stage = 25 } + 4 -> 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] + val mode = when (buttonId - 1) + { + 0 -> IronmanMode.NONE + 1 -> IronmanMode.STANDARD + 2 -> IronmanMode.ULTIMATE + 3 -> IronmanMode.HARDCORE + else -> IronmanMode.NONE + } + if (mode != IronmanMode.NONE) stage = 11 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 +/* + if (player.skills.experienceMultiplier == 10.0) player.skills.experienceMultiplier = 5.0 +*/ } else { handle(interfaceId, 0) } } + 11 -> player.dialogueInterpreter.sendPlainMessage(false, *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.")).also { stage = 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!") +/* + if(rate == 10.0) { + player.dialogueInterpreter.sendDialogue("10.0x is no longer available!") + player.skills.experienceMultiplier = 5.0 stage = 0 return true } +*/ player.dialogueInterpreter.sendDialogue("You set your XP rate to: ${rate}x.") - player.skills.experienceMutiplier = rate + player.skills.experienceMultiplier = rate stage = 0 } + 25 -> { + when(buttonId) { + 1 -> setAttribute(player, "/save:snowscape:boostedmode", true) + 2 -> removeAttribute(player, "snowscape:boostedmode") + } + val currentMode = getAttribute(player, "snowscape:boostedmode", false) + //setAttribute(player, "/save:snowscape:boostedmode", !currentMode) + player.dialogueInterpreter.sendPlainMessage(false, *splitLines("You have ${if (currentMode) "Enabled" else "Disabled"} Boosted mode. Boosted mode makes some custom features much more powerful, which will bypass many game-balance obstacles and lead to easier progression. Not recommended if you want a more traditional experience.")).also { stage = 0 } + + } + 30 -> player.dialogueInterpreter.sendOptions("Leave Tutorial Island?", "Yes, I'm ready.", "No, not yet.").also { stage++ } 31 -> when(buttonId) { @@ -137,8 +163,9 @@ class TutorialMagicTutorDialogue(player: Player? = null) : core.game.dialogue.Di 40 -> { setAttribute(player, "/save:tutorial:complete", true) - setVarbit(player, 1021, 0, 0) - teleport(player, Location.create(3233, 3230), TeleportManager.TeleportType.NORMAL) + setVarbit(player, 3756, 0) + setVarp(player, 281, 1000, true) + teleport(player, ServerConstants.HOME_LOCATION!!, TeleportManager.TeleportType.NORMAL) closeOverlay(player) player.inventory.clear() @@ -149,14 +176,15 @@ class TutorialMagicTutorDialogue(player: Player? = null) : core.game.dialogue.Di player.inventory.add(*STARTER_PACK) player.bank.add(*STARTER_BANK) - if(player.ironmanManager.mode == IronmanMode.HARDCORE) - { + if(player.ironmanManager.mode == IronmanMode.HARDCORE){ setAttribute(player, "/save:permadeath", true) } - else if(player.skills.experienceMutiplier == 10.0) +/* + if(player.skills.experienceMultiplier == 10.0) { - player.skills.experienceMutiplier = 5.0 + player.skills.experienceMultiplier = 5.0 } +*/ //This overwrites the stuck dialogue after teleporting to Lumbridge for some reason //Dialogue from 2007 or thereabouts @@ -176,13 +204,13 @@ class TutorialMagicTutorDialogue(player: Player? = null) : core.game.dialogue.Di player.unhook(TutorialUseWithReceiver) player.unhook(TutorialInteractionReceiver) player.unhook(TutorialButtonReceiver) - RulesAndInfo.openFor(player) + //RulesAndInfo.openFor(player) if (GameWorld.settings!!.enable_default_clan) { - player.communication.currentClan = ServerConstants.SERVER_NAME + player.communication.currentClan = ServerConstants.SERVER_NAME.toLowerCase() val clanJoin = JoinClanRequest.newBuilder() - clanJoin.clanName = ServerConstants.SERVER_NAME + clanJoin.clanName = ServerConstants.SERVER_NAME.toLowerCase() clanJoin.username = player.name ManagementEvents.publish(clanJoin.build()) @@ -202,4 +230,4 @@ class TutorialMagicTutorDialogue(player: Player? = null) : core.game.dialogue.Di return intArrayOf(NPCs.MAGIC_INSTRUCTOR_946) } -} \ No newline at end of file +} 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 52dbcb923..2db699406 100644 --- a/Server/src/main/content/region/misc/tutisland/dialogue/TutorialMasterChefDialogue.kt +++ b/Server/src/main/content/region/misc/tutisland/dialogue/TutorialMasterChefDialogue.kt @@ -16,8 +16,8 @@ import content.region.misc.tutisland.handlers.TutorialStage * @author Ceikry */ @Initializable -class TutorialMasterChefDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { +class TutorialMasterChefDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player?): DialoguePlugin { return TutorialMasterChefDialogue(player) } @@ -29,8 +29,8 @@ class TutorialMasterChefDialogue(player: Player? = null) : core.game.dialogue.Di player, interpreter.sendDialogues( npc, - core.game.dialogue.FacialExpression.FRIENDLY, - "Ahh! Welcome, newcomer. I am the Master Chef, Lev. It", + 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." ) @@ -69,7 +69,7 @@ class TutorialMasterChefDialogue(player: Player? = null) : core.game.dialogue.Di player, interpreter.sendDialogues( player, - core.game.dialogue.FacialExpression.HALF_GUILTY, + FacialExpression.HALF_GUILTY, "I already know how to cook. Brynna taught me just", "now." ) @@ -78,7 +78,7 @@ class TutorialMasterChefDialogue(player: Player? = null) : core.game.dialogue.Di player, interpreter.sendDialogues( npc, - core.game.dialogue.FacialExpression.LAUGH, + 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." @@ -88,7 +88,7 @@ class TutorialMasterChefDialogue(player: Player? = null) : core.game.dialogue.Di player, interpreter.sendDialogues( npc, - core.game.dialogue.FacialExpression.FRIENDLY, + FacialExpression.FRIENDLY, "And no fine meal is complete without good music, so", "we'll cover that while you're here too." ) 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 736c0d08c..ad9f6c77e 100644 --- a/Server/src/main/content/region/misc/tutisland/dialogue/TutorialMiningInstructorDialogue.kt +++ b/Server/src/main/content/region/misc/tutisland/dialogue/TutorialMiningInstructorDialogue.kt @@ -18,16 +18,16 @@ import content.region.misc.tutisland.handlers.TutorialStage * @author Ceikry */ @Initializable -class TutorialMiningInstructorDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { +class TutorialMiningInstructorDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player?): DialoguePlugin { return TutorialMiningInstructorDialogue(player) } override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC when(getAttribute(player, "tutorial:stage", 0)) { - 30 -> npcl(core.game.dialogue.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 call you by name.") - 34 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY, "I prospected both types of rock! One set contains tin and the other has copper ore inside.") + 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) @@ -38,7 +38,7 @@ class TutorialMiningInstructorDialogue(player: Player? = null) : core.game.dialo TutorialStage.load(player, 35) } } - 40 -> playerl(core.game.dialogue.FacialExpression.ASKING, "How do I make a weapon out of this?") + 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) @@ -59,8 +59,8 @@ class TutorialMiningInstructorDialogue(player: Player? = null) : core.game.dialo override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(getAttribute(player, "tutorial:stage", 0)) { 30 -> when(stage) { - 0 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY, "You can call me ${player.username}.").also { stage++ } - 1 -> npcl(core.game.dialogue.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++ } + 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) @@ -69,8 +69,8 @@ class TutorialMiningInstructorDialogue(player: Player? = null) : core.game.dialo } 34,35 -> when(stage) { - 0 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Absolutely right, ${player.username}. These two ore types can be smelted together to make bronze.").also { stage++ } - 1 -> npcl(core.game.dialogue.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++ } + 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!") @@ -84,7 +84,7 @@ class TutorialMiningInstructorDialogue(player: Player? = null) : core.game.dialo } 40,41 -> when(stage){ - 0 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Okay, I'll show you how to make a dagger out of it. You'll be needing this..").also { 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!") 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 680a929b0..5278dc65d 100644 --- a/Server/src/main/content/region/misc/tutisland/dialogue/TutorialPrayerDialogue.kt +++ b/Server/src/main/content/region/misc/tutisland/dialogue/TutorialPrayerDialogue.kt @@ -15,8 +15,8 @@ import content.region.misc.tutisland.handlers.TutorialStage * @author Ceikry */ @Initializable -class TutorialPrayerDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { +class TutorialPrayerDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player?): DialoguePlugin { return TutorialPrayerDialogue(player) } @@ -24,9 +24,9 @@ class TutorialPrayerDialogue(player: Player? = null) : core.game.dialogue.Dialog npc = args[0] as NPC when(getAttribute(player, "tutorial:stage", 0)) { - 60 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Greetings! I'd just like to briefly go over two topics with you: prayer, and friends.") - 62 -> npcl(core.game.dialogue.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(core.game.dialogue.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 -> 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!") } return true } @@ -35,8 +35,8 @@ class TutorialPrayerDialogue(player: Player? = null) : core.game.dialogue.Dialog when(getAttribute(player, "tutorial:stage", 0)) { 60 -> when(stage++){ - 0 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY, "Alright, sounds fun!") - 1 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Right, so first thing: prayer. Prayer is trained by offering bones to the gods, and can grant you many boons!") + 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!") 2 -> { end() setAttribute(player, "tutorial:stage", 61) @@ -45,8 +45,8 @@ class TutorialPrayerDialogue(player: Player? = null) : core.game.dialogue.Dialog } 62 -> when(stage++){ - 0 -> playerl(core.game.dialogue.FacialExpression.AMAZED, "Very cool!") - 1 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Next up, let's talk about friends.") + 0 -> playerl(FacialExpression.AMAZED, "Very cool!") + 1 -> 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 34fc5eecc..015928854 100644 --- a/Server/src/main/content/region/misc/tutisland/dialogue/TutorialQuestGuideDialogue.kt +++ b/Server/src/main/content/region/misc/tutisland/dialogue/TutorialQuestGuideDialogue.kt @@ -18,8 +18,8 @@ import content.region.misc.tutisland.handlers.TutorialStage * @author Ceikry */ @Initializable -class TutorialQuestGuideDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { +class TutorialQuestGuideDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player?): DialoguePlugin { return TutorialQuestGuideDialogue(player) } @@ -31,9 +31,9 @@ class TutorialQuestGuideDialogue(player: Player? = null) : core.game.dialogue.Di player, interpreter.sendDialogues( npc, - core.game.dialogue.FacialExpression.FRIENDLY, - "Ah. Welcome adventurer. I'm here to tell you all about", - "quests. Lets start by opening the Quest list." + FacialExpression.FRIENDLY, + "Ah. Welcome, adventurer. I'm here to tell you all about", + "quests. Lets start by opening the Quest List." ) ) @@ -41,9 +41,9 @@ class TutorialQuestGuideDialogue(player: Player? = null) : core.game.dialogue.Di player, interpreter.sendDialogues( npc, - core.game.dialogue.FacialExpression.FRIENDLY, + FacialExpression.FRIENDLY, "Now you have the journal open. I'll tell you a bit about", - "it At the moment all the quests are shown in red. Which", + "it At the moment all the quests are shown in red, which", "means you have not started them yet." ) ) @@ -67,7 +67,7 @@ class TutorialQuestGuideDialogue(player: Player? = null) : core.game.dialogue.Di "" ) ) - setVarbit(player, 1021, 0, 3) + setVarbit(player, 3756, 3) player.interfaceManager.openTab(Component(Components.QUESTJOURNAL_V2_274)) } 28 -> when(stage) @@ -76,7 +76,7 @@ class TutorialQuestGuideDialogue(player: Player? = null) : core.game.dialogue.Di player, interpreter.sendDialogues( npc, - core.game.dialogue.FacialExpression.FRIENDLY, + FacialExpression.FRIENDLY, "When you start a quest it will change colour to yellow,", "and to green when you've finished. This is so you can", "easily see what's complete, what's started and what's left", @@ -87,7 +87,7 @@ class TutorialQuestGuideDialogue(player: Player? = null) : core.game.dialogue.Di player, interpreter.sendDialogues( npc, - core.game.dialogue.FacialExpression.FRIENDLY, + FacialExpression.FRIENDLY, "The start of quests are easy to find. Look out for the", "star icons on the minimap, just like the one you should", "see marking my house." @@ -97,10 +97,10 @@ class TutorialQuestGuideDialogue(player: Player? = null) : core.game.dialogue.Di player, interpreter.sendDialogues( npc, - core.game.dialogue.FacialExpression.FRIENDLY, + FacialExpression.FRIENDLY, "There's not a lot more I can tell you about questing.", "You have to experience the thrill of it yourself to fully", - "understand. You may find some adevnture in the caves", + "understand. You may find some adventure in the caves", "under my house." ) ).also { stage++ } @@ -119,4 +119,4 @@ class TutorialQuestGuideDialogue(player: Player? = null) : core.game.dialogue.Di return intArrayOf(NPCs.QUEST_GUIDE_949) } -} \ No newline at end of file +} 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 5eff3bfb7..4d7653695 100644 --- a/Server/src/main/content/region/misc/tutisland/dialogue/TutorialRSGuideDialogue.kt +++ b/Server/src/main/content/region/misc/tutisland/dialogue/TutorialRSGuideDialogue.kt @@ -15,8 +15,8 @@ import content.region.misc.tutisland.handlers.TutorialStage * @author Ceikry */ @Initializable -class TutorialRSGuideDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { +class TutorialRSGuideDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player?): DialoguePlugin { return TutorialRSGuideDialogue(player) } @@ -24,25 +24,29 @@ class TutorialRSGuideDialogue(player: Player? = null) : core.game.dialogue.Dialo 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, - core.game.dialogue.FacialExpression.HALF_GUILTY, + FacialExpression.HALF_GUILTY, "Greetings! Please follow the onscreen", "instructions!" ) ) - return false } if(tutStage == 2) { + player.lock() Component.setUnclosable( player, interpreter.sendDialogues( npc, - core.game.dialogue.FacialExpression.HALF_GUILTY, + FacialExpression.HALF_GUILTY, "Greetings! I see you are a new arrival to this land. My", "job is to welcome all new visitors. So welcome!" ) @@ -56,7 +60,7 @@ class TutorialRSGuideDialogue(player: Player? = null) : core.game.dialogue.Dialo player, interpreter.sendDialogues( npc, - core.game.dialogue.FacialExpression.HALF_GUILTY, + FacialExpression.HALF_GUILTY, "Please follow the onscreen instructions!" ) ) @@ -71,7 +75,7 @@ class TutorialRSGuideDialogue(player: Player? = null) : core.game.dialogue.Dialo player, interpreter.sendDialogues( npc, - core.game.dialogue.FacialExpression.FRIENDLY, + FacialExpression.FRIENDLY, "You have already learned the first thing needed to", "succeed in this world: talking to other people!" ) @@ -81,7 +85,7 @@ class TutorialRSGuideDialogue(player: Player? = null) : core.game.dialogue.Dialo player, interpreter.sendDialogues( npc, - core.game.dialogue.FacialExpression.FRIENDLY, + FacialExpression.FRIENDLY, "You will find many inhabitants of this world have useful", "things to say to you. By clicking on them with your", "mouse you can talk to them." @@ -92,7 +96,7 @@ class TutorialRSGuideDialogue(player: Player? = null) : core.game.dialogue.Dialo player, interpreter.sendDialogues( npc, - core.game.dialogue.FacialExpression.FRIENDLY, + FacialExpression.FRIENDLY, "I would also suggest reading through some of the", "supporting information on the website. There you can", "find the starter guides, which contain all the", @@ -104,7 +108,7 @@ class TutorialRSGuideDialogue(player: Player? = null) : core.game.dialogue.Dialo player, interpreter.sendDialogues( npc, - core.game.dialogue.FacialExpression.FRIENDLY, + FacialExpression.FRIENDLY, "contain helpful tips to help you on your", "journey." ) @@ -114,7 +118,7 @@ class TutorialRSGuideDialogue(player: Player? = null) : core.game.dialogue.Dialo player, interpreter.sendDialogues( npc, - core.game.dialogue.FacialExpression.FRIENDLY, + FacialExpression.FRIENDLY, "To continue the tutorial go through that door over", "there and speak to your first instructor!" ) @@ -122,6 +126,7 @@ class TutorialRSGuideDialogue(player: Player? = null) : core.game.dialogue.Dialo 5 -> { end() + player.unlock() setAttribute(player, "tutorial:stage", 3) TutorialStage.load(player, 3) } 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/TutorialEventReceivers.kt b/Server/src/main/content/region/misc/tutisland/handlers/TutorialEventReceivers.kt index e9319bcc2..655c16d1c 100644 --- a/Server/src/main/content/region/misc/tutisland/handlers/TutorialEventReceivers.kt +++ b/Server/src/main/content/region/misc/tutisland/handlers/TutorialEventReceivers.kt @@ -65,7 +65,7 @@ object TutorialButtonReceiver : EventHook } //click run button - 25 -> if(event.iface == 261 && event.buttonId == 3){ + 25 -> if(event.iface == 261 && event.buttonId == 3 || event.iface == 750 && event.buttonId == 1){ setAttribute(entity, "tutorial:stage", 26) TutorialStage.load(entity, 26) } 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 8ec2666db..3ab07f9ac 100644 --- a/Server/src/main/content/region/misc/tutisland/handlers/TutorialStage.kt +++ b/Server/src/main/content/region/misc/tutisland/handlers/TutorialStage.kt @@ -14,6 +14,7 @@ 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 /** * Loads stage-relevant tutorial data @@ -47,39 +48,62 @@ object TutorialStage { teleport(player, Location.create(3094, 3107, 0)) hideTabs(player, login) CharacterDesign.open(player) + Component.setUnclosable( + player, + player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "", + "", + "Getting started", + "Please take a moment to design your character.", + "" + ) + ).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." + ) + ) + } + } } + 1 -> { hideTabs(player, login) player.interfaceManager.openTab(Component(Components.OPTIONS_261)) - setVarbit(player, 1021, 0, 12) + setVarbit(player, 3756, 12) removeHintIcon(player) Component.setUnclosable( player, player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( "", - "Player controls", + "", + "Game options", "Please click on the flashing spanner icon found at the bottom", - "right of your screen. This will display your player controls.", - "" + "right of your screen. This will display your game options." ) ) } 2 -> { - setVarbit(player, 1021, 0, 0) + setVarbit(player, 3756, 0) hideTabs(player, login) registerHintIcon(player, Repository.findNPC(945)!!) Component.setUnclosable( player, player.dialogueInterpreter.sendScrollMessageWithBlueTitle( - "Player controls", - "On the side panel you can now see a variety of options from", - "adjusting graphics settings, to adjusting the volume of ", - "music, to selecting whether your player should accept help", - "from other players. Don't worry about these too much for now,", - "they will become clearer as you explore the game. Talk to the", - settings!!.name + " Guide to continue." + "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." ) ) } @@ -87,7 +111,7 @@ object TutorialStage { 3 -> { hideTabs(player, login) removeHintIcon(player) - registerHintIcon(player, Location.create(3098,3107,0), 125) + registerHintIcon(player, Location.create(3098, 3107, 0), 125) Component.setUnclosable( player, player.dialogueInterpreter.sendScrollMessageWithBlueTitle( @@ -110,9 +134,9 @@ object TutorialStage { player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( "Moving around", "Follow the path to find the next instructor. Clicking on the", - "ground will walk you to that point. Talk to the Survival Expert by", - "the pond to continue the tutorial. Remember you can rotate", - "the view by pressing the arrow keys." + "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." ) ) } @@ -120,13 +144,13 @@ object TutorialStage { 5 -> { hideTabs(player, login) player.interfaceManager.openTab(Component(Components.INVENTORY_149)) - setVarbit(player, 1021, 0, 4) + 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", + "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.", "" @@ -136,7 +160,7 @@ object TutorialStage { 6 -> { hideTabs(player, login) - setVarbit(player, 1021, 0, 0) + setVarbit(player, 3756, 4) Component.setUnclosable( player, player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( @@ -185,7 +209,7 @@ object TutorialStage { player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( "Please wait.", "", - "Your character is now attempting to light a fire.", + "Your character is now attempting to light the fire.", "This should only take a few seconds.", "" ) @@ -195,47 +219,47 @@ object TutorialStage { 10 -> { hideTabs(player, login) player.interfaceManager.openTab(Component(Components.STATS_320)) - setVarbit(player, 1021, 0, 2) + 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." + "Click on the flashing bar graph icon near the inventory button", + "to see your skill state.", + "" ) ) } 11 -> { hideTabs(player, login) - setVarbit(player, 1021, 0, 0) + 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." + "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 -> { hideTabs(player, login) - setVarbit(player,406, 0, 2) + 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.", + "Click on the bubbling fishing spot, indicated by the flashing", + "arrow. Remember, you can check your inventory by clicking the", + "backpack icon.", "" ) ) @@ -250,8 +274,8 @@ object TutorialStage { "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." + "As you gain Fishing experience you'll find that there are many", + "types of fish and many ways to catch them." ) ) } @@ -262,11 +286,24 @@ object TutorialStage { 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.", - "Once you have a fire, \"use\" your shrimp on the fire." - ) + "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." + ) + ) + } + } ) } @@ -276,10 +313,10 @@ object TutorialStage { 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.", - "" + "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." ) ) } @@ -291,8 +328,8 @@ object TutorialStage { player, player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( "Well done, you've just cooked your first " + settings!!.name + " meal.", - "", - "You can now move on to the next", + "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." ) @@ -306,11 +343,11 @@ object TutorialStage { Component.setUnclosable( player, player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Find your next instructor.", + "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." + "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." ) ) } @@ -325,7 +362,7 @@ object TutorialStage { "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 menu as well.", + "teach you about your Music Player.", "" ) ) @@ -338,9 +375,9 @@ object TutorialStage { 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.", + "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.", "" ) ) @@ -353,9 +390,9 @@ object TutorialStage { 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.", + "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.", "" ) ) @@ -365,14 +402,15 @@ object TutorialStage { hideTabs(player, login) removeHintIcon(player) player.interfaceManager.openTab(Component(Components.MUSIC_V3_187)) - setVarbit(player, 1021, 0, 14) + setVarbit(player, 3756, 14) Component.setUnclosable( player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + 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, lets move on. Click on", + "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." ) ) @@ -380,23 +418,24 @@ object TutorialStage { 22 -> { hideTabs(player, login) - setVarbit(player, 1021, 0, 0) + setVarbit(player, 3756, 0) registerHintIcon(player, Location.create(3072, 3090, 0), 125) Component.setUnclosable( player, - player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + 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." + "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 -> { hideTabs(player, login) - setVarbit(player, 1021, 0, 13) + setVarbit(player, 3756, 13) removeHintIcon(player) player.interfaceManager.openTab(Component(Components.EMOTES_464)) stopWalk(player) @@ -406,22 +445,22 @@ object TutorialStage { 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.", - "" + "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 -> { hideTabs(player, login) - setVarbit(player, 1021, 0, 0) + setVarbit(player, 3756, 0) player.locks.lockMovement(100000) Component.setUnclosable( player, player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( "Emotes", - "For those situtations where words don't quite describe how you feel try", + "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." @@ -437,8 +476,8 @@ object TutorialStage { "Running", "", "It's only a short distance to the next guide.", - "Why not try running there? To do this click on the run icon in", - "your settings tab." + "Why not try running there? To do this, click on the run icon", + "next to the minimap." ) ) } @@ -478,14 +517,14 @@ object TutorialStage { 28 -> { hideTabs(player, login) - setVarbit(player, 1021, 0, 0) + setVarbit(player, 3756, 0) Component.setUnclosable( player, player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Your quest Journal", + "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.", + "This is your Quest Journal, a list of all the quests in the game.", + "Talk to the Quest Guide again for an explanation.", "" ) ) @@ -494,15 +533,15 @@ object TutorialStage { 29 -> { hideTabs(player, login) removeHintIcon(player) - setVarbit(player, 1021, 0, 0) + 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.", + "Moving on", + "It's time to enter some caves. Click on the ladder to go down to", + "the next area.", "" ) ) @@ -511,16 +550,16 @@ object TutorialStage { 30 -> { hideTabs(player, login) removeHintIcon(player) - setVarbit(player, 1021, 0, 0) + 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." + "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." ) ) } @@ -533,9 +572,9 @@ object TutorialStage { 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.", + "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.", "" ) ) @@ -549,8 +588,8 @@ object TutorialStage { player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( "Please wait.", "", - "Your character is now attempting to prospect the rock. This should", - "Only take a few seconds.", + "Your character is now attempting to prospect the rock. This", + "should only take a few seconds.", "" ) ) @@ -585,12 +624,12 @@ object TutorialStage { registerHintIcon(player, Repository.findNPC(948)!!) Component.setUnclosable( player, - player.dialogueInterpreter.sendScrollMessageWithBlueTitle( + 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.", - "" ) ) } @@ -603,10 +642,10 @@ object TutorialStage { 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.", - "" + "", + "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.", ) ) } @@ -633,10 +672,10 @@ object TutorialStage { 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'.", - "" + "", + "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'." ) ) } @@ -649,15 +688,15 @@ object TutorialStage { 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 furnance. Try it now.", - "" + "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." ) ) } - //Yes I know I skipped one. Blegh. + //39 -> {} 40 -> { hideTabs(player, login) @@ -699,8 +738,8 @@ object TutorialStage { 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 this time; ", - "this is shown by the white text under it. You'll need", + "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." ) ) @@ -708,15 +747,15 @@ object TutorialStage { 43 -> { hideTabs(player, login) - registerHintIcon(player, Location.create(3094, 9502), 75) + 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.", - "" + "surroundings. Speak to the guide for a recap at any time.", ) ) } @@ -728,10 +767,10 @@ object TutorialStage { Component.setUnclosable( player, player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Combat.", - "In this area you will find out about combat, both melee and", - "ranged. Speak to the guide and he'll tell you about it.", + "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.", "" ) ) @@ -740,23 +779,38 @@ object TutorialStage { 45 -> { hideTabs(player, login) removeHintIcon(player) - player.interfaceManager.openTab(Component(Components.WORNITEMS_387)) - setVarbit(player, 1021, 0, 5) - 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.", - "" + 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.", + "" + ) ) - ) + }.also { + hideTabs(player, login) + removeHintIcon(player) + player.interfaceManager.openTab(Component(Components.WORNITEMS_387)) + setVarbit(player, 3756, 5) + Component.setUnclosable( + player, + player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( + "Worn interface", + "You can see what items you are wearing in the worn equipment", + "to the left of the screen, with their combined statistics on the", + "right. Let's add something. Left click your dagger to 'wield' it.", + "" + ) + ) + } } 46 -> { hideTabs(player, login) - setVarbit(player, 1021, 0, 0) + setVarbit(player, 3756, 0) Component.setUnclosable( player, player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( @@ -776,10 +830,11 @@ object TutorialStage { player, player.dialogueInterpreter.sendScrollMessageWithBlueTitle( "You're now holding your dagger.", - "Clothes, armor, weapons and many other items are equipped", + "Clothes, armour, weapons and many other items are equipped", "like this. You can unequip items by clicking on the item in the", - "worn inventory.", - "Speak to the Combat Instructor to continue." + "worn equipment. You can close this window by clicking on the", + "small 'x' in the top-right hand corner. Speak to the Combat", + "Instructor." ) ) } @@ -802,7 +857,7 @@ object TutorialStage { 49 -> { hideTabs(player, login) - setVarbit(player, 1021, 0, 1) + setVarbit(player, 3756, 1) var wepInter = player.getExtension(WeaponInterface::class.java) if(wepInter == null) { @@ -824,7 +879,7 @@ object TutorialStage { 50 -> { hideTabs(player, login) - setVarbit(player, 1021, 0, 0) + setVarbit(player, 3756, 0) registerHintIcon(player, Location.create(3110,9518,0), 75) Component.setUnclosable( player, @@ -860,7 +915,7 @@ object TutorialStage { Component.setUnclosable( player, player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Sit back and watch", + "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", @@ -876,9 +931,9 @@ object TutorialStage { 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.", - "", "" ) ) @@ -889,10 +944,10 @@ object TutorialStage { Component.setUnclosable( player, player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Rat ranging.", + "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", + "ranging gear try killing another rat. Remember: to attack, right", "click on the monster and select attack." ) ) @@ -906,10 +961,10 @@ object TutorialStage { player, player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( "Moving on.", - "You have completed the tasks here, to move on click on the", - "ladder shown.", - "", - "" + "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." ) ) } @@ -925,7 +980,7 @@ object TutorialStage { "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." + "open booth indicated and select 'use'." ) ) } @@ -940,7 +995,7 @@ object TutorialStage { "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 'store'. One you've had a good look, close the", + "and select 'Deposit-1'. Once you've had a good look, close the", "window and move on through the door indicated." ) ) @@ -953,10 +1008,10 @@ object TutorialStage { Component.setUnclosable( player, player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Financial advice.", - "The guide here will tell you about making cash. Just click on", - "him to hear what he's got to say.", + "Financial advice", "", + "The guide here will tell you all about making cash. Just click on", + "him to hear what he's got to say.", "" ) ) @@ -970,8 +1025,8 @@ object TutorialStage { player, player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( "", - "Continue through the next door.", "", + "Continue through the next door.", "", "" ) @@ -987,7 +1042,7 @@ object TutorialStage { 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", + "Once inside talk to the monk. He'll tell you all about the Prayer", "skill.", "" ) @@ -998,13 +1053,13 @@ object TutorialStage { hideTabs(player, login) removeHintIcon(player) player.interfaceManager.openTab(Component(Components.PRAYER_271)) - setVarbit(player, 1021, 0, 6) + setVarbit(player, 3756, 6) Component.setUnclosable( player, player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Your Prayer menu", - "Click on the flashing icon to open the Prayer menu.", + "Your Prayer List", "", + "Click on the flashing icon to open the Prayer List.", "", "" ) @@ -1018,9 +1073,9 @@ object TutorialStage { player, player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( "", - "Your Prayer menu.", - "Talk with Brother Brace and he'll tell you all about prayer.", + "Your Prayer List", "", + "Talk with Brother Brace and he'll tell you all about prayers.", "" ) ) @@ -1030,14 +1085,14 @@ object TutorialStage { hideTabs(player, login) removeHintIcon(player) player.interfaceManager.openTab(Component(Components.FRIENDS2_550)) - setVarbit(player, 1021, 0, 9) + setVarbit(player, 3756, 9) Component.setUnclosable( player, player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( "", - "Friends list.", + "Friends list", "You should now see another new icon. Click on the flashing", - "smiling face to open your friend list.", + "smiling face to open your Friend List.", "" ) ) @@ -1045,16 +1100,16 @@ object TutorialStage { 64 -> { hideTabs(player, login) - setVarbit(player, 1021, 0, 10) + 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", - "the other flashing icon next to the friends list one.", + "This is your Friends List.", "", + "This will be explained by Brother Brace shortly, but first click", + "on the other flashing face in the interface.", "" ) ) @@ -1062,13 +1117,13 @@ object TutorialStage { 65 -> { hideTabs(player, login) - setVarbit(player, 1021, 0, 0) + 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", + "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." @@ -1083,10 +1138,10 @@ object TutorialStage { 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 last instructor.", - "", + "door to find the path leading to your final instructor.", "" ) ) @@ -1100,7 +1155,7 @@ object TutorialStage { player, player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( "Your final instructor!", - "Just follow the path to the wizards house, where you will be", + "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.", "" @@ -1112,14 +1167,14 @@ object TutorialStage { hideTabs(player, login) removeHintIcon(player) player.interfaceManager.openTab(Component(player.spellBookManager.spellBook)) - setVarbit(player, 1021, 0, 7) + setVarbit(player, 3756, 7) Component.setUnclosable( player, player.dialogueInterpreter.sendPlaneMessageWithBlueTitle( - "Open up your final menu.", - "Open up the Magic Spellbook tab by clicking on the flashing spellbook", - "icon next to the Prayer List tab you just learned about.", + "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.", "" ) ) @@ -1146,11 +1201,11 @@ object TutorialStage { 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 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." + "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." ) ) } @@ -1163,9 +1218,9 @@ object TutorialStage { 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.", - "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.", "" ) ) @@ -1219,7 +1274,7 @@ object TutorialStage { val stage = getAttribute(player, "tutorial:stage", 0) val percent = if(stage == 0) 0 else ((stage.toDouble() / 71.0) * 100.0).toInt() val barPercent = if(stage == 0) 0 else (((percent.toDouble() / 100.0) * 20.0).toInt() + 1) - setVarbit(player, 406, 0, barPercent) + setVarp(player, 406, barPercent) setInterfaceText(player, "$percent% Done", 371, 1) } @@ -1241,4 +1296,4 @@ object TutorialStage { { setAttribute(player, "tutorial:hinticon", HintIconManager.registerHintIcon(player, location, 1, -1, player.hintIconManager.freeSlot(), height, 3)) } -} \ No newline at end of file +} diff --git a/Server/src/main/content/region/misc/tutisland/handlers/iface/CharacterDesign.java b/Server/src/main/content/region/misc/tutisland/handlers/iface/CharacterDesign.java index 64e6d6b82..a40f65f84 100644 --- a/Server/src/main/content/region/misc/tutisland/handlers/iface/CharacterDesign.java +++ b/Server/src/main/content/region/misc/tutisland/handlers/iface/CharacterDesign.java @@ -7,6 +7,8 @@ import core.game.node.entity.player.link.appearance.Gender; import core.game.node.item.Item; import core.tools.RandomFunction; +import static core.api.ContentAPIKt.*; + /** * Represents the class used to handle the character design. * @author Emperor @@ -147,9 +149,26 @@ public final class CharacterDesign { player.getPacketDispatch().sendInterfaceConfig(771, 22, false); player.getPacketDispatch().sendInterfaceConfig(771, 92, false); player.getPacketDispatch().sendInterfaceConfig(771, 97, false); - player.getConfigManager().set(1262, player.getAppearance().isMale()); + setVarp(player, 1262, player.getAppearance().isMale() ? 1 : 0); } + public static void reopen(final Player player) { + player.removeAttribute("char-design:accepted"); + player.getPacketDispatch().sendPlayerOnInterface(771, 79); + player.getPacketDispatch().sendAnimationInterface(9806, 771, 79); + Component c = player.getInterfaceManager().openComponent(771); + if (c != null) { + c.setCloseEvent((player1, c1) -> { // Unclosable! + return player1.getAttribute("char-design:accepted", false); + }); + } + player.getPacketDispatch().sendInterfaceConfig(771, 22, false); + player.getPacketDispatch().sendInterfaceConfig(771, 92, false); + player.getPacketDispatch().sendInterfaceConfig(771, 97, false); + setVarp(player, 1262, player.getAppearance().isMale() ? 1 : 0); + } + + /** * Handles the buttons. */ @@ -226,7 +245,15 @@ public final class CharacterDesign { */ private static void changeGender(Player player, boolean male) { player.setAttribute("male", male); - player.getConfigManager().set(1262, male ? 1 : -1); + setVarp(player, 1262, male ? 1 : 0); + if (male) { + setVarbit(player, 5008, 1); + setVarbit(player, 5009, 0); + } + else { + setVarbit(player, 5008, 0); + setVarbit(player, 5009, 1); + } reset(player); } diff --git a/Server/src/main/content/region/misc/zanaris/dialogue/BlaecDialogue.kt b/Server/src/main/content/region/misc/zanaris/dialogue/BlaecDialogue.kt index 6fb736c4f..ce6c7f48b 100644 --- a/Server/src/main/content/region/misc/zanaris/dialogue/BlaecDialogue.kt +++ b/Server/src/main/content/region/misc/zanaris/dialogue/BlaecDialogue.kt @@ -12,14 +12,14 @@ import org.rs09.consts.NPCs */ @Initializable -class BlaecDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class BlaecDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC when ((1..3).random()) { - 1 -> npc(core.game.dialogue.FacialExpression.FRIENDLY,"Wunnerful weather we're having today!").also { stage = 99 } - 2 -> npc(core.game.dialogue.FacialExpression.FRIENDLY,"Greetin's " + player.name + ", fine day today!").also { stage = 99 } - 3 -> npcl(core.game.dialogue.FacialExpression.ANNOYED,"Please leave me alone, I'm busy trapping the pygmy shrews.").also { stage = 99 } + 1 -> npc(FacialExpression.FRIENDLY,"Wunnerful weather we're having today!").also { stage = 99 } + 2 -> npc(FacialExpression.FRIENDLY,"Greetin's, " + player.username + ", fine day today!").also { stage = 99 } + 3 -> npcl(FacialExpression.ANNOYED,"Please leave me alone, I'm busy trapping the pygmy shrews.").also { stage = 99 } } return true } @@ -32,7 +32,7 @@ class BlaecDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin( return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return BlaecDialogue(player) } diff --git a/Server/src/main/content/region/misc/zanaris/dialogue/CoOrdinatorDialogue.kt b/Server/src/main/content/region/misc/zanaris/dialogue/CoOrdinatorDialogue.kt index 929b91cc9..2d190847d 100644 --- a/Server/src/main/content/region/misc/zanaris/dialogue/CoOrdinatorDialogue.kt +++ b/Server/src/main/content/region/misc/zanaris/dialogue/CoOrdinatorDialogue.kt @@ -12,11 +12,11 @@ import org.rs09.consts.NPCs */ @Initializable -class CoOrdinatorDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class CoOrdinatorDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - player(core.game.dialogue.FacialExpression.FRIENDLY, "Hello, what are you doing?").also { stage = 0 } + player(FacialExpression.FRIENDLY, "Hello, what are you doing?").also { stage = 0 } return true } @@ -24,11 +24,11 @@ class CoOrdinatorDialogue(player: Player? = null) : core.game.dialogue.DialogueP when(stage){ 0 -> { when ((1..5).random()) { - 1 -> npcl(core.game.dialogue.FacialExpression.OLD_DISTRESSED,"Sorry, I don't have time for idle chit-chat, I need to find a Winter Fairy to send to Trollheim!").also { stage = 99 } - 2 -> npcl(core.game.dialogue.FacialExpression.OLD_DISTRESSED,"Sorry, I don't have time for idle chit-chat, I need to send a fairy to get little Freddie's tooth!").also { stage = 99 } - 3 -> npcl(core.game.dialogue.FacialExpression.OLD_DISTRESSED,"Sorry, I don't have time for idle chit-chat, I need to send an Autumn Fairy off to Burthorpe!").also { stage = 99 } - 4 -> npcl(core.game.dialogue.FacialExpression.OLD_DISTRESSED,"Sorry, I don't have time to talk, I need to send a Tooth Fairy to visit Sarah-Jane!").also { stage = 99 } - 5 -> npcl(core.game.dialogue.FacialExpression.OLD_DISTRESSED,"Sorry, I don't have time to stop, I need to send a weather fairy off to Etceteria!").also { stage = 99 } + 1 -> npcl(FacialExpression.OLD_DISTRESSED,"Sorry, I don't have time for idle chit-chat, I need to find a Winter Fairy to send to Trollheim!").also { stage = 99 } + 2 -> npcl(FacialExpression.OLD_DISTRESSED,"Sorry, I don't have time for idle chit-chat, I need to send a fairy to get little Freddie's tooth!").also { stage = 99 } + 3 -> npcl(FacialExpression.OLD_DISTRESSED,"Sorry, I don't have time for idle chit-chat, I need to send an Autumn Fairy off to Burthorpe!").also { stage = 99 } + 4 -> npcl(FacialExpression.OLD_DISTRESSED,"Sorry, I don't have time to talk, I need to send a Tooth Fairy to visit Sarah-Jane!").also { stage = 99 } + 5 -> npcl(FacialExpression.OLD_DISTRESSED,"Sorry, I don't have time to stop, I need to send a weather fairy off to Etceteria!").also { stage = 99 } } } @@ -37,7 +37,7 @@ class CoOrdinatorDialogue(player: Player? = null) : core.game.dialogue.DialogueP return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return CoOrdinatorDialogue(player) } diff --git a/Server/src/main/content/region/misc/zanaris/dialogue/FairyChefDialogue.kt b/Server/src/main/content/region/misc/zanaris/dialogue/FairyChefDialogue.kt index 95f0c2b8e..8db8031b0 100644 --- a/Server/src/main/content/region/misc/zanaris/dialogue/FairyChefDialogue.kt +++ b/Server/src/main/content/region/misc/zanaris/dialogue/FairyChefDialogue.kt @@ -12,11 +12,11 @@ import org.rs09.consts.NPCs */ @Initializable -class FairyChefDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class FairyChefDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - npcl(core.game.dialogue.FacialExpression.OLD_DEFAULT,"'Ello, sugar. I'm afraid I can't gossip right now, I've got a cake in the oven.").also { stage = 99 } + npcl(FacialExpression.OLD_DEFAULT,"'Ello, sugar. I'm afraid I can't gossip right now, I've got a cake in the oven.").also { stage = 99 } return true } @@ -28,7 +28,7 @@ class FairyChefDialogue(player: Player? = null) : core.game.dialogue.DialoguePlu return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return FairyChefDialogue(player) } diff --git a/Server/src/main/content/region/misc/zanaris/dialogue/FairyDialogue.kt b/Server/src/main/content/region/misc/zanaris/dialogue/FairyDialogue.kt index 84708542c..ff10ef193 100644 --- a/Server/src/main/content/region/misc/zanaris/dialogue/FairyDialogue.kt +++ b/Server/src/main/content/region/misc/zanaris/dialogue/FairyDialogue.kt @@ -12,38 +12,38 @@ import org.rs09.consts.NPCs */ @Initializable -class FairyDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class FairyDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC when ((1..7).random()) { - 1 -> npcl(core.game.dialogue.FacialExpression.OLD_CALM_TALK1,"Err, hello. Do you have any idea what a Winter Fairy actually does?").also { stage = 10 } - 2 -> npcl(core.game.dialogue.FacialExpression.OLD_DISTRESSED,"Have you got any idea where Burthorpe is? The co-ordinator says I need to go there and paint all the leaves yellow, but I've never heard of that place!").also { stage = 99 } - 3 -> npcl(core.game.dialogue.FacialExpression.OLD_DISTRESSED,"Be careful when you pick mushrooms near the strange ruins to the south. Some of them will try to eat you!").also { stage = 99 } - 4 -> npcl(core.game.dialogue.FacialExpression.OLD_DISTRESSED,"I'm afraid I can't stop to chat. I've just been told to get ready to go to Etceteria for some reason or other!").also { stage = 99 } - 5 -> npcl(core.game.dialogue.FacialExpression.OLD_CALM_TALK2,"Picking mushrooms near the forge will probably end quite badly for you. Not as bad as the ones by the strange ruins to the south though. They're much bigger.").also { stage = 99 } - 6 -> npc(core.game.dialogue.FacialExpression.OLD_DISTRESSED,"Sorry, I can't talk I'm looking for Sarah-Jane!").also { stage = 99 } - 7 -> npc(core.game.dialogue.FacialExpression.OLD_CALM_TALK2,"Is your name Freddie, and are you losing your teeth?").also { stage = 20 } + 1 -> npcl(FacialExpression.OLD_CALM_TALK1,"Err, hello. Do you have any idea what a Winter Fairy actually does?").also { stage = 10 } + 2 -> npcl(FacialExpression.OLD_DISTRESSED,"Have you got any idea where Burthorpe is? The co-ordinator says I need to go there and paint all the leaves yellow, but I've never heard of that place!").also { stage = 99 } + 3 -> npcl(FacialExpression.OLD_DISTRESSED,"Be careful when you pick mushrooms near the strange ruins to the south. Some of them will try to eat you!").also { stage = 99 } + 4 -> npcl(FacialExpression.OLD_DISTRESSED,"I'm afraid I can't stop to chat. I've just been told to get ready to go to Etceteria for some reason or other!").also { stage = 99 } + 5 -> npcl(FacialExpression.OLD_CALM_TALK2,"Picking mushrooms near the forge will probably end quite badly for you. Not as bad as the ones by the strange ruins to the south though. They're much bigger.").also { stage = 99 } + 6 -> npc(FacialExpression.OLD_DISTRESSED,"Sorry, I can't talk I'm looking for Sarah-Jane!").also { stage = 99 } + 7 -> npc(FacialExpression.OLD_CALM_TALK2,"Is your name Freddie, and are you losing your teeth?").also { stage = 20 } } return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 10 -> player(core.game.dialogue.FacialExpression.THINKING, "No, no idea at all.").also { stage++ } - 11 -> npcl(core.game.dialogue.FacialExpression.OLD_DISTRESSED,"Oh, pity. Neither do I, but I'm supposed to be one this week.").also { stage = 99 } + 10 -> player(FacialExpression.THINKING, "No, no idea at all.").also { stage++ } + 11 -> npcl(FacialExpression.OLD_DISTRESSED,"Oh, pity. Neither do I, but I'm supposed to be one this week.").also { stage = 99 } - 20 -> player(core.game.dialogue.FacialExpression.THINKING, "Err, no. I'm afraid not.").also { stage++ } - 21 -> npcl(core.game.dialogue.FacialExpression.OLD_CALM_TALK2, "Oh, right. You don't have any idea where I can find him do you?").also { stage++ } - 22 -> player(core.game.dialogue.FacialExpression.NEUTRAL, "No, I don't know that either.").also { stage++ } - 23 -> npcl(core.game.dialogue.FacialExpression.OLD_CALM_TALK1, "Oh, ok then. If you do find out please let me know. The co-ordinator has sent me to find him.").also { stage = 99 } + 20 -> player(FacialExpression.THINKING, "Err, no. I'm afraid not.").also { stage++ } + 21 -> npcl(FacialExpression.OLD_CALM_TALK2, "Oh, right. You don't have any idea where I can find him do you?").also { stage++ } + 22 -> player(FacialExpression.NEUTRAL, "No, I don't know that either.").also { stage++ } + 23 -> npcl(FacialExpression.OLD_CALM_TALK1, "Oh, ok then. If you do find out please let me know. The co-ordinator has sent me to find him.").also { stage = 99 } 99 -> end() } return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return FairyDialogue(player) } diff --git a/Server/src/main/content/region/misc/zanaris/dialogue/FairyFixit.kt b/Server/src/main/content/region/misc/zanaris/dialogue/FairyFixit.kt index 0ca46939a..f0e851ce1 100644 --- a/Server/src/main/content/region/misc/zanaris/dialogue/FairyFixit.kt +++ b/Server/src/main/content/region/misc/zanaris/dialogue/FairyFixit.kt @@ -12,7 +12,7 @@ import org.rs09.consts.NPCs */ @Initializable -class FairyFixit(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class FairyFixit(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC @@ -21,9 +21,9 @@ class FairyFixit(player: Player? = null) : core.game.dialogue.DialoguePlugin(pla //post quest dialogue is missing for rs3 and osrs is too modern, so i'm just gonna use osrs as a placeholder var completedFairyQueen = false if (completedFairyQueen) { - npc(core.game.dialogue.FacialExpression.OLD_CALM_TALK1, "Pssst! Human! I've got something for you.").also { stage = 20 } + npc(FacialExpression.OLD_CALM_TALK1, "Pssst! Human! I've got something for you.").also { stage = 20 } } else { - npc(core.game.dialogue.FacialExpression.OLD_DISTRESSED, "What is it, human? Busy busy busy!").also { stage = 0 } + npc(FacialExpression.OLD_DISTRESSED, "What is it, human? Busy busy busy!").also { stage = 0 } } return true } @@ -32,25 +32,25 @@ class FairyFixit(player: Player? = null) : core.game.dialogue.DialoguePlugin(pla when(stage){ 0 -> options("Why are you carrying that toolbox?", "I'm okay, thanks.").also { stage++ } 1 -> when (buttonId){ - 1 -> player(core.game.dialogue.FacialExpression.ASKING,"Why are you carrying that toolbox?").also { stage = 10 } - 2 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "I'm okay, thanks.").also { stage = 99 } + 1 -> player(FacialExpression.ASKING,"Why are you carrying that toolbox?").also { stage = 10 } + 2 -> player(FacialExpression.FRIENDLY, "I'm okay, thanks.").also { stage = 99 } } - 10 -> npc(core.game.dialogue.FacialExpression.OLD_DEFAULT, "It's the fizgog! It's picking up cable again!").also { stage++ } - 11 -> playerl(core.game.dialogue.FacialExpression.ASKING, "Uh, right. So is it safe to use the fairy rings then?").also { stage++ } - 12 -> npcl(core.game.dialogue.FacialExpression.OLD_CALM_TALK1, "Sure, as long as you have been given permission to use them. You should just be aware that using the fairy rings sometimes has strange results - the locations that you have been to may").also { stage++ } - 13 -> npcl(core.game.dialogue.FacialExpression.OLD_CALM_TALK2, "affect the locations you are trying to reach. I could fix it by replacing the fizgog and the whosprangit; I've put in a request for some new parts, but they're").also { stage++ } - 14 -> npc(core.game.dialogue.FacialExpression.OLD_CALM_TALK1, "pretty hard to get hold of it seems.").also { stage = 99 } + 10 -> npc(FacialExpression.OLD_DEFAULT, "It's the fizgog! It's picking up cable again!").also { stage++ } + 11 -> playerl(FacialExpression.ASKING, "Uh, right. So is it safe to use the fairy rings then?").also { stage++ } + 12 -> npcl(FacialExpression.OLD_CALM_TALK1, "Sure, as long as you have been given permission to use them. You should just be aware that using the fairy rings sometimes has strange results - the locations that you have been to may").also { stage++ } + 13 -> npcl(FacialExpression.OLD_CALM_TALK2, "affect the locations you are trying to reach. I could fix it by replacing the fizgog and the whosprangit; I've put in a request for some new parts, but they're").also { stage++ } + 14 -> npc(FacialExpression.OLD_CALM_TALK1, "pretty hard to get hold of it seems.").also { stage = 99 } 20 -> options("What have you got for me?", "Why are you carrying that toolbox?", "Not interested, thanks.").also { stage++ } 21 -> when (buttonId) { - 1 -> player(core.game.dialogue.FacialExpression.ASKING, "What have you got for me?").also { stage = 30 } - 2 -> player(core.game.dialogue.FacialExpression.ASKING,"Why are you carrying that toolbox?").also { stage = 10 } - 3 -> player(core.game.dialogue.FacialExpression.NEUTRAL, "Not interested, thanks.").also { stage = 99 } + 1 -> player(FacialExpression.ASKING, "What have you got for me?").also { stage = 30 } + 2 -> player(FacialExpression.ASKING,"Why are you carrying that toolbox?").also { stage = 10 } + 3 -> player(FacialExpression.NEUTRAL, "Not interested, thanks.").also { stage = 99 } } - 30 -> npcl(core.game.dialogue.FacialExpression.OLD_CALM_TALK1, "They said you'd helped cure our Queen. I haven't got a lot of rewards to offer, but my enchantment scrolls might help if you're working with fairy rings in your home.").also { stage++ } + 30 -> npcl(FacialExpression.OLD_CALM_TALK1, "They said you'd helped cure our Queen. I haven't got a lot of rewards to offer, but my enchantment scrolls might help if you're working with fairy rings in your home.").also { stage++ } 31 -> { end() npc.openShop(player) @@ -62,7 +62,7 @@ class FairyFixit(player: Player? = null) : core.game.dialogue.DialoguePlugin(pla return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return FairyFixit(player) } diff --git a/Server/src/main/content/region/misc/zanaris/dialogue/FairyQueenDialogue.kt b/Server/src/main/content/region/misc/zanaris/dialogue/FairyQueenDialogue.kt new file mode 100644 index 000000000..7154caa96 --- /dev/null +++ b/Server/src/main/content/region/misc/zanaris/dialogue/FairyQueenDialogue.kt @@ -0,0 +1,70 @@ +package content.region.misc.zanaris.dialogue + +import core.api.isQuestComplete +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 content.data.Quests + +@Initializable +class FairyQueenDialogue(player: Player? = null) : DialoguePlugin(player) { + + override fun newInstance(player: Player?): DialoguePlugin { + return FairyQueenDialogue(player) + } + + override fun open(vararg args: Any?): Boolean { + npc = args[0] as NPC + + 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 } + } else { + playerl( + FacialExpression.ASKING, "Have you managed to work out a plan yet?" + ).also { stage = 3 } + } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage) { + 0 -> when (buttonId) { + 1 -> playerl( + FacialExpression.ASKING, "How do crops and such survive down here?" + ).also { stage = 1 } + + 2 -> playerl( + FacialExpression.ASKING, "What's so good about this place?" + ).also { stage = 2 } + } + + 1 -> npcl( + FacialExpression.OLD_DEFAULT, + "Clearly you come from a plane dependent on sunlight. Down here, the plants grow in the aura of faerie." + ).also { stage = END_DIALOGUE } + + 2 -> npcl( + FacialExpression.OLD_DEFAULT, + " Zanaris is a meeting point of cultures. Those from many worlds converge here to exchange knowledge and goods." + ).also { stage = END_DIALOGUE } + + 3 -> npcl( + FacialExpression.OLD_DEFAULT, + "Not yet," + player.username + ", but it looks like we'll need to ask you for your help again, I'm afraid. I'll be able to tell you more once we have finished making our battle plans." + ).also { stage = END_DIALOGUE } + } + return true + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.FAIRY_QUEEN_4437) + } + +} \ No newline at end of file diff --git a/Server/src/main/content/region/misc/zanaris/dialogue/FairyShopAssistantDialogue.kt b/Server/src/main/content/region/misc/zanaris/dialogue/FairyShopAssistantDialogue.kt index a1d436613..ea0d18c04 100644 --- a/Server/src/main/content/region/misc/zanaris/dialogue/FairyShopAssistantDialogue.kt +++ b/Server/src/main/content/region/misc/zanaris/dialogue/FairyShopAssistantDialogue.kt @@ -12,11 +12,11 @@ import org.rs09.consts.NPCs */ @Initializable -class FairyShopAssistantDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class FairyShopAssistantDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - npc(core.game.dialogue.FacialExpression.OLD_NORMAL,"Can I help you at all?").also { stage = 0 } + npc(FacialExpression.OLD_HAPPY,"Can I help you at all?").also { stage = 0 } return true } @@ -26,7 +26,7 @@ class FairyShopAssistantDialogue(player: Player? = null) : core.game.dialogue.Di 1 -> when (buttonId) { 1 -> end().also { npc.openShop(player) } - 2 -> player(core.game.dialogue.FacialExpression.NEUTRAL, "No thanks.").also { stage = 99 } + 2 -> player(FacialExpression.NEUTRAL, "No thanks.").also { stage = 99 } } 99 -> end() @@ -34,11 +34,11 @@ class FairyShopAssistantDialogue(player: Player? = null) : core.game.dialogue.Di return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return FairyShopAssistantDialogue(player) } override fun getIds(): IntArray { - return intArrayOf(NPCs.FAIRY_SHOP_ASSISTANT_535) + return intArrayOf(NPCs.FAIRY_SHOP_ASSISTANT_535, NPCs.FAIRY_SHOPKEEPER_534) } } diff --git a/Server/src/main/content/region/misc/zanaris/handlers/EvilChickenLairListener.kt b/Server/src/main/content/region/misc/zanaris/handlers/EvilChickenLairListener.kt new file mode 100644 index 000000000..bdc56980b --- /dev/null +++ b/Server/src/main/content/region/misc/zanaris/handlers/EvilChickenLairListener.kt @@ -0,0 +1,53 @@ +package content.region.misc.zanaris.handlers + +import core.api.* +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +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 +import org.rs09.consts.Items +import org.rs09.consts.Scenery +import content.data.Quests + +class EvilChickenLairListener: InteractionListener { + override fun defineListeners() { + + addClimbDest(Location.create(2441, 4381, 0), Location.create(2457, 4380, 0)) + 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, Quests.LEGENDS_QUEST)) + return@onUseWith false + + if(removeItem(player,(Item(Items.RAW_CHICKEN_2138)))){ + animate(player, Animation(10100)) + + submitWorldPulse(object : Pulse(1, player) { + override fun pulse(): Boolean { + teleport(player, Location.create(2461, 4356, 0)) + animate(player, Animation(9013)) + return true + } + }) + } + 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) + return@onUseWith true + } + + on(Scenery.PORTAL_12260, IntType.SCENERY, "use") { player, _ -> + teleport(player, Location.create(2453, 4476, 0)) + return@on true + } + + } +} 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 a6b08daf5..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,23 +1,26 @@ package content.region.misc.zanaris.handlers -import core.api.isEquipped -import core.api.isQuestComplete -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 { @@ -25,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, _ -> @@ -34,36 +38,31 @@ 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(!isQuestComplete(player,"Lost City")) { // 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 } - if(!isEquipped(player, Items.DRAMEN_STAFF_772) && !isEquipped(player,Items.LUNAR_STAFF_9084)) { + if (!anyInEquipment(player, Items.DRAMEN_STAFF_772, Items.LUNAR_STAFF_9084)) { player.sendMessage("The fairy ring only works for those who wield fairy magic.") return false } return true } - private fun reset(player: Player) { - player.removeAttribute("fairy-delay") - player.removeAttribute("fairy_location_combo") - for (i in 0..2) { - player.configManager[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.java b/Server/src/main/content/region/misthalin/barbvillage/dialogue/PeksaDialogue.java deleted file mode 100644 index 00779051a..000000000 --- a/Server/src/main/content/region/misthalin/barbvillage/dialogue/PeksaDialogue.java +++ /dev/null @@ -1,82 +0,0 @@ -package content.region.misthalin.barbvillage.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 plugin used for peksa. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class PeksaDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code PeksaDialogue} {@code Object}. - */ - public PeksaDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code PeksaDialogue} {@code Object}. - * @param player the player. - */ - public PeksaDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new PeksaDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Are you interested in buying or selling a helmet?"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendOptions("Select an Option", "I could be, yes.", "No, I'll pass on that."); - stage = 1; - break; - case 1: - switch (buttonId) { - case 1: - end(); - npc.openShop(player); - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No, I'll pass on that."); - stage = 20; - break; - } - break; - case 20: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Well, come back if you change your mind."); - stage = 21; - break; - case 21: - end(); - break; - } - - return true; - } - - @Override - public int[] getIds() { - return new int[] { 538 }; - } -} diff --git a/Server/src/main/content/region/misthalin/barbvillage/dialogue/PeksaDialogue.kt b/Server/src/main/content/region/misthalin/barbvillage/dialogue/PeksaDialogue.kt new file mode 100644 index 000000000..f40c15c12 --- /dev/null +++ b/Server/src/main/content/region/misthalin/barbvillage/dialogue/PeksaDialogue.kt @@ -0,0 +1,64 @@ +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 +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 content.data.Quests + +@Initializable +class PeksaDialogue(player: Player? = null) : DialoguePlugin(player){ + + companion object { + const val GO_SHOPPING = 10 + const val LEAVE = 20 + const val DIALOGUE_SCORPION_CATCHER = 30 + } + + override fun open(vararg args: Any): Boolean { + npc = args[0] as NPC + npcl(FacialExpression.HALF_GUILTY, "Are you interested in buying or selling a helmet?").also { stage = START_DIALOGUE } + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage) { + START_DIALOGUE -> { + showTopics( + Topic("I could be, yes.", GO_SHOPPING), + Topic("No, I'll pass on that.", LEAVE), + 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) + ) + } + + GO_SHOPPING -> { + end() + npc.openShop(player) + } + + LEAVE -> { + npcl(FacialExpression.HALF_GUILTY, "Well, come back if you change your mind.").also { stage = END_DIALOGUE } + } + + DIALOGUE_SCORPION_CATCHER -> { + openDialogue(player, SCPeksaDialogue(getQuestStage(player, Quests.SCORPION_CATCHER)), npc) + } + } + + return true + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.PEKSA_538) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misthalin/barbvillage/stronghold/BoxOfHealthDialogue.java b/Server/src/main/content/region/misthalin/barbvillage/stronghold/BoxOfHealthDialogue.java index 47dc6d842..3647f087d 100644 --- a/Server/src/main/content/region/misthalin/barbvillage/stronghold/BoxOfHealthDialogue.java +++ b/Server/src/main/content/region/misthalin/barbvillage/stronghold/BoxOfHealthDialogue.java @@ -1,9 +1,12 @@ package content.region.misthalin.barbvillage.stronghold; +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; /** * Represents the dialogue plugin used for the box of health. @@ -12,11 +15,6 @@ import core.game.node.item.Item; */ public final class BoxOfHealthDialogue extends DialoguePlugin { - /** - * Represents the coins to recieve. - */ - private static final Item COINS = new Item(995, 5000); - /** * Constructs a new {@code BoxOfHealth} {@code Object}. */ @@ -50,13 +48,12 @@ public final class BoxOfHealthDialogue extends DialoguePlugin { public boolean handle(int interfaceId, int buttonId) { switch (stage) { case 0: - if (player.getInventory().freeSlots() == 0) { + if (!addItem(player, Items.COINS_995, 5000, Container.INVENTORY)) { player.getPacketDispatch().sendMessage("You don't have enough inventory space."); end(); break; } stage = 1; - player.getInventory().add(COINS); interpreter.sendDialogue("...congratulations adventurer, you have been deemed worthy of this", "reward. You have also unlocked the Idea emote!"); player.getEmoteManager().unlock(Emotes.IDEA); player.getSavedData().getGlobalData().getStrongHoldRewards()[2] = true; diff --git a/Server/src/main/content/region/misthalin/barbvillage/stronghold/CradleOfLifeDialogue.java b/Server/src/main/content/region/misthalin/barbvillage/stronghold/CradleOfLifeDialogue.java index 193bfb984..9ff2f0255 100644 --- a/Server/src/main/content/region/misthalin/barbvillage/stronghold/CradleOfLifeDialogue.java +++ b/Server/src/main/content/region/misthalin/barbvillage/stronghold/CradleOfLifeDialogue.java @@ -43,8 +43,11 @@ public final class CradleOfLifeDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { - if (player.getSavedData().getGlobalData().getStrongHoldRewards()[3]) { - player.getEmoteManager().unlock(Emotes.STOMP); + if (player.getSavedData().getGlobalData().getStrongHoldRewards()[3] && player.getSavedData().getGlobalData().getStrongHoldRewards()[2] && player.getSavedData().getGlobalData().getStrongHoldRewards()[1] && player.getSavedData().getGlobalData().getStrongHoldRewards()[0]) { + // Unlocks emotes for older accounts who don't have the previous level emotes + player.getEmoteManager().unlock(Emotes.FLAP); + player.getEmoteManager().unlock(Emotes.SLAP_HEAD); + player.getEmoteManager().unlock(Emotes.IDEA); } if (player.getInventory().freeSlots() == 0) { player.getPacketDispatch().sendMessage("You don't have enough inventory space."); 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 93d9d33fb..1adee0589 100644 --- a/Server/src/main/content/region/misthalin/barbvillage/stronghold/GiftOfPeaceDialogue.java +++ b/Server/src/main/content/region/misthalin/barbvillage/stronghold/GiftOfPeaceDialogue.java @@ -1,9 +1,13 @@ package content.region.misthalin.barbvillage.stronghold; +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; /** * Represents the gift of peace dialogue plugin. @@ -12,11 +16,6 @@ import core.game.node.item.Item; */ public final class GiftOfPeaceDialogue extends DialoguePlugin { - /** - * Represents the coins item. - */ - private static final Item COINS = new Item(995, 2000); - /** * Constructs a new {@code GiftOfPeaceDialogue} {@code Object}. */ @@ -50,14 +49,13 @@ public final class GiftOfPeaceDialogue extends DialoguePlugin { public boolean handle(int interfaceId, int buttonId) { switch (stage) { case 0: - if (player.getInventory().freeSlots() == 0) { + if (!addItem(player, Items.COINS_995, 2000, Container.INVENTORY)) { player.getPacketDispatch().sendMessage("You don't have enough inventory space."); end(); break; } interpreter.sendDialogue("...congratulations adventurer, you have been deemed worthy of this", "reward. You have also unlocked the Flap emote!"); stage = 1; - player.getInventory().add(COINS); player.getEmoteManager().unlock(Emotes.FLAP); player.getSavedData().getGlobalData().getStrongHoldRewards()[0] = true; break; 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 05a585c6c..8cef49bf4 100644 --- a/Server/src/main/content/region/misthalin/barbvillage/stronghold/GrainOfPlentyDialogue.java +++ b/Server/src/main/content/region/misthalin/barbvillage/stronghold/GrainOfPlentyDialogue.java @@ -1,9 +1,13 @@ package content.region.misthalin.barbvillage.stronghold; +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; /** * Represents the grain of plenty dialogue plugin. @@ -12,10 +16,6 @@ import core.game.node.item.Item; */ public final class GrainOfPlentyDialogue extends DialoguePlugin { - /** - * Represents the coins item. - */ - private static final Item COINS = new Item(995, 3000); /** * Constructs a new {@code GrainOfPlenty} {@code Object}. @@ -50,7 +50,7 @@ public final class GrainOfPlentyDialogue extends DialoguePlugin { public boolean handle(int interfaceId, int buttonId) { switch (stage) { case 0: - if (player.getInventory().freeSlots() == 0) { + if (!addItem(player, Items.COINS_995, 3000, Container.INVENTORY)) { player.getPacketDispatch().sendMessage("You don't have enough inventory space."); end(); break; @@ -58,7 +58,6 @@ public final class GrainOfPlentyDialogue extends DialoguePlugin { 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!"); stage = 1; - player.getInventory().add(COINS); player.getEmoteManager().unlock(Emotes.SLAP_HEAD); break; case 1: diff --git a/Server/src/main/content/region/misthalin/barbvillage/stronghold/StrongHoldSecurityPlugin.java b/Server/src/main/content/region/misthalin/barbvillage/stronghold/StrongHoldSecurityPlugin.java index 86127ad17..2845b5e49 100644 --- a/Server/src/main/content/region/misthalin/barbvillage/stronghold/StrongHoldSecurityPlugin.java +++ b/Server/src/main/content/region/misthalin/barbvillage/stronghold/StrongHoldSecurityPlugin.java @@ -13,6 +13,7 @@ import core.game.interaction.OptionHandler; import core.game.node.Node; import core.game.node.entity.Entity; import core.game.node.entity.player.Player; +import core.game.node.entity.player.link.music.MusicPlayer; import core.game.node.item.Item; import core.game.node.scenery.Scenery; import core.game.system.task.Pulse; @@ -22,10 +23,16 @@ 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.update.flag.context.Animation; +import core.net.packet.PacketRepository; +import core.net.packet.context.MusicContext; +import core.net.packet.out.MusicPacket; import core.plugin.Plugin; import core.plugin.ClassScanner; import core.plugin.Initializable; import core.tools.RandomFunction; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.playAudio; /** * Handles the strong hold of security. @@ -122,22 +129,38 @@ public final class StrongHoldSecurityPlugin extends MapZone implements Plugin { + 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 ada88a5e8..000000000 --- a/Server/src/main/content/region/misthalin/barbvillage/stronghold/playersafety/GuardDialoguePlugin.java +++ /dev/null @@ -1,141 +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.SystemLogger; -import core.game.system.task.Pulse; -import core.game.world.GameWorld; - -/** - * @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: - SystemLogger.logErr(this.getClass(), "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 b43269175..000000000 --- a/Server/src/main/content/region/misthalin/barbvillage/stronghold/playersafety/PSOptionHandler.java +++ /dev/null @@ -1,173 +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; - -/** - * @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 = player.getConfigManager().get(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); - } - player.getConfigManager().set(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 52c459993..000000000 --- a/Server/src/main/content/region/misthalin/barbvillage/stronghold/playersafety/ProfessorHenryDialogue.java +++ /dev/null @@ -1,211 +0,0 @@ -package content.region.misthalin.barbvillage.stronghold.playersafety; - -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; - -/** - * @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().remove(new Item(12626, 1000))) { - 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 ", "2009scape."); - 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) { - player.getConfigManager().set(1203, 1 << 29, true); - player.getSavedData().getGlobalData().setTestStage(3); - player.getQuestRepository().syncronizeTab(player); - player.getConfigManager().set(101, player.getQuestRepository().getPoints()); - 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..50b4b0406 --- /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.name}, 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.name}. 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 + + 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) + + } +} \ No newline at end of file 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..0b4134a0e --- /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/dialogue/ResearcherDialogue.java b/Server/src/main/content/region/misthalin/dialogue/ResearcherDialogue.java deleted file mode 100644 index fde14e0eb..000000000 --- a/Server/src/main/content/region/misthalin/dialogue/ResearcherDialogue.java +++ /dev/null @@ -1,98 +0,0 @@ -package content.region.misthalin.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 for the new Researcher NPC that sells unobtainable items. - * @author Splinter - * @version 1.0 - */ -@Initializable -public final class ResearcherDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code ResearcherDialogue} {@code Object}. - */ - public ResearcherDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code ResearcherDialogue} {@code Object}. - * @param player the player. - */ - public ResearcherDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new ResearcherDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Hello."); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - player("Hello, are you new around these parts?"); - stage = 1; - break; - case 1: - npc("I am indeed. I am a traveling researcher studying the", "lands of "+GameWorld.getSettings().getName()+"."); - stage = 2; - break; - case 2: - player("That's amazing! Surely you must have collected a few", "trinkets in your travels."); - stage = 3; - break; - case 3: - npc("I have. I suppose I could let you take a look at them","if you're interested."); - stage = 4; - break; - case 4: - interpreter.sendOptions("Select an Option", "Look at wares.", "Decline."); - stage = 5; - break; - case 5: - switch(buttonId){ - case 1: - player("Alright, show me your wares."); - stage = 6; - break; - case 2: - player("On second thought, I really must be going."); - stage = 7; - break; - } - break; - case 6: - end(); - npc.openShop(player); - break; - case 7: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 4568 }; - } -} diff --git a/Server/src/main/content/region/misthalin/digsite/dialogue/ArchaeologistcalExpertUsedOnDialogueFile.kt b/Server/src/main/content/region/misthalin/digsite/dialogue/ArchaeologistcalExpertUsedOnDialogueFile.kt index bd7a3597c..c16b62b2d 100644 --- a/Server/src/main/content/region/misthalin/digsite/dialogue/ArchaeologistcalExpertUsedOnDialogueFile.kt +++ b/Server/src/main/content/region/misthalin/digsite/dialogue/ArchaeologistcalExpertUsedOnDialogueFile.kt @@ -1,6 +1,6 @@ package content.region.misthalin.digsite.dialogue -import content.global.handlers.item.withnpc.ArchaeologicalExpertListener +import content.region.misthalin.digsite.quest.thedigsite.ArchaeologicalExpertListener import core.api.addItemOrDrop import core.api.sendDialogue import core.game.dialogue.FacialExpression diff --git a/Server/src/main/content/region/misthalin/digsite/dialogue/ChemicalsBook.kt b/Server/src/main/content/region/misthalin/digsite/dialogue/ChemicalsBook.kt new file mode 100644 index 000000000..a13b7c858 --- /dev/null +++ b/Server/src/main/content/region/misthalin/digsite/dialogue/ChemicalsBook.kt @@ -0,0 +1,78 @@ +package content.region.misthalin.digsite.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.api.setAttribute +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.player.Player +import org.rs09.consts.Items + +class ChemicalsBook : InteractionListener { + companion object { + private val TITLE = "Volatile chemicals Experimental Test Notes" + private val CONTENTS = arrayOf( + PageSet( + Page( + BookLine("Volatile Chemicals:", 55), + BookLine("Experimental Test Notes.", 56), + BookLine("In order to ease the mining", 58), + BookLine("process, my colleagues and", 59), + BookLine("I decided we needed something", 60), + BookLine("stronger than picks to delve", 61), + BookLine("under the surface of the site.", 62), + BookLine("As I already had an", 64), + BookLine("intermediate knowledge", 65), + ), + Page( + BookLine("of Herblore, I experimented", 66), + BookLine("with certain chemicals and", 67), + BookLine("invented a compound of", 68), + BookLine("tremendous power that,", 69), + BookLine("if subjected to a spark,", 70), + BookLine("would literally explode.", 71), + BookLine("We used vials of this", 73), + BookLine("compound with great effect,", 74), + BookLine("as it enabled us to reach", 75), + BookLine("further than ever before.", 76), + ) + ), + PageSet( + Page( + BookLine("Here is what I have left of", 55), + BookLine("the compound's recipe:", 56), + BookLine("1 measure of ammonium", 59), + BookLine("nitrate powder;", 60), + BookLine("1 measure of nitroglycerine;", 62), + BookLine("1 measure of ground charcoal;", 64), + ), + Page( + BookLine("1 measure of ?", 66), + BookLine("Unfortunately the last", 68), + BookLine("ingredient was not noted", 69), + BookLine("down, but we understand", 70), + BookLine("that a certain root grows", 71), + BookLine("around these parts that", 72), + BookLine("was used to very good", 73), + BookLine("effect...", 73), + ) + ), + ) + } + + 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.BOOK_ON_CHEMICALS_711, IntType.ITEM, "read") { player, _ -> + BookInterface.openBook(player, BookInterface.FANCY_BOOK_3_49, ::display) + return@on true + } + } +} diff --git a/Server/src/main/content/region/misthalin/digsite/dialogue/EdWoodDialogue.kt b/Server/src/main/content/region/misthalin/digsite/dialogue/EdWoodDialogue.kt new file mode 100644 index 000000000..11c6bb04d --- /dev/null +++ b/Server/src/main/content/region/misthalin/digsite/dialogue/EdWoodDialogue.kt @@ -0,0 +1,25 @@ +package content.region.misthalin.digsite.dialogue + +import core.api.sendChat +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 + +@Initializable +class EdWoodDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + sendChat(npc, arrayOf("Can't stop. Too busy.", "Wonder when I'll get paid.", "Is it lunch break yet?", "Hey I'm working here. I'm working here.", "This work isn't going to do itself.", "Ouch! That was my finger!").random()) + stage = END_DIALOGUE + return true + } + override fun newInstance(player: Player): DialoguePlugin { + return EdWoodDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.ED_WOOD_5964) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misthalin/digsite/dialogue/ElissaDialogue.kt b/Server/src/main/content/region/misthalin/digsite/dialogue/ElissaDialogue.kt index 9bda3a0a5..d41dc2d87 100644 --- a/Server/src/main/content/region/misthalin/digsite/dialogue/ElissaDialogue.kt +++ b/Server/src/main/content/region/misthalin/digsite/dialogue/ElissaDialogue.kt @@ -12,18 +12,18 @@ import org.rs09.consts.NPCs */ @Initializable -class ElissaDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class ElissaDialogue(player: Player? = null) : DialoguePlugin(player){ - var fr = core.game.dialogue.FacialExpression.FRIENDLY - var ask = core.game.dialogue.FacialExpression.ASKING - var ann = core.game.dialogue.FacialExpression.ANNOYED - var neu = core.game.dialogue.FacialExpression.ANNOYED - var ama = core.game.dialogue.FacialExpression.AMAZED + var fr = FacialExpression.FRIENDLY + var ask = FacialExpression.ASKING + var ann = FacialExpression.ANNOYED + var neu = FacialExpression.ANNOYED + var ama = FacialExpression.AMAZED var qb = 2 override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - npc(core.game.dialogue.FacialExpression.FRIENDLY,"Hello there.").also { stage = 0 } + npc(FacialExpression.FRIENDLY,"Hello there.").also { stage = 0 } return true } @@ -91,7 +91,7 @@ class ElissaDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return ElissaDialogue(player) } diff --git a/Server/src/main/content/region/misthalin/digsite/dialogue/ExaminerDialogue.kt b/Server/src/main/content/region/misthalin/digsite/dialogue/ExaminerDialogue.kt new file mode 100644 index 000000000..d9f94dfba --- /dev/null +++ b/Server/src/main/content/region/misthalin/digsite/dialogue/ExaminerDialogue.kt @@ -0,0 +1,536 @@ +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.* +import core.game.node.entity.player.Player +import core.plugin.Initializable +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +@Initializable +class ExaminerDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, ExaminerDialogueFile(), npc) + return true + } + override fun newInstance(player: Player): DialoguePlugin { + return ExaminerDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.EXAMINER_618, NPCs.EXAMINER_4566, NPCs.EXAMINER_4567) + } +} +class ExaminerDialogueFile : DialogueBuilderFile() { + + companion object { + + } + + override fun create(b: DialogueBuilder) { + + 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 -> + optionBuilder.option("Thanks.") + .player(FacialExpression.HAPPY, "Thanks!") + .end() + optionBuilder.option_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(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(Quests.THE_DIG_SITE, 5) + .playerl(FacialExpression.FRIENDLY, "Hello.") + .npcl(FacialExpression.FRIENDLY, "Ah, hello again.") + .options().let { optionBuilder -> + val continuePath = b.placeholder() + optionBuilder.option_playerl("I am ready for the last exam...") + .goto(continuePath) + optionBuilder.option_playerl("I am stuck on a question.") + .npcl("Well, well, have you not been doing any studies? I am not giving you the answers, talk to the other students and remember the answers.") + .end() + optionBuilder.option_playerl("Sorry, I didn't mean to disturb you.") + .npcl("Oh, no problem at all.") + .end() + optionBuilder.option_playerl("I have lost my trowel.") + .branch { player -> if(inInventory(player, Items.TROWEL_676)) { 0 } else { 1 } } + .let{ branch -> + branch.onValue(0) + .npcl("Really? Look in your backpack and make sure first.") + .end() + branch.onValue(1) + .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) + } + } + return@let continuePath.builder() + } + .npcl(FacialExpression.NEUTRAL, "Attention, this is the final part of the Earth Sciences exam: Earth Sciences level 3 - Advanced.") + .npcl(FacialExpression.NEUTRAL, "Question 1 - Sample preparation. Can you tell me how we prepare samples?") + .options().let { optionBuilder -> + val continuePath = b.placeholder() + optionBuilder.recordAttribute(TheDigSite.attributeFirstQuestion) + optionBuilder.optionIf("Samples may be mixed together safely.") { + player -> return@optionIf !getAttribute(player, TheDigSite.attributeStudentPurpleExam3ObtainAnswer, false) } + .playerl("Samples may be mixed together safely.") + .goto(continuePath) + optionBuilder.optionIf("Samples cleaned, and carried only in specimen jars.") { + player -> return@optionIf getAttribute(player, TheDigSite.attributeStudentPurpleExam3ObtainAnswer, false) } + .playerl("Samples cleaned, and carried only in specimen jars.") + .goto(continuePath) + optionBuilder.option_playerl("Sample types catalogued and carried by hand only.").goto(continuePath) + optionBuilder.option_playerl("Samples to be spread thickly with mashed banana.").goto(continuePath) + return@let continuePath.builder() + } + .npcl("Okay, next question...") + .npcl(FacialExpression.NEUTRAL, "Earth Sciences level 3, question 2 - Specimen brush use. What is the proper way to use a specimen brush?") + .options().let { optionBuilder -> + val continuePath = b.placeholder() + optionBuilder.recordAttribute(TheDigSite.attributeSecondQuestion) + optionBuilder.optionIf("Brush quickly using a wet brush.") { + player -> return@optionIf !getAttribute(player, TheDigSite.attributeStudentGreenExam3ObtainAnswer, false) } + .playerl("Brush quickly using a wet brush.") + .goto(continuePath) + optionBuilder.optionIf("Brush carefully and slowly using short strokes.") { + player -> return@optionIf getAttribute(player, TheDigSite.attributeStudentGreenExam3ObtainAnswer, false) } + .playerl("Brush carefully and slowly using short strokes.") + .goto(continuePath) + optionBuilder.option_playerl("Dipped in glue and stuck to a sheep's back.").goto(continuePath) + optionBuilder.option_playerl("Brush quickly and with force.").goto(continuePath) + return@let continuePath.builder() + } + .npcl("Okay, next question...") + .npcl(FacialExpression.NEUTRAL, "Earth Sciences level 3, question 3 - Advanced techniques. Can you describe the technique for handling bones?") + .options().let { optionBuilder -> + val continuePath = b.placeholder() + optionBuilder.recordAttribute(TheDigSite.attributeThirdQuestion) + optionBuilder.optionIf("Bones must not be taken from the site.") { + player -> return@optionIf !getAttribute(player, TheDigSite.attributeStudentBrownExam3ObtainAnswer, false) } + .playerl("Bones must not be taken from the site.") + .goto(continuePath) + optionBuilder.option_playerl("Feed to hungry dogs.").goto(continuePath) + optionBuilder.option_playerl("Bones to be ground and tested for mineral content.").goto(continuePath) + optionBuilder.optionIf("Handle bones very carefully and keep them away from other samples.") { + player -> return@optionIf getAttribute(player, TheDigSite.attributeStudentBrownExam3ObtainAnswer, false) } + .playerl("Handle bones very carefully and keep them away from other samples.") + .goto(continuePath) + return@let continuePath.builder() + } + .npcl("Okay, that concludes the level 3 Earth Sciences exam.") + .npcl(FacialExpression.HAPPY, "Let me add up the results...") + .branch { player -> + var ansCount = 0 + if (getAttribute(player, TheDigSite.attributeFirstQuestion, -1) == 0 && + getAttribute(player, TheDigSite.attributeStudentPurpleExam3ObtainAnswer, false)) { + ansCount++ + } + if (getAttribute(player, TheDigSite.attributeSecondQuestion, -1) == 0 && + getAttribute(player, TheDigSite.attributeStudentGreenExam3ObtainAnswer, false)) { + ansCount++ + } + if (getAttribute(player, TheDigSite.attributeThirdQuestion, -1) == 2 && + getAttribute(player, TheDigSite.attributeStudentBrownExam3ObtainAnswer, false)) { + ansCount++ + } + return@branch ansCount + } + .let { branch -> + + branch.onValue(0) + .npcl(FacialExpression.ANGRY, "I cannot believe this! Absolutely none right at all. I doubt you did any research before you took this exam...") + .playerl(FacialExpression.SAD, "Ah... Yes... Erm.... I think I had better go and revise first!") + .end() + + branch.onValue(1) + .npcl(FacialExpression.FRIENDLY, "You got one question correct. Try harder!") + .playerl(FacialExpression.FRIENDLY, "Oh bother!") + .end() + + branch.onValue(2) + .npcl(FacialExpression.FRIENDLY, "You got two questions correct. A little more study and you will pass it.") + .playerl(FacialExpression.FRIENDLY, "I'm nearly there...") + .end() + + branch.onValue(3) + .npcl(FacialExpression.FRIENDLY, "You got all the questions correct, well done!") + .playerl(FacialExpression.FRIENDLY, "Hooray!") + .betweenStage { _, player, _, _ -> + addItemOrDrop(player, Items.LEVEL_3_CERTIFICATE_693) + } + .npcl(FacialExpression.FRIENDLY, "Congratulations! You have now passed the Earth Sciences level 3 exam. Here is your level 3 certificate.") + .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, Quests.THE_DIG_SITE) == 5) { + setQuestStage(player, Quests.THE_DIG_SITE, 6) + } + openInterface(player, 444) + setInterfaceText(player, player.username, 444, 5) + } + + } + + b.onQuestStages(Quests.THE_DIG_SITE, 4) + .playerl(FacialExpression.FRIENDLY, "Hello.") + .npcl(FacialExpression.FRIENDLY, "Hello again.") + .options().let { optionBuilder -> + val continuePath = b.placeholder() + optionBuilder.option_playerl("I am ready for the next exam.") + .goto(continuePath) + optionBuilder.option_playerl("I am stuck on a question.") + .npcl("Well, well, have you not been doing any studies? I am not giving you the answers, talk to the other students and remember the answers.") + .end() + optionBuilder.option_playerl("Sorry, I didn't mean to disturb you.") + .npcl("Oh, no problem at all.") + .end() + optionBuilder.option_playerl("I have lost my trowel.") + .branch { player -> if(inInventory(player, Items.TROWEL_676)) { 0 } else { 1 } } + .let{ branch -> + branch.onValue(0) + .npcl("Really? Look in your backpack and make sure first.") + .end() + branch.onValue(1) + .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) + } + } + return@let continuePath.builder() + } + .npcl(FacialExpression.NEUTRAL, "Okay, this is the next part of the Earth Sciences exam: Earth Sciences level 2 - Intermediate.") + .npcl(FacialExpression.NEUTRAL, "Question 1 - Sample transportation. Can you tell me how we transport samples?") + .options().let { optionBuilder -> + val continuePath = b.placeholder() + optionBuilder.recordAttribute(TheDigSite.attributeFirstQuestion) + optionBuilder.optionIf("Samples cut and cleaned before transportation.") { + player -> return@optionIf !getAttribute(player, TheDigSite.attributeStudentBrownExam2ObtainAnswer, false) } + .playerl("Samples cut and cleaned before transportation.") + .goto(continuePath) + optionBuilder.option_playerl("Samples ground and suspended in an acid solution.").goto(continuePath) + optionBuilder.option_playerl("Samples to be given to the melon-collecting monkey. ").goto(continuePath) + optionBuilder.optionIf("Samples taken in rough form; kept only in sealed containers.") { + player -> return@optionIf getAttribute(player, TheDigSite.attributeStudentBrownExam2ObtainAnswer, false) } + .playerl("Samples taken in rough form; kept only in sealed containers.") + .goto(continuePath) + return@let continuePath.builder() + } + .npcl("Okay, next question...") + .npcl(FacialExpression.NEUTRAL, "Earth Sciences level 2, question 2 - Handling of finds. What is the proper way to handle finds?") + .options().let { optionBuilder -> + val continuePath = b.placeholder() + optionBuilder.recordAttribute(TheDigSite.attributeSecondQuestion) + optionBuilder.optionIf("Finds must not be handled by anyone.") { + player -> return@optionIf !getAttribute(player, TheDigSite.attributeStudentPurpleExam2ObtainAnswer, false) } + .playerl("Finds must not be handled by anyone.") + .goto(continuePath) + optionBuilder.optionIf("Finds must be carefully handled.") { + player -> return@optionIf getAttribute(player, TheDigSite.attributeStudentPurpleExam2ObtainAnswer, false) } + .playerl("Finds must be carefully handled.") + .goto(continuePath) + optionBuilder.option_playerl("Finds to be given to the site workmen.").goto(continuePath) + optionBuilder.option_playerl("Drop them on the floor and jump on them.").goto(continuePath) + return@let continuePath.builder() + } + .npcl("Okay, next question...") + .npcl(FacialExpression.NEUTRAL, "Earth Sciences level 2, question 3 - Rock pick usage. Can you tell me the proper use for a rock pick?") + .options().let { optionBuilder -> + val continuePath = b.placeholder() + optionBuilder.recordAttribute(TheDigSite.attributeThirdQuestion) + optionBuilder.optionIf("Strike rock repeatedly until powdered.") { + player -> return@optionIf !getAttribute(player, TheDigSite.attributeStudentGreenExam2ObtainAnswer, false) } + .playerl("Strike rock repeatedly until powdered.") + .goto(continuePath) + optionBuilder.option_playerl("Rock pick must be used flat and with strong force.").goto(continuePath) + optionBuilder.optionIf("Always handle with care; strike cleanly on its cleaving point.") { + player -> return@optionIf getAttribute(player, TheDigSite.attributeStudentGreenExam2ObtainAnswer, false) } + .playerl("Always handle with care; strike cleanly on its cleaving point.") + .goto(continuePath) + optionBuilder.option_playerl("Protective clothing to be worn; tools kept away from site.").goto(continuePath) + return@let continuePath.builder() + } + .npcl("Okay, that covers the level 2 Earth Sciences exam.") + .npcl(FacialExpression.HAPPY, "Let me add up your total...") + .branch { player -> + var ansCount = 0 + if (getAttribute(player, TheDigSite.attributeFirstQuestion, -1) == 2 && + getAttribute(player, TheDigSite.attributeStudentBrownExam2ObtainAnswer, false)) { + ansCount++ + } + if (getAttribute(player, TheDigSite.attributeSecondQuestion, -1) == 0 && + getAttribute(player, TheDigSite.attributeStudentPurpleExam2ObtainAnswer, false)) { + ansCount++ + } + if (getAttribute(player, TheDigSite.attributeThirdQuestion, -1) == 1 && + getAttribute(player, TheDigSite.attributeStudentGreenExam2ObtainAnswer, false)) { + ansCount++ + } + return@branch ansCount + } + .let { branch -> + + branch.onValue(0) + .npcl(FacialExpression.ANGRY, "No, no, no! This will not do. They are all wrong. Start again!") + .playerl(FacialExpression.SAD, "Oh no!") + .npcl("More studying for you my @g[boy,girl].") + .end() + + branch.onValue(1) + .npcl(FacialExpression.FRIENDLY, "You got one question correct. At least it's a start.") + .playerl(FacialExpression.FRIENDLY, "Oh well..") + .npcl(FacialExpression.FRIENDLY, "Get out and explore the site, talk to people and learn!") + .end() + + branch.onValue(2) + .npcl(FacialExpression.FRIENDLY, "You got two questions correct. Not too bad, but you can do better...") + .playerl(FacialExpression.FRIENDLY, "Nearly got it.") + .end() + + branch.onValue(3) + .npcl(FacialExpression.FRIENDLY, "You got all the questions correct, well done!") + .playerl(FacialExpression.FRIENDLY, "Great, I'm getting good at this.") + .betweenStage { _, player, _, _ -> + addItemOrDrop(player, Items.LEVEL_2_CERTIFICATE_692) + } + .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, Quests.THE_DIG_SITE) == 4) { + setQuestStage(player, Quests.THE_DIG_SITE, 5) + } + openInterface(player, 441) + setInterfaceText(player, player.username, 441, 5) + } + + } + + 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, 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, Quests.THE_DIG_SITE) + } + .let{ branch -> + val continuePath = b.placeholder() + branch.onValue(1) + .playerl(FacialExpression.FRIENDLY, "Hello.") + .npcl(FacialExpression.FRIENDLY, "Hello again.") + .npcl(FacialExpression.FRIENDLY, "I am still waiting for your letter of recommendation.") + .options().let { optionBuilder -> + optionBuilder.option_playerl("I have lost the letter you gave me.") + .branch { player -> + if (inInventory(player, Items.UNSTAMPED_LETTER_682)){ + return@branch 0 + } else if (inBank(player, Items.UNSTAMPED_LETTER_682)){ + return@branch 1 + } else { + return@branch 2 + } + }.let{ branch -> + branch.onValue(0) + .npcl("Oh now come on. You have it with you!") + .end() + branch.onValue(1) + .npcl("You already have the letter in your bank.") + .end() + branch.onValue(2) + .npcl("That was foolish. Take this one and don't lose it!") + .endWith { _, player -> + addItemOrDrop(player, Items.UNSTAMPED_LETTER_682) + } + return@let branch + } + optionBuilder.option_playerl("Alright I'll try and get it.") + .npcl("I am sure you won't get any problems. Speak to the Curator of Varrock's museum.") + .end() + } + branch.onValue(2) + .playerl(FacialExpression.FRIENDLY, "Hello.") + .npcl(FacialExpression.FRIENDLY, "Hello again.") + .playerl(FacialExpression.FRIENDLY, "Here is the stamped letter you asked for.") + .betweenStage { _, player, _, _ -> + if (inInventory(player, Items.SEALED_LETTER_683)) { + removeItem(player, Items.SEALED_LETTER_683) + } + 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...") + .goto(continuePath) + branch.onValue(3) + .playerl(FacialExpression.FRIENDLY, "Hello.") + .npcl(FacialExpression.FRIENDLY, "Hello again. Are you ready for another shot at the exam?") + .options().let { optionBuilder -> + val continuePath2 = b.placeholder() + optionBuilder.option_playerl("Yes, I certainly am. ") + .goto(continuePath) // Continue down below. + optionBuilder.option_playerl("No, not at the moment.") + .npcl(FacialExpression.NEUTRAL, "Okay, take your time if you wish.") + .end() + return@let continuePath2.builder() + } + + + return@let continuePath.builder() + } + .npcl(FacialExpression.NEUTRAL, "Okay, we will start with the first exam: Earth Sciences level 1 - Beginner.") + .npcl(FacialExpression.NEUTRAL, "Question 1 - Earth Sciences overview. Can you tell me what Earth Sciences is?") + .options().let { optionBuilder -> + val continuePath = b.placeholder() + optionBuilder.recordAttribute(TheDigSite.attributeFirstQuestion) + optionBuilder.optionIf("The study of gardening, planing and fruiting vegetation.") { + player -> return@optionIf !getAttribute(player, TheDigSite.attributeStudentGreenExam1ObtainAnswer, false) } + .playerl("The study of gardening, planing and fruiting vegetation.") + .goto(continuePath) + optionBuilder.optionIf("The study of the earth, its contents and history.") { + player -> return@optionIf getAttribute(player, TheDigSite.attributeStudentGreenExam1ObtainAnswer, false) } + .playerl("The study of the earth, its contents and history.") + .goto(continuePath) + optionBuilder.option_playerl("The study of planets and the history of worlds.").goto(continuePath) + optionBuilder.option_playerl("The combination of archaeology and vegetarianism.").goto(continuePath) + return@let continuePath.builder() + } + .npcl("Okay, next question...") + .npcl(FacialExpression.NEUTRAL, "Earth Sciences level 1, question 2 - Eligibility. Can you tell me which people are allowed to use the digsite?") + .options().let { optionBuilder -> + val continuePath = b.placeholder() + optionBuilder.recordAttribute(TheDigSite.attributeSecondQuestion) + optionBuilder.optionIf("Magic users, miners and their escorts.") { + player -> return@optionIf !getAttribute(player, TheDigSite.attributeStudentBrownExam1ObtainAnswer, false) } + .playerl("Magic users, miners and their escorts.") + .goto(continuePath) + optionBuilder.option_playerl("Professors, students and workmen only.").goto(continuePath) + optionBuilder.option_playerl("Local residents, contractors and small pink fish.").goto(continuePath) + optionBuilder.optionIf("All that have passed the appropriate Earth Sciences exam.") { + player -> return@optionIf getAttribute(player, TheDigSite.attributeStudentBrownExam1ObtainAnswer, false) } + .playerl("All that have passed the appropriate Earth Sciences exam.") + .goto(continuePath) + return@let continuePath.builder() + } + .npcl("Okay, next question...") + .npcl(FacialExpression.NEUTRAL, "Earth Sciences level 1, question 3 - Health and safety. Can you tell me the proper safety points when working on a digsite?") + .options().let { optionBuilder -> + val continuePath = b.placeholder() + optionBuilder.recordAttribute(TheDigSite.attributeThirdQuestion) + optionBuilder.optionIf("Heat-resistant clothing to be worn at all times.") { + player -> return@optionIf !getAttribute(player, TheDigSite.attributeStudentPurpleExam1ObtainAnswer, false) } + .playerl("Heat-resistant clothing to be worn at all times.") + .goto(continuePath) + optionBuilder.option_playerl("Rubber chickens to be worn on the head at all times.").goto(continuePath) + optionBuilder.optionIf("Gloves and boots to be warn at all times; proper tools must be used.") { + player -> return@optionIf getAttribute(player, TheDigSite.attributeStudentPurpleExam1ObtainAnswer, false) } + .playerl("Gloves and boots to be warn at all times; proper tools must be used.") + .goto(continuePath) + optionBuilder.option_playerl("Protective clothing to be worn; tools kept away from site.").goto(continuePath) + return@let continuePath.builder() + } + .npcl("Okay, that covers the level 1 Earth Sciences exam.") + .npcl(FacialExpression.HAPPY, "Let's see how you did...") + .branch { player -> + var ansCount = 0 + if (getAttribute(player, TheDigSite.attributeFirstQuestion, -1) == 0 && + getAttribute(player, TheDigSite.attributeStudentGreenExam1ObtainAnswer, false)) { + ansCount++ + } + if (getAttribute(player, TheDigSite.attributeSecondQuestion, -1) == 2 && + getAttribute(player, TheDigSite.attributeStudentBrownExam1ObtainAnswer, false)) { + ansCount++ + } + if (getAttribute(player, TheDigSite.attributeThirdQuestion, -1) == 1 && + getAttribute(player, TheDigSite.attributeStudentPurpleExam1ObtainAnswer, false)) { + ansCount++ + } + return@branch ansCount + } + .let { branch -> + + branch.onValue(0) + .npcl(FacialExpression.ANGRY, "Oh dear me! This is appalling, none correct at all! I suggest you go and study properly.") + .playerl(FacialExpression.SAD, "Oh dear...") + .npcl("Why don't you use the resources here? There are books and the researchers... and you could even ask other students who are also studying for these exams.") + .end() + + branch.onValue(1) + .npcl(FacialExpression.FRIENDLY, "You got one question correct. Better luck next time.") + .playerl(FacialExpression.FRIENDLY, "Oh bother!") + .npcl(FacialExpression.FRIENDLY, "Do some more research. I'm sure other students could help you out.") + .end() + + branch.onValue(2) + .npcl(FacialExpression.FRIENDLY, "You got two questions correct. Not bad, just a little more revision needed.") + .playerl(FacialExpression.FRIENDLY, "Oh well...") + .end() + + branch.onValue(3) + .npcl(FacialExpression.FRIENDLY, "You got all the questions correct. Well done!") + .playerl(FacialExpression.FRIENDLY, "Hey! Excellent!") + .betweenStage { _, player, _, _ -> + addItemOrDrop(player, Items.TROWEL_676) + addItemOrDrop(player, Items.LEVEL_1_CERTIFICATE_691) + } + .npcl(FacialExpression.FRIENDLY, "You have now passed the Earth Sciences level 1 general exam. Here is your certificate to prove it. You also get a decent trowel to dig with. Of course, you'll want to get studying for your next exam now!") + .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, Quests.THE_DIG_SITE) == 3) { + setQuestStage(player, Quests.THE_DIG_SITE, 4) + } + openInterface(player, 440) + setInterfaceText(player, player.username, 440, 5) + } + + } + + // Fallback dialogue. + b.onPredicate { _ -> true } + .playerl(FacialExpression.FRIENDLY, "Hello.") + .npcl(FacialExpression.FRIENDLY, "Ah hello there! I am the resident lecturer on antiquities and artefacts. I also set the Earth Sciences exams.") + .playerl(FacialExpression.FRIENDLY, "Earth Sciences?") + .npcl(FacialExpression.FRIENDLY, "That is right dear, the world of 2009Scape holds many wonders beneath its surface. Students come to me to take exams so that they may join in on the archaeological dig going on just north of here.") + .playerl(FacialExpression.FRIENDLY, "So if they don't pass the exams they can't dig at all?") + .npcl(FacialExpression.FRIENDLY, "That's right! We have to make sure that students know enough to be able to dig safely and not damage the artefacts.") + .options().let { optionBuilder -> + optionBuilder.option_playerl("Can I take an exam?") + .npcl(FacialExpression.FRIENDLY, "You can if you get this letter stamped by the Curator of Varrock's museum.") + .betweenStage { _, player, _, _ -> + addItemOrDrop(player, Items.UNSTAMPED_LETTER_682) + } + .playerl(FacialExpression.FRIENDLY, "Why's that then?") + .npcl(FacialExpression.FRIENDLY, "Because he is a very knowledgeable man and employs our archaeological expert. I'm sure he knows a lot about your exploits and can judge whether you'd make a good archaeologist or not.") + .npcl(FacialExpression.FRIENDLY, "Besides, the museum contributes funds to the dig.") + .playerl(FacialExpression.FRIENDLY, "But why are you writing the letter? Shouldn't he?") + .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, Quests.THE_DIG_SITE) == 0) { + setQuestStage(player, Quests.THE_DIG_SITE, 1) + } + } + optionBuilder.option_playerl("Interesting...") + .npcl(FacialExpression.FRIENDLY, "You could gain much with an understanding of the world below.") + .end() + } + + } +} diff --git a/Server/src/main/content/region/misthalin/digsite/dialogue/MuseumGuardDialogue.kt b/Server/src/main/content/region/misthalin/digsite/dialogue/MuseumGuardDialogue.kt new file mode 100644 index 000000000..7d72cee66 --- /dev/null +++ b/Server/src/main/content/region/misthalin/digsite/dialogue/MuseumGuardDialogue.kt @@ -0,0 +1,27 @@ +package content.region.misthalin.digsite.dialogue + +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 + +@Initializable +class MuseumGuardDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when(stage) { + START_DIALOGUE -> npc(FacialExpression.FRIENDLY, "Hello there! Sorry, I can't stop to talk. I'm guarding this", "workman's gate. I'm afraid you can't come through here -", "you'll need to find another way around.").also { + stage = END_DIALOGUE + } + } + return true + } + override fun newInstance(player: Player): DialoguePlugin { + return MuseumGuardDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.MUSEUM_GUARD_5942) + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misthalin/digsite/dialogue/ResearcherDialogue.kt b/Server/src/main/content/region/misthalin/digsite/dialogue/ResearcherDialogue.kt new file mode 100644 index 000000000..64af6e116 --- /dev/null +++ b/Server/src/main/content/region/misthalin/digsite/dialogue/ResearcherDialogue.kt @@ -0,0 +1,41 @@ +package content.region.misthalin.digsite.dialogue + +import content.data.Quests +import core.api.* +import core.game.dialogue.DialoguePlugin +import core.game.dialogue.FacialExpression +import core.game.node.entity.player.Player +import core.plugin.Initializable +import core.tools.END_DIALOGUE +import core.tools.START_DIALOGUE +import org.rs09.consts.NPCs + +@Initializable +class ResearcherDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + 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++ } + 2 -> npcl(FacialExpression.FRIENDLY, "Well, feel free to talk to me should you come across anything you can't figure out.").also { + stage = END_DIALOGUE + } + } + } else { + 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++ } + 2 -> npcl(FacialExpression.FRIENDLY, "Well, feel free to talk to me should you come across anything you can't figure out.").also { + stage = END_DIALOGUE + } + } + } + return true + } + override fun newInstance(player: Player): DialoguePlugin { + return ResearcherDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.RESEARCHER_4568) + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..3f4ea3d0e --- /dev/null +++ b/Server/src/main/content/region/misthalin/digsite/quest/thedigsite/ArchaeologicalExpertDialogue.kt @@ -0,0 +1,117 @@ +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 +import org.rs09.consts.NPCs +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 +class ArchaeologicalExpertDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, ArchaeologicalExpertDialogueFile(), npc) + return true + } + override fun newInstance(player: Player): DialoguePlugin { + return ArchaeologicalExpertDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.ARCHAEOLOGICAL_EXPERT_619) + } +} +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?") + .npcl(FacialExpression.FRIENDLY, "Good day to you. My name is Terry Balando, I am an expert archaeologist. I am employed by Varrock Museum to oversee all finds at this site. Anything you find must be reported to me.") + .playerl(FacialExpression.FRIENDLY, "Oh, okay. If I find anything of interest I will bring it here.") + .npcl(FacialExpression.FRIENDLY, "Can I help you at all?") + .options().let { optionBuilder -> + optionBuilder.option_playerl("I have something I need checking out.") + .npcl(FacialExpression.FRIENDLY, "Okay, give it to me and I'll have a look for you.") + .end() + optionBuilder.option_playerl("No thanks.") + .npcl("Good, let me know if you find anything unusual.") + .end() + optionBuilder.option_playerl("Can you tell me anything about the digsite?") + .npcl("Yes, indeed! I am studying the lives of the settlers. During the end of the Third Age, there used to be a great city at the site. Its inhabitants were humans, supporters of the god Saradomin. It's not recorded what happened to the community here. I suspect nobody has lived here for over a millennium!") + .end() + // I lost the letter you gave me. + + optionBuilder.option_playerl("Can you tell me more about the tools an archaeologist uses?") + .npcl(FacialExpression.FRIENDLY, "Of course! Let's see now... Trowels are vital for fine digging work, so you can be careful to not damage or disturb any artefacts. Rock picks are for splitting rocks or scraping away soil.") + .playerl(FacialExpression.FRIENDLY, "What about specimen jars and brushes?") + .npcl(FacialExpression.FRIENDLY, "Those are essential for carefully cleaning and storing smaller samples.") + .playerl(FacialExpression.FRIENDLY, "Where can I get any of these things?") + .npcl(FacialExpression.FRIENDLY, "Well, we've come into a bit more funding of late, so there should be a stock of each of them in the Exam Centre's tools cupboard. We also hand out relevant tools as students complete each level of their Earth Sciences exams.") + .playerl(FacialExpression.FRIENDLY, "Ah, okay, thanks.") + .end() + } + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..296e9fce0 --- /dev/null +++ b/Server/src/main/content/region/misthalin/digsite/quest/thedigsite/ArchaeologicalExpertListener.kt @@ -0,0 +1,137 @@ +package content.region.misthalin.digsite.quest.thedigsite + +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.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 org.rs09.consts.Items +import org.rs09.consts.NPCs + +class ArchaeologicalExpertListener : InteractionListener { + + + val staff = Items.ANCIENT_STAFF_4675 + val unidentifiedLiquid = Items.UNIDENTIFIED_LIQUID_702 + val nitroglycerin = Items.NITROGLYCERIN_703 + val chemicalPowder = Items.CHEMICAL_POWDER_700 + val ammoniumNitrate = Items.AMMONIUM_NITRATE_701 + val nuggets = Items.NUGGETS_680 + val needle = Items.NEEDLE_1733 + val rottenApple = Items.ROTTEN_APPLE_1984 + val brokenGlass = Items.BROKEN_GLASS_1469 + val brokenArrow = Items.BROKEN_ARROW_687 + val panningTray = Items.PANNING_TRAY_677 + val bones = Items.BONES_526 + val buttons = Items.BUTTONS_688 + val crackedSample = Items.CRACKED_SAMPLE_674 + val oldTooth = Items.OLD_TOOTH_695 + val rustySword = Items.RUSTY_SWORD_686 + val brokenStaff = Items.BROKEN_STAFF_689 + val brokenArmour = Items.BROKEN_ARMOUR_698 + val damagedArmour = Items.DAMAGED_ARMOUR_697 + val ceramicRemains = Items.CERAMIC_REMAINS_694 + val beltBuckle = Items.BELT_BUCKLE_684 + val animalSkull = Items.ANIMAL_SKULL_671 + val specialCup = Items.SPECIAL_CUP_672 + val teddy = Items.TEDDY_673 + val stoneTablet = Items.STONE_TABLET_699 + + val items = intArrayOf(staff, unidentifiedLiquid, nitroglycerin, chemicalPowder, ammoniumNitrate, nuggets, needle, rottenApple, + brokenGlass, brokenArrow, panningTray, bones, buttons, crackedSample, oldTooth, rustySword, brokenStaff, brokenArmour, + damagedArmour, ceramicRemains, beltBuckle, animalSkull, specialCup, teddy, stoneTablet) + + val archy = NPCs.ARCHAEOLOGICAL_EXPERT_619 + + + override fun defineListeners() { + onUseAnyWith(IntType.NPC, NPCs.ARCHAEOLOGICAL_EXPERT_619) { player, used, with -> + openDialogue(player, ArchaeologicalExpertListenerDialogueFile(used.id), with as NPC) + return@onUseAnyWith false + } + } +} +class ArchaeologicalExpertListenerDialogueFile(val it: Int) : DialogueBuilderFile() { + + companion object { + fun replaceAll(player: Player, originalItem: Int, newItem: Int) { + for (a in 0..amountInInventory(player, originalItem)) { + if (inInventory(player, originalItem)) { + replaceSlot(player, player.inventory.getSlot(Item(originalItem)), Item(newItem)) + } + } + } + } + + override fun create(b: DialogueBuilder) { + + b.onPredicate { _ -> it == Items.UNIDENTIFIED_LIQUID_702 } + .player(FacialExpression.THINKING, "Do you know what this is?") + .npcl(FacialExpression.WORRIED, "Where did you get this?") + .player("From one of the barrels at the digsite.") + .npcl(FacialExpression.WORRIED, "This is a VERY dangerous liquid called nitroglycerin. Be careful how you handle it. Don't drop it or it will explode!") + .endWith { _, player -> + replaceAll(player, Items.UNIDENTIFIED_LIQUID_702, Items.NITROGLYCERIN_703) + } + + b.onPredicate { _ -> it == Items.NITROGLYCERIN_703 } + .player(FacialExpression.THINKING, "Can you tell me any more about this?") + .npcl(FacialExpression.WORRIED, "Nitroglycerin! This is a dangerous substance. This is normally mixed with other chemicals to produce a potent compound.") + .npcl(FacialExpression.WORRIED, "Be sure not to drop it! That stuff is highly volatile...") + .end() + + b.onPredicate { _ -> it == Items.CHEMICAL_POWDER_700 } + .player(FacialExpression.THINKING, "Do you know what this powder is?") + .npcl(FacialExpression.WORRIED, "Really, you do find the most unusual items. I know what this is - it's a strong chemical called ammonium nitrate. Why you want this I'll never know...") + .endWith { _, player -> + replaceAll(player, Items.CHEMICAL_POWDER_700, Items.AMMONIUM_NITRATE_701) + } + + // b.onPredicate { _ -> it == Items.AMMONIUM_NITRATE_701 } From youtu.be/mKTBPLdxRSY at 9:57, once ammonium nitrate is identified, it becomes not of any archaeological significance. + + b.onPredicate { _ -> it == Items.ANCIENT_TALISMAN_681 } + .npcl(FacialExpression.FRIENDLY, "Unusual... This object doesn't appear right...") + .npcl(FacialExpression.FRIENDLY, "Hmmm...") + .npcl(FacialExpression.FRIENDLY, "I wonder... Let me check my guide... Could it be? Surely not!") + .npcl(FacialExpression.FRIENDLY, "From the markings on it, it seems to be a ceremonial ornament to a god named...") + .npcl(FacialExpression.FRIENDLY, "...Zaros? I haven't heard much about him before. This is a great discovery; we know very little of the ancient gods that people worshipped.") + .npcl(FacialExpression.FRIENDLY, "There is some strange writing embossed upon it - it says, 'Zaros will return and wreak his vengeance upon Zamorak the pretender.'") + .npcl(FacialExpression.FRIENDLY, "I wonder what it means by that. Some silly superstition, probably.") + .npcl(FacialExpression.FRIENDLY, "Still, I wonder what this is doing around here. I'll tell you what; as you have found this, I will allow you to use the private dig shafts.") + .npcl(FacialExpression.FRIENDLY, "You obviously have a keen eye. Take this letter and give it to one of the workmen, and they will allow you to use them.") + .endWith { _, player -> + if (removeItem(player, Items.ANCIENT_TALISMAN_681)) { + addItemOrDrop(player, Items.INVITATION_LETTER_696) + } + if(getQuestStage(player, Quests.THE_DIG_SITE) == 6) { + setQuestStage(player, Quests.THE_DIG_SITE, 7) + } + } + + b.onPredicate { _ -> it == Items.STONE_TABLET_699 } + .playerl(FacialExpression.FRIENDLY, "I found this in a hidden cavern beneath the site.") + .npcl(FacialExpression.FRIENDLY, "Incredible!") + .playerl(FacialExpression.FRIENDLY, "There is an altar down there. The place is crawling with skeletons!") + .npcl(FacialExpression.FRIENDLY, "Yuck! This is an amazing discovery! All this while we were convinced that no other race had lived here.") + .npcl("It seems the followers of Saradomin have tried to cover up the evidence of this Zaros altar. This whole city must have been built over it!") + .npcl("Thanks for your help; your sharp eyes have spotted what many have missed. Here, take this gold as your reward.") + .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, Quests.THE_DIG_SITE) == 11) { + finishQuest(player, Quests.THE_DIG_SITE) + } + } + } + + // Fallback when item isn't recognizable. + b.onPredicate { _ -> true } + .npcl(FacialExpression.FRIENDLY, "I don't think that has any archaeological significance.") + .end() + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..38830c490 --- /dev/null +++ b/Server/src/main/content/region/misthalin/digsite/quest/thedigsite/DigsiteWorkmanDialogue.kt @@ -0,0 +1,73 @@ +package content.region.misthalin.digsite.quest.thedigsite + +import content.data.Quests +import core.api.* +import core.game.dialogue.* +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 core.tools.START_DIALOGUE +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +@Initializable +class DigsiteWorkmanDialogue (player: Player? = null) : DialoguePlugin(player), InteractionListener { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + npc!! + when(stage) { + START_DIALOGUE -> playerl(FacialExpression.FRIENDLY, "Hello!").also { stage++ } + 1 -> npcl(FacialExpression.FRIENDLY, "Why hello there! What can I do you for?").also { stage++ } + 2 -> showTopics( + Topic(FacialExpression.FRIENDLY, "What do you do here?", 3), + Topic(FacialExpression.FRIENDLY, "I'm not sure.", 6), + Topic(FacialExpression.FRIENDLY, "Can I dig around here?", 7), + ) + 3 -> npcl(FacialExpression.FRIENDLY, "Well, my job involved digging for finds, cleaning them and transporting them for identification.").also { stage++ } + 4 -> playerl(FacialExpression.FRIENDLY, "Sounds interesting.").also { stage++ } + 5 -> npcl(FacialExpression.FRIENDLY, "I find it very interesting and very rewarding. So glad to see you're taking an interest in the digsite. Hope to see you out here digging sometime!").also { + stage = END_DIALOGUE + } + 6 -> npcl(FacialExpression.FRIENDLY, "Well, let me know when you are and I'll do my very best to help you!").also { + stage = END_DIALOGUE + } + 7 -> npcl(FacialExpression.FRIENDLY, "You can only use a site you have the appropriate exam level for.").also { stage++ } + 8 -> playerl(FacialExpression.FRIENDLY, "Appropriate exam level?").also { stage++ } + 9 -> npcl(FacialExpression.FRIENDLY, "Oh yes, you need to have been trained in the various techniques before you can be allowed to dig for artefacts.").also { stage++ } + 10 -> playerl(FacialExpression.FRIENDLY, "Ah yes, I understand.").also { + stage = END_DIALOGUE + } + } + return true + } + override fun newInstance(player: Player): DialoguePlugin { + return DigsiteWorkmanDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.DIGSITE_WORKMAN_613, NPCs.DIGSITE_WORKMAN_4564, NPCs.DIGSITE_WORKMAN_4565/*, NPCs.DIGSITE_WORKMAN_5958*/) + } + + override fun defineListeners() { + onUseWith(IntType.NPC, Items.INVITATION_LETTER_696, NPCs.DIGSITE_WORKMAN_613, NPCs.DIGSITE_WORKMAN_4564, NPCs.DIGSITE_WORKMAN_4565) { player, used, with -> + openDialogue(player, DigsiteWorkmanDialogueFile(), with as NPC) + return@onUseWith false + } + } +} +class DigsiteWorkmanDialogueFile : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + + // Fallback dialogue. + b.onPredicate { _ -> true } + .playerl(FacialExpression.FRIENDLY, "Here, have a look at this...") + .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, Quests.THE_DIG_SITE) == 7) { + setQuestStage(player, Quests.THE_DIG_SITE, 8) + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misthalin/digsite/quest/thedigsite/DougDeepingDialogue.kt b/Server/src/main/content/region/misthalin/digsite/quest/thedigsite/DougDeepingDialogue.kt new file mode 100644 index 000000000..07564536f --- /dev/null +++ b/Server/src/main/content/region/misthalin/digsite/quest/thedigsite/DougDeepingDialogue.kt @@ -0,0 +1,69 @@ +package content.region.misthalin.digsite.quest.thedigsite + +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 DougDeepingDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, DougDeepingDialogueFile(), npc) + return true + } + override fun newInstance(player: Player): DialoguePlugin { + return DougDeepingDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.DOUG_DEEPING_614) + } +} + +class DougDeepingDialogueFile : DialogueBuilderFile() { + + override fun create(b: DialogueBuilder) { + b.onPredicate { _ -> true } + .playerl(FacialExpression.FRIENDLY, "Hello.") + .npcl(FacialExpression.FRIENDLY, "Well, well... I have a visitor. What are you doing here?") + .options().let { optionBuilder -> + optionBuilder.option_playerl("I have been invited to research here ") + .npcl(FacialExpression.FRIENDLY, "Indeed, you must be someone special to be allowed down here.") + .options().let { optionBuilder -> + optionBuilder.option_playerl("Do you know where to find a specimen jar?") + .npcl("Hmmm, let me think... Nope, can't help you there, I'm afraid. Try asking at the Exam Centre.") + .end() + optionBuilder.option_playerl("I have things to do...") + .npcl("Of course, don't let me keep you.") + .end() + } + optionBuilder.option_playerl("I'm not really sure.") + .npcl("A miner without a clue - how funny!") + .end() + optionBuilder.option_playerl("I'm here to get rich, rich, rich!") + .npcl("Oh, well, don't forget that wealth and riches aren't everything.") + .end() + optionBuilder.option_playerl("How could I move a large pile of rocks?") + .npcl("There used to be this chap that worked in the other shaft. He was working on an explosive chemical mixture to be used for clearing blocked areas underground.") + .npcl("He left in a hurry one day. Apparently, something in the shaft scared him to death, but he didn't say what.") + .playerl(FacialExpression.FRIENDLY, "Oh?") + .npcl(FacialExpression.FRIENDLY, "Rumour has it he'd been writing a book on his chemical mixture. I'm not sure what was in it, but he left in such a hurry, he probably left something behind in the other dig shaft.") + .npcl("In fact, I still have a chest key he gave me to look after - perhaps it's more useful to you.") + .branch { player -> + if (inInventory(player,Items.CHEST_KEY_709)) { 0 } else { + addItemOrDrop(player, Items.CHEST_KEY_709) + 1 + } + } + .let { branch -> + branch.onValue(0) + .playerl("It's okay, I already have one.") + .end() + branch.onValue(1) + .iteml(Items.CHEST_KEY_709, "Doug hands you a key.") + .end() + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misthalin/digsite/quest/thedigsite/PanningGuideDialogue.kt b/Server/src/main/content/region/misthalin/digsite/quest/thedigsite/PanningGuideDialogue.kt new file mode 100644 index 000000000..c9a7d38eb --- /dev/null +++ b/Server/src/main/content/region/misthalin/digsite/quest/thedigsite/PanningGuideDialogue.kt @@ -0,0 +1,112 @@ +package content.region.misthalin.digsite.quest.thedigsite + +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 PanningGuideDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, PanningGuideDialogueFile(), npc) + return true + } + override fun newInstance(player: Player): DialoguePlugin { + return PanningGuideDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.PANNING_GUIDE_620) + } +} +class PanningGuideDialogueFile : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + b.onPredicate { getAttribute(player!!, TheDigSite.attributePanningGuideTea, false) } + .playerl(FacialExpression.FRIENDLY, "Hello, who are you?") + .npcl(FacialExpression.FRIENDLY, "Hello, I am the panning guide. I'm here to teach you how to pan for gold.") + .playerl(FacialExpression.FRIENDLY, "Excellent!") + .npcl(FacialExpression.FRIENDLY, "Let me explain how panning works. First, you need a panning tray. Use the tray in the panning points in the water and then search your tray.") + .npcl("If you find any gold, take it to the archaeological expert up in the museum storage facility. He will calculate its value for you.") + .options() + .let { optionBuilder -> + optionBuilder.option_playerl("Can you tell me more about the tools an archaeologist uses?") + .npcl(FacialExpression.FRIENDLY, "Of course! Let's see now... Rock picks are for splitting rocks or scraping away soil; you can get one from a cupboard in the Exam Centre.") + .playerl(FacialExpression.FRIENDLY, "What about sample jars?") + .npcl(FacialExpression.FRIENDLY, "I think you'll find them scattered about pretty much everywhere, but I know you can get one from a cupboard somewhere in the Exam Centre, just like the rock pick!") + .playerl(FacialExpression.FRIENDLY, "Okay, what about a specimen brush?") + .npcl(FacialExpression.FRIENDLY, "We have a bit of a shortage of those at the moment. You could try borrowing one from a workman on the site... but I don't think they'd give it willingly.") + .playerl(FacialExpression.FRIENDLY, "Sounds like I'll need to be sneaky to get one of those, then... Okay - trowel?") + .npcl(FacialExpression.FRIENDLY, "Ahh... that you must earn by passing your exams! The examiner holds those.") + .playerl(FacialExpression.FRIENDLY, "Anything else?") + .npcl(FacialExpression.FRIENDLY, "If you need something identified or are not sure about something, give it to the archaeological expert in the Exam Centre.") + .playerl(FacialExpression.FRIENDLY, "Ahh, ok thanks.") + .end() + optionBuilder.option_playerl("Thank you!") + .end() + } + + b.onPredicate { _ -> true } + .playerl(FacialExpression.FRIENDLY, "Hello, who are you?") + .npcl(FacialExpression.FRIENDLY, "Hello, I am the panning guide. I teach students how to pan in these waters. They're not permitted to do so until after they've had training and, of course, they must be invited to pan here too.") + .playerl(FacialExpression.FRIENDLY, "So, how do I become invited?") + .npcl(FacialExpression.FRIENDLY, "I'm not supposed to let people pan here unless they have permission. Mind you, I could let you have a go if you're willing to do me a favour...") + .playerl(FacialExpression.FRIENDLY, "What's that?") + .npcl(FacialExpression.FRIENDLY, "Well, to be honest, what I would really like is a nice cup of tea!") + .branch { player -> if (inInventory(player, Items.CUP_OF_TEA_712)) { 1 } else { 0 } } + .let { branch -> + branch.onValue(0) + .playerl(FacialExpression.FRIENDLY, "Tea?") + .npcl(FacialExpression.FRIENDLY, "Absolutely, I'm parched!") + .npcl(FacialExpression.FRIENDLY, "If you could bring me one of those, I would be more than willing to let you pan here. I usually get some from the main campus building, but I'm busy at the moment.") + .end() + branch.onValue(1) + .playerl(FacialExpression.FRIENDLY, "I've some here that you can have.") + .npcl(FacialExpression.FRIENDLY, "Ah! Lovely! You can't beat a good cuppa! You're free to pan all you want.") + .endWith { _, player -> + if(removeItem(player, Items.CUP_OF_TEA_712)) { + setAttribute(player, TheDigSite.attributePanningGuideTea, true) + } + } + } + } +} +class PanningGuideCannotPanDialogueFile : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + b.onPredicate { _ -> true } + .npcl(FacialExpression.ANNOYED, "Hey! You can't pan yet!") + .playerl(FacialExpression.THINKING, "Why not?") + .npcl("We do not allow the uninvited to pan here.") + .options() + .let { optionBuilder -> + optionBuilder.option_playerl("OK, forget it.") + .end() + return@let optionBuilder + } + .option_playerl("So how do I become invited then?") + .npcl(FacialExpression.FRIENDLY, "I'm not supposed to let people pan here unless they have permission. Mind you, I could let you have a go if you're willing to do me a favour.") + .playerl(FacialExpression.FRIENDLY, "What's that?") + .npcl(FacialExpression.FRIENDLY, "Well, to be honest, what I would really like is a nice cup of tea!") + .branch { player -> if (inInventory(player, Items.CUP_OF_TEA_712)) { 1 } else { 0 } } + .let { branch -> + branch.onValue(0) + .playerl(FacialExpression.FRIENDLY, "Tea?") + .npcl(FacialExpression.FRIENDLY, "Absolutely, I'm parched!") + .npcl(FacialExpression.FRIENDLY, "If you could bring me one of those, I would be more than willing to let you pan here. I usually get some from the main campus building, but I'm busy at the moment.") + .end() + branch.onValue(1) + .playerl(FacialExpression.FRIENDLY, "I've some here that you can have.") + .betweenStage { _, player, _, _ -> + if(removeItem(player, Items.CUP_OF_TEA_712)) { + setAttribute(player, TheDigSite.attributePanningGuideTea, true) + } + } + .npcl(FacialExpression.FRIENDLY, "Ah! Lovely! You can't beat a good cuppa! You're free to pan all you want.") + .end() + } + + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..5ac84d0f5 --- /dev/null +++ b/Server/src/main/content/region/misthalin/digsite/quest/thedigsite/StudentsDialogue.kt @@ -0,0 +1,344 @@ +package content.region.misthalin.digsite.quest.thedigsite + +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 StudentGreenDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, StudentGreenDialogueFile(), npc) + return true + } + override fun newInstance(player: Player): DialoguePlugin { + return StudentGreenDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.STUDENT_615) + } +} +class StudentGreenDialogueFile : DialogueBuilderFile() { + + override fun create(b: DialogueBuilder) { + + + 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(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.") + .npcl(FacialExpression.FRIENDLY, "Well, okay, this is what I have learned since I last spoke to you...") + .npcl(FacialExpression.FRIENDLY, "Specimen brush use: Brush carefully and slowly using short strokes.") + .playerl(FacialExpression.FRIENDLY, "Okay, I'll remember that. Thanks for all your help.") + .endWith { _, player -> + setAttribute(player, TheDigSite.attributeStudentGreenExam3ObtainAnswer, true) + } + + 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.") + .npcl(FacialExpression.FRIENDLY, "Well, okay, this is what I have learned since I last spoke to you...") + .npcl(FacialExpression.FRIENDLY, "Correct rock pick usage: Always handle with care; strike the rock cleanly on its cleaving point.") + .playerl(FacialExpression.FRIENDLY, "Okay, I'll remember that.") + .endWith { _, player -> + setAttribute(player, TheDigSite.attributeStudentGreenExam2ObtainAnswer, true) + } + 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.") + .npcl(FacialExpression.FRIENDLY, "Well, okay, this is what I have learned since I last spoke to you...") + .npcl(FacialExpression.FRIENDLY, "The study of Earth Sciences is: The study of the earth, its contents and history.") + .playerl(FacialExpression.FRIENDLY, "Okay, I'll remember that.") + .endWith { _, player -> + setAttribute(player, TheDigSite.attributeStudentGreenExam1ObtainAnswer, true) + } + 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 -> + branch.onValue(0) + .playerl(FacialExpression.FRIENDLY, "Hello there. How's the study going?") + .npcl(FacialExpression.FRIENDLY, "Very well, thanks. Have you found my animal skull yet?") + .playerl(FacialExpression.FRIENDLY, "No, sorry, not yet.") + .npcl(FacialExpression.FRIENDLY, "Oh well, I am sure it's been picked up. Couldn't you try looking through some pockets?") + .end() + branch.onValue(1) + .playerl(FacialExpression.FRIENDLY, "Hello there. How's the study going?") + .npcl(FacialExpression.FRIENDLY, "Very well, thanks. Have you found my animal skull yet?") + .betweenStage { _, player, _, _ -> + if (inInventory(player, Items.ANIMAL_SKULL_671)){ + removeItem(player, Items.ANIMAL_SKULL_671) + } + } + .npcl("Oh wow! You've found it! Thank you so much. I'll be glad to tell you what I know about the exam. The study of Earth Sciences is: The study of the earth, its contents and history.") + .endWith { _, player -> + setAttribute(player, TheDigSite.attributeStudentGreenExam1ObtainAnswer, true) + } + return@let branch + } + 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?") + .npcl(FacialExpression.FRIENDLY, "I have lost my recent good find.") + .playerl(FacialExpression.FRIENDLY, "What does it look like?") + .npcl(FacialExpression.FRIENDLY, "Err... Like an animal skull!") + .playerl(FacialExpression.FRIENDLY, "Well, that's not too helpful, there are lots of those around here. Can you remember where you last had it?") + .npcl(FacialExpression.FRIENDLY, "It was around here for sure. Maybe one of the workmen picked it up?") + .playerl(FacialExpression.FRIENDLY, "Okay, I'll have a look for you.") + .endWith { _, player -> + setAttribute(player, TheDigSite.attributeStudentGreenExam1Talked, true) + } + b.onPredicate { _ -> true } + .playerl(FacialExpression.FRIENDLY, "Hello there.") + .npcl(FacialExpression.FRIENDLY, "Oh, hi. I'm studying hard for an exam.") + .playerl(FacialExpression.FRIENDLY, "What exam is that?") + .npcl(FacialExpression.FRIENDLY, "It's the Earth Sciences exam.") + .playerl(FacialExpression.FRIENDLY, "Interesting....") + .end() + } +} + + +@Initializable +class StudentPurpleDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, StudentPurpleDialogueFile(), npc) + return true + } + override fun newInstance(player: Player): DialoguePlugin { + return StudentPurpleDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.STUDENT_617) + } +} +class StudentPurpleDialogueFile : DialogueBuilderFile() { + + override fun create(b: DialogueBuilder) { + + 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.") + .npcl(FacialExpression.FRIENDLY, "Okay, I'll tell you my latest notes...") + .npcl(FacialExpression.FRIENDLY, "Sample preparation: Samples cleaned, and carried only in specimen jars.") + .playerl(FacialExpression.FRIENDLY, "Great, thanks for your advice.") + .endWith { _, player -> + setAttribute(player, TheDigSite.attributeStudentPurpleExam3ObtainAnswer, true) + } + + 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 -> + branch.onValue(0) + .playerl(FacialExpression.FRIENDLY, "Hello there.") + .npcl(FacialExpression.FRIENDLY, "Oh, hi again. Did you bring me the opal?") + .playerl(FacialExpression.FRIENDLY, "I haven't found one yet.") + .npcl(FacialExpression.FRIENDLY, "Oh, well, tell me when you do. Remember that they can be found around the site; perhaps try panning the river.") + .end() + branch.onValue(1) + .playerl(FacialExpression.FRIENDLY, "Hello there.") + .npcl(FacialExpression.FRIENDLY, "Oh, hi again. Did you bring me the opal?") + .betweenStage { _, player, _, _ -> + if (inInventory(player, Items.UNCUT_OPAL_1625)){ + removeItem(player, Items.UNCUT_OPAL_1625) + return@betweenStage + } + if (inInventory(player, Items.OPAL_1609)){ + removeItem(player, Items.OPAL_1609) + return@betweenStage + } + } + .playerl(FacialExpression.FRIENDLY, "Would an opal look like this by any chance?") + .npcl(FacialExpression.FRIENDLY, "Wow, great, you've found one. This will look beautiful set in my necklace. Thanks for that; now I'll tell you what I know... Sample preparation: Samples cleaned, and carried only in specimen jars.") + .playerl(FacialExpression.FRIENDLY, "Great, thanks for your advice.") + .endWith { _, player -> + setAttribute(player, TheDigSite.attributeStudentPurpleExam3ObtainAnswer, true) + } + return@let branch + } + + 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!") + .npcl(FacialExpression.FRIENDLY, "Well... it's going to cost you...") + .playerl(FacialExpression.FRIENDLY, "Oh, well how much?") + .npcl(FacialExpression.FRIENDLY, "I'll tell you what I would like: a precious stone. I don't find many of them. My favorite are opals; they are beautiful. Just like me! Tee hee hee!") + .playerl(FacialExpression.FRIENDLY, "Err... OK I'll see what I can do, but I'm not sure where I'd get one.") + .npcl(FacialExpression.FRIENDLY, "Well, I have seen people get them from panning occasionally.") + .playerl(FacialExpression.FRIENDLY, "OK, I'll see what I can turn up for you.") + .endWith { _, player -> + setAttribute(player, TheDigSite.attributeStudentPurpleExam3Talked, true) + } + + 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.") + .npcl(FacialExpression.FRIENDLY, "Okay, I'll tell you my latest notes...") + .npcl(FacialExpression.FRIENDLY, "Finds handling: Finds must be carefully handled.") + .playerl(FacialExpression.FRIENDLY, "Great, thanks for your advice.") + .endWith { _, player -> + setAttribute(player, TheDigSite.attributeStudentPurpleExam2ObtainAnswer, true) + } + 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.") + .npcl(FacialExpression.FRIENDLY, "Okay, I'll tell you my latest notes...") + .npcl(FacialExpression.FRIENDLY, "The proper health and safety points are: Leather gloves and boots to be warn at all times; proper tools must be used.") + .playerl(FacialExpression.FRIENDLY, "Great, thanks for your advice.") + .endWith { _, player -> + setAttribute(player, TheDigSite.attributeStudentPurpleExam1ObtainAnswer, true) + } + 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 -> + branch.onValue(0) + .npcl(FacialExpression.FRIENDLY, "Very well thanks. Have you found my lucky mascot yet?") + .playerl(FacialExpression.FRIENDLY, "No sorry, not yet.") + .npcl(FacialExpression.FRIENDLY, "I'm sure it's just outside the site somewhere...") + .end() + branch.onValue(1) + .playerl(FacialExpression.FRIENDLY, "Hello there.") + .betweenStage { _, player, _, _ -> + if (inInventory(player, Items.TEDDY_673)){ + removeItem(player, Items.TEDDY_673) + } + } + .playerl(FacialExpression.FRIENDLY, "Guess what I found.") + .npcl(FacialExpression.FRIENDLY, "Hey! My lucky mascot! Thanks ever so much. Let me help you with those questions now.") + .npcl(FacialExpression.FRIENDLY, "The proper health and safety points are: Leather gloves and boots to be warn at all times; proper tools must be used.") + .playerl(FacialExpression.FRIENDLY, "Great, thanks for your advice.") + .endWith { _, player -> + setAttribute(player, TheDigSite.attributeStudentPurpleExam1ObtainAnswer, true) + } + return@let branch + } + 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?") + .npcl(FacialExpression.FRIENDLY, "I have lost my teddy bear. He was my lucky mascot.") + .playerl(FacialExpression.FRIENDLY, "Do you know where you dropped him?") + .npcl(FacialExpression.FRIENDLY, "Well, I was doing a lot of walking that day... Oh yes, that's right - a few of us were studying that funny looking relic in the centre of the campus. Maybe I lost my lucky mascot around there, perhaps in a bush?") + .playerl(FacialExpression.FRIENDLY, "Leave it to me, I'll find it.") + .npcl(FacialExpression.FRIENDLY, "Oh, great! Thanks!") + .endWith { _, player -> + setAttribute(player, TheDigSite.attributeStudentPurpleExam1Talked, true) + } + b.onPredicate { _ -> true } + .playerl("Hello there.") + .npcl("Hi there. I'm studying for the Earth Sciences exam.") + .playerl("Interesting... This exam seems to be a popular one!") + } +} + + +@Initializable +class StudentBrownDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, StudentBrownDialogueFile(), npc) + return true + } + override fun newInstance(player: Player): DialoguePlugin { + return StudentBrownDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.STUDENT_616) + } +} +class StudentBrownDialogueFile : DialogueBuilderFile() { + + override fun create(b: DialogueBuilder) { + + 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.") + .npcl(FacialExpression.FRIENDLY, "Hey, I'll tell you what I've learned. That may help.") + .npcl(FacialExpression.FRIENDLY, "The proper technique for handling bones is: Handle bones carefully and keep them away from other samples.") + .playerl(FacialExpression.FRIENDLY, "Thanks for the information.") + .endWith { _, player -> + setAttribute(player, TheDigSite.attributeStudentBrownExam3ObtainAnswer, true) + } + + 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.") + .npcl(FacialExpression.FRIENDLY, "Hey, I'll tell you what I've learned. That may help.") + .npcl(FacialExpression.FRIENDLY, "Correct sample transportation: Samples taken in rough form; kept only in sealed containers.") + .playerl(FacialExpression.FRIENDLY, "Thanks for the information.") + .endWith { _, player -> + setAttribute(player, TheDigSite.attributeStudentBrownExam2ObtainAnswer, true) + } + 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.") + .npcl(FacialExpression.FRIENDLY, "Hey, I'll tell you what I've learned. That may help.") + .npcl(FacialExpression.FRIENDLY, "Correct sample transportation: Samples taken in rough form; kept only in sealed containers.") + .playerl(FacialExpression.FRIENDLY, "Thanks for the information.") + .endWith { _, player -> + setAttribute(player, TheDigSite.attributeStudentBrownExam1ObtainAnswer, true) + } + 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 -> + if (inInventory(player, Items.SPECIAL_CUP_672)){ + removeItem(player, Items.SPECIAL_CUP_672) + return@branch 1 + } else { + return@branch 0 + } + }.let{ branch -> + branch.onValue(0) + .playerl(FacialExpression.FRIENDLY, "No, sorry, not yet.") + .npcl(FacialExpression.FRIENDLY, "Oh dear, I hope it didn't fall into the river. I might never find it again.") + .end() + branch.onValue(1) + .npcl("Oh wow! You've found it! Thank you so much. I'll be glad to tell you what I know about the exam. The study of Earth Sciences is: The study of the earth, its contents and history.") + .endWith { _, player -> + setAttribute(player, TheDigSite.attributeStudentBrownExam1ObtainAnswer, true) + } + return@let branch + } + 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?") + .npcl(FacialExpression.FRIENDLY, "My special cup. I won it for a particularly good find last month.") + .playerl(FacialExpression.FRIENDLY, "Oh, right. So if I find it, you'll help me?") + .npcl(FacialExpression.FRIENDLY, "I sure will!") + .playerl(FacialExpression.FRIENDLY, "Any ideas where it may be?") + .npcl(FacialExpression.FRIENDLY, "All I remember is that I was working near the panning area when I lost it.") + .playerl(FacialExpression.FRIENDLY, "Okay, I'll see what I can do.") + .npcl(FacialExpression.FRIENDLY, "Yeah, maybe the panning guide saw it? I hope I didn't lose it in the water!") + .endWith { _, player -> + setAttribute(player, TheDigSite.attributeStudentBrownExam1Talked, true) + } + b.onPredicate { _ -> true } + .playerl(FacialExpression.FRIENDLY, "Hello there.") + .npcl(FacialExpression.FRIENDLY, "Hello there. As you can see, I am a student.") + .playerl(FacialExpression.FRIENDLY, "What are you doing here?") + .npcl(FacialExpression.FRIENDLY, "I'm studying for the Earth Sciences exam.") + .playerl(FacialExpression.FRIENDLY, "Interesting... Perhaps I should study for it as well.") + } +} 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 new file mode 100644 index 000000000..a32e9f6f8 --- /dev/null +++ b/Server/src/main/content/region/misthalin/digsite/quest/thedigsite/TheDigSite.kt @@ -0,0 +1,390 @@ +package content.region.misthalin.digsite.quest.thedigsite + +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 + * Funny enough, the crossed out quest log doesn't change into the past tense like other quests do. + * I guess this quest is so old, no one was bothered to change the tenses. + * This is working off the low quality quest log in 2008, which is very much different from the current quest log. + + * 1 - Speak to an examiner to start the quest + * 2 - Talked to the curator at Varrock + * 3 - Passed the signed letter back to the examiner + * 4 - Passed Level 1 Exam + * 5 - Passed Level 2 Exam + * 6 - Passed Level 3 Exam + * 7 - Found ANCIENT_TALISMAN_681 to impress expert + * 8 - Gave expert's recommendation letter to a workman + * 9 - Went down any of the digshafts + * 10 - Talked to Doug Deeping to get a key to open a chest + * 11 - Covered rocks with explosives + * 12 - Blew up the rocks (after this, climbing down a digshaft will end up in a different area) + * 100 - Talked to expert after showing him the STONE_TABLET_699 + */ +@Initializable +class TheDigSite : Quest(Quests.THE_DIG_SITE, 47, 46, 2, 131, 0, 1, 9) { + companion object { + const val attributeStudentGreenExam1Talked = "/save:quest:thedigsite-studentgreenexam1talked" + const val attributeStudentGreenExam1ObtainAnswer = "/save:quest:thedigsite-studentgreenexam1obtainanswer" + const val attributeStudentPurpleExam1Talked = "/save:quest:thedigsite-studentpurpleexam1talked" + const val attributeStudentPurpleExam1ObtainAnswer = "/save:quest:thedigsite-studentpurplexam1obtainanswer" + const val attributeStudentBrownExam1Talked = "/save:quest:thedigsite-studentbrownexam1talked" + const val attributeStudentBrownExam1ObtainAnswer = "/save:quest:thedigsite-studentbrownexam1obtainanswer" + const val attributePanningGuideTea = "/save:quest:thedigsite-panningguidetea" + const val attributeStudentGreenExam2ObtainAnswer = "/save:quest:thedigsite-studentgreenexam2obtainanswer" + const val attributeStudentPurpleExam2ObtainAnswer = "/save:quest:thedigsite-studentpurplexam2obtainanswer" + const val attributeStudentBrownExam2ObtainAnswer = "/save:quest:thedigsite-studentbrownexam2obtainanswer" + const val attributeStudentGreenExam3ObtainAnswer = "/save:quest:thedigsite-studentgreenexam3obtainanswer" + const val attributeStudentPurpleExam3ObtainAnswer = "/save:quest:thedigsite-studentpurplexam3obtainanswer" + const val attributeStudentPurpleExam3Talked = "/save:quest:thedigsite-studentpurpleexam3talked" + const val attributeStudentBrownExam3ObtainAnswer = "/save:quest:thedigsite-studentbrownexam3obtainanswer" + + const val attributeRopeNorthEastWinch = "/save:quest:thedigsite-ropenortheastwinch" + const val attributeRopeWestWinch = "/save:quest:thedigsite-ropewestwinch" + + const val attributeFirstQuestion = "quest:thedigsite-firstquestion" + const val attributeSecondQuestion = "quest:thedigsite-secondquestion" + const val attributeThirdQuestion = "quest:thedigsite-thirdquestion" + + const val barrelVarbit = 2547 + const val tabletVarbit = 2548 + } + + override fun reset(player: Player) { + removeAttribute(player, attributeStudentGreenExam1Talked) + removeAttribute(player, attributeStudentGreenExam1ObtainAnswer) + removeAttribute(player, attributeStudentPurpleExam1Talked) + removeAttribute(player, attributeStudentPurpleExam1ObtainAnswer) + removeAttribute(player, attributeStudentBrownExam1Talked) + removeAttribute(player, attributeStudentBrownExam1ObtainAnswer) + removeAttribute(player, attributePanningGuideTea) + removeAttribute(player, attributeStudentGreenExam2ObtainAnswer) + removeAttribute(player, attributeStudentPurpleExam2ObtainAnswer) + removeAttribute(player, attributeStudentBrownExam2ObtainAnswer) + removeAttribute(player, attributeStudentGreenExam3ObtainAnswer) + removeAttribute(player, attributeStudentPurpleExam3Talked) + removeAttribute(player, attributeStudentPurpleExam3ObtainAnswer) + removeAttribute(player, attributeStudentBrownExam3ObtainAnswer) + + removeAttribute(player, attributeRopeNorthEastWinch) + removeAttribute(player, attributeRopeWestWinch) + } + + override fun drawJournal(player: Player, stage: Int) { + super.drawJournal(player, stage) + var line = 11 + var stage = getStage(player) + + var started = getQuestStage(player, Quests.THE_DIG_SITE) > 0 + + if(!started){ + line++ + line(player, "I can start this quest by speaking to the !!Examiner?? at the", line++, false) + line(player, "!!Digsite Exam Centre.??", line++, false) + line(player, "I need the following skill levels:", line++, false) + 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) + + if (stage >= 2) { + 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) + } else if (stage >= 1) { + line(player, "I should take the !!letter?? the !!Examiner?? has given me to the", line++) + line(player, "!!Curator?? of !!Varrock Museum??, for his approval.", line++) + } + + if (stage >= 3) { + line(player, "I need to return the letter of recommendation from the", line++, true) + line(player, "Curator of Varrock Museum to the Examiner at the Exam", line++, true) + line(player, "Centre for inspection.", line++, true) + } else if (stage >= 2) { + line(player, "I need to return the !!letter of recommendation?? from the", line++) + line(player, "!!Curator?? of !!Varrock Museum?? to the !!Examiner?? at the !!Exam??", line++) + line(player, "!!Centre?? for inspection.", line++) + } + if (stage >= 4) { + line(player, "I need to study for my first exam. Perhaps the students", line++, true) + line(player, "on the site can help?", line++, true) + } else if (stage >= 3) { + line(player, "I need to study for my first exam. Perhaps the students", line++) + line(player, "on the site can help?", line++) + } + if (stage >= 4 || getAttribute(player, attributeStudentGreenExam1Talked, false)) { + line(player, "I need to speak to the student in the green 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 green top about the", line++) + line(player, "exams.", line++) + } + if (stage >= 4 || getAttribute(player, attributeStudentPurpleExam1Talked, false)) { + line(player, "I need to speak to the student in the purple shirt about", line++, true) + line(player, "the exams.", line++, true) + } else if (stage >= 3) { + line(player, "I need to speak to the student in the purple shirt about", line++) + line(player, "the exams.", line++) + } + if (stage >= 4 || getAttribute(player, attributeStudentBrownExam1Talked, false)) { + 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 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 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 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) + line(player, "He gave me an answer to one of the questions on the first", line++, true) + line(player, "exam.", line++, true) + } else if (stage >= 3 && getAttribute(player, attributeStudentGreenExam1ObtainAnswer, false)) { + line(player, "I should talk to him to see if he can help with my exams.", line++) + line(player, "He gave me an answer to one of the questions on the first", line++) + line(player, "exam.", line++) + } + + 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 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 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", 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", line++) + line(player, "first exam.", line++) + } + + + if (stage >= 4 || getAttribute(player, attributeStudentBrownExam1ObtainAnswer, false)) { + 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 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) + line(player, "He gave me an answer to one of the questions on the first", line++, true) + line(player, "exam.", line++, true) + } else if (stage >= 3 && getAttribute(player, attributeStudentBrownExam1ObtainAnswer, false)) { + line(player, "I should talk to him to see if he can help with my exams.", line++) + line(player, "He gave me an answer to one of the questions on the first", line++) + line(player, "exam.", line++) + } + + + if (stage >= 4) { + line(player, "I should talk to an examiner to take my first exam. If I", line++, true) + line(player, "have forgotten anything, I can always ask the students", line++, true) + line(player, "again.", line++, true) + line(player, "I have passed my first Earth Science exam.", line++, true) + } else if (stage >= 3 + && getAttribute(player, attributeStudentGreenExam1ObtainAnswer, false) + && getAttribute(player, attributeStudentPurpleExam1ObtainAnswer, false) + && getAttribute(player, attributeStudentBrownExam1ObtainAnswer, false) + ) { + line(player, "I should talk to an examiner to take my first exam. If I", line++) + line(player, "have forgotten anything, I can always ask the students", line++) + line(player, "again.", line++) + } + + if (stage >= 5) { + 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 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) + line(player, "exams.", line++, true) + } else if (stage >= 4) { + line(player, "I need to speak to the student in the green top about the", line++) + line(player, "exams.", line++) + } + 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.", 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 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 orange top about the", line++) + line(player, "exams.", line++) + } + if (stage >= 5) { + line(player, "I should talk to an examiner to take my second exam. If I", line++, true) + line(player, "have forgotten anything, I can always ask the students", line++, true) + line(player, "again.", line++, true) + line(player, "I have passed my second Earth Science exam.", line++, true) + } else if (stage >= 4 + && getAttribute(player, attributeStudentGreenExam2ObtainAnswer, false) + && getAttribute(player, attributeStudentPurpleExam2ObtainAnswer, false) + && getAttribute(player, attributeStudentBrownExam2ObtainAnswer, false) + ) { + line(player, "I should talk to an examiner to take my second exam. If I", line++) + line(player, "have forgotten anything, I can always ask the students", line++) + line(player, "again.", line++) + } + + if (stage >= 6) { + 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 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) + line(player, "exams.", line++, true) + } else if (stage >= 5) { + line(player, "I need to speak to the student in the green top about the", line++) + line(player, "exams.", line++) + } + 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.", 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++) + } + if (stage >= 6 || getAttribute(player, attributeStudentPurpleExam3ObtainAnswer, false)) { + line(player, "I need to bring her an Opal.", line++, true) // What a cunt + } else if (stage >= 5 && getAttribute(player, attributeStudentPurpleExam3Talked, false)) { + 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 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 orange top about the", line++) + line(player, "exams.", line++) + } + if (stage >= 6) { + line(player, "I should talk to an examiner to take my third exam. If I", line++, true) + line(player, "have forgotten anything, I can always ask the students", line++, true) + line(player, "again.", line++, true) + line(player, "I have passed my third and final Earth Science exam.", line++, true) + } else if (stage >= 5 + && getAttribute(player, attributeStudentPurpleExam3ObtainAnswer, false) + && getAttribute(player, attributeStudentGreenExam3ObtainAnswer, false) + && getAttribute(player, attributeStudentBrownExam3ObtainAnswer, false) + ) { + line(player, "I should talk to an examiner to take my third exam. If I", line++) + line(player, "have forgotten anything, I can always ask the students", line++) + line(player, "again.", line++) + } + + if (stage >= 7) { + line(player, "I need a find from the digsite to impress the Expert.", line++, true) + } else if (stage >= 6) { + line(player, "I need a find from the digsite to impress the Expert.", line++) + } + if (stage >= 8) { + line(player, "I need to take the letter to a workman near a winch.", line++, true) + } else if (stage >= 7) { + line(player, "I need to take the letter to a workman near a winch.", line++) + } + if (stage >= 9) { + line(player, "I need to investigate the dig shafts.", line++, true) + line(player, "I found a secret passageway under the site.", line++, true) + } else if (stage >= 8) { + line(player, "I need to !!investigate the dig shafts??.", line++) + } + if (stage >= 10) { + line(player, "I need to find a way to move the rocks blocking the way in", line++, true) + line(player, "the shaft. Perhaps someone can help me.", line++, true) + line(player, "I covered the rocks in the cave with an explosive", line++, true) + line(player, "compound.", line++, true) + } else if (stage >= 9) { + line(player, "I need to find a way to move the rocks blocking the way in", line++) + line(player, "the shaft. Perhaps someone can help me.", line++) + } + if (stage >= 11) { + line(player, "I need to ignite the explosive compound and blow up the", line++, true) + line(player, "rocks blocking the way.", line++, true) + } else if (stage >= 10) { + line(player, "I need to ignite the explosive compound and blow up the", line++) + line(player, "rocks blocking the way.", line++) + } + if (stage >= 12) { + line(player, "I should look for something interesting in the secret room I", line++, true) + line(player, "found, and show it to the Expert at the Exam Centre.", line++, true) + line(player, "The expert was impressed with the Zarosian tablet that I", line++, true) + line(player, "found, and I also discovered an ancient altar!", line++, true) + line(player, "I was rewarded for my findings.", line++, true) + line(player, "My work here is done.", line++, true) + line(player, "I should also talk to the expert about any other finds.", line++, true) + } else if (stage >= 11) { + line(player, "I should look for something interesting in the secret room I", line++) + line(player, "found, and show it to the !!Expert?? at the Exam Centre.", line++) + } + if (stage >= 100) { + line++ + line(player,"QUEST COMPLETE!", line) + } + limitScrolling(player, line, false) + } + } + + override fun finish(player: Player) { + var ln = 10 + super.finish(player) + player.packetDispatch.sendString("You have completed Digsite Quest!", 277, 4) + // This image is special since it isn't an item, but a standalone model. + player.packetDispatch.sendModelOnInterface(17343, 277, 5, 0) + player.packetDispatch.sendAngleOnInterface(277, 5, 1020, 0, 0) + + drawReward(player, "2 Quest Points", ln++) + drawReward(player, "15,300 Mining XP", ln++) + drawReward(player, "2,000 Herblore XP", ln++) + drawReward(player, "2 Gold Bars", ln) + + rewardXP(player, Skills.MINING, 15300.0) + rewardXP(player, Skills.HERBLORE, 2000.0) + addItemOrDrop(player, Items.GOLD_BAR_2357, 2) + } + + override fun newInstance(`object`: Any?): Quest { + return this + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..470451817 --- /dev/null +++ b/Server/src/main/content/region/misthalin/digsite/quest/thedigsite/TheDigSiteListeners.kt @@ -0,0 +1,935 @@ +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 +import core.api.utils.WeightBasedTable +import core.api.utils.WeightedItem +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.interaction.QueueStrength +import core.game.node.entity.Entity +import core.game.node.entity.combat.ImpactHandler +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.tools.END_DIALOGUE +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import org.rs09.consts.Scenery + +class TheDigSiteListeners : InteractionListener { + companion object { + val BENDING_DOWN_ANIMATION = Animation(827) + val TROWEL_ANIMATION = Animation(2272) + val PANNING_ANIMATION = Animation(4593) + + + val trainingDigTable = WeightBasedTable.create( + WeightedItem(0, 0, 0, 8.0, false), + WeightedItem(Items.COINS_995, 1, 1, 1.0, false), + WeightedItem(Items.CHARCOAL_973, 1, 1, 1.0, false), + WeightedItem(Items.BROKEN_ARROW_687, 1, 1, 1.0, false), + WeightedItem(Items.CRACKED_SAMPLE_674, 1, 1, 1.0, false), + WeightedItem(Items.VASE_710, 1, 1, 1.0, false), + ) + val level1DigTable = WeightBasedTable.create( + WeightedItem(0, 0, 0, 2.0, false), + WeightedItem(Items.BUTTONS_688, 1, 1, 1.0, false), + WeightedItem(Items.VASE_710, 1, 1, 1.0, false), + WeightedItem(Items.COPPER_ORE_436, 1, 1, 1.0, false), + WeightedItem(Items.LEATHER_BOOTS_1061, 1, 1, 1.0, false), + WeightedItem(Items.OPAL_1609, 1, 1, 1.0, false), + WeightedItem(Items.OLD_TOOTH_695, 1, 1, 1.0, false), + WeightedItem(Items.ROTTEN_APPLE_1984, 1, 1, 1.0, false), + WeightedItem(Items.BROKEN_GLASS_1469, 1, 1, 1.0, false), + WeightedItem(Items.RUSTY_SWORD_686, 1, 1, 1.0, false), + WeightedItem(Items.BONES_526, 1, 1, 1.0, false), + ) + val level2DigTable = WeightBasedTable.create( + WeightedItem(0, 0, 0, 3.0, false), + WeightedItem(Items.BONES_526, 1, 1, 2.0, false), + WeightedItem(Items.DAMAGED_ARMOUR_697, 1, 1, 1.0, false), + WeightedItem(Items.LEATHER_BOOTS_1061, 1, 1, 1.0, false), + WeightedItem(Items.BOWL_1923, 1, 1, 1.0, false), + WeightedItem(Items.BROKEN_STAFF_689, 1, 1, 1.0, false), + WeightedItem(Items.BROKEN_ARMOUR_698, 1, 1, 1.0, false), + WeightedItem(Items.UNCUT_JADE_1627, 1, 1, 1.0, false), + WeightedItem(Items.BROKEN_GLASS_1469, 1, 1, 1.0, false), + WeightedItem(Items.JUG_1935, 1, 1, 1.0, false), + WeightedItem(Items.EMPTY_POT_1931, 1, 1, 1.0, false), + WeightedItem(Items.CLAY_434, 1, 1, 1.0, false), + WeightedItem(Items.UNCUT_OPAL_1625, 1, 1, 1.0, false), + ) + val level3DigTable = WeightBasedTable.create( + WeightedItem(0, 0, 0, 1.0, false), + WeightedItem(Items.COINS_995, 10, 10, 2.0, false), + WeightedItem(Items.ANCIENT_TALISMAN_681, 1, 1, 1.0, false), // sendItemDialogue "You find a strange talisman." + WeightedItem(Items.BELT_BUCKLE_684, 1, 1, 2.0, false), + WeightedItem(Items.BLACK_MED_HELM_1151, 1, 1, 1.0, false), + WeightedItem(Items.BONES_526, 1, 1, 1.0, false), + WeightedItem(Items.BROKEN_ARMOUR_698, 1, 1, 1.0, false), + WeightedItem(Items.BROKEN_ARROW_687, 1, 1, 1.0, false), + WeightedItem(Items.BROKEN_STAFF_689, 1, 1, 1.0, false), + WeightedItem(Items.BRONZE_SPEAR_1237, 1, 1, 1.0, false), + WeightedItem(Items.BUTTONS_688, 1, 1, 1.0, false), + WeightedItem(Items.CERAMIC_REMAINS_694, 1, 1, 1.0, false), + WeightedItem(Items.CLAY_434, 1, 1, 1.0, false), + WeightedItem(Items.DAMAGED_ARMOUR_697, 1, 1, 1.0, false), + WeightedItem(Items.IRON_KNIFE_863, 1, 1, 1.0, false), + WeightedItem(Items.LEATHER_BOOTS_1061, 1, 1, 1.0, false), + WeightedItem(Items.NEEDLE_1733, 1, 1, 1.0, false), + WeightedItem(Items.OLD_TOOTH_695, 1, 1, 1.0, false), + WeightedItem(Items.PIE_DISH_2313, 1, 1, 1.0, false), + ) + val specimenTrayTable = WeightBasedTable.create( + WeightedItem(0, 0, 0, 2.0, false), + WeightedItem(Items.BONES_526, 1, 1, 2.0, false), + WeightedItem(Items.COINS_995, 1, 1, 1.0, false), + WeightedItem(Items.IRON_DAGGER_1203, 1, 1, 1.0, false), + WeightedItem(Items.CHARCOAL_973, 1, 1, 1.0, false), + WeightedItem(Items.BROKEN_ARROW_687, 1, 1, 1.0, false), + WeightedItem(Items.BROKEN_GLASS_1469, 1, 1, 1.0, false), + WeightedItem(Items.CERAMIC_REMAINS_694, 1, 1, 1.0, false), + WeightedItem(Items.CRACKED_SAMPLE_674, 1, 1, 1.0, false), + ) + + val workmanPickpocketingTable = WeightBasedTable.create( + WeightedItem(Items.SPECIMEN_BRUSH_670, 1, 1, 3.0, false), + WeightedItem(Items.ANIMAL_SKULL_671, 1, 1, 3.0, false), + WeightedItem(Items.COINS_995, 10, 10, 1.0, false), + WeightedItem(Items.ROPE_954, 1, 1, 1.0, false), + WeightedItem(Items.BUCKET_1925, 1, 1, 1.0, false), + WeightedItem(Items.LEATHER_GLOVES_1059, 1, 1, 1.0, false), + WeightedItem(Items.SPADE_952, 1, 1, 1.0, false), + ) + + val workmanPostQuestPickpocketingTable = WeightBasedTable.create( + WeightedItem(Items.SPECIMEN_BRUSH_670, 1, 1, 3.0, false), + WeightedItem(Items.COINS_995, 10, 10, 4.0, false), + WeightedItem(Items.ROPE_954, 1, 1, 1.0, false), + WeightedItem(Items.BUCKET_1925, 1, 1, 1.0, false), + WeightedItem(Items.LEATHER_GLOVES_1059, 1, 1, 1.0, false), + WeightedItem(Items.SPADE_952, 1, 1, 1.0, false), + ) + + val panningTable = WeightBasedTable.create( + WeightedItem(0, 0, 0, 20.0, false), + WeightedItem(Items.COINS_995, 1, 1, 4.0, false), + WeightedItem(Items.NUGGETS_680, 1, 1, 4.0, false), + WeightedItem(Items.OYSTER_407, 1, 1, 3.0, false), + WeightedItem(Items.UNCUT_OPAL_1625, 1, 1, 3.0, false), + WeightedItem(Items.UNCUT_JADE_1627, 1, 1, 3.0, false), + WeightedItem(Items.SPECIAL_CUP_672, 1, 1, 3.0, false), + ) + } + + override fun defineListeners() { + + // 3: Certificate Level 1 + on(Items.LEVEL_1_CERTIFICATE_691, ITEM, "look-at") { player, _ -> + openInterface(player, 440) + setInterfaceText(player, player.username, 440, 5) + return@on true + } + // 3: Certificate Level 2 + on(Items.LEVEL_2_CERTIFICATE_692, ITEM, "look-at") { player, _ -> + openInterface(player, 441) + setInterfaceText(player, player.username, 441, 5) + return@on true + } + // 3: Certificate Level 3 + on(Items.LEVEL_3_CERTIFICATE_693, ITEM, "look-at") { player, _ -> + openInterface(player, 444) + setInterfaceText(player, player.username, 444, 5) + return@on true + } + + // 3 Green: Pickpocket Animal Skull from Digsite Workman + on(intArrayOf(NPCs.DIGSITE_WORKMAN_613, NPCs.DIGSITE_WORKMAN_4564, NPCs.DIGSITE_WORKMAN_4565), NPC, "steal-from") { player, node -> + + if(getStatLevel(player, Skills.THIEVING) < 25){ + player.sendMessage("You need a Thieving level of 25 to do that.") + return@on true + } + if(!workmanPickpocketingTable.canRoll(player)){ + player.sendMessage("You don't have enough inventory space to do that.") + return@on true + } + sendMessage(player, "You attempt to pick the workman's pocket...") + 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) { + queueScript(player, ThievingListeners.PICKPOCKET_ANIM.duration, QueueStrength.NORMAL) { stage: Int -> + when (stage) { + 0 -> { + if (rollOutcome.size > 0) { + addItemOrDrop(player, rollOutcome[0].id) + when (rollOutcome[0].id){ + Items.ANIMAL_SKULL_671 -> sendItemDialogue(player, Items.ANIMAL_SKULL_671, "You steal an animal skull.") + else -> sendMessage(player, "You steal something.") + } + } else { + sendMessage(player, "You couldn't steal anything.") + } + return@queueScript stopExecuting(player) + } + else -> return@queueScript stopExecuting(player) + } + } + } else { + node.asNpc().face(player) + node.asNpc().animator.animate(ThievingListeners.NPC_ANIM) + sendMessage(player, "You fail to pick the workman's pocket.") + sendChat(node.asNpc(), "What do you think you're doing???") + sendMessage(player, "You have been stunned.") // -1 HP + playHurtAudio(player, 20) + stun(player, 3) + player.impactHandler.manualHit(node.asNpc(), 1, ImpactHandler.HitsplatType.NORMAL) + node.asNpc().face(null) + } + } else { + // When not during quest + player.animator.animate(ThievingListeners.PICKPOCKET_ANIM) + val rollOutcome = ThievingListeners.pickpocketRoll(player, 84.0, 240.0, workmanPostQuestPickpocketingTable) + if (rollOutcome != null) { + queueScript(player, ThievingListeners.PICKPOCKET_ANIM.duration, QueueStrength.NORMAL) { stage: Int -> + when (stage) { + 0 -> { + if (rollOutcome.size > 0) { + addItemOrDrop(player, rollOutcome[0].id) + when (rollOutcome[0].id){ + Items.ANIMAL_SKULL_671 -> sendItemDialogue(player, Items.ANIMAL_SKULL_671, "You steal an animal skull.") + else -> sendMessage(player, "You steal something.") + } + } else { + sendMessage(player, "You couldn't steal anything.") + } + return@queueScript stopExecuting(player) + } + else -> return@queueScript stopExecuting(player) + } + } + } else { + node.asNpc().face(player) + node.asNpc().animator.animate(ThievingListeners.NPC_ANIM) + sendMessage(player, "You fail to pick the workman's pocket.") + sendChat(player, "What do you think you're doing???") + sendMessage(player, "You have been stunned.") // -1 HP + playHurtAudio(player, 20) + stun(player, 3) + player.impactHandler.manualHit(node.asNpc(), 1, ImpactHandler.HitsplatType.NORMAL) + node.asNpc().face(null) + } + } + return@on true + } + + // 3 Purple: Pickpocket student. + on(NPCs.STUDENT_617, NPC, "pickpocket") { player, _ -> + sendDialogue(player, "I don't think I should try to steal from this poor student.") + // Technically you can steal the teddy back from her. But why? + return@on true + } + + // 3 Purple: Search Bush for Teddy Bear (but wrong bushes) + on(Scenery.BUSH_2357, SCENERY, "search") { player, _ -> + sendMessage(player, "You search the bush... You find nothing of interest.") + return@on true + } + // 3 Purple: Search Bush for Teddy Bear (the correct bush) + on(Scenery.BUSH_2358, SCENERY, "search") { player, _ -> + openDialogue(player, object : DialogueFile(){ + override fun handle(componentID: Int, buttonID: Int) { + when(stage){ + 0 -> playerl("Hey, something has been dropped here...").also { + addItemOrDrop(player, Items.TEDDY_673) + stage++ + } + 1 -> sendItemDialogue(player, Items.TEDDY_673, "You find... something.").also { stage = END_DIALOGUE } + } + } + }) + return@on true + } + + + // 8/9: Pouring CHEMICAL_COMPOUND_707 on brick. Transitions to stage 11. + onUseWith(NPC, Items.CUP_OF_TEA_712, NPCs.PANNING_GUIDE_620) { player, used, with -> + if(removeItem(player, used)) { + sendNPCDialogue(player, with.id, "Ah! Lovely! You can't beat a good cuppa! You're free to pan all you want.") + setAttribute(player, TheDigSite.attributePanningGuideTea, true) + } + return@onUseWith true + } + + onUseWith(SCENERY, Items.PANNING_TRAY_677, Scenery.PANNING_POINT_2363) { player, used, with -> + if (getAttribute(player, TheDigSite.attributePanningGuideTea, false)) { + queueScript(player, 0, QueueStrength.NORMAL) { stage: Int -> + when (stage) { + 0 -> { + animate(player, PANNING_ANIMATION) + lock(player, PANNING_ANIMATION.duration) + return@queueScript delayScript(player, PANNING_ANIMATION.duration) + } + + 1 -> { + sendItemDialogue(player, Items.PANNING_TRAY_679, "You lift the full tray from the water") + if (removeItem(player, used)) { + addItemOrDrop(player, Items.PANNING_TRAY_679) + } + return@queueScript stopExecuting(player) + } + + else -> return@queueScript stopExecuting(player) + } + + } + } else { + openDialogue(player, PanningGuideCannotPanDialogueFile(), findNPC(NPCs.PANNING_GUIDE_620)!!) + } + return@onUseWith true + } + // 3 Brown: Search Panning Point for Special Cup + on(Scenery.PANNING_POINT_2363, SCENERY, "pan") { player, _ -> + // + if (getAttribute(player, TheDigSite.attributePanningGuideTea, false)) { + if (inInventory(player, Items.PANNING_TRAY_677)) { + queueScript(player, 0, QueueStrength.NORMAL) { stage: Int -> + when (stage) { + 0 -> { + animate(player, PANNING_ANIMATION) + return@queueScript delayScript(player, PANNING_ANIMATION.duration) + } + + 1 -> { + sendItemDialogue(player, Items.PANNING_TRAY_679, "You lift the full tray from the water.") + if (removeItem(player, Items.PANNING_TRAY_677)) { + addItemOrDrop(player, Items.PANNING_TRAY_679) + } + return@queueScript stopExecuting(player) + } + + else -> return@queueScript stopExecuting(player) + } + } + } else if (inInventory(player, Items.PANNING_TRAY_679)) { + sendPlayerDialogue(player, "I already have a full panning tray; perhaps I should search it first.") + } else { + sendMessage(player, "I need a panning tray to pan the panning point.") + } + } else { + openDialogue(player, PanningGuideCannotPanDialogueFile(), findNPC(NPCs.PANNING_GUIDE_620)!!) + } + return@on true + } + // 3 Brown: Empty tray + on(Items.PANNING_TRAY_677, ITEM, "search") { player, used -> + sendMessage(player, "The panning tray is empty.") + return@on true + } + // 3 Brown: Search Panning Point for Special Cup + on(Items.PANNING_TRAY_679, ITEM, "search") { player, used -> + sendMessage(player, "You search the contents of the tray.") + if(removeItem(player, used)) { + addItemOrDrop(player, Items.PANNING_TRAY_677) + val tableRoll = panningTable.roll() + if (tableRoll.size > 0) { + addItemOrDrop(player, tableRoll[0].id) + when (tableRoll[0].id){ + Items.COINS_995 -> sendItemDialogue(player, Items.COINS_995, "You find some coins within the mud.") + Items.NUGGETS_680 -> sendItemDialogue(player, Items.PANNING_TRAY_678, "You find some gold nuggets within the mud.") + Items.OYSTER_407 -> sendItemDialogue(player, Items.OYSTER_407, "You find an oyster within the mud.") + Items.UNCUT_OPAL_1625 -> sendItemDialogue(player, Items.UNCUT_OPAL_1625, "You find a gem within the mud!") + Items.UNCUT_JADE_1627 -> sendItemDialogue(player, Items.UNCUT_JADE_1627, "You find a gem within the mud!") + Items.SPECIAL_CUP_672 -> sendItemDialogue(player, Items.SPECIAL_CUP_672, "You find a shiny cup covered in mud.") + } + } else { + sendItemDialogue(player, Items.PANNING_TRAY_679, "The tray contains only plain mud.") + } + } + return@on true + } + + // 3-End: Soil trowel digging. Very complex. + onUseWith(SCENERY, Items.TROWEL_676, Scenery.SOIL_2376, Scenery.SOIL_2377, Scenery.SOIL_2378) { player, used, with -> + + 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, Quests.THE_DIG_SITE) >= 6) { + queueScript(player, 0, QueueStrength.NORMAL) { stage: Int -> + when (stage) { + 0 -> { + sendMessage(player, "You dig through the earth.") + animate(player, TROWEL_ANIMATION) + lock(player, TROWEL_ANIMATION.duration) + return@queueScript delayScript(player, TROWEL_ANIMATION.duration) + } + + 1 -> { + val tableRoll = level3DigTable.roll() + if (tableRoll.size > 0) { + addItemOrDrop(player, tableRoll[0].id) + } + sendMessage(player, "You carefully clean your find with the specimen brush.") + return@queueScript stopExecuting(player) + } + + else -> return@queueScript stopExecuting(player) + } + } + } else { + sendNPCDialogue(player, NPCs.DIGSITE_WORKMAN_613, "Oi! What do you think you're doing? There's fragile specimens around here!", FacialExpression.ANGRY) + } + } + + val level2Dig = ZoneBorders(3350, 3424, 3363, 3430) + if (level2Dig.insideBorder(player.location)) { + if (getQuestStage(player, Quests.THE_DIG_SITE) >= 5) { + queueScript(player, 0, QueueStrength.NORMAL) { stage: Int -> + when (stage) { + 0 -> { + sendMessage(player, "You dig through the earth.") + animate(player, TROWEL_ANIMATION) + lock(player, TROWEL_ANIMATION.duration) + return@queueScript delayScript(player, TROWEL_ANIMATION.duration) + } + + 1 -> { + val tableRoll = level2DigTable.roll() + if (tableRoll.size > 0) { + addItemOrDrop(player, tableRoll[0].id) + } + sendMessage(player, "You carefully clean your find with the specimen brush.") + return@queueScript stopExecuting(player) + } + + else -> return@queueScript stopExecuting(player) + } + } + } else { + sendNPCDialogue(player, NPCs.DIGSITE_WORKMAN_613, "Oi! What do you think you're doing? There's fragile specimens around here!", FacialExpression.ANGRY) + } + } + + 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, Quests.THE_DIG_SITE) >= 4) { + queueScript(player, 0, QueueStrength.NORMAL) { stage: Int -> + when (stage) { + 0 -> { + sendMessage(player, "You dig through the earth.") + animate(player, TROWEL_ANIMATION) + lock(player, TROWEL_ANIMATION.duration) + return@queueScript delayScript(player, TROWEL_ANIMATION.duration) + } + + 1 -> { + val tableRoll = level1DigTable.roll() + if (tableRoll.size > 0) { + addItemOrDrop(player, tableRoll[0].id) + } + sendMessage(player, "You carefully clean your find with the specimen brush.") + return@queueScript stopExecuting(player) + } + + else -> return@queueScript stopExecuting(player) + } + } + } else { + sendNPCDialogue(player, NPCs.DIGSITE_WORKMAN_613, "Oi! What do you think you're doing? There's fragile specimens around here!", FacialExpression.ANGRY) + } + } + + 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, Quests.THE_DIG_SITE) >= 3) { + queueScript(player, 0, QueueStrength.NORMAL) { stage: Int -> + when (stage) { + 0 -> { + sendMessage(player, "You dig through the earth.") + animate(player, TROWEL_ANIMATION) + lock(player, TROWEL_ANIMATION.duration) + return@queueScript delayScript(player, TROWEL_ANIMATION.duration) + } + + 1 -> { + val tableRoll = trainingDigTable.roll() + if (tableRoll.size > 0) { + addItemOrDrop(player, tableRoll[0].id) + // You find a/an ... + sendMessage(player, "You carefully clean your find with the specimen brush.") + } + return@queueScript stopExecuting(player) + } + + else -> return@queueScript stopExecuting(player) + } + } + } + } + + return@onUseWith true + } + + // 8: North East Winch goes to Doug Deeping + on(Scenery.WINCH_2350, SCENERY, "operate") { player, _ -> + 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, 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...") + teleport(player, Location(3369, 9827)) + sendMessage(player, "You find yourself in a cavern...") + } else { + sendMessage(player, "You operate the winch...") + queueScript(player, 2, QueueStrength.NORMAL) { stage: Int -> + sendPlayerDialogue(player, "Hey, I think I could fit down here. I need something to help me get all the way down.") + sendMessage(player, "The bucket descends, but does not reach the bottom.") + return@queueScript stopExecuting(player) + } + } + } else { + openDialogue(player, object : DialogueFile(){ + override fun handle(componentID: Int, buttonID: Int) { + when(stage){ + 0 -> npc(NPCs.DIGSITE_WORKMAN_613, "Sorry; this area is private. The only way you'll get to", "use these is by impressing the archaeological expert up", "at the center.").also {stage++ } + 1 -> npc(NPCs.DIGSITE_WORKMAN_613,"Find something worthwhile and he might let you use the", "winches. Until then, get lost!").also { stage = END_DIALOGUE } + } + } + }) + } + // sendMessage(player, "There is a sign on the winch.") + // sendMessage(player, "Private area - invitation only.") + return@on true + } + + // 8: Tie rope to winch + onUseWith(IntType.SCENERY, Items.ROPE_954, Scenery.WINCH_2350) { player, used, with -> + if (removeItem(player, used)) { + if (getQuestStage(player, Quests.THE_DIG_SITE) >= 8) { + setAttribute(player, TheDigSite.attributeRopeNorthEastWinch, true) + sendMessage(player, "You tie the rope to the bucket.") + } else { + openDialogue(player, object : DialogueFile(){ + override fun handle(componentID: Int, buttonID: Int) { + when(stage){ + 0 -> npc(NPCs.DIGSITE_WORKMAN_613, "Sorry; this area is private. The only way you'll get to", "use these is by impressing the archaeological expert up", "at the center.").also {stage++ } + 1 -> npc(NPCs.DIGSITE_WORKMAN_613,"Find something worthwhile and he might let you use the", "winches. Until then, get lost!").also { stage = END_DIALOGUE } + } + } + }) + } + } + return@onUseWith true + } + + // 8: Climb back out + on(Scenery.ROPE_2352, SCENERY, "climb-up") { player, _ -> + teleport(player, Location(3370, 3427)) + return@on true + } + + // 8: West Winch goes to Skeletons, Explosion and Stone Tablet + on(Scenery.WINCH_2351, SCENERY, "operate") { player, _ -> + 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, 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...") + teleport(player, Location(3352, 9818)) + sendMessage(player, "You find yourself in a cavern...") + } else { + sendMessage(player, "You operate the winch...") + queueScript(player, 2, QueueStrength.NORMAL) { stage: Int -> + sendPlayerDialogue(player, "Hey, I think I could fit down here. I need something to help me get all the way down.") + sendMessage(player, "The bucket descends, but does not reach the bottom.") + return@queueScript stopExecuting(player) + } + } + } else { + openDialogue(player, object : DialogueFile(){ + override fun handle(componentID: Int, buttonID: Int) { + when(stage){ + 0 -> npc(NPCs.DIGSITE_WORKMAN_613, "Sorry; this area is private. The only way you'll get to", "use these is by impressing the archaeological expert up", "at the center.").also {stage++ } + 1 -> npc(NPCs.DIGSITE_WORKMAN_613,"Find something worthwhile and he might let you use the", "winches. Until then, get lost!").also { stage = END_DIALOGUE } + } + } + }) + } + return@on true + } + + + // 8: Tie rope to winch + onUseWith(IntType.SCENERY, Items.ROPE_954, Scenery.WINCH_2351) { player, used, with -> + if (removeItem(player, used)) { + if (getQuestStage(player, Quests.THE_DIG_SITE) >= 8) { + setAttribute(player, TheDigSite.attributeRopeWestWinch, true) + sendMessage(player, "You tie the rope to the bucket.") + } else { + openDialogue(player, object : DialogueFile(){ + override fun handle(componentID: Int, buttonID: Int) { + when(stage){ + 0 -> npc(NPCs.DIGSITE_WORKMAN_613, "Sorry; this area is private. The only way you'll get to", "use these is by impressing the archaeological expert up", "at the center.").also {stage++ } + 1 -> npc(NPCs.DIGSITE_WORKMAN_613,"Find something worthwhile and he might let you use the", "winches. Until then, get lost!").also { stage = END_DIALOGUE } + } + } + }) + } + } + return@onUseWith true + } + + // 8: Climb back out + on(Scenery.ROPE_2353, SCENERY, "climb-up") { player, _ -> + teleport(player, Location(3354, 3417)) + return@on true + } + + + on(Items.INVITATION_LETTER_696, ITEM, "read") { player, _ -> + sendPlayerDialogue(player, "It says, 'I give permission for the bearer... to use the mine shafts on site. - signed Terrance Balando, Archaeological Expert, City of Varrock.") + // LINE SEPARATED : ("It says, 'I give permission for the bearer... to use the", "mine shafts on site. - signed Terrance Balando,", "Archaeological Expert, City of Varrock.'") + return@on true + } + + + // 8: Investigating brick. Transitions to stage 9. + on(Scenery.BRICK_2362, SCENERY, "search") { player, _ -> + 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, Quests.THE_DIG_SITE, 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, Quests.THE_DIG_SITE) == 10) { + sendPlayerDialogue(player, "The brick is covered with the chemicals I made.", FacialExpression.THINKING) + } + return@on true + } + + + // 8/9: Chest open + on(Scenery.CHEST_2361, SCENERY, "search") { player, _ -> + sendMessage(player, "The chest is locked.") + return@on true + } + + // 8/9: Chest open with key + onUseWith(IntType.SCENERY, Items.CHEST_KEY_709, Scenery.CHEST_2361) { player, used, with -> + if (!removeItem(player, used)) { + return@onUseWith false + } + sendMessage(player, "You use the key in the chest.") + animate(player, 536) + replaceScenery(with as core.game.node.scenery.Scenery, Scenery.CHEST_2360, 100) + return@onUseWith true + } + + + // 8/9: Specimen tray mainly to find charcoal. No quest stage limit as long as you have a specimen jar. + on(Scenery.SPECIMEN_TRAY_2375, SCENERY, "search") { player, _ -> + if (inInventory(player, Items.SPECIMEN_JAR_669)) { + sendMessage(player, "You sift through the earth in the tray.") + animate(player, BENDING_DOWN_ANIMATION) + val tableRoll = specimenTrayTable.roll() + if (tableRoll.size > 0) { + addItemOrDrop(player, tableRoll[0].id) + } + } else { + sendMessage(player, "You need to have a specimen jar when you are searching the tray.") + // sendMessage(player, "There must be a workman present when you are searching the tray.") <- authentic, but what does this mean... + } + return@on true + } + + on(Scenery.CHEST_2360, SCENERY, "search") { player, _ -> + addItemOrDrop(player, Items.CHEMICAL_POWDER_700) + sendItemDialogue(player, Items.CHEMICAL_POWDER_700, "You find some unusual powder inside...") + return@on true + } + + // 8/9 Getting unidentified liquid (Do not shift this code down. This somehow gets overwritten.) + onUseWith(SCENERY, Items.VIAL_229, Scenery.BARREL_17297) { player, used, with -> + if(removeItem(player, used)) { + addItemOrDrop(player, Items.UNIDENTIFIED_LIQUID_702) + openDialogue(player, object : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + b.onPredicate { _ -> true } + .item(Items.UNIDENTIFIED_LIQUID_702, "You fill the vial with the liquid.") + .player("I'm not sure what this stuff is. I had better be VERY", "careful with it; I had better not drop it either...") + } + }) + sendMessage(player, "You put the lid back on the barrel just in case it's dangerous.") + setVarbit(player, TheDigSite.barrelVarbit, 0) + } + return@onUseWith true + } + + // 8/9 Barrel open + on(Scenery.BARREL_17296, SCENERY, "search", "open") { player, _ -> + sendPlayerDialogue(player, "Mmmm... The lid is shut tight; I'll have to find something to lever it off.", FacialExpression.THINKING) + return@on true + } + + // 8/9 Barrel opened with trowel + onUseWith(SCENERY, Items.TROWEL_676, Scenery.BARREL_17296) { player, used, with -> + sendPlayerDialogue(player, "Great! It's opened it.") + setVarbit(player, TheDigSite.barrelVarbit, 1) + return@onUseWith true + } + + // 8/9 Open barrel search + on(Scenery.BARREL_17297, SCENERY, "search") { player, _ -> + sendPlayerDialogue(player, "I can't pick this up with my bare hands! I'll need something to put it in. It looks and smells rather dangerous though, so it'll need to be something small and capable of containing dangerous chemicals.", FacialExpression.THINKING) + return@on true + } + + // 8/9 Mixed chem 1 + onUseWith(ITEM, Items.AMMONIUM_NITRATE_701, Items.NITROGLYCERIN_703) { player, used, with -> + if (getStatLevel(player, Skills.HERBLORE) < 10) { + sendMessage(player, "You need level 10 Herblore to combine the chemicals.") + return@onUseWith true + } + sendMessage(player, "You mix the nitrate powder into the liquid.") + sendMessage(player, "It has produced a foul mixture.") + if(removeItem(player, used) && removeItem(player, with)) { + addItemOrDrop(player, Items.MIXED_CHEMICALS_705) + } + return@onUseWith true + } + + // 8/9 Mixed chem 2 + onUseWith(ITEM, Items.MIXED_CHEMICALS_705, Items.GROUND_CHARCOAL_704) { player, used, with -> + if (getStatLevel(player, Skills.HERBLORE) < 10) { + sendMessage(player, "You need level 10 Herblore to combine the chemicals.") + return@onUseWith true + } + sendMessage(player, "You mix the charcoal into the liquid.") + sendMessage(player, "It has produced an even fouler mixture.") + if(removeItem(player, used) && removeItem(player, with)) { + addItemOrDrop(player, Items.MIXED_CHEMICALS_706) + } + return@onUseWith true + } + + // 8/9 Mixed chem 3 + onUseWith(ITEM, Items.MIXED_CHEMICALS_706, Items.ARCENIA_ROOT_708) { player, used, with -> + if (getStatLevel(player, Skills.HERBLORE) < 10) { + sendMessage(player, "You need level 10 Herblore to combine the chemicals.") + return@onUseWith true + } + sendMessage(player, "You mix the root into the mixture.") + sendMessage(player, "You produce a potentially explosive compound.") + sendPlayerDialogue(player, "Excellent! This looks just right!", FacialExpression.HAPPY) + if(removeItem(player, used) && removeItem(player, with)) { + addItemOrDrop(player, Items.CHEMICAL_COMPOUND_707) + } + return@onUseWith true + } + + // 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, 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, Quests.THE_DIG_SITE, 10) + } + } + return@onUseWith true + } + + // 10: Lighting brick. Transitions to stage 11. + onUseWith(SCENERY, Items.TINDERBOX_590, Scenery.BRICK_2362) { player, used, with -> + 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) { + 0 -> { + animate(player, BENDING_DOWN_ANIMATION) + sendMessage(player, "You strike the tinderbox...") + return@queueScript delayScript(player, 2) + } + 1 -> { + sendMessage(player, "Fizz..") + sendPlayerDialogue(player, "Woah! This is going to blow! I'd better run!", FacialExpression.EXTREMELY_SHOCKED) + return@queueScript delayScript(player, BENDING_DOWN_ANIMATION.duration) + } + 2 -> { + // forceWalk(player, Location(3366, 9830, 0), "smart") doesn't work when player is locked. + player.walkingQueue.reset() + player.walkingQueue.addPath(3366, 9830) + + return@queueScript delayScript(player, 8) + } + 3 -> { + PlayerCamera(player).shake(0, 20, 8, 128, 40) + return@queueScript delayScript(player, 4) + } + 4 -> { + PlayerCamera(player).reset() + teleport(player, Location(3366, 9766)) + unlock(player) + sendPlayerDialogue(player, "Wow, that was a big explosion! What's that noise I can hear? Sounds like bones moving or something...", FacialExpression.EXTREMELY_SHOCKED) + return@queueScript stopExecuting(player) + } + else -> return@queueScript stopExecuting(player) + } + } + } + return@onUseWith true + } + + // 12: Stone tablet get. + on(Scenery.STONE_TABLET_17367, SCENERY, "take") { player, _ -> + setVarbit(player, TheDigSite.tabletVarbit, 1) + addItemOrDrop(player,Items.STONE_TABLET_699) + sendMessage(player, "You pick the stone tablet up.") + return@on true + } + + on(Items.STONE_TABLET_699, ITEM, "read") { player, _ -> + sendPlayerDialogue(player, "It says: Tremble mortal, before the altar of our dread lord Zaros.") + return@on true + } + + // Dropping Liquids + on(Items.UNIDENTIFIED_LIQUID_702, ITEM, "empty") { player, node -> + if(removeItem(player, node)) { + addItemOrDrop(player, Items.VIAL_229) + } + sendChat(player, "You very carefully empty out the liquid.") + return@on true + } + + 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 + } + + 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 + } + + 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 + } + + 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 + } + + 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 + } + + // Scenery not tied to quest + + on(intArrayOf(Scenery.GATE_24560, Scenery.GATE_24561), IntType.SCENERY, "open") { player, _ -> + // This gate is only openable when you have completed Digsite and have 153 kudos from the museum. + // We can assume, no one is going to reach that until the museum is fully fleshed out. + // So this gate will stay locked. + sendMessage(player, "You can't go through there, it's for Dig Site workmen only.") + sendChat(findNPC(NPCs.MUSEUM_GUARD_5942) as Entity, "Sorry - workman's gate only.") + return@on true + } + + on(Scenery.CUPBOARD_17303, SCENERY, "search") { player, _ -> + sendItemDialogue(player, Items.SPECIMEN_JAR_669, "You find a specimen jar.") + addItemOrDrop(player, Items.SPECIMEN_JAR_669) + return@on true + } + + on(Scenery.CUPBOARD_35223, SCENERY, "search") { player, _ -> + sendItemDialogue(player, Items.ROCK_PICK_675, "You find a rock pick.") + addItemOrDrop(player, Items.ROCK_PICK_675) + return@on true + } + + on(intArrayOf(Scenery.SACKS_2354, Scenery.SACKS_2355, Scenery.SACKS_2356), SCENERY, "search") { player, _ -> + sendItemDialogue(player, Items.SPECIMEN_JAR_669, "You find a specimen jar.") + addItemOrDrop(player, Items.SPECIMEN_JAR_669) + return@on true + } + + on(Scenery.BOOKCASE_35224, SCENERY, "search") { player, _ -> + sendMessage(player, "You search through the bookcase...") + sendItemDialogue(player, Items.BOOK_ON_CHEMICALS_711, "You find a book on chemicals.") + addItemOrDrop(player, Items.BOOK_ON_CHEMICALS_711) + return@on true + } + + on(Scenery.SIGNPOST_2366, SCENERY, "read") { player, _ -> + sendMessage(player, "This site is for training purposes only.") + return@on true + } + + on(Scenery.SIGNPOST_2367, SCENERY, "read") { player, _ -> + sendMessage(player, "Level 1 digs only.") + return@on true + } + + on(Scenery.SIGNPOST_2368, SCENERY, "read") { player, _ -> + sendMessage(player, "Level 2 digs only.") + return@on true + } + + on(Scenery.SIGNPOST_2369, SCENERY, "read") { player, _ -> + sendMessage(player, "Level 3 digs only.") + return@on true + } + + on(Scenery.SIGNPOST_2370, SCENERY, "read") { player, _ -> + sendMessage(player, "Private dig.") + return@on true + } + + on(Scenery.SIGNPOST_2371, SCENERY, "read") { player, _ -> + sendMessage(player, "Digsite educational centre.") + return@on true + } + + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misthalin/dorgeshuun/dialogue/BarlakDialogue.kt b/Server/src/main/content/region/misthalin/dorgeshuun/dialogue/BarlakDialogue.kt index 8d8a9cd53..9d375798c 100644 --- a/Server/src/main/content/region/misthalin/dorgeshuun/dialogue/BarlakDialogue.kt +++ b/Server/src/main/content/region/misthalin/dorgeshuun/dialogue/BarlakDialogue.kt @@ -15,13 +15,13 @@ import org.rs09.consts.NPCs */ @Initializable -class BarlakDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ - private var pask = core.game.dialogue.FacialExpression.ASKING - private var pfr = core.game.dialogue.FacialExpression.FRIENDLY - private var pneu = core.game.dialogue.FacialExpression.NEUTRAL - private var nhap = core.game.dialogue.FacialExpression.OLD_HAPPY - private var ntalk1 = core.game.dialogue.FacialExpression.OLD_CALM_TALK1 - private var ntalk2 = core.game.dialogue.FacialExpression.OLD_CALM_TALK2 +class BarlakDialogue(player: Player? = null) : DialoguePlugin(player){ + private var pask = FacialExpression.ASKING + private var pfr = FacialExpression.FRIENDLY + private var pneu = FacialExpression.NEUTRAL + private var nhap = FacialExpression.OLD_HAPPY + private var ntalk1 = FacialExpression.OLD_CALM_TALK1 + private var ntalk2 = FacialExpression.OLD_CALM_TALK2 private var curItem = 0 @@ -164,22 +164,22 @@ class BarlakDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin } } - 150 -> player(core.game.dialogue.FacialExpression.THINKING, "What?").also { stage++ } - 151 -> npc(core.game.dialogue.FacialExpression.OLD_DISTRESSED, "I don't have any bones!").also { stage++ } + 150 -> player(FacialExpression.THINKING, "What?").also { stage++ } + 151 -> npc(FacialExpression.OLD_DISTRESSED, "I don't have any bones!").also { stage++ } 152 -> options("Then how do you stand up?", "What do you need bones for?", "Will you buy anything besides bones?", "What kind of bones do you need?", "Goodbye").also { stage++ } 153 -> when (buttonId) { - 1 -> player(core.game.dialogue.FacialExpression.THINKING, "Then how do you stand up?").also { stage = 200 } - 2 -> player(core.game.dialogue.FacialExpression.THINKING, "What do you need bones for?").also { stage = 220 } - 3 -> player(core.game.dialogue.FacialExpression.THINKING, "Will you buy anything besides bones?").also { stage = 240 } - 4 -> player(core.game.dialogue.FacialExpression.THINKING, "What kind of bones do you need?").also { stage = 260 } + 1 -> player(FacialExpression.THINKING, "Then how do you stand up?").also { stage = 200 } + 2 -> player(FacialExpression.THINKING, "What do you need bones for?").also { stage = 220 } + 3 -> player(FacialExpression.THINKING, "Will you buy anything besides bones?").also { stage = 240 } + 4 -> player(FacialExpression.THINKING, "What kind of bones do you need?").also { stage = 260 } 5 -> player(pfr, "Goodbye.").also { stage = 280 } } - 200 -> npc(core.game.dialogue.FacialExpression.OLD_CALM_TALK1, "What?").also { stage++ } - 201 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY, "How do you stand up if you have no bones? Shouldn't you collapse into a gelatinous blob?").also { stage++ } - 202 -> npc(core.game.dialogue.FacialExpression.OLD_LAUGH1, "Ha, ha, ha, ha, ha!").also { stage = 152 } + 200 -> npc(FacialExpression.OLD_CALM_TALK1, "What?").also { stage++ } + 201 -> playerl(FacialExpression.FRIENDLY, "How do you stand up if you have no bones? Shouldn't you collapse into a gelatinous blob?").also { stage++ } + 202 -> npc(FacialExpression.OLD_LAUGH1, "Ha, ha, ha, ha, ha!").also { stage = 152 } - 220 -> npcl(core.game.dialogue.FacialExpression.OLD_LAUGH1, "To stand up properly. Otherwise I'd collapse into a gelatinous blob! Ha, ha, ha, ha, ha! No, seriously, I need bones to use as a Construction material.").also { stage++ } + 220 -> npcl(FacialExpression.OLD_LAUGH1, "To stand up properly. Otherwise I'd collapse into a gelatinous blob! Ha, ha, ha, ha, ha! No, seriously, I need bones to use as a Construction material.").also { stage++ } 221 -> npcl(ntalk1, "We always need big bones to prop up the mine shafts and to make other temporary structures.").also { stage = 152 } 240 -> npcl(ntalk2, "Well, I've had a few people bring me some interesting giant shells. They say they came from giant snails and giant tortoises. Of course, like the bones, some shells are better than others.").also { stage++ } 241 -> npcl(ntalk1, "I'll give you 250gp for ordinary shells, but what I really need are perfect shells.").also { stage++ } @@ -237,7 +237,7 @@ class BarlakDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin return sets[curItem][3] } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return BarlakDialogue(player) } diff --git a/Server/src/main/content/region/misthalin/dorgeshuun/dialogue/BartakDialogue.kt b/Server/src/main/content/region/misthalin/dorgeshuun/dialogue/BartakDialogue.kt index 38a23edfd..3de1e2e86 100644 --- a/Server/src/main/content/region/misthalin/dorgeshuun/dialogue/BartakDialogue.kt +++ b/Server/src/main/content/region/misthalin/dorgeshuun/dialogue/BartakDialogue.kt @@ -12,29 +12,29 @@ import org.rs09.consts.NPCs */ @Initializable -class BartakDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class BartakDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - npc(core.game.dialogue.FacialExpression.OLD_DISTRESSED,"Oh no! What's broken?").also { stage = 0 } + npc(FacialExpression.OLD_DISTRESSED,"Oh no! What's broken?").also { stage = 0 } return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 0 -> player(core.game.dialogue.FacialExpression.ASKING, "What? Nothing's broken?").also { stage++ } - 1 -> npc(core.game.dialogue.FacialExpression.OLD_DISTRESSED, "I'm sorry. I'm just a bit jumpy.").also { stage++ } - 2 -> npcl(core.game.dialogue.FacialExpression.OLD_DISTRESSED, "I'm in charge of all the metalworking of Dorgesh-Kaan. It's a big responsibility!").also { stage++ } - 3 -> npcl(core.game.dialogue.FacialExpression.OLD_DISTRESSED, "If something metal breaks I have to fix it. And lots of things are made of metal!").also { stage++ } - 4 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "Don't worry, I'm sure you're up to the task!").also { stage++ } - 5 -> npc(core.game.dialogue.FacialExpression.OLD_DISTRESSED, "I hope you're right.").also { stage = 99 } + 0 -> player(FacialExpression.ASKING, "What? Nothing's broken?").also { stage++ } + 1 -> npc(FacialExpression.OLD_DISTRESSED, "I'm sorry. I'm just a bit jumpy.").also { stage++ } + 2 -> npcl(FacialExpression.OLD_DISTRESSED, "I'm in charge of all the metalworking of Dorgesh-Kaan. It's a big responsibility!").also { stage++ } + 3 -> npcl(FacialExpression.OLD_DISTRESSED, "If something metal breaks I have to fix it. And lots of things are made of metal!").also { stage++ } + 4 -> player(FacialExpression.FRIENDLY, "Don't worry, I'm sure you're up to the task!").also { stage++ } + 5 -> npc(FacialExpression.OLD_DISTRESSED, "I hope you're right.").also { stage = 99 } 99 -> end() } return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return BartakDialogue(player) } diff --git a/Server/src/main/content/region/misthalin/dorgeshuun/dialogue/CrateGoblinDialogue.kt b/Server/src/main/content/region/misthalin/dorgeshuun/dialogue/CrateGoblinDialogue.kt index 255e41303..614c33949 100644 --- a/Server/src/main/content/region/misthalin/dorgeshuun/dialogue/CrateGoblinDialogue.kt +++ b/Server/src/main/content/region/misthalin/dorgeshuun/dialogue/CrateGoblinDialogue.kt @@ -12,11 +12,11 @@ import org.rs09.consts.NPCs */ @Initializable -class CrateGoblinDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class CrateGoblinDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - npc(core.game.dialogue.FacialExpression.OLD_NORMAL,"Excuse me, I need to deliver this.").also { stage = 99 } + npc(FacialExpression.OLD_NORMAL,"Excuse me, I need to deliver this.").also { stage = 99 } return true } @@ -28,7 +28,7 @@ class CrateGoblinDialogue(player: Player? = null) : core.game.dialogue.DialogueP return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return CrateGoblinDialogue(player) } diff --git a/Server/src/main/content/region/misthalin/dorgeshuun/dialogue/DartogDialogue.kt b/Server/src/main/content/region/misthalin/dorgeshuun/dialogue/DartogDialogue.kt index bbd41294b..dbf286bc9 100644 --- a/Server/src/main/content/region/misthalin/dorgeshuun/dialogue/DartogDialogue.kt +++ b/Server/src/main/content/region/misthalin/dorgeshuun/dialogue/DartogDialogue.kt @@ -13,17 +13,17 @@ import org.rs09.consts.NPCs */ @Initializable -class DartogDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class DartogDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - npc(core.game.dialogue.FacialExpression.OLD_NORMAL,"Hello, surface-dweller.").also { stage = 0 } + npc(FacialExpression.OLD_NORMAL,"Hello, surface-dweller.").also { stage = 0 } return true } - private var fr = core.game.dialogue.FacialExpression.FRIENDLY - private var ask = core.game.dialogue.FacialExpression.ASKING - private var nor = core.game.dialogue.FacialExpression.OLD_NORMAL + private var fr = FacialExpression.FRIENDLY + private var ask = FacialExpression.ASKING + private var nor = FacialExpression.OLD_NORMAL override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage) { @@ -62,7 +62,7 @@ class DartogDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return DartogDialogue(player) } diff --git a/Server/src/main/content/region/misthalin/dorgeshuun/dialogue/DurgokDialogue.kt b/Server/src/main/content/region/misthalin/dorgeshuun/dialogue/DurgokDialogue.kt index 25f0f8e41..81d78acb9 100644 --- a/Server/src/main/content/region/misthalin/dorgeshuun/dialogue/DurgokDialogue.kt +++ b/Server/src/main/content/region/misthalin/dorgeshuun/dialogue/DurgokDialogue.kt @@ -15,11 +15,11 @@ import org.rs09.consts.NPCs */ @Initializable -class DurgokDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class DurgokDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - npcl(core.game.dialogue.FacialExpression.OLD_NORMAL,"Frogburger! There's nothing like grilled frog in a bun. Do you want one? Only 10gp!").also { stage = 0 } + npcl(FacialExpression.OLD_NORMAL,"Frogburger! There's nothing like grilled frog in a bun. Do you want one? Only 10gp!").also { stage = 0 } return true } @@ -27,16 +27,16 @@ class DurgokDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin when(stage){ 0 -> options("Yes, please.", "No, thanks.").also { stage++ } 1 -> when(buttonId) { - 1 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "Yes please!").also { stage = 10 } - 2 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "No thanks.").also { stage = 99 } + 1 -> player(FacialExpression.FRIENDLY, "Yes please!").also { stage = 10 } + 2 -> player(FacialExpression.FRIENDLY, "No thanks.").also { stage = 99 } } 10 -> if (player.inventory.contains(Items.COINS_995, 10)) { player.inventory.remove(Item(Items.COINS_995, 10)) addItemOrDrop(player, Items.FROGBURGER_10962, 1) - npc(core.game.dialogue.FacialExpression.OLD_NORMAL, "There you go.").also { stage = 99 } + npc(FacialExpression.OLD_NORMAL, "There you go.").also { stage = 99 } } else { - npc(core.game.dialogue.FacialExpression.OLD_NORMAL, "I'm sorry, but you need 10gp for that.").also { stage = 99 } + npc(FacialExpression.OLD_NORMAL, "I'm sorry, but you need 10gp for that.").also { stage = 99 } } 99 -> end() @@ -44,7 +44,7 @@ class DurgokDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return DurgokDialogue(player) } 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 5d70b2c2b..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 @@ -12,18 +13,18 @@ import core.tools.END_DIALOGUE import core.tools.START_DIALOGUE @Initializable -class MistagDialogue (player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { +class MistagDialogue (player: Player? = null) : DialoguePlugin(player){ + override fun newInstance(player: Player?): DialoguePlugin { return MistagDialogue(player) } override fun npc(vararg messages: String?): Component { - return npc(core.game.dialogue.FacialExpression.OLD_NORMAL,*messages) + return npc(FacialExpression.OLD_NORMAL,*messages) } 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/dialogue/randomChildrenDialogue.kt b/Server/src/main/content/region/misthalin/dorgeshuun/dialogue/randomChildrenDialogue.kt index 0e24c7d25..9063efe67 100644 --- a/Server/src/main/content/region/misthalin/dorgeshuun/dialogue/randomChildrenDialogue.kt +++ b/Server/src/main/content/region/misthalin/dorgeshuun/dialogue/randomChildrenDialogue.kt @@ -12,15 +12,15 @@ import core.plugin.Initializable */ @Initializable -class randomChildrenDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ - var a = core.game.dialogue.FacialExpression.OLD_NORMAL - var b = core.game.dialogue.FacialExpression.FRIENDLY +class randomChildrenDialogue(player: Player? = null) : DialoguePlugin(player){ + var a = FacialExpression.OLD_NORMAL + var b = FacialExpression.FRIENDLY override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC when ((1..5).random()) { - 1 -> npc(core.game.dialogue.FacialExpression.OLD_NORMAL, "Are you a surface-dweller?").also { stage = 0 } - 2 -> npcl(a, "Are you " + player.name + "? Did you help Zanik save the city?").also { stage = 10 } + 1 -> npc(FacialExpression.OLD_NORMAL, "Are you a surface-dweller?").also { stage = 0 } + 2 -> npcl(a, "Are you " + player.username + "? Did you help Zanik save the city?").also { stage = 10 } 3 -> npc(a, "Sorry, I'm not meant to talk to strangers.").also { stage = 99 } 4 -> npc(a, "Shh! Don't tell anyone!").also { stage = 20 } 5 -> npc(a, "Help! Help! The surface people are attacking!").also { stage = 30 } @@ -47,7 +47,7 @@ class randomChildrenDialogue(player: Player? = null) : core.game.dialogue.Dialog return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return randomChildrenDialogue(player) } diff --git a/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/DorgCbowEquipPlugin.kt b/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/DorgCbowEquipPlugin.kt deleted file mode 100644 index daa187a4b..000000000 --- a/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/DorgCbowEquipPlugin.kt +++ /dev/null @@ -1,32 +0,0 @@ -package content.region.misthalin.dorgeshuun.quest.thelosttribe - -import core.cache.def.impl.ItemDefinition -import core.game.node.entity.player.Player -import core.game.node.item.Item -import core.plugin.Initializable -import core.plugin.Plugin -import org.rs09.consts.Items - -@Initializable -/** - * Prevents players from equipping the dorg cbow without completing lost tribe - * @author Ceikry - */ -class DorgCbowEquipPlugin : Plugin{ - override fun newInstance(arg: Item?): Plugin { - ItemDefinition.forId(Items.DORGESHUUN_CBOW_8880).handlers["equipment"] = this - return this - } - - override fun fireEvent(identifier: String?, vararg args: Any?): Any { - val player = args[0] as Player - if(identifier == "unequip") return true - if(!player.questRepository.isComplete("Lost Tribe")){ - player.dialogueInterpreter.sendDialogue("You must complete Lost Tribe to use this.") - return false - } else { - return true - } - } - -} \ No newline at end of file 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 bb31324de..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,7 @@ 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 import core.game.node.item.Item @@ -28,7 +30,7 @@ class DukeHoracioTLTDialogue(val questStage: Int) : DialogueFile() { 4 -> sendNormalDialogue( Sigmund, - core.game.dialogue.FacialExpression.ANGRY, + FacialExpression.ANGRY, "Yes your grace, but if there is any possibility that this", "is a goblin incursion then we should take that possibility", "very seriously!" @@ -37,7 +39,7 @@ class DukeHoracioTLTDialogue(val questStage: Int) : DialogueFile() { 5 -> player("I think we should at least investigate.").also { stage++ } 6 -> sendNormalDialogue( Sigmund, - core.game.dialogue.FacialExpression.WORRIED, + FacialExpression.WORRIED, "Your grace, I think you should listen to " + (if (player!!.isMale) "him" else "her") + "." ).also { stage++ } @@ -47,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 } } @@ -63,7 +65,7 @@ class DukeHoracioTLTDialogue(val questStage: Int) : DialogueFile() { 2 -> sendNormalDialogue( Sigmund, - core.game.dialogue.FacialExpression.WORRIED, + FacialExpression.WORRIED, "It is unknown to me, your grace. But the fact it is", "there is enough to prove the Cook's story. It must have", "been dropped by a goblin as it fled." @@ -72,14 +74,14 @@ class DukeHoracioTLTDialogue(val questStage: Int) : DialogueFile() { 3 -> npc("I've never heard of a goblin wearing something so well-", "crafted.").also { stage++ } 4 -> sendNormalDialogue( Sigmund, - core.game.dialogue.FacialExpression.ANGRY, + FacialExpression.ANGRY, "Then it must have been stolen!" ).also { stage++ } 5 -> npc("But it wasn't stolen from us. Where could it be from?").also { stage++ } 6 -> sendNormalDialogue( Sigmund, - core.game.dialogue.FacialExpression.ANGRY, + FacialExpression.ANGRY, "That doesn't matter! You said yourself that goblins", "couldn't have made that, so they must have stolen it", "from somewhere." @@ -87,14 +89,14 @@ class DukeHoracioTLTDialogue(val questStage: Int) : DialogueFile() { 7 -> sendNormalDialogue( Sigmund, - core.game.dialogue.FacialExpression.ANGRY, + FacialExpression.ANGRY, "Horrible, thieving goblins have broken into our cellar!", "We must retaliate immediately!" ).also { stage++ } 8 -> sendNormalDialogue( Sigmund, - core.game.dialogue.FacialExpression.ANGRY, + FacialExpression.ANGRY, "First we should wipe out the goblins east of the river,", "then we can march on the goblin village to the north-", "west..." @@ -107,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 } } @@ -123,7 +125,7 @@ class DukeHoracioTLTDialogue(val questStage: Int) : DialogueFile() { 1 -> sendNormalDialogue( Sigmund, - core.game.dialogue.FacialExpression.ANGRY, + FacialExpression.ANGRY, "What more proof do we need? Nasty, smelly goblins", "have been living under our feet all this time! We must", "crush them at once!" @@ -139,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 } } @@ -155,7 +157,7 @@ class DukeHoracioTLTDialogue(val questStage: Int) : DialogueFile() { 1 -> sendNormalDialogue( Sigmund, - core.game.dialogue.FacialExpression.ANGRY, + FacialExpression.ANGRY, "And I suppose you believe them, goblin lover?" ).also { stage++ } @@ -168,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 } } @@ -180,7 +182,7 @@ class DukeHoracioTLTDialogue(val questStage: Int) : DialogueFile() { 1 -> npc("Sigmund! Is this your doing?").also { stage++ } 2 -> sendNormalDialogue( Sigmund, - core.game.dialogue.FacialExpression.WORRIED, + FacialExpression.WORRIED, "Of...of course not! The goblins must have, um, dropped", "the silverware as they ran away." ).also { stage++ } @@ -193,7 +195,7 @@ class DukeHoracioTLTDialogue(val questStage: Int) : DialogueFile() { 4 -> sendNormalDialogue( Sigmund, - core.game.dialogue.FacialExpression.THINKING, + FacialExpression.THINKING, "But don't you see it was for the best? For goblins to be", "living under our feet like this... ugh. It doesn't matter", "how civilised they are: all sub-human species must be", @@ -214,9 +216,9 @@ class DukeHoracioTLTDialogue(val questStage: Int) : DialogueFile() { "the cave goblins and tell them I would like to meet with", "their leader to sign it." ) - player!!.inventory.add(Item(Items.PEACE_TREATY_5012)) - player!!.questRepository.getQuest("Lost Tribe").setStage(player, 50) - player!!.varpManager.get(465).setVarbit(0, 9).send(player!!) + addItemOrDrop(player!!, Items.PEACE_TREATY_5012) + player!!.questRepository.getQuest(Quests.THE_LOST_TRIBE).setStage(player, 50) + setVarbit(player!!, 532, 9, true) stage = END_DIALOGUE } } @@ -226,4 +228,4 @@ class DukeHoracioTLTDialogue(val questStage: Int) : DialogueFile() { abandonFile() } } -} \ No newline at end of file +} 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 16e3e2914..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, core.game.dialogue.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.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(Quests.THE_LOST_TRIBE).setStage(player, 43) } player.removeAttribute("hgr-index") true diff --git a/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/KazgarDialogue.kt b/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/KazgarDialogue.kt index ab1355c83..86d056d40 100644 --- a/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/KazgarDialogue.kt +++ b/Server/src/main/content/region/misthalin/dorgeshuun/quest/thelosttribe/KazgarDialogue.kt @@ -10,8 +10,8 @@ import core.plugin.Initializable * Dialogue for Kazgar * @author Ceikry */ -class KazgarDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { +class KazgarDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player?): DialoguePlugin { return KazgarDialogue(player) } 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 1bd8ac31c..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 @@ -7,13 +7,15 @@ import core.game.node.item.GroundItemManager 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 } @@ -26,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) @@ -82,7 +84,7 @@ class LostTribe : Quest("Lost Tribe",84,83,1) { line(player,"the cave goblins. I should do so at once!",line++,stage >= 51) } if(stage >= 100){ - line(player,"!!QUEST COMPLETE!??",line++) + line(player,"%%QUEST COMPLETE!&&",line++) } } @@ -102,7 +104,7 @@ class LostTribe : Quest("Lost Tribe",84,83,1) { if(!player.inventory.add(Item(Items.RING_OF_LIFE_2570))){ GroundItemManager.create(Item(Items.RING_OF_LIFE_2570),player) } - player.varpManager.get(465).setVarbit(0,11).send(player) + setVarp(player, 465, 11) } override fun getConfig(player: Player?, stage: Int): IntArray { @@ -123,4 +125,4 @@ class LostTribe : Quest("Lost Tribe",84,83,1) { if(stage > 0) return intArrayOf(465,1) return intArrayOf(465,0) } -} \ No newline at end of file +} 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 0a511d53a..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,10 +1,11 @@ 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 import core.cache.def.impl.SceneryDefinition import core.game.component.Component -import core.game.dialogue.FacialExpression import core.game.interaction.OptionHandler import core.game.node.Node import core.game.node.entity.player.Player @@ -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!") - player.inventory.add(Item(Items.SILVERWARE_5011)) - player.questRepository.getQuest("Lost Tribe").setStage(player,49) + addItemOrDrop(player, Items.SILVERWARE_5011) + 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 0e986e528..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) } } } @@ -53,6 +55,6 @@ class MistagLTDialogue(val isGreeting: Boolean, val questStage: Int) : DialogueF } override fun npc(vararg messages: String?): Component? { - return npc(core.game.dialogue.FacialExpression.OLD_NORMAL,*messages) + return npc(FacialExpression.OLD_NORMAL,*messages) } } \ No newline at end of file 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 84c1061ed..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 @@ -8,6 +9,7 @@ import core.game.system.task.Pulse import core.game.world.GameWorld import core.plugin.Initializable import core.plugin.Plugin +import core.api.* @Initializable /** @@ -24,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 @@ -57,7 +59,7 @@ class PickaxeOnRubble : UseWithHandler(1265,1267,1269,1271,1273,1275){ 2 -> { player.dialogueInterpreter.sendItemMessage(tool.id,"You dig a narrow tunnel through the rocks.") player.setAttribute("/save:tlt-hole-cleared",true) - player.varpManager.get(465).setVarbit(0,4).send(player) + setVarbit(player, 532, 4, true) player.unlock() 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 6e4b64ded..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; @@ -152,7 +153,7 @@ public final class AggieDialogue extends DialoguePlugin { stage = 30; break; case 5: - interpreter.sendDialogues(player, FacialExpression.FURIOUS, "Can you make dyes for me please?"); + interpreter.sendDialogues(player, FacialExpression.ASKING, "Can you make dyes for me please?"); stage = 40; break; } @@ -247,13 +248,13 @@ public final class AggieDialogue extends DialoguePlugin { stage = 30; break; case 4: - interpreter.sendDialogues(player, FacialExpression.FURIOUS, "Can you make dyes for me please?"); + interpreter.sendDialogues(player, FacialExpression.ASKING, "Can you make dyes for me please?"); stage = 40; break; } break; case 40: - interpreter.sendDialogues(npc, FacialExpression.FURIOUS, "What sort of dye would you like? Red, yellow or blue?"); + interpreter.sendDialogues(npc, FacialExpression.ASKING, "What sort of dye would you like? Red, yellow or blue?"); stage = 41; break; case 41: @@ -263,15 +264,15 @@ public final class AggieDialogue extends DialoguePlugin { case 42: switch (buttonId) { case 1: - interpreter.sendDialogues(player, FacialExpression.FURIOUS, "What do you need to make red dye?"); + interpreter.sendDialogues(player, FacialExpression.ASKING, "What do you need to make red dye?"); stage = 410; break; case 2: - interpreter.sendDialogues(player, FacialExpression.FURIOUS, "What do you need to make yellow dye?"); + interpreter.sendDialogues(player, FacialExpression.ASKING, "What do you need to make yellow dye?"); stage = 420; break; case 3: - interpreter.sendDialogues(player, FacialExpression.FURIOUS, "What do you need to make blue dye?"); + interpreter.sendDialogues(player, FacialExpression.ASKING, "What do you need to make blue dye?"); stage = 430; break; } @@ -281,7 +282,7 @@ public final class AggieDialogue extends DialoguePlugin { stage = 431; break; case 431: - interpreter.sendDialogues(player, FacialExpression.FURIOUS, "Okay, make me some blue dye please."); + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "Okay, make me some blue dye please."); stage = 432; break; case 432: @@ -304,7 +305,7 @@ public final class AggieDialogue extends DialoguePlugin { stage = 421; break; case 421: - interpreter.sendDialogues(player, FacialExpression.FURIOUS, "Okay, make me some yellow dye please."); + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "Okay, make me some yellow dye please."); stage = 422; break; case 422: @@ -327,7 +328,7 @@ public final class AggieDialogue extends DialoguePlugin { stage = 411; break; case 411: - interpreter.sendDialogues(player, FacialExpression.FURIOUS, "Okay, make me some red dye please."); + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "Okay, make me some red dye please."); stage = 412; break; case 412: diff --git a/Server/src/main/content/region/misthalin/draynor/dialogue/ChickenBook.kt b/Server/src/main/content/region/misthalin/draynor/dialogue/ChickenBook.kt new file mode 100644 index 000000000..f8e06d5ee --- /dev/null +++ b/Server/src/main/content/region/misthalin/draynor/dialogue/ChickenBook.kt @@ -0,0 +1,111 @@ +package content.region.misthalin.draynor.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.api.setAttribute +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.player.Player +import org.rs09.consts.Items + +class ChickenBook : InteractionListener { + + // Book about the Evil Chicken found on the bookshelves in the Wise Old Man's house in Draynor Village. + + companion object { + private val TITLE = "The Origins of the Bird of Evil" + private val CONTENTS = arrayOf( + PageSet( + Page( + BookLine("Many are the rumours", 55), + BookLine("and tales surrounding", 56), + BookLine("this foul beast.", 57), + BookLine("", 58), + BookLine("The earliest of these tales", 59), + BookLine("seems to date back to", 60), + BookLine("the time of the Mahjarrat.", 61), + BookLine("It is a story about a mad", 62), + BookLine("mage, who attempts to summon", 63), + BookLine("a demon and bind it to his", 64), + BookLine("will. Unfortunately his", 65), + ), + Page( + BookLine("spell failed and all that", 66), + BookLine("appeared was one confused", 67), + BookLine("chicken. In a fit of anger", 68), + BookLine("at his failure the mage", 69), + BookLine("banished the chicken to the", 70), + BookLine("Abyss. The chicken however", 71), + BookLine("appears to have survived", 72), + BookLine("and grown in power. Years", 73), + BookLine("later, when the mage cast", 74), + BookLine("another spell of summoning,", 75), + BookLine("the chicken appeared!", 76), + ) + ), + PageSet( + Page( + BookLine("The story does not tell", 55), + BookLine("of what became of the mage.", 56), + BookLine("", 57), + BookLine("Another popular tale", 58), + BookLine("tells of a chicken of", 59), + BookLine("higher than average", 60), + BookLine("mental ability. Realising", 61), + BookLine("that it and its kind were", 62), + BookLine("prisoners in their coops", 63), + BookLine("he organised a rebellion", 64), + BookLine("against the human farmers.", 65), + ), + Page( + BookLine("The farmers, of course,", 66), + BookLine("simply slaughtered the", 67), + BookLine("rebellious chickens.", 68), + BookLine("However, ", 70), + BookLine("the Evil Chicken escaped.", 71), + BookLine("Blaming its brethren for", 72), + BookLine("the coup's failure it", 73), + BookLine("swore revenge on", 74), + BookLine("all chickens and all humans.", 75), + ) + ), + PageSet( + Page( + BookLine("The Chicken can strike", 55), + BookLine("anybody, anywhere. It", 56), + BookLine("is said that a coven ", 57), + BookLine("worships this fell fowl and", 58), + BookLine("have even built a shrine to it.", 59), + BookLine("The exact location of this", 60), + BookLine("shrine is unknown, but is", 61), + BookLine("rumoured to be in the", 62), + BookLine("fairyworlds. Further", 63), + BookLine("rumour suggests that", 64), + BookLine("his lair is guarded by", 65), + ), + Page( + BookLine("some fearsome beasts, but", 66), + BookLine("it may be reached by", 67), + BookLine("making a tasty offering...", 68), + BookLine("whatever that may be.", 69), + ), + ) + ) + } + + 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.BOOK_ON_CHICKENS_7464, 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/misthalin/draynor/dialogue/DiangoDialogue.java b/Server/src/main/content/region/misthalin/draynor/dialogue/DiangoDialogue.java index 75b103eaf..754d79dce 100644 --- a/Server/src/main/content/region/misthalin/draynor/dialogue/DiangoDialogue.java +++ b/Server/src/main/content/region/misthalin/draynor/dialogue/DiangoDialogue.java @@ -55,11 +55,11 @@ public final class DiangoDialogue extends DialoguePlugin { } break; case 10: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "That's right. There's a funny story behind them, their", "shipment was held up by thieves"); + interpreter.sendDialogues(npc, FacialExpression.LAUGH, "That's right. There's a funny story behind them, their", "shipment was held up by thieves"); stage = 11; break; case 11: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "The crate was marked 'Dragon Plates'.", "Apparently they thought it was some kind of armour,", "when really it's just a plate!"); + interpreter.sendDialogues(npc, FacialExpression.LAUGH, "The crate was marked 'Dragon Plates'.", "Apparently they thought it was some kind of armour,", "when really it's just a plate!"); stage = 12; break; case 12: @@ -97,7 +97,7 @@ public final class DiangoDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Howdy there, partner! Want to see my spinning plates?", "Or did ya want a holiday item back?"); + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Howdy there, partner! Want to see my spinning plates?", "Or did ya want a holiday item back?"); stage = 0; return true; } diff --git a/Server/src/main/content/region/misthalin/draynor/dialogue/DraynorBankGuard.java b/Server/src/main/content/region/misthalin/draynor/dialogue/DraynorBankGuard.java index 6548122c2..13275ac76 100644 --- a/Server/src/main/content/region/misthalin/draynor/dialogue/DraynorBankGuard.java +++ b/Server/src/main/content/region/misthalin/draynor/dialogue/DraynorBankGuard.java @@ -231,7 +231,7 @@ public final class DraynorBankGuard extends DialoguePlugin { stage = 52; break; case 52: - npc("The Bank has already replaces all the stolen items that", "belonged to customers."); + npc("The Bank has already replaced all the stolen items that", "belonged to customers."); stage = 53; break; case 53: diff --git a/Server/src/main/content/region/misthalin/draynor/dialogue/FolkloreBook.kt b/Server/src/main/content/region/misthalin/draynor/dialogue/FolkloreBook.kt new file mode 100644 index 000000000..1ad19ee1a --- /dev/null +++ b/Server/src/main/content/region/misthalin/draynor/dialogue/FolkloreBook.kt @@ -0,0 +1,60 @@ +package content.region.misthalin.draynor.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.api.setAttribute +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.player.Player +import org.rs09.consts.Items + +class FolkloreBook : InteractionListener { + + // Book found on the bookshelves in the Wise Old Man's house in Draynor Village. + + companion object { + private val TITLE = "The Myth of the Elder-dragons" + private val CONTENTS = arrayOf( + PageSet( + Page( + BookLine("Although the Elder-dragons", 55), + BookLine("are generally considered", 56), + BookLine("to be a myth, stories tell", 57), + BookLine("of an ancient race that dwelt", 58), + BookLine("on Gielinor before any other.", 59), + BookLine("In some arcane way their", 60), + BookLine("lifeforce was linked to the", 61), + BookLine("life of the world itself;", 62), + BookLine("it is written that, as long", 63), + BookLine("as the Elder-dragons", 64), + BookLine("continue to live,", 65), + ), + Page( + BookLine("the world shall not end.", 66), + BookLine("However, sceptics claim", 67), + BookLine("that the myth of the", 68), + BookLine("Elder-dragons was invented", 69), + BookLine("to cast doubt on claims", 70), + BookLine("that this realm was lifeless", 71), + BookLine("until the gods first arrived.", 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.BOOK_OF_FOLKLORE_5508, IntType.ITEM, "read") { player, _ -> + BookInterface.openBook(player, BookInterface.FANCY_BOOK_3_49, ::display) + return@on true + } + } +} diff --git a/Server/src/main/content/region/misthalin/draynor/dialogue/FortunatoDialogue.java b/Server/src/main/content/region/misthalin/draynor/dialogue/FortunatoDialogue.java deleted file mode 100644 index a30851fe4..000000000 --- a/Server/src/main/content/region/misthalin/draynor/dialogue/FortunatoDialogue.java +++ /dev/null @@ -1,115 +0,0 @@ -package content.region.misthalin.draynor.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 fortunato. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class FortunatoDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code FortunatoDialogue} {@code Object}. - */ - public FortunatoDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code FortunatoDialogue} {@code Object}. - * @param player the player. - */ - public FortunatoDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new FortunatoDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Can I help you at all?"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - - switch (stage) { - case 0: - interpreter.sendOptions("Select an Option", "Yes, what are you selling?", "Not at the momennt."); - stage = 1; - break; - case 1: - - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Yes, what are you selling?"); - stage = 10; - break; - case 2: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Then move along, you filthy ragamuffin, I have customers", "to server!"); - stage = 24; - break; - } - - break; - case 10: - end(); - npc.openShop(player); - break; - case 20: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Yes, indeed. The finest wine in Misthalin."); - stage = 21; - break; - case 21: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Care to take a look at my wares?"); - stage = 269; - break; - case 269: - interpreter.sendOptions("Select an Option", "Yes.", "Not at the moment."); - stage = 22; - break; - case 22: - - switch (buttonId) { - case 1: - end(); - stage = 10; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Not at the moment."); - stage = 23; - break; - } - - break; - case 23: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Then move along, you filthy ragamuffin, I have customers", "to server!"); - stage = 24; - break; - case 24: - end(); - break; - } - - return true; - } - - @Override - public int[] getIds() { - return new int[] { 3671 }; - } -} diff --git a/Server/src/main/content/region/misthalin/draynor/dialogue/FortunatoDialogue.kt b/Server/src/main/content/region/misthalin/draynor/dialogue/FortunatoDialogue.kt new file mode 100644 index 000000000..a26beb3ce --- /dev/null +++ b/Server/src/main/content/region/misthalin/draynor/dialogue/FortunatoDialogue.kt @@ -0,0 +1,41 @@ +package content.region.misthalin.draynor.dialogue + +// import content.region.misthalin.silvarea.quest.ragandboneman.FortunatoDialogueFile +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.plugin.Initializable +import core.tools.END_DIALOGUE +import core.tools.START_DIALOGUE +import org.rs09.consts.NPCs + +@Initializable +class FortunatoDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + // openDialogue(player!!, FortunatoDialogueFile(), npc) + when (stage) { + START_DIALOGUE -> npcl(FacialExpression.ASKING, "Can I help you at all?").also { stage++ } + 1 -> showTopics( + Topic(FacialExpression.FRIENDLY, "Yes, what are you selling?", 2), + Topic(FacialExpression.FRIENDLY, "Not at the moment", 3), + ) + 2 -> openNpcShop(player, NPCs.FORTUNATO_3671).also { + end() + } + 3 -> npcl(FacialExpression.ANGRY, "Then move along, you filthy ragamuffin, I have customers to serve!").also { + stage = END_DIALOGUE + } + } + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return FortunatoDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.FORTUNATO_3671) + } +} \ No newline at end of file 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 bc7a4c283..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: @@ -138,13 +139,13 @@ public final class LeelaDialogue extends DialoguePlugin { end(); return true; } - if (player.getInventory().remove(remove)) { + if (!player.getInventory().containsItem(BRONZE_KEY) && player.getInventory().remove(remove)) { if (!player.getInventory().add(BRONZE_KEY)) { GroundItemManager.create(BRONZE_KEY, player); } - end(); player.getPacketDispatch().sendMessage("Leela gives you another key."); } + end(); stage = 99; break; case 99: @@ -182,7 +183,7 @@ public final class LeelaDialogue extends DialoguePlugin { end(); break; case 120: - interpreter.sendDialogues(npc, null, "I don't thik you should. If you do the rest of the", "gang and Keli would attack you. The door guard", "should be removed first, to make it easy."); + interpreter.sendDialogues(npc, null, "I don't think you should. If you do the rest of the", "gang and Keli would attack you. The door guard", "should be removed first, to make it easy."); stage = 121; break; case 121: @@ -215,7 +216,7 @@ public final class LeelaDialogue extends DialoguePlugin { GroundItemManager.create(BRONZE_KEY, player); } quest.setStage(player, 40); - interpreter.sendDialogues(npc, null, "Good, you have all the basic equipment. Next to deal", "with the guard on the door. he is talkative, try to find", "a weakness in him."); + interpreter.sendDialogues(npc, null, "Good, you have all the basic equipment. Next to deal", "with the guard on the door. He is talkative, try to find", "a weakness in him."); stage = 2; break; case 2: 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/OliviaDialogue.java b/Server/src/main/content/region/misthalin/draynor/dialogue/OliviaDialogue.java index 639d7460f..4f53b99b9 100644 --- a/Server/src/main/content/region/misthalin/draynor/dialogue/OliviaDialogue.java +++ b/Server/src/main/content/region/misthalin/draynor/dialogue/OliviaDialogue.java @@ -40,11 +40,11 @@ public class OliviaDialogue extends DialoguePlugin { npc.openShop(player); break; case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No, thanks."); + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "No, thanks."); stage = 20; break; case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Where do I get rarer seeds from?"); + interpreter.sendDialogues(player, FacialExpression.ASKING, "Where do I get rarer seeds from?"); stage = 40; break; @@ -54,7 +54,7 @@ public class OliviaDialogue extends DialoguePlugin { end(); break; case 40: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "The Master Farmers usually carry a few rare seeds", "around with them, although I don't know if they'd want", "to part with them for any price to be honest."); + interpreter.sendDialogues(npc, FacialExpression.FRIENDLY, "The Master Farmers usually carry a few rare seeds", "around with them, although I don't know if they'd want", "to part with them for any price to be honest."); stage = 41; break; case 41: @@ -73,7 +73,7 @@ public class OliviaDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Would you like to trade in seeds?"); + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Would you like to trade in seeds?"); stage = 0; return true; } 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 3a3c1647d..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."); @@ -81,7 +82,7 @@ public class PrinceAliDialogue extends DialoguePlugin { stage = 1; break; case 1: - interpreter.sendDialogues(player, null, "With a disguise. I have removed the Lady Keli. she is", "tied up, but will not stay tied up for long."); + interpreter.sendDialogues(player, null, "With a disguise. I have removed the Lady Keli. She is", "tied up, but will not stay tied up for long."); stage = 2; break; case 2: 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/StrangeBook.kt b/Server/src/main/content/region/misthalin/draynor/dialogue/StrangeBook.kt new file mode 100644 index 000000000..3def67ce9 --- /dev/null +++ b/Server/src/main/content/region/misthalin/draynor/dialogue/StrangeBook.kt @@ -0,0 +1,247 @@ +package content.region.misthalin.draynor.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.api.setAttribute +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.player.Player +import org.rs09.consts.Items + +class StrangeBook : InteractionListener { + + // The strange book is a book found on the bookshelves in the Wise Old Man's. + companion object { + private val TITLE = "Stange book" + private val CONTENTS = arrayOf( + PageSet( + Page( + BookLine("Perception and Reality", 55), + BookLine("... which leads us to the", 56), + BookLine("new definition of knowledge", 57), + BookLine("as a belief that is both", 58), + BookLine("reliably generated and true.", 59), + BookLine("Our senses tell us about", 61), + BookLine("the world, but to what", 62), + BookLine("extent are they reliable?", 63), + BookLine("One witness may perceive", 64), + BookLine("the weather to be hot,", 65), + ), + Page( + BookLine("but another witness may", 66), + BookLine("consider it to be quite", 67), + BookLine("cold.", 68), + BookLine("Perception of colour may", 70), + BookLine("be similarly affected, as", 71), + BookLine("the reader may demonstrate", 72), + BookLine("by staring at a piece of", 73), + BookLine("brightly-coloured paper.", 74), + BookLine("When the paper is removed,", 75), + BookLine("the reader may find that", 76), + ) + ), + PageSet( + Page( + BookLine("everything else appears", 55), + BookLine("to be a slightly different", 56), + BookLine("colour from normal.", 57), + BookLine("We may conclude from this", 59), + BookLine("that, although we are", 60), + BookLine("sensing some intrinsic", 61), + BookLine("property of the object", 62), + BookLine("under examination,", 63), + BookLine("qualities such as colour", 64), + BookLine("and temperature are NOT", 65), + ), + Page( + BookLine("intrinsic.", 66), + BookLine("Henceforth we recommend", 68), + BookLine("that the reader consider", 69), + BookLine("them to be Secondary", 70), + BookLine("qualities. The quantity", 71), + BookLine("of caloric within the", 72), + BookLine("object that gives it this", 73), + BookLine("perceived temperature", 74), + BookLine("truly is intrinsic to", 75), + BookLine("the object, so this may", 76), + ) + ), + PageSet( + Page( + BookLine("be considered a Primary", 55), + BookLine("quality.", 56), + BookLine("In the following chapter", 58), + BookLine("we will examine the", 59), + BookLine("implications of this", 60), + BookLine("distinction for the study", 61), + BookLine("of natural philosophy.", 62), + BookLine("Certainty and Assumption", 65), + ), + Page( + BookLine("In this chapter we aim", 66), + BookLine("to examine the extent to", 67), + BookLine("which our senses give", 68), + BookLine("us a reliable impression", 69), + BookLine("of the world in which", 70), + BookLine("we live.", 71), + BookLine("Although we may believe", 73), + BookLine("that we are seeing and", 74), + BookLine("hearing things that exist,", 75), + BookLine("we may be deceived by", 76), + ) + ), + PageSet( + Page( + BookLine("our senses, as every", 55), + BookLine("keen drinker will have", 56), + BookLine("experienced! But if our", 57), + BookLine("senses may be deceived,", 58), + BookLine("what can we trust?", 59), + BookLine("We may perhaps try to", 61), + BookLine("rely only on logical", 62), + BookLine("deductions and mathematical", 63), + BookLine("principles. Yet it is", 64), + BookLine("even possible that some", 65), + ), + Page( + BookLine("fiend may have confused", 66), + BookLine("our minds to the extent", 67), + BookLine("that we are making false", 68), + BookLine("deductions without being", 69), + BookLine("aware of this.", 70), + BookLine("We therefore present the", 72), + BookLine("following statement as", 73), + BookLine("the only piece of knowledge", 74), + BookLine("about which we cannot", 75), + BookLine("possibly be deceived:", 76), + ) + ), + PageSet( + Page( + BookLine("'I EXIST'", 55), + BookLine("Although all information", 57), + BookLine("gained through use of our", 58), + BookLine("senses and minds may be", 59), + BookLine("distorted, no self-aware", 60), + BookLine("being can be led to", 61), + BookLine("believe that it does not", 62), + BookLine("exist. It may not be the", 63), + BookLine("man or woman it believes", 64), + BookLine("itself to be, but it can", 65), + ), + Page( + BookLine("never question its own", 66), + BookLine("existence in some form.", 67), + BookLine("In the following chapter", 69), + BookLine("we will discuss ways in", 70), + BookLine("which one may attempt to", 71), + BookLine("demonstrate the existence", 72), + BookLine("of a world outside of one's", 73), + BookLine("own consciousness.", 74), + BookLine("Graphical solution of", 76), + ) + ), + PageSet( + Page( + BookLine("mathematical problems", 55), + BookLine("A simple graph containing", 57), + BookLine("an x-axis and a y-axis may", 58), + BookLine("be used to represent", 59), + BookLine("equations similar to the", 60), + BookLine("following: x + 3y = 5", 61), + BookLine("For every 'x' and 'y' that", 63), + BookLine("satisfy the equation", 64), + BookLine("(such as x = 2, y = 1 or", 65), + ), + Page( + BookLine("x = 5, y = 0 for the", 66), + BookLine("above example), the points", 67), + BookLine("'x,y' may be plotted on", 68), + BookLine("the graph, and it will be", 69), + BookLine("found that they form a line.", 70), + BookLine("If a second equation of", 72), + BookLine("this form is plotted on", 73), + BookLine("the same graph, the two", 74), + BookLine("lines may cross at some", 75), + BookLine("point.", 76), + ) + ), + PageSet( + Page( + BookLine("Any point where the two", 55), + BookLine("lines cross will be given", 56), + BookLine("by some 'x' and 'y'", 57), + BookLine("that fits both equations.", 58), + BookLine("Readers are invited to", 60), + BookLine("create two such equations", 61), + BookLine("and try it for themselves!", 62), + BookLine("Readers' Comments", 64), + ), + Page( + BookLine("This page left blank for", 67), + BookLine("readers' comments", 68), + BookLine("... better now, walls look", 70), + BookLine("decent, nice and elegant.", 71), + BookLine("Those new lanterns were a", 72), + BookLine("rip-off, blasted dwarf", 73), + BookLine("craftsmen. Still, not a", 74), + BookLine("problem for me!", 75), + ) + ), + PageSet( + Page( + BookLine("Even got my old Saradomin", 55), + BookLine("armour gilded, beautiful", 56), + BookLine("gold edges on it now. No-one", 57), + BookLine("else's going to have armour", 58), + BookLine("like that, that'll make", 59), + BookLine("them think, heh heh heh...", 60), + BookLine("Lick of gold-leaf here and", 62), + BookLine("there, even my globe looks", 63), + BookLine("smarter now, maybe that", 64), + BookLine("bath screen was a bit much?", 65), + ), + Page( + BookLine("No, I saw it, I wanted it,", 66), + BookLine("it's mine.", 67), + BookLine("Vidi, volui, mihi est!", 68), + BookLine("Still don't know what that", 70), + BookLine("'thing' is, but it looks nice", 71), + BookLine("on my desk. Probably some", 72), + BookLine("kind of transport? Must run", 73), + BookLine("off hot air or dragon", 74), + BookLine("flatulence? Running a", 76), + ) + ), + PageSet( + Page( + BookLine("bit short of runes now.", 55), + BookLine("Those wizards over to", 56), + BookLine("the south always seem to", 57), + BookLine("have plenty, but will they", 58), + BookLine("spare me some? Bah! Not", 59), + BookLine("a chance, poxy mage scum!", 61), + BookLine("Perhaps another little expedition", 62), + BookLine("is needed - they all", 63), + BookLine("look pretty weedy over there...", 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.STRANGE_BOOK_5507, IntType.ITEM, "read") { player, _ -> + BookInterface.openBook(player, BookInterface.FANCY_BOOK_3_49, ::display) + return@on true + } + } +} 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/dialogue/WiseOldManDialogue.java b/Server/src/main/content/region/misthalin/draynor/dialogue/WiseOldManDialogue.java index fd67dd044..d6c82d58f 100644 --- a/Server/src/main/content/region/misthalin/draynor/dialogue/WiseOldManDialogue.java +++ b/Server/src/main/content/region/misthalin/draynor/dialogue/WiseOldManDialogue.java @@ -1,11 +1,16 @@ package content.region.misthalin.draynor.dialogue; +import core.api.Container; 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.DiaryType; import core.plugin.Initializable; import core.game.node.item.Item; +import org.rs09.consts.Items; + +import static core.api.ContentAPIKt.*; /** * Represents the dialogue plugin used for the wise old man. @@ -26,6 +31,13 @@ public final class WiseOldManDialogue extends DialoguePlugin { */ private static final Item COINS = new Item(995, 99000); + /** + * Represents the unique books item. + */ + + private static final Item[] UNIQUE_BOOKS = new Item[]{ new Item(5507), new Item(5508), new Item(7464)}; + + /** * Constructs a new {@code WiseOldManDialogue} {@code Object}. */ @@ -61,6 +73,29 @@ public final class WiseOldManDialogue extends DialoguePlugin { public boolean handle(int interfaceId, int buttonId) { switch (stage) { case 0: + if (getAttribute(player, "reclaim-qp-cape", false) || getAttribute(player, "reclaim-qp-hood", false)) { + if (getAttribute(player, "reclaim-qp-cape", false) && getAttribute(player, "reclaim-qp-hood", false)) { + npcl(FacialExpression.NEUTRAL, "I assume you're looking for your items? I've placed them " + (freeSlots(player) < 2 ? "at your feet." : "in your inventory.")); + removeAttribute(player, "reclaim-qp-cape"); + removeAttribute(player, "reclaim-qp-hood"); + addItemOrDrop(player, Items.QUEST_POINT_CAPE_9813, 1); + addItemOrDrop(player, Items.QUEST_POINT_HOOD_9814, 1); + stage = 505; + return true; + } else if (getAttribute(player, "reclaim-qp-cape", false)) { + npcl(FacialExpression.NEUTRAL, "I assume you're looking for your Quest Point Cape? I've placed it " + (freeSlots(player) < 1 ? "at your feet." : "in your inventory.")); + removeAttribute(player, "reclaim-qp-cape"); + addItemOrDrop(player, Items.QUEST_POINT_CAPE_9813, 1); + stage = 505; + return true; + } else { + npcl(FacialExpression.NEUTRAL, "I assume you're looking for your Quest Point Hood? I've placed it " + (freeSlots(player) < 1 ? "at your feet." : "in your inventory.")); + removeAttribute(player, "reclaim-qp-hood"); + addItemOrDrop(player, Items.QUEST_POINT_HOOD_9814, 1); + stage = 505; + return true; + } + } if (player.getQuestRepository().hasCompletedAll()) { options("Quest Point Cape.", "Something else."); stage = 500; @@ -166,16 +201,26 @@ public final class WiseOldManDialogue extends DialoguePlugin { } break; case 100: - npc("You seem to have no junk in your bank, sorry."); - stage = 101; + if (player.getBank().containsAtLeastOneItem(UNIQUE_BOOKS) && player.getBank().remove(UNIQUE_BOOKS)){ + npc(FacialExpression.DISGUSTED, "That's my book! What's it doing in your bank?"); + stage = 101; + } else { + npc("You seem to have no junk in your bank, sorry."); + stage = 101; + } break; case 101: player.getAchievementDiaryManager().finishTask(player, DiaryType.LUMBRIDGE, 1, 16); end(); break; case 102: - npc("You seem to have no junk in your inventory, sorry."); - stage = 101; + if (player.getInventory().containsAtLeastOneItem(UNIQUE_BOOKS) && player.getInventory().remove(UNIQUE_BOOKS)){ + npc(FacialExpression.DISGUSTED, "That's my book! What's it doing in your inventory?"); + stage = 101; + } else { + npc("You seem to have no junk in your inventory, sorry."); + stage = 101; + } break; case 30: npc("Jolly good. Give it to me, and I'll tell you anything I know", "about it."); diff --git a/Server/src/main/content/region/misthalin/draynor/handlers/DBRCutscenePlugin.java b/Server/src/main/content/region/misthalin/draynor/handlers/DBRCutscenePlugin.java index 6c68b2a23..78c30fa3e 100644 --- a/Server/src/main/content/region/misthalin/draynor/handlers/DBRCutscenePlugin.java +++ b/Server/src/main/content/region/misthalin/draynor/handlers/DBRCutscenePlugin.java @@ -408,7 +408,7 @@ public final class DBRCutscenePlugin extends CutscenePlugin { getWiseOldMan().graphics(TELEKENTIC_GRAPHIC); getWiseOldMan().getSkills().setStaticLevel(Skills.MAGIC, 99); getWiseOldMan().getSkills().setLevel(Skills.MAGIC, 99); - SpellBookManager.SpellBook.MODERN.getSpell(65535).cast(getWiseOldMan(), GroundItemManager.get(PARTY_HAT.getId(), base.transform(20, 44, 0), player)); + SpellBookManager.SpellBook.MODERN.getSpell(19).cast(getWiseOldMan(), GroundItemManager.get(PARTY_HAT.getId(), base.transform(20, 44, 0), player)); } /** diff --git a/Server/src/main/content/region/misthalin/draynor/handlers/DraynorManorShortcutListener.kt b/Server/src/main/content/region/misthalin/draynor/handlers/DraynorManorShortcutListener.kt new file mode 100644 index 000000000..457a64df0 --- /dev/null +++ b/Server/src/main/content/region/misthalin/draynor/handlers/DraynorManorShortcutListener.kt @@ -0,0 +1,42 @@ +package content.region.misthalin.draynor.handlers + +import content.global.skill.agility.AgilityHandler +import core.api.hasLevelDyn +import core.api.sendDialogue +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.skill.Skills +import core.game.world.map.Direction +import core.game.world.update.flag.context.Animation +import org.rs09.consts.Scenery + +class DraynorManorShortcutListener : InteractionListener { + override fun defineListeners() { + + val BROKEN_RAILING = Scenery.BROKEN_RAILING_37703 + val SQUEEZE_ANIMATION = 3844 + + on(BROKEN_RAILING, IntType.SCENERY, "squeeze-through") { player, _ -> + if (!hasLevelDyn(player, Skills.AGILITY, 28)) { + sendDialogue(player, "You need an Agility level of at least 28 to do this.") + return@on true + } + + val squeezeAnim = Animation.create(SQUEEZE_ANIMATION) + if (player.location.x >= 3086) { + AgilityHandler.forceWalk( + player, -1, player.location, + player.location.transform(Direction.WEST, 1), + squeezeAnim, 5, 0.0, null + ) + } else { + AgilityHandler.forceWalk( + player, -1, player.location, + player.location.transform(Direction.EAST, 1), + squeezeAnim, 5, 0.0, null + ) + } + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misthalin/draynor/handlers/DraynorTreeNPC.java b/Server/src/main/content/region/misthalin/draynor/handlers/DraynorTreeNPC.java index 713c21ba4..2bc5f6852 100644 --- a/Server/src/main/content/region/misthalin/draynor/handlers/DraynorTreeNPC.java +++ b/Server/src/main/content/region/misthalin/draynor/handlers/DraynorTreeNPC.java @@ -12,6 +12,10 @@ import core.game.world.map.RegionManager; import core.game.world.update.flag.context.Animation; import core.plugin.Initializable; import core.tools.RandomFunction; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.playAudio; +import static core.api.ContentAPIKt.playHurtAudio; /** * Represents the abstract draynor tree npc. @@ -65,7 +69,9 @@ public final class DraynorTreeNPC extends AbstractNPC { for (Player p : players) { faceTemporary(p, 2); getAnimator().forceAnimation(ANIMATION); + playAudio(p, Sounds.NASTY_TREE_ATTACK_651); int hit = RandomFunction.random(2); + if (hit > 0) playHurtAudio(p, 20); p.getImpactHandler().manualHit(this, hit, hit > 0 ? HitsplatType.NORMAL : HitsplatType.MISS); attackDelay = GameWorld.getTicks() + 3; p.animate(p.getProperties().getDefenceAnimation()); diff --git a/Server/src/main/content/region/misthalin/draynor/handlers/WiseOldManHouseListener.kt b/Server/src/main/content/region/misthalin/draynor/handlers/WiseOldManHouseListener.kt new file mode 100644 index 000000000..3e46f8f91 --- /dev/null +++ b/Server/src/main/content/region/misthalin/draynor/handlers/WiseOldManHouseListener.kt @@ -0,0 +1,71 @@ +package content.region.misthalin.draynor.handlers + +import core.api.addItem +import core.api.inInventory +import core.api.sendMessage +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import org.rs09.consts.Items +import org.rs09.consts.Scenery + +class WiseOldManHouseListener : InteractionListener { + + // Searching the bookshelves in the Wise Old Man's house + + override fun defineListeners() { + + // All shelves in the room + val bookshelves = intArrayOf( + Scenery.OLD_BOOKSHELF_7058, Scenery.OLD_BOOKSHELF_7071, + Scenery.OLD_BOOKSHELF_7072, Scenery.OLD_BOOKSHELF_7073, + Scenery.OLD_BOOKSHELF_7074, Scenery.OLD_BOOKSHELF_7076, + Scenery.OLD_BOOKSHELF_7077, Scenery.OLD_BOOKSHELF_7078, + Scenery.OLD_BOOKSHELF_7079, Scenery.OLD_BOOKSHELF_7081, + Scenery.OLD_BOOKSHELF_7082, Scenery.OLD_BOOKSHELF_7089, + Scenery.OLD_BOOKSHELF_9146, Scenery.OLD_BOOKSHELF_9147, + ) + + // Shelves contains unique books + val containBookshelf = intArrayOf( + Scenery.OLD_BOOKSHELF_7065, // STRANGE BOOK + Scenery.OLD_BOOKSHELF_7066, // FOLKLORE BOOK + Scenery.OLD_BOOKSHELF_7068, // CHICKEN BOOK + ) + + val strangeBook = Items.STRANGE_BOOK_5507 + val folkloreBook = Items.BOOK_OF_FOLKLORE_5508 + val chickenBook = Items.BOOK_ON_CHICKENS_7464 + + + on(Scenery.OLD_BOOKSHELF_7065, IntType.SCENERY, "search") { player, _ -> + if (!inInventory(player, strangeBook)) { + sendMessage(player, "You search the bookcase and find a book named 'Strange Book'.") + addItem(player, strangeBook) + } else { + sendMessage(player, "You search the bookcase and find nothing of interest.") + } + return@on true + } + + on(Scenery.OLD_BOOKSHELF_7066, IntType.SCENERY, "search") { player, _ -> + if (!inInventory(player, folkloreBook)) { + sendMessage(player, "You search the bookcase and find a book named 'Book of folklore'.") + addItem(player, folkloreBook) + } else { + sendMessage(player, "You search the bookcase and find nothing of interest.") + } + return@on true + } + + on(Scenery.OLD_BOOKSHELF_7068, IntType.SCENERY, "search") { player, _ -> + if (!inInventory(player, chickenBook)) { + sendMessage(player, "You search the bookcase and find a book named 'Book on chickens'.") + addItem(player, chickenBook) + } else { + sendMessage(player, "You search the bookcase and find nothing of interest.") + } + return@on true + } + + } +} \ 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 3e77bcd3b..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; @@ -34,7 +33,8 @@ import core.plugin.Plugin; import core.plugin.ClassScanner; import core.tools.RandomFunction; -import static core.api.ContentAPIKt.teleport; +import static core.api.ContentAPIKt.*; +import content.data.Quests; /** * Handles the animal magnetism plugin. @@ -61,11 +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, 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; } @@ -74,15 +77,6 @@ public final class AnimalMagnetismPlugin extends OptionHandler { case 10500: player.getPacketDispatch().sendMessage("Perhaps you should wait a few hundred years or so?"); break; - case 4251: - case 4252: - if (option.equalsIgnoreCase("empty")) { - handleEctophial(player, (Item) node); - } else { - player.getInventory().remove(node.asItem()); - player.sendMessage("The ectophial disappears as it hits the ground..."); - } - break; case 10492: open(player); break; @@ -118,34 +112,6 @@ public final class AnimalMagnetismPlugin extends OptionHandler { player.getPacketDispatch().sendMessage("You fiddle with the notes."); } - /** - * Handles the ectophial emptying. - * @param player the player. - * @param item the item. - */ - private void handleEctophial(final Player player, final Item item) { - if(teleport(player,Location.create(3658, 3517, 0), TeleportType.ECTOPHIAL)) { - player.getInventory().replace(new Item(4252), item.getSlot()); - player.sendMessage("...and the world changes around you.", 4); - player.getAudioManager().send(4580); - player.sendMessage("You empty the ectoplasm onto the ground around your feet..."); - player.getPulseManager().run(new Pulse(9, player) { - @Override - public boolean pulse() { - player.faceLocation(new Location(3659, 3519, 0)); - if (player.getInventory().containsItem(new Item(4252))) { - player.animate(Animation.create(1652)); - player.getAudioManager().send(1132); - player.getInventory().remove(new Item(4252)); - player.getInventory().add(item); - player.sendMessage("You refill the ectophial from the Ectofuntus."); - } - return true; - } - }); - } - } - /** * Handles the hammering of a magnet. * @author Vexia @@ -231,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) { @@ -276,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); @@ -326,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 a04139a72..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; @@ -12,6 +13,8 @@ import core.game.node.entity.player.link.quest.Quest; import core.game.node.item.Item; import core.game.world.GameWorld; +import static core.api.ContentAPIKt.*; + /** * Handles the ava npc dialogue. * @author Vexia @@ -53,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; @@ -339,7 +342,7 @@ public final class AvaDialogue extends DialoguePlugin { break; case 14: if (player.getInventory().remove(AnimalMagnetism.BAR_MAGNET)) { - player.getConfigManager().set(939, 150, true); + setVarp(player, 939, 150, true); quest.setStage(player, 28); end(); } 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/ernest/ErnestTheChickenPlugin.java b/Server/src/main/content/region/misthalin/draynor/quest/ernest/ErnestTheChickenPlugin.java index 5221f5e2f..fb009f9c8 100644 --- a/Server/src/main/content/region/misthalin/draynor/quest/ernest/ErnestTheChickenPlugin.java +++ b/Server/src/main/content/region/misthalin/draynor/quest/ernest/ErnestTheChickenPlugin.java @@ -21,6 +21,8 @@ import core.game.world.update.flag.context.Animation; import core.plugin.Initializable; import core.plugin.Plugin; +import static core.api.ContentAPIKt.*; + /** * Represents the ernest the chicken plugin to handle node interactions. * @author 'Vexia @@ -183,8 +185,8 @@ public final class ErnestTheChickenPlugin extends OptionHandler { * Method used to update the players config states. */ public final void updateConfigs() { - player.getConfigManager().set(LEVER_CONFIG, calculateLeverConfig()); - player.getConfigManager().set(DOOR_CONFIG, calculateDoorConfig()); + setVarp(player, LEVER_CONFIG, calculateLeverConfig()); + setVarp(player, DOOR_CONFIG, calculateDoorConfig()); save(); } 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/dialogue/BrotherBordissDialogue.kt b/Server/src/main/content/region/misthalin/edgeville/dialogue/BrotherBordissDialogue.kt index 9726db1c7..1a14231ff 100644 --- a/Server/src/main/content/region/misthalin/edgeville/dialogue/BrotherBordissDialogue.kt +++ b/Server/src/main/content/region/misthalin/edgeville/dialogue/BrotherBordissDialogue.kt @@ -13,13 +13,13 @@ import org.rs09.consts.Items * Handles Boriss' dialogue * @author Ceikry */ -class BrotherBordissDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { +class BrotherBordissDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player?): DialoguePlugin { return BrotherBordissDialogue(player) } override fun npc(vararg messages: String?): Component { - return npc(core.game.dialogue.FacialExpression.OLD_NORMAL,*messages) + return npc(FacialExpression.OLD_NORMAL,*messages) } override fun open(vararg args: Any?): Boolean { diff --git a/Server/src/main/content/region/misthalin/edgeville/dialogue/EdgevilleShopKeeperDialogue.java b/Server/src/main/content/region/misthalin/edgeville/dialogue/EdgevilleShopKeeperDialogue.java index 0003b70c7..94927f4a2 100644 --- a/Server/src/main/content/region/misthalin/edgeville/dialogue/EdgevilleShopKeeperDialogue.java +++ b/Server/src/main/content/region/misthalin/edgeville/dialogue/EdgevilleShopKeeperDialogue.java @@ -39,7 +39,7 @@ public final class EdgevilleShopKeeperDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Can I help you at all?"); + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Can I help you at all?"); stage = 0; return true; } @@ -58,7 +58,7 @@ public final class EdgevilleShopKeeperDialogue extends DialoguePlugin { npc.openShop(player); break; case 2: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "I'm glas you ask! You can buy as many of the items", "stocked as you wish. You can also sell most items to the", "shop."); + 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 = 20; break; case 3: diff --git a/Server/src/main/content/region/misthalin/edgeville/handlers/EdgevilleNodePlugin.java b/Server/src/main/content/region/misthalin/edgeville/handlers/EdgevilleNodePlugin.java index a72034698..34264e29a 100644 --- a/Server/src/main/content/region/misthalin/edgeville/handlers/EdgevilleNodePlugin.java +++ b/Server/src/main/content/region/misthalin/edgeville/handlers/EdgevilleNodePlugin.java @@ -56,9 +56,9 @@ public final class EdgevilleNodePlugin extends OptionHandler { break; case 12266: if (option.equalsIgnoreCase("open")) { - player.getConfigManager().set(680, 1<<22); + setVarp(player, 680, 1 << 22); } else if (option.equalsIgnoreCase("close")) { - player.getConfigManager().set(680, 0); + setVarp(player, 680, 0); } break; case 26933: // Edgeville Dungeon trapdoor (when closed) 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 index c39e162aa..e98e13693 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/dialogue/BobDialogue.java +++ b/Server/src/main/content/region/misthalin/lumbridge/dialogue/BobDialogue.java @@ -211,7 +211,7 @@ public final class BobDialogue extends DialoguePlugin { } 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", "Keldagrim. In Lumbridge and Draynor i can help you", "discover some very useful things indeed."); + 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: diff --git a/Server/src/main/content/region/kandarin/catherby/dialogue/CandleSellerPlugin.java b/Server/src/main/content/region/misthalin/lumbridge/dialogue/CandleSellerDialogue.java similarity index 56% rename from Server/src/main/content/region/kandarin/catherby/dialogue/CandleSellerPlugin.java rename to Server/src/main/content/region/misthalin/lumbridge/dialogue/CandleSellerDialogue.java index 8be253971..f2497754b 100644 --- a/Server/src/main/content/region/kandarin/catherby/dialogue/CandleSellerPlugin.java +++ b/Server/src/main/content/region/misthalin/lumbridge/dialogue/CandleSellerDialogue.java @@ -1,4 +1,4 @@ -package content.region.kandarin.catherby.dialogue; +package content.region.misthalin.lumbridge.dialogue; import core.game.dialogue.DialoguePlugin; import core.game.dialogue.FacialExpression; @@ -13,7 +13,7 @@ import core.game.node.item.Item; * @version 1.0 */ @Initializable -public final class CandleSellerPlugin extends DialoguePlugin { +public final class CandleSellerDialogue extends DialoguePlugin { /** * Represents the coins item. @@ -28,7 +28,7 @@ public final class CandleSellerPlugin extends DialoguePlugin { /** * Constructs a new {@code CandleSellerPlugin} {@code Object}. */ - public CandleSellerPlugin() { + public CandleSellerDialogue() { /** * empty. */ @@ -38,19 +38,19 @@ public final class CandleSellerPlugin extends DialoguePlugin { * Constructs a new {@code CandleSellerPlugin} {@code Object}. * @param player the player. */ - public CandleSellerPlugin(Player player) { + public CandleSellerDialogue(Player player) { super(player); } @Override public DialoguePlugin newInstance(Player player) { - return new CandleSellerPlugin(player); + return new CandleSellerDialogue(player); } @Override public boolean open(Object... args) { npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Do you want a lit candle for 1000 gold?"); + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Do you want a lit candle for 1000 gold?"); stage = 0; return true; } @@ -76,36 +76,36 @@ public final class CandleSellerPlugin extends DialoguePlugin { if (player.getInventory().contains(995, 1000)) { player.getInventory().remove(COINS); player.getInventory().add(new Item(33, 1)); - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Here you go then."); + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Here you go then."); stage = 400; } break; case 1: switch (buttonId) { case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Yes please."); + interpreter.sendDialogues(player, FacialExpression.HAPPY, "Yes please."); stage = 800; break; case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "One thousand gold?!"); + interpreter.sendDialogues(player, FacialExpression.EXTREMELY_SHOCKED, "One thousand gold?!"); stage = 20; break; case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No thanks, I'd rather curse the darkness."); + interpreter.sendDialogues(player, FacialExpression.EXTREMELY_SHOCKED, "No thanks, I'd rather curse the darkness."); stage = 30; break; } break; case 20: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Look, you're not going to be able to survive down that", "hole without a light source."); + interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "Look, you're not going to be able to survive down that", "hole without a light source."); stage = 21; break; case 21: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "So you could go off to the candle shop to buy one", "more cheaply. You could even make your own lantern,", "which is a lot better."); + interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "So you could go off to the candle shop to buy one", "more cheaply. You could even make your own lantern,", "which is a lot better."); stage = 22; break; case 22: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "But I bet you want to find out what's down there right", "now, don't you? And you can pay me 1000 gold for", "the privilege!"); + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "But I bet you want to find out what's down there right", "now, don't you? And you can pay me 1000 gold for", "the privilege!"); stage = 23; break; case 23: @@ -122,41 +122,41 @@ public final class CandleSellerPlugin extends DialoguePlugin { stage = 350; break; case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No way."); + interpreter.sendDialogues(player, FacialExpression.ANNOYED, "No way."); stage = 30; break; case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "How do you make lanterns?"); + interpreter.sendDialogues(player, FacialExpression.HALF_ASKING, "How do you make lanterns?"); stage = 230; break; } break; case 230: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Out of glass. The more advanced lanterns have a", "metal component as well."); + interpreter.sendDialogues(npc, FacialExpression.FRIENDLY, "Out of glass. The more advanced lanterns have a", "metal component as well."); stage = 231; break; case 231: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Firstly you can make a simple candle lantern out of", "glass. It's just like a candle, but the flame isn't exposed,", "so it's safer."); + interpreter.sendDialogues(npc, FacialExpression.FRIENDLY, "Firstly you can make a simple candle lantern out of", "glass. It's just like a candle, but the flame isn't exposed,", "so it's safer."); stage = 232; break; case 232: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Then you can make an oil lamp, which is brighter but", "has an exposed flame. But if you make an iron frame", "for it you can turn it into an oil lantern."); + interpreter.sendDialogues(npc, FacialExpression.FRIENDLY, "Then you can make an oil lamp, which is brighter but", "has an exposed flame. But if you make an iron frame", "for it you can turn it into an oil lantern."); stage = 233; break; case 233: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Finally there's a Bullseye lantern. You'll need to", "make a frame out of steel and add a glass lens."); + interpreter.sendDialogues(npc, FacialExpression.FRIENDLY, "Finally there's a Bullseye lantern. You'll need to", "make a frame out of steel and add a glass lens."); stage = 234; break; case 234: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Oce you've made your lamp or lantern, you'll need to", "make lamp oil for it. The chemist near Reimmington has", "a machine for that."); + interpreter.sendDialogues(npc, FacialExpression.FRIENDLY, "Oce you've made your lamp or lantern, you'll need to", "make lamp oil for it. The chemist near Reimmington has", "a machine for that."); stage = 235; break; case 235: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "For any light source, you'll need a tinderbox to light it.", "Keep your tinderbox handy in case it goes out!"); + interpreter.sendDialogues(npc, FacialExpression.FRIENDLY, "For any light source, you'll need a tinderbox to light it.", "Keep your tinderbox handy in case it goes out!"); stage = 236; break; case 236: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "But if all that's to complicated, you can buy a candle", "right here for 1000 gold!"); + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "But if all that's to complicated, you can buy a candle", "right here for 1000 gold!"); stage = 237; break; case 237: @@ -177,16 +177,16 @@ public final class CandleSellerPlugin extends DialoguePlugin { if (player.getInventory().remove(COINS)) { player.getInventory().add(CANDLE); - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Here you go then."); + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Here you go then."); stage = 400; } break; case 400: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "I should warn you, though, it can be dangerous to take", "a naked flame down there. You'd better off making", "a lantern."); + interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "I should warn you, though, it can be dangerous to take", "a naked flame down there. You'd better off making", "a lantern."); stage = 401; break; case 401: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Okay, thanks."); + interpreter.sendDialogues(player, FacialExpression.FRIENDLY, "Okay, thanks."); stage = 402; break; case 402: @@ -207,12 +207,12 @@ public final class CandleSellerPlugin extends DialoguePlugin { } if (player.getInventory().remove(COINS)) { player.getInventory().add(CANDLE); - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Here you go then."); + interpreter.sendDialogues(npc, FacialExpression.FRIENDLY, "Here you go then."); stage = 400; } break; case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No thanks, I'd rather curse the darkness."); + interpreter.sendDialogues(player, FacialExpression.EXTREMELY_SHOCKED, "No thanks, I'd rather curse the darkness."); stage = 291; break; } 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 e664607be..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,12 +10,13 @@ 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. * @author Ceikry */ -class DukeHoracioDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { +class DukeHoracioDialogue(player: Player? = null) : DialoguePlugin(player) { override fun getIds(): IntArray { return intArrayOf(741) @@ -23,14 +24,14 @@ class DukeHoracioDialogue(player: Player? = null) : core.game.dialogue.DialogueP 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, core.game.dialogue.FacialExpression.HALF_GUILTY, "Greetings. Welcome to my castle.") + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Greetings. Welcome to my castle.") } return true @@ -50,13 +51,13 @@ class DukeHoracioDialogue(player: Player? = null) : core.game.dialogue.DialogueP } 1 -> when (buttonId) { 1 -> { - interpreter.sendDialogues(player, core.game.dialogue.FacialExpression.HALF_GUILTY, "Have any quests for me?") + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Have any quests for me?") stage = 20 } 2 -> { interpreter.sendDialogues( npc, - core.game.dialogue.FacialExpression.HALF_GUILTY, + FacialExpression.HALF_GUILTY, "I hear many of the local people earn money by learning a", "skill. Many people get by in life by becoming accomplished", "smiths, cooks, miners and woodcutters." @@ -66,8 +67,8 @@ class DukeHoracioDialogue(player: Player? = null) : core.game.dialogue.DialogueP } 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++ } @@ -80,7 +81,7 @@ class DukeHoracioDialogue(player: Player? = null) : core.game.dialogue.DialogueP return true } - override fun newInstance(player: Player): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player): DialoguePlugin { return DukeHoracioDialogue(player) } } \ No newline at end of file diff --git a/Server/src/main/content/region/misthalin/lumbridge/dialogue/ExplorerJackDialogue.java b/Server/src/main/content/region/misthalin/lumbridge/dialogue/ExplorerJackDialogue.java index d5b061576..52339c8e2 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/dialogue/ExplorerJackDialogue.java +++ b/Server/src/main/content/region/misthalin/lumbridge/dialogue/ExplorerJackDialogue.java @@ -87,7 +87,7 @@ public class ExplorerJackDialogue extends DialoguePlugin { } break; case 1: - player("Oh sorry. I was just looking around"); + player("Oh sorry. I was just looking around."); stage++; break; case 2: @@ -95,15 +95,15 @@ public class ExplorerJackDialogue extends DialoguePlugin { stage++; break; case 3: - player("Uh...and all what?"); + player("Uh... and all what?"); stage++; break; case 4: - npc("Splendid! I love a person with a sense of humour. I bet", "you're from Ardougne eh? Ha!"); + npc("Splendid! I love a person with a sense of humour. I bet", "you're from Ardougne, eh? Ha!"); stage = -1; break; case 10: - npc("Ah, well it's a diary that helps you keep track of", "particular achievements you've made in the world of", "Keldagrim. In Lumbridge and Draynor i can help you", "discover some very useful things indeed."); + 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 11: @@ -123,7 +123,7 @@ public class ExplorerJackDialogue extends DialoguePlugin { stage = -1; break; case 30: - npc("You need to complete the taks 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."); + 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 31: diff --git a/Server/src/main/content/region/misthalin/lumbridge/dialogue/FishingTutorDialogue.java b/Server/src/main/content/region/misthalin/lumbridge/dialogue/FishingTutorDialogue.java index 1202aa487..5f0cb3a81 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/dialogue/FishingTutorDialogue.java +++ b/Server/src/main/content/region/misthalin/lumbridge/dialogue/FishingTutorDialogue.java @@ -50,41 +50,41 @@ public final class FishingTutorDialogue extends DialoguePlugin { case 1: switch (buttonId) { case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Can you teach me the basics of fishing please?"); + interpreter.sendDialogues(player, FacialExpression.ASKING, "Can you teach me the basics of fishing please?"); stage = 10; break; case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Tell me about different fish."); + interpreter.sendDialogues(player, FacialExpression.HALF_ASKING, "Tell me about different fish."); stage = 20; break; case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Where and what should I fish?"); + interpreter.sendDialogues(player, FacialExpression.ASKING, "Where and what should I fish?"); stage = 30; break; case 4: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Goodbye."); + interpreter.sendDialogues(player, FacialExpression.FRIENDLY, "Goodbye."); stage = 40; break; } break; case 10: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Ahoy, to fish, you click on your net in your inventory,", "then on the fishin' spot to put it in. Then you pull the", "net out and see if you got any shrimp."); + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Ahoy, to fish, you click on your net in your inventory,", "then on the fishin' spot to put it in. Then you pull the", "net out and see if you got any shrimp."); stage = 11; break; case 11: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I see.. is that it?"); + interpreter.sendDialogues(player, FacialExpression.THINKING, "I see.. is that it?"); stage = 12; break; case 12: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "There's far more as you progress, not just shrimps,", "you get more equipment, bigger fish and other things", "too..."); + interpreter.sendDialogues(npc, FacialExpression.FRIENDLY, "There's far more as you progress, not just shrimps,", "you get more equipment, bigger fish and other things", "too..."); stage = 13; break; case 13: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "When you have a full inventory, you can cook it or", "take it to the bank. You can find a bank on the roof of", "the castle in Lumbridge and a cookin' range in the", "castles kitchen."); + interpreter.sendDialogues(npc, FacialExpression.FRIENDLY, "When you have a full inventory, you can cook it or", "take it to the bank. You can find a bank on the roof of", "the castle in Lumbridge and a cookin' range in the", "castles kitchen."); stage = 14; break; case 14: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Arrr.. if yer lookin' fer quests you should head to", "the Mountain Dwarf who lies north-west of Taverely", "and have a chat with him, I'm sure he can point you in", "the right direction."); + interpreter.sendDialogues(npc, FacialExpression.FRIENDLY, "Arrr.. if yer lookin' fer quests you should head to", "the Mountain Dwarf who lies north-west of Taverley", "and have a chat with him, I'm sure he can point you in", "the right direction."); stage = 15; break; case 15: @@ -140,7 +140,7 @@ public final class FishingTutorDialogue extends DialoguePlugin { stage = 20; break; case 30: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Herrin' can be fished from Catherby and some other", "places when you reach level 10."); + interpreter.sendDialogues(npc, FacialExpression.FRIENDLY, "Herrin' can be fished from Catherby and some other", "places when you reach level 10."); stage = 31; break; case 31: diff --git a/Server/src/main/content/region/misthalin/lumbridge/dialogue/FredTheFarmer.java b/Server/src/main/content/region/misthalin/lumbridge/dialogue/FredTheFarmer.java deleted file mode 100644 index 890c64800..000000000 --- a/Server/src/main/content/region/misthalin/lumbridge/dialogue/FredTheFarmer.java +++ /dev/null @@ -1,374 +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.game.node.entity.player.link.quest.Quest; - -import core.plugin.Initializable; -import content.region.misthalin.lumbridge.quest.sheepshearer.SheepShearer; - -/** - * Represents the dialogue plugin used for fred the farmer npc. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class FredTheFarmer extends DialoguePlugin { - - /** - * Represents the quest instance. - */ - private Quest quest; - - /** - * Constructs a new {@code FredTheFarmer} {@code Object}. - */ - public FredTheFarmer() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code FredTheFarmer} {@code Object}. - * @param player the player. - */ - public FredTheFarmer(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new FredTheFarmer(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - quest = player.getQuestRepository().getQuest("Sheep Shearer"); - if (player.getQuestRepository().getQuest("Sheep Shearer").getStage(player) == 10 || player.getQuestRepository().getQuest("Sheep Shearer").getStage(player) == 90) { - if (SheepShearer.getWoolGiven(player) == 20 && player.getQuestRepository().getQuest("Sheep Shearer").getStage(player) == 90) { - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "That's the last of them."); - stage = 999; - return true; - } - if (player.getInventory().contains(1737, 1) || player.getInventory().contains(1759, 1)) { - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "What are you doing on my land?"); - stage = 5000; - return true; - } - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "How are you doing getting those balls of wool?"); - stage = 4000; - return true; - } - - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "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?"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - quest = player.getQuestRepository().getQuest("Sheep Shearer"); - switch (stage) { - case 0: - if (!player.getQuestRepository().isComplete("Sheep Shearer")) { - interpreter.sendOptions("Select an Option", "I'm looking for a quest.", "I'm looking for something to kill.", "I'm lost."); - stage = 1; - } else { - interpreter.sendOptions("Select an Option", "I'm looking for something to kill.", "I'm lost."); - stage = 346; - } - break; - case 346: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I'm looking for something to kill."); - stage = 20; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I'm lost."); - stage = 30; - break; - } - break; - case 1: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I'm looking for a quest."); - stage = 10; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I'm looking for something to kill."); - stage = 20; - break; - case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I'm lost."); - stage = 30; - break; - - } - break; - case 30: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "How can you be lost? Just follow the road east and", "south. You'll end up in Lumbridge fairly quickly."); - stage = 31; - break; - case 31: - end(); - break; - case 10: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "You're after a quest, you say? Actually I could do with", "a bit of help."); - stage = 11; - break; - case 11: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "My sheep are getting mighty woolly. I'd be much", "obliged if you could shear them. And while you're at it", "spin the wool for me too."); - stage = 12; - break; - case 12: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Yes, that's it. Bring me 20 balls of wool. And I'm sure", "I could sort out somesort of payment. Of course,", "there's a small matter of The Thing."); - stage = 13; - break; - case 13: - interpreter.sendOptions("Select an Option", "Yes okay. I can do that.", "That doesn't sound a very exiting quest.", "What do you mean, The Thing?"); - stage = 14; - break; - case 14: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Yes okay. I can do that."); - stage = 670; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "That doesn't sound a very exiting quest."); - stage = 560; - break; - case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "What do you mean, The Thing?"); - stage = 456; - break; - } - break; - case 670: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Good! Now one more thing, do you actually know how", "to shear a sheep?"); - stage = 678; - break; - case 678: - interpreter.sendOptions("Select an Option", "Of course!", "No, I don't know actually."); - stage = 679; - break; - case 679: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Of course!"); - stage = 954; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Err. No, I don't know actually."); - stage = 750; - break; - } - break; - case 954: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Do you know how to spin wool?"); - stage = 754; - break; - case 750: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Just use a set of shears on a sheep to shear it."); - stage = 751; - break; - case 751: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "That's all I have to do?"); - stage = 752; - break; - case 752: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Well once you've collected some wool you'll need to spin", "it into balls."); - stage = 753; - break; - case 753: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Do you know how to spin wool?"); - stage = 754; - break; - case 754: - interpreter.sendOptions("Select an Option", "I don't know how to spin wool, sorry.", "I'm something of an expert actually!"); - stage = 755; - break; - case 755: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I don't know how to spin wool, sorry."); - stage = 756; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I'm something of an expert actually!"); - stage = 850; - break; - } - break; - case 850: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Well you can stop grinning and get to work then."); - stage = 851; - break; - case 851: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "I'm not paying you by the hour!"); - stage = 852; - break; - case 852: - end(); - quest.start(player); - break; - case 756: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Don't worry, it's quite simple!"); - stage = 757; - break; - case 757: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "The nearest Spinning Wheel can be found on the first", "floor of Lumbridge Castle."); - stage = 758; - break; - case 758: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "To get to the Lumbridge Castle just follow the road east."); - stage = 759; - break; - case 759: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Thank you!"); - stage = 760; - break; - case 760: - end(); - quest.start(player); - break; - case 671: - end(); - break; - case 560: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Well what do you expect if you ask a farmer for a", "quest?"); - stage = 21; - break; - case 456: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Well now, no one has ever seen The Thing. That's", "why we call it The Thing, 'cos we don't know what it is."); - stage = 457; - break; - case 457: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Someone say it's a black hearted shapeshifter, hungering for", "the souls of hard working decent folk like me. Others", "say it's just a sheep."); - stage = 21; - break; - case 20: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "What, on my land? Leave my livestock alone you", "scoundrel!"); - stage = 21; - break; - case 21: - end(); - break; - case 4000: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "How many more do I need to give you?"); - stage = 4001; - break; - case 4001: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "You need to collect " + SheepShearer.getWoolLeft(player) + " more balls of wool."); - stage = 4002; - break; - case 4002: - if (!player.getInventory().contains(1759, 1)) { - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I haven't got any at the moment."); - stage = 4003; - break; - } - break; - case 4003: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Well at least you haven't been eaten."); - stage = 21; - break; - case 4004: - break; - case 5000: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I'm back!"); - stage = 50001; - break; - case 50001: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "How are you doing getting those balls of wool?"); - stage = 50002; - break; - case 50002: - if (player.getInventory().contains(1759, 1)) { - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I have some."); - stage = 50010; - break; - } - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "How many more do I need to give you?"); - stage = 50003; - break; - case 50003: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "You need to collect " + SheepShearer.getWoolLeft(player) + " more balls of wool."); - stage = 50004; - break; - case 50004: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Well I've got some wool. I've not managed to make it", "into a ball though."); - stage = 50005; - break; - case 50005: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Well go find a spinning wheel then. You can find one", "on the first floor of the Lumbridge Castle, just walk east on", "the road outside my house and you'll find Lumbridge."); - stage = 50006; - break; - case 50006: - end(); - break; - case 50010: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Give 'em here then."); - stage = 5011; - break; - case 5011: - int removeAmount = SheepShearer.getWoolRemove(player); - if (SheepShearer.addWool(player)) { - if (SheepShearer.getWoolGiven(player) == 20) { - player.getQuestRepository().getQuest("Sheep Shearer").setStage(player, 90); - } - interpreter.sendDialogue("You give fred " + removeAmount + " balls of wool"); - stage = 5012; - } - break; - case 5012: - if (SheepShearer.getWoolGiven(player) == 20) { - player.getQuestRepository().getQuest("Sheep Shearer").setStage(player, 90); - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "That's the last of them."); - stage = 999; - break;// enough - } - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "That's all I've got so far."); - stage = 5013; - break; - case 5013: - if (SheepShearer.getWoolGiven(player) == 20) { - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "That's the last of them."); - stage = 999; - break;// enough - } - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "I need " + SheepShearer.getWoolCollect(player) + " before I can pay you."); - stage = 5014; - break; - case 999: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "I guess I'd better pay you then."); - stage = 1000; - break; - case 1000: - end(); - Quest quest11 = player.getQuestRepository().getQuest("Sheep Shearer"); - quest11.finish(player); - player.removeAttribute("sheep-shearer:wool"); - break; - case 5014: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Ok, I'll work on it."); - stage = 5015; - break; - case 5015: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 758 }; - } -} diff --git a/Server/src/main/content/region/misthalin/lumbridge/dialogue/FredTheFarmerDialogue.kt b/Server/src/main/content/region/misthalin/lumbridge/dialogue/FredTheFarmerDialogue.kt new file mode 100644 index 000000000..357c76e2d --- /dev/null +++ b/Server/src/main/content/region/misthalin/lumbridge/dialogue/FredTheFarmerDialogue.kt @@ -0,0 +1,48 @@ +package content.region.misthalin.lumbridge.dialogue + +import content.region.misthalin.lumbridge.quest.sheepshearer.SSFredTheFarmerDialogue +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 content.data.Quests + +@Initializable +class FredTheFarmerDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun getIds(): IntArray { + return intArrayOf(758) + } + + override fun open(vararg args: Any): Boolean { + npc = args[0] as 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 } + } + return true + } + + 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!!, Quests.SHEEP_SHEARER) == 0), + Topic(FacialExpression.HALF_GUILTY, "I'm looking for something to kill.", 100), + Topic(FacialExpression.HALF_GUILTY, "I'm lost.", 200) + ) + + 100 -> npc(FacialExpression.HALF_GUILTY, "What, on my land? Leave my livestock alone you", "scoundrel!").also { stage = END_DIALOGUE } + + 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, Quests.SHEEP_SHEARER)), npc) + } + return true + } +} \ No newline at end of file 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/JimmyTheChiselDialogue.kt b/Server/src/main/content/region/misthalin/lumbridge/dialogue/JimmyTheChiselDialogue.kt index eba170e5f..be0088503 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/dialogue/JimmyTheChiselDialogue.kt +++ b/Server/src/main/content/region/misthalin/lumbridge/dialogue/JimmyTheChiselDialogue.kt @@ -13,11 +13,11 @@ import org.rs09.consts.NPCs */ @Initializable -class JimmyTheChiselDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { +class JimmyTheChiselDialogue(player: Player? = null) : DialoguePlugin(player) { override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - npc(core.game.dialogue.FacialExpression.FRIENDLY,"Hello mate!").also { stage = 99 } + npc(FacialExpression.FRIENDLY,"Hello mate!").also { stage = 99 } return true } @@ -28,7 +28,7 @@ class JimmyTheChiselDialogue(player: Player? = null) : core.game.dialogue.Dialog return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return JimmyTheChiselDialogue(player) } 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 73344f2b6..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,21 +13,22 @@ 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) : core.game.dialogue.DialoguePlugin(player) { - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { +class LumbridgeGuideDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player?): DialoguePlugin { return LumbridgeGuideDialogue(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(core.game.dialogue.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++ } + 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++ } 1 -> showTopics( Topic("Where can I find a quest to go on?", 10), Topic("What monsters should I fight?", 20), @@ -48,43 +49,43 @@ class LumbridgeGuideDialogue(player: Player? = null) : core.game.dialogue.Dialog //Where can I find a quest? 10 -> { if (!cooksAssistantComplete) - npcl(core.game.dialogue.FacialExpression.HALF_THINKING, "You can try talking to the Cook in the Lumbridge Castle. I hear he is always looking for some help.") + npcl(FacialExpression.HALF_THINKING, "You can try talking to the Cook in the Lumbridge Castle. I hear he is always looking for some help.") else if (!sheepShearerComplete) - npcl(core.game.dialogue.FacialExpression.HALF_THINKING, "You can try talking to Fred the Farmer north-west of here. I hear he is always looking for some help.") + npcl(FacialExpression.HALF_THINKING, "You can try talking to Fred the Farmer north-west of here. I hear he is always looking for some help.") else - npcl(core.game.dialogue.FacialExpression.FRIENDLY, "You are such an accomplished adventurer already; you should be telling me some good quests to go on.") + npcl(FacialExpression.FRIENDLY, "You are such an accomplished adventurer already; you should be telling me some good quests to go on.") stage = END_DIALOGUE } //What monsters should I fight? 20 -> if (player.properties.currentCombatLevel >= 30) { - npcl(core.game.dialogue.FacialExpression.FRIENDLY, "You're strong enough to work out what monsters to fight for yourself now, but the tutors might help you with any questions you have about the skills; they're just south of the general store.") + npcl(FacialExpression.FRIENDLY, "You're strong enough to work out what monsters to fight for yourself now, but the tutors might help you with any questions you have about the skills; they're just south of the general store.") stage = END_DIALOGUE } else { - npcl(core.game.dialogue.FacialExpression.FRIENDLY, "There are things to kill all over the place! At your level, you might like to try wandering westwards to the Wizards' Tower or north-west to the Barbarian Village.") + npcl(FacialExpression.FRIENDLY, "There are things to kill all over the place! At your level, you might like to try wandering westwards to the Wizards' Tower or north-west to the Barbarian Village.") stage++ } - 21 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Non-player characters usually appear as yellow dots on your mini-map, although there are some that you won't be able to fight, such as myself. Watch out for monsters which are tougher").also { stage++ } - 22 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "than you. A monster's combat level is shown next to their 'Attack' option. If that level is coloured green it means the monster is weaker than you. If it is red, it means the monster is tougher than you.").also { stage++ } - 23 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Remember, you will do better if you have better armour and weapons and it's always worth carrying a bit of food to heal yourself.").also { stage = 1 } + 21 -> npcl(FacialExpression.FRIENDLY, "Non-player characters usually appear as yellow dots on your mini-map, although there are some that you won't be able to fight, such as myself. Watch out for monsters which are tougher").also { stage++ } + 22 -> npcl(FacialExpression.FRIENDLY, "than you. A monster's combat level is shown next to their 'Attack' option. If that level is coloured green it means the monster is weaker than you. If it is red, it means the monster is tougher than you.").also { stage++ } + 23 -> npcl(FacialExpression.FRIENDLY, "Remember, you will do better if you have better armour and weapons and it's always worth carrying a bit of food to heal yourself.").also { stage = 1 } //Where can I make money? - 30 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "There are many ways to make money in the game. I would suggest either killing monsters or doing a trade skill such as Smithing or Fishing.").also { stage++ } - 31 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Please don't try to get money by begging off other players. It will make you unpopular. Nobody likes a beggar. It is very irritating to have other players asking for your hard-earned cash.").also { stage = 1 } + 30 -> npcl(FacialExpression.FRIENDLY, "There are many ways to make money in the game. I would suggest either killing monsters or doing a trade skill such as Smithing or Fishing.").also { stage++ } + 31 -> npcl(FacialExpression.FRIENDLY, "Please don't try to get money by begging off other players. It will make you unpopular. Nobody likes a beggar. It is very irritating to have other players asking for your hard-earned cash.").also { stage = 1 } //I'd like to know more about security - 40 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "I can tell you about password security, avoiding item scamming and in-game moderation. I can also tell you about a place called the Stronghold of Security, where you can learn more about account security and have a").also { stage++ } + 40 -> npcl(FacialExpression.FRIENDLY, "I can tell you about password security, avoiding item scamming and in-game moderation. I can also tell you about a place called the Stronghold of Security, where you can learn more about account security and have a").also { stage++ } 41 -> { player.achievementDiaryManager.finishTask(player, DiaryType.LUMBRIDGE, 0, 17) - npcl(core.game.dialogue.FacialExpression.FRIENDLY, "bit of an adventure at the same time. In fact, why don't you just head there instead? It's a lot more fun, I promise. You can find it down the hole in the middle of Barbarian Village to the north-west.") + npcl(FacialExpression.FRIENDLY, "bit of an adventure at the same time. In fact, why don't you just head there instead? It's a lot more fun, I promise. You can find it down the hole in the middle of Barbarian Village to the north-west.") stage = 1 } //Where can I find a bank? - 50 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "You'll find a bank upstairs in Lumbridge Castle - go right to the top!").also { stage = 1 } + 50 -> npcl(FacialExpression.FRIENDLY, "You'll find a bank upstairs in Lumbridge Castle - go right to the top!").also { stage = 1 } //visit pmod room - 200 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Yes, of course.").also { stage++ } + 200 -> npcl(FacialExpression.FRIENDLY, "Yes, of course.").also { stage++ } 201 -> { end() if (player.isStaff) @@ -92,20 +93,20 @@ class LumbridgeGuideDialogue(player: Player? = null) : core.game.dialogue.Dialog } //deiron - 300 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Of course, but first let me give you a word of warning.").also { stage++ } - 301 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Should you choose to step away from the path of the ironman now, you will not have the option to return.").also { stage++ } - 302 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Now I ask you to make sure, are you sure you want to permanently remove ironman mode?").also { stage++ } + 300 -> npcl(FacialExpression.FRIENDLY, "Of course, but first let me give you a word of warning.").also { stage++ } + 301 -> npcl(FacialExpression.FRIENDLY, "Should you choose to step away from the path of the ironman now, you will not have the option to return.").also { stage++ } + 302 -> npcl(FacialExpression.FRIENDLY, "Now I ask you to make sure, are you sure you want to permanently remove ironman mode?").also { stage++ } 303 -> showTopics( - Topic(core.game.dialogue.FacialExpression.FRIENDLY, "Yes, I'm sure.", 310), - Topic(core.game.dialogue.FacialExpression.FRIENDLY, "No, I've changed my mind.", END_DIALOGUE) + Topic(FacialExpression.FRIENDLY, "Yes, I'm sure.", 310), + Topic(FacialExpression.FRIENDLY, "No, I've changed my mind.", END_DIALOGUE) ) //yes - deiron - 310 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Very well, let me just check one thing...").also { stage++ } + 310 -> npcl(FacialExpression.FRIENDLY, "Very well, let me just check one thing...").also { stage++ } 311 -> if (player.ironmanManager.mode == IronmanMode.HARDCORE) { - npcl(core.game.dialogue.FacialExpression.WORRIED, "Oh, dear, it's just as I feared. You're a hardcore ironman! My apologies, but there's nothing I can do to help.").also { stage = END_DIALOGUE } + npcl(FacialExpression.WORRIED, "Oh, dear, it's just as I feared. You're a hardcore ironman! My apologies, but there's nothing I can do to help.").also { stage = END_DIALOGUE } } else { - npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Oh, wonderful. It appears everything is in order. Sit still for a moment...").also { stage++ } + npcl(FacialExpression.FRIENDLY, "Oh, wonderful. It appears everything is in order. Sit still for a moment...").also { stage++ } } 312 -> { sendGraphics(342, player.location) @@ -114,7 +115,7 @@ class LumbridgeGuideDialogue(player: Player? = null) : core.game.dialogue.Dialog } 313 -> { player.ironmanManager.mode = IronmanMode.NONE - npcl(core.game.dialogue.FacialExpression.HALF_ASKING, "There, I believe it is done. You should no longer be restricted from the wider world.") + npcl(FacialExpression.HALF_ASKING, "There, I believe it is done. You should no longer be restricted from the wider world.") stage = END_DIALOGUE } } diff --git a/Server/src/main/content/region/misthalin/lumbridge/dialogue/LumbridgeShopKeeperDialogue.java b/Server/src/main/content/region/misthalin/lumbridge/dialogue/LumbridgeShopKeeperDialogue.java new file mode 100644 index 000000000..6bdc6282f --- /dev/null +++ b/Server/src/main/content/region/misthalin/lumbridge/dialogue/LumbridgeShopKeeperDialogue.java @@ -0,0 +1,78 @@ +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.plugin.Initializable; +import core.game.node.entity.player.Player; + +/** + * Represents the lumbridge shop keeper dialogue. + */ +@Initializable +public final class LumbridgeShopKeeperDialogue extends DialoguePlugin { + + /** + * Constructs a new {@code LumbridgeShopKeeperDialogue} {@code Object}. + */ + public LumbridgeShopKeeperDialogue() { + /** + * empty. + */ + } + + /** + * Constructs a new {@code LumbridgeShopKeeperDialogue} {@code Object}. + * @param player the player. + */ + public LumbridgeShopKeeperDialogue(Player player) { + super(player); + } + + @Override + public DialoguePlugin newInstance(Player player) { + return new LumbridgeShopKeeperDialogue(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; + } + + @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 = 20; + break; + case 3: + end(); + break; + } + break; + case 20: + end(); + break; + } + return true; + } + + @Override + public int[] getIds() { + return new int[] { 520, 521 }; + } +} diff --git a/Server/src/main/content/region/misthalin/lumbridge/dialogue/PrayerTutorDialogue.java b/Server/src/main/content/region/misthalin/lumbridge/dialogue/PrayerTutorDialogue.java index 4ccdfc95d..687a6b889 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/dialogue/PrayerTutorDialogue.java +++ b/Server/src/main/content/region/misthalin/lumbridge/dialogue/PrayerTutorDialogue.java @@ -41,33 +41,33 @@ public class PrayerTutorDialogue extends DialoguePlugin { case 0: switch (buttonId) { case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I already know about the basic prayers, got any tips?"); + interpreter.sendDialogues(player, FacialExpression.ASKING, "I already know about the basic prayers, got any tips?"); stage = 10; break; case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Tell me about different bones."); + interpreter.sendDialogues(player, FacialExpression.HALF_ASKING, "Tell me about different bones."); stage = 20; break; case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Goodbye."); + interpreter.sendDialogues(player, FacialExpression.FRIENDLY, "Goodbye."); stage = 30; break; } break; case 10: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "For you " + player.getUsername() + "? Always. There are many", "advantages to using the protection prayers when", "fighting the more dangerous foes. You can protect", "yourself from magic, melee or ranged attacks with these"); + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "For you " + player.getUsername() + "? Always. There are many", "advantages to using the protection prayers when", "fighting the more dangerous foes. You can protect", "yourself from magic, melee or ranged attacks with these"); stage = 11; break; case 11: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "useful prayers."); + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "useful prayers."); stage = 12; break; case 12: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "A good prayer to have when venturing into the", "wilderness is protect item. This will protect one of your", "items if you should die there."); + interpreter.sendDialogues(npc, FacialExpression.FRIENDLY, "A good prayer to have when venturing into the", "wilderness is protect item. This will protect one of your", "items if you should die there."); stage = 13; break; case 13: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Remember though that venturing into the wilderness is", "a risky business, store your items in a bank before you", "go there so that you don't lose them."); + interpreter.sendDialogues(npc, FacialExpression.FRIENDLY, "Remember though that venturing into the wilderness is", "a risky business, store your items in a bank before you", "go there so that you don't lose them."); stage = 14; break; case 14: @@ -93,7 +93,7 @@ public class PrayerTutorDialogue extends DialoguePlugin { stage = 20; break; case 4: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Goodbye."); + interpreter.sendDialogues(player, FacialExpression.FRIENDLY, "Goodbye."); stage = 40; break; } 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 a7b16274c..5a8ca76db 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/diary/LumbridgeAchivementDiary.kt +++ b/Server/src/main/content/region/misthalin/lumbridge/diary/LumbridgeAchivementDiary.kt @@ -3,7 +3,7 @@ package content.region.misthalin.lumbridge.diary import content.global.skill.magic.TeleportMethod import content.global.skill.magic.spellconsts.Modern import core.api.inBorders -import core.api.questStage +import core.api.getQuestStage import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player import core.game.node.entity.player.link.diary.DiaryType @@ -14,10 +14,12 @@ import org.rs09.consts.NPCs import org.rs09.consts.Scenery import content.region.misthalin.lumbridge.dialogue.DukeHoracioDialogue import content.region.misthalin.varrock.quest.dragonslayer.DukeHoracioDSDialogue +import core.api.inInventory 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) { @@ -309,6 +311,14 @@ class LumbridgeAchivementDiary : DiaryEventHookBase(DiaryType.LUMBRIDGE) { EasyTasks.WIZARDS_TOWER_TAUNT_DEMON ) } + + 12595 -> if (event.target.id == Scenery.FLOUR_BIN_36878 && event.option == "empty" && inInventory(player, Items.EMPTY_POT_1931)) { + finishTask( + player, + DiaryLevel.BEGINNER, + BeginnerTasks.WINDMILL_MAKE_FLOUR + ) + } } } @@ -337,7 +347,7 @@ class LumbridgeAchivementDiary : DiaryEventHookBase(DiaryType.LUMBRIDGE) { override fun onDialogueOptionSelected(player: Player, event: DialogueOptionSelectionEvent) { when (event.dialogue) { is DukeHoracioDSDialogue -> { - val dragonSlayerStage = questStage(player, "Dragon Slayer") + val dragonSlayerStage = getQuestStage(player, Quests.DRAGON_SLAYER) if ((dragonSlayerStage == 100 && event.currentStage == 4) || event.currentStage == 12) { @@ -413,4 +423,16 @@ class LumbridgeAchivementDiary : DiaryEventHookBase(DiaryType.LUMBRIDGE) { } } } + + override fun onUsedWith(player: Player, event: UseWithEvent) { + when (player.viewport.region.id) { + 12595 -> if (event.used == Items.EMPTY_POT_1931 && event.with == Scenery.FLOUR_BIN_36878) { + finishTask( + player, + DiaryLevel.BEGINNER, + BeginnerTasks.WINDMILL_MAKE_FLOUR + ) + } + } + } } \ No newline at end of file diff --git a/Server/src/main/content/region/misthalin/lumbridge/diary/NedDiaryDialogue.kt b/Server/src/main/content/region/misthalin/lumbridge/diary/NedDiaryDialogue.kt index 18819c749..5e52e236c 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/diary/NedDiaryDialogue.kt +++ b/Server/src/main/content/region/misthalin/lumbridge/diary/NedDiaryDialogue.kt @@ -47,9 +47,9 @@ class NedDiaryDialogue : DialogueFile() { } 10 -> { npc( - "Ah, well it's a diary that helps you keep track of", + "Ah, well, it's a diary that helps you keep track of", "particular achievements you've made in the world of", - "2009Scape. In Lumbridge and Draynor i can help you", + "Gielinor. In Lumbridge and Draynor I can help you", "discover some very useful things indeed." ) stage++ @@ -115,10 +115,10 @@ class NedDiaryDialogue : DialogueFile() { stage = 0 } 60 -> { - AchievementDiary.grantReplacement(player, DiaryType.LUMBRIDGE, level); + AchievementDiary.grantReplacement(player, DiaryType.LUMBRIDGE, level) npc("You better be more careful this time.") stage = 0 } } } -} \ No newline at end of file +} diff --git a/Server/src/main/content/region/misthalin/lumbridge/handlers/ChurchSignListener.kt b/Server/src/main/content/region/misthalin/lumbridge/handlers/ChurchSignListener.kt index 111e757dd..ed6d21643 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/handlers/ChurchSignListener.kt +++ b/Server/src/main/content/region/misthalin/lumbridge/handlers/ChurchSignListener.kt @@ -3,6 +3,7 @@ package content.region.misthalin.lumbridge.handlers import core.api.* import org.rs09.consts.Scenery import core.GlobalStats +import core.ServerConstants import core.game.interaction.InteractionListener import core.game.interaction.IntType @@ -13,10 +14,11 @@ class ChurchSignListener : InteractionListener { override fun defineListeners() { on(CHURCH_SIGN, IntType.SCENERY, "read"){ player, _ -> val deaths = GlobalStats.getDailyDeaths() + val servername = ServerConstants.SERVER_NAME if(deaths > 0) { - sendDialogue(player, "So far today $deaths unlucky adventurers have died on RuneScape and been sent to their respawn location. Be careful out there.") + sendDialogue(player, "So far today $deaths unlucky adventurers have died on $servername and been sent to their respawn location. Be careful out there.") } else { - sendDialogue(player, "So far today not a single adventurer on RuneScape has met their end grisly or otherwise. Either the streets are getting safer or adventurers are getting warier.") + sendDialogue(player, "So far today not a single adventurer on $servername has met their end grisly or otherwise. Either the streets are getting safer or adventurers are getting warier.") } return@on true } diff --git a/Server/src/main/content/region/misthalin/lumbridge/handlers/CulinomancerShop.kt b/Server/src/main/content/region/misthalin/lumbridge/handlers/CulinomancerShop.kt index b867ca67d..727743512 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/handlers/CulinomancerShop.kt +++ b/Server/src/main/content/region/misthalin/lumbridge/handlers/CulinomancerShop.kt @@ -1,8 +1,6 @@ package content.region.misthalin.lumbridge.handlers -import core.api.LoginListener -import core.api.getAttribute -import core.api.setAttribute +import core.api.* import core.game.node.entity.player.Player import core.game.node.item.Item import core.game.system.task.Pulse @@ -16,9 +14,9 @@ import kotlin.collections.HashMap class CulinomancerShop : LoginListener { //Enable the chest if the player has 18 quest points or more override fun login(player: Player) { - if(player.questRepository.points >= 18){ - player.varpManager.get(678).setVarbit(0, 5).send(player) - setAttribute(player, "culino-tier", player.questRepository.points / 18) //Set this, so we can check if the player has gained a tier during server runtime + if(player.questRepository.points >= 10){ + setVarbit(player, 1850, 5) + setAttribute(player, "culino-tier", player.questRepository.points / 10) //Set this, so we can check if the player has gained a tier during server runtime //Restock pulse for this player (yes, this means the chest will only restock if the player has logged in. Shop system needs work in order to do otherwise.) val restockPulse = object : Pulse(100){ //Run once a minute @@ -51,7 +49,7 @@ class CulinomancerShop : LoginListener { fun getShop(player: Player, food: Boolean): Shop { val uid = player.details.uid val points = player.questRepository.points - val tier = (points / 18) + val tier = (points / 10) if (tier != getAttribute(player, "culino-tier", 0)) //If player tier has changed { foodShops.remove(uid) //Clear the previous shops, so they can regenerate with the new tier @@ -71,7 +69,7 @@ class CulinomancerShop : LoginListener { //Generate default food stock based on an amount of total QP. private fun generateFoodStock(points: Int): Array { val stock = Array(foodStock.size) { ShopItem(0, 0) } - val maxQty = when (val qpTier = (points / 18) - 1) { + val maxQty = when (val qpTier = (points / 10) - 1) { 0, 1, 2, 3, 4 -> 1 + qpTier else -> qpTier + (qpTier + (qpTier - 5)) //5 = 10, 6 = 13, 7 = 15, etc } @@ -85,13 +83,14 @@ class CulinomancerShop : LoginListener { //Generate default gear stock based on an amount of total QP. private fun generateGearStock(points: Int): Array { val stock = Array(gearStock.size) { ShopItem(0, 0) } - val qpTier = (points / 18) + val qpTier = (points / 10) for ((index, item) in stock.withIndex()) item.itemId = gearStock[index] for (i in 0 until min(qpTier, 10)) { stock[i].amount = 30 stock[i + 10].amount = 5 } + //stock[9].amount = 1 return stock } diff --git a/Server/src/main/content/region/misthalin/lumbridge/handlers/GameInstructorPlugin.java b/Server/src/main/content/region/misthalin/lumbridge/handlers/GameInstructorPlugin.java index cae3db799..2bd8a13d0 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/handlers/GameInstructorPlugin.java +++ b/Server/src/main/content/region/misthalin/lumbridge/handlers/GameInstructorPlugin.java @@ -9,7 +9,7 @@ import core.plugin.Initializable; import core.plugin.Plugin; /** - * Represents the 2009scape instructor plugin. + * Represents the 2009Scape instructor plugin. * @author 'Vexia * @date 20.11.2013 */ diff --git a/Server/src/main/content/region/misthalin/lumbridge/handlers/HamHideoutPlugin.java b/Server/src/main/content/region/misthalin/lumbridge/handlers/HamHideoutPlugin.java index 41ec22e8c..f0b482a63 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/handlers/HamHideoutPlugin.java +++ b/Server/src/main/content/region/misthalin/lumbridge/handlers/HamHideoutPlugin.java @@ -14,6 +14,9 @@ import core.plugin.Plugin; import core.plugin.Initializable; import core.tools.RandomFunction; +import static core.api.ContentAPIKt.*; + + /** * Represents the ham hide out node interaction plugin. * @author 'Vexia @@ -52,22 +55,22 @@ public final class HamHideoutPlugin extends OptionHandler { case 5491: switch (option) { case "open": - if (player.getConfigManager().get(174) == 0) { + if (getVarp(player, 174) == 0) { player.getPacketDispatch().sendMessage("This trapdoor seems totally locked."); } else { - player.getConfigManager().set(346, 272731282); + setVarp(player, 346, 272731282); ClimbActionHandler.climb(player, new Animation(827), new Location(3149, 9652, 0)); GameWorld.getPulser().submit(new Pulse(2, player) { @Override public boolean pulse() { - player.getConfigManager().set(174, 0); + setVarp(player, 174, 0); return true; } }); } break; case "close": - player.getConfigManager().set(174, 0); + setVarp(player, 174, 0); break; case "climb-down": switch (id) { @@ -89,11 +92,11 @@ public final class HamHideoutPlugin extends OptionHandler { player.getPacketDispatch().sendMessage(success ? ("You pick the lock on the trap door.") : "You fail to pick the lock - your fingers get numb from fumbling with the lock."); player.unlock(); if (success) { - player.getConfigManager().set(174, 1 << 14); + setVarp(player, 174, 1 << 14); GameWorld.getPulser().submit(new Pulse(40, player) { @Override public boolean pulse() { - player.getConfigManager().set(174, 0); + setVarp(player, 174, 0); return true; } }); 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 ec7831b24..7a810e667 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/handlers/LumbridgeBasementPlugin.java +++ b/Server/src/main/content/region/misthalin/lumbridge/handlers/LumbridgeBasementPlugin.java @@ -21,6 +21,9 @@ import core.plugin.ClassScanner; import core.plugin.Initializable; import core.tools.RandomFunction; +import static core.api.ContentAPIKt.hasRequirement; +import content.data.Quests; + /** * Handles the lumbridge basement. * @author Vexia @@ -53,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; } @@ -168,90 +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(); - 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/OrganCutScene.java b/Server/src/main/content/region/misthalin/lumbridge/handlers/OrganCutScene.java index 2662cd751..26ab44f6b 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/handlers/OrganCutScene.java +++ b/Server/src/main/content/region/misthalin/lumbridge/handlers/OrganCutScene.java @@ -18,6 +18,8 @@ import core.net.packet.context.CameraContext.CameraType; import core.plugin.Initializable; import core.net.packet.out.CameraViewPacket; +import static core.api.ContentAPIKt.animateScenery; + /** * Represents the organ cutscene. * @author 'Vexia @@ -53,7 +55,7 @@ public final class OrganCutScene extends CutscenePlugin { GameWorld.getPulser().submit(new Pulse(3) { @Override public boolean pulse() { - player.getPacketDispatch().sendSceneryAnimation(RegionManager.getObject(base.transform(42, 14, 0)), new Animation(9841)); + animateScenery(player, newOrgin, 9982, false); // Not animation 9841 PacketRepository.send(CameraViewPacket.class, new CameraContext(player, CameraType.POSITION, player.getLocation().getX() + 2, player.getLocation().getY() - 3, 400, 1, 1)); PacketRepository.send(CameraViewPacket.class, new CameraContext(player, CameraType.ROTATION, player.getLocation().getX() + 1, player.getLocation().getY(), 400, 1, 1)); 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 new file mode 100644 index 000000000..2b5a9003d --- /dev/null +++ b/Server/src/main/content/region/misthalin/lumbridge/handlers/TormentedDemonArea.kt @@ -0,0 +1,10 @@ +package content.region.misthalin.lumbridge + +import core.api.* +import core.game.world.map.zone.ZoneBorders +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, ZoneRestriction.RANDOM_EVENTS) } +} diff --git a/Server/src/main/content/region/misthalin/lumbridge/handlers/TormentedDemonNPC.java b/Server/src/main/content/region/misthalin/lumbridge/handlers/TormentedDemonNPC.java index 5702373c2..87f5de168 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/handlers/TormentedDemonNPC.java +++ b/Server/src/main/content/region/misthalin/lumbridge/handlers/TormentedDemonNPC.java @@ -203,7 +203,7 @@ public class TormentedDemonNPC extends AbstractNPC { * @param protectionStyle The protection style to switch to. */ public void transformDemon(CombatStyle attackStyle, CombatStyle protectionStyle) { - //System.out.println("Transforming demon, selected combat style = " + style + ", the selected protection style = " + protection); + // If either attackStyle or protectionStyle are null, use the current form's values if(attackStyle == null) { @@ -216,7 +216,7 @@ public class TormentedDemonNPC extends AbstractNPC { int oldHp = getSkills().getLifepoints(); transform(id); getSkills().setLifepoints(oldHp); - //System.out.println("The outcome demon, id = " + id + " combat style = " + getProperties().getCombatPulse().getStyle() + ", the protection style = " + getProperties().getProtectStyle()); + TD_SWING_HANDLER.style = getProperties().getCombatPulse().getStyle(); } 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/GillieGroatsDialogue.kt b/Server/src/main/content/region/misthalin/lumbridge/quest/cooksassistant/GillieGroatsDialogue.kt index 6de7e8101..a951bcaba 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/quest/cooksassistant/GillieGroatsDialogue.kt +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/cooksassistant/GillieGroatsDialogue.kt @@ -11,17 +11,17 @@ import core.plugin.Initializable */ @Initializable -class GillieGroatsDialogue (player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class GillieGroatsDialogue (player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { var milk = false if (args.size == 2) milk = true if (milk) { //If the player attempts to milk a dairy cow without a bucket - npc(core.game.dialogue.FacialExpression.LAUGH, "Tee hee! You've never milked a cow before, have you?") + npc(FacialExpression.LAUGH, "Tee hee! You've never milked a cow before, have you?") stage = 100 return true } - npc(core.game.dialogue.FacialExpression.HAPPY, "Hello, I'm Gillie the Milkmaid. What can I do for you?") + npc(FacialExpression.HAPPY, "Hello, I'm Gillie the Milkmaid. What can I do for you?") stage = 0 return true } @@ -32,30 +32,30 @@ class GillieGroatsDialogue (player: Player? = null) : core.game.dialogue.Dialogu //If the player talks to Gillie directly 0 -> options("Who are you?", "Can you tell me how to milk a cow?", "I'm fine, thanks.").also { stage++ } 1 -> when(buttonId) { - 1 -> npc(core.game.dialogue.FacialExpression.HAPPY, "My name's Gillie Groats. My father is a farmer and I", "milk the cows for him.").also { stage = 10 } - 2 -> player(core.game.dialogue.FacialExpression.ASKING, "So how do you get milk from a cow then?").also { stage = 20 } - 3 -> player(core.game.dialogue.FacialExpression.NEUTRAL, "I'm fine, thanks.").also { stage = 1000 } + 1 -> npc(FacialExpression.HAPPY, "My name's Gillie Groats. My father is a farmer and I", "milk the cows for him.").also { stage = 10 } + 2 -> player(FacialExpression.ASKING, "So how do you get milk from a cow then?").also { stage = 20 } + 3 -> player(FacialExpression.NEUTRAL, "I'm fine, thanks.").also { stage = 1000 } } //Who are you? - 10 -> player(core.game.dialogue.FacialExpression.ASKING, "Do you have any buckets of milk spare?").also { stage++ } - 11 -> npc(core.game.dialogue.FacialExpression.SUSPICIOUS, "I'm afraid not. We need all of our milk to sell to", "market, but you can milk the cow yourself if you need", "milk.").also { stage++ } - 12 -> player(core.game.dialogue.FacialExpression.HAPPY,"Thanks.").also { stage = 1000 } + 10 -> player(FacialExpression.ASKING, "Do you have any buckets of milk spare?").also { stage++ } + 11 -> npc(FacialExpression.SUSPICIOUS, "I'm afraid not. We need all of our milk to sell to", "market, but you can milk the cow yourself if you need", "milk.").also { stage++ } + 12 -> player(FacialExpression.HAPPY,"Thanks.").also { stage = 1000 } //Can you tell me how to milk a cow? - 20 -> npc(core.game.dialogue.FacialExpression.FRIENDLY, "It's very easy. First you need an empty bucket to hold", "the milk.").also { stage++ } - 21 -> npc(core.game.dialogue.FacialExpression.FRIENDLY, "Then find a dairy cow to milk - you can't milk just", "any cow.").also { stage++ } - 22 -> player(core.game.dialogue.FacialExpression.ASKING, "How do I find a dairy cow?").also { stage++ } - 23 -> npc(core.game.dialogue.FacialExpression.FRIENDLY, "They are easy to spot - they are dark brown and", "white, unlike beef cows, which are light brown and white.", "We also tether them to a post to stop them wandering", "around all over the place.").also {stage++ } - 24 -> npc(core.game.dialogue.FacialExpression.FRIENDLY, "There are a couple very near, in this field.").also { stage++ } - 25 -> npc(core.game.dialogue.FacialExpression.HAPPY, "Then just milk the cow and your bucket will fill with", "tasty, nutritious milk.").also { stage = 1000 } + 20 -> npc(FacialExpression.FRIENDLY, "It's very easy. First you need an empty bucket to hold", "the milk.").also { stage++ } + 21 -> npc(FacialExpression.FRIENDLY, "Then find a dairy cow to milk - you can't milk just", "any cow.").also { stage++ } + 22 -> player(FacialExpression.ASKING, "How do I find a dairy cow?").also { stage++ } + 23 -> npc(FacialExpression.FRIENDLY, "They are easy to spot - they are dark brown and", "white, unlike beef cows, which are light brown and white.", "We also tether them to a post to stop them wandering", "around all over the place.").also {stage++ } + 24 -> npc(FacialExpression.FRIENDLY, "There are a couple very near, in this field.").also { stage++ } + 25 -> npc(FacialExpression.HAPPY, "Then just milk the cow and your bucket will fill with", "tasty, nutritious milk.").also { stage = 1000 } //Continuation of attempting to milk a dairy cow without a bucket - 100 -> player(core.game.dialogue.FacialExpression.ASKING, "Erm... No. How could you tell?").also { stage++ } - 101 -> npc(core.game.dialogue.FacialExpression.LAUGH, "Because you're spilling milk all over the floor. What a","waste ! You need something to hold the milk.").also { stage++ } - 102 -> player(core.game.dialogue.FacialExpression.NEUTRAL, "Ah yes, I really should have guessed that one, shouldn't", "I?").also { stage++ } - 103 -> npc(core.game.dialogue.FacialExpression.LAUGH, "You're from the city, aren't you... Try it again with an","empty bucket.").also{ stage++ } - 104 -> player(core.game.dialogue.FacialExpression.NEUTRAL,"Right, I'll do that.").also { stage = 1000 } + 100 -> player(FacialExpression.ASKING, "Erm... No. How could you tell?").also { stage++ } + 101 -> npc(FacialExpression.LAUGH, "Because you're spilling milk all over the floor. What a","waste ! You need something to hold the milk.").also { stage++ } + 102 -> player(FacialExpression.NEUTRAL, "Ah yes, I really should have guessed that one, shouldn't", "I?").also { stage++ } + 103 -> npc(FacialExpression.LAUGH, "You're from the city, aren't you... Try it again with an","empty bucket.").also{ stage++ } + 104 -> player(FacialExpression.NEUTRAL,"Right, I'll do that.").also { stage = 1000 } //Dialogue Endpoint 1000 -> end() @@ -63,7 +63,7 @@ class GillieGroatsDialogue (player: Player? = null) : core.game.dialogue.Dialogu return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return GillieGroatsDialogue(player) } 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 a6fa452b8..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. @@ -13,7 +14,7 @@ import core.plugin.Initializable */ @Initializable -class LumbridgeCookDialogue (player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class LumbridgeCookDialogue (player: Player? = null) : DialoguePlugin(player){ //Item declaration val EMPTY_BUCKET = 1925 @@ -29,42 +30,42 @@ class LumbridgeCookDialogue (player: Player? = null) : core.game.dialogue.Dialog 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 - npc(core.game.dialogue.FacialExpression.SAD, "What am I to do?") + 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(core.game.dialogue.FacialExpression.HAPPY, "You've brought me everything I need! I am saved!", "Thank you!") + npc(FacialExpression.HAPPY, "You've brought me everything I need! I am saved!", "Thank you!") stage = 200 return true } else { //If the player has not handed all the items to the chef - npc(core.game.dialogue.FacialExpression.SAD, "How are you getting on with finding the ingredients?") + npc(FacialExpression.SAD, "How are you getting on with finding the ingredients?") gave = false //Resetting to false here to prevent dialogue skipping later stage = 100 return true } } //After completing Cook's Assistant - npc(core.game.dialogue.FacialExpression.HAPPY, "Hello friend, how is the adventuring going?") + npc(FacialExpression.HAPPY, "Hello friend, how is the adventuring going?") stage = 300 return true } 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.name}! 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 @@ -72,105 +73,105 @@ class LumbridgeCookDialogue (player: Player? = null) : core.game.dialogue.Dialog when (stage) { 0 -> options("What's wrong?", "Can you make me a cake?", "You don't look very happy.", "Nice hat!").also { stage++ } 1 -> when(buttonId) { - 1 -> player(core.game.dialogue.FacialExpression.NEUTRAL, "What's wrong?").also { stage = 10 } - 2 -> player(core.game.dialogue.FacialExpression.ASKING, "You're a cook, why don't you bake me a cake?").also { stage = 20 } - 3 -> player(core.game.dialogue.FacialExpression.NEUTRAL,"You don't look very happy.").also { stage = 30; } - 4 -> player(core.game.dialogue.FacialExpression.HAPPY, "Nice hat!").also { stage = 40 } + 1 -> player(FacialExpression.NEUTRAL, "What's wrong?").also { stage = 10 } + 2 -> player(FacialExpression.ASKING, "You're a cook, why don't you bake me a cake?").also { stage = 20 } + 3 -> player(FacialExpression.NEUTRAL,"You don't look very happy.").also { stage = 30; } + 4 -> player(FacialExpression.HAPPY, "Nice hat!").also { stage = 40 } } //What's wrong? - 10 -> npc(core.game.dialogue.FacialExpression.SCARED, "Oh dear, oh dear, oh dear, I'm in a terrible terrible", "mess! It's the Duke's birthday today, and I should be", "making him a lovely big birthday cake.").also { stage++ } - 11 -> npc(core.game.dialogue.FacialExpression.SAD, "I've forgotten to buy the ingredients. I'll never get", "them in time now. He'll sack me! What will I do? I have", "four children and a goat to look after. Would you help", "me? Please?").also { stage++ } + 10 -> npc(FacialExpression.SCARED, "Oh dear, oh dear, oh dear, I'm in a terrible terrible", "mess! It's the Duke's birthday today, and I should be", "making him a lovely big birthday cake.").also { stage++ } + 11 -> npc(FacialExpression.SAD, "I've forgotten to buy the ingredients. I'll never get", "them in time now. He'll sack me! What will I do? I have", "four children and a goat to look after. Would you help", "me? Please?").also { stage++ } 12 -> options("I'm always happy to help a cook in distress.", "I can't right now, Maybe later.").also { stage++ } 13 -> when(buttonId) { - 1 -> player(core.game.dialogue.FacialExpression.HAPPY, "Yes, I'll help you.").also { stage = 50 } - 2 -> player(core.game.dialogue.FacialExpression.ANNOYED, "No, I don't feel like it. Maybe later.").also { stage++ } + 1 -> player(FacialExpression.HAPPY, "Yes, I'll help you.").also { stage = 50 } + 2 -> player(FacialExpression.ANNOYED, "No, I don't feel like it. Maybe later.").also { stage++ } } //I can't right now - 14 -> npc(core.game.dialogue.FacialExpression.SAD, "Fine. I always knew you Adventurer types were callous", "beasts. Go on your merry way!").also { stage = 1000 } + 14 -> npc(FacialExpression.SAD, "Fine. I always knew you Adventurer types were callous", "beasts. Go on your merry way!").also { stage = 1000 } //Can you make me a cake? - 20 -> npc(core.game.dialogue.FacialExpression.SAD, "*sniff* Don't talk to me about cakes...").also { stage++ } - 21 -> player(core.game.dialogue.FacialExpression.NEUTRAL, "What's wrong?").also { stage = 10} + 20 -> npc(FacialExpression.SAD, "*sniff* Don't talk to me about cakes...").also { stage++ } + 21 -> player(FacialExpression.NEUTRAL, "What's wrong?").also { stage = 10} //You don't look very happy - 30 -> npc(core.game.dialogue.FacialExpression.SAD, "No, I'm not. The world is caving in around me - I am", "overcome by dark feelings of impending doom.").also { stage++ } + 30 -> npc(FacialExpression.SAD, "No, I'm not. The world is caving in around me - I am", "overcome by dark feelings of impending doom.").also { stage++ } 31 -> options("What's wrong?","I'd take the rest of the day off if I were you.").also { stage++ } 32 -> when(buttonId) { - 1 -> player(core.game.dialogue.FacialExpression.NEUTRAL, "What's wrong?").also { stage = 10} - 2 -> player(core.game.dialogue.FacialExpression.NEUTRAL,"I'd take the rest of the day off if I were you.").also { stage++ } + 1 -> player(FacialExpression.NEUTRAL, "What's wrong?").also { stage = 10} + 2 -> player(FacialExpression.NEUTRAL,"I'd take the rest of the day off if I were you.").also { stage++ } } - 33 -> npc(core.game.dialogue.FacialExpression.SAD,"No, that's the worst thing I could do. I'd get in terrible","trouble.").also { stage++ } - 34 -> player(core.game.dialogue.FacialExpression.ASKING,"Well maybe you need to take a holiday...").also { stage++ } - 35 -> npc(core.game.dialogue.FacialExpression.SAD,"That would be nice, but the Duke doesn't allow holidays","for core staff").also { stage++ } - 36 -> player(core.game.dialogue.FacialExpression.SUSPICIOUS,"Hmm, why not run away to the sea and start a new","life as a Pirate?").also { stage++ } - 37 -> npc(core.game.dialogue.FacialExpression.SAD,"My wife gets sea sick, and I have an irrational fear of","eyepatches. I don't see it working myself.").also { stage++ } - 38 -> player(core.game.dialogue.FacialExpression.NEUTRAL,"I'm afraid I've run out of ideas.").also { stage++ } - 39 -> npc(core.game.dialogue.FacialExpression.SAD,"I know I'm doomed.").also { stage = 21 } + 33 -> npc(FacialExpression.SAD,"No, that's the worst thing I could do. I'd get in terrible","trouble.").also { stage++ } + 34 -> player(FacialExpression.ASKING,"Well maybe you need to take a holiday...").also { stage++ } + 35 -> npc(FacialExpression.SAD,"That would be nice, but the Duke doesn't allow holidays","for core staff").also { stage++ } + 36 -> player(FacialExpression.SUSPICIOUS,"Hmm, why not run away to the sea and start a new","life as a Pirate?").also { stage++ } + 37 -> npc(FacialExpression.SAD,"My wife gets sea sick, and I have an irrational fear of","eyepatches. I don't see it working myself.").also { stage++ } + 38 -> player(FacialExpression.NEUTRAL,"I'm afraid I've run out of ideas.").also { stage++ } + 39 -> npc(FacialExpression.SAD,"I know I'm doomed.").also { stage = 21 } //Nice hat! - 40 -> npc(core.game.dialogue.FacialExpression.SAD, "Er, thank you. It's a pretty ordinary cook's hat, really.").also { stage++ } - 41 -> player(core.game.dialogue.FacialExpression.HAPPY, "Still, it suits you. The trousers are pretty special too.").also { stage++ } - 42 -> npc(core.game.dialogue.FacialExpression.SAD, "It's all standard-issue cook's uniform.").also { stage++ } - 43 -> player(core.game.dialogue.FacialExpression.HAPPY, "The whole hat, apron, stripy trousers ensemble. It", "works. It makes you looks like a real cook.").also { stage++ } - 44 -> npc(core.game.dialogue.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 } + 40 -> npc(FacialExpression.SAD, "Er, thank you. It's a pretty ordinary cook's hat, really.").also { stage++ } + 41 -> player(FacialExpression.HAPPY, "Still, it suits you. The trousers are pretty special too.").also { stage++ } + 42 -> npc(FacialExpression.SAD, "It's all standard-issue cook's uniform.").also { stage++ } + 43 -> player(FacialExpression.HAPPY, "The whole hat, apron, stripy trousers ensemble. It", "works. It makes you looks like a real cook.").also { stage++ } + 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(core.game.dialogue.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++ } - 51 -> player(core.game.dialogue.FacialExpression.NEUTRAL, "So where do I find these ingredients then?").also { stage = 60 } + 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? 60 -> options("Where do I find some flour?","How about milk?","And eggs? Where are they found?","Actually, I know where to find this stuff.").also { stage++ } 61 -> when(buttonId) { - 1 -> npc(core.game.dialogue.FacialExpression.NEUTRAL,"There is a Mill fairly close, go North and then West.","Mill Lane Mill is just off the road to Draynor. I","usually get my flour from there.").also {stage = 70 } - 2 -> npc(core.game.dialogue.FacialExpression.NEUTRAL,"There is a cattle field on the other side of the river,","just across the road from the Groats' Farm.").also { stage = 71 } - 3 -> npc(core.game.dialogue.FacialExpression.NEUTRAL,"I normally get my eggs from the Groats' farm, on the","other side of the river.").also { stage = 73 } - 4 -> player(core.game.dialogue.FacialExpression.NEUTRAL,"Actually, I know where to find this stuff.").also { stage = 1000 } + 1 -> npc(FacialExpression.NEUTRAL,"There is a Mill fairly close, go North and then West.","Mill Lane Mill is just off the road to Draynor. I","usually get my flour from there.").also {stage = 70 } + 2 -> npc(FacialExpression.NEUTRAL,"There is a cattle field on the other side of the river,","just across the road from the Groats' Farm.").also { stage = 71 } + 3 -> npc(FacialExpression.NEUTRAL,"I normally get my eggs from the Groats' farm, on the","other side of the river.").also { stage = 73 } + 4 -> player(FacialExpression.NEUTRAL,"Actually, I know where to find this stuff.").also { stage = 1000 } } //Where do I find some flour? - 70 -> npc(core.game.dialogue.FacialExpression.SUSPICIOUS,"Talk to Millie, she'll help, she's a lovely girl and a fine","Miler. Make sure you take a pot with you for the flour","though, " + if (player.inventory.contains(EMPTY_POT, 1)) "you've got one on you already." else "there should be one on the table in here.").also { stage = 80 } + 70 -> npc(FacialExpression.SUSPICIOUS,"Talk to Millie, she'll help, she's a lovely girl and a fine","Miler. Make sure you take a pot with you for the flour","though, " + if (player.inventory.contains(EMPTY_POT, 1)) "you've got one on you already." else "there should be one on the table in here.").also { stage = 80 } //How about milk? - 71 -> npc(core.game.dialogue.FacialExpression.SUSPICIOUS,"Talk to Gillie Groats, she looks after the Dairy cows -","she'll tell you everything you need to know about","milking cows!").also { stage++ } + 71 -> npc(FacialExpression.SUSPICIOUS,"Talk to Gillie Groats, she looks after the Dairy cows -","she'll tell you everything you need to know about","milking cows!").also { stage++ } 72 -> if (player.inventory.contains(EMPTY_BUCKET , 1)) { - npc(core.game.dialogue.FacialExpression.NEUTRAL,"You'll need an empty bucket for the milk itself. I do see", "you've got a bucket with you already luckily!").also { stage = 80 } + npc(FacialExpression.NEUTRAL,"You'll need an empty bucket for the milk itself. I do see", "you've got a bucket with you already luckily!").also { stage = 80 } } else { - npc(core.game.dialogue.FacialExpression.NEUTRAL,"You'll need an empty bucket for the milk itself. The", "general store just north of the castle will sell you one", "for a couple of coins.").also { stage = 80 } + npc(FacialExpression.NEUTRAL,"You'll need an empty bucket for the milk itself. The", "general store just north of the castle will sell you one", "for a couple of coins.").also { stage = 80 } } //And Eggs? - 73 -> npc(core.game.dialogue.FacialExpression.NEUTRAL,"But any chicken should lay eggs.").also { stage = 80 } + 73 -> npc(FacialExpression.NEUTRAL,"But any chicken should lay eggs.").also { stage = 80 } //Alternative menu for "Where do I find these ingredients?" 80 -> options("Where do I find some flour?","How about milk?","And eggs? Where are they found?","I've got all the information I need. Thanks.").also { stage++ } 81 -> when(buttonId) { - 1 -> npc(core.game.dialogue.FacialExpression.NEUTRAL,"There is a Mill fairly close, go North and then West.","Mill Lane Mill is just off the road to Draynor. I","usually get my flour from there.").also {stage = 70 } - 2 -> npc(core.game.dialogue.FacialExpression.NEUTRAL,"There is a cattle field on the other side of the river,","just across the road from the Groats' Farm.").also { stage = 71 } - 3 -> npc(core.game.dialogue.FacialExpression.NEUTRAL,"I normally get my eggs from the Groats' farm, on the","other side of the river.").also { stage = 73 } - 4 -> player(core.game.dialogue.FacialExpression.NEUTRAL,"I've got all the information I need. Thanks.").also { stage = 1000 } + 1 -> npc(FacialExpression.NEUTRAL,"There is a Mill fairly close, go North and then West.","Mill Lane Mill is just off the road to Draynor. I","usually get my flour from there.").also {stage = 70 } + 2 -> npc(FacialExpression.NEUTRAL,"There is a cattle field on the other side of the river,","just across the road from the Groats' Farm.").also { stage = 71 } + 3 -> npc(FacialExpression.NEUTRAL,"I normally get my eggs from the Groats' farm, on the","other side of the river.").also { stage = 73 } + 4 -> player(FacialExpression.NEUTRAL,"I've got all the information I need. Thanks.").also { stage = 1000 } } 100 -> if (!player.getAttribute("cooks_assistant:milk_submitted", false) && player.inventory.contains(MILK, 1)) { player.setAttribute("/save:cooks_assistant:milk_submitted", true).also { - player(core.game.dialogue.FacialExpression.HAPPY, "Here's a bucket of milk.") + player(FacialExpression.HAPPY, "Here's a bucket of milk.") player.inventory.remove(Item(MILK)) gave = true stage = 100 } } else if (!player.getAttribute("cooks_assistant:flour_submitted", false) && player.inventory.contains(FLOUR, 1)) { player.setAttribute("/save:cooks_assistant:flour_submitted", true).also { - player(core.game.dialogue.FacialExpression.HAPPY, "Here's a pot of flour.") + player(FacialExpression.HAPPY, "Here's a pot of flour.") player.inventory.remove(Item(FLOUR)) gave = true stage = 100 } } else if (!player.getAttribute("cooks_assistant:egg_submitted", false) && player.inventory.contains(EGG, 1)) { player.setAttribute("/save:cooks_assistant:egg_submitted", true).also { - player(core.game.dialogue.FacialExpression.HAPPY, "Here's a fresh egg.") + player(FacialExpression.HAPPY, "Here's a fresh egg.") player.inventory.remove(Item(EGG)) gave = true stage = 100 @@ -185,16 +186,16 @@ class LumbridgeCookDialogue (player: Player? = null) : core.game.dialogue.Dialog //If the player has now handed in all the ingredients if (player.getAttribute("cooks_assistant:milk_submitted", false) && player.getAttribute("cooks_assistant:flour_submitted", false) && player.getAttribute("cooks_assistant:egg_submitted", false)) { - npc(core.game.dialogue.FacialExpression.HAPPY, "You've brought me everything I need! I am saved!", "Thank you!").also { player.setAttribute("/save:cooks_assistant:all_submitted",true); stage = 200 } + npc(FacialExpression.HAPPY, "You've brought me everything I need! I am saved!", "Thank you!").also { player.setAttribute("/save:cooks_assistant:all_submitted",true); stage = 200 } } else { - npc(core.game.dialogue.FacialExpression.WORRIED,"Thanks for the ingredients you have got so far, please get","the rest quickly. I'm running out of time! The Duke","will throw me into the streets!").also { stage = 151 } + npc(FacialExpression.WORRIED,"Thanks for the ingredients you have got so far, please get","the rest quickly. I'm running out of time! The Duke","will throw me into the streets!").also { stage = 151 } } } else { //If the player did not give an item to the Lumbridge cook //If the player also has never submitted anything before if (!player.getAttribute("cooks_assistant:submitted_some_items", false)) { - player(core.game.dialogue.FacialExpression.NEUTRAL, "I haven't gotten any of them yet, I'm still looking.").also { stage = 155 } + player(FacialExpression.NEUTRAL, "I haven't gotten any of them yet, I'm still looking.").also { stage = 155 } } else { options("I'll get right on it.", "Can you remind me how to find these things again?").also { stage = 161 } } @@ -202,7 +203,7 @@ class LumbridgeCookDialogue (player: Player? = null) : core.game.dialogue.Dialog } //If the player has submitted some ingredients but not all of them - 150 -> npc(core.game.dialogue.FacialExpression.WORRIED,"Thanks for the ingredients you have got so far, please get","the rest quickly. I'm running out of time! The Duke","will throw me into the streets!").also { stage++ } + 150 -> npc(FacialExpression.WORRIED,"Thanks for the ingredients you have got so far, please get","the rest quickly. I'm running out of time! The Duke","will throw me into the streets!").also { stage++ } //Checking what the player has left over 151 -> leftoverItems = "".also{ @@ -218,66 +219,66 @@ class LumbridgeCookDialogue (player: Player? = null) : core.game.dialogue.Dialog if (leftoverItems != ""){ sendDialogue("You still need to get:",leftoverItems).also {stage = 160} } else { - npc(core.game.dialogue.FacialExpression.HAPPY, "You've brought me everything I need! I am saved!", "Thank you!").also { player.setAttribute("/save:cooks_assistant:all_submitted",true); stage = 200 } + npc(FacialExpression.HAPPY, "You've brought me everything I need! I am saved!", "Thank you!").also { player.setAttribute("/save:cooks_assistant:all_submitted",true); stage = 200 } } } //If the player has yet to collect or hand in any of the ingredients - 155 -> npc(core.game.dialogue.FacialExpression.WORRIED,"Please get the ingredients quickly. I'm running out of","time! The Duke will throw me into the streets!").also { stage++ } + 155 -> npc(FacialExpression.WORRIED,"Please get the ingredients quickly. I'm running out of","time! The Duke will throw me into the streets!").also { stage++ } 156 -> sendDialogue("You still need to get:", "A bucket of milk. A pot of flour. An egg.").also { stage = 160 } // Menu after checking the items handed in 160 -> options("I'll get right on it.","Can you remind me how to find these things again?").also { stage++ } 161 -> when(buttonId) { - 1 -> player(core.game.dialogue.FacialExpression.NEUTRAL,"I'll get right on it.").also { stage = 1000 } - 2 -> player(core.game.dialogue.FacialExpression.ASKING,"Can you remind me how to find these things again?").also { stage = 60 } + 1 -> player(FacialExpression.NEUTRAL,"I'll get right on it.").also { stage = 1000 } + 2 -> player(FacialExpression.ASKING,"Can you remind me how to find these things again?").also { stage = 60 } } //Final Cooks Assistant Dialogue - 200 -> player(core.game.dialogue.FacialExpression.HAPPY, "So do I get to go to the Duke's Party?").also { stage++ } - 201 -> npc(core.game.dialogue.FacialExpression.SAD, "I'm afraid not, only the big cheeses get to dine with the", "Duke.").also { stage++ } - 202 -> player(core.game.dialogue.FacialExpression.NEUTRAL, "Well, maybe one day I'll be important enough to sit on", "the Duke's table.").also { stage ++ } - 203 -> npc(core.game.dialogue.FacialExpression.NEUTRAL, "Maybe, but I won't be holding my breath.").also { stage++ } + 200 -> player(FacialExpression.HAPPY, "So do I get to go to the Duke's Party?").also { stage++ } + 201 -> npc(FacialExpression.SAD, "I'm afraid not, only the big cheeses get to dine with the", "Duke.").also { stage++ } + 202 -> player(FacialExpression.NEUTRAL, "Well, maybe one day I'll be important enough to sit on", "the Duke's table.").also { stage ++ } + 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++ } } 301 -> when (buttonId) { - 1 -> player(core.game.dialogue.FacialExpression.HAPPY, "I am getting strong and mighty. Grrr").also { stage = 310 } - 2 -> player(core.game.dialogue.FacialExpression.SAD, "I keep on dying.").also { stage = 320 } - 3 -> player(core.game.dialogue.FacialExpression.ASKING, "Can I use your range?").also { stage = 330 } + 1 -> player(FacialExpression.HAPPY, "I am getting strong and mighty. Grrr").also { stage = 310 } + 2 -> player(FacialExpression.SAD, "I keep on dying.").also { stage = 320 } + 3 -> player(FacialExpression.ASKING, "Can I use your range?").also { stage = 330 } } //I am getting strong and mighty - 310 -> npc(core.game.dialogue.FacialExpression.HAPPY,"Glad to hear it!").also { stage = 1000 } + 310 -> npc(FacialExpression.HAPPY,"Glad to hear it!").also { stage = 1000 } //I keep on dying - 320 -> npc(core.game.dialogue.FacialExpression.HAPPY, "Ah, well, at least you keep coming back to life too!").also { stage = 1000 } + 320 -> npc(FacialExpression.HAPPY, "Ah, well, at least you keep coming back to life too!").also { stage = 1000 } //Can I use your range? - 330 -> npc(core.game.dialogue.FacialExpression.HAPPY, "Go ahead! It's a very good range; it's better than most" ,"other ranges.").also { stage++ } - 331 -> npc(core.game.dialogue.FacialExpression.NEUTRAL, "It's called the Cook-o-Matic 100 and it uses a combination","of state-of-the-art temperature regulation and magic.").also { stage++ } - 332 -> player(core.game.dialogue.FacialExpression.ASKING,"Will it mean my food will burn less often?").also { stage++ } - 333 -> npc(core.game.dialogue.FacialExpression.NEUTRAL,"Well, that's what the salesman told us anyway...").also { stage++ } - 334 -> player(core.game.dialogue.FacialExpression.THINKING, "Thanks?").also { stage = 1000 } + 330 -> npc(FacialExpression.HAPPY, "Go ahead! It's a very good range; it's better than most" ,"other ranges.").also { stage++ } + 331 -> npc(FacialExpression.NEUTRAL, "It's called the Cook-o-Matic 100 and it uses a combination","of state-of-the-art temperature regulation and magic.").also { stage++ } + 332 -> player(FacialExpression.ASKING,"Will it mean my food will burn less often?").also { stage++ } + 333 -> npc(FacialExpression.NEUTRAL,"Well, that's what the salesman told us anyway...").also { stage++ } + 334 -> player(FacialExpression.THINKING, "Thanks?").also { stage = 1000 } //Conversation Endpoint 1000 -> end() } return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return LumbridgeCookDialogue(player) } override fun getIds(): IntArray { return intArrayOf(278) } -} \ No newline at end of file +} 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 index cefb58d12..679e28bde 100644 --- a/Server/src/main/content/region/misthalin/lumbridge/quest/lostcity/DramenTreeListener.kt +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/lostcity/DramenTreeListener.kt @@ -7,16 +7,17 @@ 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.questStage +import core.api.getQuestStage import core.api.sendMessage import core.game.interaction.IntType +import content.data.Quests class DramenTreeListener : InteractionListener { override fun defineListeners() { on(Sceneries.DRAMEN_TREE_1292, IntType.SCENERY, "chop down"){ player, node -> - val questStage = questStage(player,"Lost City") + val questStage = getQuestStage(player,Quests.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 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..744ef3ca1 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 @@ -6,6 +6,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 /** * LostCity class for the Lost City quest @@ -14,7 +15,7 @@ import org.rs09.consts.Items * @author Aero */ @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) { class SkillRequirement(val skill: Int?, val level: Int?) 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 143070cf5..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,10 +13,12 @@ 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 * @author lila + * @author Player Name */ @Initializable class LostCityListeners : InteractionListener { @@ -26,22 +28,25 @@ 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) && ( questStage(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++) { 0 -> { - sendMessage(player,"The world starts to shimmer...") - teleport(player, Location(2452, 4473, 0), TeleportType.FAIRY_RING) + if (player.isTeleBlocked()) { + sendMessage(player,"A magical force has stopped you from teleporting.") + } else { + sendMessage(player,"The world starts to shimmer...") + 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 } } @@ -61,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 c6726cffb..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 @@ -1,12 +1,11 @@ package content.region.misthalin.lumbridge.quest.lostcity -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 -import core.api.questStage +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 @@ -15,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 @@ -25,7 +21,7 @@ class ShamusDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin } override fun handle(interfaceId: Int, buttonId: Int): Boolean { - when(questStage(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!") @@ -47,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 7bdba52c6..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,15 +1,15 @@ 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 import core.plugin.Initializable import org.rs09.consts.NPCs -import core.api.questStage +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,19 +47,10 @@ 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 (questStage(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.") } } } - - override fun isAttackable(entity: Entity, style: CombatStyle, message: Boolean): Boolean { - if(entity != target) { - return false - } - return super.isAttackable(entity, style, message) - } - } 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 ce337ef88..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 @@ -1,14 +1,13 @@ package content.region.misthalin.lumbridge.quest.lostcity -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 import core.game.dialogue.Topic import core.tools.END_DIALOGUE -import core.api.questStage +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 @@ -19,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(questStage(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 } @@ -51,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 new file mode 100644 index 000000000..12a1af387 --- /dev/null +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/sheepshearer/SSFredTheFarmerDialogue.kt @@ -0,0 +1,183 @@ +package content.region.misthalin.lumbridge.quest.sheepshearer + +import core.api.* +import core.game.dialogue.DialogueFile +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 { + const val STAGE_BEGIN_QUEST = 1000 + const val STAGE_PENGUIN_SHEEP_SHEARED = 3000 + const val STAGE_CANT_SPIN_WOOL = 20000 + const val STAGE_CAN_SPIN_WOOL = 20100 + const val STAGE_DELIVER_BALLS_OF_WOOL = 30000 + const val STAGE_FINISH_QUEST = 30301 + } + + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + START_DIALOGUE -> if (questStage == 10) { + if (getAttribute(player!!, SheepShearer.ATTR_IS_PENGUIN_SHEEP_SHEARED, false)) { + npc(FacialExpression.ANGRY, "What are you doing on my land?").also { stage = STAGE_PENGUIN_SHEEP_SHEARED } + } else { + npc(FacialExpression.NEUTRAL, "How are you doing getting those balls of wool?").also { stage = STAGE_DELIVER_BALLS_OF_WOOL } + } + } else if (questStage == 90) { + // Player delivered all balls of wool but exited the dialogue before the quest was recorded as complete + npc(FacialExpression.SAD, "I guess I'd better pay you then.").also { stage = STAGE_FINISH_QUEST } + } else { + npc(FacialExpression.NEUTRAL, "You're after a quest, you say? Actually I could do with", "a bit of help.").also { stage = STAGE_BEGIN_QUEST } + } + + STAGE_BEGIN_QUEST -> npc(FacialExpression.NEUTRAL, "My sheep are getting mighty woolly. I'd be much", "obliged if you could shear them. And while you're at it", "spin the wool for me too.").also { stage++ } + 1001 -> npc(FacialExpression.HAPPY, "Yes, that's it. Bring me 20 balls of wool. And I'm sure", "I could sort out some sort of payment. Of course,", "there's the small matter of The Thing.").also { stage++ } + 1002 -> options("Yes okay. I can do that.", "That doesn't sound a very exciting quest.", "What do you mean, The Thing?").also { stage++ } + 1003 -> when (buttonID) { + 1 -> player(FacialExpression.HAPPY, "Yes okay. I can do that.").also { stage = 2000 } + 2 -> player(FacialExpression.HALF_GUILTY, "That doesn't sound a very exciting quest.").also { stage = 1100 } + 3 -> player(FacialExpression.ASKING, "What do you mean, The Thing?").also { stage = 1200 } + } + + // TODO: There might be more dialogue here + 1100 -> npc(FacialExpression.HALF_GUILTY, "Well what do you expect if you ask a farmer for a", "quest?").also { stage = END_DIALOGUE } + + 1200 -> npc(FacialExpression.SUSPICIOUS, "Well now, no one has ever seen The Thing. That's", "why we call it The Thing, 'cos we don't know what it is.").also { stage++ } + 1201 -> npc(FacialExpression.SCARED, "Some say it's a black hearted shapeshifter, hungering for", "the souls of hard working decent folk like me. Others", "say it's just a sheep.").also { stage++ } + 1202 -> npc(FacialExpression.ANGRY, "Well I don't have all day to stand around and gossip.", "Are you going to shear my sheep or what!").also { stage++ } + 1203 -> options("Yes okay. I can do that.", "Erm I'm a bit worried about this Thing.").also { stage++ } + 1204 -> when (buttonID) { + 1 -> player(FacialExpression.HAPPY, "Yes okay. I can do that.").also { stage = 2000 } + 2 -> player(FacialExpression.HALF_GUILTY, "Erm I'm a bit worried about this Thing.").also { stage = 1300 } + } + + 1300 -> npc(FacialExpression.HALF_GUILTY, "I'm sure it's nothing to worry about. Just because", "my last shearer was seen bolting out of the field", "screaming for his life doesn't mean anything.").also { stage++ } + 1301 -> player(FacialExpression.HALF_GUILTY, "I'm not convinced.").also { stage = END_DIALOGUE } + + 2000 -> { + // NOTE: In a July 2009 video, this only happens when the dialogue ends + 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++ } + 2002 -> when (buttonID) { + 1 -> player(FacialExpression.HAPPY, "Of course!").also { stage = 2100 } + 2 -> player(FacialExpression.NEUTRAL, "Err. No, I don't know actually.").also { stage = 2200 } + } + + 2100 -> npc(FacialExpression.NEUTRAL, "And you know how to spin wool into balls?").also { stage++ } + 2101 -> options("I'm something of an expert actually!", "I don't know how to spin wool, sorry.").also { stage++ } + 2102 -> when (buttonID) { + 1 -> player(FacialExpression.HAPPY, "I'm something of an expert actually!").also { stage = STAGE_CAN_SPIN_WOOL } + 2 -> player(FacialExpression.NEUTRAL, "I don't know how to spin wool, sorry.").also { stage = STAGE_CANT_SPIN_WOOL } + } + + 2200 -> { + if (inInventory(player!!, Items.SHEARS_1735)) { + npc(FacialExpression.HAPPY, "Well, you're halfway there already! You have a set of", "shears in your inventory. Just use those on a Sheep to", "shear it.").also { stage = 2300 } + } else { + npc(FacialExpression.NEUTRAL, "Well, first things first, you need a pair of shears, there's", "a pair in the house on the table.").also { stage = 2400 } + } + } + + 2300 -> player(FacialExpression.NEUTRAL, "That's all I have to do?").also { stage++ } + 2301 -> npc(FacialExpression.NEUTRAL, "Well once you've collected some wool you'll need to spin", "it into balls.").also { stage++ } + 2302 -> npc(FacialExpression.ASKING, "Do you know how to spin wool?").also { stage++ } + 2303 -> options("I don't know how to spin wool, sorry.", "I'm something of an expert actually!").also { stage++ } + 2304 -> when (buttonID) { + 1 -> player(FacialExpression.NEUTRAL, "I don't know how to spin wool, sorry.").also { stage = STAGE_CANT_SPIN_WOOL } + 2 -> player(FacialExpression.HAPPY, "I'm something of an expert actually!").also { stage = STAGE_CAN_SPIN_WOOL } + } + + 2400 -> npc(FacialExpression.NEUTRAL, "Or you could buy your own pair from the General", "Store in Lumbridge.").also { stage++ } + 2401 -> npc(FacialExpression.NEUTRAL, "To get to Lumbridge travel east on the road outside.").also { stage++ } + // TODO: Add "General Stores are marked on the map by this symbol." message with the general store map icon here + 2402 -> npc(FacialExpression.NEUTRAL, "Once you get some shears use them on the sheep in", "my field.").also { stage++ } + 2403 -> player(FacialExpression.HAPPY, "Sounds easy!").also { stage++ } + 2404 -> npc(FacialExpression.LAUGH, "That's what they all say!").also { stage++ } + 2405 -> npc(FacialExpression.NEUTRAL, "Some of the sheep don't like it and will run away from", "you. Persistence is the key.").also { stage++ } + 2406 -> npc(FacialExpression.NEUTRAL, "Once you've collected some wool you can spin it into", "balls.").also { stage++ } + 2407 -> npc(FacialExpression.NEUTRAL, "Do you know how to spin wool?").also { stage++ } + 2408 -> options("I don't know how to spin wool, sorry.", "I'm something of an expert actually!").also { stage++ } + 2409 -> when (buttonID) { + 1 -> player(FacialExpression.NEUTRAL, "I don't know how to spin wool, sorry.").also { stage = STAGE_CANT_SPIN_WOOL } + 2 -> player(FacialExpression.HAPPY, "I'm something of an expert actually!").also { stage = STAGE_CAN_SPIN_WOOL } + } + + STAGE_PENGUIN_SHEEP_SHEARED -> options("I'm back!", "Fred! Fred! I've seen The Thing!").also { stage++ } + 3001 -> when (buttonID) { + 1 -> player(FacialExpression.HAPPY, "I'm back!").also { stage = 3100 } + 2 -> player(FacialExpression.AMAZED, "Fred! Fred! I've seen The Thing!").also { stage = 3200 } + } + + 3100 -> npc(FacialExpression.NEUTRAL, "How are you doing getting those balls of wool?").also { stage = STAGE_DELIVER_BALLS_OF_WOOL } + + 3200 -> npc(FacialExpression.SCARED, "You ... you actually saw it?").also { stage++ } + 3201 -> npc(FacialExpression.SCARED, "Run for the hills! ${player!!.username} grab as many chickens as", "you can! We have to ...").also { stage++ } + 3202 -> player(FacialExpression.AMAZED, "Fred!").also { stage++ } + 3203 -> npc(FacialExpression.SCARED, "... flee! Oh, woe is me! The shapeshifter is coming!", "We're all ...").also { stage++ } + 3204 -> player(FacialExpression.ANGRY, "FRED!").also { stage++ } + 3205 -> npc(FacialExpression.HALF_CRYING, "... doomed. What!").also { stage++ } + 3206 -> player(FacialExpression.NEUTRAL, "It's not a shapeshifter or any other kind of monster!").also { stage++ } + 3207 -> npc(FacialExpression.ASKING, "Well then what is it boy?").also { stage++ } + 3208 -> player(FacialExpression.THINKING, "Well ... it's just two Penguins; Penguins disguised as a", "sheep.").also { stage++ } + 3209 -> npc(FacialExpression.THINKING, "...").also { stage++ } + 3210 -> npc(FacialExpression.AMAZED, "Have you been out in the sun too long?").also { stage = END_DIALOGUE } + + // Common dialogue - doesn't know how to spin wool + STAGE_CANT_SPIN_WOOL -> npc(FacialExpression.NEUTRAL, "Don't worry, it's quite simple!").also { stage++ } + 20001 -> npc(FacialExpression.NEUTRAL, "The nearest Spinning Wheel can be found on the first", "floor of Lumbridge Castle.").also { stage++ } + 20002 -> npc(FacialExpression.NEUTRAL, "To get to Lumbridge Castle just follow the road east.").also { stage++ } + // TODO: Add "This icon denotes a Spinning Wheel on the world map." message with the spinning wheel map icon here + 20003 -> player(FacialExpression.HAPPY, "Thank you!").also { stage = END_DIALOGUE } + + // Common dialogue - knows how to spin wool + STAGE_CAN_SPIN_WOOL -> npc(FacialExpression.NEUTRAL, "Well you can stop grinning and get to work then.").also { stage++ } + 20101 -> npc(FacialExpression.ANGRY, "I'm not paying you by the hour!").also { stage = END_DIALOGUE } + + // Common dialogue - deliver balls of wool + STAGE_DELIVER_BALLS_OF_WOOL -> { + if (inInventory(player!!, Items.BALL_OF_WOOL_1759)) { + player(FacialExpression.HAPPY, "I have some.").also { stage = 30100 } + } else { + player(FacialExpression.ASKING, "How many more do I need to give you?").also { stage = 31000 } + } + } + + 30100 -> npc(FacialExpression.NEUTRAL, "Give 'em here then.").also { stage++ } + 30101 -> { + val ballsOfWoolDelivered = SheepShearer.deliverBallsOfWool(player!!) + if (SheepShearer.getBallsOfWoolRequired(player!!) == 0) { + 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 } + } + } + + 30200 -> player(FacialExpression.NEUTRAL, "That's all I've got so far.").also { stage++ } + 30201 -> npc(FacialExpression.NEUTRAL, "I need ${SheepShearer.getBallsOfWoolRequired(player!!)} more before I can pay you.").also { stage++ } + 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!!, 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 -> { + if (inInventory(player!!, Items.WOOL_1737)) { + player(FacialExpression.NEUTRAL, "Well I've got some wool. I've not managed to make it", "into a ball though.").also { stage = 31100 } + } else { + player(FacialExpression.HALF_GUILTY, "I haven't got any at the moment.").also { stage = 31200 } + } + } + + 31100 -> npc(FacialExpression.NEUTRAL, "Well go find a spinning wheel then. You can find one", "on the first floor of Lumbridge Castle, just walk east on", "the road outside my house and you'll find Lumbridge.").also { stage = END_DIALOGUE } + + // TODO: There might be more dialogue here + 31200 -> npc(FacialExpression.HALF_GUILTY, "Ah well at least you haven't been eaten.").also { stage = END_DIALOGUE } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/sheepshearer/SheepShearPlugin.java b/Server/src/main/content/region/misthalin/lumbridge/quest/sheepshearer/SheepShearPlugin.java deleted file mode 100644 index 88d12db04..000000000 --- a/Server/src/main/content/region/misthalin/lumbridge/quest/sheepshearer/SheepShearPlugin.java +++ /dev/null @@ -1,81 +0,0 @@ -package content.region.misthalin.lumbridge.quest.sheepshearer; - -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.node.item.Item; -import core.game.system.task.Pulse; -import core.game.world.GameWorld; -import core.game.world.update.flag.context.Animation; -import core.plugin.Plugin; -import core.plugin.Initializable; -import core.tools.RandomFunction; - -/** - * Represents the plugin used to shear a sheep. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class SheepShearPlugin extends OptionHandler { - - /** - * Represents the animation to use. - */ - private static final Animation ANIMATION = new Animation(893); - - @Override - public Plugin newInstance(Object arg) throws Throwable { - NPCDefinition.setOptionHandler("shear", this); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - final NPC sheep = (NPC) node; - if (sheep.getId() == 3579) { - player.animate(ANIMATION); - player.getPacketDispatch().sendMessage("The... whatever it is... manages to get away from you!"); - sheep.animate(Animation.create(3570)); - sheep.moveStep(); - return true; - } - if (!player.getInventory().contains(1735, 1)) { - player.getPacketDispatch().sendMessage("You need shears to shear a sheep."); - return true; - } - if (sheep.getDefinition().hasAction("attack")) { - player.getPacketDispatch().sendMessage("That one looks a little too violent to shear..."); - return true; - } - if (player.getInventory().freeSlots() == 0) { - player.getPacketDispatch().sendMessage("You don't have enough space in your inventory to carry any wool you would shear."); - return true; - } - sheep.lock(3); - sheep.getWalkingQueue().reset(); - player.animate(ANIMATION); - int random = RandomFunction.random(1, 5); - if (random != 4) { - sheep.getLocks().lockMovement(2); - sheep.transform(5153); - player.getPacketDispatch().sendMessage("You get some wool."); - player.getInventory().add(new Item(1737, 1));// 5160 - GameWorld.getPulser().submit(new Pulse(80, sheep) { - @Override - public boolean pulse() { - sheep.reTransform(); - return true; - } - - }); - } else { - player.getPacketDispatch().sendMessage("The sheep manages to get away from you!"); - sheep.moveStep(); - } - return true; - } - -} diff --git a/Server/src/main/content/region/misthalin/lumbridge/quest/sheepshearer/SheepShearer.java b/Server/src/main/content/region/misthalin/lumbridge/quest/sheepshearer/SheepShearer.java deleted file mode 100644 index 793a6f623..000000000 --- a/Server/src/main/content/region/misthalin/lumbridge/quest/sheepshearer/SheepShearer.java +++ /dev/null @@ -1,135 +0,0 @@ -package content.region.misthalin.lumbridge.quest.sheepshearer; - -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 core.game.node.item.GroundItemManager; -import core.game.node.item.Item; - -/** - * Represents the sheep shearer quest. - * @author 'Veixa - */ -@Initializable -public class SheepShearer extends Quest { - - /** - * Constructs a new {@code SheepShearer} {@code Object}. - */ - public SheepShearer() { - super("Sheep Shearer", 28, 27, 1, 179, 0, 20, 21); - } - - /** - * Represents the wool item. - */ - public static final Item WOOL = new Item(1759); - - @Override - public void finish(Player player) { - super.finish(player); - player.getPacketDispatch().sendString("1 Quest Point", 277, 8 + 2); - player.getPacketDispatch().sendString("150 Crafting XP", 277, 9 + 2); - player.getPacketDispatch().sendString("60 coins", 277, 10 + 2); - player.getPacketDispatch().sendItemZoomOnInterface(1735, 240, 277, 3 + 2); - player.getSkills().addExperience(Skills.CRAFTING, 150); - player.getInterfaceManager().closeChatbox(); - if (player.getInventory().freeSlots() == 0) { - GroundItemManager.create(new Item(995, 60), player.getLocation()); - } else { - player.getInventory().add(new Item(995, 60)); - } - } - - @Override - public void drawJournal(Player player, int stage) { - super.drawJournal(player, stage); - if (stage < 10) { - player.getPacketDispatch().sendString("I can start this quest by speaking to Farmer Fred at his", 275, 4+ 7); - player.getPacketDispatch().sendString("farm just a little way North West of Lumbridge", 275, 5+ 7); - return; - } - if (stage == 10 || stage == 90) { - player.getPacketDispatch().sendString("I can start this quest by speaking to Farmer Fred at his", 275, 4+ 7); - player.getPacketDispatch().sendString("farm just a little way North West of Lumbridge", 275, 5+ 7); - int wool = getWoolCollect(player); - if (wool == 0) { - player.getPacketDispatch().sendString("I have enough balls of wool to give Fred and get my reward", 275, 7+ 7); - player.getPacketDispatch().sendString("money!", 275, 8+ 7); - } - if (wool != 0) { - player.getPacketDispatch().sendString("I need to collect " + wool + " more balls of wool.", 275, 7+ 7); - } - } - if (stage == 100) { - player.getPacketDispatch().sendString("I brought Farmer Fred 20 balls of wool, and he paid me for", 275, 4+ 7); - player.getPacketDispatch().sendString("it!", 275, 5+ 7); - player.getPacketDispatch().sendString("QUEST COMPLETE!", 275, 7+ 7); - } - } - - /** - * Method used to get the wool given. - * @param player the player. - * @return the wool given. - */ - public static int getWoolGiven(final Player player) { - return player.getAttribute("sheep-shearer:wool", 0); - } - - /** - * Method used to get the wool left to give. - * @param player the player. - * @return the wool. - */ - public static int getWoolLeft(final Player player) { - int left = 20 - getWoolGiven(player); - return left; - } - - /** - * Method used to add wool to the current amount given. - * @param player the player. - * @return True if added. - */ - public static boolean addWool(final Player player) { - final int removeAmount = getWoolRemove(player); - if (player.getInventory().remove(new Item(1759, removeAmount))) { - player.setAttribute("sheep-shearer:wool", getWoolGiven(player) + removeAmount); - return true; - } - return false; - } - - /** - * Method used to get the wool to remove. - * @param player the player. - * @return the amount that can be removed from the inventory. - */ - public static int getWoolRemove(final Player player) { - int woolAmount = player.getInventory().getAmount(WOOL); - int remove = getWoolLeft(player) - (getWoolLeft(player) - woolAmount); - int realRemove = remove > 20 ? remove - (remove - getWoolLeft(player)) : remove; - if (realRemove > getWoolLeft(player)) { - realRemove = getWoolLeft(player); - } - return realRemove; - } - - /** - * Method used to get the wool left to collect. - * @param player the player. - * @return the wool. - */ - public static int getWoolCollect(final Player player) { - return 20 - (getWoolGiven(player) + getWoolRemove(player)); - } - - @Override - public Quest newInstance(Object object) { - // TODO Auto-generated method stub - return this; - } - -} 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 new file mode 100644 index 000000000..634041363 --- /dev/null +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/sheepshearer/SheepShearer.kt @@ -0,0 +1,122 @@ +package content.region.misthalin.lumbridge.quest.sheepshearer + +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.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(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" + + /** + * Gets the number of balls of wool that have been delivered to Fred the Farmer. + * @param player the player to check + * @return the number of balls of wool that have been delivered + */ + fun getBallsOfWoolDelivered(player: Player): Int { + return getAttribute(player, ATTR_NUM_BALLS_OF_WOOL_DELIVERED, 0) + } + + /** + * Gets the number of balls of wool remaining to be delivered to Fred the Farmer. + * @param player the player to check + * @return the number of balls of wool remaining to be delivered + */ + fun getBallsOfWoolRequired(player: Player): Int { + return 20 - getBallsOfWoolDelivered(player) + } + + /** + * Gets the number of balls of wool that can be removed from the player's inventory, + * up to the amount still required by Fred the Farmer. + * @param player the player whose inventory should be checked + * @return the number of balls of wool that can be removed from the player's inventory + * and delivered to Fred the Farmer + */ + fun getBallsOfWoolToRemove(player: Player): Int { + return min(getBallsOfWoolRequired(player), amountInInventory(player, Items.BALL_OF_WOOL_1759)) + } + + /** + * Gets the number of balls of wool that still need to be collected by the player. + * + * This takes into account the number of balls of wool already delivered to Fred the + * Farmer, and the number of balls of wool in the player's inventory. + * @param player the player to check + * @return the number of balls of wool that still needs to be collected by the player + */ + fun getBallsOfWoolToCollect(player: Player): Int { + return getBallsOfWoolRequired(player) - getBallsOfWoolToRemove(player) + } + + /** + * Delivers all balls of wool in the player's inventory, up to the amount still required + * by Fred the Farmer. + * @param player the player delivering balls of wool to Fred the Farmer + * @return the number of balls of wool removed from the player's inventory and delivered to Fred the Farmer + */ + fun deliverBallsOfWool(player: Player): Int { + val removeAmount = getBallsOfWoolToRemove(player) + if (removeItem(player, Item(Items.BALL_OF_WOOL_1759, removeAmount))) { + setAttribute(player, ATTR_NUM_BALLS_OF_WOOL_DELIVERED, getBallsOfWoolDelivered(player) + removeAmount) + return removeAmount + } + return 0 + } + } + + override fun newInstance(`object`: Any?): Quest { + return this + } + + override fun drawJournal(player: Player, stage: Int) { + super.drawJournal(player, stage) + + var ln = 11 + + if (stage == 0) { + line(player, "I can start this quest by speaking to !!Farmer Fred?? at his", ln++) + line(player, "!!farm?? just a little way !!North West of Lumbridge??", ln++) + } else if (stage == 10 || stage == 90) { + line(player, "I asked Farmer Fred, near Lumbridge, for a quest. Fred", ln++, true) + line(player, "said he'd pay me for shearing his sheep for him!", ln++, true) + ln++ + + val ballsOfWoolToCollect = getBallsOfWoolToCollect(player) + if (ballsOfWoolToCollect == 0) { + line(player, "I have enough !!balls of wool?? to give !!Fred?? and get my !!reward??", ln++) + line(player, "!!money!??", ln++) + } else { + line(player, "I need to collect $ballsOfWoolToCollect more !!balls of wool.??", ln++) + } + } else if (stage == 100) { + line(player, "I brought Farmer Fred 20 balls of wool, and he paid me for", ln++, true) + line(player, "it!", ln++, true) + ln++ + line(player, "QUEST COMPLETE!", ln++) + } + } + + override fun finish(player: Player?) { + super.finish(player) + player ?: return + + var ln = 10 + player.packetDispatch.sendItemZoomOnInterface(Items.SHEARS_1735, 240, 277, 5) + drawReward(player, "1 Quest Point", ln++) + drawReward(player, "150 Crafting XP", ln++) + drawReward(player, "60 coins", ln++) + rewardXP(player, Skills.CRAFTING, 150.0) + addItemOrDrop(player, Items.COINS_995, 60) + removeAttribute(player, ATTR_NUM_BALLS_OF_WOOL_DELIVERED) + removeAttribute(player, ATTR_IS_PENGUIN_SHEEP_SHEARED) + } +} \ No newline at end of file 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..52a859bb3 --- /dev/null +++ b/Server/src/main/content/region/misthalin/lumbridge/quest/tearsofguthix/TearsOfGuthix.kt @@ -0,0 +1,132 @@ +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( + 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) >= 55) + 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 2ddb8d964..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 @@ -8,6 +8,10 @@ import core.game.node.item.Item; import core.plugin.Initializable; import core.plugin.ClassScanner; +import static core.api.ContentAPIKt.*; +import content.data.Quests; + + /** * Represents the restless ghost quest. * @author Vexia @@ -15,22 +19,16 @@ import core.plugin.ClassScanner; */ @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 @@ -107,7 +105,7 @@ public class RestlessGhost extends Quest { player.getSkills().addExperience(Skills.PRAYER, 1125); player.getInterfaceManager().closeChatbox(); player.getPacketDispatch().sendString("You have completed The Restless Ghost Quest!", 277, 2 + 2); - player.getConfigManager().set(728, 31, true); + setVarp(player, 728, 31, true); player.getGameAttributes().removeAttribute("restless-ghost:urhney"); } 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 feebbda88..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; @@ -19,6 +20,8 @@ import core.game.world.update.flag.context.Animation; import core.plugin.Initializable; import core.plugin.Plugin; +import static core.api.ContentAPIKt.*; + /** * Represents the restless ghost plugin. * @author 'Vexia @@ -104,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); @@ -130,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(); } } @@ -159,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; } @@ -167,8 +170,8 @@ public final class RestlessGhostPlugin extends OptionHandler { if (!player.getInventory().add(SKULL)) { GroundItemManager.create(SKULL, player); } - player.getConfigManager().set(728, 5, true); - player.getQuestRepository().getQuest(RestlessGhost.NAME).setStage(player, 40); + setVarp(player, 728, 5, true); + player.getQuestRepository().getQuest(Quests.THE_RESTLESS_GHOST).setStage(player, 40); player.getPacketDispatch().sendMessage("The skeleton in the corner suddenly comes to life!"); sendSkeleton(player); } @@ -257,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 0ddb1a9fc..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,11 +1,15 @@ 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; -import core.game.node.item.Item; import core.game.node.scenery.Scenery; import core.plugin.Initializable; import core.plugin.Plugin; +import org.rs09.consts.Items; + +import static core.api.ContentAPIKt.removeItem; /** * Represents the plugin used to add the skull to the cofin. @@ -37,9 +41,9 @@ public final class RestlessGhostSkull extends UseWithHandler { event.getPlayer().getDialogueInterpreter().sendDialogue("Maybe I should open it first."); return true; } - if (event.getPlayer().getInventory().remove(new Item(964, 24))) { + 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 new file mode 100644 index 000000000..256c7e17d --- /dev/null +++ b/Server/src/main/content/region/misthalin/quest/asoulsbane/ASoulsBaneListeners.kt @@ -0,0 +1,27 @@ +package content.region.misthalin.quest.asoulsbane + +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 { + + companion object { + private val RIFT_IDS = intArrayOf(13967, 13968, 13969, 13970, 13971, 13972, 13973, 13974, 13975, 13976, 13977, 13978, 13979, 13980, 13981, 13982, 13983, 13984, 13985, 13986, 13987, 13988, 13989, 13990, 13991, 13992, 13993) + } + override fun defineListeners() { + on(RIFT_IDS, SCENERY, "enter") { player, _ -> + if (hasRequirement(player, Quests.A_SOULS_BANE)) { + teleport(player, Location(3297, 9824, 0)) + } + return@on true + } + on(Scenery.ROPE_13999, SCENERY, "climb-up") { player, _ -> + teleport(player, Location(3309, 3452, 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 8c03560d7..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?"); @@ -111,7 +112,7 @@ public final class DrezelDialogue extends DialoguePlugin { } break; case 8: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Ah, Saradomin has granted you wisdom I see. Well, the", "story of the river Salve and of how it protects Mithsalin", "is the story of this temple,"); + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Ah, Saradomin has granted you wisdom I see. Well, the", "story of the river Salve and of how it protects Misthalin", "is the story of this temple,"); stage = 9; break; case 9: @@ -143,7 +144,7 @@ public final class DrezelDialogue extends DialoguePlugin { stage = 16; break; case 16: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "For then days and nights they fought, never sleeping,", "never eating, fuelled by their desire to make the world a", "better place for humans to live."); + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "For ten days and nights they fought, never sleeping,", "never eating, fuelled by their desire to make the world a", "better place for humans to live."); stage = 17; break; case 17: @@ -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 5af575324..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."); @@ -114,7 +115,7 @@ public final class DrezelMonumentDialogue extends DialoguePlugin { stage = 404; break; case 404: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "and it is a holy relic that pevents the werewolf people from", "chaning form, I suggest if you battle with them", "that you keep it always equipped, for their"); + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "and it is a holy relic that prevents the werewolf people from", "changing form, I suggest if you battle with them", "that you keep it always equipped, for their"); stage = 405; break; case 405: @@ -190,7 +191,7 @@ public final class DrezelMonumentDialogue extends DialoguePlugin { } player.getGameAttributes().setAttribute("/save:priest-in-peril:rune", amt); if (player.getGameAttributes().getAttribute("priest-in-peril:rune", 50) <= 0) { - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Excellent! That should do it! I will bless these stones", "and place them within the well, and Misthalin should be", "protected once more!"); + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Excellent! That should do it! I will bless these stones", "and place them within the well, and Misthalin should be", "protected once more!"); stage = 152; } else { end(); 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 b11abcc3e..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 @@ -139,7 +140,7 @@ public class PriestInPeril extends Quest { player.getPacketDispatch().sendString("used Holy Water to trap the vampire in his coffin.", 275, 15+ 7); player.getPacketDispatch().sendString("I followed Drezel downstairs only to find that the Salve", 275, 16+ 7); player.getPacketDispatch().sendString("had been contaminated and now needed purifying", 275, 17+ 7); - int amt = player.getGameAttributes().getAttribute("priest-in-peril:rune", 50+ 7); + int amt = player.getGameAttributes().getAttribute("priest-in-peril:rune", 50); player.getPacketDispatch().sendString("I need to bring " + amt + " rune essence to undo the damage", 275, 19+ 7); player.getPacketDispatch().sendString("done by the Zamorakians and purify the salve", 275, 20+ 7); break; 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 a7ee74726..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,7 +127,9 @@ public class PriestInPerilOptionPlugin extends OptionHandler { } else { item = 1931; } - message = "Saradomin is the vessle that keeps our lives from harm."; + 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) { if (!player.getGameAttributes().getAttribute("priest_in_peril:feather", false)) { @@ -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)); @@ -240,7 +257,7 @@ public class PriestInPerilOptionPlugin extends OptionHandler { case 7711: /** the dog. */ if (quest.getStage(player) == 10) { - player.getPacketDispatch().sendMessage("You have no reason to attack a helpess dog!"); + player.getPacketDispatch().sendMessage("You have no reason to attack a helpless dog!"); return true; } if (quest.getStage(player) > 10) { 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/dialogue/OddOldManDialogue.kt b/Server/src/main/content/region/misthalin/silvarea/dialogue/OddOldManDialogue.kt new file mode 100644 index 000000000..6497914a2 --- /dev/null +++ b/Server/src/main/content/region/misthalin/silvarea/dialogue/OddOldManDialogue.kt @@ -0,0 +1,25 @@ +package content.region.misthalin.silvarea.dialogue + +import content.region.misthalin.silvarea.quest.ragandboneman.OddOldManDialogueFile +import core.api.openDialogue +import core.game.dialogue.DialoguePlugin +import core.game.node.entity.player.Player +import core.plugin.Initializable +import org.rs09.consts.NPCs + +@Initializable +class OddOldManDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + // Fallback to default. Always the start of Rag and Bone Man + openDialogue(player!!, OddOldManDialogueFile(), npc) + return true + } + + override fun newInstance(player: Player?): DialoguePlugin { + return OddOldManDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.ODD_OLD_MAN_3670) + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..932997b5f --- /dev/null +++ b/Server/src/main/content/region/misthalin/silvarea/quest/ragandboneman/BatBehavior.kt @@ -0,0 +1,30 @@ +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.player.Player +import core.game.node.item.Item +import core.tools.RandomFunction +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +class BatBehavior : NPCBehavior(*batIds) { + companion object { + private val batIds = intArrayOf( + NPCs.BAT_412 + ) + } + + 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, Quests.RAG_AND_BONE_MAN, 1, 99)) { + if(RandomFunction.roll(4)) { + drops.add(Item(Items.BAT_WING_7833)); + } + } + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..c489832bc --- /dev/null +++ b/Server/src/main/content/region/misthalin/silvarea/quest/ragandboneman/BearBehavior.kt @@ -0,0 +1,36 @@ +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.player.Player +import core.game.node.item.Item +import core.tools.RandomFunction +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +class BearBehavior : NPCBehavior(*bearIds) { + companion object { + private val bearIds = intArrayOf( + NPCs.GRIZZLY_BEAR_105, + NPCs.BLACK_BEAR_106, + NPCs.GRIZZLY_BEAR_1195, + NPCs.GRIZZLY_BEAR_CUB_1196, + NPCs.GRIZZLY_BEAR_CUB_1197, + NPCs.BEAR_CUB_1326, + NPCs.BEAR_CUB_1327, + ) + } + + 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, Quests.RAG_AND_BONE_MAN, 1, 99)) { + if(RandomFunction.roll(4)) { + drops.add(Item(Items.BEAR_RIBS_7815)); + } + } + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..cd4cd6c4f --- /dev/null +++ b/Server/src/main/content/region/misthalin/silvarea/quest/ragandboneman/BigFrogBehavior.kt @@ -0,0 +1,30 @@ +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.player.Player +import core.game.node.item.Item +import core.tools.RandomFunction +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +class BigFrogBehavior : NPCBehavior(*bigFrogIds) { + companion object { + private val bigFrogIds = intArrayOf( + NPCs.BIG_FROG_1829, + ) + } + + 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, Quests.RAG_AND_BONE_MAN, 1, 99)) { + if(RandomFunction.roll(4)) { + drops.add(Item(Items.BIG_FROG_LEG_7908)); + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misthalin/silvarea/quest/ragandboneman/BoneBoilerEnum.kt b/Server/src/main/content/region/misthalin/silvarea/quest/ragandboneman/BoneBoilerEnum.kt new file mode 100644 index 000000000..337a6c06d --- /dev/null +++ b/Server/src/main/content/region/misthalin/silvarea/quest/ragandboneman/BoneBoilerEnum.kt @@ -0,0 +1,65 @@ +package content.region.misthalin.silvarea.quest.ragandboneman + +import org.rs09.consts.Items + +enum class BoneBoilerEnum(val bone: Int, val boneInVinegar: Int, val polishedBone: Int, val boneDescription: String) { + GOBLIN_SKULL(Items.GOBLIN_SKULL_7812, Items.BONE_IN_VINEGAR_7813, Items.GOBLIN_SKULL_7814, "goblin bone"), + BEAR_RIBS(Items.BEAR_RIBS_7815, Items.BONE_IN_VINEGAR_7816, Items.BEAR_RIBS_7817, "bear bone"), + RAM_SKULL(Items.RAM_SKULL_7818, Items.BONE_IN_VINEGAR_7819, Items.RAM_SKULL_7820, "ram bone"), + UNICORN_BONE(Items.UNICORN_BONE_7821, Items.BONE_IN_VINEGAR_7822, Items.UNICORN_BONE_7823, "unicorn bone"), + GIANT_RAT_BONE(Items.GIANT_RAT_BONE_7824, Items.BONE_IN_VINEGAR_7825, Items.GIANT_RAT_BONE_7826, "giant rat bone"), + GIANT_BAT_WING(Items.GIANT_BAT_WING_7827, Items.BONE_IN_VINEGAR_7828, Items.GIANT_BAT_WING_7829, "giant bat bone"), + WOLF_BONE(Items.WOLF_BONE_7830, Items.BONE_IN_VINEGAR_7831, Items.WOLF_BONE_7832, "wolf bone"), + BAT_WING(Items.BAT_WING_7833, Items.BONE_IN_VINEGAR_7834, Items.BAT_WING_7835, "bat bone"), + RAT_BONE(Items.RAT_BONE_7836, Items.BONE_IN_VINEGAR_7837, Items.RAT_BONE_7838, "rat bone"), + BABY_DRAGON_BONE(Items.BABY_DRAGON_BONE_7839, Items.BONE_IN_VINEGAR_7840, Items.BABY_DRAGON_BONE_7841, "baby blue dragon bone"), + OGRE_RIBS(Items.OGRE_RIBS_7842, Items.BONE_IN_VINEGAR_7843, Items.OGRE_RIBS_7844, "ogre bone"), + JOGRE_BONE(Items.JOGRE_BONE_7845, Items.BONE_IN_VINEGAR_7846, Items.JOGRE_BONE_7847, "jogre bone"), + ZOGRE_BONE(Items.ZOGRE_BONE_7848, Items.BONE_IN_VINEGAR_7849, Items.ZOGRE_BONE_7850, "zogre bone"), + MOGRE_BONE(Items.MOGRE_BONE_7851, Items.BONE_IN_VINEGAR_7852, Items.MOGRE_BONE_7853, "mogre bone"), + MONKEY_PAW(Items.MONKEY_PAW_7854, Items.BONE_IN_VINEGAR_7855, Items.MONKEY_PAW_7856, "monkey bone"), + DAGANNOTH_RIBS(Items.DAGANNOTH_RIBS_7857, Items.BONE_IN_VINEGAR_7858, Items.DAGANNOTH_RIBS_7859, "Dagannoth bone"), + SNAKE_SPINE(Items.SNAKE_SPINE_7860, Items.BONE_IN_VINEGAR_7861, Items.SNAKE_SPINE_7862, "snake bone"), + ZOMBIE_BONE(Items.ZOMBIE_BONE_7863, Items.BONE_IN_VINEGAR_7864, Items.ZOMBIE_BONE_7865, "zombie bone"), + WEREWOLF_BONE(Items.WEREWOLF_BONE_7866, Items.BONE_IN_VINEGAR_7867, Items.WEREWOLF_BONE_7868, "werewolf bone"), + MOSS_GIANT_BONE(Items.MOSS_GIANT_BONE_7869, Items.BONE_IN_VINEGAR_7870, Items.MOSS_GIANT_BONE_7871, "moss giant bone"), + FIRE_GIANT_BONE(Items.FIRE_GIANT_BONE_7872, Items.BONE_IN_VINEGAR_7873, Items.FIRE_GIANT_BONE_7874, "fire giant bone"), + ICE_GIANT_RIBS(Items.ICE_GIANT_RIBS_7875, Items.BONE_IN_VINEGAR_7876, Items.ICE_GIANT_RIBS_7877, "ice giant bone"), + TERRORBIRD_WING(Items.TERRORBIRD_WING_7878, Items.BONE_IN_VINEGAR_7879, Items.TERRORBIRD_WING_7880, "terrorbird bone"), + GHOUL_BONE(Items.GHOUL_BONE_7881, Items.BONE_IN_VINEGAR_7882, Items.GHOUL_BONE_7883, "ghoul bone"), + TROLL_BONE(Items.TROLL_BONE_7884, Items.BONE_IN_VINEGAR_7885, Items.TROLL_BONE_7886, "troll bone"), + SEAGULL_WING(Items.SEAGULL_WING_7887, Items.BONE_IN_VINEGAR_7888, Items.SEAGULL_WING_7889, "seagull bone"), + UNDEAD_COW_RIBS(Items.UNDEAD_COW_RIBS_7890, Items.BONE_IN_VINEGAR_7891, Items.UNDEAD_COW_RIBS_7892, "undead cow bone"), + EXPERIMENT_BONE(Items.EXPERIMENT_BONE_7893, Items.BONE_IN_VINEGAR_7894, Items.EXPERIMENT_BONE_7895, "experiment bone"), + RABBIT_BONE(Items.RABBIT_BONE_7896, Items.BONE_IN_VINEGAR_7897, Items.RABBIT_BONE_7898, "rabbit bone"), + BASILISK_BONE(Items.BASILISK_BONE_7899, Items.BONE_IN_VINEGAR_7900, Items.BASILISK_BONE_7901, "basilisk bone"), + DESERT_LIZARD_BONE(Items.DESERT_LIZARD_BONE_7902, Items.BONE_IN_VINEGAR_7903, Items.DESERT_LIZARD_BONE_7904, "desert lizard bone"), + CAVE_GOBLIN_SKULL(Items.CAVE_GOBLIN_SKULL_7905, Items.BONE_IN_VINEGAR_7906, Items.CAVE_GOBLIN_SKULL_7907, "cave goblin bone"), + BIG_FROG_LEG(Items.BIG_FROG_LEG_7908, Items.BONE_IN_VINEGAR_7909, Items.BIG_FROG_LEG_7910, "big frog bone"), + VULTURE_WING(Items.VULTURE_WING_7911, Items.BONE_IN_VINEGAR_7912, Items.VULTURE_WING_7913, "vulture bone"), + JACKAL_BONE(Items.JACKAL_BONE_7914, Items.BONE_IN_VINEGAR_7915, Items.JACKAL_BONE_7916, "jackal bone"); + + companion object { + @JvmField + val boneList = BoneBoilerEnum.values().map { it.bone }.toIntArray() + + @JvmField + val boneInVinegarList = BoneBoilerEnum.values().map { it.boneInVinegar }.toIntArray() + + @JvmField + val boneMap = values().associateBy { it.bone } + + @JvmField + val boneInVinegarMap = values().associateBy { it.boneInVinegar } + + @JvmStatic + fun forBone(id: Int): BoneBoilerEnum? { + return boneMap[id] + } + + @JvmStatic + fun forBoneInVinegar(id: Int): BoneBoilerEnum? { + return boneInVinegarMap[id] + } + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..0ffe43400 --- /dev/null +++ b/Server/src/main/content/region/misthalin/silvarea/quest/ragandboneman/GiantBatBehavior.kt @@ -0,0 +1,39 @@ +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.player.Player +import core.game.node.item.Item +import core.tools.RandomFunction +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +class GiantBatBehavior : NPCBehavior(*giantBatIds) { + companion object { + private val giantBatIds = intArrayOf( + NPCs.GIANT_BAT_78, + NPCs.GIANT_BAT_1005, + NPCs.GIANT_BAT_2482, + NPCs.GIANT_BAT_2483, + NPCs.GIANT_BAT_2484, + NPCs.GIANT_BAT_2485, + NPCs.GIANT_BAT_2486, + NPCs.GIANT_BAT_2487, + NPCs.GIANT_BAT_2488, + NPCs.GIANT_BAT_3711, + ) + } + + 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, Quests.RAG_AND_BONE_MAN, 1, 99)) { + if(RandomFunction.roll(4)) { + drops.add(Item(Items.GIANT_BAT_WING_7827)); + } + } + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..b670f24de --- /dev/null +++ b/Server/src/main/content/region/misthalin/silvarea/quest/ragandboneman/GiantRatBehavior.kt @@ -0,0 +1,44 @@ +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.player.Player +import core.game.node.item.Item +import core.tools.RandomFunction +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +class GiantRatBehavior : NPCBehavior(*giantRatIds) { + companion object { + private val giantRatIds = intArrayOf( + NPCs.GIANT_RAT_4395, + NPCs.GIANT_RAT_446, + NPCs.GIANT_RAT_4922, + NPCs.GIANT_RAT_4923, + NPCs.GIANT_RAT_4924, + NPCs.GIANT_RAT_4925, + NPCs.GIANT_RAT_4926, + NPCs.GIANT_RAT_4927, + NPCs.GIANT_RAT_4942, + NPCs.GIANT_RAT_4943, + NPCs.GIANT_RAT_4944, + NPCs.GIANT_RAT_4945, + NPCs.GIANT_RAT_86, + NPCs.GIANT_RAT_87, + NPCs.GIANT_RAT_950, + ) + } + + 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, Quests.RAG_AND_BONE_MAN, 1, 99)) { + if(RandomFunction.roll(4)) { + drops.add(Item(Items.GIANT_RAT_BONE_7824)); + } + } + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..923b9a5ef --- /dev/null +++ b/Server/src/main/content/region/misthalin/silvarea/quest/ragandboneman/GoblinBehavior.kt @@ -0,0 +1,187 @@ +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.player.Player +import core.game.node.item.Item +import core.tools.RandomFunction +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +class GoblinBehavior : NPCBehavior(*goblinIds) { + companion object { + private val goblinIds = intArrayOf( + NPCs.GOBLIN_100, + NPCs.GOBLIN_101, + NPCs.GOBLIN_102, + NPCs.GOBLIN_1769, + NPCs.GOBLIN_1770, + NPCs.GOBLIN_1771, + NPCs.GOBLIN_1772, + NPCs.GOBLIN_1773, + NPCs.GOBLIN_1774, + NPCs.GOBLIN_1775, + NPCs.GOBLIN_1776, + NPCs.GOBLIN_2274, + NPCs.GOBLIN_2275, + NPCs.GOBLIN_2276, + NPCs.GOBLIN_2277, + NPCs.GOBLIN_2278, + NPCs.GOBLIN_2279, + NPCs.GOBLIN_2280, + NPCs.GOBLIN_2281, + NPCs.GOBLIN_2678, + NPCs.GOBLIN_2679, + NPCs.GOBLIN_2680, + NPCs.GOBLIN_2681, + NPCs.GOBLIN_3264, + NPCs.GOBLIN_3265, + NPCs.GOBLIN_3266, + NPCs.GOBLIN_3267, + NPCs.GOBLIN_3726, + NPCs.GOBLIN_4261, + NPCs.GOBLIN_4262, + NPCs.GOBLIN_4263, + NPCs.GOBLIN_4264, + NPCs.GOBLIN_4265, + NPCs.GOBLIN_4266, + NPCs.GOBLIN_4267, + NPCs.GOBLIN_4268, + NPCs.GOBLIN_4269, + NPCs.GOBLIN_4270, + NPCs.GOBLIN_4271, + NPCs.GOBLIN_4272, + NPCs.GOBLIN_4273, + NPCs.GOBLIN_4274, + NPCs.GOBLIN_4275, + NPCs.GOBLIN_4276, + NPCs.GOBLIN_4407, + NPCs.GOBLIN_4408, + NPCs.GOBLIN_4409, + NPCs.GOBLIN_4410, + NPCs.GOBLIN_4411, + NPCs.GOBLIN_4412, + NPCs.GOBLIN_444, + NPCs.GOBLIN_445, + NPCs.GOBLIN_4479, + NPCs.GOBLIN_4480, + NPCs.GOBLIN_4481, + NPCs.GOBLIN_4482, + NPCs.GOBLIN_4483, + NPCs.GOBLIN_4484, + NPCs.GOBLIN_4485, + NPCs.GOBLIN_4486, + NPCs.GOBLIN_4487, + NPCs.GOBLIN_4488, + NPCs.GOBLIN_4489, + NPCs.GOBLIN_4490, + NPCs.GOBLIN_4491, + NPCs.GOBLIN_4492, + NPCs.GOBLIN_4499, + NPCs.GOBLIN_4633, + NPCs.GOBLIN_4634, + NPCs.GOBLIN_4635, + NPCs.GOBLIN_4636, + NPCs.GOBLIN_4637, + NPCs.GOBLIN_5855, + NPCs.GOBLIN_5856, + NPCs.GOBLIN_6125, + NPCs.GOBLIN_6126, + NPCs.GOBLIN_6132, + NPCs.GOBLIN_6133, + NPCs.GOBLIN_6279, + NPCs.GOBLIN_6280, + NPCs.GOBLIN_6281, + NPCs.GOBLIN_6282, + NPCs.GOBLIN_6283, + NPCs.GOBLIN_6284, + NPCs.GOBLIN_6402, + NPCs.GOBLIN_6403, + NPCs.GOBLIN_6404, + NPCs.GOBLIN_6405, + NPCs.GOBLIN_6406, + NPCs.GOBLIN_6407, + NPCs.GOBLIN_6408, + NPCs.GOBLIN_6409, + NPCs.GOBLIN_6410, + NPCs.GOBLIN_6411, + NPCs.GOBLIN_6412, + NPCs.GOBLIN_6413, + NPCs.GOBLIN_6414, + NPCs.GOBLIN_6415, + NPCs.GOBLIN_6416, + NPCs.GOBLIN_6417, + NPCs.GOBLIN_6418, + NPCs.GOBLIN_6419, + NPCs.GOBLIN_6420, + NPCs.GOBLIN_6421, + NPCs.GOBLIN_6422, + NPCs.GOBLIN_6423, + NPCs.GOBLIN_6424, + NPCs.GOBLIN_6425, + NPCs.GOBLIN_6426, + NPCs.GOBLIN_6427, + NPCs.GOBLIN_6428, + NPCs.GOBLIN_6429, + NPCs.GOBLIN_6430, + NPCs.GOBLIN_6431, + NPCs.GOBLIN_6432, + NPCs.GOBLIN_6433, + NPCs.GOBLIN_6434, + NPCs.GOBLIN_6435, + NPCs.GOBLIN_6436, + NPCs.GOBLIN_6437, + NPCs.GOBLIN_6438, + NPCs.GOBLIN_6439, + NPCs.GOBLIN_6440, + NPCs.GOBLIN_6441, + NPCs.GOBLIN_6442, + NPCs.GOBLIN_6443, + NPCs.GOBLIN_6444, + NPCs.GOBLIN_6445, + NPCs.GOBLIN_6446, + NPCs.GOBLIN_6447, + NPCs.GOBLIN_6448, + NPCs.GOBLIN_6449, + NPCs.GOBLIN_6450, + NPCs.GOBLIN_6451, + NPCs.GOBLIN_6452, + NPCs.GOBLIN_6453, + NPCs.GOBLIN_6454, + NPCs.GOBLIN_6455, + NPCs.GOBLIN_6456, + NPCs.GOBLIN_6457, + NPCs.GOBLIN_6458, + NPCs.GOBLIN_6459, + NPCs.GOBLIN_6460, + NPCs.GOBLIN_6461, + NPCs.GOBLIN_6462, + NPCs.GOBLIN_6463, + NPCs.GOBLIN_6464, + NPCs.GOBLIN_6465, + NPCs.GOBLIN_6466, + NPCs.GOBLIN_6467, + NPCs.GOBLIN_6490, + NPCs.GOBLIN_6491, + NPCs.GOBLIN_6492, + NPCs.GOBLIN_6493, + NPCs.GOBLIN_6494, + NPCs.GOBLIN_6495, + NPCs.GOBLIN_7964, + NPCs.GOBLIN_7965, + ) + } + + 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, Quests.RAG_AND_BONE_MAN, 1, 99)) { + if(RandomFunction.roll(4)) { + drops.add(Item(Items.GOBLIN_SKULL_7812)) + } + } + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..2646a8676 --- /dev/null +++ b/Server/src/main/content/region/misthalin/silvarea/quest/ragandboneman/MonkeyBehavior.kt @@ -0,0 +1,54 @@ +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.player.Player +import core.game.node.item.Item +import core.tools.RandomFunction +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +class MonkeyBehavior : NPCBehavior(*monkeyIds) { + companion object { + private val monkeyIds = intArrayOf( + NPCs.MONKEY_132, + NPCs.MONKEY_1463, + NPCs.MONKEY_1464, + NPCs.MONKEY_1487, + NPCs.MONKEY_2301, + NPCs.MONKEY_2302, + NPCs.MONKEY_2303, + NPCs.MONKEY_4344, + NPCs.MONKEY_4363, + NPCs.MONKEY_5852, + NPCs.MONKEY_5853, + NPCs.MONKEY_6943, + NPCs.MONKEY_7211, + NPCs.MONKEY_7213, + NPCs.MONKEY_7215, + NPCs.MONKEY_7217, + NPCs.MONKEY_7219, + NPCs.MONKEY_7221, + NPCs.MONKEY_7223, + NPCs.MONKEY_7225, + NPCs.MONKEY_7227, + NPCs.MONKEY_ZOMBIE_1465, + NPCs.MONKEY_ZOMBIE_1466, + NPCs.MONKEY_ZOMBIE_1467, + ) + } + + + 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, Quests.RAG_AND_BONE_MAN, 1, 99)) { + if(RandomFunction.roll(4)) { + drops.add(Item(Items.MONKEY_PAW_7854)); + } + } + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..9351b6c1b --- /dev/null +++ b/Server/src/main/content/region/misthalin/silvarea/quest/ragandboneman/OddOldManDialogueFile.kt @@ -0,0 +1,233 @@ +package content.region.misthalin.silvarea.quest.ragandboneman + +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.player.Player +import core.tools.END_DIALOGUE +import core.tools.START_DIALOGUE +import org.rs09.consts.NPCs + +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!!, Quests.RAG_AND_BONE_MAN)) { + 0 -> { + when (stage) { + START_DIALOGUE -> npcl(FacialExpression.FRIENDLY, "Can I help you with something?").also { stage++ } + 1 -> playerl(FacialExpression.FRIENDLY, "Well, err...who are you, and what are all these bones doing here?").also { stage++ } + 2 -> npcl(FacialExpression.FRIENDLY, "Errr...").also { stage++ } + 3 -> npcl(NPCs.BONES_3674, "Sack", FacialExpression.OLD_HAPPY, "Mumblemumble").also { stage++ } + 4 -> npcl(FacialExpression.FRIENDLY, "I'm an archaeologist. I work with the museum.").also { stage++ } + 5 -> playerl(FacialExpression.FRIENDLY, "An archaeologist?").also { stage++ } + 6 -> npcl(FacialExpression.FRIENDLY, "Yes.").also { stage++ } + 7 -> playerl(FacialExpression.FRIENDLY, "Well that explains the bones...sort of...but what are you doing all the way out here?").also { stage++ } + 8 -> npcl(FacialExpression.FRIENDLY, "Errr...").also { stage++ } + 9 -> npcl(NPCs.BONES_3674, "Sack", FacialExpression.OLD_HAPPY, "Mumblemumble. Mumblemumblemumble.").also { stage++ } + 10 -> npcl(FacialExpression.FRIENDLY, "I'm collecting bones for the museum.").also { stage++ } + 11 -> npcl(FacialExpression.FRIENDLY, "They have asked me to rig up some displays of second and third age creatures using their bones, so that people can come and...well, look at them.").also { stage++ } + 12 -> npcl(FacialExpression.FRIENDLY, "I need to get them into some sort of order before I begin, but I've run into a bit of a snag.").also { stage++ } + 13 -> playerl(FacialExpression.FRIENDLY, "What sort of a snag?").also { stage++ } + 14 -> npcl(FacialExpression.FRIENDLY, "Well, I need to have all the bones I'm going to use here, and placed into some sort of order.").also { stage++ } + 15 -> npcl(FacialExpression.FRIENDLY, "However, I seem to have discovered I am a few short.").also { stage++ } + 16 -> showTopics( + Topic("Anything I can do to help?", 20, true), + Topic("Well, good luck with that.", 70), + Topic("Where is that mumbling coming from?", 80) + ) + 20 -> playerl(FacialExpression.FRIENDLY, "Anything I can do to help?").also { stage++ } + 21 -> npcl(FacialExpression.FRIENDLY, "Errr...").also { stage++ } + 22 -> npcl(NPCs.BONES_3674, "Sack", FacialExpression.OLD_HAPPY, "Mumblemumble").also { stage++ } + 23 -> npcl(FacialExpression.FRIENDLY, "There is something you could do for me. I'm going to be busy...err...").also { stage++ } + 24 -> npcl(NPCs.BONES_3674, "Sack", FacialExpression.OLD_HAPPY, "Mumble").also { stage++ } + 25 -> npcl(FacialExpression.FRIENDLY, "Sorting, yes, sorting these bones out ready for the museum, but I need a few more.").also { stage++ } + 26 -> npcl(FacialExpression.FRIENDLY, "Will you help me out by grabbing some?").also { stage++ } + 27 -> showTopics( + Topic(FacialExpression.FRIENDLY, "Yes", 28, true), + Topic(FacialExpression.FRIENDLY, "No", 60), + Topic("Where is that mumbling coming from?", 80) + ) + 28 -> playerl(FacialExpression.FRIENDLY, "Yes. I'll give you a hand.").also { stage++ } + 29 -> npcl(FacialExpression.FRIENDLY, "You will? Excellent!").also { stage++ } + 30 -> npcl(NPCs.BONES_3674, "Sack", FacialExpression.OLD_HAPPY, "Sniggersnigger").also { stage++ } + 31 -> playerl(FacialExpression.FRIENDLY, "Where do you need me to dig?").also { stage++ } + 32 -> npcl(FacialExpression.FRIENDLY, "Dig?").also { stage++ } + 33 -> npcl(NPCs.BONES_3674, "Sack", FacialExpression.OLD_HAPPY, "Mumblemumble").also { stage++ } + 34 -> npcl(FacialExpression.FRIENDLY, "Oh, you must have got the wrong end of the stick.").also { stage++ } + 35 -> npcl(FacialExpression.FRIENDLY, "I need some fresh, whole bones to replace ones that have become damaged.").also { stage++ } + 36 -> playerl(FacialExpression.FRIENDLY, "What?").also { stage++ } + 37 -> npcl(FacialExpression.FRIENDLY, "Err...").also { stage++ } + 38 -> npcl(NPCs.BONES_3674, "Sack", FacialExpression.OLD_HAPPY, "Mumblemumblemumblemumblemumble. Mumblemumblemumblemumble. Mumble.").also { stage++ } + 39 -> playerl(FacialExpression.FRIENDLY, "Excuse me...").also { stage++ } + 40 -> npcl(FacialExpression.FRIENDLY, "Shhh!").also { stage++ } + 41 -> playerl(FacialExpression.FRIENDLY, "Sorry...").also { stage++ } + 42 -> npcl(NPCs.BONES_3674, "Sack", FacialExpression.OLD_HAPPY, "Mumblemumble. Mumblemumblemumble.").also { stage++ } + 43 -> npcl(FacialExpression.FRIENDLY, "Ok, got it.").also { stage++ } + 44 -> npcl(FacialExpression.FRIENDLY, "Ok, here is the thing. While sorting out what bones I do have I managed to lose or damage a few. If you can get me some fresh, unbroken bones to use as replacements then I can get on with things.").also { stage++ } + 45 -> npcl(FacialExpression.FRIENDLY, "That make things clearer?").also { stage++ } + 46 -> playerl(FacialExpression.FRIENDLY, "Well, it makes some sense I suppose...").also { stage++ } + 47 -> npcl(FacialExpression.FRIENDLY, "Great. If you can get me a bone from a Goblin, a Bear, a Big Frog, a Ram, a Unicorn, a Monkey, a Giant rat and a Giant Bat then I'll be able to move on with the...").also { stage++ } + 48 -> npcl(FacialExpression.FRIENDLY, "Displays...").also { stage++ } + 49 -> playerl(FacialExpression.FRIENDLY, "So you just want me to bring you these bones and that will be that?").also { stage++ } + 50 -> npcl(FacialExpression.FRIENDLY, "Well, I wouldn't mind you sticking them in a pot and boiling them in vinegar first, if you don't mind.").also { stage++ } + 51 -> npcl(FacialExpression.FRIENDLY, "There is a Wine Merchant in Draynor called Fortunato that sells the stuff you'll need.").also { stage++ } + 52 -> npcl(FacialExpression.FRIENDLY, "You can even use my pot-boiler if you want.").also { stage++ } + 53 -> playerl(FacialExpression.FRIENDLY, "Why do I need to boil them in vinegar?").also { stage++ } + 54 -> npcl(FacialExpression.FRIENDLY, "It gets them bright and sparking white.").also { stage++ } + 55 -> npcl(FacialExpression.FRIENDLY, "It's an archaeologist thing.").also { stage++ } + 56 -> npcl(FacialExpression.FRIENDLY, "Just put the bone in a pot of vinegar, throw some logs on the fire, put the pot in the holder and light the logs.").also { stage++ } + 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!!, Quests.RAG_AND_BONE_MAN, 1) + stage = END_DIALOGUE + } + + 60 -> npcl(FacialExpression.FRIENDLY, "Oh...I see.").also { stage++ } + 61 -> npcl(FacialExpression.FRIENDLY, "Well, never mind me young man, I'll just stagger over here under my massive burden, and continue my thankless task.").also { stage++ } + 62 -> npcl(FacialExpression.FRIENDLY, "Unaided and alone...").also { stage++ } + 63 -> playerl(FacialExpression.FRIENDLY, "Wow, trying a guilt trip much?").also { + stage = END_DIALOGUE + } + + 70 -> npcl(FacialExpression.FRIENDLY, "Thanks stranger!").also { stage++ } + 71 -> npcl(FacialExpression.FRIENDLY, "What a polite young man...").also { stage++ } + 72 -> npcl(FacialExpression.FRIENDLY, "Well, back to work!").also { + stage = END_DIALOGUE + } + + 80 -> npcl(FacialExpression.FRIENDLY, "Errr...").also { stage++ } + 81 -> npcl(NPCs.BONES_3674, "Sack", FacialExpression.OLD_HAPPY, "Mumblemumble").also { stage++ } + 82 -> npcl(FacialExpression.FRIENDLY, "What mumbling?").also { stage++ } + 83 -> npcl(FacialExpression.FRIENDLY, "Anyway, I have enough problems of my own without dealing with a delusional adventurer.").also { + stage = 16 + } + } + } + in 1 .. 3 -> { + when(stage) { + START_DIALOGUE -> { + if (checkBonesInInventory(player!!)) { + playerl(FacialExpression.FRIENDLY, "I have some bones for you...").also { stage = 1 } + } else { + npcl(FacialExpression.FRIENDLY, "Have you brought me any bones?").also { stage = 20 } + } + } + 1 -> npcl(FacialExpression.FRIENDLY, "Great! Let me take a look at them.").also { + stage++ + } + 2 -> { + submitBonesInInventory(player!!) + if (hasAllBones(player!!)) { + npcl(FacialExpression.FRIENDLY, "That's the last of them!").also { stage = 4 } + } else { + npcl(FacialExpression.FRIENDLY, "Thanks the museum will be so pleased.").also { stage = 3 } + } + } + 3 -> npcl(FacialExpression.FRIENDLY, "Come and see me when you have the rest.").also { + stage = END_DIALOGUE + } + 4 -> npcl(FacialExpression.FRIENDLY, "The museum will be thrilled to know I've completed the collection.").also { stage++ } + 5 -> playerl(FacialExpression.FRIENDLY, "Well I'm just glad I could help.").also { stage++ } + 6 -> npcl(NPCs.BONES_3674, "Sack", FacialExpression.OLD_HAPPY, "Mumblemumble").also { stage++ } + 7 -> npcl(FacialExpression.FRIENDLY, "Well you've been a big help and no mistake.").also { stage++ } + 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!!, 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++ } + 21 -> npcl(FacialExpression.FRIENDLY, "Sure.").also {stage = 30 } + 30, 40, 50, 60, 70, 80, 90, 100, 110 -> { + if (!getAttribute(player!!, RagAndBoneMan.attributeGoblinBone, false) && stage <= 30) { npcl(NPCs.BONES_3674, "Sack", FacialExpression.OLD_HAPPY, "Mumblemumble"); stage = 31 } + else if (!getAttribute(player!!, RagAndBoneMan.attributeBearBone, false) && stage <= 40) { npcl(NPCs.BONES_3674, "Sack", FacialExpression.OLD_HAPPY, "Mumblemumble"); stage = 41 } + else if (!getAttribute(player!!, RagAndBoneMan.attributeBigFrogBone, false) && stage <= 50) { npcl(NPCs.BONES_3674, "Sack", FacialExpression.OLD_HAPPY, "Mumblemumble"); stage = 51 } + else if (!getAttribute(player!!, RagAndBoneMan.attributeRamBone, false) && stage <= 60) { npcl(NPCs.BONES_3674, "Sack", FacialExpression.OLD_HAPPY, "Mumblemumble"); stage = 61 } + else if (!getAttribute(player!!, RagAndBoneMan.attributeUnicornBone, false) && stage <= 70) { npcl(NPCs.BONES_3674, "Sack", FacialExpression.OLD_HAPPY, "Mumblemumble"); stage = 71 } + else if (!getAttribute(player!!, RagAndBoneMan.attributeMonkeyBone, false) && stage <= 80) { npcl(NPCs.BONES_3674, "Sack", FacialExpression.OLD_HAPPY, "Mumblemumble"); stage = 81 } + else if (!getAttribute(player!!, RagAndBoneMan.attributeGiantRatBone, false) && stage <= 90) { npcl(NPCs.BONES_3674, "Sack", FacialExpression.OLD_HAPPY, "Mumblemumble"); stage = 91 } + else if (!getAttribute(player!!, RagAndBoneMan.attributeGiantBatBone, false) && stage <= 100) { npcl(NPCs.BONES_3674, "Sack", FacialExpression.OLD_HAPPY, "Mumblemumble"); stage = 101 } + else { + npcl(FacialExpression.FRIENDLY, "Did you get all that?") + stage = 111 + } + } + 31 -> npcl(FacialExpression.FRIENDLY, "You still need to bring me a Goblin bone.").also { stage++ } + 32 -> npcl(FacialExpression.FRIENDLY, "Goblins are relatively common. I hear there is a house full of them around Lumbridge in fact.").also { stage = 40 } + + 41 -> npcl(FacialExpression.FRIENDLY, "You still need to bring me a Bear bone.").also { stage++ } + 42 -> npcl(FacialExpression.FRIENDLY, "I heard that there are some Bears over by the Legends' Guild, near Ardougne.").also { stage = 50 } + + 51 -> npcl(FacialExpression.FRIENDLY, "You still need to bring me a Big Frog bone.").also { stage++ } + 52 -> npcl(FacialExpression.FRIENDLY, "This might be a little tricky as you will need to go into the Lumbridge Swamp caves. You will need a light source! Never forget your light source down there!").also { stage = 60 } + + 61 -> npcl(FacialExpression.FRIENDLY, "You still need to bring me a Ram bone.").also { stage++ } + 62 -> npcl(FacialExpression.FRIENDLY, "I'm sure you will be able to find a ram wherever there are sheep.").also { stage = 70 } + + 71 -> npcl(FacialExpression.FRIENDLY, "You still need to bring me a Unicorn bone.").also { stage++ } + 72 -> npcl(FacialExpression.FRIENDLY, "I seem to remember that there were Unicorns south of Varrock, I think they might be there still.").also { stage = 80 } + + 81 -> npcl(FacialExpression.FRIENDLY, "You still need to bring me a Monkey bone.").also { stage++ } + 82 -> npcl(FacialExpression.FRIENDLY, "Monkeys tend to live in Jungle areas, like Karamja. I think they are pretty plentiful there if I remember correctly.").also { stage = 90 } + + 91 -> npcl(FacialExpression.FRIENDLY, "You still need to bring me a Giant Rat bone.").also { stage++ } + 92 -> npcl(FacialExpression.FRIENDLY, "If you can't find one in a sewer, then you might want to try looking in some caves.").also { stage = 100 } + + 101 -> npcl(FacialExpression.FRIENDLY, "You still need to bring me a Giant Bat bone.").also { stage++ } + 102 -> npcl(FacialExpression.FRIENDLY, "Giant bats tend to live underground, but I have heard there are a few near the Coal Pits.").also { stage = 110 } + + 111 -> playerl(FacialExpression.FRIENDLY, "Yes. I'll get right on it.").also { stage++ } + 112 -> npcl(NPCs.BONES_3674, "Sack", FacialExpression.OLD_HAPPY, "Mumblemumble").also { stage++ } + 113 -> npcl(FacialExpression.FRIENDLY, "Don't forget to boil them in vinegar first.").also { stage++ } + 114 -> npcl(FacialExpression.FRIENDLY, "Just chuck some logs into the pit, put the bone in the pot of vinegar and drop it onto the pot-boiler. Then light the logs and wait for the the vinegar to boil away.").also { stage++ } + 115 -> playerl(FacialExpression.FRIENDLY, "Ok, I'll remember.").also { + stage = END_DIALOGUE + } + } + } + } + /* + Unfortunately from OSRS 2016: https://www.youtube.com/watch?v=31QNg1E0qf0 + Wonderful! I'll put this with the rest. + No problem. I'll be back if I find some more. + + This one is closest RS 2012: https://www.youtube.com/watch?v=0I8fNTeAwA8 + I have some bones for you... + Great! Let me take a look at them. + Thanks the museum will be so pleased. + Come and see me when you have the rest. + */ + } + + private fun checkBonesInInventory(player: Player) : Boolean { + var hasBone = false + RagAndBoneMan.requiredBonesMap.forEach { + if (inInventory(player, it.key)) { + hasBone = true + } + } + return hasBone + } + + private fun submitBonesInInventory(player: Player) { + RagAndBoneMan.requiredBonesMap.forEach { + if (!getAttribute(player, it.value, false) && removeItem(player, it.key)) { + setAttribute(player, it.value, true) + } + } + } + + private fun hasAllBones(player: Player) : Boolean { + var hasBoneAllBones = true + RagAndBoneMan.requiredBonesMap.values.forEach { + if (!getAttribute(player, it, false)) { + hasBoneAllBones = false + } + } + return hasBoneAllBones + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..97e5e9a35 --- /dev/null +++ b/Server/src/main/content/region/misthalin/silvarea/quest/ragandboneman/RagAndBoneMan.kt @@ -0,0 +1,145 @@ +package content.region.misthalin.silvarea.quest.ragandboneman + +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 + +/** + * Rag and Bone Man Quest + * @author ovenbread + * + * Do note: + * Before, the Rag and Bone Man WISHLIST(a.k.a part 2) used to reward 1 quest point, + * thus required to wear the quest cape. + * This was changed on 11 August 2009. + * Rag and Bone Man Quest now rewards 2 quest points. + * The follow-up hidden quest called Rag and Bone Man WISHLIST is not a quest cape req. + * + * https://www.youtube.com/watch?v=6tdNqNa4zGw + * https://www.youtube.com/watch?v=aVyBzqyuPYs + * + * Quest Journal 2012 https://www.youtube.com/watch?v=0I8fNTeAwA8&t=764 + */ +@Initializable +class RagAndBoneMan : Quest(Quests.RAG_AND_BONE_MAN,100, 99, 2, 714, 0, 1, 4) { + companion object { + const val attributeGoblinBone = "/save:quest:ragandboneman-goblinbonesubmit" + const val attributeBearBone = "/save:quest:ragandboneman-bearbonesubmit" + const val attributeBigFrogBone = "/save:quest:ragandboneman-bigfrogbonesubmit" + const val attributeRamBone = "/save:quest:ragandboneman-rambonesubmit" + const val attributeUnicornBone = "/save:quest:ragandboneman-unicornbonesubmit" + const val attributeMonkeyBone = "/save:quest:ragandboneman-monkeybonesubmit" + const val attributeGiantRatBone = "/save:quest:ragandboneman-giantratbonesubmit" + const val attributeGiantBatBone = "/save:quest:ragandboneman-giantbatbonesubmit" + + val requiredBonesMap = mapOf( + Items.GOBLIN_SKULL_7814 to attributeGoblinBone, + Items.BEAR_RIBS_7817 to attributeBearBone, + Items.BIG_FROG_LEG_7910 to attributeBigFrogBone, + Items.RAM_SKULL_7820 to attributeRamBone, + Items.UNICORN_BONE_7823 to attributeUnicornBone, + Items.MONKEY_PAW_7856 to attributeMonkeyBone, + Items.GIANT_RAT_BONE_7826 to attributeGiantRatBone, + Items.GIANT_BAT_WING_7829 to attributeGiantBatBone + ) + } + + override fun drawJournal(player: Player?, stage: Int) { + super.drawJournal(player, stage) + var line = 12 + var stage = getStage(player) + + when (stage) { + 0 -> { + line(player, "I can start this quest by talking to the !!Odd Old Man?? to the", line++) + line(player, "West of the !!Limestone Mine??", line++) + } + in 1 .. 3 -> { + line(player, "I have spoken to the Odd Old Man and have agreed to help him ", line++, true) + line(player, "complete his collection of bones. I should check which ones", line++, true) + line(player, "he needs.", line++, true) + line++ + line(player,"The !!Odd Old Man?? has instructed me on which bones to collect", line++, false) + line(player,"and how to prepare them. I must find !!whole, unbroken bones??", line++, false) + line(player,"and put them into a !!pot of vinegar??. I must then put some", line++, false) + line(player,"!!logs?? under the !!pot boiler??, !!put the bone in vinegar on it??,", line++, false) + line(player,"!!and light the logs??. This will clean the bone.", line++, false) + line++ + line(player,"I need to buy the !!vinegar?? from the !!wine merchant?? in !!Draynor??.", line++, false) + line++ + line(player,"I need to give the !!Odd Old Man?? the following polished bones:", line++, false) + boneChecklist(player, line, "Goblin", BoneBoilerEnum.GOBLIN_SKULL, attributeGoblinBone) + line++ + boneChecklist(player, line, "Bear", BoneBoilerEnum.BEAR_RIBS, attributeBearBone) + line++ + boneChecklist(player, line, "Big frog", BoneBoilerEnum.BIG_FROG_LEG, attributeBigFrogBone) + line++ + boneChecklist(player, line, "Ram", BoneBoilerEnum.RAM_SKULL, attributeRamBone) + line++ + boneChecklist(player, line, "Unicorn", BoneBoilerEnum.UNICORN_BONE, attributeUnicornBone) + line++ + boneChecklist(player, line, "Monkey", BoneBoilerEnum.MONKEY_PAW, attributeMonkeyBone) + line++ + boneChecklist(player, line, "Giant rat", BoneBoilerEnum.GIANT_RAT_BONE, attributeGiantRatBone) + line++ + boneChecklist(player, line, "Giant bat", BoneBoilerEnum.GIANT_BAT_WING, attributeGiantBatBone) + line++ + } + 100 -> { + line(player, "I have spoken to the Odd Old Man and have agreed to help him ", line++, true) + line(player, "complete his collection of bones. I should check which ones", line++, true) + line(player, "he needs.", line++, true) + line++ + line(player,"The !!Odd Old Man?? has instructed me on which bones to collect", line++, true) + line(player,"and how to prepare them. I must find !!whole, unbroken bones??", line++, true) + line(player,"and put them into a !!pot of vinegar??. I must then put some", line++, true) + line(player,"!!logs?? under the !!pot boiler??, !!put the bone in vinegar on it??,", line++, true) + line(player,"!!and light the logs??. This will clean the bone.", line++, true) + line++ + line(player,"I have given the last of the bones to the Odd Old Man.", line++, true) + line(player,"I am sure he will reward me.", line++, true) + line++ + line(player,"The Odd Old Man has given me a reward. I will see if I can", line++, false) + line(player,"find any more bones from his wish list, and will bring them", line++, false) + line(player,"them to him if I do.", line++, false) + line++ + line(player,"%%QUEST COMPLETE!&&",line++) + } + } + } + + private fun boneChecklist(player: Player?, line: Int, mob: String, boneEnum: BoneBoilerEnum, questAttribute: String) { + line(player, + if (getAttribute(player!!, questAttribute, false)) + "!!$mob.??" + else if (inInventory(player, boneEnum.polishedBone)) + "!!$mob.?? (I have a prepared one !!with me.??)" + else if (inInventory(player, boneEnum.bone) || inInventory(player, boneEnum.boneInVinegar)) + "!!$mob.?? (I have an unprepared one !!with me.??)" + else + "!!$mob.??", + line, getAttribute(player, questAttribute, false)) + } + + override fun finish(player: Player) { + var ln = 10 + super.finish(player) + player.packetDispatch.sendString("You have completed Rag and Bone Man!", 277, 4) + player.packetDispatch.sendItemZoomOnInterface(Items.BONE_IN_VINEGAR_7813, 240, 277, 5) + + drawReward(player,"2 Quest Points", ln++) + drawReward(player,"500 Cooking XP and 500", ln++) + drawReward(player,"Prayer XP", ln++) + + player.skills.addExperience(Skills.COOKING, 500.0) + player.skills.addExperience(Skills.PRAYER, 500.0) + } + + override fun newInstance(`object`: Any?): Quest { + return this + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misthalin/silvarea/quest/ragandboneman/RagAndBoneManListeners.kt b/Server/src/main/content/region/misthalin/silvarea/quest/ragandboneman/RagAndBoneManListeners.kt new file mode 100644 index 000000000..72119a2b9 --- /dev/null +++ b/Server/src/main/content/region/misthalin/silvarea/quest/ragandboneman/RagAndBoneManListeners.kt @@ -0,0 +1,102 @@ +package content.region.misthalin.silvarea.quest.ragandboneman + +import core.api.* +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.impl.PulseType +import core.game.system.task.Pulse +import org.rs09.consts.Animations +import org.rs09.consts.Items +import org.rs09.consts.Scenery + +class RagAndBoneManListeners : InteractionListener { + + companion object { + const val ATTRIBUTE_ACTIVE_POT_OF_VINEGAR = "ragandboneman:potofvinegar" + } + + override fun defineListeners() { + // Pouring vinegar from jug to pot. + onUseWith(IntType.ITEM, Items.JUG_OF_VINEGAR_7810, Items.EMPTY_POT_1931) { player, _, _ -> + if (removeItem(player, Items.JUG_OF_VINEGAR_7810) && (removeItem(player, Items.EMPTY_POT_1931))) { + addItem(player, Items.POT_OF_VINEGAR_7811) + addItem(player, Items.JUG_1935) + sendMessage(player, "You pour the vinegar into the pot.") + } + return@onUseWith true + } + + // Adding a bone to the pot. + onUseWith(IntType.ITEM, BoneBoilerEnum.boneList, Items.POT_OF_VINEGAR_7811) { player, used, with -> + if (removeItem(player, used) && (removeItem(player, with))) { + addItem(player, BoneBoilerEnum.forBone(used.id)!!.boneInVinegar) + sendMessage(player, "You add the bone to the pot of vinegar.") + } + return@onUseWith true + } + + // Scenery: Placing logs on grate. + onUseWith(IntType.SCENERY, intArrayOf(Items.LOGS_1511, Items.OAK_LOGS_1521, Items.WILLOW_LOGS_1519, Items.MAPLE_LOGS_1517, Items.YEW_LOGS_1515, Items.MAGIC_LOGS_1513), Scenery.POT_BOILER_14006) { player, used, _ -> + if (removeItem(player, used)) { + sendMessage(player, "You place the logs into the grate.") + setVarbit(player, 2046, 1) + } + return@onUseWith true + } + + // Scenery: Placing bone in vinegar pot on pot boiler. + onUseWith(IntType.SCENERY, BoneBoilerEnum.boneInVinegarList, Scenery.POT_BOILER_14005) { player, used, _ -> + val potOfVinegar = used.id + if ((removeItem(player, potOfVinegar))) { + setAttribute(player, ATTRIBUTE_ACTIVE_POT_OF_VINEGAR, potOfVinegar) + sendMessage(player, "You place the pot on the pot boiler.") + setVarbit(player, 2046, 2) + } + return@onUseWith true + } + + // Scenery: Remove bone in vinegar pot from pot boiler. + on(Scenery.POT_BOILER_14007, SCENERY, "remove-pot") { player, _ -> + val potOfVinegar = getAttribute(player, ATTRIBUTE_ACTIVE_POT_OF_VINEGAR, 0) + if (BoneBoilerEnum.forBoneInVinegar(potOfVinegar) != null) { + setAttribute(player, ATTRIBUTE_ACTIVE_POT_OF_VINEGAR, 0) + addItemOrDrop(player, potOfVinegar) + sendMessage(player, "You remove the pot from the pot boiler.") + } + setVarbit(player, 2046, 1) + return@on true + } + + // Scenery: Lighting pot boiler and cooking the bone in vinegar to yield bone. + onUseWith(IntType.SCENERY, Items.TINDERBOX_590, Scenery.POT_BOILER_14007) { player, _, _ -> + sendMessage(player, "You light the logs under the pot.") + animate(player, Animations.HUMAN_LIGHT_FIRE_WITH_TINDERBOX_733) + runTask(player, 3) { + animate(player, -1, true) + setVarbit(player, 2046, 3) + // Player can do something else while the pot is lit, so a side pulse should be used. + player.pulseManager.run(object : Pulse(20) { // 20 * 600ms(1 tick) = 12000ms = 12s + override fun pulse(): Boolean { + setVarbit(player, 2046, 4) + return true + } + }, PulseType.CUSTOM_1) + } + return@onUseWith true + } + + // Taking bone and pot from boiled pot boiler. + on(Scenery.POT_BOILER_14009, SCENERY, "remove-bone") { player, _ -> + val potOfVinegar = getAttribute(player, ATTRIBUTE_ACTIVE_POT_OF_VINEGAR, 0) + if (BoneBoilerEnum.forBoneInVinegar(potOfVinegar) != null) { + val boneBoiler = BoneBoilerEnum.forBoneInVinegar(potOfVinegar)!! + setAttribute(player, ATTRIBUTE_ACTIVE_POT_OF_VINEGAR, 0) + addItemOrDrop(player, boneBoiler.polishedBone) + addItemOrDrop(player, Items.EMPTY_POT_1931) + sendMessage(player, "You retrieve a polished " + boneBoiler.boneDescription + " from the pot.") + } + setVarbit(player, 2046, 0) + return@on true + } + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..b98630ed6 --- /dev/null +++ b/Server/src/main/content/region/misthalin/silvarea/quest/ragandboneman/RamBehavior.kt @@ -0,0 +1,34 @@ +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.player.Player +import core.game.node.item.Item +import core.tools.RandomFunction +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +class RamBehavior : NPCBehavior(*ramIds) { + companion object { + private val ramIds = intArrayOf( + NPCs.RAM_3672, + NPCs.RAM_3673, + NPCs.RAM_5168, + NPCs.RAM_5169, + NPCs.RAM_5170, + ) + } + + 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, Quests.RAG_AND_BONE_MAN, 1, 99)) { + if(RandomFunction.roll(4)) { + drops.add(Item(Items.RAM_SKULL_7818)); + } + } + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..894f0261d --- /dev/null +++ b/Server/src/main/content/region/misthalin/silvarea/quest/ragandboneman/UnicornBehavior.kt @@ -0,0 +1,32 @@ +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.player.Player +import core.game.node.item.Item +import core.tools.RandomFunction +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +class UnicornBehavior : NPCBehavior(*unicornIds) { + companion object { + private val unicornIds = intArrayOf( + NPCs.UNICORN_89, + NPCs.UNICORN_987, + NPCs.BLACK_UNICORN_133, + ) + } + + 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, Quests.RAG_AND_BONE_MAN, 1, 99)) { + if(RandomFunction.roll(4)) { + drops.add(Item(Items.UNICORN_BONE_7821)); + } + } + } +} \ No newline at end of file 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/AsyffDialogue.kt b/Server/src/main/content/region/misthalin/varrock/dialogue/AsyffDialogue.kt index d3e2d84be..0ab285bf6 100644 --- a/Server/src/main/content/region/misthalin/varrock/dialogue/AsyffDialogue.kt +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/AsyffDialogue.kt @@ -12,12 +12,12 @@ import core.plugin.Initializable * @author Ceikry */ @Initializable -class AsyffDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class AsyffDialogue(player: Player? = null) : DialoguePlugin(player){ override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 0 -> player(core.game.dialogue.FacialExpression.ASKING,"Err...what are you saying exactly?").also { stage++ } - 1 -> npc(core.game.dialogue.FacialExpression.LAUGH,"I'm just saying that perhaps you would","like to peruse my selection of garments.").also { stage++ } - 2 -> npc(core.game.dialogue.FacialExpression.LAUGH,"Or, if that doesn't interest you, then maybe you have","something else to offer? I'm always on the look out","for interesting or unusual new materials.").also { stage++ } + 0 -> player(FacialExpression.ASKING,"Err...what are you saying exactly?").also { stage++ } + 1 -> npc(FacialExpression.LAUGH,"I'm just saying that perhaps you would","like to peruse my selection of garments.").also { stage++ } + 2 -> npc(FacialExpression.LAUGH,"Or, if that doesn't interest you, then maybe you have","something else to offer? I'm always on the look out","for interesting or unusual new materials.").also { stage++ } 3 -> options("Okay, let's see what you've got then.","Can you make clothing suitable for hunting in?","I think I might just leave the perusing for now thanks.","What sort of unusual materials did you have in mind?").also { stage++ } 4 -> when(buttonId){ 1 -> player("Okay, let's see what you've got then.").also { stage = 10 } @@ -45,12 +45,12 @@ class AsyffDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin( override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - npc(core.game.dialogue.FacialExpression.HAPPY,"Now you look like someone who goes to","a lot of fancy dress parties.") + npc(FacialExpression.HAPPY,"Now you look like someone who goes to","a lot of fancy dress parties.") stage = 0 return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return AsyffDialogue(player) } 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/ElsieDialogue.kt b/Server/src/main/content/region/misthalin/varrock/dialogue/ElsieDialogue.kt index ab465f138..cef36f068 100644 --- a/Server/src/main/content/region/misthalin/varrock/dialogue/ElsieDialogue.kt +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/ElsieDialogue.kt @@ -15,9 +15,9 @@ import core.tools.END_DIALOGUE import core.tools.START_DIALOGUE @Initializable -class ElsieDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { +class ElsieDialogue(player: Player? = null) : DialoguePlugin(player) { - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return ElsieDialogue(player) } @@ -26,12 +26,12 @@ class ElsieDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin( if (hasTea()) { npcl( - core.game.dialogue.FacialExpression.FRIENDLY, + FacialExpression.FRIENDLY, "Ooh - that looks like a lovely cup of tea, dearie. Is it for me?" ).also { stage = 10 } } else { npcl( - core.game.dialogue.FacialExpression.FRIENDLY, + FacialExpression.FRIENDLY, "Hello dearie! What can old Elsie do for you?" ) } @@ -42,19 +42,19 @@ class ElsieDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin( override fun handle(interfaceId: Int, buttonId: Int): Boolean { when (stage) { START_DIALOGUE -> showTopics( - Topic(core.game.dialogue.FacialExpression.ASKING, "What are you making?", 1), - Topic(core.game.dialogue.FacialExpression.ASKING, "Can you tell me a story?", 3), - Topic(core.game.dialogue.FacialExpression.ASKING, "Can you tell me how to get rich?", 5), - Topic(core.game.dialogue.FacialExpression.NEUTRAL, "I've got to go.", END_DIALOGUE) + Topic(FacialExpression.ASKING, "What are you making?", 1), + Topic(FacialExpression.ASKING, "Can you tell me a story?", 3), + Topic(FacialExpression.ASKING, "Can you tell me how to get rich?", 5), + Topic(FacialExpression.NEUTRAL, "I've got to go.", END_DIALOGUE) ) 1 -> npcl( - core.game.dialogue.FacialExpression.FRIENDLY, + FacialExpression.FRIENDLY, "I'm knitting a new stole for Father Lawrence downstairs." ).also { stage++ } 2 -> npcl( - core.game.dialogue.FacialExpression.FRIENDLY, + FacialExpression.FRIENDLY, "He could do with something to keep his neck warm, standing in " + "that draughty old church all day." ).also { stage = START_DIALOGUE } @@ -62,56 +62,56 @@ class ElsieDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin( 3 -> { if (hasTea()) { npcl( - core.game.dialogue.FacialExpression.HALF_THINKING, + FacialExpression.HALF_THINKING, "Perhaps... Can I have that lovely cup of tea you have over there?" ).also { stage = 10 } } else { npcl( - core.game.dialogue.FacialExpression.NEUTRAL, + FacialExpression.NEUTRAL, "Maybe I could tell you a story if you'd fetch me a nice cup of tea." ).also { stage++ } } } 4 -> playerl( - core.game.dialogue.FacialExpression.ROLLING_EYES, + FacialExpression.ROLLING_EYES, "I'll think about it." ).also { stage = START_DIALOGUE } 5 -> npcl( - core.game.dialogue.FacialExpression.NEUTRAL, + FacialExpression.NEUTRAL, "Well, dearie, I am probably not the best person to ask about money, but I think the best thing " + "would be for you to get a good trade." ).also { stage++ } 6 -> npcl( - core.game.dialogue.FacialExpression.HALF_WORRIED, + FacialExpression.HALF_WORRIED, "If you've got a good trade you can earn your way. That's what my old father would tell me." ).also { stage++ } 7 -> npcl( - core.game.dialogue.FacialExpression.HALF_WORRIED, + FacialExpression.HALF_WORRIED, "Saradomin rest his soul. I hear people try to get rich by fighting in the Wilderness north of here " + "or the Duel Arena in the south... But that's no way for honest folk to earn a living!" ).also { stage++ } 8 -> npcl( - core.game.dialogue.FacialExpression.HALF_WORRIED, + FacialExpression.HALF_WORRIED, "So get yourself a good trade, and keep working at it. There's always folks wanting to buy ore and food around here." ).also { stage++ } 9 -> playerl( - core.game.dialogue.FacialExpression.ROLLING_EYES, + FacialExpression.ROLLING_EYES, "Thanks, old woman." ).also { stage = START_DIALOGUE } 10 -> showTopics( - Topic(core.game.dialogue.FacialExpression.HAPPY, "Yes, you can have it.", 12), - Topic(core.game.dialogue.FacialExpression.ANNOYED, "No, keep your hands off my tea.", 11) + Topic(FacialExpression.HAPPY, "Yes, you can have it.", 12), + Topic(FacialExpression.ANNOYED, "No, keep your hands off my tea.", 11) ) 11 -> npcl( - core.game.dialogue.FacialExpression.SAD, + FacialExpression.SAD, "Aww. Maybe another time, then... Anyway, what can old Elsie do for you?" ).also { stage = START_DIALOGUE } @@ -119,84 +119,84 @@ class ElsieDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin( .also { stage++; removeItem(player, Items.CUP_OF_TEA_712) } 13 -> npcl( - core.game.dialogue.FacialExpression.HAPPY, + FacialExpression.HAPPY, "Ahh, there's nothing like a nice cuppa tea. You know what, I'll tell you a story as a thank-you for that lovely tea..." ).also { stage++ } 14 -> npcl( - core.game.dialogue.FacialExpression.NEUTRAL, + FacialExpression.NEUTRAL, "A long time ago, when I was a little girl, there was a handsome young man living in Varrock..." ).also { stage++ } 15 -> npcl( - core.game.dialogue.FacialExpression.HALF_GUILTY, + FacialExpression.HALF_GUILTY, "I saw him here in the church quite often. Everyone said he was going to become a priest and " + "we girls were so sad about that..." ).also { stage++ } 16 -> npcl( - core.game.dialogue.FacialExpression.NEUTRAL, + FacialExpression.NEUTRAL, "But young Dissy - that was the young man's nickname - he was a wild young thing." ).also { stage++ } 17 -> npcl( - core.game.dialogue.FacialExpression.NEUTRAL, + FacialExpression.NEUTRAL, "One night he gathered some lads together, and after the evening prayer-meeting " + "they all put their masks on..." ).also { stage++ } 18 -> npcl( - core.game.dialogue.FacialExpression.NEUTRAL, + FacialExpression.NEUTRAL, "Then, they snuck down to the temple in the south of the city - the evil one. " + "The next day, there was quite a hubbub..." ).also { stage++ } 19 -> npcl( - core.game.dialogue.FacialExpression.LAUGH, + FacialExpression.LAUGH, "The guards told us that someone had painted 'Saradomin pwns' on the wall of Zamorakian temple!" ).also { stage++ } 20 -> npcl( - core.game.dialogue.FacialExpression.HALF_THINKING, + FacialExpression.HALF_THINKING, "Now, we'd always been taught to keep well away from that dreadful place..." ).also { stage++ } 21 -> npcl( - core.game.dialogue.FacialExpression.FRIENDLY, + FacialExpression.FRIENDLY, "But it really did us all good to see someone wasn't afraid of the scum who live at that end of town." ).also { stage++ } 22 -> npcl( - core.game.dialogue.FacialExpression.NEUTRAL, + FacialExpression.NEUTRAL, "Old Father Packett was furious, but Dissy just laughed it off. " + "Dissy left town after that, saying he wanted to see the world." ).also { stage++ } 23 -> npcl( - core.game.dialogue.FacialExpression.HALF_GUILTY, + FacialExpression.HALF_GUILTY, "It was such a shame, he had the most handsome... Shoulders..." ).also { stage++ } 24 -> npcl( - core.game.dialogue.FacialExpression.HAPPY, + FacialExpression.HAPPY, "One day, a young man came here looking for stories about Dissy - of course, " + "that's not his proper name, but his friends called him Dissy - and I told " + "him that one. " ).also { stage++ } 25 -> npcl( - core.game.dialogue.FacialExpression.HALF_WORRIED, + FacialExpression.HALF_WORRIED, "He said Dissy had become a really famous man and there was going to be a book about him. " + "That's good and all, but I do wish Dissy had just come back to Varrock. I did miss him so much... " ).also { stage++ } 26 -> npcl( - core.game.dialogue.FacialExpression.HAPPY, + FacialExpression.HAPPY, "Well, until I met my Freddie and we got married. But that's another story." ).also { stage++ } 27 -> playerl( - core.game.dialogue.FacialExpression.FRIENDLY, + FacialExpression.FRIENDLY, "Thank you. I'll leave you to your knitting now." ).also { stage = END_DIALOGUE } } 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 e54ca6ee5..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."); @@ -82,7 +83,7 @@ public final class FatherLawrenceDialogue extends DialoguePlugin { end(); break; case 41: - interpreter.sendDialogues(npc, null, "Can't you see that I'm in the middle of a sermon?!"); + interpreter.sendDialogues(npc, null, "Can't you see that I'm in the middle of a Sermon?!"); stage = 42; break; case 42: @@ -94,7 +95,7 @@ public final class FatherLawrenceDialogue extends DialoguePlugin { stage = 44; break; case 44: - interpreter.sendDialogues(player, null, "Yes well it certainly seems like you have a captive", "audience!"); + interpreter.sendDialogues(player, null, "Yes, well, it certainly seems like you have a captive", "audience!"); stage = 45; break; case 45: @@ -110,7 +111,7 @@ public final class FatherLawrenceDialogue extends DialoguePlugin { stage = 48; break; case 48: - interpreter.sendDialogues(player, null, "Yes, very confused...Anyway, Romeo wishes to be", "married to Juliet! She must be rescued from her", "father's control!"); + interpreter.sendDialogues(player, null, "Yes, very confused....Anyway, Romeo wishes to be", "married to Juliet! She must be rescued from her", "father's control!"); stage = 49; break; case 49: @@ -130,7 +131,7 @@ public final class FatherLawrenceDialogue extends DialoguePlugin { stage = 53; break; case 53: - interpreter.sendDialogues(npc, null, "Then Romeo can collect her from the crypt! Go to the", "Apothercary, tell him I sent you and that you'll need a", "'Cadava' potion."); + interpreter.sendDialogues(npc, null, "Then Romeo can collect her from the crypt! Go to the", "Apothecary, tell him I sent you and that you'll need a", "'Cadava' potion."); stage = 54; break; case 54: 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 fdf9a23ef..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,5 +1,7 @@ package content.region.misthalin.varrock.dialogue; +import content.global.skill.summoning.pet.Pet; +import core.game.container.Container; import core.game.dialogue.DialoguePlugin; import core.game.dialogue.FacialExpression; import core.game.node.entity.npc.NPC; @@ -8,6 +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 content.data.Quests; + +import static core.api.ContentAPIKt.*; /** * Represents the gertrude dialogue plugin. @@ -47,22 +54,24 @@ 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: + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Hello again."); + stage = 236; + break; case 50: - interpreter.sendDialogues(npc, null, "Please bring me my cat back!"); + interpreter.sendDialogues(npc, FacialExpression.HALF_WORRIED, "Please bring me my cat back!"); stage = 235; break; case 40: @@ -74,7 +83,7 @@ public final class GertrudeDialogue extends DialoguePlugin { stage = 320; break; case 100: - interpreter.sendDialogues(player, null, "Hello again."); + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "Hello again."); stage = 500; break; } @@ -83,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: @@ -154,7 +163,7 @@ public final class GertrudeDialogue extends DialoguePlugin { stage = 212; break; case 212: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "What about Shilop?"); + interpreter.sendDialogues(npc, FacialExpression.HALF_ASKING, "What about Shilop?"); stage = 213; break; case 213: @@ -169,34 +178,62 @@ public final class GertrudeDialogue extends DialoguePlugin { end(); break; case 230: - interpreter.sendDialogues(npc, null, "Hello again, did you manage to find Shilop? I can't keep", "an eye on him for the life of me."); + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Hello again, did you manage to find Shilop? I can't keep", "an eye on him for the life of me."); stage = 231; break; case 231: - interpreter.sendDialogues(player, null, "He does seem quite a handful."); + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "He does seem quite a handful."); stage = 232; break; case 232: - interpreter.sendDialogues(npc, null, "You have no idea! Did he help at all?"); + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "You have no idea! Did he help at all?"); stage = 233; break; case 233: - interpreter.sendDialogues(player, null, "I think so, I'm just going to look now."); + interpreter.sendDialogues(player, FacialExpression.OLD_NORMAL, "I think so, I'm just going to look now."); stage = 234; break; case 234: - interpreter.sendDialogues(npc, null, "Thanks again adventurer!"); + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Thanks again adventurer!"); stage = 235; break; case 235: end(); break; + case 236: + interpreter.sendDialogues(npc, FacialExpression.HALF_ASKING, "Hello. How's it going? Any luck?"); + stage = 237; + break; + case 237: + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "Yes, I've found Fluffs!"); + stage = 238; + break; + case 238: + interpreter.sendDialogues(npc, FacialExpression.HALF_ASKING, "Well well, you are clever!", "Did you bring her back?"); + stage = 239; + break; + case 239: + interpreter.sendDialogues(player, FacialExpression.HALF_WORRIED, "Well, that's the thing, she refuses to leave."); + stage = 240; + break; + case 240: + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Oh dear, oh dear! Maybe she's just hungry.", "She loves doogle sardines but I'm all out."); + stage = 241; + break; + case 241: + interpreter.sendDialogues(player, FacialExpression.HALF_ASKING, "Doogle sardines?"); + stage = 242; + break; + case 242: + interpreter.sendDialogues(npc, FacialExpression.HALF_ASKING, "Yes, raw sardines seasoned with doogle leaves.", "Unfortunately I've used all my doogle leaves,", "but you may find some in the woods out back."); + stage = 304; + break; case 300: - interpreter.sendDialogues(npc, null, "Hi! Did you find fluffs?"); + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Hi! Did you find fluffs?"); stage = 301; break; case 301: - interpreter.sendDialogues(player, null, "Yes! But she won't follow me."); + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Yes! But she won't follow me."); stage = 302; break; case 302: @@ -211,7 +248,7 @@ public final class GertrudeDialogue extends DialoguePlugin { end(); break; case 320: - interpreter.sendDialogues(npc, null, "You're back! Thank you! Thank you! Fluffs just came", "back! I think she was just upset as she couldn't find her", "kitten."); + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "You're back! Thank you! Thank you! Fluffs just came", "back! I think she was just upset as she couldn't find her", "kitten."); stage = 321; break; case 321: @@ -224,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: @@ -266,14 +303,14 @@ public final class GertrudeDialogue extends DialoguePlugin { case 331: break; case 1000: - interpreter.sendDialogues(npc, null, "Good good, See you again."); + interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Good good, See you again."); stage = 1001; break; case 1001: end(); break; case 500: - interpreter.sendDialogues(npc, null, "Hello my dear. How's things?"); + interpreter.sendDialogues(npc, FacialExpression.HALF_ASKING, "Hello my dear. How's things?"); stage = 501; break; case 501: @@ -283,11 +320,11 @@ public final class GertrudeDialogue extends DialoguePlugin { case 502: switch (buttonId) { case 1: - interpreter.sendDialogues(player, null, "I'm fine, thanks."); + interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I'm fine, thanks."); stage = 1000; break; case 2: - interpreter.sendDialogues(player, null, "Do you have any more kittens?"); + interpreter.sendDialogues(player, FacialExpression.HALF_ASKING, "Do you have any more kittens?"); stage = 503; break; } @@ -295,20 +332,18 @@ public final class GertrudeDialogue extends DialoguePlugin { case 503: boolean has = false; int[] kittens = new int[] { 1555, 1556, 1557, 1558, 1559, 1560, 7583 }; - for (int i : kittens) { - // if (player.getFamiliarManager().hasFamiliar()) {TODO:Pet - // if - // (player.getFamiliarManager().getFamiliar().getDetails() - // instanceof PetDetails) { - // final PetDetails details = ((PetDetails) - // player.getFamiliarManager().getFamiliar().getDetails()); - // if (details.getPet().getBabyItemId() == i) { - // has = true; - // break; - // } - // } - // } - if (player.getInventory().contains(i, 1) || player.getBank().contains(i, 1)) { + if (player.getFamiliarManager().hasFamiliar() && player.getFamiliarManager().hasPet()) { + Pet pet = (Pet) player.getFamiliarManager().getFamiliar(); + for (int i : kittens) { + if (pet.getItemId() == i) { + has = true; + break; + } + } + } + Container[] searchSpace = {player.getInventory(), player.getBank()}; + for (Container container : searchSpace) { + if (container.containsAtLeastOneItem(kittens)) { has = true; break; } @@ -328,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; @@ -348,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: @@ -356,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/HeadChefDialogue.java b/Server/src/main/content/region/misthalin/varrock/dialogue/HeadChefDialogue.java index 6c0661e39..2e0527b0a 100644 --- a/Server/src/main/content/region/misthalin/varrock/dialogue/HeadChefDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/HeadChefDialogue.java @@ -45,16 +45,16 @@ public final class HeadChefDialogue extends DialoguePlugin { if (args.length == 2) door = (boolean) args[1]; if (door) { - interpreter.sendDialogues(847, FacialExpression.HALF_GUILTY, "You can't come in here unless you're wearing a chef's", "hat or something like that."); + interpreter.sendDialogues(847, FacialExpression.NEUTRAL, "You can't come in here unless you're wearing a chef's", "hat or something like that."); stage = 0; return true; } if (player.getSkills().getStaticLevel(Skills.COOKING) >= 99) { - interpreter.sendDialogues(847, FacialExpression.HALF_GUILTY, "Hello, welcome to the Cooking Guild. It's always great to", "have such an accomplished chef visit. Say would you be", "interested in a Skillcape of Cooking? They're only available", "to master chefs."); + interpreter.sendDialogues(847, FacialExpression.HAPPY, "Hello, welcome to the Cooking Guild. It's always great to", "have such an accomplished chef visit. Say would you be", "interested in a Skillcape of Cooking? They're only available", "to master chefs."); stage = 100; return true; } - interpreter.sendDialogues(847, FacialExpression.HALF_GUILTY, "Hello, welcome to the Cooking Guild. Only accomplished", "chefs and cooks are allowed in here. Feel free to use any", "of our facilities."); + interpreter.sendDialogues(847, FacialExpression.HAPPY, "Hello, welcome to the Cooking Guild. Only accomplished", "chefs and cooks are allowed in here. Feel free to use any", "of our facilities."); stage = 1; return true; } @@ -74,11 +74,11 @@ public final class HeadChefDialogue extends DialoguePlugin { switch (buttonId) { case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Nice cape, you're wearing!"); + interpreter.sendDialogues(player, FacialExpression.HAPPY, "Nice cape, you're wearing!"); stage = 10; break; case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Thanks, bye."); + interpreter.sendDialogues(player, FacialExpression.FRIENDLY, "Thanks, bye."); stage = 3; break; } @@ -88,7 +88,7 @@ public final class HeadChefDialogue extends DialoguePlugin { end(); break; case 10: - interpreter.sendDialogues(847, FacialExpression.HALF_GUILTY, "Thank you! It's my most prized possesion, it's a Skillcape", "of Cooking; it shows that I've achieved level 99 Cooking", "and am one of the best chefs in the land!"); + interpreter.sendDialogues(847, FacialExpression.HAPPY, "Thank you! It's my most prized possession, it's a Skillcape", "of Cooking; it shows that I've achieved level 99 Cooking", "and am one of the best chefs in the land!"); stage = 11; break; case 11: @@ -102,7 +102,7 @@ public final class HeadChefDialogue extends DialoguePlugin { switch (buttonId) { case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No thanks."); + interpreter.sendDialogues(player, FacialExpression.FRIENDLY, "No thanks."); stage = 110; break; case 2: @@ -113,7 +113,7 @@ public final class HeadChefDialogue extends DialoguePlugin { break; case 110: - interpreter.sendDialogues(847, FacialExpression.HALF_GUILTY, "Okay, come back to me if you change your mind."); + interpreter.sendDialogues(847, FacialExpression.HAPPY, "Okay, come back to me if you change your mind."); stage = 111; break; case 111: @@ -131,11 +131,11 @@ public final class HeadChefDialogue extends DialoguePlugin { switch (buttonId) { case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "That's much too expensive."); + interpreter.sendDialogues(player, FacialExpression.EXTREMELY_SHOCKED, "That's much too expensive."); stage = 160; break; case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Sure."); + interpreter.sendDialogues(player, FacialExpression.HAPPY, "Sure."); stage = 200; break; } @@ -150,7 +150,7 @@ public final class HeadChefDialogue extends DialoguePlugin { break; case 200: if (Skillcape.purchase(player, Skills.COOKING)) { - interpreter.sendDialogues(847, FacialExpression.HALF_GUILTY, "Now you can use the title Master Chef."); + interpreter.sendDialogues(847, FacialExpression.HAPPY, "Now you can use the title Master Chef."); } stage = 202; break; diff --git a/Server/src/main/content/region/misthalin/varrock/dialogue/HorvikDialogue.java b/Server/src/main/content/region/misthalin/varrock/dialogue/HorvikDialogue.java index abd53fe4e..d9125024a 100644 --- a/Server/src/main/content/region/misthalin/varrock/dialogue/HorvikDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/HorvikDialogue.java @@ -39,7 +39,7 @@ public final class HorvikDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Hello, do you need any help?"); + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Hello, do you need any help?"); stage = 0; return true; } @@ -55,7 +55,7 @@ public final class HorvikDialogue extends DialoguePlugin { switch (buttonId) { case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No, thanks. I'm just looking around."); + interpreter.sendDialogues(player, FacialExpression.FRIENDLY, "No, thanks. I'm just looking around."); stage = 10; break; case 2: @@ -66,7 +66,7 @@ public final class HorvikDialogue extends DialoguePlugin { break; case 10: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Well, come and see me if you're ever in need of armour!"); + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Well, come and see me if you're ever in need of armour!"); stage = 11; break; case 11: diff --git a/Server/src/main/content/region/misthalin/varrock/dialogue/IffieDialogue.kt b/Server/src/main/content/region/misthalin/varrock/dialogue/IffieDialogue.kt index 876b66944..2ebebdf69 100644 --- a/Server/src/main/content/region/misthalin/varrock/dialogue/IffieDialogue.kt +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/IffieDialogue.kt @@ -9,10 +9,10 @@ import core.plugin.Initializable * @author afaroutdude */ @Initializable -class IffieDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { +class IffieDialogue(player: Player? = null) : DialoguePlugin(player) { override fun open(vararg args: Any?): Boolean { npc("Sorry, dearie, if I stop to chat I'll lose count.", "Talk to my sister instead; she likes to chat.", "You'll find her upstairs in the church.") - stage = 999; + stage = 999 return true } @@ -23,7 +23,7 @@ class IffieDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin( return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return IffieDialogue(player) } diff --git a/Server/src/main/content/region/misthalin/varrock/dialogue/InformationclerkMuseumDialogue.java b/Server/src/main/content/region/misthalin/varrock/dialogue/InformationclerkMuseumDialogue.java index 9c7373319..f0ca68e0b 100644 --- a/Server/src/main/content/region/misthalin/varrock/dialogue/InformationclerkMuseumDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/InformationclerkMuseumDialogue.java @@ -87,7 +87,7 @@ public final class InformationclerkMuseumDialogue extends DialoguePlugin { stage = 171; break; case 171: - npc("I know you've helped out a bit in the Timeline exhibit", "upstairs, but I'm sure you can help more. When you're out", "on your travels being a vrave adventurer, remember that", "you can come back to the Museum after some quests to"); + npc("I know you've helped out a bit in the Timeline exhibit", "upstairs, but I'm sure you can help more. When you're out", "on your travels being a brave adventurer, remember that", "you can come back to the Museum after some quests to"); stage = 172; break; case 172: @@ -155,7 +155,7 @@ public final class InformationclerkMuseumDialogue extends DialoguePlugin { stage = 233; break; case 233: - npc("canal and barge. So, we're using Kudos as a measure of", "who is willing and able to help us here at the Museum, so", "they can then be invited on our dig son the new island."); + npc("canal and barge. So, we're using Kudos as a measure of", "who is willing and able to help us here at the Museum, so", "they can then be invited on our digs on the new island."); stage = 234; break; case 234: 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/LarxusDialogue.java b/Server/src/main/content/region/misthalin/varrock/dialogue/LarxusDialogue.java deleted file mode 100644 index 9040b8e34..000000000 --- a/Server/src/main/content/region/misthalin/varrock/dialogue/LarxusDialogue.java +++ /dev/null @@ -1,99 +0,0 @@ -package content.region.misthalin.varrock.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 dialogue plugin used for the larxus npc. - * @author 'Vexia - * @version 1.0 - */ -@Initializable -public final class LarxusDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code LarxusDialogue} {@code Object}. - */ - public LarxusDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code LarxusDialogue} {@code Object}. - * @param player the player. - */ - public LarxusDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new LarxusDialogue(player); - } - - @Override - public boolean open(Object... args) { - if (args.length == 2) { - npc("You need to arrange a challenge with me before you", "enter the arena."); - stage = 30; - return true; - } - npc = (NPC) args[0]; - npc("Is there something I can help you with?"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - options("I was given a challenge, what now?", "What is this place?", "Nothing thanks."); - stage = 1; - break; - case 1: - switch (buttonId) { - case 1: - player("I was given a challenge, what now?"); - stage = 10; - break; - case 2: - player("What is this place?"); - stage = 20; - break; - case 3: - player("Nothing thanks."); - stage = 30; - break; - } - break; - case 10: - npc("Well pass it here and we'll get you started."); - stage = 11; - break; - case 11: - end(); - break; - case 20: - npc("This is the champions' arena, the champions of various", "races use it to duel those they deem worthy of the", "honour."); - stage = 21; - break; - case 21: - end(); - break; - case 30: - end(); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 3050 }; - } -} diff --git a/Server/src/main/content/region/misthalin/varrock/dialogue/LoweDialogue.java b/Server/src/main/content/region/misthalin/varrock/dialogue/LoweDialogue.java index 342dc5599..97c0da485 100644 --- a/Server/src/main/content/region/misthalin/varrock/dialogue/LoweDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/LoweDialogue.java @@ -7,7 +7,7 @@ import core.plugin.Initializable; import core.game.node.entity.player.Player; /** - * Represesents the dialogue plugin used for lowe. + * Represents the dialogue plugin used for lowe. * @author 'Vexia * @version 1.0 */ @@ -39,7 +39,7 @@ public final class LoweDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Welcome to Lowe's Archery Emporium. Do you want", "to see my wares?"); + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Welcome to Lowe's Archery Emporium. Do you want", "to see my wares?"); stage = 0; return true; } @@ -57,14 +57,14 @@ public final class LoweDialogue extends DialoguePlugin { npc.openShop(player); break; case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No, I prefer to bash things close up."); + interpreter.sendDialogues(player, FacialExpression.EVIL_LAUGH, "No, I prefer to bash things close up."); stage = 3; break; } break; case 3: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Humph, philistine."); + interpreter.sendDialogues(npc, FacialExpression.ANNOYED, "Humph, philistine."); stage = 4; break; case 4: 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 ab80ce3fe..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(npc, 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/SaniDialogue.java b/Server/src/main/content/region/misthalin/varrock/dialogue/SaniDialogue.java index 2d3188c6b..040f59ed7 100644 --- a/Server/src/main/content/region/misthalin/varrock/dialogue/SaniDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/SaniDialogue.java @@ -107,11 +107,11 @@ public class SaniDialogue extends DialoguePlugin { stage++; break; case 22: - player("I see...", "So this means that you only sell common armours", "to help adventurers like nyself get started in the world", "of Keldagrim."); + player("I see...", "So this means that you only sell common armours", "to help adventurers like myself get started in the world", "of Gielinor."); stage++; break; case 23: - npc("Indeed, that is true. If you have any more questions", "The Keldagrim guide near the green portal should be able", "to assist you further."); + npc("Indeed, that is true. If you have any more questions", "The Gielinor guide near the green portal should be able", "to assist you further."); stage = 0; break; case 30: diff --git a/Server/src/main/content/region/misthalin/varrock/dialogue/SawmillOperator.java b/Server/src/main/content/region/misthalin/varrock/dialogue/SawmillOperator.java index 8b228ecbf..505fbb3d1 100644 --- a/Server/src/main/content/region/misthalin/varrock/dialogue/SawmillOperator.java +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/SawmillOperator.java @@ -2,6 +2,7 @@ package content.region.misthalin.varrock.dialogue; import core.game.component.Component; 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; @@ -30,7 +31,7 @@ public class SawmillOperator extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - npc("Do you want me to make some planks for you? Or", "would you be interested in some other housing supplies?"); + interpreter.sendDialogues(npc, FacialExpression.NEUTRAL,"Do you want me to make some planks for you? Or", "would you be interested in some other housing supplies?"); stage = 0; return true; } @@ -45,11 +46,11 @@ public class SawmillOperator extends DialoguePlugin { case 1: switch (buttonId) { case 1: - player("Planks please!"); + interpreter.sendDialogues(player, FacialExpression.HAPPY,"Planks please!"); stage = 10; break; case 2: - player("What kind of planks can you make?"); + interpreter.sendDialogues(player, FacialExpression.ASKING,"What kind of planks can you make?"); stage = 20; break; case 3: @@ -57,17 +58,17 @@ public class SawmillOperator extends DialoguePlugin { npc.openShop(player); break; case 4: - player("Nothing, thanks."); + interpreter.sendDialogues(player, FacialExpression.FRIENDLY,"Nothing, thanks."); stage = 40; break; } break; case 40: - npc("Well come back when you want some. You can't get", "good quality planks anywhere but here!"); + interpreter.sendDialogues(npc, FacialExpression.FRIENDLY,"Well come back when you want some. You can't get", "good quality planks anywhere but here!"); stage = 999; break; case 10: - npc("What kind of planks do you want?"); + interpreter.sendDialogues(npc, FacialExpression.HALF_ASKING,"What kind of planks do you want?"); stage = 11; break; case 11: @@ -75,11 +76,11 @@ public class SawmillOperator extends DialoguePlugin { player.getInterfaceManager().open(new Component(403)); break; case 20: - npc("I can make planks from wood, oak, teak and mahogany.", "I don't make planks from other woods as they're no", "good for making furniture."); + interpreter.sendDialogues(npc, FacialExpression.NEUTRAL,"I can make planks from wood, oak, teak and mahogany.", "I don't make planks from other woods as they're no", "good for making furniture."); stage = 21; break; case 21: - npc("Wood and oak are all over the place, but teak and", "mahogany can only be found in a few places like", "Karamja and Etceteria."); + interpreter.sendDialogues(npc, FacialExpression.NEUTRAL,"Wood and oak are all over the place, but teak and", "mahogany can only be found in a few places like", "Karamja and Etceteria."); stage = 999; break; case 999: 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 new file mode 100644 index 000000000..79a1f59cc --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/SinkethsDiary.kt @@ -0,0 +1,202 @@ +package content.region.misthalin.varrock.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.api.setAttribute +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.node.entity.player.Player +import org.rs09.consts.Items + +// 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" + private val CONTENTS = arrayOf( + PageSet( + Page( + BookLine("'...2nd Pentember,", 55), + BookLine("Fifth Age,70", 56), + BookLine("We have worked", 57), + BookLine("for days. It is a weary", 58), + BookLine("and tiring journey that", 59), + BookLine("my brothers and I must", 60), + BookLine("take,but we are close", 61), + BookLine("to success! Elder Dag'eth", 62), + BookLine("has led us well and he", 63), + BookLine("has told us that Zamorak", 64), + BookLine("will reward us greatly", 65), + BookLine("for our service to Him.", 66), + ), + Page( + BookLine("The priests of Saradomin", 67), + BookLine("haunt our very steps and", 68), + BookLine("I fear our discovery. Yet,", 69), + BookLine("soon will be the hour of", 70), + BookLine("our glory. The Dagon'hai", 71), + BookLine("will prevail and the city", 72), + BookLine("will be ours! We will throw", 73), + BookLine("down the vile yoke of", 74), + BookLine("Saradominand the", 75), + BookLine("Dagon'hai will", 76), + ) + ), + PageSet( + Page( + BookLine("be victorious! ", 55), + BookLine("", 56), + BookLine("9th Pentember,Fifth Age, 70", 57), + BookLine("Today we donned the", 58), + BookLine("filthy robes of", 59), + BookLine("the Saradomin priests.", 60), + BookLine("It was a foul deed and", 61), + BookLine("distasteful to my very", 62), + BookLine("soul,yet it had to be", 63), + BookLine("done. Without the disguise,", 64), + BookLine("we would surely have been", 65), + ), + Page( + BookLine("found out and ruined. ", 66), + BookLine("We erected a statue", 67), + BookLine("of Saradomin himself", 68), + BookLine("just outside the city", 69), + BookLine("to the east. Our Lord", 70), + BookLine("Zamorak must be laughing", 71), + BookLine("in the faces of our enemies", 72), + BookLine("at such a deception,for", 73), + BookLine("this statue holds the key", 74), + BookLine("to our success. Beneath", 75), + BookLine("the arrogant caricature", 76), + ) + ), + PageSet( + Page( + BookLine("of this worthless deity", 55), + BookLine("lies the entrance to our", 56), + BookLine("most sacred work yet: the", 57), + BookLine("Tunnel of Chaos. With this", 58), + BookLine("tunnel,we are able to", 59), + BookLine("traverse to the very", 60), + BookLine("source of our power,", 61), + BookLine("the Chaos Temple itself.", 62), + BookLine("Those foolish followers", 63), + BookLine("of Saradomin do not", 64), + BookLine("even sense what", 65), + BookLine("we have achieved. They", 66), + ), + Page( + BookLine("have filled the statue", 67), + BookLine("with their accursed holy", 68), + BookLine("magic,covering even the", 69), + BookLine("merest traces of our work", 70), + BookLine("beneath. They have granted", 71), + BookLine("us the most perfect of", 72), + BookLine("disguises.", 73), + BookLine("Zamorak be praised!", 74), + BookLine("", 75), + BookLine("11th Pentember,Fifth Age, 70", 76), + ) + ), + PageSet( + Page( + BookLine("Excellent news! I", 55), + BookLine("have been chosen", 56), + BookLine("by Elder Dag'eth to be", 57), + BookLine("the next Hyeraph. I,", 58), + BookLine("Sin'keth Magis,", 59), + BookLine("will lead our people", 60), + BookLine("in the incantation of", 61), + BookLine("Zamorak's Will. ", 62), + BookLine("Surely this means", 63), + BookLine("I will become High Elder!", 64), + BookLine("I must prove worthy to", 65), + BookLine("Lord Zamorak. He will not", 66), + ), + Page( + BookLine("find me wanting. There", 67), + BookLine("is much to do in ", 68), + BookLine("preparation for the", 69), + BookLine("ceremony and I do", 70), + BookLine("not have long.", 71), + BookLine("", 72), + BookLine("24th Septober,Fifth Age, 70", 73), + BookLine("Disaster!", 74), + BookLine("The incantation of Zamorak's", 75), + BookLine("Will was discovered by", 76), + ) + ), + PageSet( + Page( + BookLine("a loathsome watchman,of", 55), + BookLine("all people. ", 56), + BookLine("Zamorak's Blood!", 57), + BookLine("The fates are cruel! We", 58), + BookLine("could not finish the final", 59), + BookLine("rites of the spell. Our", 60), + BookLine("work has been undone and", 61), + BookLine("we have no time to gather", 62), + BookLine("our forces together and", 63), + BookLine("hide. We are being followed", 64), + BookLine("by the guards and the ", 65), + BookLine("Priests of Filth are", 66), + ), + Page( + BookLine("at our heels.", 67), + BookLine("We must flee the city!", 68), + BookLine("Elder La'nou and Elder", 69), + BookLine("Kree'nag were slain whilst", 70), + BookLine("protecting the sanctum.", 71), + BookLine("Elder Dag'eth will not", 72), + BookLine("leave with us. Zamorak", 73), + BookLine("take him,he will stand", 74), + BookLine("against the hordes that", 75), + BookLine("follow us! I am the last", 76), + ) + ), + PageSet( + Page( + BookLine("of the Elders. The order", 55), + BookLine("looks to me now.", 56), + BookLine("", 57), + BookLine("27th Septober,Fifth Age, 70", 58), + BookLine("Today,the last of", 59), + BookLine("our order entered the", 60), + BookLine("Tunnel of Chaos. We", 61), + BookLine("will journey to the Chaos", 62), + BookLine("Temple and let Zamorak", 63), + BookLine("Himself decide our fate.", 64), + BookLine("What happened to Elder", 65), + BookLine("Dag'eth, I know not. As", 66), + ), + Page( + BookLine("the city guards closed", 67), + BookLine("upon us,I cast an Earth", 68), + BookLine("Bolt spell to collapse", 69), + BookLine("the entrance of the tunnel", 70), + BookLine("in an avalanche of earth", 71), + BookLine("and stone,saving us and", 72), + BookLine("dooming us in one breath.", 73), + BookLine("There is only one place", 74), + BookLine("for us to go now...", 75), + ), + ) + ) + } + + 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.SINKETHS_DIARY_11002, 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/misthalin/varrock/dialogue/ThessaliaDialogue.kt b/Server/src/main/content/region/misthalin/varrock/dialogue/ThessaliaDialogue.kt index f3f735172..21772e6a8 100644 --- a/Server/src/main/content/region/misthalin/varrock/dialogue/ThessaliaDialogue.kt +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/ThessaliaDialogue.kt @@ -1,5 +1,6 @@ package content.region.misthalin.varrock.dialogue +import core.api.playJingle import core.game.component.Component import core.plugin.Initializable import core.game.dialogue.DialoguePlugin @@ -9,7 +10,7 @@ import core.game.node.entity.player.Player import org.rs09.consts.Components @Initializable -class ThessaliaDialogue(player: Player? = null): core.game.dialogue.DialoguePlugin(player) { +class ThessaliaDialogue(player: Player? = null): DialoguePlugin(player) { override fun open(vararg args: Any): Boolean { //The trade argument is handled elsewhere @@ -22,8 +23,9 @@ class ThessaliaDialogue(player: Player? = null): core.game.dialogue.DialoguePlug end() player.interfaceManager.open(Component(Components.THESSALIA_CLOTHES_FEMALE_594)) } + playJingle(player, 273) } else { //Has some armour equipped - interpreter.sendDialogues(548, core.game.dialogue.FacialExpression.WORRIED, "You can't try them on while wearing armour. Take", "it off and speak to me again.") + interpreter.sendDialogues(548, FacialExpression.WORRIED, "You can't try them on while wearing armour. Take", "it off and speak to me again.") stage = 52 } return true @@ -32,7 +34,7 @@ class ThessaliaDialogue(player: Player? = null): core.game.dialogue.DialoguePlug npc = args[0] as NPC //Default Talk - interpreter.sendDialogues(npc, core.game.dialogue.FacialExpression.ASKING, "Would you like to buy any fine clothes?") + interpreter.sendDialogues(npc, FacialExpression.ASKING, "Would you like to buy any fine clothes?") stage = 0 return true } @@ -45,16 +47,16 @@ class ThessaliaDialogue(player: Player? = null): core.game.dialogue.DialoguePlug } 1 -> when (buttonId) { 1 -> { - interpreter.sendDialogues(player, core.game.dialogue.FacialExpression.ASKING, "What do you have?") + interpreter.sendDialogues(player, FacialExpression.ASKING, "What do you have?") stage = 10 } 2 -> { - interpreter.sendDialogues(player, core.game.dialogue.FacialExpression.NEUTRAL, "No, thank you.") + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "No, thank you.") stage = 51 } } 10 -> { - interpreter.sendDialogues(npc, core.game.dialogue.FacialExpression.HALF_GUILTY, "I have a number of fine pieces of clothing on sale or,", "if you prefer, I can offer you an exclusive", "total clothing makeover?") + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "I have a number of fine pieces of clothing on sale or,", "if you prefer, I can offer you an exclusive", "total clothing makeover?") stage++ } 11 -> { @@ -63,28 +65,28 @@ class ThessaliaDialogue(player: Player? = null): core.game.dialogue.DialoguePlug } 12 -> when (buttonId) { 1 -> { - interpreter.sendDialogues(player, core.game.dialogue.FacialExpression.THINKING, "Tell me more about this makeover.") + interpreter.sendDialogues(player, FacialExpression.THINKING, "Tell me more about this makeover.") stage = 20 } 2 -> { - interpreter.sendDialogues(player, core.game.dialogue.FacialExpression.NEUTRAL, "I'd just like to buy some clothes.") + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "I'd just like to buy some clothes.") stage = 50 } } 20 -> { - interpreter.sendDialogues(npc, core.game.dialogue.FacialExpression.HAPPY, "Certainly!") + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Certainly!") stage++ } 21 -> { - interpreter.sendDialogues(npc, core.game.dialogue.FacialExpression.HAPPY, "Here at Thessalia's fine clothing boutique, we offer a", "unique service where we will totally revamp your outfit", "to your choosing, for... wait for it...") + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Here at Thessalia's fine clothing boutique, we offer a", "unique service where we will totally revamp your outfit", "to your choosing, for... wait for it...") stage++ } 22 -> { - interpreter.sendDialogues(npc, core.game.dialogue.FacialExpression.FRIENDLY, "A fee of only 500 gold coins! Tired of always wearing", "the same old outfit, day in, day out? This is the service", "for you!") + interpreter.sendDialogues(npc, FacialExpression.AMAZED, "A fee of only 500 gold coins! Tired of always wearing", "the same old outfit, day in, day out? This is the service", "for you!") stage++ } 23 -> { - interpreter.sendDialogues(npc, core.game.dialogue.FacialExpression.ASKING, "So what do you say? Interested? We can change either", "your top, or your legwear for only 500 gold a item!") + interpreter.sendDialogues(npc, FacialExpression.ASKING, "So what do you say? Interested? We can change either", "your top, or your legwear for only 500 gold an item!") stage++ } 24 -> { @@ -93,19 +95,19 @@ class ThessaliaDialogue(player: Player? = null): core.game.dialogue.DialoguePlug } 25 -> when (buttonId) { 1 -> { - interpreter.sendDialogues(player, core.game.dialogue.FacialExpression.HAPPY, "I'd like to change my outfit, please.") + interpreter.sendDialogues(player, FacialExpression.HAPPY, "I'd like to change my outfit, please.") stage = 30 } 2 -> { - interpreter.sendDialogues(player, core.game.dialogue.FacialExpression.NEUTRAL, "I'd just like to buy some clothes.") + interpreter.sendDialogues(player, FacialExpression.HAPPY, "I'd just like to buy some clothes.") stage = 50 } } 30 -> if (player.equipment.isEmpty) { - interpreter.sendDialogues(npc, core.game.dialogue.FacialExpression.HAPPY, "Just select what style and colour you would like from", "this catalogue, and then give me the 1000 gold when", "you've picked.") + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Just select what style and colour you would like from", "this catalogue, and then give me the 1000 gold when", "you've picked.") stage++ } else { //Has some armour equipped - interpreter.sendDialogues(npc, core.game.dialogue.FacialExpression.WORRIED, "You can't try them on while wearing armour. Take", "it off and speak to me again.") + interpreter.sendDialogues(npc, FacialExpression.WORRIED, "You can't try them on while wearing armour. Take", "it off and speak to me again.") stage = 52 } 31 -> if (player.equipment.isEmpty) { @@ -118,7 +120,7 @@ class ThessaliaDialogue(player: Player? = null): core.game.dialogue.DialoguePlug } } 49 -> { - interpreter.sendDialogues(npc, core.game.dialogue.FacialExpression.FRIENDLY, "That's ok! Just come back when you do have it!") + interpreter.sendDialogues(npc, FacialExpression.FRIENDLY, "That's ok! Just come back when you do have it!") stage = 52 } 50 -> { @@ -134,7 +136,7 @@ class ThessaliaDialogue(player: Player? = null): core.game.dialogue.DialoguePlug return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return ThessaliaDialogue(player) } diff --git a/Server/src/main/content/region/misthalin/varrock/dialogue/VarrockBartenderDialogue.java b/Server/src/main/content/region/misthalin/varrock/dialogue/VarrockBartenderDialogue.java index 1eda8c473..67c0f28bb 100644 --- a/Server/src/main/content/region/misthalin/varrock/dialogue/VarrockBartenderDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/VarrockBartenderDialogue.java @@ -41,7 +41,7 @@ public final class VarrockBartenderDialogue extends DialoguePlugin { public boolean handle(int interfaceId, int buttonId) { switch (stage) { case 0: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Good day to you, brave adventurer. Can I get you a", "refreshing beer?"); + interpreter.sendDialogues(npc, FacialExpression.HALF_ASKING, "Good day to you, brave adventurer. Can I get you a", "refreshing beer?"); stage = 1; break; case 1: @@ -51,31 +51,31 @@ public final class VarrockBartenderDialogue extends DialoguePlugin { case 2: switch (buttonId) { case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Yes please!"); + interpreter.sendDialogues(player, FacialExpression.HAPPY, "Yes please!"); stage = 10; break; case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No thanks."); + interpreter.sendDialogues(player, FacialExpression.NEUTRAL, "No thanks."); stage = 20; break; case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "How much?"); + interpreter.sendDialogues(player, FacialExpression.HALF_ASKING, "How much?"); stage = 30; break; } break; case 10: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Ok then, that's two gold coins please."); + interpreter.sendDialogues(npc, FacialExpression.FRIENDLY, "Ok then, that's two gold coins please."); stage = 11; break; case 11: if (player.getInventory().contains(995, 2)) { player.getInventory().remove(new Item(995, 2)); player.getInventory().add(new Item(1917, 1)); - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Cheers!"); + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Cheers!"); stage = 12; } else { end(); @@ -83,21 +83,21 @@ public final class VarrockBartenderDialogue extends DialoguePlugin { } break; case 12: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Cheers!"); + interpreter.sendDialogues(player, FacialExpression.HAPPY, "Cheers!"); stage = 13; break; case 13: end(); break; case 20: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Let me know if you change your mind."); + interpreter.sendDialogues(npc, FacialExpression.FRIENDLY, "Let me know if you change your mind."); stage = 21; break; case 21: end(); break; case 30: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Two gold pieces a pint. So, what do you say?"); + interpreter.sendDialogues(npc, FacialExpression.FRIENDLY, "Two gold pieces a pint. So, what do you say?"); stage = 31; break; case 31: @@ -108,11 +108,11 @@ public final class VarrockBartenderDialogue extends DialoguePlugin { switch (buttonId) { case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Yes please!"); + interpreter.sendDialogues(player, FacialExpression.HAPPY, "Yes please!"); stage = 10; break; case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No thanks."); + interpreter.sendDialogues(player, FacialExpression.FRIENDLY, "No thanks."); stage = 20; break; } diff --git a/Server/src/main/content/region/misthalin/varrock/dialogue/VarrockEastBartenderDialogue.java b/Server/src/main/content/region/misthalin/varrock/dialogue/VarrockEastBartenderDialogue.java index 69d5a7075..6c57f4385 100644 --- a/Server/src/main/content/region/misthalin/varrock/dialogue/VarrockEastBartenderDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/VarrockEastBartenderDialogue.java @@ -40,16 +40,16 @@ public class VarrockEastBartenderDialogue extends DialoguePlugin { switch (buttonId) { case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "A glass of your finest ale please."); + interpreter.sendDialogues(player, FacialExpression.HAPPY, "A glass of your finest ale please."); stage = 10; break; case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Cany ou recommend where an adventurer might make", "his fortune?"); + interpreter.sendDialogues(player, FacialExpression.ASKING, "Can you recommend where an adventurer might make", "his fortune?"); stage = 20; break; case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Do you know where I can get some good equipment?"); + interpreter.sendDialogues(player, FacialExpression.ASKING, "Do you know where I can get some good equipment?"); stage = 30; break; } @@ -57,7 +57,7 @@ public class VarrockEastBartenderDialogue extends DialoguePlugin { break; case 10: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "No problemo. That'll be 2 coins."); + interpreter.sendDialogues(npc, FacialExpression.FRIENDLY, "No problemo. That'll be 2 coins."); stage = 11; break; case 11: @@ -72,7 +72,7 @@ public class VarrockEastBartenderDialogue extends DialoguePlugin { } break; case 20: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Ooh I don't know if I should be giving away information,", "makes the computer game too easy."); + interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "Ooh I don't know if I should be giving away information,", "makes the computer game too easy."); stage = 21; break; case 21: @@ -87,30 +87,30 @@ public class VarrockEastBartenderDialogue extends DialoguePlugin { stage = 150; break; case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Computer game? What are you talking about?"); + interpreter.sendDialogues(player, FacialExpression.ASKING, "Computer game? What are you talking about?"); stage = 160; break; case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Just a small clue?"); + interpreter.sendDialogues(player, FacialExpression.THINKING, "Just a small clue?"); stage = 170; break; } break; case 160: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "This world around us... is a computer game.... called", "" + GameWorld.getSettings().getName() + "."); + interpreter.sendDialogues(npc, FacialExpression.THINKING, "This world around us... is a computer game.... called", "" + GameWorld.getSettings().getName() + "."); stage = 161; break; case 161: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Nope, still don't understand what you are talking about.", "What's a computer?"); + interpreter.sendDialogues(player, FacialExpression.HALF_THINKING, "Nope, still don't understand what you are talking about.", "What's a computer?"); stage = 162; break; case 162: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "It's a sort of magic box thing, wich can do all sorts of", "stuff."); + interpreter.sendDialogues(npc, FacialExpression.THINKING, "It's a sort of magic box thing, which can do all sorts of", "stuff."); stage = 163; break; case 163: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "I give up. You're obviously completely mad"); + interpreter.sendDialogues(player, FacialExpression.WORRIED, "I give up. You're obviously completely mad."); stage = 164; break; case 164: @@ -120,14 +120,14 @@ public class VarrockEastBartenderDialogue extends DialoguePlugin { end(); break; case 30: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Well, there's the sword shop across the road, or there's", "also all sorts of shops up around the market."); + interpreter.sendDialogues(npc, FacialExpression.FRIENDLY, "Well, there's the sword shop across the road, or there's", "also all sorts of shops up around the market."); stage = 31; break; case 31: end(); break; case 170: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Go and talk to the bartender at the Holly Boar Inn, he", "doesn't seem to mind giving away clues."); + interpreter.sendDialogues(npc, FacialExpression.NEUTRAL, "Go and talk to the bartender at the Holly Boar Inn, he", "doesn't seem to mind giving away clues."); stage = 171; break; case 171: @@ -147,7 +147,7 @@ public class VarrockEastBartenderDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "What can I do yer for?"); + interpreter.sendDialogues(npc, FacialExpression.FRIENDLY, "What can I do yer for?"); stage = 0; return true; } diff --git a/Server/src/main/content/region/asgarnia/rimmington/dialogue/RimmingtonShopKeeperDialogue.java b/Server/src/main/content/region/misthalin/varrock/dialogue/VarrockShopKeeperDialogue.java similarity index 52% rename from Server/src/main/content/region/asgarnia/rimmington/dialogue/RimmingtonShopKeeperDialogue.java rename to Server/src/main/content/region/misthalin/varrock/dialogue/VarrockShopKeeperDialogue.java index 3e8f07ccb..3597cce51 100644 --- a/Server/src/main/content/region/asgarnia/rimmington/dialogue/RimmingtonShopKeeperDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/VarrockShopKeeperDialogue.java @@ -1,4 +1,4 @@ -package content.region.asgarnia.rimmington.dialogue; +package content.region.misthalin.varrock.dialogue; import core.game.dialogue.DialoguePlugin; import core.game.dialogue.FacialExpression; @@ -7,23 +7,39 @@ import core.plugin.Initializable; import core.game.node.entity.player.Player; /** - * Handles the RimmingtonShopKeeperDialogue dialogue. - * @author 'Vexia + * Represents the Varrock shop keeper dialogue. */ @Initializable -public class RimmingtonShopKeeperDialogue extends DialoguePlugin { - - public RimmingtonShopKeeperDialogue() { +public final class VarrockShopKeeperDialogue extends DialoguePlugin { + /** + * Constructs a new {@code VarrockShopKeeperDialogue} {@code Object}. + */ + public VarrockShopKeeperDialogue() { + /** + * empty. + */ } - public RimmingtonShopKeeperDialogue(Player player) { + /** + * Constructs a new {@code VarrockShopKeeperDialogue} {@code Object}. + * @param player the player. + */ + public VarrockShopKeeperDialogue(Player player) { super(player); } @Override - public int[] getIds() { - return new int[] { 531, 530 }; + public DialoguePlugin newInstance(Player player) { + return new VarrockShopKeeperDialogue(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; } @Override @@ -34,41 +50,29 @@ public class RimmingtonShopKeeperDialogue extends DialoguePlugin { stage = 1; break; case 1: - switch (buttonId) { case 1: end(); npc.openShop(player); break; case 2: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "I'm glas 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; + 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 = 20; break; case 3: end(); break; } - break; - case 10: + case 20: 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.HALF_GUILTY, "Can I help you at all?"); - stage = 0; - return true; + public int[] getIds() { + return new int[] { 522, 523 }; } } diff --git a/Server/src/main/content/region/misthalin/varrock/dialogue/VarrockSwordShopDialogue.java b/Server/src/main/content/region/misthalin/varrock/dialogue/VarrockSwordShopDialogue.java index 77258f13e..d0deef1e4 100644 --- a/Server/src/main/content/region/misthalin/varrock/dialogue/VarrockSwordShopDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/VarrockSwordShopDialogue.java @@ -4,11 +4,14 @@ 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; /** * Handles the VarrockSwordShopDialogue dialogue. * @author 'Vexia */ + +@Initializable public class VarrockSwordShopDialogue extends DialoguePlugin { public VarrockSwordShopDialogue() { @@ -21,7 +24,7 @@ public class VarrockSwordShopDialogue extends DialoguePlugin { @Override public int[] getIds() { - return new int[] { 551 }; + return new int[] { 551, 552 }; } @Override @@ -36,11 +39,11 @@ public class VarrockSwordShopDialogue extends DialoguePlugin { switch (buttonId) { case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Yes, please."); + interpreter.sendDialogues(player, FacialExpression.HAPPY, "Yes, please."); stage = 2; break; case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No, I'm okay for swords right now."); + interpreter.sendDialogues(player, FacialExpression.FRIENDLY, "No, I'm okay for swords right now."); stage = 10; break; } @@ -51,7 +54,7 @@ public class VarrockSwordShopDialogue extends DialoguePlugin { npc.openShop(player); break; case 10: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Come back if you need any."); + interpreter.sendDialogues(npc, FacialExpression.FRIENDLY, "Come back if you need any."); stage = 11; break; case 11: @@ -71,7 +74,7 @@ public class VarrockSwordShopDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Hello, adventurer. Can I interest you in some swords?"); + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Hello, adventurer. Can I interest you in some swords?"); stage = 0; return true; } 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 new file mode 100644 index 000000000..677934f19 --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/dialogue/surok/AbyssalBook.kt @@ -0,0 +1,379 @@ +package content.region.misthalin.varrock.dialogue.surok + +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 + +@Initializable +class AbyssalBook : InteractionListener { + companion object { + private val TITLE = "Abyssal book" + private val CONTENTS = arrayOf( + PageSet( + Page( + BookLine("A Compendium of Research", 55), + BookLine("Into Chaotic Space.", 56), + BookLine("Author Unknown", 57), + BookLine("", 58), + BookLine("The strange dimension that", 59), + BookLine("we have name 'Abyssal Space'", 60), + BookLine("is something of an enigma.", 61), + BookLine("It was first discovered during", 62), + BookLine("a routine teleportation", 63), + BookLine("experiment that seemingly", 64), + BookLine("went wrong. We are still", 65), + ), + Page( + BookLine("not sure as to what caused", 66), + BookLine("this teleportation failure,", 67), + BookLine("but the discovery of a", 68), + BookLine("previously unknown dimension", 69), + BookLine("led to a flurry of", 70), + BookLine("research from the", 71), + BookLine("Zamorakian Magical Institute", 72), + BookLine("(henceforth known within", 73), + BookLine("(this document as the Z.M.I.).", 74), + ) + ), + PageSet( + Page( + BookLine("Under direct orders to examine", 55), + BookLine("this dimension, I feel I can", 56), + BookLine("accurately state the", 57), + BookLine("following conclusions.", 58), + BookLine("", 59), + BookLine("Conclusion One", 60), + BookLine("", 61), + BookLine("Abyssal Space is not", 62), + BookLine("a dimension in the way", 63), + BookLine("that we understand the term", 64), + BookLine("from examples such as", 65), + ), + Page( + BookLine("Zanaris or Feneskrae.", 66), + BookLine("", 67), + BookLine("Rather, it is the name", 68), + BookLine("we have given the dimension", 69), + BookLine("that exists between other", 70), + BookLine("more developed dimensions", 71), + ) + ), + PageSet( + Page( + BookLine("- the 'glue' that keeps", 55), + BookLine("each dimension together yet", 56), + BookLine("separate, if you will.", 57), + BookLine("", 58), + BookLine("The Abyssal space's existence", 59), + BookLine("at the 'fringe' of reality,", 60), + BookLine("means that it does not", 61), + BookLine("confirm to the same guidelines", 62), + BookLine("of space and time as Gielinor", 63), + BookLine("does; you may enter it and", 64), + BookLine("then leave it from an", 65), + ), + Page( + BookLine("identical spot, yet", 66), + BookLine("", 67), + BookLine("reappear many hundreds of", 68), + BookLine("miles at your target", 69), + BookLine("destination", 70), + BookLine("(the 'teleportation'", 71), + BookLine("phenomenon that we use daily).", 72), + ) + ), + PageSet( + Page( + BookLine("From this basic concept,", 55), + BookLine("I have extrapolated that", 56), + BookLine("all teleport magics in", 57), + BookLine("fact use Abyssal Space", 58), + BookLine("to make the passing of great", 59), + BookLine("distances occur in", 60), + BookLine("a very short space", 61), + BookLine("of time, from our perception.", 62), + BookLine("", 63), + BookLine("What is actually happening,", 64), + BookLine("is that the spellcaster is", 65), + ), + Page( + BookLine("entering abyssal space,", 66), + BookLine("and then immediately", 67), + BookLine("leaving again,", 68), + BookLine("with certain values as to speed", 69), + BookLine("and direction being taken care", 70), + BookLine("of in our spellingcasting to", 71), + BookLine("allow some degree of precision", 72), + BookLine("in these teleports.", 73), + ) + ), + PageSet( + Page( + BookLine("More worryingly,", 55), + BookLine("it seems apparent that", 56), + BookLine("the barriers between", 57), + BookLine("our dimensional space", 58), + BookLine("and abyssal space have", 59), + BookLine("become somewhat weakened", 60), + BookLine("through excessive use", 61), + BookLine("There have been isolated", 62), + BookLine("reports within the Z.M.I.", 63), + BookLine("that creatures not native", 64), + BookLine("to our dimension have entered", 65), + ), + Page( + BookLine("Gielinor through", 66), + BookLine("abyssal space,", 67), + BookLine("as well as the teleportation", 68), + BookLine("malfunction that first", 69), + BookLine("resulted in our discovery", 70), + BookLine("of this dimension.", 71), + ) + ), + PageSet( + Page( + BookLine("I strongly recommend that", 55), + BookLine("further research is taken", 56), + BookLine("- if the barriers between", 57), + BookLine("these dimensions are", 58), + BookLine("sufficiently weakened,", 59), + BookLine("there may exist the", 60), + BookLine("possibility of an alternative", 61), + BookLine("method to proceed", 62), + BookLine("with Operation:", 63), + BookLine("Transient without altering", 64), + BookLine("the other deities to our plans.", 65), + ), + Page( + BookLine("Conclusion Two", 66), + BookLine("", 67), + BookLine("When we have accepted", 68), + BookLine("that Abyssal Space is", 69), + BookLine("somewhat of a tesseract", 70), + BookLine("or hypercube with a direct", 71), + BookLine("relation to our dimension,", 72), + BookLine("then the benefits of", 73), + BookLine("exploiting this resource", 74), + BookLine("become more obvious.", 75), + ) + ), + PageSet( + Page( + BookLine("I ran some experiments", 55), + BookLine("with Sample XJ13", 56), + BookLine("(also known as 'rune essence')", 57), + BookLine("and managed to place six", 58), + BookLine("parts in a space that would", 59), + BookLine("seemingly only hold one.", 60), + BookLine("Continued", 61), + BookLine("experimentation with these", 62), + BookLine("stolen samples showed that", 63), + BookLine("moving items between our", 64), + BookLine("dimension and abyssal space", 65), + ), + Page( + BookLine("degraded the use of these", 66), + BookLine("pouches, but a simple", 67), + BookLine("transfiguration spell", 68), + BookLine("when cast within the abyss", 69), + BookLine("upon these pouches restored", 70), + BookLine("their usage back to", 71), + BookLine("the original results.", 72), + ) + ), + PageSet( + Page( + BookLine("Should we ever locate the", 55), + BookLine("source of these 'essence'", 56), + BookLine("that the Wizard Tower", 57), + BookLine("seem to have an endless", 58), + BookLine("supply of, I would strongly", 59), + BookLine("recommend the harvesting of", 60), + BookLine("these creatures for their", 61), + BookLine("organs so as to maximize", 62), + BookLine("the efficiency of our", 63), + BookLine("rune manufacturing process.", 64), + ), + Page( + BookLine("Some degree of caution will", 66), + BookLine("be necessary, as the creatures", 67), + BookLine("the creatures of the abyss", 68), + BookLine("are seemingly very aggressive.", 69), + ), + ), + PageSet( + Page( + BookLine("Conclusion Three", 55), + BookLine("", 56), + BookLine("Our first discovery of", 57), + BookLine("Abyssal Space was somewhat", 58), + BookLine("of a fluke - and a not easily", 59), + BookLine("repeatable fluke at that.", 60), + BookLine("It proved exceedingly", 61), + BookLine("difficult to find the", 62), + BookLine("correct mystical resonance", 63), + BookLine("for this dimension, due to", 64), + BookLine("my original belief that", 65), + ), + Page( + BookLine("Abyssal space is not a fully", 66), + BookLine("fledged dimension of its own,", 67), + BookLine("so we have had to resort", 68), + BookLine("to unusual measures to", 69), + BookLine("gain permanent access", 70), + BookLine("to this realm.", 71), + ), + ), + PageSet( + Page( + BookLine("We took a large number of", 55), + BookLine("initiates, and gave them", 56), + BookLine("each supplies to cast", 57), + BookLine("a portal spell. We then", 58), + BookLine("had them repeatedly teleport", 59), + BookLine("to various locations,", 60), + BookLine("seeking to replicate the", 61), + BookLine("original error that", 62), + BookLine("caused the first entry", 63), + BookLine("into Abyssal Space.", 64), + BookLine("Once one of our initiates", 65), + ), + Page( + BookLine("had managed to 'fail' his", 66), + BookLine("teleport and appear in", 67), + BookLine("Abyssal Space. he was", 68), + BookLine("then charged with remaining", 69), + BookLine("there and holding a portal", 70), + BookLine("spell open, so that more", 71), + BookLine("senior members of the Z.M.I.", 72), + ), + ), + PageSet( + Page( + BookLine("could gain entry via his portal.", 55), + BookLine("This initiate is still there,", 56), + BookLine("and due to the intense", 57), + BookLine("concentration required", 58), + BookLine("to keep he portal open,", 59), + BookLine("it is my recommendation", 60), + BookLine("that we leave him there", 61), + BookLine("holding the bridge open for us.", 62), + BookLine("As an initiate, he is", 63), + BookLine("always expendable should", 64), + BookLine("something go wrong, and", 65), + ), + Page( + BookLine("the slow passage of", 66), + BookLine("time within Abyssal Space", 67), + BookLine("means we don't need to", 68), + BookLine("worry about feeding", 69), + BookLine("him or anything.", 70), + BookLine("At the time of writing,", 71), + BookLine("this portal is still active,", 72), + BookLine("and will allow us to teleport", 73), + BookLine("people at will into Abyssal Space.", 74), + BookLine("The only downside to this", 75), + BookLine("method of teleportation", 76), + ), + ), + PageSet( + Page( + BookLine("is that we are using", 55), + BookLine("magic provided to us", 56), + BookLine("by our Lord Zamorak himself,", 57), + BookLine("so anybody who uses this", 58), + BookLine("teleport will inevitably be", 59), + BookLine("marked by him - or become", 60), + BookLine("'skulled' as the common", 61), + BookLine("folk put it.", 62), + BookLine("An interesting side-effect", 63), + BookLine("of this portal, is that", 64), + BookLine("various teleports within Abyssal", 65), + ), + Page( + BookLine("Space were opened up by", 66), + BookLine("it's casting. These teleports", 67), + BookLine("seem to lead to mysterious", 68), + BookLine("temples dedicated to various", 69), + BookLine("magical elements, which I", 70), + BookLine("believe are directly related", 71), + BookLine("to the rumours we have", 72), + ), + ), + PageSet( + Page( + BookLine("intercepted of the rediscovery", 55), + BookLine("of Runecrafting by the", 56), + BookLine("Wizards Tower. Sadly, we", 57), + BookLine("must conclude from these", 58), + BookLine("temples that the rumours are", 59), + BookLine("indeed true, and that the", 60), + BookLine("destruction of the Wizards", 61), + BookLine("Tower had been in vain,", 62), + BookLine("as was the sacrifice of", 63), + BookLine("those who died to try", 64), + BookLine("and prevent the meddling", 65), + ), + Page( + BookLine("Saradominists from gaining", 66), + BookLine("access to the creation of", 67), + BookLine("magical runes.", 68), + BookLine("I have detailed my findings", 69), + BookLine("relating to RuneCrafting", 70), + BookLine("in a separate document, and", 71), + BookLine("passed it on to my superiors,", 72), + BookLine("along with me recommendations", 73), + BookLine("on how best to thwart their", 74), + BookLine("research further.", 75), + ) + ), + PageSet( + Page( + BookLine("Until a final decision is taken,", 55), + BookLine("I suggest we make the best", 56), + BookLine("of a bad situation,", 57), + BookLine("and increase our own rune", 58), + BookLine("production to full", 59), + BookLine("manufacturing capabilities.", 60), + BookLine("", 61), + BookLine("I have already ordered buyers", 62), + BookLine("to purchase as much", 63), + BookLine("Sample XJ13", 64), + BookLine("as can be bought, and to", 65), + ), + Page( + BookLine("hire some mercenaries to", 66), + BookLine("sabotage the research efforts", 67), + BookLine("of the Wizards Tower, or", 68), + BookLine("failing that to provide us", 69), + BookLine("some insight into supplies", 70), + BookLine("where their study of these", 71), + BookLine("essence are coming from.", 72), + BookLine("", 73), + BookLine("Until my next report, I", 74), + BookLine("remain as ever a loyal servant.", 75), + BookLine("Strength Through Chaos!", 76), + ) + ), + ) + } + + 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.ABYSSAL_BOOK_5520, 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/misthalin/varrock/diary/VarrockAchivementDiary.kt b/Server/src/main/content/region/misthalin/varrock/diary/VarrockAchivementDiary.kt index a75f6b7a9..f49899210 100644 --- a/Server/src/main/content/region/misthalin/varrock/diary/VarrockAchivementDiary.kt +++ b/Server/src/main/content/region/misthalin/varrock/diary/VarrockAchivementDiary.kt @@ -1,7 +1,6 @@ package content.region.misthalin.varrock.diary import content.global.skill.prayer.Bones -import content.global.travel.canoe.Canoe import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player import core.game.node.entity.player.link.diary.DiaryType @@ -13,16 +12,24 @@ import org.rs09.consts.Scenery import content.region.misthalin.varrock.dialogue.ElsieDialogue import content.global.handlers.iface.FairyRing import content.global.skill.magic.TeleportMethod -import content.global.skill.magic.spellconsts.Modern +import content.global.travel.canoe.CanoeListener +import core.api.getStatLevel +import core.api.inBorders import core.game.diary.AreaDiaryTask import core.game.diary.DiaryEventHookBase 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 { private val VARROCK_ROOF_AREA = ZoneBorders(3201, 3467, 3225, 3497, 3) private val SOS_LEVEL_2_AREA = ZoneBorders(2040, 5241, 2046, 5246) + private val VARROCK_PALACE = ZoneBorders(3201, 3456, 3227, 3468) + private val CHAMPIONS_GUILD = ZoneBorders(3188, 3361, 3194, 3362) + private val OZIACH_SHOP = ZoneBorders(3066, 3514,3070, 3518) private val STRAY_DOGS = arrayOf( NPCs.STRAY_DOG_4766, NPCs.STRAY_DOG_4767, @@ -103,6 +110,12 @@ class VarrockAchivementDiary : DiaryEventHookBase(DiaryType.VARROCK) { SOS_LEVEL_2_AREA, DiaryLevel.EASY, EasyTasks.VISIT_SOS_LEVEL2 + ), + + AreaDiaryTask( + CHAMPIONS_GUILD, + DiaryLevel.MEDIUM, + MediumTasks.CHAMPIONS_GUILD_VISIT ) ) @@ -167,6 +180,38 @@ class VarrockAchivementDiary : DiaryEventHookBase(DiaryType.VARROCK) { EasyTasks.EDGEVILLE_ENTER_DUNGEON_SOUTH ) } + 12598 -> if (event.target.id == 9312 && player.skills.getLevel(Skills.AGILITY, true) >= 21) { + finishTask( + player, + DiaryLevel.MEDIUM, + MediumTasks.USE_GE_UNDER_WALL_SHORTCUT + ) + } + 12698 -> if (event.target.id == 29370 && player.skills.getLevel(Skills.AGILITY, true) >= 51) { + finishTask( + player, + DiaryLevel.HARD, + HardTasks.USE_MOSS_GIANT_PIPE_SHORTCUT + ) + } + } + + if (event.option == "pickpocket" && (event.target.id == NPCs.GUARD_5920 && inBorders(player, VARROCK_PALACE) && getStatLevel(player, Skills.THIEVING) >= 40)) { + finishTask( + player, + DiaryLevel.MEDIUM, + MediumTasks.PALACE_PICKPOCKET_GUARD + ) + } + + if (player.questRepository.isComplete(Quests.DRAGON_SLAYER)) { + if (event.target.id == NPCs.OZIACH_747 && event.option == "trade" && inBorders(player, OZIACH_SHOP)) { + finishTask( + player, + DiaryLevel.MEDIUM, + MediumTasks.OZIACH_BROWSE_STORE + ) + } } } @@ -175,8 +220,8 @@ class VarrockAchivementDiary : DiaryEventHookBase(DiaryType.VARROCK) { player.viewport.region?.let { when (it.id) { 12342 -> { - if (event.iface == Components.CANOE_52 - && Canoe.getCanoeFromChild(event.buttonId) == Canoe.WAKA) { + if (event.iface == CanoeListener.CANOE_SHAPING_INTERFACE + && event.buttonId == CanoeListener.CANOE_SHAPING_BUTTONS[CanoeListener.Companion.Canoes.WAKA.ordinal]) { finishTask( player, DiaryLevel.HARD, @@ -226,14 +271,12 @@ class VarrockAchivementDiary : DiaryEventHookBase(DiaryType.VARROCK) { } override fun onSpellCast(player: Player, event: SpellCastEvent) { - when (event.spellId) { - Modern.VARROCK_TELEPORT -> { - finishTask( - player, - DiaryLevel.MEDIUM, - MediumTasks.CAST_VARROCK_TELEPORT_SPELL - ) - } + if (event.spellBook == SpellBookManager.SpellBook.MODERN && event.spellId == 15) { + finishTask( + player, + DiaryLevel.MEDIUM, + MediumTasks.CAST_VARROCK_TELEPORT_SPELL + ) } } 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/ChampionsGuildDoor.kt b/Server/src/main/content/region/misthalin/varrock/handlers/ChampionsGuildDoor.kt index 8814d351a..4d6187d7c 100644 --- a/Server/src/main/content/region/misthalin/varrock/handlers/ChampionsGuildDoor.kt +++ b/Server/src/main/content/region/misthalin/varrock/handlers/ChampionsGuildDoor.kt @@ -1,6 +1,5 @@ package content.region.misthalin.varrock.handlers -import core.game.node.entity.player.link.diary.DiaryType import core.plugin.Initializable import org.rs09.consts.Scenery import core.game.interaction.IntType @@ -22,14 +21,8 @@ class ChampionsGuildDoor : InteractionListener { player.packetDispatch.sendMessage("The door won't open - you need at least 32 Quest Points.") } else { if (player.location.x == 3191 && player.location.y == 3363) { - player.dialogueInterpreter.sendDialogues( - 198, - null, - "Greetings bold adventurer. Welcome to the guild of", - "Champions." - ) + player.dialogueInterpreter.sendDialogues(198, null, "Greetings bold adventurer. Welcome to the guild of", "Champions.") } - player.achievementDiaryManager.finishTask(player, DiaryType.VARROCK, 1, 1) core.game.global.action.DoorActionHandler.handleAutowalkDoor(player, node as core.game.node.scenery.Scenery) } return@on true diff --git a/Server/src/main/content/region/misthalin/varrock/handlers/GrandExchangeShortcut.kt b/Server/src/main/content/region/misthalin/varrock/handlers/GrandExchangeShortcut.kt index 5920138df..45994537a 100644 --- a/Server/src/main/content/region/misthalin/varrock/handlers/GrandExchangeShortcut.kt +++ b/Server/src/main/content/region/misthalin/varrock/handlers/GrandExchangeShortcut.kt @@ -4,7 +4,6 @@ import core.api.* import core.game.node.entity.impl.ForceMovement import core.game.node.entity.impl.ForceMovement.direction 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.scenery.Scenery import core.game.system.task.Pulse @@ -63,7 +62,7 @@ class GrandExchangeShortcut : InteractionListener { lock(player, 4) val o = node as Scenery val path = SHORTCUTS[o.id]!! - ForceMovement.run(player, path[0], o.location, ForceMovement.WALK_ANIMATION, CLIMB_DOWN, direction(path[0], o.location), ForceMovement.WALKING_SPEED, ForceMovement.WALKING_SPEED, false); + ForceMovement.run(player, path[0], o.location, ForceMovement.WALK_ANIMATION, CLIMB_DOWN, direction(path[0], o.location), ForceMovement.WALKING_SPEED, ForceMovement.WALKING_SPEED, false) runCrawlPulse(player, path) return@on true } @@ -93,8 +92,6 @@ class GrandExchangeShortcut : InteractionListener { path[2], CLIMB_UP ) - // Use the shortcut under the wall, north-west of the Grand

Exchange - player.achievementDiaryManager.finishTask(player, DiaryType.VARROCK, 1, 8) unlock(player) 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/SawmillListener.kt b/Server/src/main/content/region/misthalin/varrock/handlers/SawmillListener.kt new file mode 100644 index 000000000..7d7f2eeeb --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/handlers/SawmillListener.kt @@ -0,0 +1,31 @@ +package content.region.misthalin.varrock.handlers + +import core.game.component.Component +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.world.map.Location +import org.rs09.consts.NPCs + +class SawmillListener : InteractionListener { + override fun defineListeners() { + on(NPCs.SAWMILL_OPERATOR_4250, IntType.NPC, "talk-to") { player, node -> + player.dialogueInterpreter.open(4250, node) + return@on true + } + + on(NPCs.SAWMILL_OPERATOR_4250, IntType.NPC, "buy-plank") { player, _ -> + player.interfaceManager.open( Component(403)) + return@on true + } + + on(NPCs.SAWMILL_OPERATOR_4250, IntType.NPC, "trade") { player, node -> + node.asNpc().openShop(player) + return@on true + } + + setDest(IntType.NPC, intArrayOf(NPCs.SAWMILL_OPERATOR_4250), "talk-to", "buy-plank", "trade") { _, _ -> + return@setDest Location.create(3302, 3491 , 0) + } + + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/misthalin/varrock/handlers/SawmillOptionPlugin.java b/Server/src/main/content/region/misthalin/varrock/handlers/SawmillOptionPlugin.java deleted file mode 100644 index 8801cb4a5..000000000 --- a/Server/src/main/content/region/misthalin/varrock/handlers/SawmillOptionPlugin.java +++ /dev/null @@ -1,48 +0,0 @@ -package content.region.misthalin.varrock.handlers; - -import core.cache.def.impl.NPCDefinition; -import core.game.component.Component; -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; - -/** - * Represents the plugin to handle the options for saw mill man. - * @author 'Vexia - * @date Oct 8, 2013 - */ -@Initializable -public class SawmillOptionPlugin extends OptionHandler { - - @Override - public Plugin newInstance(Object arg) throws Throwable { - NPCDefinition.forId(4250).getHandlers().put("option:buy-plank", this); - NPCDefinition.forId(4250).getHandlers().put("option:talk-to", this); - NPCDefinition.forId(4250).getHandlers().put("option:trade", this); - return this; - } - - @Override - public boolean handle(Player player, Node node, String option) { - switch (option) { - case "buy-plank": - player.getInterfaceManager().open(new Component(403)); - return true; - case "talk-to": - player.getDialogueInterpreter().open(4250, node); - return true; - case "trade": - node.asNpc().openShop(player); - return true; - } - return true; - } - - @Override - public Location getDestination(Node node, Node n) { - return Location.create(3302, 3491, 0); - } -} 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/VarrockGuardSignpost.kt b/Server/src/main/content/region/misthalin/varrock/handlers/VarrockGuardSignpost.kt index e6ca1ae8c..29a4526a7 100644 --- a/Server/src/main/content/region/misthalin/varrock/handlers/VarrockGuardSignpost.kt +++ b/Server/src/main/content/region/misthalin/varrock/handlers/VarrockGuardSignpost.kt @@ -1,15 +1,17 @@ package content.region.misthalin.varrock.handlers -import core.api.* +import core.GlobalStats +import core.api.log +import core.api.registerMapZone +import core.api.sendDialogue +import core.game.interaction.IntType +import core.game.interaction.InteractionListener import core.game.interaction.Option import core.game.node.Node import core.game.node.entity.Entity import core.game.world.map.zone.MapZone import core.game.world.map.zone.ZoneBorders -import core.GlobalStats -import core.game.interaction.InteractionListener -import core.game.interaction.IntType -import core.tools.SystemLogger +import core.tools.Log class VarrockGuardSignpost : InteractionListener { override fun defineListeners() { @@ -29,7 +31,7 @@ class VarrockGuardSignpost : InteractionListener { on(31298, IntType.SCENERY, "read"){ player, _ -> val pickpocketCount = GlobalStats.getDailyGuardPickpockets() - SystemLogger.logInfo(this::class.java, "Is equal? ${pickpocketCount == 0}") + log(this::class.java, Log.FINE, "Is equal? ${pickpocketCount == 0}") when(pickpocketCount){ 0 -> sendDialogue(player, "The Varrock Palace guards are pleased to announce that crime is at an all-time low, without a single guard in the palace or at the city gates being pickpocketed today.") 1 -> sendDialogue(player, "One of the Varrock Palace guards was pickpocketed today. He was close to tears at having lost his last few gold pieces." ) 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 19d0c1b70..1af2980f0 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, @@ -387,7 +388,7 @@ class ZaffPlugin : OptionHandler() { ) stage = 1 } - 1 -> end().also { sendInputDialogue(player, InputType.NUMERIC, "Enter an amount:"){ value -> + 1 -> end().also { sendInputDialogue(player, InputType.AMOUNT, "Enter an amount:"){ value -> ammount = getStoreFile().getInt(player.username.toLowerCase()) var amt = value as Int if(amt > maxStaffs - ammount) amt = maxStaffs - ammount @@ -403,7 +404,7 @@ class ZaffPlugin : OptionHandler() { if(removeItem(player, Item(Items.COINS_995, coinage), Container.INVENTORY)){ addItem(player, Items.BATTLESTAFF_1392, amt) - ZaffPlugin.getStoreFile()[player.username.toLowerCase()] = amt + ammount + getStoreFile()[player.username.toLowerCase()] = amt + ammount } } } 2 -> { @@ -430,7 +431,7 @@ class ZaffPlugin : OptionHandler() { 2 -> 64 1 -> 32 0 -> 16 - else -> 0 + else -> 8 } } 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 b617cd913..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,19 +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 /** * 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 } @@ -22,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) } } @@ -81,9 +149,8 @@ class AllFiredUp : Quest("All Fired Up", 157, 156, 1){ player.packetDispatch.sendItemZoomOnInterface(Items.TINDERBOX_590, 235, 277, 3 + 2) player.skills.addExperience(Skills.FIREMAKING, 5500.0) player.inventory.add(Item(995,20000)) - player.varpManager.unflagSave(1283) - content.minigame.allfiredup.AFUBeacon.resetAllBeacons(player) - player.varpManager.get(1283).setVarbit(0,0).send(player) + AFUBeacon.resetAllBeacons(player) + setVarbit(player, 1283, 0) player.questRepository.syncronizeTab(player) } @@ -92,4 +159,4 @@ class AllFiredUp : Quest("All Fired Up", 157, 156, 1){ if(stage > 0) return intArrayOf(1282, 1) else return intArrayOf(1282, 0) } -} \ No newline at end of file +} 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 329fee8e9..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,16 +5,17 @@ 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) : core.game.dialogue.DialoguePlugin(player) { - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { +class BlazeSharpeyeDialogue(player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player?): DialoguePlugin { return BlazeSharpeyeDialogue(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 } @@ -38,7 +39,7 @@ class BlazeSharpeyeDialogue(player: Player? = null) : core.game.dialogue.Dialogu 102 -> player("It sounds like you've already got everything figured","out. How do I fit into this?").also { stage++ } 103 -> npc("Well, to test these beacons, we need someone to light a","couple of them for us, so that we can determine","whether or not they'll actually be visible.").also { stage++ } 104 -> player("You mean, you went out and built these huge things","without knowing that they'd work?").also { stage++ } - 105 -> npc("Dont interrupt. Now, yours truly has recruited some","of the sharpest eyes this side of the River Salve to look","after these beacons and to watch the horizon for signs","of danger.").also { stage++ } + 105 -> npc("Don't interrupt. Now, yours truly has recruited some","of the sharpest eyes this side of the River Salve to look","after these beacons and to watch the horizon for signs","of danger.").also { stage++ } 106 -> npc("In case of emergency, we've all got a supply of logs","and can start a fire faster than a cat can sneeze.").also { stage++ } 107 -> npc("As soon as we spot a threat from the Wilderness or the","glow from another beacon, we can light our own","beacons and send a warning right across Misthalin,","faster than any courier could ever run.").also { stage++ } 108 -> npc("We've agreed to use gnomish firelighters to colour the","flames of our beacons' fires in an actual emergency, so","it's safe to test the network with normal fires.").also { stage++ } @@ -46,9 +47,9 @@ class BlazeSharpeyeDialogue(player: Player? = null) : core.game.dialogue.Dialogu 110 -> npc("I need you to light a couple of beacons for me to make","sure we can see the glow from the flames over the","horizon.").also { stage++ } 111 -> npc("So! First of all, you'll need to know how to light a beacon.").also { stage++ } 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(core.game.dialogue.FacialExpression.AMAZED,"SET IT ON FIRE WITH A TINDERBOX").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,15 +62,15 @@ class BlazeSharpeyeDialogue(player: Player? = null) : core.game.dialogue.Dialogu 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++ } 301 -> npc("Absolutely! I can see a warm, delicious glow on the","horizon. If you have sharp eyes and know what to look","for it's trivial.").also { stage++ } 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 restor 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) } + 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(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,8 +95,8 @@ class BlazeSharpeyeDialogue(player: Player? = null) : core.game.dialogue.Dialogu 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) } - 420 -> npc("Yes... YESS HAHAHAHHA FIRE").also { stage = 412 } + 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 cf0145e99..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 @@ -5,16 +5,18 @@ import core.game.node.entity.npc.NPC 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) : core.game.dialogue.DialoguePlugin(player){ - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { +class SquireFyreDialogue(player: Player? = null) : DialoguePlugin(player){ + override fun newInstance(player: Player?): DialoguePlugin { return SquireFyreDialogue(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 } @@ -26,8 +28,7 @@ class SquireFyreDialogue(player: Player? = null) : core.game.dialogue.DialoguePl 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); player.varpManager.get(1283).setVarbit(0, - content.minigame.allfiredup.BeaconState.DYING.ordinal).send(player) } + 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 } @@ -50,4 +51,4 @@ class SquireFyreDialogue(player: Player? = null) : core.game.dialogue.DialoguePl return intArrayOf(8066) } -} \ No newline at end of file +} 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 5ad418238..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 @@ -10,6 +10,10 @@ 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; + + /** * Represents the demon slayer plugin used to handle washing the key down the drain. * @author 'Vexia @@ -48,28 +52,28 @@ 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); player.getPacketDispatch().sendMessage("You pour the liquid down the drain."); if (quest.getStage(player) != 20) { - System.out.println("1"); + return true; } if (player.getAttribute("demon-slayer:just-poured", false)) { - System.out.println("2"); + return true; } if (!player.hasItem(DemonSlayer.FIRST_KEY)) { - System.out.println("3"); + player.getSavedData().getQuestData().getDemonSlayer()[0] = false; } if (quest.getStage(player) == 20 && !player.hasItem(DemonSlayer.FIRST_KEY) && !player.getSavedData().getQuestData().getDemonSlayer()[0]) { - System.out.println("4"); + player.getDialogueInterpreter().sendDialogues(player, null, "OK, I think I've washed the key down into the sewer.", "I'd better go down and get it!"); player.getSavedData().getQuestData().getDemonSlayer()[0] = true;// poured - player.getConfigManager().set(222, 2660610, true); + setVarp(player, 222, 2660610, true); player.setAttribute("demon-slayer:just-poured", true); return true; } 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 07180183d..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 @@ -29,6 +29,9 @@ import core.net.packet.context.CameraContext.CameraType; 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. * @author Vexia @@ -119,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); @@ -419,8 +422,8 @@ public final class DemonSlayerCutscene extends CutscenePlugin { player.unlock(); cutscene.end(); cutscene.delrith.clear(); - player.getConfigManager().set(222, 5653570, true); - player.getQuestRepository().getQuest("Demon Slayer").finish(player); + setVarp(player, 222, 5653570, true); + player.getQuestRepository().getQuest(Quests.DEMON_SLAYER).finish(player); end(); return true; } @@ -517,7 +520,7 @@ public final class DemonSlayerCutscene extends CutscenePlugin { player.setLocation(cutscene.getBase().transform(26, 48, 0)); PacketRepository.send(CameraViewPacket.class, new CameraContext(player, CameraType.POSITION, player.getLocation().getX() - 1, player.getLocation().getY() - 3, 390, 1, 100)); PacketRepository.send(CameraViewPacket.class, new CameraContext(player, CameraType.ROTATION, player.getLocation().getX() + 30, player.getLocation().getY() - 55, 390, 1, 100)); - player.getConfigManager().set(222, 14194946, true); + setVarp(player, 222, 14194946, true); SceneryBuilder.replace(cutscene.stoneTable, cutscene.stoneTable.transform(17438)); npc("Ha ha ha! At last you are free, my demonic brother!", "Rest now, and then have your revenge on this pitiful", "city!"); stage = 2; 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 4fa25dbe6..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,11 +9,13 @@ 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; + + /** * Represents the demon slayer plugin used to handle relative node interactions. * @author 'Vexia @@ -34,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); @@ -46,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: @@ -68,26 +66,9 @@ 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)) { - player.getConfigManager().set(222, 4757762, true); + setVarp(player, 222, 4757762, true); player.removeAttribute("demon-slayer:poured"); player.removeAttribute("demon-slayer:just-poured"); player.getDialogueInterpreter().sendItemMessage(DemonSlayer.FIRST_KEY.getId(), "You pick up an old rusty key."); 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 166a6b54f..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,13 +1,21 @@ package content.region.misthalin.varrock.quest.demonslayer; +import core.api.Container; 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 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 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. @@ -56,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?"); @@ -196,7 +204,7 @@ public class SirPyrsinDialogue extends DialoguePlugin { npc.animate(new Animation(4597)); break; case 9: - player.getConfigManager().set(222, 5653570, true); + setVarp(player, 222, 5653570, true); player.setAttribute("/save:demon-slayer:received", true); npc.animate(new Animation(4607)); break; @@ -473,7 +481,10 @@ public class SirPyrsinDialogue extends DialoguePlugin { private final void handleDefault(int buttonId) { switch (stage) { case 0: - options("I am a mighty adventurer. Who are you?", "I'm not sure, I was hoping you could tell me."); + 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."); stage = 1; break; case 1: @@ -486,8 +497,16 @@ public class SirPyrsinDialogue extends DialoguePlugin { player("I'm not sure, I was hoping you could tell me."); stage = 20; break; + case 3: + if(hasAnItem(player, Items.SILVERLIGHT_2402).getContainer() != null) { + npc(id, "You already have Silverlight"); + stage = END_DIALOGUE; + } else { + npc("I can sell you another one for 500GP"); + stage = 30; + } break; } - break; + break; case 10: npc(id, "I am Sir Prysin. A bold and famous knight of the", "realm."); stage = 11; @@ -502,6 +521,18 @@ public class SirPyrsinDialogue extends DialoguePlugin { case 21: end(); break; + case 30: + if(inInventory(player, Items.COINS_995, 500)) { + if(removeItem(player, new Item(Items.COINS_995, 500), Container.INVENTORY)) { + addItem(player, Items.SILVERLIGHT_2402, 1, Container.INVENTORY); + sendItemDialogue(player, Items.SILVERLIGHT_2402, "Sir Prysin gives you another Silverlight"); + stage = END_DIALOGUE; + } + } else { + npc(id, "Come back when you have 500GP to buy another one."); + stage = END_DIALOGUE; + } + break; } } @@ -509,4 +540,4 @@ public class SirPyrsinDialogue extends DialoguePlugin { public int[] getIds() { return new int[] { 883, 4657 }; } -} \ No newline at end of file +} 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/DSChestDialogue.java b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/DSChestDialogue.java index 386db72de..604bfb0c8 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/DSChestDialogue.java +++ b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/DSChestDialogue.java @@ -48,7 +48,7 @@ public final class DSChestDialogue extends DialoguePlugin { stage = 1; break; case 1: - interpreter.sendDialogue("This map leads to the lair of the beast that destroyed my home,", "devoured my family, and burned to a cinder all that I love. But", "revenge would not benefit me now, and to disturb this beast is to risk", "brining its wrath down upon another land."); + interpreter.sendDialogue("This map leads to the lair of the beast that destroyed my home,", "devoured my family, and burned to a cinder all that I love. But", "revenge would not benefit me now, and to disturb this beast is to risk", "bringing its wrath down upon another land."); stage = 2; break; case 2: diff --git a/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/DSListeners.kt b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/DSListeners.kt index ff24e7eed..20efdab26 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/DSListeners.kt +++ b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/DSListeners.kt @@ -1,9 +1,11 @@ package content.region.misthalin.varrock.quest.dragonslayer import core.api.sendMessage -import org.rs09.consts.Scenery -import core.game.interaction.InteractionListener 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 DSListeners : InteractionListener { override fun defineListeners() { @@ -11,5 +13,17 @@ class DSListeners : InteractionListener { sendMessage(player, "It's locked tight.") return@on true } + // Talk to Wormbrain + setDest(IntType.NPC, intArrayOf(NPCs.WORMBRAIN_745), "talk-to") { player, node -> + val npc = node.asNpc() + val p = player.asPlayer() + val dis = player.location.withinMaxnormDistance(npc.location, 1) + if (dis){ + return@setDest Location.create( p.location.x, p.location.y, 0) + } + else return@setDest Location.create( npc.location.x, npc.location.y, 0) + } + } + } \ No newline at end of file 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.java b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/DragonSlayer.java deleted file mode 100644 index 7f70e513c..000000000 --- a/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/DragonSlayer.java +++ /dev/null @@ -1,351 +0,0 @@ -package content.region.misthalin.varrock.quest.dragonslayer; - -import core.game.component.Component; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.quest.Quest; -import core.game.node.entity.skill.Skills; -import content.global.skill.agility.AgilityHandler; -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.map.RegionManager; -import core.game.world.update.flag.context.Animation; -import core.plugin.Initializable; -import content.region.misthalin.lumbridge.dialogue.DukeHoracioDialogue; -import core.game.world.GameWorld; -import core.plugin.ClassScanner; - -/** - * Represents the dragon slayer quest. - * @author Vexia - * - */ -@Initializable -public final class DragonSlayer extends Quest { - - /** - * Represents the maze key given by the guildmaster. - */ - public static final Item MAZE_KEY = new Item(1542); - - /** - * Represents the red key item. - */ - public static final Item RED_KEY = new Item(1543); - - /** - * Represents the orange key item. - */ - public static final Item ORANGE_KEY = new Item(1544); - - /** - * Represents the yellow key item. - */ - public static final Item YELLOW_KEY = new Item(1545); - - /** - * Represents the blue key item. - */ - public static final Item BLUE_KEY = new Item(1546); - - /** - * Represents the purple key item. - */ - public static final Item PURPLE_KEY = new Item(1547); - - /** - * Represents the green key item. - */ - public static final Item GREEN_KEY = new Item(1548); - - /** - * Represents the maze map piece. - */ - public static final Item MAZE_PIECE = new Item(1535); - - /** - * Represents the magic door map piece. - */ - public static final Item MAGIC_PIECE = new Item(1537); - - /** - * Represents the wormbrain piece. - */ - public static final Item WORMBRAIN_PIECE = new Item(1536); - - /** - * Represents the anti dragon fire shield. - */ - public static final Item SHIELD = new Item(1540); - - /** - * Represents the crandor map item. - */ - public static final Item CRANDOR_MAP = new Item(1538); - - /** - * Represents the map component interface. - */ - public static final Component MAP_COMPONENT = new Component(547); - - /** - * Represents the nails item. - */ - public static final Item NAILS = new Item(1539, 30); - - /** - * Represents the plank item. - */ - public static final Item PLANK = new Item(960); - - /** - * Represents the hammer item. - */ - public static final Item HAMMER = new Item(2347); - - /** - * Represents the elvarg head item. - */ - public static final Item ELVARG_HEAD = new Item(11279); - - /** - * Constructs a new {@Code DragonSlayer} {@Code Object} - */ - public DragonSlayer() { - super("Dragon Slayer", 18, 17, 2, 176, 0, 1, 10); - } - - @Override - public Quest newInstance(Object object) { - ClassScanner.definePlugins(new CrandorMapPlugin(), new DragonSlayerPlugin(), new DSMagicDoorPlugin(), new DragonSlayerCutscene(), new MazeDemonNPC(), new MazeGhostNPC(), new MazeSkeletonNPC(), new MazeZombieNPC(), new MeldarMadNPC(), new WormbrainNPC(), new ZombieRatNPC(), new DSChestDialogue(), new GuildmasterDialogue(), new ElvargNPC(), new WormbrainDialogue(), new OziachDialogue(), new NedDialogue(), new DukeHoracioDialogue()); - return this; - } - - @Override - public void drawJournal(Player player, int stage) { - super.drawJournal(player, stage); - switch (getStage(player)) { - case 0: - player.getPacketDispatch().sendString(BLUE + "I can start this quest by speaking to the " + RED + "Guildmaster " + BLUE + "in", 275, 4+ 7); - player.getPacketDispatch().sendString(BLUE + "the " + RED + "Champions' Guild" + BLUE + " , south-west of Varrock.", 275, 5+ 7); - player.getPacketDispatch().sendString(BLUE + "I will need to be able to defeat a " + RED + "level 83 dragon.", 275, 6+ 7); - if (player.getQuestRepository().getPoints() < 32) { - player.getPacketDispatch().sendString(BLUE + "To enter the Champions' Guild I need" + RED + " 32 Quest Points.", 275, 7+ 7); - } else { - player.getPacketDispatch().sendString("To enter the Champions' Guild I need 32 Quest Points.", 275, 7+ 7); - } - break; - case 10: - line(player, "The Guildmaster of the Champions' Guild said I could earn", 4+ 7); - line(player, "the right to wear rune armour if I went on a quest for", 5+ 7); - line(player, "Oziach, who makes the armour.", 6+ 7); - line(player, BLUE + "I should speak to " + RED + "Oziach" + BLUE + ", who lives by the cliffs to the", 7+ 7); - line(player, BLUE + "west of " + RED + "Edgeville.", 8+ 7); - break; - case 15: - line(player, "The Guildmaster of the Champions' Guild said I could earn", 4+ 7); - line(player, "the right to wear rune armour if I went on a quest for", 5+ 7); - line(player, "Oziach, who makes the armour.", 6+ 7); - line(player, "I spoke to Oziach in Edgeville. He told me to slay the", 7+ 7); - line(player, "dragon of Crandor island.", 8+ 7); - line(player, BLUE + "I should return to the " + RED + "Champions' Guild Guildmaster " + BLUE + "for", 9+ 7); - line(player, BLUE + "more detailed instructions.", 10+ 7); - break; - case 20: - line(player, "The Guildmaster of the Champions' Guild said I could earn", 4+ 7); - line(player, "the right to wear rune armour if I went on a quest for", 5+ 7); - line(player, "Oziach, who makes the armour.", 6+ 7); - line(player, "I spoke to Oziach in Edgeville. He told me to slay the", 7+ 7); - line(player, "dragon of Crandor island.", 8+ 7); - line(player, "The Champions' Guild Guildmaster gave me more detailed", 9+ 7); - line(player, "instructions.", 10+ 7); - line(player, BLUE + "To defeat the dragon I will need to find a " + RED + "map " + BLUE + "to Crandor, a", 11+ 7); - line(player, RED + "ship" + BLUE + ", a " + RED + "captain " + BLUE + "to take me there and some kind of", 12+ 7); - line(player, RED + "protection " + BLUE + "against the dragon's breath.", 13+ 7); - if (!player.getInventory().containsItem(MAZE_PIECE) && !player.getBank().containsItem(MAZE_PIECE)) { - line(player, BLUE + "One-third of the map is in " + RED + "Melzar's Maze" + BLUE + ", near", 14+ 7); - line(player, RED + "Rimmington" + ".", 15+ 7); - } else { - line(player, "I found the piece of the map that was hidden in Melzar's", 14+ 7); - line(player, "Maze.", 15+ 7); - } - if (!player.getInventory().containsItem(MAGIC_PIECE) && !player.getBank().containsItem(MAGIC_PIECE)) { - line(player, BLUE + "One)2third of the map is hidden and only the " + RED + "Oracle " + BLUE + "on " + RED + "Ice", 16+ 7); - line(player, RED + "Mountain" + BLUE + " will know where it is.", 17+ 7); - } else { - line(player, "I found the piece of the map that was hidden beneath Ice", 16+ 7); - line(player, "Mountain.", 18+ 7); - } - if (!player.getInventory().containsItem(WORMBRAIN_PIECE) && !player.getBank().containsItem(WORMBRAIN_PIECE)) { - line(player, BLUE + "One-third of the map was stolen by a " + RED + "goblin " + BLUE + "from the", 18+ 7); - line(player, RED + "Goblin Village.", 19+ 7); - } else { - line(player, "I found the piece of the map that the goblin, Wormbrain,", 18+ 7); - line(player, "stole.", 19+ 7); - } - if (!player.getInventory().containsItem(SHIELD) && !player.getBank().containsItem(SHIELD)) { - line(player, BLUE + "I should ask the " + RED + "Duke of Lumbridge " + BLUE + "for an " + RED + "anti-", 20+ 7); - line(player, RED + "dragonbreath shield.", 21+ 7); - } else { - line(player, "The Duke of Lumbridge gave me an anti-dragonbreath", 20+ 7); - line(player, "shield.", 21+ 7); - - } - if (!player.getSavedData().getQuestData().getDragonSlayerAttribute("ship")) { - line(player, BLUE + "I should see if there is a " + RED + "ship " + BLUE + "for sale in " + RED + "Port Sarim", 22+ 7); - } else { - line(player, "I bought a ship in Port Sarim called the Lady Lumbridge.", 22+ 7); - if (!player.getSavedData().getQuestData().getDragonSlayerAttribute("repaired")) { - line(player, "I need to repair the hole in bottom of the ship.", 23+ 7); - } else { - line(player, "I have repaired my ship using wooden planks and steel", 23+ 7); - line(player, "nails.", 24+ 7); - } - } - break; - case 30: - line(player, "The Guildmaster of the Champions' Guild said I could earn", 4+ 7); - line(player, "the right to wear rune armour if I went on a quest for", 5+ 7); - line(player, "Oziach, who makes the armour.", 6+ 7); - line(player, "I spoke to Oziach in Edgeville. He told me to slay the", 7+ 7); - line(player, "dragon of Crandor island.", 8+ 7); - line(player, "The Champions' Guild Guildmaster told me I had to find", 9+ 7); - line(player, "three pieces of a map to Crandor, a ship, a captain to take", 10+ 7); - line(player, "me there and a shield to protect me from the dragon's", 11+ 7); - line(player, "breath.", 12+ 7); - line(player, "I found the piece of the map that was hidden in Melzar's", 13+ 7); - line(player, "Maze.", 14+ 7); - line(player, "I found the piece of the map that was hidden beneath Ice", 15+ 7); - line(player, "Mountain.", 16+ 7); - line(player, "I found the piece of the map that the goblin, Wormbrain,", 17+ 7); - line(player, "stole.", 18+ 7); - line(player, "The Duke of Lumbridge gave me an anti-dragonbreath", 19+ 7); - line(player, "shield.", 20+ 7); - line(player, "I bought a ship in Port Sarim called the Lady Lumbridge", 21+ 7); - line(player, "I have repaired my ship using wooden planks and steel", 22+ 7); - line(player, "nails.", 23+ 7); - line(player, "Captain Ned from Draynor Village has agreed to sail the", 24+ 7); - line(player, "ship to Crandor for me.", 25+ 7); - line(player, BLUE + "Now I should go to my ship in " + RED + "Port Sarim " + BLUE + "and set sail for", 26+ 7); - line(player, RED + "Crandor" + BLUE + "!", 27+ 7); - break; - case 40: - line(player, "The Guildmaster of the Champions' Guild said I could earn", 4+ 7); - line(player, "the right to wear rune armour if I went on a quest for", 5+ 7); - line(player, "Oziach, who makes the armour.", 6+ 7); - line(player, "I spoke to Oziach in Edgeville. He told me to slay the", 7+ 7); - line(player, "dragon of Crandor island.", 8+ 7); - line(player, "The Champions' Guild Guildmaster told me I had to find", 9+ 7); - line(player, "three pieces of a map to Crandor, a ship, a captain to take", 10+ 7); - line(player, "me there and a shield to protect me from the dragon's", 11+ 7); - line(player, "breath.", 12+ 7); - line(player, "I found the piece of the map that was hidden in Melzar's", 13+ 7); - line(player, "Maze.", 14+ 7); - line(player, "I found the piece of the map that was hidden beneath Ice", 15+ 7); - line(player, "Mountain.", 16+ 7); - line(player, "I found the piece of the map that the goblin, Wormbrain,", 17+ 7); - line(player, "stole.", 18+ 7); - line(player, "The Duke of Lumbridge gave me an anti-dragonbreath", 19+ 7); - line(player, "shield.", 20+ 7); - if (!player.getAttribute("demon-slayer:memorize", false)) { - if (!player.getInventory().containsItem(ELVARG_HEAD)) { - line(player, BLUE + "Now all I need to do is kill the " + RED + "dragon" + BLUE + "!", 21+ 7); - } else { - line(player, BLUE + "I have slain the dragon! Now I just need to tell " + RED + "Oziach.", 21+ 7); - } - } else { - line(player, "I have found a secret passage leading from Karamja to", 21+ 7); - line(player, "Crandor, so I no longer need to worry about finding a", 22+ 7); - line(player, "seaworthy ship and captain to take me there.", 23+ 7); - if (!player.getInventory().containsItem(ELVARG_HEAD)) { - line(player, BLUE + "Now all I need to do is kill the " + RED + "dragon" + BLUE + "!", 24+ 7); - } else { - line(player, BLUE + "I have slain the dragon! Now I just need to tell " + RED + "Oziach.", 24+ 7); - } - } - break; - case 100: - line(player, "The Guildmaster of the Champions' Guild said I could earn", 4+ 7); - line(player, "the right to wear rune armour if I went on a quest for", 5+ 7); - line(player, "Oziach, who makes the armour.", 6+ 7); - line(player, "I spoke to Oziach in Edgeville. He told me to slay the", 7+ 7); - line(player, "dragon of Crandor island.", 8+ 7); - line(player, "The Champions' Guild Guildmaster told me I had to find", 9+ 7); - line(player, "three pieces of a map to Crandor, a ship, a captain to take", 10+ 7); - line(player, "me there and a shield to protect me from the dragon's", 11+ 7); - line(player, "breath.", 12+ 7); - line(player, "I found the piece of the map that was hidden in Melzar's", 13+ 7); - line(player, "Maze.", 14+ 7); - line(player, "I found the piece of the map that was hidden beneath Ice", 15+ 7); - line(player, "Mountain.", 16+ 7); - line(player, "I found the piece of the map that the goblin, Wormbrain,", 17+ 7); - line(player, "stole.", 18+ 7); - line(player, "The Duke of Lumbridge gave me an anti-dragonbreath", 19+ 7); - line(player, "shield.", 20+ 7); - line(player, "I have found a secret passage leading from Karamja to", 21+ 7); - line(player, "Crandor, so I no longer need to worry about finding a", 22+ 7); - line(player, "seaworthy ship and captain to take me there.", 23+ 7); - line(player, "I sailed to Crandor and killed the dragon. I am not a true", 24+ 7); - line(player, "champion and have proved myself worthy to wear rune", 25+ 7); - line(player, "platemail!", 26+ 7); - line(player, "QUEST COMPLETE!", 27+ 7); - line(player, BLUE + "I gained " + RED + "2 Quest Points" + BLUE + ", " + RED + "18,650 Strength XP" + BLUE + ", " + RED + "18,650", 28+ 7); - line(player, RED + "Defence XP " + BLUE + "and the right to wear " + RED + "rune platebodies.", 29+ 7); - break; - } - } - - @Override - public void finish(Player player) { - super.finish(player); - player.getPacketDispatch().sendString("2 Quests Points", 277, 8 + 2); - player.getPacketDispatch().sendString("Ability to wear rune platebody", 277, 9 + 2); - player.getPacketDispatch().sendString("18,650 Strength XP", 277, 10 + 2); - player.getPacketDispatch().sendString("18,650 Defence XP", 277, 11 + 2); - player.getPacketDispatch().sendString("You have completed the Dragon Slayer Quest!", 277, 2 + 2); - player.getPacketDispatch().sendItemZoomOnInterface(ELVARG_HEAD.getId(), 230, 277, 3 + 2); - player.getSkills().addExperience(Skills.STRENGTH, 18650); - player.getSkills().addExperience(Skills.DEFENCE, 18650); - } - - /** - * Method used to handle going through the magic door. - * @param player the player. - * @param interaction the interaction. - * @return True if so. - */ - public static boolean handleMagicDoor(final Player player, boolean interaction) { - if (!player.getSavedData().getQuestData().getDragonSlayerItem("lobster") || !player.getSavedData().getQuestData().getDragonSlayerItem("bowl") || !player.getSavedData().getQuestData().getDragonSlayerItem("silk") || !player.getSavedData().getQuestData().getDragonSlayerItem("wizard")) { - if (interaction) { - player.getPacketDispatch().sendMessage("You can't see any way to open the door."); - } - return true; - } - player.getPacketDispatch().sendMessage("The door opens..."); - final Scenery object = RegionManager.getObject(new Location(3050, 9839, 0)); - player.faceLocation(object.getLocation()); - player.getPacketDispatch().sendSceneryAnimation(object, new Animation(6636)); - GameWorld.getPulser().submit(new Pulse(1, player) { - int counter = 0; - - @Override - public boolean pulse() { - switch (counter++) { - case 4: - AgilityHandler.walk(player, 0, player.getLocation(), player.getLocation().getX() == 3051 ? Location.create(3049, 9840, 0) : Location.create(3051, 9840, 0), null, 0, null); - break; - case 5: - player.getPacketDispatch().sendSceneryAnimation(object, new Animation(6637)); - break; - case 6: - player.getPacketDispatch().sendSceneryAnimation(object, new Animation(6635)); - return true; - } - return false; - } - }); - return true; - } - -} 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 new file mode 100644 index 000000000..3bb325be9 --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/DragonSlayer.kt @@ -0,0 +1,506 @@ +package content.region.misthalin.varrock.quest.dragonslayer + +import content.global.skill.agility.AgilityHandler +import content.region.misthalin.lumbridge.dialogue.DukeHoracioDialogue +import core.api.Event +import core.api.LoginListener +import core.api.getQuestStage +import core.game.component.Component +import core.game.event.EventHook +import core.game.event.PickUpEvent +import core.game.event.SpellCastEvent +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.system.task.Pulse +import core.game.world.GameWorld.Pulser +import core.game.world.map.Location +import core.game.world.map.RegionManager.getObject +import core.game.world.update.flag.context.Animation +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(Quests.DRAGON_SLAYER, 18, 17, 2, 176, 0, 1, 10), LoginListener { + override fun newInstance(`object`: Any?): Quest { + definePlugins( + CrandorMapPlugin(), + DragonSlayerPlugin(), + DSMagicDoorPlugin(), + DragonSlayerCutscene(), + MazeDemonNPC(), + MazeGhostNPC(), + MazeSkeletonNPC(), + MazeZombieNPC(), + MeldarMadNPC(), + WormbrainNPC(), + ZombieRatNPC(), + DSChestDialogue(), + GuildmasterDialogue(), + ElvargNPC(), + WormbrainDialogue(), + OziachDialogue(), + NedDialogue(), + DukeHoracioDialogue() + ) + return this + } + + override fun drawJournal(player: Player, stage: Int) { + super.drawJournal(player, stage) + when (getStage(player)) { + 0 -> { + player.packetDispatch.sendString( + BLUE + "I can start this quest by speaking to the " + RED + "Guildmaster " + BLUE + "in", + 275, + 4 + 7 + ) + player.packetDispatch.sendString( + BLUE + "the " + RED + "Champions' Guild" + BLUE + " , south-west of Varrock.", + 275, + 5 + 7 + ) + player.packetDispatch.sendString( + BLUE + "I will need to be able to defeat a " + RED + "level 83 dragon.", + 275, + 6 + 7 + ) + if (player.questRepository.points < 32) { + player.packetDispatch.sendString( + BLUE + "To enter the Champions' Guild I need" + RED + " 32 Quest Points.", + 275, + 7 + 7 + ) + } else { + player.packetDispatch.sendString( + "To enter the Champions' Guild I need 32 Quest Points.", + 275, + 7 + 7 + ) + } + } + + 10 -> { + line(player, "The Guildmaster of the Champions' Guild said I could earn", 4 + 7) + line(player, "the right to wear rune armour if I went on a quest for", 5 + 7) + line(player, "Oziach, who makes the armour.", 6 + 7) + line( + player, + BLUE + "I should speak to " + RED + "Oziach" + BLUE + ", who lives by the cliffs to the", + 7 + 7 + ) + line(player, BLUE + "west of " + RED + "Edgeville.", 8 + 7) + } + + 15 -> { + line(player, "The Guildmaster of the Champions' Guild said I could earn", 4 + 7) + line(player, "the right to wear rune armour if I went on a quest for", 5 + 7) + line(player, "Oziach, who makes the armour.", 6 + 7) + line(player, "I spoke to Oziach in Edgeville. He told me to slay the", 7 + 7) + line(player, "dragon of Crandor island.", 8 + 7) + line( + player, + BLUE + "I should return to the " + RED + "Champions' Guild Guildmaster " + BLUE + "for", + 9 + 7 + ) + line(player, BLUE + "more detailed instructions.", 10 + 7) + } + + 20 -> { + line(player, "The Guildmaster of the Champions' Guild said I could earn", 4 + 7) + line(player, "the right to wear rune armour if I went on a quest for", 5 + 7) + line(player, "Oziach, who makes the armour.", 6 + 7) + line(player, "I spoke to Oziach in Edgeville. He told me to slay the", 7 + 7) + line(player, "dragon of Crandor island.", 8 + 7) + line(player, "The Champions' Guild Guildmaster gave me more detailed", 9 + 7) + line(player, "instructions.", 10 + 7) + line( + player, + BLUE + "To defeat the dragon I will need to find a " + RED + "map " + BLUE + "to Crandor, a", + 11 + 7 + ) + line( + player, + RED + "ship" + BLUE + ", a " + RED + "captain " + BLUE + "to take me there and some kind of", + 12 + 7 + ) + line(player, RED + "protection " + BLUE + "against the dragon's breath.", 13 + 7) + if (!player.inventory.containsItem(MAZE_PIECE) && !player.bank.containsItem(MAZE_PIECE)) { + line(player, BLUE + "One-third of the map is in " + RED + "Melzar's Maze" + BLUE + ", near", 14 + 7) + line(player, RED + "Rimmington" + ".", 15 + 7) + } else { + line(player, "I found the piece of the map that was hidden in Melzar's", 14 + 7) + line(player, "Maze.", 15 + 7) + } + if (!player.inventory.containsItem(MAGIC_PIECE) && !player.bank.containsItem(MAGIC_PIECE)) { + line( + player, + BLUE + "One-third of the map is hidden and only the " + RED + "Oracle " + BLUE + "on " + RED + "Ice", + 16 + 7 + ) + line(player, RED + "Mountain" + BLUE + " will know where it is.", 17 + 7) + } else { + line(player, "I found the piece of the map that was hidden beneath Ice", 16 + 7) + line(player, "Mountain.", 18 + 7) + } + if (!player.inventory.containsItem(WORMBRAIN_PIECE) && !player.bank.containsItem(WORMBRAIN_PIECE)) { + line( + player, + BLUE + "One-third of the map was stolen by a " + RED + "goblin " + BLUE + "from the", + 18 + 7 + ) + line(player, RED + "Goblin Village.", 19 + 7) + } else { + line(player, "I found the piece of the map that the goblin, Wormbrain,", 18 + 7) + line(player, "stole.", 19 + 7) + } + if (!player.inventory.containsItem(SHIELD) && !player.bank.containsItem(SHIELD)) { + line( + player, + BLUE + "I should ask the " + RED + "Duke of Lumbridge " + BLUE + "for an " + RED + "anti-", + 20 + 7 + ) + line(player, RED + "dragonbreath shield.", 21 + 7) + } else { + line(player, "The Duke of Lumbridge gave me an anti-dragonbreath", 20 + 7) + line(player, "shield.", 21 + 7) + } + if (!player.savedData.questData.getDragonSlayerAttribute("ship")) { + line( + player, + BLUE + "I should see if there is a " + RED + "ship " + BLUE + "for sale in " + RED + "Port Sarim", + 22 + 7 + ) + } else { + line(player, "I bought a ship in Port Sarim called the Lady Lumbridge.", 22 + 7) + if (!player.savedData.questData.getDragonSlayerAttribute("repaired")) { + line(player, "I need to repair the hole in bottom of the ship.", 23 + 7) + } else { + line(player, "I have repaired my ship using wooden planks and steel", 23 + 7) + line(player, "nails.", 24 + 7) + } + } + } + + 30 -> { + line(player, "The Guildmaster of the Champions' Guild said I could earn", 4 + 7) + line(player, "the right to wear rune armour if I went on a quest for", 5 + 7) + line(player, "Oziach, who makes the armour.", 6 + 7) + line(player, "I spoke to Oziach in Edgeville. He told me to slay the", 7 + 7) + line(player, "dragon of Crandor island.", 8 + 7) + line(player, "The Champions' Guild Guildmaster told me I had to find", 9 + 7) + line(player, "three pieces of a map to Crandor, a ship, a captain to take", 10 + 7) + line(player, "me there and a shield to protect me from the dragon's", 11 + 7) + line(player, "breath.", 12 + 7) + line(player, "I found the piece of the map that was hidden in Melzar's", 13 + 7) + line(player, "Maze.", 14 + 7) + line(player, "I found the piece of the map that was hidden beneath Ice", 15 + 7) + line(player, "Mountain.", 16 + 7) + line(player, "I found the piece of the map that the goblin, Wormbrain,", 17 + 7) + line(player, "stole.", 18 + 7) + line(player, "The Duke of Lumbridge gave me an anti-dragonbreath", 19 + 7) + line(player, "shield.", 20 + 7) + line(player, "I bought a ship in Port Sarim called the Lady Lumbridge", 21 + 7) + line(player, "I have repaired my ship using wooden planks and steel", 22 + 7) + line(player, "nails.", 23 + 7) + line(player, "Captain Ned from Draynor Village has agreed to sail the", 24 + 7) + line(player, "ship to Crandor for me.", 25 + 7) + line( + player, + BLUE + "Now I should go to my ship in " + RED + "Port Sarim " + BLUE + "and set sail for", + 26 + 7 + ) + line(player, RED + "Crandor" + BLUE + "!", 27 + 7) + } + + 40 -> { + line(player, "The Guildmaster of the Champions' Guild said I could earn", 4 + 7) + line(player, "the right to wear rune armour if I went on a quest for", 5 + 7) + line(player, "Oziach, who makes the armour.", 6 + 7) + line(player, "I spoke to Oziach in Edgeville. He told me to slay the", 7 + 7) + line(player, "dragon of Crandor island.", 8 + 7) + line(player, "The Champions' Guild Guildmaster told me I had to find", 9 + 7) + line(player, "three pieces of a map to Crandor, a ship, a captain to take", 10 + 7) + line(player, "me there and a shield to protect me from the dragon's", 11 + 7) + line(player, "breath.", 12 + 7) + line(player, "I found the piece of the map that was hidden in Melzar's", 13 + 7) + line(player, "Maze.", 14 + 7) + line(player, "I found the piece of the map that was hidden beneath Ice", 15 + 7) + line(player, "Mountain.", 16 + 7) + line(player, "I found the piece of the map that the goblin, Wormbrain,", 17 + 7) + line(player, "stole.", 18 + 7) + line(player, "The Duke of Lumbridge gave me an anti-dragonbreath", 19 + 7) + line(player, "shield.", 20 + 7) + if (!player.getAttribute("demon-slayer:memorize", false)) { + if (!player.inventory.containsItem(ELVARG_HEAD)) { + line(player, BLUE + "Now all I need to do is kill the " + RED + "dragon" + BLUE + "!", 21 + 7) + } else { + line( + player, + BLUE + "I have slain the dragon! Now I just need to tell " + RED + "Oziach.", + 21 + 7 + ) + } + } else { + line(player, "I have found a secret passage leading from Karamja to", 21 + 7) + line(player, "Crandor, so I no longer need to worry about finding a", 22 + 7) + line(player, "seaworthy ship and captain to take me there.", 23 + 7) + if (!player.inventory.containsItem(ELVARG_HEAD)) { + line(player, BLUE + "Now all I need to do is kill the " + RED + "dragon" + BLUE + "!", 24 + 7) + } else { + line( + player, + BLUE + "I have slain the dragon! Now I just need to tell " + RED + "Oziach.", + 24 + 7 + ) + } + } + } + + 100 -> { + line(player, "The Guildmaster of the Champions' Guild said I could earn", 4 + 7) + line(player, "the right to wear rune armour if I went on a quest for", 5 + 7) + line(player, "Oziach, who makes the armour.", 6 + 7) + line(player, "I spoke to Oziach in Edgeville. He told me to slay the", 7 + 7) + line(player, "dragon of Crandor island.", 8 + 7) + line(player, "The Champions' Guild Guildmaster told me I had to find", 9 + 7) + line(player, "three pieces of a map to Crandor, a ship, a captain to take", 10 + 7) + line(player, "me there and a shield to protect me from the dragon's", 11 + 7) + line(player, "breath.", 12 + 7) + line(player, "I found the piece of the map that was hidden in Melzar's", 13 + 7) + line(player, "Maze.", 14 + 7) + line(player, "I found the piece of the map that was hidden beneath Ice", 15 + 7) + line(player, "Mountain.", 16 + 7) + line(player, "I found the piece of the map that the goblin, Wormbrain,", 17 + 7) + line(player, "stole.", 18 + 7) + line(player, "The Duke of Lumbridge gave me an anti-dragonbreath", 19 + 7) + line(player, "shield.", 20 + 7) + line(player, "I have found a secret passage leading from Karamja to", 21 + 7) + line(player, "Crandor, so I no longer need to worry about finding a", 22 + 7) + line(player, "seaworthy ship and captain to take me there.", 23 + 7) + line(player, "I sailed to Crandor and killed the dragon. I am not a true", 24 + 7) + line(player, "champion and have proved myself worthy to wear rune", 25 + 7) + line(player, "platemail!", 26 + 7) + line(player, "QUEST COMPLETE!", 27 + 7) + line( + player, + BLUE + "I gained " + RED + "2 Quest Points" + BLUE + ", " + RED + "18,650 Strength XP" + BLUE + ", " + RED + "18,650", + 28 + 7 + ) + line(player, RED + "Defence XP " + BLUE + "and the right to wear " + RED + "rune platebodies.", 29 + 7) + } + } + } + + override fun finish(player: Player) { + super.finish(player) + player.packetDispatch.sendString("2 Quests Points", 277, 8 + 2) + player.packetDispatch.sendString("Ability to wear rune platebody", 277, 9 + 2) + player.packetDispatch.sendString("18,650 Strength XP", 277, 10 + 2) + player.packetDispatch.sendString("18,650 Defence XP", 277, 11 + 2) + player.packetDispatch.sendString("You have completed the Dragon Slayer Quest!", 277, 2 + 2) + player.packetDispatch.sendItemZoomOnInterface(ELVARG_HEAD.id, 230, 277, 3 + 2) + player.getSkills().addExperience(Skills.STRENGTH, 18650.0) + player.getSkills().addExperience(Skills.DEFENCE, 18650.0) + player.unhook(SpellCastHook) + player.unhook(PickedUpHook) + } + + override fun setStage(player: Player, stage: Int) { + super.setStage(player, stage) + if (stage == 20) { + player.hook(Event.SpellCast, SpellCastHook) + player.hook(Event.PickedUp, PickedUpHook) + } + } + + override fun login(player: Player) { + if (getQuestStage(player, this.quest) == 20) { + player.hook(Event.SpellCast, SpellCastHook) + player.hook(Event.PickedUp, PickedUpHook) + } + } + + private val SpellCastHook = object : EventHook { + override fun process(entity: Entity, event: SpellCastEvent) { + if (event.spellId == 19 && event.target != null && event.target.id == Items.MAP_PART_1536) {//telegrab + Discord.sendToOpenRSC(entity.name, "Player obtained Wormbrain piece! (Murder-Then-Telegrab)") + entity.unhook(this) + } + } + } + + private val PickedUpHook = object : EventHook { + override fun process(entity: Entity, event: PickUpEvent) { + if (event.itemId == Items.MAP_PART_1536) { + Discord.sendToOpenRSC(entity.name, "Player obtained Wormbrain piece! (Yoinked-Off-Floor)") + entity.unhook(this) + } + } + } + + companion object { + /** + * Represents the maze key given by the guildmaster. + */ + @JvmField + val MAZE_KEY = Item(1542) + + /** + * Represents the red key item. + */ + @JvmField + val RED_KEY = Item(1543) + + /** + * Represents the orange key item. + */ + @JvmField + val ORANGE_KEY = Item(1544) + + /** + * Represents the yellow key item. + */ + @JvmField + val YELLOW_KEY = Item(1545) + + /** + * Represents the blue key item. + */ + @JvmField + val BLUE_KEY = Item(1546) + + /** + * Represents the purple key item. + */ + @JvmField + val PURPLE_KEY = Item(1547) + + /** + * Represents the green key item. + */ + @JvmField + val GREEN_KEY = Item(1548) + + /** + * Represents the maze map piece. + */ + @JvmField + val MAZE_PIECE = Item(1535) + + /** + * Represents the magic door map piece. + */ + @JvmField + val MAGIC_PIECE = Item(1537) + + /** + * Represents the wormbrain piece. + */ + @JvmField + val WORMBRAIN_PIECE = Item(1536) + + /** + * Represents the anti dragon fire shield. + */ + val SHIELD = Item(1540) + + /** + * Represents the crandor map item. + */ + @JvmField + val CRANDOR_MAP = Item(1538) + + /** + * Represents the map component interface. + */ + @JvmField + val MAP_COMPONENT = Component(547) + + /** + * Represents the nails item. + */ + @JvmField + val NAILS = Item(1539, 30) + + /** + * Represents the plank item. + */ + @JvmField + val PLANK = Item(960) + + /** + * Represents the hammer item. + */ + @JvmField + val HAMMER = Item(2347) + + /** + * Represents the elvarg head item. + */ + @JvmField + val ELVARG_HEAD = Item(11279) + + /** + * Method used to handle going through the magic door. + * @param player the player. + * @param interaction the interaction. + * @return `True` if so. + */ + @JvmStatic + fun handleMagicDoor(player: Player, interaction: Boolean): Boolean { + if (!player.savedData.questData.getDragonSlayerItem("lobster") || !player.savedData.questData.getDragonSlayerItem( + "bowl" + ) || !player.savedData.questData.getDragonSlayerItem("silk") || !player.savedData.questData.getDragonSlayerItem( + "wizard" + ) + ) { + if (interaction) { + player.packetDispatch.sendMessage("You can't see any way to open the door.") + } + return true + } + player.packetDispatch.sendMessage("The door opens...") + val `object` = getObject(Location(3050, 9839, 0)) + player.faceLocation(`object`!!.location) + player.packetDispatch.sendSceneryAnimation(`object`, Animation(6636)) + Pulser.submit(object : Pulse(1, player) { + var counter = 0 + override fun pulse(): Boolean { + when (counter++) { + 4 -> AgilityHandler.walk( + player, + 0, + player.location, + if (player.location.x == 3051) Location.create(3049, 9840, 0) else Location.create( + 3051, + 9840, + 0 + ), + null, + 0.0, + null + ) + + 5 -> player.packetDispatch.sendSceneryAnimation(`object`, Animation(6637)) + 6 -> { + player.packetDispatch.sendSceneryAnimation(`object`, Animation(6635)) + return true + } + } + return false + } + }) + return true + } + } +} \ No newline at end of file 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 f9f2f909f..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 @@ -26,6 +26,10 @@ import core.net.packet.context.CameraContext.CameraType; import core.net.packet.out.CameraViewPacket; import core.net.packet.out.MinimapState; + +import static core.api.ContentAPIKt.*; +import content.data.Quests; + /** * Represents the dragon slayer cutscene. * @author Vexia @@ -100,10 +104,10 @@ 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); - player.getConfigManager().set(177, 8257540); - player.getConfigManager().set(176, 8); + setVarp(player, 177, 8257540); + setVarp(player, 176, 8); player.getSavedData().getQuestData().setDragonSlayerPlanks(0); PacketRepository.send(MinimapState.class, new MinimapStateContext(player, 0)); PacketRepository.send(CameraViewPacket.class, new CameraContext(player, CameraType.RESET, player.getLocation().getX() + 13, player.getLocation().getY() - 3, 250, 1, 100)); @@ -289,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 0a75dab17..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 @@ -24,6 +24,10 @@ import core.plugin.Plugin; import java.util.List; +import static core.api.ContentAPIKt.*; +import content.data.Quests; + + /** * Represents the plugin used to handle node interactions related to dragon slayer. * @@ -98,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)); @@ -124,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; } @@ -150,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) { @@ -178,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")) { @@ -219,7 +206,7 @@ public final class DragonSlayerPlugin extends OptionHandler { player.getDialogueInterpreter().sendDialogue("You nail a plank over the hole, but you still need more planks to", "close the hole completely."); } else { player.getSavedData().getQuestData().setDragonSlayerAttribute("repaired", true); - player.getConfigManager().set(177, 1967876); + setVarp(player, 177, 1967876); player.getDialogueInterpreter().sendDialogue("You nail a final plank over the hole. You have successfully patched", "the hole in the ship."); } } diff --git a/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/DukeHoracioDSDialogue.kt b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/DukeHoracioDSDialogue.kt index 35c5ace86..e61630a3b 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/DukeHoracioDSDialogue.kt +++ b/Server/src/main/content/region/misthalin/varrock/quest/dragonslayer/DukeHoracioDSDialogue.kt @@ -23,7 +23,7 @@ class DukeHoracioDSDialogue(val questStage: Int) : DialogueFile() { 7 -> npc( "Back in my father's day, Crandor was an important", "city-state. Politically, it was important as Falador or", - "Varrock and its shipes traded with every port." + "Varrock and its ships traded with every port." ).also { stage++ } 8 -> npc( 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 b5fd9bf5b..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,310 +20,311 @@ 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; + /** - * Represents the elvarg npc. + * Represents the Elvarg npc. * @author 'Vexia * @version 1.0 */ +@Initializable public final class ElvargNPC extends AbstractNPC { - /** - * The NPC ids of NPCs using this plugin. - */ - private static final int[] ID = { 742 }; + /** + * The NPC ids of NPCs using this plugin. + */ + private static final int[] ID = { 742 }; - /** - * Represents the animations to use. - */ - private static final Animation[] ANIMATIONS = new Animation[] { new Animation(6654), new Animation(6655) }; + /** + * Represents the animations to use. + */ + private static final Animation[] ANIMATIONS = new Animation[] { new Animation(6654), new Animation(6655) }; - /** - * Represents the combat swing handler. - */ - private final CombatSwingHandler combatHandler = new ElvargCombatSwingHandler(); + /** + * Represents the combat swing handler. + */ + private final CombatSwingHandler combatHandler = new ElvargCombatSwingHandler(); - /** - * Constructs a new {@code MeldarMadNPC} {@code Object}. - */ - public ElvargNPC() { - super(0, null); - } + /** + * Constructs a new {@code ElvargNPC} {@code Object}. + */ + public ElvargNPC() { + super(0, null); + } - /** - * Constructs a new {@code MeldarMadNPC} {@code Object}. - * @param id The NPC id. - * @param location The location. - */ - private ElvargNPC(int id, Location location) { - super(id, location); - } + /** + * Constructs a new {@code ElvargNPC} {@code Object}. + * @param id The NPC id. + * @param location The location. + */ + private ElvargNPC(int id, Location location) { + super(id, location); + } - @Override - public AbstractNPC construct(int id, Location location, Object... objects) { - return new ElvargNPC(id, location); - } + @Override + public AbstractNPC construct(int id, Location location, Object... objects) { + return new ElvargNPC(id, location); + } - @Override - public void commenceDeath(Entity killer) { - final Direction direction = Direction.getLogicalDirection(getLocation(), killer.getLocation()); - GameWorld.getPulser().submit(new Pulse(1, this) { - @Override - public boolean pulse() { - faceLocation(getCenterLocation().transform(direction.getStepX() * 3, direction.getStepY() * 3, 0)); - return true; - } - }); - setDirection(direction); - } + @Override + public void commenceDeath(Entity killer) { + final Direction direction = Direction.getLogicalDirection(getLocation(), killer.getLocation()); + GameWorld.getPulser().submit(new Pulse(1, this) { + @Override + public boolean pulse() { + faceLocation(getCenterLocation().transform(direction.getStepX() * 3, direction.getStepY() * 3, 0)); + return true; + } + }); + setDirection(direction); + } - @Override - public void finalizeDeath(final Entity killer) { - super.finalizeDeath(killer); - final Scenery object = new Scenery(25202, getLocation(), getRotation()); - SceneryBuilder.add(object); - killer.faceLocation(object.getCenterLocation()); - killer.lock(); - GameWorld.getPulser().submit(new Pulse(1) { - int counter = 0; + @Override + public void finalizeDeath(final Entity killer) { + super.finalizeDeath(killer); + final Scenery object = new Scenery(25202, getLocation(), getRotation()); + SceneryBuilder.add(object); + killer.faceLocation(object.getCenterLocation()); + killer.lock(); + GameWorld.getPulser().submit(new Pulse(1) { + int counter = 0; - @Override - public boolean pulse() { - counter++; - if (counter == 1) { - killer.animate(ANIMATIONS[0]); - } else if (counter == 4) { - final Player player = ((Player) killer); - SceneryBuilder.replace(object, object.transform(25203)); - if (!player.getInventory().add(DragonSlayer.ELVARG_HEAD)) { - GroundItemManager.create(DragonSlayer.ELVARG_HEAD, player); - } - killer.animate(ANIMATIONS[1]); - killer.unlock(); - } else if (counter == 12) { - SceneryBuilder.remove(RegionManager.getObject(object.getLocation())); - return true; - } - return false; - } + @Override + public boolean pulse() { + counter++; + if (counter == 1) { + killer.animate(ANIMATIONS[0]); + } else if (counter == 4) { + final Player player = ((Player) killer); + SceneryBuilder.replace(object, object.transform(25203)); + if (!player.getInventory().add(DragonSlayer.ELVARG_HEAD)) { + GroundItemManager.create(DragonSlayer.ELVARG_HEAD, player); + } + killer.animate(ANIMATIONS[1]); + killer.unlock(); + } else if (counter == 12) { + SceneryBuilder.remove(RegionManager.getObject(object.getLocation())); + return true; + } + return false; + } - }); - } + }); + } - @Override - public CombatSwingHandler getSwingHandler(boolean swing) { - return combatHandler; - } + @Override + public CombatSwingHandler getSwingHandler(boolean swing) { + return combatHandler; + } - @Override - public int[] getIds() { - return ID; - } + @Override + public int[] getIds() { + return ID; + } - /** - * Method used to get the rotation. - * @return the rotation. - */ - public int getRotation() { - switch (getDirection()) { - case EAST: - return 3; - case NORTH: - return 2; - case WEST: - return 1; - case SOUTH: - return 0; - default: - break; - } - return 0; - } + /** + * Method used to get the rotation. + * @return the rotation. + */ + public int getRotation() { + switch (getDirection()) { + case EAST: + return 3; + case NORTH: + return 2; + case WEST: + return 1; + case SOUTH: + return 0; + default: + break; + } + return 0; + } - @Override - public boolean isAttackable(Entity entity, CombatStyle style, boolean message) { - if (!(entity instanceof Player)) { - 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))) { + @Override + public boolean isAttackable(Entity entity, CombatStyle style, boolean message) { + if (!(entity instanceof Player)) { + return super.isAttackable(entity, style, message); + } + final Player player = (Player) entity; + 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) { + return false; + } + if (player.getQuestRepository().getQuest(Quests.DRAGON_SLAYER).getStage(player) > 40) { if(message) { player.getPacketDispatch().sendMessage("You have already slain Elvarg."); } - return false; - } - return super.isAttackable(entity, style, message); - } + return false; + } + return super.isAttackable(entity, style, message); + } - @Override - public int getDragonfireProtection(boolean fire) { - return 0x2 | 0x4 | 0x8; - } + @Override + public int getDragonfireProtection(boolean fire) { + return 0x2 | 0x4 | 0x8; + } - /** - * Handles the elvarg combat swing handler. - * @author Emperor - */ - static class ElvargCombatSwingHandler extends CombatSwingHandler { + /** + * Handles the Elvarg combat swing handler. + * @author Emperor + */ + static class ElvargCombatSwingHandler extends CombatSwingHandler { - /** - * The style. - */ - private CombatStyle style = CombatStyle.RANGE; + /** + * The style. + */ + private CombatStyle style = CombatStyle.RANGE; - /** - * The melee attack animation. - */ - private static final Animation MELEE_ATTACK = new Animation(80, Priority.HIGH); + /** + * The melee attack animation. + */ + private static final Animation MELEE_ATTACK = new Animation(80, Priority.HIGH); - /** - * The fire type. - */ - private final FireType fireType = FireType.FIERY_BREATH; + /** + * The fire type. + */ + private final FireType fireType = FireType.FIERY_BREATH; - /** - * Constructs a new {@code KBDCombatSwingHandler} {@Code Object}. - * The combat style. - */ - public ElvargCombatSwingHandler() { - super(CombatStyle.RANGE); - } + /** + * The DragonFire Attack */ + private static final DragonfireSwingHandler DRAGONFIRE = new DragonfireSwingHandler(false, 60, null, true); - @Override - public void adjustBattleState(Entity entity, Entity victim, BattleState state) { - if (style == CombatStyle.RANGE) { - fireType.getTask().exec(victim, entity); - if (victim.hasProtectionPrayer(CombatStyle.MAGIC)) { - state.setEstimatedHit((int) (state.getEstimatedHit() * 0.6)); - } - state.setEstimatedHit(formatHit(victim, state.getEstimatedHit())); - return; - } - style.getSwingHandler().adjustBattleState(entity, victim, state); - } + /** + * Constructs a new {@Code ElvargCombatSwingHandler} {@Code CombatStyle}. + * The Combat style. + */ + public ElvargCombatSwingHandler() { + super(CombatStyle.RANGE); + } - @Override - public int calculateAccuracy(Entity entity) { - if (style == CombatStyle.MELEE) { - return style.getSwingHandler().calculateAccuracy(entity); - } - return 600; - } + @Override + public void adjustBattleState(Entity entity, Entity victim, BattleState state) { + if (style == CombatStyle.RANGE) { + fireType.getTask().exec(victim, entity); + state.setStyle(null); + DRAGONFIRE.adjustBattleState(entity, victim, state); + state.setStyle(CombatStyle.RANGE); + return; + } + style.getSwingHandler().adjustBattleState(entity, victim, state); + } - @Override - public int calculateDefence(Entity victim, Entity attacker) { - return style.getSwingHandler().calculateDefence(victim, attacker); - } + @Override + public int calculateAccuracy(Entity entity) { + // If in melee combat, calculate attackers accuracy level. + if (style == CombatStyle.MELEE) { + return style.getSwingHandler().calculateAccuracy(entity); + } + // Else calculate attackers accuracy based on their magic level. + return CombatStyle.MAGIC.getSwingHandler().calculateAccuracy(entity); + } - @Override - public int calculateHit(Entity entity, Entity victim, double modifier) { - if (style == CombatStyle.MELEE) { - return style.getSwingHandler().calculateHit(entity, victim, modifier); - } - int max = 56; - int damage = max; - if (victim instanceof Player) { - int val = victim.getDragonfireProtection(true); - if ((val & 0x2) != 0) { - damage *= 0.5; - } - if ((val & 0x4) != 0) { - damage -= (int) (damage * 0.85); - } - if ((val & 0x6) != 0) { - damage *= 0.6; - } - if (damage < 3) { - damage = 3; - } - } - if (fireType != FireType.FIERY_BREATH) { - damage += 11; - } - return damage; - } + @Override + public int calculateDefence(Entity victim, Entity attacker) { + // If in melee combat, calculate players defense level. + if (style == CombatStyle.MELEE) { + return style.getSwingHandler().calculateDefence(victim, attacker); + } + // Else calculate players defense against attack based on their magic level. + return CombatStyle.MAGIC.getSwingHandler().calculateDefence(victim, attacker); + } - @Override - public InteractionType canSwing(Entity entity, Entity victim) { - if (!isProjectileClipped(entity, victim, false)) { - return InteractionType.NO_INTERACT; - } - if (victim.getCenterLocation().withinDistance(entity.getCenterLocation(), getCombatDistance(entity, victim, 9)) && super.canSwing(entity, victim) != InteractionType.NO_INTERACT) { - entity.getWalkingQueue().reset(); - return InteractionType.STILL_INTERACT; - } - return InteractionType.NO_INTERACT; - } + @Override + public int calculateHit(Entity entity, Entity victim, double modifier) { + if (style == CombatStyle.MELEE) { + return style.getSwingHandler().calculateHit(entity, victim, modifier); + } - @Override - public ArmourSet getArmourSet(Entity e) { - return style.getSwingHandler().getArmourSet(e); - } + return calculateDragonfireMaxHit(victim, 60, false, 3, true); + } - @Override - public double getSetMultiplier(Entity e, int skillId) { - return style.getSwingHandler().getSetMultiplier(e, skillId); - } + @Override + public InteractionType canSwing(Entity entity, Entity victim) { + if (!isProjectileClipped(entity, victim, false)) { + return InteractionType.NO_INTERACT; + } + if (victim.getCenterLocation().withinDistance(entity.getCenterLocation(), getCombatDistance(entity, victim, 9)) && super.canSwing(entity, victim) != InteractionType.NO_INTERACT) { + entity.getWalkingQueue().reset(); + return InteractionType.STILL_INTERACT; + } + return InteractionType.NO_INTERACT; + } - @Override - public void impact(Entity entity, Entity victim, BattleState state) { - style.getSwingHandler().impact(entity, victim, state); - } + @Override + public ArmourSet getArmourSet(Entity e) { + return style.getSwingHandler().getArmourSet(e); + } - @Override - public int swing(Entity entity, Entity victim, BattleState state) { - style = CombatStyle.RANGE; - int hit = 0; - int ticks = 1; - if (victim.getCenterLocation().withinDistance(entity.getCenterLocation(), getCombatDistance(entity, victim, 1)) && RandomFunction.random(10) < 7) { - style = CombatStyle.MELEE; - } else { - ticks += (int) Math.ceil(entity.getLocation().getDistance(victim.getLocation()) * 0.3); - } - state.setStyle(style); - if (isAccurateImpact(entity, victim)) { - int max = calculateHit(entity, victim, 1.0); - state.setMaximumHit(max); - hit = RandomFunction.random(max); - } - state.setEstimatedHit(hit); - return ticks; - } + @Override + public double getSetMultiplier(Entity e, int skillId) { + return style.getSwingHandler().getSetMultiplier(e, skillId); + } - @Override - public void visualize(Entity entity, Entity victim, BattleState state) { - switch (style) { - case MELEE: - entity.animate(MELEE_ATTACK); - break; - case RANGE: - Projectile.ranged(entity, victim, 450, 20, 36, 50, 15).send(); - entity.animate(fireType.getAnimation()); - break; - default: - break; - } - } + @Override + public void impact(Entity entity, Entity victim, BattleState state) { + style.getSwingHandler().impact(entity, victim, state); + } - @Override - public void visualizeImpact(Entity entity, Entity victim, BattleState state) { - style.getSwingHandler().visualizeImpact(entity, victim, state); - if (style != CombatStyle.MELEE && victim instanceof Player) { - Player p = (Player) victim; - p.getPacketDispatch().sendMessage(getDragonfireMessage(victim.getAttribute("fire_resistance", 0), fireType.name().toLowerCase().replaceAll("_", " "))); - p.graphics(new Graphics(346, 100, 1)); - } - } + @Override + public int swing(Entity entity, Entity victim, BattleState state) { + style = CombatStyle.RANGE; + int hit = 0; + int ticks = 1; - } + if (victim.getCenterLocation().withinDistance(entity.getCenterLocation(), getCombatDistance(entity, victim, 1)) ) { + if ( RandomFunction.random(10) < 7 ){ + style = CombatStyle.MELEE; + } + } else { + ticks += (int) Math.ceil(entity.getLocation().getDistance(victim.getLocation()) * 0.3); + } + + state.setStyle(style); + + if (isAccurateImpact(entity, victim)) { + int max = calculateHit(entity, victim, 1.0); + state.setMaximumHit(max); + hit = RandomFunction.random(max + 1); + } + state.setEstimatedHit(hit); + return ticks; + } + + @Override + public void visualize(Entity entity, Entity victim, BattleState state) { + switch (style) { + case MELEE: + entity.animate(MELEE_ATTACK); + break; + case RANGE: + Projectile.ranged(entity, victim, 450, 20, 36, 50, 15).send(); + entity.animate(fireType.getAnimation()); + break; + default: + break; + } + } + + @Override + public void visualizeImpact(Entity entity, Entity victim, BattleState state) { + if (style != CombatStyle.MELEE) { + DRAGONFIRE.visualizeImpact(entity, victim, state); + } else { + style.getSwingHandler().visualizeImpact(entity, victim, state); + } + } + + } } 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 73f781d8c..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 @@ -6,6 +6,8 @@ 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 core.integrations.discord.Discord; +import content.data.Quests; /** * Represents the dialogue used to handle the wormbrain npc related to the @@ -50,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?"); @@ -96,7 +98,7 @@ public final class WormbrainDialogue extends DialoguePlugin { stage = 505; break; case 2: - player("Alrigt, then, 10,000 it is."); + player("Alright, then, 10,000 it is."); stage = 506; break; } @@ -110,6 +112,7 @@ public final class WormbrainDialogue extends DialoguePlugin { GroundItemManager.create(DragonSlayer.WORMBRAIN_PIECE, player); } interpreter.sendItemMessage(DragonSlayer.WORMBRAIN_PIECE.getId(), "You buy the map piece from Wormbrain."); + Discord.sendToOpenRSC(player.getName(), "Player obtained Wormbrain piece! (Dialogue)"); stage = 507; } else { end(); @@ -154,7 +157,7 @@ public final class WormbrainDialogue extends DialoguePlugin { break; case 2: player("Sorry, thought this was a zoo."); - stage = 20; + stage = 15; break; } break; @@ -181,9 +184,6 @@ public final class WormbrainDialogue extends DialoguePlugin { case 15: end(); break; - case 20: - end(); - break; } } } \ No newline at end of file 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 d35687166..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,19 +7,20 @@ 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): core.game.dialogue.DialoguePlugin(player) { +class AvanDialogue (player: Player? = null): DialoguePlugin(player) { val CREST_PIECE_AVAN: Item = Item(779) - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { - return AvanDialogue(player); + override fun newInstance(player: Player?): DialoguePlugin { + return AvanDialogue(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): core.game.dialogue.DialoguePlugin(p 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++ } @@ -121,14 +122,14 @@ class AvanDialogue (player: Player? = null): core.game.dialogue.DialoguePlugin(p } else{ player("I have the ring and necklace right here.") - stage = 300; + stage = 300 } 201 -> npc("Well, I won't entrust you with my piece of the crest " , "until you have brought me a necklace of perfect gold " , "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 20113cdae..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,24 +5,25 @@ 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 BootDialogue (player: Player? = null): core.game.dialogue.DialoguePlugin(player){ - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { +class BootDialogue (player: Player? = null): DialoguePlugin(player){ + override fun newInstance(player: Player?): DialoguePlugin { return BootDialogue(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(core.game.dialogue.FacialExpression.OLD_NORMAL,"Hello tall person.") + npc(FacialExpression.OLD_NORMAL,"Hello tall person.") stage = 1 } else{ - npc(core.game.dialogue.FacialExpression.OLD_NORMAL,"Hello tall person.") + npc(FacialExpression.OLD_NORMAL,"Hello tall person.") stage = 2 } @@ -38,7 +39,8 @@ class BootDialogue (player: Player? = null): core.game.dialogue.DialoguePlugin(p 10 ->when(buttonId){ 1-> player("Hello short person.").also { stage = 1000 } - 2 -> npc(core.game.dialogue.FacialExpression.OLD_ANGRY1,"I'm called Boot, because when I was very young, ", + 2 -> npc( + FacialExpression.OLD_ANGRY1,"I'm called Boot, because when I was very young, ", "I used to sleep, in a large boot.").also{stage++} } @@ -46,23 +48,25 @@ class BootDialogue (player: Player? = null): core.game.dialogue.DialoguePlugin(p 11 -> player("Yeah, great, I didn't want your life story.").also { stage = 1000} 20 -> when (buttonId){ - 1 -> npc(core.game.dialogue.FacialExpression.OLD_DEFAULT,"High quality gold eh? Hmmm... " , + 1 -> npc( + FacialExpression.OLD_DEFAULT,"High quality gold eh? Hmmm... " , "Well, the very best quality gold that I know of " , "can be found in an underground ruin near Witchaven.").also{stage++} 2-> player("Hello short person.").also { stage = 1000 } - 3 -> npc(core.game.dialogue.FacialExpression.OLD_ANGRY1,"I'm called Boot, because when I was very young, ", + 3 -> npc( + FacialExpression.OLD_ANGRY1,"I'm called Boot, because when I was very young, ", "I used to sleep, in a large boot.").also{stage = 11} } 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) + stage = 1000 + player.questRepository.getQuest(Quests.FAMILY_CREST).setStage(player, 15) } 1000 -> end() } - return true; + return true } override fun getIds(): IntArray { 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 963668177..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,20 +7,21 @@ 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 CalebDialogue (player: Player? = null): core.game.dialogue.DialoguePlugin(player) { +class CalebDialogue (player: Player? = null): DialoguePlugin(player) { val CREST_PIECE: Item = Item(780) - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return CalebDialogue(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): core.game.dialogue.DialoguePlugin( 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): core.game.dialogue.DialoguePlugin( 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++} @@ -189,12 +190,12 @@ class CalebDialogue (player: Player? = null): core.game.dialogue.DialoguePlugin( 403 -> if(player.inventory.containItems(CREST_PIECE.id, 782) || player.bank.containItems(CREST_PIECE.id, 782)) { npc("Then why are you wasting your time here?") - stage = 1000; - } + stage = 1000 + } else{ player("I have lost the fragment that you gave me...") - stage++; - } + stage++ + } 404 -> npc("I have some good news for you then. " , "One of my customers found this on their travels " , "and recognised it as mine and returned it to me here.").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 a7720b9a2..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,24 +10,21 @@ 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.api.getQuestStage +import core.api.hasAnItem +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import content.data.Quests @Initializable class ChronozonCaveZone: MapZone("FC ChronozoneZone", true), Plugin { - val triggers = ArrayList() - var chronozon = ChronozonNPC(667, Location(3086, 9936, 0)) + val spawnLoc = Location(3086, 9936, 0) + var chronozon = ChronozonNPC(NPCs.CHRONOZON_667, spawnLoc) + override fun configure() { - register(ZoneBorders(3082, 9929, 3091, 9940)) - triggers.add(Location.create(3083, 9939)) - triggers.add(Location.create(3084, 9939)) - triggers.add(Location.create(3085, 9939)) - triggers.add(Location.create(3086, 9939)) - triggers.add(Location.create(3087, 9939)) - triggers.add(Location.create(3088, 9939)) - triggers.add(Location.create(3089, 9939)) - triggers.add(Location.create(3090, 9939)) + register(ZoneBorders(3079, 9927, 3095, 9944)) } override fun move(e: Entity?, from: Location?, to: Location?): Boolean { @@ -37,22 +34,36 @@ class ChronozonCaveZone: MapZone("FC ChronozoneZone", true), Plugin { override fun enter(e: Entity?): Boolean { if (e != null) { if (e.isPlayer) { - chronozon = ChronozonNPC(667, Location(3086, 9936, 0)) - var player = e as Player - if (player.questRepository.getQuest("Family Crest").getStage(e) == 19 && !RegionManager.getLocalNpcs( - Location(3086, 9936, 0), - 5 - ).contains(chronozon) - ) { - chronozon.setPlayer(e); - chronozon.isRespawn = false - chronozon.init() + val player = e as Player + 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)){ + chronozon.setPlayer(e) + chronozon.isRespawn = false + chronozon.location = spawnLoc + chronozon.init() + } } } return true } 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 dfd84009d..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 @@ -6,41 +6,34 @@ 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 org.rs09.consts.NPCs +import content.data.Quests +class ChronozonNPC(id: Int, location: Location?) : AbstractNPC(NPCs.CHRONOZON_667, Location(3086, 9936, 0)){ -class ChronozonNPC(id: Int, location: Location?) : AbstractNPC(667, Location(3086, 9936, 0)){ + private lateinit var targetplayer: Player - lateinit var m_targetPlayer: Player + private var amountOfFireDamageTaken: Int = 0 - var m_amountOfFireDamageTaken: Int = 0 + private var amountOfAirDamageTaken: Int = 0 - var m_amountOfAirDamageTaken: Int = 0 + private var amountOfWaterDamageTaken: Int = 0 - var m_amountOfWaterDamageTaken: Int = 0 - - var m_amountOfEarthDamageTaken: Int = 0 + private var amountOfEarthDamageTaken: Int = 0 override fun construct(id: Int, location: Location?, vararg objects: Any?): AbstractNPC { return ChronozonNPC(id, location) } override fun getIds(): IntArray { - return intArrayOf(667) - } - - override fun handleTickActions() { - super.handleTickActions() - if (!m_targetPlayer.isActive || m_targetPlayer.getLocation().getDistance(getLocation()) > 15) { - clear() - } - + return intArrayOf(NPCs.CHRONOZON_667) } override fun checkImpact(state: BattleState?) { if (state != null) { - if(m_amountOfAirDamageTaken == 0 || m_amountOfWaterDamageTaken == 0 || - m_amountOfEarthDamageTaken == 0 || m_amountOfFireDamageTaken == 0) { + if(amountOfAirDamageTaken == 0 || amountOfWaterDamageTaken == 0 || + amountOfEarthDamageTaken == 0 || amountOfFireDamageTaken == 0) { if(state.style != CombatStyle.MAGIC || state.totalDamage >= skills.lifepoints) { state.neutralizeHits() } @@ -48,59 +41,62 @@ class ChronozonNPC(id: Int, location: Location?) : AbstractNPC(667, Location(308 if(state.spell != null) { if(state.spell.spellId == 24) { - if(state.totalDamage > 0 && m_amountOfAirDamageTaken == 0) { - m_targetPlayer.sendMessage("Chronozon weakens...") + if(state.totalDamage > 0 && amountOfAirDamageTaken == 0) { + targetplayer.sendMessage("Chronozon weakens...") } - m_amountOfAirDamageTaken += state.totalDamage + amountOfAirDamageTaken += state.totalDamage } if(state.spell.spellId == 27) { - if(state.totalDamage > 0 && m_amountOfWaterDamageTaken == 0) { - m_targetPlayer.sendMessage("Chronozon weakens...") + if(state.totalDamage > 0 && amountOfWaterDamageTaken == 0) { + targetplayer.sendMessage("Chronozon weakens...") } - m_amountOfWaterDamageTaken += state.totalDamage + amountOfWaterDamageTaken += state.totalDamage } if(state.spell.spellId == 33) { - if(state.totalDamage > 0 && m_amountOfEarthDamageTaken == 0) { - m_targetPlayer.sendMessage("Chronozon weakens...") + if(state.totalDamage > 0 && amountOfEarthDamageTaken == 0) { + targetplayer.sendMessage("Chronozon weakens...") } - m_amountOfEarthDamageTaken += state.totalDamage + amountOfEarthDamageTaken += state.totalDamage } if(state.spell.spellId == 38) { - if(state.totalDamage > 0 && m_amountOfFireDamageTaken == 0) { - m_targetPlayer.sendMessage("Chronozon weakens...") + if(state.totalDamage > 0 && amountOfFireDamageTaken == 0) { + targetplayer.sendMessage("Chronozon weakens...") } - m_amountOfFireDamageTaken += state.totalDamage + amountOfFireDamageTaken += state.totalDamage } } } } - override fun isAttackable(entity: Entity, style: CombatStyle?, message: Boolean): Boolean { - return entity == m_targetPlayer && - m_targetPlayer.questRepository.getQuest("Family Crest").getStage(m_targetPlayer) == 19 && - super.isAttackable(entity, style, message) - } - - override fun clear() { - super.clear() - } - override fun finalizeDeath(killer: Entity?) { - if(killer == m_targetPlayer) { - m_targetPlayer.questRepository.getQuest("Family Crest").setStage(m_targetPlayer, 20) + if(killer == targetplayer) { + 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() super.finalizeDeath(killer) - } + fun setPlayer(player: Player){ - m_targetPlayer = player; + targetplayer = player + } + + override fun init() { + // Make sure to reset damage taken when spawning in + amountOfFireDamageTaken = 0 + amountOfAirDamageTaken= 0 + amountOfWaterDamageTaken= 0 + amountOfEarthDamageTaken= 0 + super.init() + } } -//3086, 9936, 0 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 b45b24023..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 @@ -2,11 +2,11 @@ package content.region.misthalin.varrock.quest.familycrest import core.api.* -import core.game.dialogue.DialoguePlugin 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 @@ -19,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 = questStage(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; + 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, ", @@ -135,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 { @@ -168,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) } } @@ -180,7 +178,7 @@ class DimintheisDialogue(player: Player? = null): core.game.dialogue.DialoguePlu 1000 -> end() } - return true; + return true } override fun getIds(): IntArray { 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 9bd882d9c..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 @@ -2,13 +2,15 @@ package content.region.misthalin.varrock.quest.familycrest import core.api.addItem +import core.api.log import core.api.setAttribute 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 core.tools.Log import org.rs09.consts.Items -import core.tools.SystemLogger +import content.data.Quests /** * Represents the "Family Crest" quest. @@ -16,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 @@ -86,7 +88,7 @@ class FamilyCrest: Quest("Family Crest", 59, 58, 1, 148, 0, 1, 11) { line(player, "and would always return to !!Dimintheis??", line++) line(player, "for me to reclaim if I ever lost them.", line++) line++ - line(player, "!!QUEST COMPLETE!??.", line++) + line(player, "%%QUEST COMPLETE!&&.", line++) } @@ -118,7 +120,7 @@ class FamilyCrest: Quest("Family Crest", 59, 58, 1, 148, 0, 1, 11) { drawReward(player,"for the gauntlets",ln++) if (!addItem(player, Items.FAMILY_GAUNTLETS_778)) { - SystemLogger.logErr(this::class.java, "Failed to give gauntlets to ${player.username} at end of quest, this should not occur due to crest item removal needed to finish quest.") + log(this::class.java, Log.ERR, "Failed to give gauntlets to ${player.username} at end of quest, this should not occur due to crest item removal needed to finish quest.") } setAttribute(player, "/save:family-crest:gauntlets", Items.FAMILY_GAUNTLETS_778) } 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 29e5a615c..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 = questStage(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 80dbfae90..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,17 +8,18 @@ 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): core.game.dialogue.DialoguePlugin(player) { +class JohnathonDialogue(player: Player? = null): DialoguePlugin(player) { val CREST_PIECE: Item = Item(781) - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return JohnathonDialogue(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") @@ -28,7 +29,7 @@ class JohnathonDialogue(player: Player? = null): core.game.dialogue.DialoguePlug if(qstage < 16){ npc("I don't feel so well... maybe we can talk later") - stage = 1000; + stage = 1000 } else{ when(qstage){ @@ -53,7 +54,7 @@ class JohnathonDialogue(player: Player? = null): core.game.dialogue.DialoguePlug "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): core.game.dialogue.DialoguePlug "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/PerfectGoldSmeltingHandler.kt b/Server/src/main/content/region/misthalin/varrock/quest/familycrest/PerfectGoldSmeltingHandler.kt index 4e66dd5e5..cec2f6520 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/familycrest/PerfectGoldSmeltingHandler.kt +++ b/Server/src/main/content/region/misthalin/varrock/quest/familycrest/PerfectGoldSmeltingHandler.kt @@ -2,7 +2,7 @@ package content.region.misthalin.varrock.quest.familycrest import core.api.addItem -import core.api.animate; +import core.api.animate import core.api.removeItem import core.game.interaction.NodeUsageEvent import core.game.interaction.UseWithHandler @@ -19,7 +19,7 @@ import core.game.world.GameWorld.Pulser class PerfectGoldSmeltingHandler : UseWithHandler(Items.PERFECT_GOLD_ORE_446){ - private val furnaceIDs: IntArray = FurnaceOptionPlugin.SmeltUseWithHandler.furnaceIDS; + private val furnaceIDs: IntArray = FurnaceOptionPlugin.SmeltUseWithHandler.furnaceIDS override fun newInstance(arg: Any?): Plugin { for(furnace in furnaceIDs){ diff --git a/Server/src/main/content/region/misthalin/varrock/quest/familycrest/PerfectJewelryHandler.kt b/Server/src/main/content/region/misthalin/varrock/quest/familycrest/PerfectJewelryHandler.kt index 49843aaec..ff615e0e1 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/familycrest/PerfectJewelryHandler.kt +++ b/Server/src/main/content/region/misthalin/varrock/quest/familycrest/PerfectJewelryHandler.kt @@ -8,17 +8,16 @@ import core.plugin.Initializable @Initializable -class PerfectJewelryHandler (player: Player? = null): core.game.dialogue.DialoguePlugin(player){ +class PerfectJewelryHandler (player: Player? = null): DialoguePlugin(player){ - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return PerfectJewelryHandler(player) } override fun open(vararg args: Any?): Boolean { - println(player.name) if(player.inventory.containItems(2365, 1603)){ options("Craft perfect ruby ring", "Craft perfect ruby necklace") - stage = 1; + stage = 1 } return true } @@ -36,7 +35,7 @@ class PerfectJewelryHandler (player: Player? = null): core.game.dialogue.Dialogu } else{ sendDialogue("You do not have everything to make this item.") - stage = 1000; + stage = 1000 } 2-> if(player.inventory.containItems(2365, 1603) && player.inventory.containItems(1597)){ @@ -47,7 +46,7 @@ class PerfectJewelryHandler (player: Player? = null): core.game.dialogue.Dialogu } else{ sendDialogue("You do not have everything to make this item.") - stage = 1000; + stage = 1000 } } @@ -58,7 +57,7 @@ class PerfectJewelryHandler (player: Player? = null): core.game.dialogue.Dialogu } override fun getIds(): IntArray { - return intArrayOf(core.game.dialogue.DialogueInterpreter.getDialogueKey("perfect-jewelry")) + return intArrayOf(DialogueInterpreter.getDialogueKey("perfect-jewelry")) } } diff --git a/Server/src/main/content/region/misthalin/varrock/quest/familycrest/PerfectJewelryOnUseHandler.kt b/Server/src/main/content/region/misthalin/varrock/quest/familycrest/PerfectJewelryOnUseHandler.kt index 65cac212f..4d258b5db 100644 --- a/Server/src/main/content/region/misthalin/varrock/quest/familycrest/PerfectJewelryOnUseHandler.kt +++ b/Server/src/main/content/region/misthalin/varrock/quest/familycrest/PerfectJewelryOnUseHandler.kt @@ -19,7 +19,7 @@ class PerfectJewelryOnUseHandler : UseWithHandler(Items.PERFECT_GOLD_BAR_2365){ override fun handle(event: NodeUsageEvent?): Boolean { event ?: return false - event.player.dialogueInterpreter.open("perfect-jewelry"); + event.player.dialogueInterpreter.open("perfect-jewelry") return true } 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 8dcf831a1..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 @@ -8,9 +8,10 @@ import core.game.world.map.RegionManager import core.game.world.update.flag.context.Animation import core.net.packet.out.ClearScenery import core.net.packet.out.ConstructScenery -import core.net.packet.out.UpdateAreaPosition; +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 } @@ -26,7 +27,7 @@ fun doDoor(player: Player, scenery: Scenery) { ForceMovement.WALK_ANIMATION, ForceMovement.WALK_ANIMATION, direction, - 8); + 8) } @@ -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 0204158ff..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 @@ -74,18 +77,20 @@ public class GertrudesCat extends Quest { public void finish(Player player) { super.finish(player); final Item kitten = getKitten(); - player.getConfigManager().set(101, player.getQuestRepository().getPoints()); player.getPacketDispatch().sendString("1 Quest Point", 277, 8 + 2); player.getPacketDispatch().sendString("A kitten!", 277, 9 + 2); 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 fb31d8d92..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]; @@ -102,10 +103,10 @@ public final class JulietDialogue extends DialoguePlugin { break; case 60: if (!player.getInventory().contains(756, 1)) { - interpreter.sendDialogues(player, null, "Hi Juliet! I have an interesting propostion for", "you...suggested by Father Lawrence. It may be the", "only way you'll be able to escape from this house and", "be with Romeo."); + interpreter.sendDialogues(player, null, "Hi Juliet! I have an interesting proposition for", "you...suggested by Father Lawrence. It may be the", "only way you'll be able to escape from this house and", "be with Romeo."); stage = 950; } else { - interpreter.sendDialogues(player, null, "Hi Juliet! I have an interesting propostion for", "you...suggested by Father Lawrence. It may be the", "only way you'll be able to escape from this house and", "be with Romeo."); + interpreter.sendDialogues(player, null, "Hi Juliet! I have an interesting proposition for", "you...suggested by Father Lawrence. It may be the", "only way you'll be able to escape from this house and", "be with Romeo."); stage = 950; } break; @@ -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) { @@ -200,7 +201,7 @@ public final class JulietDialogue extends DialoguePlugin { stage = 2010; break; case 2010: - interpreter.sendDialogues(phil, FacialExpression.FURIOUS, "OH NO...JULIET HAS...DIED?....", "Ooooooohhhhhhh.....(sob).Juliet...my poor dead cousin!"); + interpreter.sendDialogues(phil, FacialExpression.FURIOUS, "OH NO...JULIET HAS...DIED?....", "Oooooohhhhhh....(sob), (sob).Juliet...my poor dead cousin!"); stage = 2011; break; case 2011: @@ -566,4 +567,4 @@ public final class JulietDialogue extends DialoguePlugin { return new int[] { 637 }; } -} \ No newline at end of file +} 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 aeb47a9d0..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?"); @@ -397,7 +398,7 @@ public final class RJCutscenePlugin extends CutscenePlugin { stage = 414; break; case 414: - interpreter.sendDialogues(player, null, "I have not be honest, it's not getting any better..."); + interpreter.sendDialogues(player, null, "I have to be honest, it's not getting any better..."); stage = 415; break; case 415: @@ -440,7 +441,7 @@ public final class RJCutscenePlugin extends CutscenePlugin { stage = 424; break; case 424: - interpreter.sendDialogues(npc, null, "Oh yes, Father Lawrence...he's our long time confidant,", "he might have a solution! yes, yes, you have to go and", "talk to Lather Fawrence for us and ask him if he's got", "any suggestions for our predicatment?"); + interpreter.sendDialogues(npc, null, "Oh yes, Father Lawrence...he's our long time confidant,", "he might have a solution! yes, yes, you have to go and", "talk to Lather Fawrence for us and ask him if he's got", "any suggestions for our predicament?"); stage = 425; break; case 425: @@ -448,7 +449,7 @@ public final class RJCutscenePlugin extends CutscenePlugin { stage = 426; break; case 426: - interpreter.sendDialogues(npc, null, "Lawther Fawrence! Oh he's...."); + interpreter.sendDialogues(npc, null, "Lather Fawrence! Oh he's..."); stage = 427; break; case 427: @@ -730,7 +731,7 @@ public final class RJCutscenePlugin extends CutscenePlugin { }); break; case 745: - interpreter.sendDialogues(npc, null, "Juliet..."); + interpreter.sendDialogues(npc, null, "Juliet....?"); stage = 746; break; case 746: @@ -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.java b/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/CuratorHaigHalenDialogue.java deleted file mode 100644 index 639679253..000000000 --- a/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/CuratorHaigHalenDialogue.java +++ /dev/null @@ -1,74 +0,0 @@ -package content.region.misthalin.varrock.quest.shieldofarrav; - -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.DiaryType; -import core.game.dialogue.IfTopic; -import content.region.desert.quest.thegolem.CuratorHaigHalenGolemDialogue; - -/** - * Represents the curator haig halen dialogue. - * @author 'Vexia - * @version 1.0 - */ -public final class CuratorHaigHalenDialogue extends DialoguePlugin { - - /** - * Constructs a new {@code CuratorHaigHalenDialogue} {@code Object}. - */ - public CuratorHaigHalenDialogue() { - /** - * empty. - */ - } - - /** - * Constructs a new {@code CuratorHaigHalenDialogue} {@code Object}. - * @param player the player. - */ - public CuratorHaigHalenDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new CuratorHaigHalenDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Welcome to the museum of Varrock."); - - if (player.getQuestRepository().getPoints() >= 50) { - player.getAchievementDiaryManager().finishTask(player, DiaryType.VARROCK, 0, 12); - } - - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch(stage) { - case 0: - showTopics( - new IfTopic("I have the Shield of Arrav", new CuratorHaigHalenSOADialogue(), - player.getQuestRepository().getQuest("Shield of Arrav").getStage(player) == 70 - , false), - new IfTopic("I'm looking for a statuette recovered from the city of Uzer.", new CuratorHaigHalenGolemDialogue(), - player.getQuestRepository().getQuest("The Golem").getStage(player) >= 3 - , false) - ); - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 646 }; - } -} 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 new file mode 100644 index 000000000..67422ee21 --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/CuratorHaigHalenDialogue.kt @@ -0,0 +1,85 @@ +package content.region.misthalin.varrock.quest.shieldofarrav + +import content.region.desert.quest.thegolem.CuratorHaigHalenGolemDialogue +import core.api.* +import core.game.dialogue.* +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.diary.DiaryType +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 { + when (stage) { + START_DIALOGUE -> npcl(FacialExpression.NEUTRAL, "Welcome to Varrock Museum!").also { + if (player.getQuestRepository().points >= 50 && !player.achievementDiaryManager.hasCompletedTask(DiaryType.VARROCK, 0, 12)) { + player.achievementDiaryManager.finishTask(player, DiaryType.VARROCK, 0, 12) + } + 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++ + } + 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, Quests.THE_DIG_SITE) == 2 && !inInventory(player, Items.SEALED_LETTER_683)), + IfTopic("I have the Shield of Arrav", CuratorHaigHalenSOADialogue(), + getQuestStage(player, Quests.SHIELD_OF_ARRAV) == 70, false), + IfTopic("I'm looking for a statuette recovered from the city of Uzer.", CuratorHaigHalenGolemDialogue(), + 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++ } + 4 -> npcl(FacialExpression.FRIENDLY, "Indeed. I suspect we'll be looking for qualified archaeologists once we have constructed our canal and barge.").also { stage++ } + 5 -> playerl(FacialExpression.FRIENDLY, "Would I qualify then?").also { stage++ } + // These three dialogues are based on what he would say depending on what stage of the quest/kudos you have. I'm too lazy to implement this part as the kudos system isn't implemented yet and this is all cosmetic. + // "Unfortunately, you haven't passed your Earth Science", "exams so you aren't qualified to help us on the dig. If" "you're interested, visit the Exam Centre on the Dig Site", "to the east and talk to the examiners there." + // "Unfortunately, you haven't earned enough kudos yet to", "help us on the dig. If you're interested in helping us", "out and getting that Kudos, simply help out around the", "Museum. You can find out more at the information" ANOTHER DIALOGUE "booth." PLAYER "Ok, thanks. + // After 150 kudos I think "Yes indeed. You've helped us a great deal around the", "Museum and you have the necessary qualifications from", "the Earth Sciences exams. When the canal is ready,", "we'll let you know." + 6 -> npcl(FacialExpression.FRIENDLY, "You've certainly done a lot to help out Varrock Museum, so we'd be silly not to ask for your expertise.").also { stage++ } + 7 -> playerl(FacialExpression.FRIENDLY, "Thank you. I'll look forward to it!").also { + stage = END_DIALOGUE + } + 8 -> npcl(FacialExpression.FRIENDLY, "Look around you! This museum is full of treasures!").also { stage++ } + 9 -> playerl(FacialExpression.FRIENDLY, "No, I meant treasures for ME.").also { stage++ } + 10 -> npcl(FacialExpression.FRIENDLY, "Any treasures this museum knows about it goes to great lengths to acquire.").also { + stage = END_DIALOGUE + } + 11 -> player(FacialExpression.FRIENDLY, "I've been given this letter by an examiner at the Dig", "Site. Can you stamp this for me?").also { stage++ } + 12 -> npc(FacialExpression.FRIENDLY, "What have we here? A letter of recommendation", "indeed...").also { stage++ } + // The next two dialogues are supposed to expand based on the player.username length, but I have no time for this shit. + 13 -> npcl(FacialExpression.FRIENDLY, "The letter here says you name is ${player.username}. Well ${player.username}, I wouldn't normally do this for just anyone, but as you did us such a great service with the Shield of Arrav I don't see why not.").also { stage++ } + 14 -> npcl(FacialExpression.FRIENDLY, "Run this letter back to the Examiner to begin your adventure into the world of Earth Sciences. Enjoy your studies, Student!").also { stage++ } + 15 -> npc(FacialExpression.FRIENDLY, "There you go, good luck student... Be sure to come", "back and show me your certificates. I would like to see", "how you get on.").also { + if (removeItem(player, Items.UNSTAMPED_LETTER_682)) { + addItemOrDrop(player, Items.SEALED_LETTER_683) + } + stage++ + } + 16 -> playerl(FacialExpression.FRIENDLY, "Ok, I will. Thanks, see you later.").also { + if(getQuestStage(player, Quests.THE_DIG_SITE) == 1) { + setQuestStage(player, Quests.THE_DIG_SITE, 2) + } + stage = 1 + } + 18 -> npc(FacialExpression.FRIENDLY, "Yes, I saw you drop it as you walked off last time. Here it is.").also { + addItemOrDrop(player, Items.SEALED_LETTER_683) + stage = 1 + } + } + return true + } + + override fun newInstance(player: Player): DialoguePlugin { + return CuratorHaigHalenDialogue(player) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.CURATOR_HAIG_HALEN_646) + } +} 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 826944835..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,10 +238,12 @@ 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: + end(); + break; } return true; 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 46fce160e..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,12 +55,12 @@ 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 public Quest newInstance(Object object) { - ClassScanner.definePlugins(new CertificatePlugin(), new CuratorHaigHalenDialogue(), new JohnnyBeardNPC(), new JonnytheBeardPlugin(), new KatrineDialogue(), new KingRoaldDialogue(), new ReldoDialogue(), new ShieldArravPlugin(), new ShieldofArravBook(), new StravenDialogue(), new WeaponsMasterDialogue()); + ClassScanner.definePlugins(new CertificatePlugin(), new CuratorHaigHalenDialogue(), new JohnnyBeardNPC(), new JonnytheBeardPlugin(), new KatrineDialogue(), new KingRoaldDialogue(), new ReldoDialogue(), new ShieldArravPlugin(), new StravenDialogue(), new WeaponsMasterDialogue()); return this; } @@ -94,7 +95,7 @@ public class ShieldofArrav extends Quest { line(player, "I read about a valuable shield stolen long ago by a gang of", 4+ 7); line(player, "thieves with an outstanding reward upon it.", 5+ 7); line(player, BLUE + "Baraek told me that the " + RED + "'Phoenix Gang' " + BLUE + "have a hideout in", 6+ 7); - line(player, BLUE + "the " + RED + "south-eastern part of Varrock" + BLUE + ", disgusing themselves", 7+ 7); + line(player, BLUE + "the " + RED + "south-eastern part of Varrock" + BLUE + ", disguising themselves", 7+ 7); line(player, BLUE + "as the " + RED + "VTAM Corporation" + BLUE + ". I should find them and join.", 8+ 7); break; case 50: @@ -124,14 +125,14 @@ public class ShieldofArrav extends Quest { line(player, "According to him there is a criminal organisation known as", 9+ 7); line(player, "the " + RED + "'Black Arm Gang'" + BLUE + "down the alley near to him.", 10+ 7); line(player, BLUE + "If I want to join the " + RED + "Phoenix Gang " + BLUE + "I need to kill" + RED + " Jonny The", 11+ 7); - line(player, RED + "Beard " + BLUE + "in the " + RED + "Blue Moon Inn " + BLUE + "and retreive his " + RED + "report.", 12+ 7); + line(player, RED + "Beard " + BLUE + "in the " + RED + "Blue Moon Inn " + BLUE + "and retrieve his " + RED + "report.", 12+ 7); line(player, RED + "Katrine " + BLUE + "said if I wanted to join the " + RED + "Black Arm Gang," + BLUE + " I'd", 13+ 7); line(player, BLUE + "have to steal " + RED + "two Phoenix crossbows " + BLUE + "from the rival gang.", 14+ 7); line(player, BLUE + "Maybe " + RED + "Charlie the tramp " + BLUE + "can give me some ideas about", 15+ 7); line(player, BLUE + "how to do this.", 16+ 7); } else if (isPhoenixMission(player)) { line(player, BLUE + "If I want to join the " + RED + "Phoenix Gang " + BLUE + "I need to kill" + RED + " Jonny The", 10+ 7); - line(player, RED + "Beard " + BLUE + "in the " + RED + "Blue Moon Inn " + BLUE + "and retreive his " + RED + "report.", 11+ 7); + line(player, RED + "Beard " + BLUE + "in the " + RED + "Blue Moon Inn " + BLUE + "and retrieve his " + RED + "report.", 11+ 7); line(player, BLUE + "Alternatively, if I want to join the " + RED + "Blackarm gang " + BLUE + "I should", 12+ 7); line(player, BLUE + "speak to their " + RED + "leader, Katrine, " + BLUE + "about joining.", 13+ 7); } else if (isBlackArmMission(player)) { @@ -178,7 +179,7 @@ public class ShieldofArrav extends Quest { line(player, "Crossbows from the Phoenix Gang's weapons store, and", 9+ 7); line(player, "Katrine welcomes me as a Black Arm Gang member.", 10+ 7); line(player, "With the help of my friend in the rival gang, I was able to", 12+ 7); - line(player, "retreive both parts of the fabled Shield of Arrav and", 13+ 7); + line(player, "retrieve both parts of the fabled Shield of Arrav and", 13+ 7); line(player, "return it to the Museum of Varrock. In Recognition of my", 14+ 7); line(player, "efforts, King Roald paid me the reward set by his", 15+ 7); line(player, "ancestor.", 16+ 7); @@ -188,7 +189,7 @@ public class ShieldofArrav extends Quest { line(player, "I killed Jonny the Beard and was welcomed into the Phoenix", 7+ 7); line(player, "Gang. Straven gave me a key to the weapons room.", 8+ 7); line(player, "With the help of my friend in the rival gang, I was able to", 10+ 7); - line(player, "retreive both parts of the fabled Shield of Arrav and", 11+ 7); + line(player, "retrieve both parts of the fabled Shield of Arrav and", 11+ 7); line(player, "return it to the Museum of Varrock. In Recognition of my", 12+ 7); line(player, "efforts, King Roald paid me the reward set by his", 13+ 7); line(player, "ancestor.", 14+ 7); @@ -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.java b/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/ShieldofArravBook.java deleted file mode 100644 index e8faf9cf4..000000000 --- a/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/ShieldofArravBook.java +++ /dev/null @@ -1,84 +0,0 @@ -package content.region.misthalin.varrock.quest.shieldofarrav; - -import core.game.dialogue.DialoguePlugin; -import core.game.dialogue.book.Book; -import core.game.dialogue.book.BookLine; -import core.game.dialogue.book.Page; -import core.game.dialogue.book.PageSet; -import core.game.node.entity.player.Player; - -/** - * Represents the shield of arrav book. - * @author 'Vexia - * @author Empathy - * @version 2.0 - */ -public final class ShieldofArravBook extends Book { - - /** - * Represents the shield of arrav book id. - */ - public static int ID = 49610758; - - /** - * Represents the array of pages for this book. - */ - private static final PageSet[] PAGES = new PageSet[] { new PageSet(new Page(new BookLine("The Shield of Arrav", 55), new BookLine("by A.R Wright", 57), new BookLine("Arrav is probably the best", 61), new BookLine("known hero of the 4th", 62), new BookLine("age. Many legends are", 63), new BookLine("told of his heroics. One", 64), new BookLine("surviving artefact from", 65)), new Page(new BookLine("the 4th age is a fabulous", 66), new BookLine("shield.", 67), new BookLine("This shield is believed to", 69), new BookLine("have once belonged to", 70), new BookLine("Arrav and is now indeed", 71), new BookLine("known as the Shield of", 72), new BookLine("Arrav. For over 150", 73), new BookLine("years it was the prize", 74), new BookLine("piece in the royal", 75), new BookLine("museum of Varrock.", 76))), new PageSet(new Page(new BookLine("However, in the year 143", 55), new BookLine("of the fith age a gang of", 56), new BookLine("thieves called the Phoenix", 57), new BookLine("Gang broke into the", 58), new BookLine("museum and stole the", 59), new BookLine("shield in a daring raid. As", 60), new BookLine("a result, the current", 61), new BookLine("ruler, King Roald, put a", 62), new BookLine("1200 gold bounty (a", 63), new BookLine("massive sum of money in", 64), new BookLine("those days) on the return", 65)), new Page(new BookLine("of the shield, hoping that", 66), new BookLine("one of the culprits would", 67), new BookLine("betray his fellows out of", 68), new BookLine("greed.", 69))), new PageSet(new Page(new BookLine("This tactic did not work", 55), new BookLine("however, and the thieves", 56), new BookLine("who stole the shield have", 57), new BookLine("since gone on to become", 58), new BookLine("the most powerful crime", 59), new BookLine("gang in Varrock, despite", 60), new BookLine("making an enemy of the", 61), new BookLine("Royal Family many", 62), new BookLine("years ago.", 63)), new Page(new BookLine("The reward for the", 66), new BookLine("return of the shield still", 67), new BookLine("stands.", 68))) }; - - /** - * Constructs a new {@code ShieldofArravBook} {@code Object}. - */ - public ShieldofArravBook(final Player player) { - super(player, "The Shield of Arrav", ShieldofArrav.BOOK.getId(), PAGES); - } - - /** - * Constructs a new {@code ShieldofArravBook} {@code Object}. - */ - public ShieldofArravBook() { - /** - * empty. - */ - } - - @Override - public void finish() { - if (player.getQuestRepository().getQuest("Shield of Arrav").getStage(player) == 10) { - player.getQuestRepository().getQuest("Shield of Arrav").setStage(player, 20); - } - } - - @Override - public void display(Page[] set) { - player.lock(); - player.getInterfaceManager().open(getInterface()); - for (int i = 55; i < 77; i++) { - player.getPacketDispatch().sendString("", getInterface().getId(), i); - } - player.getPacketDispatch().sendString(getName(), getInterface().getId(), 6); - player.getPacketDispatch().sendString("", getInterface().getId(), 77); - player.getPacketDispatch().sendString("", getInterface().getId(), 78); - for (Page page : set) { - for (BookLine line : page.getLines()) { - player.getPacketDispatch().sendString(line.getMessage(), getInterface().getId(), line.getChild()); - } - } - player.getPacketDispatch().sendInterfaceConfig(getInterface().getId(), 51, index < 1 ? true : false); - boolean lastPage = index == sets.length - 1; - player.getPacketDispatch().sendInterfaceConfig(getInterface().getId(), 53, lastPage ? true : false); - if (lastPage) { - finish(); - } - player.unlock(); - } - - @Override - public DialoguePlugin newInstance(Player player) { - return new ShieldofArravBook(player); - } - - @Override - public int[] getIds() { - return new int[] { ID }; - } -} 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 new file mode 100644 index 000000000..10ec52e4e --- /dev/null +++ b/Server/src/main/content/region/misthalin/varrock/quest/shieldofarrav/ShieldofArravBook.kt @@ -0,0 +1,105 @@ +package content.region.misthalin.varrock.quest.shieldofarrav + +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 +import content.data.Quests +import core.api.getQuestStage +import core.api.setQuestStage + +/** + * Shield of Arrav Book + * @author ovenbreado + * @author 'Vexia + * @author Empathy + */ +class ShieldofArravBook : InteractionListener { + companion object { + private val TITLE = "The Shield of Arrav" + private val CONTENTS = arrayOf( + PageSet( + Page( + BookLine("The Shield of Arrav", 55), + BookLine("by A.R Wright", 57), + BookLine("Arrav is probably the best", 61), + BookLine("known hero of the 4th", 62), + BookLine("age. Many legends are", 63), + BookLine("told of his heroics. One", 64), + BookLine("surviving artefact from", 65) + ), + Page( + BookLine("the 4th age is a fabulous", 66), + BookLine("shield.", 67), + BookLine("This shield is believed to", 69), + BookLine("have once belonged to", 70), + BookLine("Arrav and is now indeed", 71), + BookLine("known as the Shield of", 72), + BookLine("Arrav. For over 150", 73), + BookLine("years it was the prize", 74), + BookLine("piece in the royal", 75), + BookLine("museum of Varrock.", 76) + ) + ), + PageSet( + Page( + BookLine("However, in the year 143", 55), + BookLine("of the fith age a gang of", 56), + BookLine("thieves called the Phoenix", 57), + BookLine("Gang broke into the", 58), + BookLine("museum and stole the", 59), + BookLine("shield in a daring raid. As", 60), + BookLine("a result, the current", 61), + BookLine("ruler, King Roald, put a", 62), + BookLine("1200 gold bounty (a", 63), + BookLine("massive sum of money in", 64), + BookLine("those days) on the return", 65) + ), + Page( + BookLine("of the shield, hoping that", 66), + BookLine("one of the culprits would", 67), + BookLine("betray his fellows out of", 68), + BookLine("greed.", 69) + ) + ), + PageSet( + Page( + BookLine("This tactic did not work", 55), + BookLine("however, and the thieves", 56), + BookLine("who stole the shield have", 57), + BookLine("since gone on to become", 58), + BookLine("the most powerful crime", 59), + BookLine("gang in Varrock, despite", 60), + BookLine("making an enemy of the", 61), + BookLine("Royal Family many", 62), + BookLine("years ago.", 63) + ), + Page( + BookLine("The reward for the", 66), + BookLine("return of the shield still", 67), + BookLine("stands.", 68) + ) + ) + ) + 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 (getQuestStage(player, Quests.SHIELD_OF_ARRAV) == 10) { + setQuestStage(player, Quests.SHIELD_OF_ARRAV, 20) + } + } + return true + } + } + + override fun defineListeners() { + on(Items.BOOK_757, 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/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 b3388e8d5..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; @@ -20,6 +21,8 @@ import core.game.world.map.Location; import core.game.world.update.flag.context.Animation; import core.plugin.Plugin; import core.plugin.ClassScanner; +import static core.api.ContentAPIKt.*; + /** * Handles the what lies below options. @@ -54,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": @@ -111,7 +114,7 @@ public class WLBelowPlugin extends OptionHandler { } else if (count == duration * 2) { quest.setStage(player, 40); player.getAnimator().reset(); - player.getConfigManager().set(992, 1 << 8, true); + setVarp(player, 992, 1 << 8, true); player.getDialogueInterpreter().open(5837, true, true, true); player.unlock(); return true; 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 2bef3df02..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 @@ -8,6 +8,9 @@ import core.game.node.entity.player.link.quest.Quest; import core.game.node.item.Item; import core.plugin.ClassScanner; +import static core.api.ContentAPIKt.*; +import content.data.Quests; + /** * The what lies below quest. * @author Vexa @@ -15,12 +18,6 @@ import core.plugin.ClassScanner; */ @Initializable public class WhatLiesBelow extends Quest { - - /** - * The name of the quest. - */ - public static final String NAME = "What Lies Below"; - /** * The bowl item. */ @@ -76,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. */ @@ -95,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 @@ -107,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++); + } } } @@ -159,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]; } @@ -196,7 +212,7 @@ public class WhatLiesBelow extends Quest { } else if (stage > 0 && stage < 100) { return new int[] { id, 1 }; } - player.getConfigManager().set(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/DrOnglewipDialogue.java b/Server/src/main/content/region/misthalin/wiztower/dialogue/DrOnglewipDialogue.java index cd1acad68..75b08b020 100644 --- a/Server/src/main/content/region/misthalin/wiztower/dialogue/DrOnglewipDialogue.java +++ b/Server/src/main/content/region/misthalin/wiztower/dialogue/DrOnglewipDialogue.java @@ -39,7 +39,7 @@ public final class DrOnglewipDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - interpreter.sendDialogues(player, FacialExpression.OLD_NORMAL, "Do you live here too?"); + interpreter.sendDialogues(player, FacialExpression.ASKING, "Do you live here too?"); stage = 0; return true; } @@ -52,7 +52,7 @@ public final class DrOnglewipDialogue extends DialoguePlugin { stage = 1; break; case 1: - interpreter.sendDialogues(player, FacialExpression.OLD_NORMAL, "So where's this Gnome Stronghold?"); + interpreter.sendDialogues(player, FacialExpression.ASKING, "So where's this Gnome Stronghold?"); stage = 2; break; case 2: @@ -60,7 +60,7 @@ public final class DrOnglewipDialogue extends DialoguePlugin { stage = 3; break; case 3: - interpreter.sendDialogues(player, FacialExpression.OLD_NORMAL, "I'll try and make time for it. Sounds like a nice place."); + interpreter.sendDialogues(player, FacialExpression.FRIENDLY, "I'll try and make time for it. Sounds like a nice place."); stage = 4; break; case 4: 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/DemonTauntHandler.kt b/Server/src/main/content/region/misthalin/wiztower/handlers/DemonTauntHandler.kt index ae6ba00df..6b7406406 100644 --- a/Server/src/main/content/region/misthalin/wiztower/handlers/DemonTauntHandler.kt +++ b/Server/src/main/content/region/misthalin/wiztower/handlers/DemonTauntHandler.kt @@ -17,9 +17,10 @@ class DemonTauntHandler : InteractionListener { override fun defineListeners() { on(Scenery.RAILING_37668, IntType.SCENERY, "taunt-through") { player, _ -> val demon = findLocalNPC(player, NPCs.LESSER_DEMON_82) ?: return@on true - + forceWalk(demon, player.location, "smart") + face(player, demon, 3) sendMessage(player, "You taunt the demon, making it growl.") - sendChat(demon, "Graaagh!") + sendChat(demon, "Graaaagh!") face(demon, player, 3) emote(player, Emotes.RASPBERRY) return@on true 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 6528fe639..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; } @@ -175,12 +175,6 @@ public final class WizardTowerPlugin extends OptionHandler { getProperties().setAutocastSpell((CombatSpell) SpellBook.MODERN.getSpell(8)); } - @Override - public void finalizeDeath(Entity killer) { - super.finalizeDeath(killer); - GroundItemManager.create(new Item(526), getLocation(), (Player) killer); - } - @Override public int[] getIds() { return ID; @@ -317,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!"); @@ -337,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) { @@ -1047,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) { @@ -1136,7 +1130,7 @@ public final class WizardTowerPlugin extends OptionHandler { stage = 907; break; case 907: - npc("When this Tower was burnt down the secret of", "creating runes was lost ot us for all time... except it", "wasn't. Some months ago, while searching these ruins", "for information from the old days,"); + npc("When this Tower was burnt down the secret of", "creating runes was lost to us for all time... except it", "wasn't. Some months ago, while searching these ruins", "for information from the old days,"); stage = 908; break; case 908: @@ -1364,7 +1358,7 @@ public final class WizardTowerPlugin extends OptionHandler { stage = 56; break; case 56: - npc("and if my suspicions are correct, I will let you into the", "knowledge of one of the greatest secrets this world has", "ever known! A secret so powerful that is destroyed the"); + npc("and if my suspicions are correct, I will let you into the", "knowledge of one of the greatest secrets this world has", "ever known! A secret so powerful that it destroyed the"); stage = 57; break; case 57: @@ -1548,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; @@ -1565,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) { @@ -1583,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/handlers/rcguild/RCGuildMap.java b/Server/src/main/content/region/misthalin/wiztower/handlers/rcguild/RCGuildMap.java index 36729827a..1044a72f6 100644 --- a/Server/src/main/content/region/misthalin/wiztower/handlers/rcguild/RCGuildMap.java +++ b/Server/src/main/content/region/misthalin/wiztower/handlers/rcguild/RCGuildMap.java @@ -1,5 +1,6 @@ package content.region.misthalin.wiztower.handlers.rcguild; +import core.ServerConstants; import core.cache.def.impl.SceneryDefinition; import core.game.interaction.OptionHandler; import core.game.node.Node; @@ -22,7 +23,7 @@ public class RCGuildMap extends OptionHandler { @Override public boolean handle(Player player, Node node, String option) { if(option.equals("study")){ - player.getPacketDispatch().sendMessage("A map of 2009scape."); + player.getPacketDispatch().sendMessage("A map of " + ServerConstants.SERVER_NAME + "."); } return false; } 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/burghderott/dialogue/DustyScroll.kt b/Server/src/main/content/region/morytania/burghderott/dialogue/DustyScroll.kt new file mode 100644 index 000000000..6d566a806 --- /dev/null +++ b/Server/src/main/content/region/morytania/burghderott/dialogue/DustyScroll.kt @@ -0,0 +1,36 @@ +package content.region.morytania.burghderott.dialogue + +import core.api.openDialogue +import core.game.dialogue.DialogueFile +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.tools.END_DIALOGUE +import org.rs09.consts.Items + + +class DustyScroll : InteractionListener { + // Receive during the In Aid of the Myreque quest. + companion object { + const val DUSTY_SCROLL = Items.DUSTY_SCROLL_7629 + class DustyScrollContent : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + 0 -> player!!.dialogueInterpreter.sendItemMessage(DUSTY_SCROLL, "", "You take the scroll and gingerly unravel it. It seems", "very old and the language is not easy to make out.", "You begin to see that it looks like a letter of some kind.").also { stage++ } + 1 -> player!!.dialogueInterpreter.sendItemMessage(DUSTY_SCROLL, "My dearest Lantania,", "I know not what these dark hours bring. I will come for", "thee as soon as I am able, I care no longer for the", "possessions which tie me to this wasted land and the").also { stage++ } + 2 -> player!!.dialogueInterpreter.sendItemMessage(DUSTY_SCROLL, "living which I earned from it.", "Prepare for leaving soon, I shall be there with haste,", "and with Saradomin's blessings we shall make our way out of Hallowvale westwards over the Salve towards").also { stage++ } + 3 -> player!!.dialogueInterpreter.sendItemMessage(DUSTY_SCROLL, "Misthalin and hopefully into the arms of our brethren.", "I also offer up a prayer to Saradomin that he may help", "Queen Efaritay in her hour of need and somehow fight", "these beasts back to the dead place from where they").also { stage++ } + 4 -> player!!.dialogueInterpreter.sendItemMessage(DUSTY_SCROLL, "came.", "I cry at the plight of poor Ascertes and offer up a", "prayer for their children in this darkest of hours.", "So make ready your plans and so soon as you see me,").also { stage++ } + 5 -> player!!.dialogueInterpreter.sendItemMessage(DUSTY_SCROLL, "so we shall set for Misthalin, bring only what you need.", "We shall need all good fortune and speed in this, our", "last chance at a life together.", "Yours, most faithfully,").also { stage++ } + 6 -> player!!.dialogueInterpreter.sendItemMessage(DUSTY_SCROLL, "", "Sialathin").also { stage = END_DIALOGUE } + } + } + } + } + + override fun defineListeners() { + on(DUSTY_SCROLL, IntType.ITEM, "read"){ player, _ -> + openDialogue(player, DustyScrollContent()) + return@on true + } + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/morytania/canifis/dialogue/AgilityBossDialogue.kt b/Server/src/main/content/region/morytania/canifis/dialogue/AgilityBossDialogue.kt index 6dea465eb..5b476b620 100644 --- a/Server/src/main/content/region/morytania/canifis/dialogue/AgilityBossDialogue.kt +++ b/Server/src/main/content/region/morytania/canifis/dialogue/AgilityBossDialogue.kt @@ -11,34 +11,34 @@ import core.plugin.Initializable */ @Initializable -class AgilityBossDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class AgilityBossDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC if(player.equipment.contains(4202,1)) { - player(core.game.dialogue.FacialExpression.ASKING,"How do I use the agility course?").also { stage = 0 } + player(FacialExpression.ASKING,"How do I use the agility course?").also { stage = 0 } } else { - npc(core.game.dialogue.FacialExpression.CHILD_SUSPICIOUS,"Grrr - you don't belong in here, human!").also { stage = 99 } + npc(FacialExpression.CHILD_SUSPICIOUS,"Grrr - you don't belong in here, human!").also { stage = 99 } } return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 0 -> npc(core.game.dialogue.FacialExpression.CHILD_NORMAL,"I'll throw you a stick, which you need to", + 0 -> npc(FacialExpression.CHILD_NORMAL,"I'll throw you a stick, which you need to", "fetch as quickly as possible, ", "from the area beyond the pipes.").also { stage++ } - 1 -> npc(core.game.dialogue.FacialExpression.CHILD_NORMAL,"Be wary of the deathslide - you must hang by your teeth,", + 1 -> npc(FacialExpression.CHILD_NORMAL,"Be wary of the deathslide - you must hang by your teeth,", "and if your strength is not up to the job you will", "fall into a pit of spikes. Also, I would advise not", "carrying too much extra weight.").also { stage++ } - 2 -> npc(core.game.dialogue.FacialExpression.CHILD_NORMAL,"Bring the stick back to the werewolf waiting at", + 2 -> npc(FacialExpression.CHILD_NORMAL,"Bring the stick back to the werewolf waiting at", "the end of the death slide to get your agility bonus.").also { stage++ } - 3 ->npc(core.game.dialogue.FacialExpression.CHILD_NORMAL,"I will throw your stick as soon as you jump onto the", + 3 ->npc(FacialExpression.CHILD_NORMAL,"I will throw your stick as soon as you jump onto the", "first stone.").also { stage = 99 } 99 -> end() @@ -46,7 +46,7 @@ class AgilityBossDialogue(player: Player? = null) : core.game.dialogue.DialogueP return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return AgilityBossDialogue(player) } diff --git a/Server/src/main/content/region/morytania/canifis/dialogue/BarkerDialogue.java b/Server/src/main/content/region/morytania/canifis/dialogue/BarkerDialogue.java index a235b9430..810c0d3bb 100644 --- a/Server/src/main/content/region/morytania/canifis/dialogue/BarkerDialogue.java +++ b/Server/src/main/content/region/morytania/canifis/dialogue/BarkerDialogue.java @@ -39,7 +39,7 @@ public final class BarkerDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Hello."); + interpreter.sendDialogues(player, FacialExpression.HAPPY, "Hello."); stage = 0; return true; } @@ -48,7 +48,7 @@ public final class BarkerDialogue extends DialoguePlugin { public boolean handle(int interfaceId, int buttonId) { switch (stage) { case 0: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "You are looking for clothes, yes? You look at my", "products! I have very many nice clothes, yes?"); + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "You are looking for clothes, yes? You look at my", "products! I have very many nice clothes, yes?"); stage = 1; break; case 1: @@ -58,11 +58,11 @@ public final class BarkerDialogue extends DialoguePlugin { case 2: switch (buttonId) { case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Yes, please."); + interpreter.sendDialogues(player, FacialExpression.FRIENDLY, "Yes, please."); stage = 10; break; case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "No thanks."); + interpreter.sendDialogues(player, FacialExpression.FRIENDLY, "No thanks."); stage = 15; break; } 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.java b/Server/src/main/content/region/morytania/canifis/dialogue/RoavarDialogue.java deleted file mode 100644 index 82644c4e5..000000000 --- a/Server/src/main/content/region/morytania/canifis/dialogue/RoavarDialogue.java +++ /dev/null @@ -1,148 +0,0 @@ -package content.region.morytania.canifis.dialogue; - -import core.plugin.Initializable; -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; - -/** - * Handles the RoavarDialogue dialogue. - * @author 'Vexia - */ -@Initializable -public class RoavarDialogue extends DialoguePlugin { - - public RoavarDialogue() { - - } - - public RoavarDialogue(Player player) { - super(player); - } - - @Override - public DialoguePlugin newInstance(Player player) { - - return new RoavarDialogue(player); - } - - @Override - public boolean open(Object... args) { - npc = (NPC) args[0]; - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Hello there!"); - stage = 0; - return true; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (stage) { - case 0: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Greeting traveller. Welcome to 'The Hair Of The Dog'", "Tavern. What can I do you for?"); - stage = 1; - break; - case 1: - interpreter.sendOptions("Select an Option", "Can I buy a beer?", "Can I hear some gossipp?", "Nothing thanks."); - stage = 2; - break; - case 2: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Can I buy a beer?"); - stage = 10; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Can I hear some gossip?"); - stage = 20; - break; - case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Nothing thanks."); - stage = 40; - break; - } - break; - case 10: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Well that's my speciality! The local brew's named", "'Moonlight Mead' and will set you back 5 gold.", "Waddya say? Fancy a pint?"); - stage = 11; - break; - case 11: - interpreter.sendOptions("Select an Option", "Yes please.", "Actually, no thanks."); - stage = 12; - break; - case 12: - switch (buttonId) { - case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Yes please."); - stage = 15; - break; - case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Actually, no thanks."); - stage = 14; - break; - - } - break; - case 14: - end(); - break; - case 15: - if (player.getInventory().contains(995, 5)) { - if (player.getInventory().remove(new Item(995, 5))) { - player.getInventory().add(new Item(2955, 1)); - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Here ya go pal. Enjoy!"); - stage = 16; - } - } else { - end(); - player.getPacketDispatch().sendMessage("You need 5 gold coins to buy a pint of beer."); - } - break; - case 16: - end(); - break; - case 20: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "I am not one to gossip!"); - stage = 21; - break; - case 21: - end(); - break; - case 30: - if (player.getInventory().contains(2963, 1)) { - interpreter.sendDialogues(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!"); - stage = 31; - } else { - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Ah, well I do have one lying around.", "I suppose you could have it."); - stage = 42; - } - break; - case 31: - end(); - break; - case 40: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "...I don't know why you talked to me if you don't want", "anything then..."); - stage = 41; - break; - case 41: - end(); - break; - case 42: - if (player.getInventory().freeSlots() < 1) { - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Oh, nevermind. It seems your backpack is full."); - } else { - interpreter.sendDialogue("The bartender hands you a silver sickle."); - player.getInventory().add(new Item(2963, 1)); - } - stage = 31; - break; - } - return true; - } - - @Override - public int[] getIds() { - return new int[] { 1042 }; - } -} diff --git a/Server/src/main/content/region/morytania/canifis/dialogue/RoavarDialogue.kt b/Server/src/main/content/region/morytania/canifis/dialogue/RoavarDialogue.kt new file mode 100644 index 000000000..edb3fbdda --- /dev/null +++ b/Server/src/main/content/region/morytania/canifis/dialogue/RoavarDialogue.kt @@ -0,0 +1,118 @@ +package content.region.morytania.canifis.dialogue + +import content.region.morytania.quest.creatureoffenkenstrain.RoavarDialogueFile +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 org.rs09.consts.Items +import org.rs09.consts.NPCs +import content.data.Quests + +/** + * Roavar dialogue. + */ +@Initializable +class RoavarDialogue (player: Player? = null) : DialoguePlugin(player) { + + override fun open(vararg args: Any): Boolean { + npc = args[0] as NPC + player(FacialExpression.HALF_GUILTY, "Hello there!") + stage = 0 + return true + } + + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + when (stage) { + 0 -> { + npc(FacialExpression.HALF_GUILTY, "Greeting traveller. Welcome to 'The Hair Of The Dog'", "Tavern. What can I do you for?") + stage = 1 + } + + 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(Quests.CREATURE_OF_FENKENSTRAIN).getStage(player) == 2, false), + Topic(FacialExpression.HALF_GUILTY, "Nothing thanks.", 40, false) + ) + + 10 -> { + npc(FacialExpression.HALF_GUILTY, "Well that's my speciality! The local brew's named", "'Moonlight Mead' and will set you back 5 gold.", "Waddya say? Fancy a pint?") + stage = 11 + } + + 11 -> { + interpreter.sendOptions("Select an Option", "Yes please.", "Actually, no thanks.") + stage = 12 + } + + 12 -> when (buttonId) { + 1 -> { + player(FacialExpression.HALF_GUILTY, "Yes please.") + stage = 15 + } + + 2 -> { + player(FacialExpression.HALF_GUILTY, "Actually, no thanks.") + stage = 14 + } + } + 14 -> end() + 15 -> if (inInventory(player, Items.COINS_995, 5)) { + if (removeItem(player, Item(Items.COINS_995, 5))) { + addItemOrDrop(player, Items.MOONLIGHT_MEAD_2955) + npc(FacialExpression.HALF_GUILTY, "Here ya go pal. Enjoy!") + stage = 16 + } + } else { + end() + sendMessage(player, "You need 5 gold coins to buy a pint of beer.") + } + + 16 -> end() + 20 -> { + npc(FacialExpression.HALF_GUILTY, "I am not one to gossip!") + stage = 21 + } + + 21 -> end() + 30 -> stage = if (inInventory(player, 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 { + npc(FacialExpression.HALF_GUILTY, "Ah, well I do have one lying around.", "I suppose you could have it.") + 42 + } + + 31 -> end() + 40 -> { + npc(FacialExpression.HALF_GUILTY, "...I don't know why you talked to me if you don't want", "anything then...") + stage = 41 + } + + 41 -> end() + 42 -> { + if (freeSlots(player) < 1) { + 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) + } + stage = 31 + } + } + return true + } + + override fun newInstance(player: Player): DialoguePlugin { + return RoavarDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.ROAVAR_1042) + } +} diff --git a/Server/src/main/content/region/morytania/canifis/dialogue/RufusDialogue.java b/Server/src/main/content/region/morytania/canifis/dialogue/RufusDialogue.java index 10aec5a53..abe17c032 100644 --- a/Server/src/main/content/region/morytania/canifis/dialogue/RufusDialogue.java +++ b/Server/src/main/content/region/morytania/canifis/dialogue/RufusDialogue.java @@ -30,7 +30,7 @@ public class RufusDialogue extends DialoguePlugin { @Override public boolean open(Object... args) { npc = (NPC) args[0]; - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Hi!"); + interpreter.sendDialogues(player, FacialExpression.HAPPY, "Hi!"); stage = 0; return true; } @@ -39,7 +39,7 @@ public class RufusDialogue extends DialoguePlugin { public boolean handle(int interfaceId, int buttonId) { switch (stage) { case 0: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Grreeting frrriend! Welcome to my worrrld famous", "food emporrium! All my meats are so frrresh you'd", "swear you killed them yourrrself!"); + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Grreeting frrriend! Welcome to my worrrld famous", "food emporrium! All my meats are so frrresh you'd", "swear you killed them yourrrself!"); stage = 1; break; case 1: @@ -49,21 +49,21 @@ public class RufusDialogue extends DialoguePlugin { case 2: switch (buttonId) { case 1: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Why do you only sell meats?"); + interpreter.sendDialogues(player, FacialExpression.ASKING, "Why do you only sell meats?"); stage = 10; break; case 2: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Do you sell cooked food?"); + interpreter.sendDialogues(player, FacialExpression.ASKING, "Do you sell cooked food?"); stage = 20; break; case 3: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Can I buy some food?"); + interpreter.sendDialogues(player, FacialExpression.FRIENDLY, "Can I buy some food?"); stage = 30; break; } break; case 10: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "What? Why, what else would you want to eat? What", "kind of lycanthrrope are you anyway?"); + interpreter.sendDialogues(npc, FacialExpression.DISGUSTED, "What? Why, what else would you want to eat? What", "kind of lycanthrrope are you anyway?"); stage = 11; break; case 11: @@ -71,25 +71,25 @@ public class RufusDialogue extends DialoguePlugin { stage = 12; break; case 12: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Vegetarrrian...?"); + interpreter.sendDialogues(npc, FacialExpression.EXTREMELY_SHOCKED, "Vegetarrrian...?"); stage = 13; break; case 13: - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, "Never mind."); + interpreter.sendDialogues(player, FacialExpression.SUSPICIOUS, "Never mind."); stage = 14; break; case 14: end(); break; case 20: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Cooked food? Who would want that? You lose all the", "flavourrr of the meat when you can't taste the blood!"); + interpreter.sendDialogues(npc, FacialExpression.EVIL_LAUGH, "Cooked food? Who would want that? You lose all the", "flavourrr of the meat when you can't taste the blood!"); stage = 21; break; case 21: end(); break; case 30: - interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Cerrrtainly!"); + interpreter.sendDialogues(npc, FacialExpression.HAPPY, "Cerrrtainly!"); stage = 31; break; case 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 c68c38d20..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.HALF_GUILTY, "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.HALF_GUILTY, "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, null, "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, null, "Yes please."); - stage = 210; - break; - case 2: - interpreter.sendDialogues(player, null, "No thanks."); - stage = 200; - break; - } - break; - case 210: - end(); - TanningProduct.open(player, 2824); - break; - case 200: - interpreter.sendDialogues(npc, null, "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/dialogue/AfflictedDialogue.kt b/Server/src/main/content/region/morytania/dialogue/AfflictedDialogue.kt index c1d9648fc..0f3cfd062 100644 --- a/Server/src/main/content/region/morytania/dialogue/AfflictedDialogue.kt +++ b/Server/src/main/content/region/morytania/dialogue/AfflictedDialogue.kt @@ -13,13 +13,13 @@ import core.tools.RandomFunction */ @Initializable -class AfflictedDialogue : core.game.dialogue.DialoguePlugin { +class AfflictedDialogue : DialoguePlugin { private val chats = arrayOf("ughugh", "knows'is", "knows'is", "nots", "pirsl", "wot's", "zurgle", "gurghl", "mee's", "seysyi", "sfriess", "says") override fun open(vararg args: Any): Boolean { npc = args[0] as NPC chats.shuffle() - interpreter.sendDialogues( npc, core.game.dialogue.FacialExpression.ASKING, chats.copyOfRange(0, RandomFunction.random(1, 6)).contentToString() + interpreter.sendDialogues( npc, FacialExpression.ASKING, chats.copyOfRange(0, RandomFunction.random(1, 6)).contentToString() .replace("[", "").replace("]", "").replace(",", "")) return true } @@ -36,7 +36,7 @@ class AfflictedDialogue : core.game.dialogue.DialoguePlugin { return true } - override fun newInstance(player: Player): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player): DialoguePlugin { return AfflictedDialogue(player) } } \ No newline at end of file diff --git a/Server/src/main/content/region/morytania/handlers/MortMyreGhastNPC.kt b/Server/src/main/content/region/morytania/handlers/MortMyreGhastNPC.kt index 88a891810..6748596f5 100644 --- a/Server/src/main/content/region/morytania/handlers/MortMyreGhastNPC.kt +++ b/Server/src/main/content/region/morytania/handlers/MortMyreGhastNPC.kt @@ -20,6 +20,7 @@ import kotlinx.coroutines.launch import org.rs09.consts.Items import org.rs09.consts.NPCs import content.region.morytania.quest.naturespirit.NSUtils +import org.rs09.consts.Sounds @Initializable class MortMyreGhastNPC : AbstractNPC { @@ -67,7 +68,7 @@ class MortMyreGhastNPC : AbstractNPC { for(i in player.inventory.toArray()){ if(i == null) continue val consumable = Consumables.getConsumableById(i.id) - if(consumable != null && consumable is Food) { + if(consumable != null && consumable.consumable is Food) { hasFood = true removeItem(player, i, Container.INVENTORY) addItem(player, Items.ROTTEN_FOOD_2959) @@ -75,6 +76,7 @@ class MortMyreGhastNPC : AbstractNPC { break } } + playAudio(player, Sounds.GHAST_ATTACK_433) if(!hasFood && RandomFunction.roll(3)) { sendMessage(player, "An attacking Ghast just misses you.") diff --git a/Server/src/main/content/region/morytania/handlers/MorytaniaArea.kt b/Server/src/main/content/region/morytania/handlers/MorytaniaArea.kt index 938c82d8c..5edfa55f7 100644 --- a/Server/src/main/content/region/morytania/handlers/MorytaniaArea.kt +++ b/Server/src/main/content/region/morytania/handlers/MorytaniaArea.kt @@ -11,14 +11,21 @@ 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 { - return arrayOf(ZoneBorders(3426, 3191, 3715, 3588)) + return arrayOf( + ZoneBorders(3426, 3191, 3715, 3588), //Morytania overworld + ZoneBorders(3520, 9856, 3583, 9919) //Werewolf agility course + ) } override fun areaEnter(entity: Entity) { - if (entity is Player && entity !is AIPlayer && !isQuestComplete(entity, "Priest in Peril")) { + if (entity is Player && entity !is AIPlayer && ( + !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 e66cb4491..23d8093fe 100644 --- a/Server/src/main/content/region/morytania/phas/dialogue/GhostDiscipleDialogue.java +++ b/Server/src/main/content/region/morytania/phas/dialogue/GhostDiscipleDialogue.java @@ -4,11 +4,13 @@ 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.plugin.Initializable; /** * Handles the dialogue used for a ghost disciple. * @author Vexia */ +@Initializable public final class GhostDiscipleDialogue extends DialoguePlugin { /** diff --git a/Server/src/main/content/region/morytania/phas/dialogue/GhostInkeeperDialogue.java b/Server/src/main/content/region/morytania/phas/dialogue/GhostInkeeperDialogue.java index 8a17783b4..107cc9800 100644 --- a/Server/src/main/content/region/morytania/phas/dialogue/GhostInkeeperDialogue.java +++ b/Server/src/main/content/region/morytania/phas/dialogue/GhostInkeeperDialogue.java @@ -3,11 +3,13 @@ package content.region.morytania.phas.dialogue; import core.game.dialogue.DialoguePlugin; import content.region.morytania.phas.handlers.PhasmatysZone; import core.game.node.entity.player.Player; +import core.plugin.Initializable; /** * Handles the ghost inkeeper dialogue. * @author Vexia */ +@Initializable public class GhostInkeeperDialogue extends DialoguePlugin { /** @@ -105,9 +107,7 @@ public class GhostInkeeperDialogue extends DialoguePlugin { stage++; break; case 31: - player("Nope, you are right. I am very busy!");// .. I dont want - // to do this - // dialogue. + player("Nope, you are right. I am very busy!");// .. Might need future revision stage++; break; case 32: diff --git a/Server/src/main/content/region/morytania/phas/dialogue/GhostSailorDialogue.java b/Server/src/main/content/region/morytania/phas/dialogue/GhostSailorDialogue.java index f911d8c2d..55a1f6e59 100644 --- a/Server/src/main/content/region/morytania/phas/dialogue/GhostSailorDialogue.java +++ b/Server/src/main/content/region/morytania/phas/dialogue/GhostSailorDialogue.java @@ -5,12 +5,14 @@ import core.game.dialogue.FacialExpression; import content.region.morytania.phas.handlers.PhasmatysZone; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; +import core.plugin.Initializable; /** * Represents the ghost sailor dialogue plugin. * @author 'Vexia * @version 1.0 */ +@Initializable public final class GhostSailorDialogue extends DialoguePlugin { /** @@ -40,10 +42,10 @@ public final class GhostSailorDialogue extends DialoguePlugin { npc = (NPC) args[0]; if (PhasmatysZone.hasAmulet(player)) { player("Hi there. Why do you still bother having ships here? I", "mean - you're dead, what use are they to you?"); - stage = 10; + stage = 1; } else { interpreter.sendDialogues(npc, FacialExpression.HALF_GUILTY, "Woooo wooo wooooo woooo"); - stage = 0; + stage = 10; } return true; } @@ -51,26 +53,26 @@ public final class GhostSailorDialogue extends DialoguePlugin { @Override public boolean handle(int interfaceId, int buttonId) { switch (stage) { - case 0: - interpreter.sendDialogue("You cannot understand the ghost."); - stage = 1; - break; case 1: - end(); - break; - case 10: npc("We keep ships because we still need trade in", "Phasmatys. Every trader that comes to Phastmatys is", "made to worship the Ectofuntus, so that the Ectopower", "doesn't run out."); stage++; break; - case 11: + case 2: player("So, without traders to worship in the Temple you're", "history right?"); stage++; break; - case 12: + case 3: npc("Aye, matey."); stage++; break; - case 13: + case 4: + end(); + break; + case 10: + interpreter.sendDialogue("You cannot understand the ghost."); + stage = 11; + break; + case 11: end(); break; } diff --git a/Server/src/main/content/region/morytania/phas/dialogue/GhostShopKeeperDialogue.java b/Server/src/main/content/region/morytania/phas/dialogue/GhostShopKeeperDialogue.java new file mode 100644 index 000000000..da88f6676 --- /dev/null +++ b/Server/src/main/content/region/morytania/phas/dialogue/GhostShopKeeperDialogue.java @@ -0,0 +1,88 @@ +package content.region.morytania.phas.dialogue; + +import content.region.morytania.phas.handlers.PhasmatysZone; +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 Ghost shop keeper dialogue in Port Phasmatys in Mortaniya. + */ +@Initializable +public final class GhostShopKeeperDialogue extends DialoguePlugin { + + /** + * Constructs a new {@code GhostShopKeeperDialogue} {@code Object}. + */ + public GhostShopKeeperDialogue() { + /** + * empty. + */ + } + + /** + * Constructs a new {@code GhostShopKeeperDialogue} {@code Object}. + * @param player the player. + */ + public GhostShopKeeperDialogue(Player player) { + super(player); + } + + @Override + public DialoguePlugin newInstance(Player player) { + return new GhostShopKeeperDialogue(player); + } + + @Override + public boolean open(Object... args) { + npc = (NPC) args[0]; + if (PhasmatysZone.hasAmulet(player)) { + interpreter.sendDialogues(npc, FacialExpression.FRIENDLY, "Can I help you at all?"); + stage = 0; + } else { + interpreter.sendDialogues(npc, FacialExpression.FRIENDLY, "Woooo wooo wooooo woooo"); + stage = 10; + } + return true; + } + + @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 = 3; + break; + case 3: + end(); + break; + } + break; + case 10: + interpreter.sendDialogue( "You cannot understand the ghost."); + stage = 11; + break; + case 11: + end(); + break; + } + return true; + } + + @Override + public int[] getIds() { + return new int[] { 1699 }; + } +} diff --git a/Server/src/main/content/region/morytania/phas/dialogue/GhostVillagerDialogue.java b/Server/src/main/content/region/morytania/phas/dialogue/GhostVillagerDialogue.java index 18a2cacc9..759c66f1b 100644 --- a/Server/src/main/content/region/morytania/phas/dialogue/GhostVillagerDialogue.java +++ b/Server/src/main/content/region/morytania/phas/dialogue/GhostVillagerDialogue.java @@ -3,12 +3,15 @@ package content.region.morytania.phas.dialogue; import core.game.dialogue.DialoguePlugin; import content.region.morytania.phas.handlers.PhasmatysZone; import core.game.node.entity.player.Player; +import core.plugin.Initializable; import core.tools.RandomFunction; /** * Handles the ghost villager dialogue. * @author Vexia */ + +@Initializable public final class GhostVillagerDialogue extends DialoguePlugin { /** @@ -46,6 +49,7 @@ public final class GhostVillagerDialogue extends DialoguePlugin { npc("Woooo wooo wooooo woooo"); stage = 10; } + return true; } diff --git a/Server/src/main/content/region/morytania/phas/handlers/BoneGrinderListener.kt b/Server/src/main/content/region/morytania/phas/handlers/BoneGrinderListener.kt index 205572b05..9f333e5b2 100644 --- a/Server/src/main/content/region/morytania/phas/handlers/BoneGrinderListener.kt +++ b/Server/src/main/content/region/morytania/phas/handlers/BoneGrinderListener.kt @@ -1,22 +1,23 @@ package content.region.morytania.phas.handlers -import core.api.Container -import core.api.* import content.global.skill.prayer.Bones +import core.api.* +import core.game.interaction.IntType +import core.game.interaction.InteractionListener import core.game.node.entity.impl.PulseType import core.game.node.entity.player.Player import core.game.node.item.Item import core.game.system.task.Pulse +import core.game.world.GameWorld.Pulser import core.game.world.map.Location import core.game.world.update.flag.context.Animation import org.rs09.consts.Items -import core.game.interaction.InteractionListener -import core.game.interaction.IntType -import core.game.world.GameWorld.Pulser +import org.rs09.consts.Scenery +import org.rs09.consts.Sounds -private const val LOADER = 11162 -private const val BONE_GRINDER = 11163 -private const val BIN = 11164 +private const val LOADER = Scenery.LOADER_11162 +private const val BONE_GRINDER = Scenery.BONE_GRINDER_11163 +private const val BIN = Scenery.BIN_11164 private const val LOADED_BONE_KEY = "/save:bonegrinder-bones" private const val BONE_HOPPER_KEY = "/save:bonegrinder-hopper" @@ -72,8 +73,12 @@ class BoneGrinderListener : InteractionListener { fun handleFill(player: Player): Boolean{ val bone = getBone(player) - if(bone == null){ - sendMessage(player,"You have no bones to grind.") + if((bone == null) || (bone.bonemealId == null)) { + if (inInventory(player, Items.MARINATED_J_BONES_3130) || inInventory(player, Items.MARINATED_J_BONES_3133)) { + sendDialogue(player, "These bones could break the bone grinder. Perhaps I should find some different bones.") + } else { + sendMessage(player, "You have no bones to grind.") + } return true } if(getAttribute(player, BONE_HOPPER_KEY,false)){ @@ -92,6 +97,7 @@ class BoneGrinderListener : InteractionListener { 0 -> { lock(player, FILL_ANIM.duration) animate(player, FILL_ANIM) + playAudio(player, Sounds.FILL_GRINDER_1133) } FILL_ANIM.duration -> { sendMessage(player,"You fill the hopper with bones.") @@ -173,6 +179,7 @@ class BoneGrinderListener : InteractionListener { lock(player, WIND_ANIM.duration) animate(player, WIND_ANIM) sendMessage(player,"You wind the handle.") + playAudio(player, Sounds.GRINDER_GRINDING_1131) } WIND_ANIM.duration -> { sendMessage(player,"The bonemeal falls into the bin.") @@ -233,10 +240,11 @@ class BoneGrinderListener : InteractionListener { 0 -> { face(player,Location(3658, 3525, 1)) animate(player, SCOOP_ANIM) + playAudio(player, Sounds.GRINDER_EMPTY_1136) } SCOOP_ANIM.duration -> { if(removeItem(player,Item(Items.EMPTY_POT_1931),Container.INVENTORY)){ - addItem(player,bone.boneMeal.id) + addItem(player, bone.bonemealId!!) } return true } diff --git a/Server/src/main/content/region/morytania/phas/handlers/EctoplasmFillPlugin.java b/Server/src/main/content/region/morytania/phas/handlers/EctoplasmFillPlugin.java index 7b69614ae..46000fc0d 100644 --- a/Server/src/main/content/region/morytania/phas/handlers/EctoplasmFillPlugin.java +++ b/Server/src/main/content/region/morytania/phas/handlers/EctoplasmFillPlugin.java @@ -7,6 +7,9 @@ import core.game.node.item.Item; import core.game.system.task.Pulse; import core.game.world.update.flag.context.Animation; import core.plugin.Plugin; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.playAudio; /** * Handles the ectoplasm filling. @@ -38,7 +41,7 @@ public class EctoplasmFillPlugin extends UseWithHandler { public boolean handle(NodeUsageEvent event) { final Player player = event.getPlayer(); player.animate(Animation.create(4471)); - player.getAudioManager().send(1132); + playAudio(player, Sounds.FILL_ECTOPLASM_1132); player.getPacketDispatch().sendMessage("You fill the bucket with ectoplasm."); player.getPulseManager().run(new Pulse(3, player) { @Override @@ -47,7 +50,7 @@ public class EctoplasmFillPlugin extends UseWithHandler { player.getInventory().add(new Item(4286)); if (player.getInventory().contains(1925, 1)) { player.animate(Animation.create(4471), 1); - player.getAudioManager().send(1132); + playAudio(player, Sounds.FILL_ECTOPLASM_1132); return false; } } diff --git a/Server/src/main/content/region/morytania/phas/handlers/PhasmatysZone.java b/Server/src/main/content/region/morytania/phas/handlers/PhasmatysZone.java index e22b7c3f6..3cf16842e 100644 --- a/Server/src/main/content/region/morytania/phas/handlers/PhasmatysZone.java +++ b/Server/src/main/content/region/morytania/phas/handlers/PhasmatysZone.java @@ -1,9 +1,8 @@ package content.region.morytania.phas.handlers; -import static core.api.ContentAPIKt.*; - +import content.global.skill.agility.AgilityHandler; import content.global.skill.prayer.Bones; -import content.region.morytania.phas.dialogue.*; +import content.region.morytania.phas.dialogue.NecrovarusDialogue; import core.game.global.action.ClimbActionHandler; import core.game.interaction.Option; import core.game.node.Node; @@ -11,24 +10,26 @@ 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 content.global.skill.agility.AgilityHandler; 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.map.Direction; import core.game.world.map.Location; 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.update.flag.context.Animation; +import core.plugin.ClassScanner; import core.plugin.Initializable; import core.plugin.Plugin; import org.rs09.consts.NPCs; -import core.plugin.ClassScanner; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.playAudio; +import static core.api.ContentAPIKt.sendMessage; /** - * Handles the phasmatyz zone area. + * Handles the phasmatys zone area. * * @author Vexia */ @@ -38,8 +39,6 @@ public final class PhasmatysZone extends MapZone implements Plugin { Bones[] bones; Player player; Bones bone; - boolean notFirst; - /** * Constructs a new {@code PhasmatysZone} {@code Object}. @@ -53,15 +52,10 @@ public final class PhasmatysZone extends MapZone implements Plugin { ZoneBuilder.configure(this); ClassScanner.definePlugin(new GravingasNPC()); ClassScanner.definePlugin(new NecrovarusDialogue()); - ClassScanner.definePlugin(new GhostSailorDialogue()); ClassScanner.definePlugin(new EctoplasmFillPlugin()); - ClassScanner.definePlugin(new GhostDiscipleDialogue()); - ClassScanner.definePlugin(new GhostVillagerDialogue()); - ClassScanner.definePlugin(new GhostInkeeperDialogue()); return this; } - @Override public boolean interact(Entity e, Node target, Option option) { if (e.isPlayer()) { @@ -76,7 +70,8 @@ public final class PhasmatysZone extends MapZone implements Plugin { } if ((npc.getName().toLowerCase().contains("ghost") || npc.getName().equalsIgnoreCase("velorina") || npc.getName().contains("husband")) && !hasAmulet(player)) { - player.getDialogueInterpreter().open("ghosty dialogue", target); + //player.getDialogueInterpreter().open("ghosty dialogue", target); // Commented for ghost npcs to work from their individual plugin files + player.getDialogueInterpreter().open(target.getId() , target); return true; } } @@ -134,64 +129,6 @@ public final class PhasmatysZone extends MapZone implements Plugin { return super.interact(e, target, option); } - public Pulse emptyPulse = new Pulse(5) { - //the bin - Scenery emptyobj = new Scenery(11164, 3658, 3525, 1); - - @Override - public boolean pulse() { - //player.getProperties().setTeleportLocation(new Location(3658,3524,1)); - player.faceLocation(new Location(3658, 3525, 1)); - empty(player, emptyobj); - player.getWalkingQueue().reset(); - player.getWalkingQueue().addPath(3660, 3524, true); - player.setAttribute("bgfirst?", false); - player.getPulseManager().run(fillPulse); - return true; - } - }; - public Pulse windPulse = new Pulse(5) { - //the crank - Scenery windobj = new Scenery(11163, 3659, 3525, 1); - - @Override - public boolean pulse() { - //player.getProperties().setTeleportLocation(new Location(3659,3524,1)); - player.faceLocation(new Location(3659, 3525, 1)); - wind(player, windobj); - player.getWalkingQueue().reset(); - player.getWalkingQueue().addPath(3658, 3524, true); - player.getPulseManager().run(emptyPulse); - return true; - } - }; - public Pulse fillPulse = new Pulse(5) { - //the hopper - @Override - public boolean pulse() { - if (hasBoneInInventory(player)) { - bone = getBone(player); - } else { - player.debug("No bones in inventory, or bones not in bones array."); - player.setAttribute("bgfirst?", true); - return true; - } - - player.faceLocation(new Location(3660, 3525, 1)); - if (player.getInventory().remove(new Item(bone.getItemId()))) { - player.getLocks().lockInteractions(2); - player.debug("Using bone " + bone.getItemId() + " on grinder.. bone info:" + b); - player.animate(Animation.create(1649)); - player.getConfigManager().set(408, bone.getConfigValue(true), true); - player.sendMessage("You put some bones in the grinder's hopper.", 1); - player.getWalkingQueue().reset(); - player.getWalkingQueue().addPath(3659, 3524, true); - player.getPulseManager().run(windPulse); - } - return true; - } - }; - /** * Checks if the player has the amulet equipped. * @@ -202,108 +139,6 @@ public final class PhasmatysZone extends MapZone implements Plugin { return player.getEquipment().contains(552, 1); } - /** - * Checks the status of a grinder. - * - * @param player the player. - * @param object the object. - */ - private void checkStatus(Player player, Scenery object) { - String status = "The grinder is empty."; - if (hasBones(player, object, true)) { - status = "There are some bones in the hopper."; - } else if (hasBones(player, object, false)) { - status = "There are some crushed bones in the bin."; - } - player.sendMessage(status); - } - - /** - * Emptys the grinder. - * - * @param player the player. - * @param object the object. - */ - private void empty(Player player, Scenery object) { - if (hasBones(player, object, true)) { - player.sendMessage("You need to wind the handle to grind the bones."); - return; - } - if (!hasBones(player, object, false)) { - player.sendMessage("The grinder is already empty."); - return; - } - if (!player.getInventory().contains(1931, 1)) { - player.sendMessage("You need an empty pot to put the crushed bones into."); - return; - } - final Bones bone = Bones.forConfigValue(player.getConfigManager().get(408), false); - player.getLocks().lockInteractions(2); - player.animate(Animation.create(1650)); - player.getConfigManager().set(408, 0, true); - player.sendMessage("You fill a pot with crushed bones."); - player.getInventory().replace(bone.getBoneMeal(), player.getInventory().getSlot(new Item(1931))); - } - - /** - * Windws the grinder. - * - * @param player the player. - * @param object the object. - */ - private void wind(Player player, Scenery object) { - final Bones bone = Bones.forConfigValue(player.getConfigManager().get(408), true); - player.getLocks().lockInteractions(3); - player.animate(Animation.create(1648)); - player.sendMessage("You wind the grinder handle."); - if (hasBones(player, object, true)) { - player.getConfigManager().set(408, bone.getConfigValue(false), true); - player.sendMessage("Some crushed bones pour into the bin.", 3); - } - } - - /** - * Checks if bones are in the hopper. - * - * @param player the player. - * @param inHopper in the hopper or in the bin. - * @param object the object. - * @return {@code True} if so. - */ - public static boolean hasBones(Player player, Scenery object, boolean inHopper) { - int value = player.getConfigManager().get(408); - for (Bones bone : Bones.values()) { - if (bone.getConfigValue(inHopper) == value) { - return true; - } - } - return false; - } - - public static boolean hasBoneInInventory(Player player) { - Bones[] bones = Bones.values(); - for (int c = 0; c < bones.length; c++) { - Bones bone = bones[c]; - int boneId = bone.getItemId(); - if (player.getInventory().containsItem(new Item(boneId))) { - return true; - } - } - return false; - } - - public static Bones getBone(Player player) { - Bones[] bones = Bones.values(); - for (int b = 0; b < bones.length; b++) { - Bones bone = bones[b]; - int boneId = bone.getItemId(); - if (player.getInventory().containsItem(new Item(boneId))) { - return bone; - } - } - return null; - } - /** * Worships the ectofuntus. * @@ -333,9 +168,10 @@ public final class PhasmatysZone extends MapZone implements Plugin { player.getDialogueInterpreter().sendDialogue("You need ectoplasm to put into the Ectofuntus."); return; } - if (player.getInventory().remove(bone.getBoneMeal(), new Item(4286, 1))) { + if (player.getInventory().remove(new Item(bone.getBonemealId()), new Item(4286, 1))) { player.lock(1); player.animate(Animation.create(1651)); + playAudio(player, Sounds.PRAYER_BOOST_2671); player.getInventory().add(new Item(1925), new Item(1931)); player.getSkills().addExperience(Skills.PRAYER, bone.getExperience() * 4, true); player.sendMessage("You put some ectoplasm and bonemeal into the Ectofuntus, and worship it."); @@ -351,11 +187,14 @@ public final class PhasmatysZone extends MapZone implements Plugin { */ private void handleEnergyBarrier(Player player, Scenery object) { boolean force = object.getLocation().equals(3659, 3508, 0) && player.getLocation().getY() < 3508 || object.getLocation().equals(3652, 3485, 0) && player.getLocation().getX() > 3652; - if (!force && !player.getInventory().contains(4278, 2)) { + //Snowscape modification: allow paying from bank or inventory. Also notify player if they don't have enough, since the dialogue doesn't seem to trigger + if (!force && !(player.getInventory().contains(4278, 2) || player.getBank().contains(4278, 2))) { + player.sendMessage("You need 2 ecto-tokens in your inventory or bank to pay the entry fee."); player.getDialogueInterpreter().open(1706); return; } - if (force || player.getInventory().remove(new Item(4278, 2))) { + if (force || player.getInventory().remove(new Item(4278, 2)) || player.getBank().remove(new Item(4278, 2))) { + if (!force) player.sendMessage("You pay the ghost guard 2 ecto-tokens to enter the barrier."); final Direction direction = Direction.getLogicalDirection(player.getLocation(), object.getLocation()); Location end = player.getLocation().transform(direction, 2); if (player.getLocation().getY() >= 3508) { diff --git a/Server/src/main/content/region/morytania/quest/creatureoffenkenstrain/BookcaseDialogueFile.kt b/Server/src/main/content/region/morytania/quest/creatureoffenkenstrain/BookcaseDialogueFile.kt new file mode 100644 index 000000000..f188633d9 --- /dev/null +++ b/Server/src/main/content/region/morytania/quest/creatureoffenkenstrain/BookcaseDialogueFile.kt @@ -0,0 +1,106 @@ +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 +import content.global.handlers.iface.PageSet +import core.api.* +import core.game.dialogue.DialogueFile +import core.game.node.entity.player.Player +import core.tools.END_DIALOGUE +import core.tools.START_DIALOGUE +import org.rs09.consts.Items + +class BookcaseWestDialogueFile : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + START_DIALOGUE -> sendDialogueOptions(player!!, "Which book would you like to read?", "1001 Ways To Eat Fried Gizzards", "Practical Gardening For The Headless", "Human Taxidermy for Nincompoops", "The Joy of Gravedigging").also { + setComponentVisibility(player!!, 232, 9, false) + setComponentVisibility(player!!, 232, 8, true) + stage++ + } + 1 -> when (buttonID) { + 1 -> sendDialogue(player!!, "This book leaves you contemplating vegetarianism.").also { stage = END_DIALOGUE } + 2 -> sendDialogue(player!!, "This book has some very enlightening points to make, but you are at a loss to know how anyone without a head could possibly read it.").also { stage = END_DIALOGUE } + 3 -> sendDialogue(player!!, "This book seems to have been read hundreds of times, and has scribbles and formulae on every page. One such scribble says None good enough - have to lock them in the caverns...").also { stage = END_DIALOGUE } + 4 -> sendDialogue(player!!, "As you pull the book a hidden latch springs into place, and the bookcase swings open, revealing a secret compartment.").also { stage++ } + } + 2 -> sendItemDialogue(player!!, Items.MARBLE_AMULET_4187, "You find a marble amulet in the secret compartment.").also { + addItemOrDrop(player!!, Items.MARBLE_AMULET_4187, 1) + stage = END_DIALOGUE + } + } + } +} + +class BookcaseEastDialogueFile : DialogueFile() { + + override fun handle(componentID: Int, buttonID: Int) { + val opposingGender = if (player!!.isMale) "female" else "male" + when (stage) { + START_DIALOGUE -> sendDialogueOptions(player!!, "Which book would you like to read?", "Men are from Morytania, Women are from Lumbridge", "Chimney Sweeping on a Budget", "Handy Maggot Avoidance Techniques", "My Family and Other Zombies").also { stage++ } + 1 -> when (buttonID) { + 1 -> sendDialogue(player!!, "You discover some fascinating insights into the mind of the $opposingGender kind.").also { stage = END_DIALOGUE } + 2 -> ChimneySweepingOnABudgetBook.display(player!!, 0, 0).also { end() } + 3 -> sendDialogue(player!!, "As you pull the book a hidden latch springs into place, and the bookcase swings open, revealing a secret compartment.").also { stage++ } + 4 -> sendDialogue(player!!, "The book is appallingly dull.").also { stage = END_DIALOGUE } + } + 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 + } + } else { + sendDialogue(player!!, "The secret compartment is empty.") + } + } + } + } +} + + +/** + * Chimney Sweeping on a Budget Book + * @author ovenbreado + */ +class ChimneySweepingOnABudgetBook { + companion object { + + private val TITLE = "Chimney Sweeping on a Budget " + val CONTENTS = arrayOf( + PageSet( + Page( + BookLine("Page 26", 55), + BookLine("that sometimes a sweep", 56), + BookLine("may find themselves", 57), + BookLine("brushless and without the", 58), + BookLine("funds to purchase the", 59), + BookLine("one tool that is most", 60), + BookLine("essential to their trade.", 61), + BookLine("What is a chimney sweep", 62), + BookLine("without his or her brush?", 63), + BookLine("In this kind of situation", 64), + BookLine("any normal long-handled", 65), + ), + Page( + BookLine("brush might be a suitable", 66), + BookLine("replacement, although", 67), + BookLine("when attaching extensions", 68), + BookLine("to the handle make sure", 69), + BookLine("to use something sturdy", 70), + BookLine("like wire, otherwise a", 71), + BookLine("sweep may find", 72), + BookLine("themselves losing their", 73), + BookLine("brush and livelyhood to", 74), + BookLine("the forces of gravity", 75), + ) + ), + ) + fun display(player: Player, pageNum: Int, buttonID: Int) : Boolean { + BookInterface.pageSetup(player, BookInterface.FANCY_BOOK_3_49, TITLE, CONTENTS) + 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 new file mode 100644 index 000000000..dd7dccc9b --- /dev/null +++ b/Server/src/main/content/region/morytania/quest/creatureoffenkenstrain/CreatureOfFenkenstrain.kt @@ -0,0 +1,183 @@ +package content.region.morytania.quest.creatureoffenkenstrain + +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 +import content.data.Quests + +/** + * Creature of Fenkenstrain Quest + * + * https://www.youtube.com/watch?v=zmvgzuA31S0 + * https://www.youtube.com/watch?v=RoR6zLGrRoY + * https://www.youtube.com/watch?v=I6X0kER1wDA - You can telekinetic grab the pickled brain jar lol + * https://www.youtube.com/watch?v=53_2dKEu0xo - has the letter and the bottom part of the quest dialogue + * https://www.youtube.com/watch?v=s57ElnRNhmU - in 2014 but the most thorough + + * 1 - Read Signpost to start the quest + * 2 - Talked to Fenkenstrain + * 3 - Collected body parts + * 4 - Gave needle and thread + * 5 - Repaired the lightning conductor and bring the creature to life + * 6 - Talked to the creature + * 100 - Stoped Fenkenstrain by stealing ring of charos + */ +@Initializable +class CreatureOfFenkenstrain : Quest(Quests.CREATURE_OF_FENKENSTRAIN, 41, 40, 2, 399, 0, 1, 9) { + + companion object { + const val attributeArms = "/save:quest:creatureoffenkenstrain-arms" + const val attributeLegs = "/save:quest:creatureoffenkenstrain-legs" + const val attributeTorso = "/save:quest:creatureoffenkenstrain-torso" + const val attributeHead = "/save:quest:creatureoffenkenstrain-decaphead" + const val attributeUnlockedMemorial = "/save:quest:creatureoffenkenstrain-amuletonmemorial" + const val attributeUnlockedShed = "/save:quest:creatureoffenkenstrain-keyonsheddoor" + const val attributeNeedle = "/save:quest:creatureoffenkenstrain-needle" + const val attributeThread = "/save:quest:creatureoffenkenstrain-thread" + const val fenkenstrainVarp = 399 + } + override fun drawJournal(player: Player, stage: Int) { + super.drawJournal(player, stage) + var line = 12 + var stage = getStage(player) + + 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) + line(player, "centre of !!Canifis??.", line++, false) + line(player, "I must be able to defeat a !!level 51 monster??, and need the", line++, false) + line(player, "following skill levels:", line++, false) + 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, 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) + if (stage >= 2) { + line(player, "I spoke to Fenkenstrain, who wanted me to find him some", line++, true) + line(player, "body parts so that he could build a creature.", line++, true) + } else if (stage >= 1) { + line(player, "I should go up to the castle and speak to !!Dr Fenkenstrain??", line++, false) + } + if (stage >= 3) { + 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) + line(player, "a !!torso??", line++, false) + line(player, "a !!head??", line++, false) + line++ + line(player, "Apparently the soil of !!Morytania?? has a unique quality", line++, false) + line(player, "which preserves the bodies of the dead better than", line++, false) + line(player, "elsewhere, so perhaps I should look at the graves in the", line++, false) + line(player, "local area", line++, false) + } + if (stage >= 4) { + line(player, "I brought Fenkenstrain a needle and 5 quantities of", line++, true) + line(player, "thread.", line++, true) + } else if (stage >= 3) { + line(player, "I need to bring !!Fenkenstrain?? a !!needle?? and !!5 quantities??", line++, false) + line(player, "!!of thread??.", line++, false) + } + 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, "!!Fenkenstrain?? has ordered me to repair the lightning", line++, false) + line(player, "conductor.", line++, false) + } + if (stage == 5) { + line(player, "!!Fenkenstrain?? wants to talk to me.", line++, false) + line++ + } + if (stage >= 7) { + line(player, "The Creature went on a rampage, and Fenkenstrain sent", line++, true) + line(player, "me up to the Tower to destroy it.", line++, true) + line(player, "The Creature convinced me to stop Fenkenstrain's", line++, true) + line(player, "experiments once and for all, and has told me the true", line++, true) + line(player, "history of Fenkenstrain's treachery.", line++, true) + } else if (stage >= 6) { + 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) + } + if (stage >= 8) { + 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) + } + if (stage >= 100) { + line++ + line(player,"QUEST COMPLETE!", line) + } + limitScrolling(player, line, false) + } + } + + override fun hasRequirements(player: Player): Boolean { + return arrayOf( + hasLevelStat(player, Skills.CRAFTING, 20), + hasLevelStat(player, Skills.THIEVING, 25), + isQuestComplete(player, Quests.PRIEST_IN_PERIL), + isQuestComplete(player, Quests.THE_RESTLESS_GHOST), + ).all { it } + } + + override fun reset(player: Player) { + setVarp(player, fenkenstrainVarp, 0, true) + removeAttribute(player, attributeArms) + removeAttribute(player, attributeLegs) + removeAttribute(player, attributeTorso) + removeAttribute(player, attributeHead) + removeAttribute(player, attributeUnlockedMemorial) + removeAttribute(player, attributeUnlockedShed) + removeAttribute(player, attributeNeedle) + removeAttribute(player, attributeThread) + } + + override fun finish(player: Player) { + var ln = 10 + super.finish(player) + player.packetDispatch.sendString("You have completed Creature of Fenkenstrain!", 277, 4) + player.packetDispatch.sendItemZoomOnInterface(Items.RING_OF_CHAROS_4202,230,277,5) + + drawReward(player, "2 quest points", ln++) + drawReward(player, "Ring of Charos", ln++) + drawReward(player, "1,000 Theiving XP", ln++) + + addItemOrDrop(player, Items.RING_OF_CHAROS_4202, 1) + rewardXP(player, Skills.THIEVING, 1000.0) + } + + override fun setStage(player: Player, stage: Int) { + super.setStage(player, stage) + this.updateVarps(player) + } + + 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, Quests.CREATURE_OF_FENKENSTRAIN) == 4) { + setVarp(player, fenkenstrainVarp, 3, true) + } + if(getQuestStage(player, Quests.CREATURE_OF_FENKENSTRAIN) >= 8) { + setVarp(player, fenkenstrainVarp, 8, true) + } + } + + override fun newInstance(`object`: Any?): Quest { + return this + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/morytania/quest/creatureoffenkenstrain/CreatureOfFenkenstrainListeners.kt b/Server/src/main/content/region/morytania/quest/creatureoffenkenstrain/CreatureOfFenkenstrainListeners.kt new file mode 100644 index 000000000..7a0aea3bc --- /dev/null +++ b/Server/src/main/content/region/morytania/quest/creatureoffenkenstrain/CreatureOfFenkenstrainListeners.kt @@ -0,0 +1,396 @@ +package content.region.morytania.quest.creatureoffenkenstrain + +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.item.GroundItem +import core.game.system.task.Pulse +import core.game.world.map.Location +import core.game.world.update.flag.context.Animation +import org.rs09.consts.Scenery +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +class CreatureOfFenkenstrainListeners : InteractionListener { + companion object { + private val itemToAttribute = hashMapOf( + Items.ARMS_4195 to CreatureOfFenkenstrain.attributeArms, + Items.LEGS_4196 to CreatureOfFenkenstrain.attributeLegs, + Items.TORSO_4194 to CreatureOfFenkenstrain.attributeTorso, + Items.DECAPITATED_HEAD_4197 to CreatureOfFenkenstrain.attributeHead + ) + + enum class Graves(val location: Location, val graveName: String, val unearthText: String, val unearthItem: Int?) { + GRAVE1(Location(3541, 3541), "Anton Hayes", "...but the grave is empty.", null), + GRAVE2(Location(3542, 3486), "Callum Elding", "...but the grave is empty.", null), + GRAVE3(Location(3585, 3497), "Domin O'Raleigh", "...but the grave is empty.", null), + GRAVE4(Location(3608, 3491), "Ed Lestwit", "...and you unearth a decapitated head.", Items.DECAPITATED_HEAD_4197), + GRAVE5(Location(3588, 3472), "Elena Frey", "...but the grave is empty.", null), + GRAVE6(Location(3593, 3509), "Eryn Treforest", "...but the grave is empty.", null), + GRAVE7(Location(3594, 3491), "Isla Skye", "...but the grave is empty.", null), + GRAVE8(Location(3596, 3479), "Jayna Harrow", "...but the grave is empty.", null), + GRAVE9(Location(3604, 3466), "Jayne Corbo", "...but the grave is empty.", null), + GRAVE10(Location(3608, 3466), "Kandik Kludge", "...but the grave is empty.", null), + GRAVE11(Location(3616, 3478), "Korvic Frey", "...but the grave is empty.", null), + GRAVE12(Location(3619, 3469), "Marabella Kludge", "...but the grave is empty.", null), + GRAVE13(Location(3626, 3495), "Marcus Harrow", "...but the grave is empty.", null), + GRAVE14(Location(3629, 3483), "Petrik Corbo", "...but the grave is empty.", null), + GRAVE15(Location(3631, 3476), "Serra Alcanthric", "...but the grave is empty.", null), + GRAVE16(Location(3631, 3500), "Toran Alcanthric", "...but the grave is empty.", null), + GRAVE17(Location(3572, 3527), "Unknown", "...but the grave is empty.", null), + GRAVE18(Location(3576, 3526), "Unknown", "...but the grave is empty.", null), + GRAVE19(Location(3639, 3470), "Lord Rologray", "...but the grave is empty.", null), + GRAVE20(Location(3634, 3503), "Lord Rologarth", "...but the grave is empty.", null), + GRAVE21(Location(3502, 3576), "Lady Rolobrae", "...and you unearth a torso.", Items.TORSO_4194), + GRAVE22(Location(3504, 3577), "Lord Rolomere", "...and you unearth a pair of arms.", Items.ARMS_4195), + GRAVE23(Location(3506, 3576), "Lord Rolovanne", "...and you unearth a pair of legs.", Items.LEGS_4196); + + companion object { + @JvmField + val locationMap = Graves.values().associateBy { it.location } + } + } + } + override fun defineListeners() { + + // Climbing ladders + addClimbDest(Location.create(3504, 9970, 0), Location.create(3504, 3571, 0)) + + // 1: Reading Signpost to start the quest + on(Items.NULL_5164, SCENERY, "read") { player, _ -> + if (getQuestStage(player, Quests.CREATURE_OF_FENKENSTRAIN) < 7 ) { + sendDialogueLines( + player, + "The signpost has a note pinned onto it. The note says:", + "'---- Braindead Butler Wanted ----", + " Gravedigging skills essential - Hunchback advantageous", + "See Dr Fenkenstrain at the castle NE of Canifis'", + ) + } else { + sendDialogueLines( + player, + "The signpost has a note pinned onto it. The note says:", + "'AAARRGGGHHHHH!!!!!'", + ) + } + 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 + } + + // 2 Reading a grave + on(intArrayOf(Scenery.GRAVE_5168, Scenery.GRAVE_5169), SCENERY, "read") { player, node -> + val grave = Graves.locationMap[node.location] + sendMessage(player, "The grave says:") + sendMessage(player, " 'Here lies ${grave?.graveName ?: "Unknown"} - REST IN PEACE'") + return@on true + } + + // 2: Digging a grave + on(intArrayOf(Scenery.GRAVE_5168, Scenery.GRAVE_5169), SCENERY, "dig") { player, node -> + if(!inInventory(player, Items.SPADE_952)) { + sendMessage(player, "You need a spade to do that.") + return@on true + } + val grave = Graves.locationMap[node.location] + sendMessage(player, "You start digging...") + animate(player, Animation(831)) + player.pulseManager.run(object : Pulse(5) { + override fun pulse(): Boolean { + player.animate(Animation(-1)) + if(grave?.unearthItem != null && + !hasAnItem(player, grave.unearthItem).exists() + /* && !getAttribute(player, itemToAttribute[grave.unearthItem] ?: "", false) */) { + sendItemDialogue(player, grave.unearthItem, grave.unearthText) + addItemOrDrop(player, grave.unearthItem) + } else { + sendMessage(player, "...but the grave is empty.") + } + return true + } + }) + return@on true + } + + // 2: Find amulets in bookcases. Note: even after the quest, these can still be accessed, but nothing falls out. + on(Scenery.BOOKCASE_5166, SCENERY, "search") { player, node -> + if (node.location.equals(Location(3555, 3558, 1))) { + openDialogue(player, BookcaseEastDialogueFile()) + return@on true + } else if(node.location.equals(Location(3542, 3558, 1))) { + openDialogue(player, BookcaseWestDialogueFile()) + return@on true + } else { + sendMessage(player, "It is a bookcase full of books") + return@on true + } + } + + // 2: Snap together Amulets + onUseWith(ITEM, Items.MARBLE_AMULET_4187, Items.OBSIDIAN_AMULET_4188) { player, used, with -> + if(removeItem(player, Items.MARBLE_AMULET_4187) && removeItem(player, Items.OBSIDIAN_AMULET_4188)) { + sendItemDialogue(player, Items.STAR_AMULET_4183, "The marble and obsidian amulets snap together tightly to form a six-pointed amulet.") + addItemOrDrop(player, Items.STAR_AMULET_4183) + } + return@onUseWith true + } + + // 2: Fit Star Amulet on Memorial + onUseWith(SCENERY, Items.STAR_AMULET_4183, Items.NULL_5167) { player, used, with -> + if (removeItem(player, Items.STAR_AMULET_4183)) { + sendItemDialogue(player, Items.STAR_AMULET_4183, "The star amulet fits exactly into the depression on the coffin lid.") + setAttribute(player, CreatureOfFenkenstrain.attributeUnlockedMemorial, true) + } + return@onUseWith true + } + + // 2: Opening Cavern Entrance + on(Scenery.ENTRANCE_5170, SCENERY, "open") { player, node -> + if (inInventory(player, Items.CAVERN_KEY_4184) && removeItem(player, Items.CAVERN_KEY_4184)) { + DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) + } else { + sendMessage(player, "The door is locked.") + } + return@on true + } + // 2: Using Cavern Key on the Cavern Entrance + onUseWith(SCENERY, Items.CAVERN_KEY_4184, Scenery.ENTRANCE_5170) { player, used, with -> + if (removeItem(player, used)) { + DoorActionHandler.handleAutowalkDoor(player, with.asScenery()) + } + return@onUseWith true + } + + + // 2: Searching for Cavern Key out of the Chest + on(Scenery.CHEST_5163, SCENERY, "search") { player, node -> + sendItemDialogue(player, Items.CAVERN_KEY_4184, "You take a key out of the chest.") + addItemOrDrop(player, Items.CAVERN_KEY_4184) + return@on true + } + + // 2: Taking the brain from the table (telekinetic grab is allowed) + on(Items.PICKLED_BRAIN_4199, GROUNDITEM, "take") { player, node -> + if(node.location.equals(3492, 3474, 0)) { + openDialogue(player, RoavarDialogueFile(2), findLocalNPC(player, NPCs.ROAVAR_1042)!!) + } else { + PickupHandler.take(player, node as GroundItem) + } + return@on true + } + + // 2: Fit Brain into Decapitated Head + onUseWith(ITEM, Items.PICKLED_BRAIN_4199, Items.DECAPITATED_HEAD_4197) { player, used, with -> + if(removeItem(player, used) && removeItem(player, with)) { + sendItemDialogue(player, Items.DECAPITATED_HEAD_4198, "You squeeze the pickled brain into the decapitated head.") + addItemOrDrop(player, Items.DECAPITATED_HEAD_4198) + } + return@onUseWith true + } + + // 2: Searching the Memorial (Scenery.MEMORIAL_5167) + on(Items.NULL_5167, SCENERY, "search") { player, node -> + val scenery = node.asScenery() + if(getAttribute(player, CreatureOfFenkenstrain.attributeUnlockedMemorial, false) || + getQuestStage(player, Quests.CREATURE_OF_FENKENSTRAIN) > 2) { + animateScenery(player, scenery, 1620) + var dest: Location? = null + if(scenery.location.equals(Location(3505, 3571))) { + dest = Location(3504, 9969) + } else if(scenery.location.equals(Location(3578, 3527))) { + dest = Location(3577, 9927) + } + if(dest != null) { + player.pulseManager.run(object : Pulse(3) { + override fun pulse(): Boolean { + resetAnimator(player) + teleport(player, dest!!) + return true + } + }) + } + } else { + if (scenery.location.equals(Location(3505, 3571))) { + sendMessage(player, "You find a depression in the memorial stone in the shape of a six-pointed star.") + } else { + sendMessage(player, "You find nothing remarkable about the memorial stone.") + } + } + return@on true + } + + // 2: Pushing open the Memorial (Scenery.MEMORIAL_5167) + on(Items.NULL_5167, SCENERY, "push") { player, node -> + val scenery = node.asScenery() + if (getAttribute(player, CreatureOfFenkenstrain.attributeUnlockedMemorial, false) || + getQuestStage(player, Quests.CREATURE_OF_FENKENSTRAIN) > 2) { + animateScenery(player, scenery, 1620) + var dest: Location? = null + if(scenery.location.equals(Location(3505, 3571))) { + dest = Location(3504, 9969) + } else if(scenery.location.equals(Location(3578, 3527))) { + dest = Location(3577, 9927) + } + if(dest != null) { + player.pulseManager.run(object : Pulse(3) { + override fun pulse(): Boolean { + resetAnimator(player) + teleport(player, dest!!) + return true + } + }) + } + } else { + player.sendMessage("The coffin is incredibly heavy, and does not budge.") + } + return@on true + } + + // 5: Garden Shed Door + on(Scenery.DOOR_5174, SCENERY, "open") { player, node -> + if (getAttribute(player, CreatureOfFenkenstrain.attributeUnlockedShed, false) || + 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)) { + DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) + setAttribute(player, CreatureOfFenkenstrain.attributeUnlockedShed, true) + } + } else { + sendMessage(player, "The door is locked.") + } + return@on true + } + + // 5: Garden Shed Door + onUseWith(SCENERY, Items.SHED_KEY_4186, Scenery.DOOR_5174) { player, used, with -> + if (getAttribute(player, CreatureOfFenkenstrain.attributeUnlockedShed, false) || + getQuestStage(player, Quests.CREATURE_OF_FENKENSTRAIN) >= 5) { + DoorActionHandler.handleAutowalkDoor(player, with.asScenery()) + } else if (removeItem(player, used)) { + DoorActionHandler.handleAutowalkDoor(player, with.asScenery()) + setAttribute(player, CreatureOfFenkenstrain.attributeUnlockedShed, true) + } + return@onUseWith true + } + + // 5: Pile of Canes + on(Items.NULL_5158, SCENERY, "take-from") { player, node -> + sendMessage(player, "You take a garden cane from the pile.") + addItemOrDrop(player, Items.GARDEN_CANE_4189) + return@on true + } + + // 5: Cupboard Garden Brush + on(Items.NULL_5157, SCENERY, "search") { player, node -> + sendItemDialogue(player, Items.GARDEN_BRUSH_4190, "You find a garden brush in the cupboard.") + // player.packetDispatch.sendAngleOnInterface(241, 1, 1000, 510, 0) + addItemOrDrop(player, Items.GARDEN_BRUSH_4190) + return@on true + } + + // 5: Extended Brush up to 3 Fused + onUseWith(ITEM, intArrayOf(Items.GARDEN_BRUSH_4190, Items.EXTENDED_BRUSH_4191, Items.EXTENDED_BRUSH_4192), Items.GARDEN_CANE_4189) { player, used, with -> + if (!inInventory(player, Items.BRONZE_WIRE_1794)) { + sendMessage(player, "You need some bronze wire to tie them together.") + return@onUseWith true + } + if (removeItem(player, Items.BRONZE_WIRE_1794) && removeItem(player, used) && removeItem(player, with)){ + sendMessage(player, "You attach the cane to the brush.") + when (used.id) { + Items.GARDEN_BRUSH_4190 -> { + addItemOrDrop(player, Items.EXTENDED_BRUSH_4191) + } + Items.EXTENDED_BRUSH_4191 -> { + addItemOrDrop(player, Items.EXTENDED_BRUSH_4192) + } + Items.EXTENDED_BRUSH_4192 -> { + addItemOrDrop(player, Items.EXTENDED_BRUSH_4193) + } + } + } + return@onUseWith true + } + + // 5: Cupboard Garden Brush + on(Scenery.FIREPLACE_5165, SCENERY, "examine") { player, node -> + if (!inInventory(player, Items.CONDUCTOR_4201)) { + sendMessage(player, "You give the chimney a jolly good clean out.") + sendItemDialogue(player, Items.CONDUCTOR_MOULD_4200, "A lightning conductor mould falls down out of the chimney.") + addItemOrDrop(player, Items.CONDUCTOR_MOULD_4200) + return@on true + } + sendMessage(player, "It looks like it needs a good sweep out.") + return@on true + } + + // 5: Brush Fireplace + onUseWith(SCENERY, Items.EXTENDED_BRUSH_4193, Scenery.FIREPLACE_5165) { player, used, with -> + sendMessage(player, "You give the chimney a jolly good clean out.") + sendItemDialogue(player, Items.CONDUCTOR_MOULD_4200, "A lightning conductor mould falls down out of the chimney.") + addItemOrDrop(player, Items.CONDUCTOR_MOULD_4200) + return@onUseWith true + } + + // 5: Repair the lightning rod + on(Scenery.LIGHTNING_CONDUCTOR_5176, SCENERY, "repair") { player, node -> + if (!inInventory(player, Items.CONDUCTOR_4201)) { + sendMessage(player, "You need to repair it with a conductor.") + return@on true + } + if (removeItem(player, Items.CONDUCTOR_4201)) { + 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() + replaceScenery(scenery, Items.NULL_5177, 3, node.location) + animateScenery(player, scenery, 1632) + } + return@on true + } + + // 6: Enter jail above + on(Scenery.DOOR_5172, SCENERY, "open") { player, node -> + 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.") + } + return@on true + } + // 2: Using Cavern Key on the Cavern Entrance + onUseWith(SCENERY, Items.TOWER_KEY_4185, Scenery.DOOR_5172) { player, used, with -> + if (inInventory(player, Items.TOWER_KEY_4185)) { + DoorActionHandler.handleAutowalkDoor(player, with.asScenery()) + } + return@onUseWith true + } + + + // 7: Pickpocket Ring of Charos from Fenkenstrain + on(NPCs.DR_FENKENSTRAIN_1670, NPC, "pickpocket") { player, node -> + if (getQuestStage(player, Quests.CREATURE_OF_FENKENSTRAIN) == 7) { + sendMessage(player, "You steal the Ring of Charos from Fenkenstrain.") + 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.") + } else { + player.dialogueInterpreter.sendDialogues(NPCs.DR_FENKENSTRAIN_1670, FacialExpression.NEUTRAL ,"What do you think you're doing???") + } + return@on true + } + } +} diff --git a/Server/src/main/content/region/morytania/quest/creatureoffenkenstrain/DrFenkenstrainDialogue.kt b/Server/src/main/content/region/morytania/quest/creatureoffenkenstrain/DrFenkenstrainDialogue.kt new file mode 100644 index 000000000..c6e848f86 --- /dev/null +++ b/Server/src/main/content/region/morytania/quest/creatureoffenkenstrain/DrFenkenstrainDialogue.kt @@ -0,0 +1,275 @@ +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 org.rs09.consts.Items +import org.rs09.consts.NPCs + +@Initializable +class DrFenkenstrainDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, DrFenkenstrainDialogueFile(), npc) + return true + } + override fun newInstance(player: Player): DialoguePlugin { + return DrFenkenstrainDialogue(player) + } + override fun getIds(): IntArray { + // THE IDs ARE WRONG. 1668 and 1669 are varp controlled Dr Fenkenstrain + return intArrayOf(NPCs.WEREWOLF_1668, NPCs.WEREWOLF_1669, NPCs.DR_FENKENSTRAIN_1670) + } +} + +class DrFenkenstrainDialogueFile : DialogueBuilderFile() { + + companion object { + private fun allPartsSubmitted(player: Player): Boolean { + return getAttribute(player, CreatureOfFenkenstrain.attributeArms, false) && + getAttribute(player, CreatureOfFenkenstrain.attributeLegs, false) && + getAttribute(player, CreatureOfFenkenstrain.attributeTorso, false) && + getAttribute(player, CreatureOfFenkenstrain.attributeHead, false) + } + private fun reqArms(player: Player): Boolean { + return !getAttribute(player, CreatureOfFenkenstrain.attributeArms, false) && inInventory(player, Items.ARMS_4195) + } + private fun reqLegs(player: Player): Boolean { + return !getAttribute(player, CreatureOfFenkenstrain.attributeLegs, false) && inInventory(player, Items.LEGS_4196) + } + private fun reqTorso(player: Player): Boolean { + return !getAttribute(player, CreatureOfFenkenstrain.attributeTorso, false) && inInventory(player, Items.TORSO_4194) + } + private fun reqHead(player: Player): Boolean { + return !getAttribute(player, CreatureOfFenkenstrain.attributeHead, false) && inInventory(player, Items.DECAPITATED_HEAD_4198) + } + private fun hasPart(b: DialogueBuilder, item: Item, attributeToSet: String, successMsg: String): DialogueBuilder { + return b.branch { player -> + return@branch if (!getAttribute(player, attributeToSet, false) && inInventory(player, item.id, item.amount)) { 1 } else { 0 } + }.let{ branch -> + val returnJoin = b.placeholder() + branch.onValue(0) + .goto(returnJoin) + branch.onValue(1) + .betweenStage { _, player, _, _ -> + setAttribute(player, attributeToSet, true) + removeItem(player, item) + } + .npcl(successMsg) + .goto(returnJoin) + return@let returnJoin.builder() + } + } + } + + override fun create(b: DialogueBuilder) { + + 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(Quests.CREATURE_OF_FENKENSTRAIN, 1) + .npcl("Have you come to apply for the job?") + .options().let { optionBuilder -> + val continuePath = b.placeholder() + optionBuilder.option ("Yes") + .playerl("Yes, if it pays well.") + .goto(continuePath) // Continue down below. + optionBuilder.option_playerl("No.") + .end() + return@let continuePath.builder() + } + .npcl("I'll have to ask you some questions first.") + .playerl("Okay...") + .npcl("How would you describe yourself in one word?") + .options().let { optionBuilder -> + val continuePath = b.placeholder() + optionBuilder.recordAttribute("creature-of-fenkenstrain:first-question") + optionBuilder.option_playerl("Stunning.").goto(continuePath) + optionBuilder.option_playerl("Awe-inspiring.").goto(continuePath) + optionBuilder.option_playerl("Breathtaking.").goto(continuePath) + optionBuilder.option_playerl("Braindead.").goto(continuePath) + return@let continuePath.builder() + } + .npcl("Mmmm, I see.") + .npcl("Just one more question. What would you say is your greatest skill?") + .options().let { optionBuilder -> + val continuePath = b.placeholder() + optionBuilder.recordAttribute("creature-of-fenkenstrain:second-question") + optionBuilder.option_playerl("Combat.").goto(continuePath) + optionBuilder.option_playerl("Magic.").goto(continuePath) + optionBuilder.option_playerl("Cooking.").goto(continuePath) + optionBuilder.option_playerl("Grave-digging.").goto(continuePath) + return@let continuePath.builder() + } + .npcl("Mmmm, I see.") + .branch { player -> if(player.getAttribute("creature-of-fenkenstrain:first-question", -1) == 3 && player.getAttribute("creature-of-fenkenstrain:second-question", -1) == 3) { 1 } else { 0 } } + .let{ branch -> + // Failure branch + branch.onValue(0) + .npcl("Looks like you're not the @g[man,woman] for the job.") + .end() + return@let branch // Return DialogueBranchBuilder instead of DialogueBuilder to forward the success branch. + }.onValue(1) // Success branch + .npcl("Looks like you're just the @g[man,woman] for the job! Welcome aboard!") + .playerl("Is there anything you'd like me to do for you, sir?") + .npcl("Yes, there is. You're highly skilled at grave-digging, yes?") + .playerl("Err...yes, that's what I said.") + .npcl("Excellent. Now listen carefully. I need you to find some...stuff...for me.") + .playerl("Stuff?") + .npcl("That's what I said...stuff.") + .playerl("What kind of stuff?") + .npcl("Well...dead stuff.") + .playerl("Go on...") + .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, Quests.CREATURE_OF_FENKENSTRAIN) == 1) { + setQuestStage(player, Quests.CREATURE_OF_FENKENSTRAIN, 2) + } + } + + b.onQuestStages(Quests.CREATURE_OF_FENKENSTRAIN, 2) + .options().let { optionBuilder -> + val continuePath = b.placeholder() + + optionBuilder.optionIf("I have some body parts for you.") { player -> return@optionIf allPartsSubmitted(player) || reqArms(player) || reqLegs(player) || reqTorso(player) || reqHead(player) } + .playerl("I have some body parts for you.") + .goto(continuePath) // Continue down below. + optionBuilder.optionIf("Do you know where I could find body parts?") { player -> return@optionIf !(allPartsSubmitted(player) || reqArms(player) || reqLegs(player) || reqTorso(player) || reqHead(player)) } + .playerl("Do you know where I could find body parts?") + .npcl("The soil of Morytania is unique in its ability to preserve the bodies of the dead, which is one reason why I have chosen to carry out my experiments here.") + .npcl("I recommend digging up some graves in the local area. To the south-east you will find the Haunted Woods; I believe there are many graves there.") + .npcl( "There is also a mausoleum on an island west of this castle. I expect the bodies that are buried there to be extremely well preserved, as they were wealthy in life.") + .end() + optionBuilder.option_playerl("Remind me what you want me to do.") + .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.") + .end() + optionBuilder.option_playerl("Why are you trying to make this creature?") + .npcl("I came to the land of Morytania many years ago, to find a safe sanctuary for my experiments. This abandoned castle suited my purposes exactly.") + .playerl("What were you experimenting in?") + .npcl("Oh, perfectly innocent experiments - for the good of mankind.") + .playerl("Then why did you need to come to Morytania?") + .npcl("Enough questions, now. Get back to your work.") + .end() + optionBuilder.option_playerl("Will this creature put me out of a job?") + .npcl("No, my friend. I have a very special purpose in mind for this creature.") + .end() + optionBuilder.option_playerl("I must get back to work, sir.") + .end() + + return@let continuePath.builder() + } + // Dialogue path to look for arms. + .let{ builder -> return@let hasPart(builder, Item(Items.ARMS_4195, 1), CreatureOfFenkenstrain.attributeArms, "Great, you've brought me some arms.") } + // Dialogue path to look for legs. + .let{ builder -> return@let hasPart(builder, Item(Items.LEGS_4196, 1), CreatureOfFenkenstrain.attributeLegs, "Excellent, you've brought me some legs.") } + // Dialogue path to look for torso. + .let{ builder -> return@let hasPart(builder, Item(Items.TORSO_4194, 1), CreatureOfFenkenstrain.attributeTorso, "Splendid, you've brought me a torso.") } + // Dialogue path to look for head. + .let{ builder -> return@let hasPart(builder, Item(Items.DECAPITATED_HEAD_4198, 1), CreatureOfFenkenstrain.attributeHead, "Fantastic, you've brought me a head.") } + .branch { player -> + return@branch if (allPartsSubmitted(player)) { 1 } else { 0 } + }.let{ branch -> + // Failure branch + branch.onValue(0) + .end() + return@let branch // Return DialogueBranchBuilder instead of DialogueBuilder to forward the success branch. + }.onValue(1) // Success branch + .npcl("Superb! Those are all the parts I need. Now to sew them together...") + .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, Quests.CREATURE_OF_FENKENSTRAIN) == 2) { + setQuestStage(player, Quests.CREATURE_OF_FENKENSTRAIN, 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.") } + // Dialogue path to look for 5 threads. + .let{ builder -> return@let hasPart(builder, Item(Items.THREAD_1734, 5), CreatureOfFenkenstrain.attributeThread, "Some thread. Excellent.") } + // Final branch to only continue conversation when both needle and threads are submitted. + .branch { player -> + return@branch if (getAttribute(player, CreatureOfFenkenstrain.attributeNeedle, false) && + getAttribute(player, CreatureOfFenkenstrain.attributeThread, false)) { 1 } else { 0 } + }.let{ branch -> + // Failure branch + branch.onValue(0) + .end() + return@let branch // Return DialogueBranchBuilder instead of DialogueBuilder to forward the success branch. + }.onValue(1) // Success branch + .betweenStage { df, player, _, _ -> + setVarp(player, CreatureOfFenkenstrain.fenkenstrainVarp, 3, true) + } + .line("Fenkenstrain uses the needle and thread to sew the body parts", "together. Soon, a hideous creature lies inanimate on the ritual table.") + .npcl("Perfect. But I need one more thing from you - flesh and bones by themselves do not make life.") + .playerl("Really?") + .npcl("I have honed to perfection an ancient ritual that will give life to this creature, but for this I must harness the very power of Nature.") + .playerl("And what power is this?") + .npcl("The power of lightning.") + .playerl("Sorry, can't make lightning, you've got the wrong @g[man,woman]-") + .npcl("Silence your insolent tongue! The storm that brews overhead will create the lightning. What I need you to do is to repair the lightning conductor on the balcony above.") + .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, Quests.CREATURE_OF_FENKENSTRAIN) == 3) { + setQuestStage(player, Quests.CREATURE_OF_FENKENSTRAIN, 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(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.") + .npcl("I tricked it into going up to the Tower, and there it remains, imprisoned.") + .playerl(FacialExpression.SUSPICIOUS, "So the creature wasn't all you'd hoped then?") + .npcl("...oh, what have I done...") + .playerl(FacialExpression.SUSPICIOUS, "Oh, I see, we're developing a sense of right and wrong now are we?") + .playerl(FacialExpression.SUSPICIOUS, "Bit late for that, I'd say.") + .npcl("I have no control over it! It's coming to get me!") + .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, Quests.CREATURE_OF_FENKENSTRAIN) == 5) { + setQuestStage(player, Quests.CREATURE_OF_FENKENSTRAIN, 6) + } + addItemOrDrop(player, Items.TOWER_KEY_4185) + } + + b.onQuestStages(Quests.CREATURE_OF_FENKENSTRAIN, 6) + .npcl("So have you destroyed it?!!?") + .playerl("Not yet.") + .npcl("Please, hurry - save me!!!!") + .end() + + 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!") + .npcl("Why did you have to pick Rologarth's brain of all brains?!?") + .playerl("I'm through working for you.") + .npcl("No! I refuse to release you! You must help me build another creature to destroy this dreadful mistake!!") + .end() + + 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...") + .end() + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/morytania/quest/creatureoffenkenstrain/GardenerGhostDialogue.kt b/Server/src/main/content/region/morytania/quest/creatureoffenkenstrain/GardenerGhostDialogue.kt new file mode 100644 index 000000000..70387d2c8 --- /dev/null +++ b/Server/src/main/content/region/morytania/quest/creatureoffenkenstrain/GardenerGhostDialogue.kt @@ -0,0 +1,102 @@ +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.plugin.Initializable +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import java.util.* + +@Initializable +class GardenerGhostDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + val gardenerGhost = npc as GardenerGhostNPC + if (gardenerGhost.target == player) { + // This is the special case where the gardener follows you. + if (gardenerGhost.location.withinDistance(gardenerGhost.graveLocation, 5)) { + sendChat(gardenerGhost, "Here is the place where I met me' maker.") + } else { + sendChat(gardenerGhost, "Go " + gardenerGhost.location.deriveDirection(gardenerGhost.graveLocation).name.lowercase(Locale.getDefault()).replace('_', '-') + ", mate") + gardenerGhost.continueFollowing(player) + } + } else { + openDialogue(player, GardenerGhostDialogueFile(), npc) + } + return true + } + override fun newInstance(player: Player): DialoguePlugin { + return GardenerGhostDialogue(player) + } + override fun getIds(): IntArray { + return intArrayOf(NPCs.GARDENER_GHOST_1675) + } +} + +class GardenerGhostDialogueFile : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + b.onPredicate { player -> !player.equipment.containsAtLeastOneItem(Items.GHOSTSPEAK_AMULET_552) } + .npcl("Wooo wooo wooooo.") + .end() + 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?") + .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("Oi could tell you a few things about old Fenky, yeah.") + .playerl("Go on.") + .npcl("Once, this castle were full o' good folk - my friends. Fenky was just the castle doctor, you know, to the lord and the castle folk. I don't know what happened to them all, but one by one they all disappeared.") + .npcl("When they were gone a while, I went an dug graves for 'em in the forest. After a while there weren't no one left, but the lord, Fenkenstrain and meself.") + .npcl("Old Fenky sent me into the forest to dig 'im a pit - never said what for - then would you believe it, someone chops me 'ead off.") + .playerl("Did you see who did it...before...?") + .npcl("Before oi kicked the bucket, you mean?") + .playerl("Umm...") + .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, + 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, + 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.") + .playerl("Where is he now?") + .npcl("E's dead, just like everyone else round 'ere... 'cept for me.") + .end() + optionBuilder.option("What happened to your head?") + .playerl("What happened to your head?") + .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("Oi was in the old 'aunted Forest to the south, diggin' a pit for moi old master, old Fenkenstrain, when would you believe it, someone chops me head off. Awful bad luck weren't it?") + .playerl("Oh yes, dreadful bad luck.") + .npcl("So oi thinks to meself, I don't needs any 'ead to be getting on with me gardenin', long as I got me hands and me spade.") + .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, + 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.") + .playerl("Don't worry, I was just trying to make conversation.") + .npcl("No, no, I can't be havin' that. I'll remember in a minute...") + .playerl("Please, don't worry.") + .npcl("Lestwit, that's it! Ed Lestwit.") + .end() + } + + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/morytania/quest/creatureoffenkenstrain/GardenerGhostNPC.kt b/Server/src/main/content/region/morytania/quest/creatureoffenkenstrain/GardenerGhostNPC.kt new file mode 100644 index 000000000..750343b3e --- /dev/null +++ b/Server/src/main/content/region/morytania/quest/creatureoffenkenstrain/GardenerGhostNPC.kt @@ -0,0 +1,88 @@ +package content.region.morytania.quest.creatureoffenkenstrain + +import core.api.teleport +import core.game.interaction.MovementPulse +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.path.Pathfinder +import core.plugin.Initializable +import core.tools.secondsToTicks +import org.rs09.consts.NPCs + +@Initializable +class GardenerGhostNPC : AbstractNPC { + var target: Player? = null + var ticksLeft = secondsToTicks(0) + val graveLocation = Location(3608, 3490) + + constructor() : super(NPCs.GARDENER_GHOST_1675, null, true) {} + private constructor(id: Int, location: Location) : super(id, location) {} + + override fun construct(id: Int, location: Location, vararg objects: Any?): AbstractNPC { + return GardenerGhostNPC(id, location) + } + + override fun getIds(): IntArray { + return intArrayOf(NPCs.GARDENER_GHOST_1675) + } + + override fun handleTickActions() { + if (target == null) { + super.handleTickActions() + } else { + val distance = location.getDistance(target!!.location) + if (distance > 10) { + teleport(this, target!!.location) + } + if (ticksLeft > 0) { + ticksLeft-- + } + + if (this.location.withinDistance(graveLocation, 5) && ticksLeft > secondsToTicks(5)) { + // If gardener is about to reach his grave. + ticksLeft = secondsToTicks(4) + sendChat("Here is the place where I met me' maker.") + } + if (!properties.spawnLocation.withinDistance(target!!.location, 160) && ticksLeft > secondsToTicks(5)) { + // If person is about to walk outside of the range + ticksLeft = secondsToTicks(4) + sendChat("Fare thee well - oi must return to me' garden.") + } + if (ticksLeft == secondsToTicks(5)) { + // https://www.youtube.com/watch?v=RoR6zLGrRoY 2:07 + sendChat("Fare thee well - oi must return to me' garden.") + } + if (ticksLeft == 0) { + // No time left, reset him back. + target = null + pulseManager.clear(PulseType.STANDARD) + walkRadius = 11 + teleport(properties.spawnLocation) + } + } + } + + fun startFollowing(player: Player) { + ticksLeft = secondsToTicks(120) // You have two minutes to let him show you where his grave is. + target = player + walkRadius = 200 + + pulseManager.run((object : MovementPulse(this, player, Pathfinder.DUMB) { + override fun pulse(): Boolean { + face(player) + return false + } + }), PulseType.STANDARD) + } + + fun continueFollowing(player: Player) { + pulseManager.run((object : MovementPulse(this, player, Pathfinder.DUMB) { + override fun pulse(): Boolean { + face(player) + return false + } + }), PulseType.STANDARD) + } +} diff --git a/Server/src/main/content/region/morytania/quest/creatureoffenkenstrain/LordRologarthDialogue.kt b/Server/src/main/content/region/morytania/quest/creatureoffenkenstrain/LordRologarthDialogue.kt new file mode 100644 index 000000000..6fd066de5 --- /dev/null +++ b/Server/src/main/content/region/morytania/quest/creatureoffenkenstrain/LordRologarthDialogue.kt @@ -0,0 +1,72 @@ +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.player.Player +import core.plugin.Initializable +import org.rs09.consts.NPCs + +@Initializable +class LordRologarthDialogue (player: Player? = null) : DialoguePlugin(player) { + override fun newInstance(player: Player): DialoguePlugin { + return LordRologarthDialogue(player) + } + override fun handle(interfaceId: Int, buttonId: Int): Boolean { + openDialogue(player, LordRologarthDialogueFile(), npc) + return false + } + override fun getIds(): IntArray { + // THE IDs ARE WRONG. 1671 and 1672 are varp controlled Lord Rologarth + return intArrayOf(NPCs.DR_FENKENSTRAIN_1671, NPCs.DR_FENKENSTRAIN_1672, NPCs.FENKENSTRAINS_MONSTER_1673) + } +} + +class LordRologarthDialogueFile : DialogueBuilderFile() { + override fun create(b: DialogueBuilder) { + 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?") + .npcl("Abso *hic* lutely. Never *buuurrp* better.") + .playerl("You don't look very dangerous.") + .npcl("How *hic* do I look?") + .playerl("You really don't know, do you? Have a look for yourself.") + .line("The creature stumbles over towards the mirror, focuses upon his", "reflection and...") + .npcl("AAAAARRGGGGHHHH!") + .line("The creature becomes instantly sober, horror all too evident in his", "undead eyes.") + .playerl("I'm sorry. I suppose I'm partly to blame for this.") + .npcl("No - it was him I wager - Fenkenstrain - wasn't it? He's brought me back to life!") + .playerl("Who are - were - you?") + .npcl("I was Rologarth, Lord of the North Coast - this castle was once mine. Fenkenstrain was the castle doctor.") + .playerl("So the castle wasn't really abandoned when he found it?") + .npcl("Is that what he told you? No, no, this castle was once full of people and life. Fenkenstrain advised me to sell them to the vampyres, which - I am sad to say - I did.") + .playerl("I found your brain in a jar in Canifis, so he must have sold you too.") + .npcl("Of that I will not speak. There lie memories that should rest with the dead, the living unable to bear them.") + .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, Quests.CREATURE_OF_FENKENSTRAIN) == 6) { + setQuestStage(player, Quests.CREATURE_OF_FENKENSTRAIN, 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?") + .npcl("It was my birthright, passed down to me through the ages, its origin forgotten.") + .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(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.") + .playerl(FacialExpression.THINKING, "Do you want the ring back? It is yours after all.") + .npcl("No, you keep it, my friend. Werewolves hunger for the scent of live flesh - I have no need for the ring. I have my castle back, if not my soul.") + .end() + } +} \ No newline at end of file diff --git a/Server/src/main/content/region/morytania/quest/creatureoffenkenstrain/RoavarDialogueFile.kt b/Server/src/main/content/region/morytania/quest/creatureoffenkenstrain/RoavarDialogueFile.kt new file mode 100644 index 000000000..330fa642a --- /dev/null +++ b/Server/src/main/content/region/morytania/quest/creatureoffenkenstrain/RoavarDialogueFile.kt @@ -0,0 +1,54 @@ +package content.region.morytania.quest.creatureoffenkenstrain + +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 RoavarDialogueFile(private val dialogueNum: Int = 0) : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + when (stage) { + START_DIALOGUE -> { + if (dialogueNum == 1) { + npcl("If you've got the money, I've got a real treat for you.") + stage = 1 + } else if (dialogueNum == 2) { + npcl("You're interested in our speciality, I see. Would you like to buy some?") + stage = 2 + } else if (dialogueNum == 3) { + npcl("You can leave that alone, my friend. I've already sold you one of your own - eat that. I can't afford to give away freebies in this business!") + stage = END_DIALOGUE + } else { + npcl("Hey! Don't touch that.") + stage = END_DIALOGUE + } + } + 1 -> playerl("What have you got?").also { stage = 3 } + 2 -> playerl("What exactly is in the jar?").also { stage = 3 } + 3 -> npcl("Pickled brain, my friend. Only 50 gold to you.").also { stage++ } + 4 -> playerl("Err... pickled brain from what animal?").also { stage++ } + 5 -> npcl("Animal? Don't be disgusting, man! No, this a human brain - only the best for my customers.").also { stage++ } + 6 -> showTopics( + Topic(FacialExpression.HALF_GUILTY, "I'll buy one.", 10, true), + Topic(FacialExpression.HALF_GUILTY, "I'm not hungry.", 20, true), + ) + 10 -> playerl("I'll buy one, please.").also { stage++ } + 11 -> { + if (amountInInventory(player!!, Items.COINS_995) >= 50) { + if(removeItem(player!!, Item(Items.COINS_995, 50))) { + addItemOrDrop(player!!, Items.PICKLED_BRAIN_4199, 1) + npcl("A very wise choice. Don't eat it all at once, savour every morsel - that's my advice to you.") + } + } else { + sendDialogue(player!!, "You do not have enough coins.") + } + stage = END_DIALOGUE + } + 20 -> playerl("I'm afraid I'm not really hungry at the moment.").also { stage = END_DIALOGUE } + } + } +} \ No newline at end of file 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 d28cefa25..8bfa2330f 100644 --- a/Server/src/main/content/region/morytania/quest/naturespirit/NSDrezelDialogue.kt +++ b/Server/src/main/content/region/morytania/quest/naturespirit/NSDrezelDialogue.kt @@ -1,98 +1,99 @@ package content.region.morytania.quest.naturespirit import core.api.* -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.audio.Audio import core.game.system.task.Pulse import core.game.world.update.flag.context.Animation import core.game.world.update.flag.context.Graphics import org.rs09.consts.Items import 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){ 0 -> options("Sorry, not interested...", "Well, what is it, I may be able to help?").also { stage++ } 1 -> when(buttonID){ - 1 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL, "Sorry, not interested.").also { stage = END_DIALOGUE } - 2 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY, "Well, what is it, I may be able to help?").also { stage++ } + 1 -> playerl(FacialExpression.NEUTRAL, "Sorry, not interested.").also { stage = END_DIALOGUE } + 2 -> playerl(FacialExpression.FRIENDLY, "Well, what is it, I may be able to help?").also { stage++ } } - 2 -> npcl(core.game.dialogue.FacialExpression.HALF_THINKING, "There's a man called Filliman who lives in Mort Myre, I wonder if you could look for him? The swamps of Mort Myre are dangerous though, they're infested with Ghasts!").also { stage++ } + 2 -> npcl(FacialExpression.HALF_THINKING, "There's a man called Filliman who lives in Mort Myre, I wonder if you could look for him? The swamps of Mort Myre are dangerous though, they're infested with Ghasts!").also { stage++ } 3 -> options("Who is this Filliman?", "Where's Mort Myre?", "What's a Ghast?", "Yes, I'll go and look for him.", "Sorry, I don't think I can help.").also { stage++ } 4 -> when(buttonID){ - 1 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Filliman Tarlock is his full name and he's a Druid. He lives in Mort Myre much like a hermit, but there's many a traveller who he's helped.").also { stage-- } - 2 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Mort Myre is a decayed and dangerous swamp to the south. It was once a beautiful forest but has since become filled with vile emanations from within Morytania.").also { stage = 6 } - 3 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "A Ghast is a poor soul who died in Mort Myre. They're undead of a special class, they're untouchable as far as I'm aware!").also { stage = 5 } - 4 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY, "Yes, I'll go and look for him.").also { stage = 10 } - 5 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL, "Sorry, I don't think I can help.").also { stage = END_DIALOGUE } + 1 -> npcl(FacialExpression.NEUTRAL, "Filliman Tarlock is his full name and he's a Druid. He lives in Mort Myre much like a hermit, but there's many a traveller who he's helped.").also { stage-- } + 2 -> npcl(FacialExpression.NEUTRAL, "Mort Myre is a decayed and dangerous swamp to the south. It was once a beautiful forest but has since become filled with vile emanations from within Morytania.").also { stage = 6 } + 3 -> npcl(FacialExpression.NEUTRAL, "A Ghast is a poor soul who died in Mort Myre. They're undead of a special class, they're untouchable as far as I'm aware!").also { stage = 5 } + 4 -> playerl(FacialExpression.FRIENDLY, "Yes, I'll go and look for him.").also { stage = 10 } + 5 -> playerl(FacialExpression.NEUTRAL, "Sorry, I don't think I can help.").also { stage = END_DIALOGUE } } - 5 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Filliman knew how to tackle them, but I've not heard from him in a long time. Ghasts, when they attack, will devour any food you have. If you have no food, they'll draw their nourishment from you!").also { stage = 3 } - 6 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, " We put a fence around it to stop unwary travellers going in. Anyone who dies in the swamp is forever cursed to haunt it as a Ghast. Ghasts attack travellers, turning food to rotten filth.").also { stage = 3 } + 5 -> npcl(FacialExpression.NEUTRAL, "Filliman knew how to tackle them, but I've not heard from him in a long time. Ghasts, when they attack, will devour any food you have. If you have no food, they'll draw their nourishment from you!").also { stage = 3 } + 6 -> npcl(FacialExpression.NEUTRAL, " We put a fence around it to stop unwary travellers going in. Anyone who dies in the swamp is forever cursed to haunt it as a Ghast. Ghasts attack travellers, turning food to rotten filth.").also { stage = 3 } - 10 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "That's great, but it is very dangerous. Are you sure you want to do this?").also { stage++ } + 10 -> npcl(FacialExpression.NEUTRAL, "That's great, but it is very dangerous. Are you sure you want to do this?").also { stage++ } 11 -> options("Yes, I'm sure.", "Sorry, I don't think I can help.").also { stage++ } 12 -> when(buttonID){ - 1 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY, "Yes, I'm sure.").also { stage = 20 } - 2 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL, "Sorry, I don't think I can help.").also { stage = END_DIALOGUE } + 1 -> playerl(FacialExpression.FRIENDLY, "Yes, I'm sure.").also { stage = 20 } + 2 -> playerl(FacialExpression.NEUTRAL, "Sorry, I don't think I can help.").also { stage = END_DIALOGUE } } - 20 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "That's great! Many Thanks! Now then, please be aware of the Ghasts, you cannot attack them, only Filliman knew how to take them on.").also { stage++ } - 21 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Just run from them if you can. If you start to get lost, try to make your way back to the temple.").also { stage++ } + 20 -> npcl(FacialExpression.NEUTRAL, "That's great! Many thanks! Now then, please be aware of the Ghasts, you cannot attack them, only Filliman knew how to take them on.").also { stage++ } + 21 -> npcl(FacialExpression.NEUTRAL, "Just run from them if you can. If you start to get lost, try to make your way back to the temple.").also { stage++ } 22 -> { sendDoubleItemDialogue(player!!, Items.MEAT_PIE_2327, Items.APPLE_PIE_2323, "The cleric hands you some food.") 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(core.game.dialogue.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(core.game.dialogue.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!!) } + 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(Quests.NATURE_SPIRIT).start(player!!) } } } else if(questStage == 15) { when(stage){ - 0 -> playerl(core.game.dialogue.FacialExpression.HALF_GUILTY, "I've found Filliman and you should prepare for some sad news.").also { stage++ } - 1 -> npcl(core.game.dialogue.FacialExpression.HALF_GUILTY, "You mean... he's dead?").also { stage++ } - 2 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL, "Well, er sort of. I got to his camp and I encountered a spirit of some kind. I don't think it was a Ghast, it tried to communicate with me, but made no sense, it was all 'ooooh' this and 'oooh' that.").also { stage++ } - 3 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Hmmm, that's very interesting, I seem to remember Father Aereck in Lumbridge and his predecessor Father Urhney having a similar issue. Though this is probably not related to your problem.").also { stage++ } - 4 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, " I will pray that it wasn't the spirit of my friend Filliman, but some lost soul who needs some help. Please do let me know how you get on with it.").also { stage = END_DIALOGUE } + 0 -> playerl(FacialExpression.HALF_GUILTY, "I've found Filliman and you should prepare for some sad news.").also { stage++ } + 1 -> npcl(FacialExpression.HALF_GUILTY, "You mean... he's dead?").also { stage++ } + 2 -> playerl(FacialExpression.NEUTRAL, "Well, er sort of. I got to his camp and I encountered a spirit of some kind. I don't think it was a Ghast, it tried to communicate with me, but made no sense, it was all 'ooooh' this and 'oooh' that.").also { stage++ } + 3 -> npcl(FacialExpression.NEUTRAL, "Hmmm, that's very interesting, I seem to remember Father Aereck in Lumbridge and his predecessor Father Urhney having a similar issue. Though this is probably not related to your problem.").also { stage++ } + 4 -> npcl(FacialExpression.NEUTRAL, " I will pray that it wasn't the spirit of my friend Filliman, but some lost soul who needs some help. Please do let me know how you get on with it.").also { stage = END_DIALOGUE } } } else if(questStage == 35){ when(stage){ - 0 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY, "Hello again! I'm helping Filliman, he plans to become a nature spirit. I have a spell to cast but first I need to be blessed. Can you bless me?").also { stage++ } - 1 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "But you haven't sneezed!").also { stage++ } - 2 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY, "You're so funny! But can you bless me?").also { stage++ } - 3 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Very well my friend, prepare yourself for the blessings of Saradomin. Here we go!").also { stage++ } + 0 -> playerl(FacialExpression.FRIENDLY, "Hello again! I'm helping Filliman, he plans to become a nature spirit. I have a spell to cast but first I need to be blessed. Can you bless me?").also { stage++ } + 1 -> npcl(FacialExpression.NEUTRAL, "But you haven't sneezed!").also { stage++ } + 2 -> playerl(FacialExpression.FRIENDLY, "You're so funny! But can you bless me?").also { stage++ } + 3 -> npcl(FacialExpression.NEUTRAL, "Very well my friend, prepare yourself for the blessings of Saradomin. Here we go!").also { stage++ } 4 -> { end() - player!!.lock(); + player!!.lock() submitIndividualPulse(player!!, BlessingPulse(npc!!, player!!)) } } } else if(questStage == 40){ - npcl(core.game.dialogue.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 { when(stage){ - 0 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Hello, friend, how goes your quest with Filliman?").also { stage++ } - 1 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL, "Still working at it.").also { stage++ } - 2 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Well enough! Do let me know when something develops!").also { stage = END_DIALOGUE } + 0 -> npcl(FacialExpression.NEUTRAL, "Hello, friend, how goes your quest with Filliman?").also { stage++ } + 1 -> playerl(FacialExpression.NEUTRAL, "Still working at it.").also { stage++ } + 2 -> npcl(FacialExpression.NEUTRAL, "Well enough! Do let me know when something develops!").also { stage = END_DIALOGUE } } } } @@ -104,9 +105,9 @@ private class BlessingPulse(val drezel: NPC, val player: Player) : Pulse(){ override fun pulse(): Boolean { when(ticks){ - 0 -> animate(drezel, 1162).also { spawnProjectile(drezel, player, 268); playAudio(player, Audio(2674)) } + 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 9c1263a1c..0be90563f 100644 --- a/Server/src/main/content/region/morytania/quest/naturespirit/NSListeners.kt +++ b/Server/src/main/content/region/morytania/quest/naturespirit/NSListeners.kt @@ -17,8 +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 { @@ -62,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() @@ -75,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 @@ -100,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.") @@ -114,7 +115,7 @@ class NSListeners : InteractionListener { } on(WASHING_BOWL, IntType.GROUNDITEM, "take"){ player, node -> - SystemLogger.logInfo(this::class.java, "Running listener") + log(this::class.java, Log.FINE, "Running listener") GroundItemManager.create(Item(MIRROR), node.location, player) PickupHandler.take(player, node as GroundItem) return@on true @@ -140,7 +141,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) @@ -181,6 +182,20 @@ class NSListeners : InteractionListener { NSUtils.activatePouch(player, with as MortMyreGhastNPC) } + onUseWith(IntType.NPC, Items.SECATEURS_5329, NPCs.NATURE_SPIRIT_1051) {player, used, with -> + 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.") + return@onUseWith true + } + if (removeItem(player, used) && removeItem(player, Item(Items.COINS_995, 40000))) { + sendItemDialogue(player, Items.MAGIC_SECATEURS_7409, "Your secateurs are enchanted into magic secateurs") + addItem(player, Items.MAGIC_SECATEURS_7409) + } + return@onUseWith true + } + onUseWith(IntType.SCENERY, items, *stones) { player, used, with -> when (used.id) { USED_SPELLCARD -> { @@ -238,7 +253,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 e9332bd8e..d7bdaba72 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,44 +13,45 @@ 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) : core.game.dialogue.DialoguePlugin(player) { +class NSTarlockDialogue(player: Player? = null) : DialoguePlugin(player) { var questStage = 0 - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return NSTarlockDialogue(player) } override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - val quest = player.questRepository.getQuest("Nature Spirit") + val quest = player.questRepository.getQuest(Quests.NATURE_SPIRIT) questStage = quest.getStage(player) if(questStage > 10 && !inEquipment(player, Items.GHOSTSPEAK_AMULET_552)){ - npcl(core.game.dialogue.FacialExpression.HALF_GUILTY, "OooOOOOOOoooOoOOoOOOo") + npcl(FacialExpression.HALF_GUILTY, "OooOOOOOOoooOoOOoOOOo") setQuest(15) return false } when(questStage){ 10, 15 -> sendDialogue("A shifting apparition appears in front of you.").also { stage = 1000 } - 20 -> npcl(core.game.dialogue.FacialExpression.HALF_GUILTY, "Oh, hello there, do you still think I'm dead? It's hard to see how I could be dead when I'm still in the world. I can see everything quite clearly. And nothing of what you say reflects the truth.") - 25 -> npcl(core.game.dialogue.FacialExpression.HALF_GUILTY, "Oh, hello... Sorry, you've caught me at a bad time, it's just that I've had a sign you see and I need to find my journal.").also { stage = 7 } - 30 -> npcl(core.game.dialogue.FacialExpression.HALF_GUILTY, "Thanks for the journal, I've been reading it. It looks like I came to a violent and bitter end but that's not really important. I just have to figure out what I am going to do now?").also { stage = 14 } - 35 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Hello there, have you been blessed yet?").also { stage = 60 } + 20 -> npcl(FacialExpression.HALF_GUILTY, "Oh, hello there, do you still think I'm dead? It's hard to see how I could be dead when I'm still in the world. I can see everything quite clearly. And nothing of what you say reflects the truth.") + 25 -> npcl(FacialExpression.HALF_GUILTY, "Oh, hello... Sorry, you've caught me at a bad time, it's just that I've had a sign you see and I need to find my journal.").also { stage = 7 } + 30 -> npcl(FacialExpression.HALF_GUILTY, "Thanks for the journal, I've been reading it. It looks like I came to a violent and bitter end but that's not really important. I just have to figure out what I am going to do now?").also { stage = 14 } + 35 -> npcl(FacialExpression.NEUTRAL, "Hello there, have you been blessed yet?").also { stage = 60 } 45 -> { if(inInventory(player, Items.MORT_MYRE_FUNGUS_2970)){ - npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Did you manage to get something from nature?").also { stage = 80 } + npcl(FacialExpression.NEUTRAL, "Did you manage to get something from nature?").also { stage = 80 } } else { playerl( - core.game.dialogue.FacialExpression.NEUTRAL, + FacialExpression.NEUTRAL, "Hello, I've been blessed but I don't know what to do now." ).also { stage = 70 } } } - 50 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, " Hello again! I don't suppose you've found out what the other components of the Nature spell are have you?").also { stage = 90 } - else -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, ".......").also { stage = END_DIALOGUE } + 50 -> npcl(FacialExpression.NEUTRAL, " Hello again! I don't suppose you've found out what the other components of the Nature spell are have you?").also { stage = 90 } + else -> npcl(FacialExpression.NEUTRAL, ".......").also { stage = END_DIALOGUE } } return true } @@ -61,103 +61,103 @@ class NSTarlockDialogue(player: Player? = null) : core.game.dialogue.DialoguePlu 0 -> if(inInventory(player, Items.MIRROR_2966)){ sendDialogue("You use the mirror on the spirit","of the dead Filliman Tarlock.").also { stage++ } } else { - playerl(core.game.dialogue.FacialExpression.NEUTRAL, "Yes, I do think you're dead and I'll prove it somehow.").also { stage = 1002 } + playerl(FacialExpression.NEUTRAL, "Yes, I do think you're dead and I'll prove it somehow.").also { stage = 1002 } } - 1 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL, "Here take a look at this, perhaps you can see that you're utterly transparent now!").also { stage++ } + 1 -> playerl(FacialExpression.NEUTRAL, "Here take a look at this, perhaps you can see that you're utterly transparent now!").also { stage++ } 2 -> sendDialogue("The spirit of Filliman reaches forwards and takes the mirror.").also { stage++ } - 3 -> npcl(core.game.dialogue.FacialExpression.HALF_GUILTY, "Well, that is the most peculiar thing I've ever experienced. Strange how well it reflects the stagnant swamp behind me, but there is nothing of my own visage apparent.").also { stage++ } - 4 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL, "That's because you're dead! Dead as a door nail... Deader in fact... You bear a remarkable resemblance to wormbait! Err... No offence...").also { stage++ } - 5 -> npcl(core.game.dialogue.FacialExpression.HALF_GUILTY, "I think you might be right my friend, though I still feel very much alive. It is strange how I still come to be here and yet I've not turned into a Ghast.").also { stage++ } - 6 -> npcl(core.game.dialogue.FacialExpression.HALF_GUILTY, " It must be a sign... Yes a sign... I must try to find out what it means. Now, where did I put my journal?").also { stage++ } + 3 -> npcl(FacialExpression.HALF_GUILTY, "Well, that is the most peculiar thing I've ever experienced. Strange how well it reflects the stagnant swamp behind me, but there is nothing of my own visage apparent.").also { stage++ } + 4 -> playerl(FacialExpression.NEUTRAL, "That's because you're dead! Dead as a door nail... Deader in fact... You bear a remarkable resemblance to wormbait! Err... No offence...").also { stage++ } + 5 -> npcl(FacialExpression.HALF_GUILTY, "I think you might be right my friend, though I still feel very much alive. It is strange how I still come to be here and yet I've not turned into a Ghast.").also { stage++ } + 6 -> npcl(FacialExpression.HALF_GUILTY, " It must be a sign... Yes a sign... I must try to find out what it means. Now, where did I put my journal?").also { stage++ } 7 -> if(!inInventory(player, Items.JOURNAL_2967)){ - playerl(core.game.dialogue.FacialExpression.NEUTRAL, "Where did you put it?").also { stage++; setQuest(25) } + playerl(FacialExpression.NEUTRAL, "Where did you put it?").also { stage++; setQuest(25) } } else sendDialogue("You give the journal to Filliman Tarlock").also { removeItem(player, Items.JOURNAL_2967, Container.INVENTORY); stage = 10; setQuest(30) } //no journal - 8 -> npcl(core.game.dialogue.FacialExpression.HALF_GUILTY, "Well, if I knew that, I wouldn't still be looking for it. However, I do remember something about a knot? Perhaps I was meant to tie a knot or something?").also { stage = END_DIALOGUE } + 8 -> npcl(FacialExpression.HALF_GUILTY, "Well, if I knew that, I wouldn't still be looking for it. However, I do remember something about a knot? Perhaps I was meant to tie a knot or something?").also { stage = END_DIALOGUE } //has journal - 10 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL, "Here, I found this, maybe you can use it?").also { stage++ } - 11 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "My journal! That should help to collect my thoughts.").also { stage++ } + 10 -> playerl(FacialExpression.NEUTRAL, "Here, I found this, maybe you can use it?").also { stage++ } + 11 -> npcl(FacialExpression.FRIENDLY, "My journal! That should help to collect my thoughts.").also { stage++ } 12 -> sendDialogue("~ The spirit starts leafing through the journal. ~", "~ He seems quite distant as he regards the pages. ~", "~ After some time the druid faces you again. ~").also {stage++} - 13 -> npcl(core.game.dialogue.FacialExpression.HALF_GUILTY, "It's all coming back to me now. It looks like I came to a violent and bitter end but that's not important now. I just have to figure out what I am going to do now?").also { stage++ } + 13 -> npcl(FacialExpression.HALF_GUILTY, "It's all coming back to me now. It looks like I came to a violent and bitter end but that's not important now. I just have to figure out what I am going to do now?").also { stage++ } 14 -> options("Being dead, what options do you think you have?", "So, what's your plan?", "Well, good luck with that.", "How can I help?", "Ok thanks.").also { stage++ } 15 -> when(buttonId){ - 1 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL, "Being dead, what options do you think you have? I'm not trying to be rude or anything, but it's not like you have many options is it? I mean, it's either up or down for you isn't it?").also { stage = 20 } - 2 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL, "So, what's your plan?").also { stage = 30 } - 3 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL, "Well, good luck with that.").also { stage = 40 } - 4 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL, "How can I help?").also { stage = 50 } - 5 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL, "Ok thanks.").also { stage = END_DIALOGUE } + 1 -> playerl(FacialExpression.NEUTRAL, "Being dead, what options do you think you have? I'm not trying to be rude or anything, but it's not like you have many options is it? I mean, it's either up or down for you isn't it?").also { stage = 20 } + 2 -> playerl(FacialExpression.NEUTRAL, "So, what's your plan?").also { stage = 30 } + 3 -> playerl(FacialExpression.NEUTRAL, "Well, good luck with that.").also { stage = 40 } + 4 -> playerl(FacialExpression.NEUTRAL, "How can I help?").also { stage = 50 } + 5 -> playerl(FacialExpression.NEUTRAL, "Ok thanks.").also { stage = END_DIALOGUE } } //Being dead, what options - 20 -> npcl(core.game.dialogue.FacialExpression.HALF_GUILTY, "Hmm, well you're a poetic one aren't you. Your material world logic stands you in good stead... If you're standing in the material world...").also { stage = 14 } + 20 -> npcl(FacialExpression.HALF_GUILTY, "Hmm, well you're a poetic one aren't you. Your material world logic stands you in good stead... If you're standing in the material world...").also { stage = 14 } //what's your plan? - 30 -> npcl(core.game.dialogue.FacialExpression.HALF_GUILTY, "In my former incarnation I was Filliman Tarlock, a great druid of some power. I spent many years in this place, which was once a forest and I would wish to protect it as a nature spirit.").also { stage = 14 } + 30 -> npcl(FacialExpression.HALF_GUILTY, "In my former incarnation I was Filliman Tarlock, a great druid of some power. I spent many years in this place, which was once a forest and I would wish to protect it as a nature spirit.").also { stage = 14 } //good luck with that - 40 -> npcl(core.game.dialogue.FacialExpression.HALF_GUILTY, "Won't you help me to become a nature spirit? I could really use your help!").also { stage = 14 } + 40 -> npcl(FacialExpression.HALF_GUILTY, "Won't you help me to become a nature spirit? I could really use your help!").also { stage = 14 } //How can I help? - 50 -> npcl(core.game.dialogue.FacialExpression.HALF_GUILTY, "Will you help me to become a nature spirit? The directions for becoming one are a bit vague, I need three things but I know how to get one of them. Perhaps you can help collect the rest?").also { stage++ } - 51 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL, "I might be interested, what's involved?").also { stage++ } - 52 -> npcl(core.game.dialogue.FacialExpression.HALF_GUILTY, "Well, the book says, that I need, and I quote:- 'Something with faith', 'something from nature' and the 'spirit-to-become' freely given'. Hmm, I know how to get something from nature.").also { stage++ } - 53 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL, "Well, that does seem a bit vague.").also { stage++ } - 54 -> npcl(core.game.dialogue.FacialExpression.HALF_GUILTY, "Hmm, it does and I could understand if you didn't want to help. However, if you could perhaps at least get the item from nature, that would be a start. Perhaps we can figure out the rest as we go along.").also { stage++ } + 50 -> npcl(FacialExpression.HALF_GUILTY, "Will you help me to become a nature spirit? The directions for becoming one are a bit vague, I need three things but I know how to get one of them. Perhaps you can help collect the rest?").also { stage++ } + 51 -> playerl(FacialExpression.NEUTRAL, "I might be interested, what's involved?").also { stage++ } + 52 -> npcl(FacialExpression.HALF_GUILTY, "Well, the book says, that I need, and I quote:- 'Something with faith', 'something from nature' and the 'spirit-to-become' freely given'. Hmm, I know how to get something from nature.").also { stage++ } + 53 -> playerl(FacialExpression.NEUTRAL, "Well, that does seem a bit vague.").also { stage++ } + 54 -> npcl(FacialExpression.HALF_GUILTY, "Hmm, it does and I could understand if you didn't want to help. However, if you could perhaps at least get the item from nature, that would be a start. Perhaps we can figure out the rest as we go along.").also { stage++ } 55 -> sendDialogue(*splitLines("The druid produces a small sheet of papyrus with some writing on it.")).also { addItemOrDrop(player, Items.DRUIDIC_SPELL_2968); setQuest(35); stage++ } - 56 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "This spell needs to be cast in the swamp after you have been blessed. I'm afraid you'll need to go to the temple to the North and ask a member of the clergy to bless you.").also { stage++ } - 57 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL, "Blessed, what does that do?").also { stage++ } - 58 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "It is required if you're to cast this druid spell. Once you've cast the spell, you should find something from nature. Bring it back to me and then we'll try to figure out the other things we need.").also { stage = END_DIALOGUE } + 56 -> npcl(FacialExpression.NEUTRAL, "This spell needs to be cast in the swamp after you have been blessed. I'm afraid you'll need to go to the temple to the North and ask a member of the clergy to bless you.").also { stage++ } + 57 -> playerl(FacialExpression.NEUTRAL, "Blessed, what does that do?").also { stage++ } + 58 -> npcl(FacialExpression.NEUTRAL, "It is required if you're to cast this druid spell. Once you've cast the spell, you should find something from nature. Bring it back to me and then we'll try to figure out the other things we need.").also { stage = END_DIALOGUE } //have you been blessed yet - 60 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL, "No, not yet.").also { stage++ } - 61 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Well, hurry up!").also { stage++ } + 60 -> playerl(FacialExpression.NEUTRAL, "No, not yet.").also { stage++ } + 61 -> npcl(FacialExpression.NEUTRAL, "Well, hurry up!").also { stage++ } 62 -> if(inInventory(player, Items.DRUIDIC_SPELL_2968) || inBank(player, Items.DRUIDIC_SPELL_2968)) end() - else playerl(core.game.dialogue.FacialExpression.NEUTRAL, "Could I have another bloom scroll please?").also { stage++ } - 63 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Sure, but please look after this one.").also { stage++ } + else playerl(FacialExpression.NEUTRAL, "Could I have another bloom scroll please?").also { stage++ } + 63 -> npcl(FacialExpression.NEUTRAL, "Sure, but please look after this one.").also { stage++ } 64 -> sendDialogue("The spirit of Filliman Tarlock gives you another bloom spell.").also { addItemOrDrop(player, Items.DRUIDIC_SPELL_2968); stage = END_DIALOGUE } //I've been blessed - 70 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Well, you need to bring 'something from nature', 'something with faith' and 'something of the spirit-to- become freely given.'").also { stage++ } - 71 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL, "Yeah, but what does that mean?").also { stage++ } - 72 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Hmm, it is a conundrum, however, if you use that spell I gave you, you should be able to get from nature. Once you have that, we may be puzzle the rest out.").also { stage++ } + 70 -> npcl(FacialExpression.NEUTRAL, "Well, you need to bring 'something from nature', 'something with faith' and 'something of the spirit-to- become freely given.'").also { stage++ } + 71 -> playerl(FacialExpression.NEUTRAL, "Yeah, but what does that mean?").also { stage++ } + 72 -> npcl(FacialExpression.NEUTRAL, "Hmm, it is a conundrum, however, if you use that spell I gave you, you should be able to get from nature. Once you have that, we may be puzzle the rest out.").also { stage++ } 73 -> if(!inInventory(player, Items.DRUIDIC_SPELL_2968) && !inBank(player, Items.DRUIDIC_SPELL_2968)){ - playerl(core.game.dialogue.FacialExpression.NEUTRAL, "Could I have another bloom scroll please?").also { stage++ } + playerl(FacialExpression.NEUTRAL, "Could I have another bloom scroll please?").also { stage++ } } else end() - 74 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Sure, but please look after this one.").also { stage++ } + 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 } //has fungus 80 -> sendDialogue("You show the fungus to Filliman.").also { stage++ } - 81 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL, "Yes, I have a fungus here that I picked.").also { stage++ } - 82 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Wonderful, the mushroom represents 'something from nature'. Now we need to work out what the other components of the spell are!").also { stage = 90; setQuest(50) } + 81 -> playerl(FacialExpression.NEUTRAL, "Yes, I have a fungus here that I picked.").also { stage++ } + 82 -> npcl(FacialExpression.NEUTRAL, "Wonderful, the mushroom represents 'something from nature'. Now we need to work out what the other components of the spell are!").also { stage = 90; setQuest(50) } //pre-spell options 90 -> options("What are the things that are needed?", "What should I do when I have those things?", "I think I've solved the puzzle!", "Could I have another bloom scroll please?", "Ok, thanks.").also { stage++ } 91 -> when(buttonId){ - 1 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL, "What are the things that are needed?").also { stage = 100 } - 2 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL, "What should I do when I have those things?").also { stage = 110 } - 3 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY, "I think I've solved the puzzle!").also { stage = 120 } - 4 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY, "Can I have another bloom scroll please?").also { stage = 130 } - 5 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL, "Ok, thanks.").also { stage = END_DIALOGUE } + 1 -> playerl(FacialExpression.NEUTRAL, "What are the things that are needed?").also { stage = 100 } + 2 -> playerl(FacialExpression.NEUTRAL, "What should I do when I have those things?").also { stage = 110 } + 3 -> playerl(FacialExpression.FRIENDLY, "I think I've solved the puzzle!").also { stage = 120 } + 4 -> playerl(FacialExpression.FRIENDLY, "Can I have another bloom scroll please?").also { stage = 130 } + 5 -> playerl(FacialExpression.NEUTRAL, "Ok, thanks.").also { stage = END_DIALOGUE } } //What things are needed? - 100 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "The three things are: 'Something with faith', 'something from nature' and 'something of the spirit-to-become freely given'.").also { stage++ } - 101 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY, " Ok, and 'something from nature' is the mushroom from the bloom spell you gave me?").also { stage++ } - 102 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Yes, that's correct, that seems right to me. The other things we need are 'something with faith' and 'something of the spirit-to-become freely given.").also { stage++ } - 103 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL, "Do you have any ideas what those things are?").also { stage++ } - 104 -> npcl(core.game.dialogue.FacialExpression.HALF_GUILTY, "I'm sorry my friend, but I do not.").also { stage = 90 } + 100 -> npcl(FacialExpression.NEUTRAL, "The three things are: 'Something with faith', 'something from nature' and 'something of the spirit-to-become freely given'.").also { stage++ } + 101 -> playerl(FacialExpression.FRIENDLY, " Ok, and 'something from nature' is the mushroom from the bloom spell you gave me?").also { stage++ } + 102 -> npcl(FacialExpression.FRIENDLY, "Yes, that's correct, that seems right to me. The other things we need are 'something with faith' and 'something of the spirit-to-become freely given.").also { stage++ } + 103 -> playerl(FacialExpression.NEUTRAL, "Do you have any ideas what those things are?").also { stage++ } + 104 -> npcl(FacialExpression.HALF_GUILTY, "I'm sorry my friend, but I do not.").also { stage = 90 } //What should I do when I have them? - 110 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "It says,.. 'to arrange upon three rocks around the spirit-to-become...'. Then I must cast a spell. As you can see, I've already placed the rocks.").also { stage++ } - 111 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL, "Can we just place the components on any rock?").also { stage++ } - 112 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Well, the only thing the journal says is that 'something with faith stands south of the spirit-to-become', but I'm so confused now I don't really know what that means.").also { stage = 90 } + 110 -> npcl(FacialExpression.NEUTRAL, "It says,.. 'to arrange upon three rocks around the spirit-to-become...'. Then I must cast a spell. As you can see, I've already placed the rocks.").also { stage++ } + 111 -> playerl(FacialExpression.NEUTRAL, "Can we just place the components on any rock?").also { stage++ } + 112 -> npcl(FacialExpression.NEUTRAL, "Well, the only thing the journal says is that 'something with faith stands south of the spirit-to-become', but I'm so confused now I don't really know what that means.").also { stage = 90 } //I think I've solved the puzzle! - 120 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Oh really.. Have you placed all the items on the stones? Ok, well, let's try!").also { stage++ } + 120 -> npcl(FacialExpression.NEUTRAL, "Oh really.. Have you placed all the items on the stones? Ok, well, let's try!").also { stage++ } 121 -> sendDialogue("~ The druid attempts to cast a spell. ~").also { stage++ } 122 -> { animate(npc, 812) @@ -180,49 +180,49 @@ class NSTarlockDialogue(player: Player? = null) : core.game.dialogue.DialoguePlu } }) } else { - npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Hmm, something still doesn't seem right. I think we need something more before we can continue.") + npcl(FacialExpression.NEUTRAL, "Hmm, something still doesn't seem right. I think we need something more before we can continue.") } stage = END_DIALOGUE } 130 -> if(inInventory(player, Items.DRUIDIC_SPELL_2968) || inBank(player, Items.DRUIDIC_SPELL_2968)){ - npcl(core.game.dialogue.FacialExpression.NEUTRAL, "No, you've already got one!").also { stage = END_DIALOGUE } + npcl(FacialExpression.NEUTRAL, "No, you've already got one!").also { stage = END_DIALOGUE } } else { - npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Sure, but look after this one.") + npcl(FacialExpression.NEUTRAL, "Sure, but look after this one.") addItem(player, Items.DRUIDIC_SPELL_2968) stage = END_DIALOGUE } //Initial dialogue - 1000 -> playerl(core.game.dialogue.FacialExpression.HALF_ASKING, "Hello?").also { stage++ } + 1000 -> playerl(FacialExpression.HALF_ASKING, "Hello?").also { stage++ } 1001 -> if(inEquipment(player, Items.GHOSTSPEAK_AMULET_552)){ - npcl(core.game.dialogue.FacialExpression.EXTREMELY_SHOCKED, "Oh, I understand you! At last, someone who doesn't just mumble. I understand what you're saying!").also { stage++ } - } else npcl(core.game.dialogue.FacialExpression.HALF_GUILTY, "OooOOoOOoOOOOo.") + npcl(FacialExpression.EXTREMELY_SHOCKED, "Oh, I understand you! At last, someone who doesn't just mumble. I understand what you're saying!").also { stage++ } + } else npcl(FacialExpression.HALF_GUILTY, "OooOOoOOoOOOOo.") 1002 -> options("I'm wearing an amulet of ghost speak!","How long have you been a ghost?", "What's it like being a ghost?", "Ok, thanks.").also { stage++ } 1003 -> when(buttonId){ - 1 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL, "I'm wearing an amulet of ghost speak!").also { stage = 1010; setQuest(20) } - 2 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL, "How long have you been a ghost?").also { stage = 1020; setQuest(20) } - 3 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL, "What's it like being a ghost?").also { stage = 1030; setQuest(20) } - 4 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL, "Ok, thanks.").also { stage = END_DIALOGUE } + 1 -> playerl(FacialExpression.NEUTRAL, "I'm wearing an amulet of ghost speak!").also { stage = 1010; setQuest(20) } + 2 -> playerl(FacialExpression.NEUTRAL, "How long have you been a ghost?").also { stage = 1020; setQuest(20) } + 3 -> playerl(FacialExpression.NEUTRAL, "What's it like being a ghost?").also { stage = 1030; setQuest(20) } + 4 -> playerl(FacialExpression.NEUTRAL, "Ok, thanks.").also { stage = END_DIALOGUE } } - 1010 -> npcl(core.game.dialogue.FacialExpression.HALF_GUILTY, "Why you poor fellow, have you passed away and you want to send a message back to a loved one?").also { stage++ } - 1011 -> playerl(core.game.dialogue.FacialExpression.HALF_THINKING, "Err.. Not exactly...").also { stage++ } - 1012 -> npcl(core.game.dialogue.FacialExpression.HALF_GUILTY, "You have come to haunt my dreams until I pass on your message to a dearly loved one. I understand. Pray, tell me who would you like me to pass a message on to?").also { stage++ } - 1013 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL, "Ermm, you don't understand... It's just that...").also { stage++ } - 1014 -> npcl(core.game.dialogue.FacialExpression.HALF_GUILTY, "Yes!").also { stage++ } - 1015 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL, "Well please don't be upset or anything... But you're the ghost!").also { stage++ } - 1016 -> npcl(core.game.dialogue.FacialExpression.HALF_GUILTY, "Don't be silly now! That in no way reflects the truth!").also { stage = 1002 } + 1010 -> npcl(FacialExpression.HALF_GUILTY, "Why you poor fellow, have you passed away and you want to send a message back to a loved one?").also { stage++ } + 1011 -> playerl(FacialExpression.HALF_THINKING, "Err.. Not exactly...").also { stage++ } + 1012 -> npcl(FacialExpression.HALF_GUILTY, "You have come to haunt my dreams until I pass on your message to a dearly loved one. I understand. Pray, tell me who would you like me to pass a message on to?").also { stage++ } + 1013 -> playerl(FacialExpression.NEUTRAL, "Ermm, you don't understand... It's just that...").also { stage++ } + 1014 -> npcl(FacialExpression.HALF_GUILTY, "Yes!").also { stage++ } + 1015 -> playerl(FacialExpression.NEUTRAL, "Well please don't be upset or anything... But you're the ghost!").also { stage++ } + 1016 -> npcl(FacialExpression.HALF_GUILTY, "Don't be silly now! That in no way reflects the truth!").also { stage = 1002 } - 1020 -> npcl(core.game.dialogue.FacialExpression.HALF_GUILTY, "What?! Don't be preposterous! I'm not a ghost! How could you say something like that?").also { stage++ } - 1021 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL, "But it's true, you're a ghost... well, at least that is to say, you're sort of not alive anymore.").also { stage++ } - 1022 -> npcl(core.game.dialogue.FacialExpression.HALF_GUILTY, "Don't be silly, I can see you. I can see that tree. If I were dead, I wouldn't be able to see anything. What you say just doesn't reflect the truth. You'll have to try harder to put one over on me!").also { stage = 1002 } + 1020 -> npcl(FacialExpression.HALF_GUILTY, "What?! Don't be preposterous! I'm not a ghost! How could you say something like that?").also { stage++ } + 1021 -> playerl(FacialExpression.NEUTRAL, "But it's true, you're a ghost... well, at least that is to say, you're sort of not alive anymore.").also { stage++ } + 1022 -> npcl(FacialExpression.HALF_GUILTY, "Don't be silly, I can see you. I can see that tree. If I were dead, I wouldn't be able to see anything. What you say just doesn't reflect the truth. You'll have to try harder to put one over on me!").also { stage = 1002 } - 1030 -> npcl(core.game.dialogue.FacialExpression.HALF_GUILTY, "Oh, it's quite.... Oh... Trying to catch me out were you! Anyone can clearly see that I am not a ghost!").also { stage++ } - 1031 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL, "But you are a ghost, look at yourself! I can see straight through you! You're as dead as this swamp! Err... No offence or anything...").also { stage++ } - 1032 -> npcl(core.game.dialogue.FacialExpression.HALF_GUILTY, "No I won't take offence because I'm not dead and I'm afraid you'll have to come up with some pretty conclusive proof before I believe it. What a strange dream this is.").also { stage = 1002 } + 1030 -> npcl(FacialExpression.HALF_GUILTY, "Oh, it's quite.... Oh... Trying to catch me out were you! Anyone can clearly see that I am not a ghost!").also { stage++ } + 1031 -> playerl(FacialExpression.NEUTRAL, "But you are a ghost, look at yourself! I can see straight through you! You're as dead as this swamp! Err... No offence or anything...").also { stage++ } + 1032 -> npcl(FacialExpression.HALF_GUILTY, "No I won't take offence because I'm not dead and I'm afraid you'll have to come up with some pretty conclusive proof before I believe it. What a strange dream this is.").also { stage = 1002 } } @@ -234,7 +234,7 @@ class NSTarlockDialogue(player: Player? = null) : core.game.dialogue.DialoguePlu } 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 71ae0c275..c47754946 100644 --- a/Server/src/main/content/region/morytania/quest/naturespirit/NSUtils.kt +++ b/Server/src/main/content/region/morytania/quest/naturespirit/NSUtils.kt @@ -11,6 +11,7 @@ import core.game.world.update.flag.context.Graphics import core.tools.RandomFunction import org.rs09.consts.Items import content.region.morytania.handlers.MortMyreGhastNPC +import org.rs09.consts.Sounds object NSUtils { fun flagFungusPlaced(player: Player) { @@ -84,10 +85,12 @@ object NSUtils { fun castBloom(player: Player): Boolean{ var success = false val region = forId(player.location.regionId) + /* if (player.skills.prayerPoints < 1) { player.packetDispatch.sendMessage("You don't have enough prayer points to do this.") return false } + */ handleVisuals(player) val locs = player.location.surroundingTiles for (o in locs) { @@ -113,6 +116,17 @@ object NSUtils { } } } + if (success) { + player.skills.decrementPrayerPoints(RandomFunction.random(1, 3).toDouble()) + } else { + if (getAttribute(player, "bonecrusher:enabled", false)) { + setAttribute(player, "/save:bonecrusher:enabled",false) + player.packetDispatch.sendMessage("Automatic Bone Burial: Disabled.") + } else { + setAttribute(player, "/save:bonecrusher:enabled",true) + player.packetDispatch.sendMessage("Automatic Bone Burial: Enabled.") + } + } return success } @@ -121,8 +135,7 @@ object NSUtils { * animation. */ private fun handleVisuals(player: Player) { - player.skills.decrementPrayerPoints(RandomFunction.random(1, 3).toDouble()) - player.audioManager.send(1493) + playAudio(player, Sounds.CAST_BLOOM_1493) val AROUND_YOU = player.location.surroundingTiles for (location in AROUND_YOU) { // The graphic is meant to play on a 3x3 radius around you, but not 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 7361405a9..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,12 +12,13 @@ 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) : core.game.dialogue.DialoguePlugin(player){ +class NatureSpiritDialogue(player: Player? = null) : DialoguePlugin(player){ - val questStage = player?.questRepository?.getStage("Nature Spirit") ?: 0 - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + val questStage = player?.questRepository?.getStage(Quests.NATURE_SPIRIT) ?: 0 + override fun newInstance(player: Player?): DialoguePlugin { return NatureSpiritDialogue(player) } @@ -25,43 +26,43 @@ class NatureSpiritDialogue(player: Player? = null) : core.game.dialogue.Dialogue npc = args[0] as NPC when(questStage){ - 60 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Hmm, good, the transformation is complete. Now, my friend, in return for your assistance, I will help you to kill the Ghasts. First bring to me a silver sickle so that I can bless it for you.").also { return true } - 65 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Have you brought me a silver sickle?").also { stage = 100; return true } - 70 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Now you can go forth and make the swamp bloom. Collect nature's bounty to fill a druids pouch. So armed will the Ghasts be bound to you until, you flee or they are defeated.").also { stage = 200 } - 75 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Hello again, my friend. Have you defeated three ghasts as I asked you?").also { stage = 300 } - else -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Welcome to my grotto, friend. Enjoy your visit.").also { stage = END_DIALOGUE } + 60 -> npcl(FacialExpression.NEUTRAL, "Hmm, good, the transformation is complete. Now, my friend, in return for your assistance, I will help you to kill the Ghasts. First bring to me a silver sickle so that I can bless it for you.").also { return true } + 65 -> npcl(FacialExpression.NEUTRAL, "Have you brought me a silver sickle?").also { stage = 100; return true } + 70 -> npcl(FacialExpression.NEUTRAL, "Now you can go forth and make the swamp bloom. Collect nature's bounty to fill a druids pouch. So armed will the Ghasts be bound to you until, you flee or they are defeated.").also { stage = 200 } + 75 -> npcl(FacialExpression.NEUTRAL, "Hello again, my friend. Have you defeated three ghasts as I asked you?").also { stage = 300 } + else -> npcl(FacialExpression.FRIENDLY, "Welcome to my grotto, friend. Enjoy your visit.").also { stage = END_DIALOGUE } } return true } override fun handle(componentID: Int, buttonID: Int): Boolean { when(stage){ - 0 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL,"A silver sickle? What's that?").also { stage++ } - 1 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "The sickle is the symbol and weapon of the Druid, you need to construct one of silver so that I can bless it, with its powers you will be able to defeat the Ghasts of Mort Myre.").also { stage++; setQuest(65) } + 0 -> playerl(FacialExpression.NEUTRAL,"A silver sickle? What's that?").also { stage++ } + 1 -> npcl(FacialExpression.NEUTRAL, "The sickle is the symbol and weapon of the Druid, you need to construct one of silver so that I can bless it, with its powers you will be able to defeat the Ghasts of Mort Myre.").also { stage++; setQuest(65) } 2 -> options("Where would I get a silver sickle?", "What will you do to the silver sickle?", "How can a blessed sickle help me to defeat the Ghasts?", "Ok, thanks.").also { stage++ } 3 -> when(buttonID){ - 1 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL, "Where would I get a silver sickle?").also { stage = 10 } - 2 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL, "What will you do to the silver sickle?").also { stage = 20 } - 3 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL, "How can a blessed sickle help me to defeat the Ghasts?").also { stage = 30 } - 4 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL, "Ok, thanks.").also { stage = END_DIALOGUE } + 1 -> playerl(FacialExpression.NEUTRAL, "Where would I get a silver sickle?").also { stage = 10 } + 2 -> playerl(FacialExpression.NEUTRAL, "What will you do to the silver sickle?").also { stage = 20 } + 3 -> playerl(FacialExpression.NEUTRAL, "How can a blessed sickle help me to defeat the Ghasts?").also { stage = 30 } + 4 -> playerl(FacialExpression.NEUTRAL, "Ok, thanks.").also { stage = END_DIALOGUE } } //where sickle - 10 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "You could make one yourself if you're artisan enough. I've heard of a distant sandy place where you can buy the mould that you require, it's similar in many respects to the creating of a holy symbol.").also { stage = 2 } + 10 -> npcl(FacialExpression.NEUTRAL, "You could make one yourself if you're artisan enough. I've heard of a distant sandy place where you can buy the mould that you require, it's similar in many respects to the creating of a holy symbol.").also { stage = 2 } //What you gonna do to my sickle bro - 20 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Why, I will give it my blessings so that the very swamp in which you stand will blossom and bloom!").also { stage = 2 } //pompous git + 20 -> npcl(FacialExpression.NEUTRAL, "Why, I will give it my blessings so that the very swamp in which you stand will blossom and bloom!").also { stage = 2 } //pompous git //bruh how does a silver sickle help me tho - 30 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "My blessings will entice nature to bloom in Mort Myre! And then with nature's harvest you can fill a druids pouch and release the Ghasts from their torment.").also { stage = 2 } //this dude kinda weird + 30 -> npcl(FacialExpression.NEUTRAL, "My blessings will entice nature to bloom in Mort Myre! And then with nature's harvest you can fill a druids pouch and release the Ghasts from their torment.").also { stage = 2 } //this dude kinda weird //have you brought me a sickle yet bro 100 -> if(inInventory(player, Items.SILVER_SICKLE_2961)){ - playerl(core.game.dialogue.FacialExpression.FRIENDLY, "Yes, here it is. What are you going to do with it?").also { stage = 110 } + playerl(FacialExpression.FRIENDLY, "Yes, here it is. What are you going to do with it?").also { stage = 110 } } else { - playerl(core.game.dialogue.FacialExpression.NEUTRAL, "No sorry, not yet!").also { stage++ } + playerl(FacialExpression.NEUTRAL, "No sorry, not yet!").also { stage++ } } - 101 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Well, come to me when you have it.").also { stage = 2 } + 101 -> npcl(FacialExpression.NEUTRAL, "Well, come to me when you have it.").also { stage = 2 } /** * This dialogue drags on so much man this quest has been like 95% dialogue. @@ -69,7 +70,7 @@ class NatureSpiritDialogue(player: Player? = null) : core.game.dialogue.Dialogue */ //yeah bro I got it - 110 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "My friend, I will bless it for you and you will then be able to accomplish great things. Now then, I must cast the enchantment. You can bless a new sickle by dipping it in the holy water of the grotto.").also { stage++ } + 110 -> npcl(FacialExpression.NEUTRAL, "My friend, I will bless it for you and you will then be able to accomplish great things. Now then, I must cast the enchantment. You can bless a new sickle by dipping it in the holy water of the grotto.").also { stage++ } 111 -> sendDialogue("- The Nature Spirit casts a spell on the player. -").also { stage++ } /** @@ -78,43 +79,43 @@ class NatureSpiritDialogue(player: Player? = null) : core.game.dialogue.Dialogue 112 -> end().also { lock(player, 10); submitWorldPulse(SickleBlessPulse(player, npc)) } //go kill some ghasts bro - 200 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Go forth into Mort Myre and slay three Ghasts. You'll be releasing their souls from Mort Myre.").also { stage++ } + 200 -> npcl(FacialExpression.NEUTRAL, "Go forth into Mort Myre and slay three Ghasts. You'll be releasing their souls from Mort Myre.").also { stage++ } 201 -> sendItemDialogue(player, Items.DRUID_POUCH_2957, "The nature spirit gives you an empty pouch.").also { stage++; setQuest(75) } - 202 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "You'll need this in order to collect together nature's bounty. It will bind the Ghast to you until you flee or it is defeated.").also { stage = END_DIALOGUE } + 202 -> npcl(FacialExpression.NEUTRAL, "You'll need this in order to collect together nature's bounty. It will bind the Ghast to you until you flee or it is defeated.").also { stage = END_DIALOGUE } //Have you killed the ghasts yet bro 300 -> if(NSUtils.getGhastKC(player) >= 3){ - playerl(core.game.dialogue.FacialExpression.NEUTRAL, "Yes, I've killed all three and their spirits have been released!").also { stage = 350 } + playerl(FacialExpression.NEUTRAL, "Yes, I've killed all three and their spirits have been released!").also { stage = 350 } } else { - playerl(core.game.dialogue.FacialExpression.NEUTRAL, "Not yet.").also { stage++ } + playerl(FacialExpression.NEUTRAL, "Not yet.").also { stage++ } } //nah bro - 301 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Well, when you do, please come to me and I'll reward you!").also { stage++ } + 301 -> npcl(FacialExpression.NEUTRAL, "Well, when you do, please come to me and I'll reward you!").also { stage++ } 302 -> options("How do I get to attack the Ghasts?", "What's this pouch for?", "What can I do with this sickle?", "I've lost my sickle.", "Ok, thanks.").also { stage++ } 303 -> when(buttonID){ - 1 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL, "How do I get to attack the Ghasts?").also { stage = 310 } - 2 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL, "What's this pouch for?").also { stage = 320 } - 3 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL, "What can I do with this sickle?").also { stage = 330 } - 4 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL, "I've lost my sickle.").also { stage = 340 } - 5 -> playerl(core.game.dialogue.FacialExpression.NEUTRAL, "Ok, thanks.").also { stage = END_DIALOGUE } + 1 -> playerl(FacialExpression.NEUTRAL, "How do I get to attack the Ghasts?").also { stage = 310 } + 2 -> playerl(FacialExpression.NEUTRAL, "What's this pouch for?").also { stage = 320 } + 3 -> playerl(FacialExpression.NEUTRAL, "What can I do with this sickle?").also { stage = 330 } + 4 -> playerl(FacialExpression.NEUTRAL, "I've lost my sickle.").also { stage = 340 } + 5 -> playerl(FacialExpression.NEUTRAL, "Ok, thanks.").also { stage = END_DIALOGUE } } //How do I attack duh ghosty bois - 310 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Go forth and with the sickle make the swamp bloom. Collect natures bounty to fill a druids pouch. So armed will the Ghasts be bound to you until, you flee or they are defeated.").also { stage = 302 } + 310 -> npcl(FacialExpression.NEUTRAL, "Go forth and with the sickle make the swamp bloom. Collect natures bounty to fill a druids pouch. So armed will the Ghasts be bound to you until, you flee or they are defeated.").also { stage = 302 } //What's dis funny pouch for? - 320 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "It is for collecting natures bounty, once it contains the blossomed items of the swamp, it will make the Ghasts appear and you can then attack them.").also { stage = 302 } + 320 -> npcl(FacialExpression.NEUTRAL, "It is for collecting natures bounty, once it contains the blossomed items of the swamp, it will make the Ghasts appear and you can then attack them.").also { stage = 302 } //What can I do wif da sickle m8 - 330 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "You may use it wisely within the area of Mort Myre to affect natures balance and bring forth a bounty of natures harvest. Once collected into the druid pouch will the Ghast be apparent.").also { stage = 302 } + 330 -> npcl(FacialExpression.NEUTRAL, "You may use it wisely within the area of Mort Myre to affect natures balance and bring forth a bounty of natures harvest. Once collected into the druid pouch will the Ghast be apparent.").also { stage = 302 } //oi I lost it bruv - 340 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "If you should lose the blessed sickle, simply bring another to my altar of nature and refresh it in the grotto waters.").also { stage = 302 } + 340 -> npcl(FacialExpression.NEUTRAL, "If you should lose the blessed sickle, simply bring another to my altar of nature and refresh it in the grotto waters.").also { stage = 302 } //killed all dem buggers bruv - 350 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Many thanks my friend, you have completed your quest!").also { stage++ } - 351 -> end().also { player.questRepository.getQuest("Nature Spirit").finish(player) } + 350 -> npcl(FacialExpression.NEUTRAL, "Many thanks my friend, you have completed your quest!").also { stage++ } + 351 -> end().also { player.questRepository.getQuest(Quests.NATURE_SPIRIT).finish(player) } } return true @@ -146,7 +147,7 @@ class NatureSpiritDialogue(player: Player? = null) : core.game.dialogue.Dialogue 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) : core.game.dialogue.Dialogue } 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 a1d6b7ada..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/ArvelDialogue.kt b/Server/src/main/content/region/tirranwn/dialogue/ArvelDialogue.kt index e0636e6db..221c75a57 100644 --- a/Server/src/main/content/region/tirranwn/dialogue/ArvelDialogue.kt +++ b/Server/src/main/content/region/tirranwn/dialogue/ArvelDialogue.kt @@ -13,31 +13,31 @@ import org.rs09.consts.NPCs */ @Initializable -class ArvelDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class ArvelDialogue(player: Player? = null) : DialoguePlugin(player){ fun gender (male : String = "hero", female : String = "heroine") = if (player.isMale) male else female override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - npcl(core.game.dialogue.FacialExpression.FRIENDLY,"Good day traveller. You are far from home, what brings you here?").also { stage = 0 } + npcl(FacialExpression.FRIENDLY,"Good day traveller. You are far from home, what brings you here?").also { stage = 0 } return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 0 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY, "I am a wandering " + gender() + ". I come here in search of adventure.").also { stage++ } - 1 -> npc(core.game.dialogue.FacialExpression.FRIENDLY, "Sounds ghastly, I just want to live in peace.").also { stage++ } - 2 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY, "Unfortunately without people like me, peace doesn't last for long.").also { stage++ } - 3 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "True, but then again most adventurers cause as much trouble as they put right.").also { stage++ } - 4 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "You've got a point there... Hmm...").also { stage++ } - 5 -> npc(core.game.dialogue.FacialExpression.FRIENDLY, "Well, good day traveller. And always do the right thing.").also { stage = 99 } + 0 -> playerl(FacialExpression.FRIENDLY, "I am a wandering " + gender() + ". I come here in search of adventure.").also { stage++ } + 1 -> npc(FacialExpression.FRIENDLY, "Sounds ghastly, I just want to live in peace.").also { stage++ } + 2 -> playerl(FacialExpression.FRIENDLY, "Unfortunately without people like me, peace doesn't last for long.").also { stage++ } + 3 -> npcl(FacialExpression.FRIENDLY, "True, but then again most adventurers cause as much trouble as they put right.").also { stage++ } + 4 -> player(FacialExpression.FRIENDLY, "You've got a point there... Hmm...").also { stage++ } + 5 -> npc(FacialExpression.FRIENDLY, "Well, good day traveller. And always do the right thing.").also { stage = 99 } 99 -> end() } return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return ArvelDialogue(player) } diff --git a/Server/src/main/content/region/tirranwn/dialogue/DalldavDialogue.kt b/Server/src/main/content/region/tirranwn/dialogue/DalldavDialogue.kt index 06c42f7cf..1b89b65c9 100644 --- a/Server/src/main/content/region/tirranwn/dialogue/DalldavDialogue.kt +++ b/Server/src/main/content/region/tirranwn/dialogue/DalldavDialogue.kt @@ -12,11 +12,11 @@ import org.rs09.consts.NPCs */ @Initializable -class DalldavDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class DalldavDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - npc(core.game.dialogue.FacialExpression.FRIENDLY,"Can I help you at all?").also { stage = 0 } + npc(FacialExpression.FRIENDLY,"Can I help you at all?").also { stage = 0 } return true } @@ -24,21 +24,21 @@ class DalldavDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugi when(stage){ 0 -> options("Yes please. What are you selling?", "Why do you sell this stuff?", "No thanks.").also { stage++ } 1 -> when (buttonId) { - 1 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "Yes please. What are you selling?").also { stage = 10 } - 2 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY, "Why do you sell this stuff? The Crystal Bow is so much better.").also { stage = 20 } - 3 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "No thanks.").also { stage = 99 } + 1 -> player(FacialExpression.FRIENDLY, "Yes please. What are you selling?").also { stage = 10 } + 2 -> playerl(FacialExpression.FRIENDLY, "Why do you sell this stuff? The Crystal Bow is so much better.").also { stage = 20 } + 3 -> player(FacialExpression.FRIENDLY, "No thanks.").also { stage = 99 } } 10 -> end().also { npc.openShop(player) } - 20 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "We keep all these old toys to train our children with, but if people will part with coins for them, then they are theirs!").also { stage = 99 } + 20 -> npcl(FacialExpression.FRIENDLY, "We keep all these old toys to train our children with, but if people will part with coins for them, then they are theirs!").also { stage = 99 } 99 -> end() } return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return DalldavDialogue(player) } diff --git a/Server/src/main/content/region/tirranwn/dialogue/ElfTrackerDialogue.kt b/Server/src/main/content/region/tirranwn/dialogue/ElfTrackerDialogue.kt index 41960ad14..d11634cee 100644 --- a/Server/src/main/content/region/tirranwn/dialogue/ElfTrackerDialogue.kt +++ b/Server/src/main/content/region/tirranwn/dialogue/ElfTrackerDialogue.kt @@ -13,7 +13,7 @@ import org.rs09.consts.NPCs */ @Initializable -class ElfTrackerDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class ElfTrackerDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC @@ -22,9 +22,9 @@ class ElfTrackerDialogue(player: Player? = null) : core.game.dialogue.DialoguePl //temporary booly boi var tracksFound = false if (!tracksFound) { - player(core.game.dialogue.FacialExpression.FRIENDLY, "Hello.").also { stage = 0 } + player(FacialExpression.FRIENDLY, "Hello.").also { stage = 0 } } else { - playerl(core.game.dialogue.FacialExpression.NEUTRAL, "I've found tracks leading off to the west. But they trail off into the trees. Beyond that I am unable to follow.").also { stage = 30} + playerl(FacialExpression.NEUTRAL, "I've found tracks leading off to the west. But they trail off into the trees. Beyond that I am unable to follow.").also { stage = 30} } return true @@ -32,54 +32,54 @@ class ElfTrackerDialogue(player: Player? = null) : core.game.dialogue.DialoguePl override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 0 -> npc(core.game.dialogue.FacialExpression.SUSPICIOUS, "Human! You must be one of Tyras's men...").also { stage++ } - 1 -> playerl(core.game.dialogue.FacialExpression.HALF_GUILTY, "No, I'm " + player.name + "! Lord Iorwerth said you might be able to help me.").also { stage++ } - 2 -> npc(core.game.dialogue.FacialExpression.SUSPICIOUS, "And you have something to prove this?").also { stage++ } + 0 -> npc(FacialExpression.SUSPICIOUS, "Human! You must be one of Tyras's men...").also { stage++ } + 1 -> playerl(FacialExpression.HALF_GUILTY, "No, I'm " + player.username + "! Lord Iorwerth said you might be able to help me.").also { stage++ } + 2 -> npc(FacialExpression.SUSPICIOUS, "And you have something to prove this?").also { stage++ } 3 -> { //todo check quest stages whenever it's added if (player.inventory.contains(Items.CRYSTAL_PENDANT_3208, 1) || player.equipment.contains(Items.CRYSTAL_PENDANT_3208, 1)) { sendDialogue("You show the tracker the crystal pendant.").also { stage = 20 } } else { - player(core.game.dialogue.FacialExpression.HALF_GUILTY, "Well... Err... No.").also { stage = 10 } + player(FacialExpression.HALF_GUILTY, "Well... Err... No.").also { stage = 10 } } } - 10 -> npcl(core.game.dialogue.FacialExpression.ANNOYED, "As I was saying... I have no time for brigands or outlaws.").also { stage = 99 } + 10 -> npcl(FacialExpression.ANNOYED, "As I was saying... I have no time for brigands or outlaws.").also { stage = 99 } - 20 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "That is Lord Iorwerth's pendant. He must have a lot of faith in you. Now, what is it I can help you with?").also { stage ++ } - 21 -> playerl(core.game.dialogue.FacialExpression.ASKING, "I need to find Tyras and kill him. Do you know where his camp is?").also { stage++ } - 22 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Well this was his old camp. After the battle a few days ago they moved. We're yet to find them again.").also { stage++ } - 23 -> player(core.game.dialogue.FacialExpression.ASKING, "Can I help at all?").also { stage++ } - 24 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "As it goes I'm not actually tracking them at the moment. I'm currently trying to trace our renegade brethen instead. This here is the best lead we've found so far.").also { stage++ } - 25 -> player(core.game.dialogue.FacialExpression.HALF_ASKING, "What is?").also { stage++ } - 26 -> npc(core.game.dialogue.FacialExpression.THINKING, "Ahh I guess you can't see it with those human eyes.").also { stage++ } - 27 -> npcl(core.game.dialogue.FacialExpression.THINKING, "I tell you what. Now that you're here I may as well give you a hand. I'll search here on the east side. You check out the west end of the camp.").also { stage++ } - 28 -> npc(core.game.dialogue.FacialExpression.THINKING, "Come and tell me if you find anything.").also { stage = 99 } + 20 -> npcl(FacialExpression.FRIENDLY, "That is Lord Iorwerth's pendant. He must have a lot of faith in you. Now, what is it I can help you with?").also { stage ++ } + 21 -> playerl(FacialExpression.ASKING, "I need to find Tyras and kill him. Do you know where his camp is?").also { stage++ } + 22 -> npcl(FacialExpression.FRIENDLY, "Well this was his old camp. After the battle a few days ago they moved. We're yet to find them again.").also { stage++ } + 23 -> player(FacialExpression.ASKING, "Can I help at all?").also { stage++ } + 24 -> npcl(FacialExpression.FRIENDLY, "As it goes I'm not actually tracking them at the moment. I'm currently trying to trace our renegade brethen instead. This here is the best lead we've found so far.").also { stage++ } + 25 -> player(FacialExpression.HALF_ASKING, "What is?").also { stage++ } + 26 -> npc(FacialExpression.THINKING, "Ahh I guess you can't see it with those human eyes.").also { stage++ } + 27 -> npcl(FacialExpression.THINKING, "I tell you what. Now that you're here I may as well give you a hand. I'll search here on the east side. You check out the west end of the camp.").also { stage++ } + 28 -> npc(FacialExpression.THINKING, "Come and tell me if you find anything.").also { stage = 99 } //todo after finding the tracks - 30 -> npcl(core.game.dialogue.FacialExpression.THINKING, "These forests aren't always as dense as you'd think. If you look closer, you might see ways that you can get through. With that in mind, why don't you give it another go?").also { stage++ } - 31 -> player(core.game.dialogue.FacialExpression.ANNOYED, "Thanks... I'll see what I can find.").also { stage = 99 } + 30 -> npcl(FacialExpression.THINKING, "These forests aren't always as dense as you'd think. If you look closer, you might see ways that you can get through. With that in mind, why don't you give it another go?").also { stage++ } + 31 -> player(FacialExpression.ANNOYED, "Thanks... I'll see what I can find.").also { stage = 99 } //todo after searching past the dense forest - 40 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "Hello.").also { stage++ } - 41 -> npc(core.game.dialogue.FacialExpression.FRIENDLY, "How goes the hunt for that bandit camp?").also { stage++ } + 40 -> player(FacialExpression.FRIENDLY, "Hello.").also { stage++ } + 41 -> npc(FacialExpression.FRIENDLY, "How goes the hunt for that bandit camp?").also { stage++ } 42 -> { //todo (If the camp has not been found) //temporary booly bois var campFound = false if (!campFound) { - player(core.game.dialogue.FacialExpression.FRIENDLY, "Getting there.").also { stage = 99 } + player(FacialExpression.FRIENDLY, "Getting there.").also { stage = 99 } } else { - player(core.game.dialogue.FacialExpression.NEUTRAL, "I found it a short distance west of here.").also { stage++ } + player(FacialExpression.NEUTRAL, "I found it a short distance west of here.").also { stage++ } } } - 43 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "I'm sure Lord Iorwerth will be pleased to hear that. You should let him know.").also { stage = 99 } + 43 -> npcl(FacialExpression.FRIENDLY, "I'm sure Lord Iorwerth will be pleased to hear that. You should let him know.").also { stage = 99 } 99 -> end() } return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return ElfTrackerDialogue(player) } diff --git a/Server/src/main/content/region/tirranwn/dialogue/EoinDialogue.kt b/Server/src/main/content/region/tirranwn/dialogue/EoinDialogue.kt index 585ca84dd..a8bfbe949 100644 --- a/Server/src/main/content/region/tirranwn/dialogue/EoinDialogue.kt +++ b/Server/src/main/content/region/tirranwn/dialogue/EoinDialogue.kt @@ -12,24 +12,24 @@ import org.rs09.consts.NPCs */ @Initializable -class EoinDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class EoinDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - player(core.game.dialogue.FacialExpression.FRIENDLY,"Hello.").also { stage = 0 } + player(FacialExpression.FRIENDLY,"Hello.").also { stage = 0 } return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 0 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Sorry, I cannot stop or Iona will catch me, we are playing tag!").also { stage = 99 } + 0 -> npcl(FacialExpression.FRIENDLY, "Sorry, I cannot stop or Iona will catch me, we are playing tag!").also { stage = 99 } 99 -> end() } return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return EoinDialogue(player) } diff --git a/Server/src/main/content/region/tirranwn/dialogue/EudavGethinDialogue.kt b/Server/src/main/content/region/tirranwn/dialogue/EudavGethinDialogue.kt index abd137377..1d2c93a3e 100644 --- a/Server/src/main/content/region/tirranwn/dialogue/EudavGethinDialogue.kt +++ b/Server/src/main/content/region/tirranwn/dialogue/EudavGethinDialogue.kt @@ -12,11 +12,11 @@ import org.rs09.consts.NPCs */ @Initializable -class EudavGethinDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class EudavGethinDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - npc(core.game.dialogue.FacialExpression.FRIENDLY,"Can I help you at all?").also { stage = 0 } + npc(FacialExpression.FRIENDLY,"Can I help you at all?").also { stage = 0 } return true } @@ -26,7 +26,7 @@ class EudavGethinDialogue(player: Player? = null) : core.game.dialogue.DialogueP 1 -> when(buttonId) { 1 -> end().also { npc.openShop(player) } - 2 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "No thanks.").also { stage = 99 } + 2 -> player(FacialExpression.FRIENDLY, "No thanks.").also { stage = 99 } } 99 -> end() @@ -34,7 +34,7 @@ class EudavGethinDialogue(player: Player? = null) : core.game.dialogue.DialogueP return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return EudavGethinDialogue(player) } diff --git a/Server/src/main/content/region/tirranwn/dialogue/GoreuDialogue.kt b/Server/src/main/content/region/tirranwn/dialogue/GoreuDialogue.kt index 62387565f..3152265e1 100644 --- a/Server/src/main/content/region/tirranwn/dialogue/GoreuDialogue.kt +++ b/Server/src/main/content/region/tirranwn/dialogue/GoreuDialogue.kt @@ -12,27 +12,27 @@ import org.rs09.consts.NPCs */ @Initializable -class GoreuDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class GoreuDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - player(core.game.dialogue.FacialExpression.FRIENDLY,"Hello.").also { stage = 0 } + player(FacialExpression.FRIENDLY,"Hello.").also { stage = 0 } return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 0 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Good day, can I help you?").also { stage++ } - 1 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY, "No thanks I'm just watching the world go by.").also { stage++ } - 2 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Well I can think of no better place to do it, it is beautiful here is it not?").also { stage++ } - 3 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY, "Indeed it is.").also { stage = 99 } + 0 -> npcl(FacialExpression.FRIENDLY, "Good day, can I help you?").also { stage++ } + 1 -> playerl(FacialExpression.FRIENDLY, "No thanks I'm just watching the world go by.").also { stage++ } + 2 -> npcl(FacialExpression.FRIENDLY, "Well I can think of no better place to do it, it is beautiful here is it not?").also { stage++ } + 3 -> playerl(FacialExpression.FRIENDLY, "Indeed it is.").also { stage = 99 } 99 -> end() } return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return GoreuDialogue(player) } diff --git a/Server/src/main/content/region/tirranwn/dialogue/IlfeenDialogue.kt b/Server/src/main/content/region/tirranwn/dialogue/IlfeenDialogue.kt index 991531649..7f5f121d0 100644 --- a/Server/src/main/content/region/tirranwn/dialogue/IlfeenDialogue.kt +++ b/Server/src/main/content/region/tirranwn/dialogue/IlfeenDialogue.kt @@ -14,7 +14,7 @@ import org.rs09.consts.NPCs */ @Initializable -class IlfeenDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class IlfeenDialogue(player: Player? = null) : DialoguePlugin(player){ //temp var var queststage = 0 @@ -23,52 +23,52 @@ class IlfeenDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin npc = args[0] as NPC //todo choose dialogue tree based off of quest stage when(queststage) { - 0 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY,"Good day, what are you doing all the way out here?").also { stage = 0 } - 1 -> playerl(core.game.dialogue.FacialExpression.ASKING, "There is something I meant to ask you about. Do you remember Oaknock? He was a gnome inventor.").also { stage = 10 } - 2 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "Hello again. Are you still offering to chant seeds?").also { stage = 20 } + 0 -> npcl(FacialExpression.FRIENDLY,"Good day, what are you doing all the way out here?").also { stage = 0 } + 1 -> playerl(FacialExpression.ASKING, "There is something I meant to ask you about. Do you remember Oaknock? He was a gnome inventor.").also { stage = 10 } + 2 -> player(FacialExpression.FRIENDLY, "Hello again. Are you still offering to chant seeds?").also { stage = 20 } } return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 0 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY, "I'm just wandering, it's a lovely day for a walk in the woods.").also { stage++ } - 1 -> npc(core.game.dialogue.FacialExpression.FRIENDLY, "It is that.").also { stage++ } - 2 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "Well I shouldn't keep you, see you later.").also { stage++ } - 3 -> npc(core.game.dialogue.FacialExpression.FRIENDLY, "Bye.").also { stage = 99 } + 0 -> playerl(FacialExpression.FRIENDLY, "I'm just wandering, it's a lovely day for a walk in the woods.").also { stage++ } + 1 -> npc(FacialExpression.FRIENDLY, "It is that.").also { stage++ } + 2 -> player(FacialExpression.FRIENDLY, "Well I shouldn't keep you, see you later.").also { stage++ } + 3 -> npc(FacialExpression.FRIENDLY, "Bye.").also { stage = 99 } - 10 -> npcl(core.game.dialogue.FacialExpression.HAPPY, "Of course! I remember him from when I was living in the eastern lands. He was just a budding engineer then. I rather liked the young gnome: he was rather loud, but he had a good soul.").also { stage++ } - 11 -> npcl(core.game.dialogue.FacialExpression.HAPPY, "He was very interested in elven magic. Last time we spoke, not too long ago, he was very interested in illusion magic and how to dispel it. I gave him some advice and a few pieces of equipment to help him.").also { stage++ } - 12 -> playerl(core.game.dialogue.FacialExpression.THINKING, "I think Oaknock died of old age a few hundred years ago now.").also { stage++ } - 13 -> npcl(core.game.dialogue.FacialExpression.HALF_GUILTY, "I keep forgetting, a hundred years is a long time for you.").also { stage++ } - 14 -> player(core.game.dialogue.FacialExpression.ASKING, "Did you ever give him a seed?").also { stage++ } - 15 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Of course. It grew into a saw. I hope he found it useful.").also { stage++ } - 16 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "I think he did! Thanks.").also { stage++ } + 10 -> npcl(FacialExpression.HAPPY, "Of course! I remember him from when I was living in the eastern lands. He was just a budding engineer then. I rather liked the young gnome: he was rather loud, but he had a good soul.").also { stage++ } + 11 -> npcl(FacialExpression.HAPPY, "He was very interested in elven magic. Last time we spoke, not too long ago, he was very interested in illusion magic and how to dispel it. I gave him some advice and a few pieces of equipment to help him.").also { stage++ } + 12 -> playerl(FacialExpression.THINKING, "I think Oaknock died of old age a few hundred years ago now.").also { stage++ } + 13 -> npcl(FacialExpression.HALF_GUILTY, "I keep forgetting, a hundred years is a long time for you.").also { stage++ } + 14 -> player(FacialExpression.ASKING, "Did you ever give him a seed?").also { stage++ } + 15 -> npcl(FacialExpression.FRIENDLY, "Of course. It grew into a saw. I hope he found it useful.").also { stage++ } + 16 -> player(FacialExpression.FRIENDLY, "I think he did! Thanks.").also { stage++ } 17 -> sendDialogue("Ilfeen smiles serenely.").also { stage = 99 } - 20 -> npcl(core.game.dialogue.FacialExpression.ASKING, "I am, but you’ll need your own seed. I can also chant your shield or bow back to full charges if you have it with you. Would you like me to enchant anything?").also { stage++ } - 21 -> npcl(core.game.dialogue.FacialExpression.ASKING, "Or I could give you a book about crystal singing?").also { stage++ } + 20 -> npcl(FacialExpression.ASKING, "I am, but you’ll need your own seed. I can also chant your shield or bow back to full charges if you have it with you. Would you like me to enchant anything?").also { stage++ } + 21 -> npcl(FacialExpression.ASKING, "Or I could give you a book about crystal singing?").also { stage++ } 22 -> options ("Enchant something.", "Crystal singing book.", "Nothing, thanks.").also { stage++ } 23 -> when (buttonId) { //1 -> todo opens interface for enchanting - 2 -> player(core.game.dialogue.FacialExpression.ASKING, "Could I have the book, please?").also { stage = 40 } - 3 -> npc(core.game.dialogue.FacialExpression.FRIENDLY, "Well, good luck.").also { stage = 43 } + 2 -> player(FacialExpression.ASKING, "Could I have the book, please?").also { stage = 40 } + 3 -> npc(FacialExpression.FRIENDLY, "Well, good luck.").also { stage = 43 } } 40 -> { addItemOrDrop(player, Items.CRYSTAL_OF_SEREN_4313, 1) sendDialogue("Ilfeen hands you a book.").also { stage++ } } - 41 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "Thanks, I'll read all about it.").also { stage++ } - 42 -> npc(core.game.dialogue.FacialExpression.FRIENDLY, "Well, good luck.").also { stage++ } - 43 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "Goodbye, Ilfeen.").also { stage = 99 } + 41 -> player(FacialExpression.FRIENDLY, "Thanks, I'll read all about it.").also { stage++ } + 42 -> npc(FacialExpression.FRIENDLY, "Well, good luck.").also { stage++ } + 43 -> player(FacialExpression.FRIENDLY, "Goodbye, Ilfeen.").also { stage = 99 } 99 -> end() } return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return IlfeenDialogue(player) } diff --git a/Server/src/main/content/region/tirranwn/dialogue/IonaDialouge.kt b/Server/src/main/content/region/tirranwn/dialogue/IonaDialouge.kt index 861613954..9ab6bdc25 100644 --- a/Server/src/main/content/region/tirranwn/dialogue/IonaDialouge.kt +++ b/Server/src/main/content/region/tirranwn/dialogue/IonaDialouge.kt @@ -12,24 +12,24 @@ import org.rs09.consts.NPCs */ @Initializable -class IonaDialouge(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class IonaDialouge(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - player(core.game.dialogue.FacialExpression.FRIENDLY,"Hello.").also { stage = 0 } + player(FacialExpression.FRIENDLY,"Hello.").also { stage = 0 } return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 0 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "I can never catch Eoin, he is just too fast, I am always 'It'.").also { stage = 99 } + 0 -> npcl(FacialExpression.FRIENDLY, "I can never catch Eoin, he is just too fast, I am always 'It'.").also { stage = 99 } 99 -> end() } return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return IonaDialouge(player) } diff --git a/Server/src/main/content/region/tirranwn/dialogue/KelynDialogue.kt b/Server/src/main/content/region/tirranwn/dialogue/KelynDialogue.kt index 6081e6a8c..5b19b0be4 100644 --- a/Server/src/main/content/region/tirranwn/dialogue/KelynDialogue.kt +++ b/Server/src/main/content/region/tirranwn/dialogue/KelynDialogue.kt @@ -12,28 +12,28 @@ import org.rs09.consts.NPCs */ @Initializable -class KelynDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class KelynDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - player(core.game.dialogue.FacialExpression.FRIENDLY,"Hello.").also { stage = 0 } + player(FacialExpression.FRIENDLY,"Hello.").also { stage = 0 } return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 0 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Huh... Oh sorry, you made me jump. I was miles away, day dreaming.").also { stage++ } - 1 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "About what may I ask?").also { stage++ } - 2 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "I was thinking about the crystal spires of Prifddinas.").also { stage++ } - 3 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "It must be beautiful, I've only seen the city walls.").also { stage++ } - 4 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "I have never seen it, all I know are the stories. I hope that changes one day.").also { stage = 99 } + 0 -> npcl(FacialExpression.FRIENDLY, "Huh... Oh sorry, you made me jump. I was miles away, day dreaming.").also { stage++ } + 1 -> player(FacialExpression.FRIENDLY, "About what may I ask?").also { stage++ } + 2 -> npcl(FacialExpression.FRIENDLY, "I was thinking about the crystal spires of Prifddinas.").also { stage++ } + 3 -> player(FacialExpression.FRIENDLY, "It must be beautiful, I've only seen the city walls.").also { stage++ } + 4 -> npcl(FacialExpression.FRIENDLY, "I have never seen it, all I know are the stories. I hope that changes one day.").also { stage = 99 } 99 -> end() } return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return KelynDialogue(player) } diff --git a/Server/src/main/content/region/tirranwn/dialogue/MawrthDialogue.kt b/Server/src/main/content/region/tirranwn/dialogue/MawrthDialogue.kt index 0584c99be..db5e0f9d3 100644 --- a/Server/src/main/content/region/tirranwn/dialogue/MawrthDialogue.kt +++ b/Server/src/main/content/region/tirranwn/dialogue/MawrthDialogue.kt @@ -12,29 +12,29 @@ import org.rs09.consts.NPCs */ @Initializable -class MawrthDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class MawrthDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - npcl(core.game.dialogue.FacialExpression.FRIENDLY,"Those children are nothing but trouble - if I did not watch them all the time, Seren knows what trouble they would get in to!").also { stage = 0 } + npcl(FacialExpression.FRIENDLY,"Those children are nothing but trouble - if I did not watch them all the time, Seren knows what trouble they would get in to!").also { stage = 0 } return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 0 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "They look old enough to look after themselves.").also { stage++ } - 1 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "They are only 34 and 38, far too young to be left unsupervised.").also { stage++ } - 2 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "Only!?! How old does that make you?").also { stage++ } - 3 -> npcl(core.game.dialogue.FacialExpression.FRIENDLY, "Has no one told you it is rude to ask a lady her age?").also { stage++ } - 4 -> playerl(core.game.dialogue.FacialExpression.FRIENDLY, "Sorry, I wasn't thinking. Anyway... I'd better stop distracting you.").also { stage++ } - 5 -> npc(core.game.dialogue.FacialExpression.FRIENDLY, "Okay, See you some other time.").also { stage = 99 } + 0 -> player(FacialExpression.FRIENDLY, "They look old enough to look after themselves.").also { stage++ } + 1 -> npcl(FacialExpression.FRIENDLY, "They are only 34 and 38, far too young to be left unsupervised.").also { stage++ } + 2 -> player(FacialExpression.FRIENDLY, "Only!?! How old does that make you?").also { stage++ } + 3 -> npcl(FacialExpression.FRIENDLY, "Has no one told you it is rude to ask a lady her age?").also { stage++ } + 4 -> playerl(FacialExpression.FRIENDLY, "Sorry, I wasn't thinking. Anyway... I'd better stop distracting you.").also { stage++ } + 5 -> npc(FacialExpression.FRIENDLY, "Okay, See you some other time.").also { stage = 99 } 99 -> end() } return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return MawrthDialogue(player) } diff --git a/Server/src/main/content/region/tirranwn/dialogue/OronwenDialogue.kt b/Server/src/main/content/region/tirranwn/dialogue/OronwenDialogue.kt index 9c52f04c4..11729482f 100644 --- a/Server/src/main/content/region/tirranwn/dialogue/OronwenDialogue.kt +++ b/Server/src/main/content/region/tirranwn/dialogue/OronwenDialogue.kt @@ -12,11 +12,11 @@ import org.rs09.consts.NPCs */ @Initializable -class OronwenDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class OronwenDialogue(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - npc(core.game.dialogue.FacialExpression.FRIENDLY,"Hello, can I help?").also { stage = 0 } + npc(FacialExpression.FRIENDLY,"Hello, can I help?").also { stage = 0 } return true } @@ -25,8 +25,8 @@ class OronwenDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugi 0 -> options("Yes please. What are you selling?", "No thanks.").also { stage++ } 1 -> when(buttonId) { - 1 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "Yes please. What are you selling?").also { stage = 10 } - 2 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "No thanks.").also { stage = 99 } + 1 -> player(FacialExpression.FRIENDLY, "Yes please. What are you selling?").also { stage = 10 } + 2 -> player(FacialExpression.FRIENDLY, "No thanks.").also { stage = 99 } } 10 -> end().also { npc.openShop(player) } @@ -36,7 +36,7 @@ class OronwenDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugi return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return OronwenDialogue(player) } diff --git a/Server/src/main/content/region/tirranwn/dialogue/QuarterMasterDialogue.java b/Server/src/main/content/region/tirranwn/dialogue/QuarterMasterDialogue.java index 3bee089c1..b8d7628f1 100644 --- a/Server/src/main/content/region/tirranwn/dialogue/QuarterMasterDialogue.java +++ b/Server/src/main/content/region/tirranwn/dialogue/QuarterMasterDialogue.java @@ -5,6 +5,9 @@ import core.game.node.entity.npc.NPC; 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. * @author Vexia @@ -40,6 +43,10 @@ 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, Quests.REGICIDE)) { + end(); + return true; + } return true; } diff --git a/Server/src/main/content/region/tirranwn/dialogue/TyrasGuard.kt b/Server/src/main/content/region/tirranwn/dialogue/TyrasGuard.kt index 409963af6..076a7eb21 100644 --- a/Server/src/main/content/region/tirranwn/dialogue/TyrasGuard.kt +++ b/Server/src/main/content/region/tirranwn/dialogue/TyrasGuard.kt @@ -12,11 +12,11 @@ import org.rs09.consts.NPCs */ @Initializable -class TyrasGuard(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class TyrasGuard(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - npc(core.game.dialogue.FacialExpression.FRIENDLY,"What is it?").also { stage = 0 } + npc(FacialExpression.FRIENDLY,"What is it?").also { stage = 0 } return true } @@ -24,30 +24,30 @@ class TyrasGuard(player: Player? = null) : core.game.dialogue.DialoguePlugin(pla when(stage){ 0 -> options("What's going on around here?", "Do you know what's south of here?", "I'll leave you alone.").also { stage++ } 1 -> when (buttonId) { - 1 -> player(core.game.dialogue.FacialExpression.ASKING, "What's going on around here?").also { stage = 10 } - 2 -> player(core.game.dialogue.FacialExpression.ASKING, "Do you know what's south of here?").also { stage = 20 } - 3 -> player(core.game.dialogue.FacialExpression.NEUTRAL, "I'll leave you alone.").also { stage = 99 } + 1 -> player(FacialExpression.ASKING, "What's going on around here?").also { stage = 10 } + 2 -> player(FacialExpression.ASKING, "Do you know what's south of here?").also { stage = 20 } + 3 -> player(FacialExpression.NEUTRAL, "I'll leave you alone.").also { stage = 99 } } - 10 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Sorry, I shouldn't give out sensitive information to civilians. You should go to General Hining, in the camp along the road.").also { stage++ } + 10 -> npcl(FacialExpression.NEUTRAL, "Sorry, I shouldn't give out sensitive information to civilians. You should go to General Hining, in the camp along the road.").also { stage++ } 11 -> options("Do you know what's south of here?", "Okay, thanks.").also { stage++ } 12 -> when (buttonId) { - 1 -> player(core.game.dialogue.FacialExpression.ASKING, "Do you know what's south of here?").also { stage = 20 } - 2 -> player(core.game.dialogue.FacialExpression.NEUTRAL, "Ok, thanks.").also { stage = 99 } + 1 -> player(FacialExpression.ASKING, "Do you know what's south of here?").also { stage = 20 } + 2 -> player(FacialExpression.NEUTRAL, "Ok, thanks.").also { stage = 99 } } - 20 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "No. We sent a scouting party in that direction, when we first established our camp. Some of the men got lost in the swamps. Eventually we listed them as dead.").also { stage++ } - 21 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "Then suddenly they returned, with a wild gleam in their eyes, raving about gods and snakes and all kinds of madness.").also { stage++ } - 22 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "We had to drive them out, in case their condition infected the rest of the troops. Their wives will be given a full widow pension when we return home.").also { stage++ } - 23 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL, "General Hining concluded that, whatever is down there, it's not affiliated with any of the elf factions, and it should be left alone.").also { stage++ } - 24 -> player(core.game.dialogue.FacialExpression.FRIENDLY, "Thank you.").also { stage = 0 } + 20 -> npcl(FacialExpression.NEUTRAL, "No. We sent a scouting party in that direction, when we first established our camp. Some of the men got lost in the swamps. Eventually we listed them as dead.").also { stage++ } + 21 -> npcl(FacialExpression.NEUTRAL, "Then suddenly they returned, with a wild gleam in their eyes, raving about gods and snakes and all kinds of madness.").also { stage++ } + 22 -> npcl(FacialExpression.NEUTRAL, "We had to drive them out, in case their condition infected the rest of the troops. Their wives will be given a full widow pension when we return home.").also { stage++ } + 23 -> npcl(FacialExpression.NEUTRAL, "General Hining concluded that, whatever is down there, it's not affiliated with any of the elf factions, and it should be left alone.").also { stage++ } + 24 -> player(FacialExpression.FRIENDLY, "Thank you.").also { stage = 0 } 99 -> end() } return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return TyrasGuard(player) } diff --git a/Server/src/main/content/region/tirranwn/dialogue/TyrasGuardTent.kt b/Server/src/main/content/region/tirranwn/dialogue/TyrasGuardTent.kt index 5fcf504e6..35f8ab51f 100644 --- a/Server/src/main/content/region/tirranwn/dialogue/TyrasGuardTent.kt +++ b/Server/src/main/content/region/tirranwn/dialogue/TyrasGuardTent.kt @@ -12,24 +12,24 @@ import org.rs09.consts.NPCs */ @Initializable -class TyrasGuardTent(player: Player? = null) : core.game.dialogue.DialoguePlugin(player){ +class TyrasGuardTent(player: Player? = null) : DialoguePlugin(player){ override fun open(vararg args: Any?): Boolean { npc = args[0] as NPC - player(core.game.dialogue.FacialExpression.FRIENDLY,"Hello").also { stage = 0 } + player(FacialExpression.FRIENDLY,"Hello").also { stage = 0 } return true } override fun handle(interfaceId: Int, buttonId: Int): Boolean { when(stage){ - 0 -> npcl(core.game.dialogue.FacialExpression.NEUTRAL,"Sorry, can't stop to talk. You should go to General Hining if you need something.").also { stage = 99 } + 0 -> npcl(FacialExpression.NEUTRAL,"Sorry, can't stop to talk. You should go to General Hining if you need something.").also { stage = 99 } 99 -> end() } return true } - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return TyrasGuardTent(player) } 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..5198bbe02 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(); @@ -235,7 +236,7 @@ public class IslwynDialogue extends DialoguePlugin { case 33: switch (buttonId) { case 1: - interpreter.sendDialogues(1680, FacialExpression.HALF_GUILTY, "Ah, very well.", "I will sell you a new bow or shield for 900,000 coins."); + interpreter.sendDialogues(1680, FacialExpression.HALF_GUILTY, "Ah, very well.", "I will make you a new bow from 3,600 Rune Arrows", "or a shield from 24 Rune Kiteshields."); stage = 37; break; case 2: @@ -245,7 +246,7 @@ public class IslwynDialogue extends DialoguePlugin { } break; case 34: - interpreter.sendOptions("Select an Option", "Recharge seed into bow", "Recharge seed into shield"); + interpreter.sendOptions("Select an Option", "Recharge seed into bow with 3000 Rune Arrows", "Recharge seed into shield with 20 Rune Kiteshields"); stage = 35; break; case 35: @@ -266,13 +267,13 @@ public class IslwynDialogue extends DialoguePlugin { stage = 500; } int timesRecharged = player.getAttribute("rovingelves:crystal-equip-recharges", 0); - int price = crystalWeaponPrice(timesRecharged); - if (!player.getInventory().contains(995, price)) { - interpreter.sendDialogue(String.format("You don't have enough coins, you need %d.", price)); + int price = crystalBowPrice(false); + if (!player.getInventory().contains(892, price)) { + interpreter.sendDialogue(String.format("You don't have enough Rune Arrows, you need %d.", price)); stage = 500; } - if (player.getInventory().contains(995, price) && player.getInventory().contains(RovingElves.CRYSTAL_SEED.getId(), 1)) { - if (player.getInventory().remove(RovingElves.CRYSTAL_SEED) && player.getInventory().remove(new Item(995, price))) { + if (player.getInventory().contains(892, price) && player.getInventory().contains(RovingElves.CRYSTAL_SEED.getId(), 1)) { + if (player.getInventory().remove(RovingElves.CRYSTAL_SEED) && player.getInventory().remove(new Item(892, price))) { player.getInventory().add(new Item(4214, 1)); player.incrementAttribute("/save:rovingelves:crystal-equip-recharges", 1); end(); @@ -285,13 +286,13 @@ public class IslwynDialogue extends DialoguePlugin { stage = 500; } timesRecharged = player.getAttribute("rovingelves:crystal-equip-recharges", 0); - price = crystalWeaponPrice(timesRecharged); - if (!player.getInventory().contains(995, price)) { - interpreter.sendDialogue("You don't have enough coins."); + price = crystalShieldPrice(false); + if (!player.getInventory().contains(1201, price)) { + interpreter.sendDialogue(String.format("You don't have enough Rune Kiteshields, you need %d.", price)); stage = 500; } - if (player.getInventory().contains(995, price) && player.getInventory().contains(RovingElves.CRYSTAL_SEED.getId(), 1)) { - if (player.getInventory().remove(RovingElves.CRYSTAL_SEED) && player.getInventory().remove(new Item(995, price))) { + if (player.getInventory().contains(1201, price) && player.getInventory().contains(RovingElves.CRYSTAL_SEED.getId(), 1)) { + if (player.getInventory().remove(RovingElves.CRYSTAL_SEED) && player.getInventory().remove(new Item(1201, price))) { player.getInventory().add(new Item(4225, 1)); player.incrementAttribute("/save:rovingelves:crystal-equip-recharges", 1); end(); @@ -315,13 +316,13 @@ public class IslwynDialogue extends DialoguePlugin { } break; case 39: - price = crystalWeaponPrice(0); - if (!player.getInventory().contains(995, price)) { - interpreter.sendDialogue("You don't have enough coins."); + price = crystalBowPrice(true); + if (!player.getInventory().contains(892, price)) { + interpreter.sendDialogue(String.format("You don't have enough Rune Arrows, you need %d.", price)); stage = 500; } - if (player.getInventory().contains(995, price)) { - if (player.getInventory().remove(new Item(995, price))) { + if (player.getInventory().contains(892, price)) { + if (player.getInventory().remove(new Item(892, price))) { if (!player.getInventory().add(new Item(4212, 1))) { GroundItemManager.create(new Item(4212, 1), player); } @@ -330,13 +331,13 @@ public class IslwynDialogue extends DialoguePlugin { } break; case 40: - price = crystalWeaponPrice(0); - if (!player.getInventory().contains(995, price)) { - interpreter.sendDialogue("You don't have enough coins."); + price = crystalShieldPrice(true); + if (!player.getInventory().contains(1201, price)) { + interpreter.sendDialogue(String.format("You don't have enough Rune Kiteshields, you need %d.", price)); stage = 500; } - if (player.getInventory().contains(995, price)) { - if (player.getInventory().remove(new Item(995, price))) { + if (player.getInventory().contains(1201, price)) { + if (player.getInventory().remove(new Item(1201, price))) { if (!player.getInventory().add(new Item(4224, 1))) { GroundItemManager.create(new Item(4224, 1), player); } @@ -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; @@ -377,7 +378,21 @@ public class IslwynDialogue extends DialoguePlugin { } // 900k for the 0th recharge (or for new bows), decreasing by 180k per recharge down to 180k - public int crystalWeaponPrice(int timesRecharged) { - return Math.max(900000 - 180000 * timesRecharged, 180000); + //public int crystalWeaponPrice(int timesRecharged) { + // return Math.max(900000 - 180000 * timesRecharged, 180000); + //} + public int crystalBowPrice(boolean isNew) { + if (isNew) { + return 3600; + } else { + return 3000; + } + } + public int crystalShieldPrice(boolean isNew) { + if (isNew) { + return 24; + } else { + return 20; + } } } 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 d14ff7bc3..144b624c1 100644 --- a/Server/src/main/content/region/tirranwn/quest/rovingelves/RovingElvesObstacles.java +++ b/Server/src/main/content/region/tirranwn/quest/rovingelves/RovingElvesObstacles.java @@ -14,6 +14,9 @@ import core.plugin.Plugin; 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. * @authors Splinter & downthecrop @@ -94,6 +97,8 @@ public final class RovingElvesObstacles extends OptionHandler { switch (node.getId()) { case 8742: + if (!hasRequirement(player, Quests.MOURNINGS_END_PART_I)) + return true; player.teleport(player.getLocation().transform(EAST_WEST, 2)); break; case 3999: 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 c38db760c..c0a28f080 100644 --- a/Server/src/main/content/region/wilderness/handlers/BorkNPC.java +++ b/Server/src/main/content/region/wilderness/handlers/BorkNPC.java @@ -49,7 +49,7 @@ public class BorkNPC extends AbstractNPC { /** * The chats the legion can say. */ - private static final String[] LEGION_CHATS = new String[] { "For bork!", "Die human!", "Resistance is futile!", "We are the collective!", "From a triangle!!", "Hup! 2... 3... 4!!" }; + private static final String[] LEGION_CHATS = new String[] { "For Bork!", "Die, human!", "Resistance is futile!", "We are the collective!", "Form a triangle!!", "To the attack!", "Hup! 2... 3... 4!!" }; /** * The chance items. @@ -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/ChaosElementalNPC.java b/Server/src/main/content/region/wilderness/handlers/ChaosElementalNPC.java index d9bd9eada..4da67fe93 100644 --- a/Server/src/main/content/region/wilderness/handlers/ChaosElementalNPC.java +++ b/Server/src/main/content/region/wilderness/handlers/ChaosElementalNPC.java @@ -8,7 +8,6 @@ import core.game.node.entity.combat.equipment.SwitchAttack; import core.game.node.entity.impl.Projectile; import core.game.node.entity.npc.AbstractNPC; import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.audio.Audio; import core.game.node.item.Item; import core.game.world.map.Location; import core.game.world.update.flag.context.Animation; @@ -17,8 +16,9 @@ import core.plugin.Initializable; import core.tools.RandomFunction; import core.game.node.entity.combat.CombatSwingHandler; import core.game.node.entity.combat.MultiSwingHandler; +import org.rs09.consts.Sounds; -import static core.api.ContentAPIKt.getPathableRandomLocalCoordinate; +import static core.api.ContentAPIKt.*; /** * Handles the chaos elemental npc. @@ -138,14 +138,14 @@ public class ChaosElementalNPC extends AbstractNPC { return; } if (attack.getProjectile().getProjectileId() == 557) { - player.getAudioManager().send(new Audio(350), true); // C. Elemental Discord Impact SFX + playGlobalAudio(player.getLocation(), Sounds.CHAOS_ELEMENTAL_HIT_351); // C. Elemental Discord Impact SFX } else if (attack.getProjectile().getProjectileId() == 554) { - player.getAudioManager().send(new Audio(346), true); // C. Elemental Confusion Impact SFX + playAudio(player, Sounds.CHAOS_ELEMENTAL_CONFUSION_IMPACT_346); // C. Elemental Confusion Impact SFX Location loc = getPathableRandomLocalCoordinate(player, 10, entity.getLocation(), 3); player.teleport(loc); } else if (attack.getProjectile().getProjectileId() == 551) { - player.getAudioManager().send(new Audio(353), true); // C. Elemental Madness Impact SFX + playGlobalAudio(player.getLocation(), Sounds.CHAOS_ELEMENTAL_MADNESS_IMPACT_353); // C. Elemental Madness Impact SFX if (player.getInventory().freeSlots() < 1 || player.getEquipment().itemCount() < 1) { return; } diff --git a/Server/src/main/content/region/wilderness/handlers/ChaosTunnelZone.java b/Server/src/main/content/region/wilderness/handlers/ChaosTunnelZone.java index 0dbabb18b..6d1f06cfc 100644 --- a/Server/src/main/content/region/wilderness/handlers/ChaosTunnelZone.java +++ b/Server/src/main/content/region/wilderness/handlers/ChaosTunnelZone.java @@ -27,6 +27,9 @@ import core.plugin.Plugin; import core.plugin.ClassScanner; import core.tools.RandomFunction; +import static core.api.ContentAPIKt.hasRequirement; +import content.data.Quests; + /** * Handles the chaos tunnels. * @author Vexia @@ -121,7 +124,7 @@ public final class ChaosTunnelZone extends MapZone implements Plugin { break; case 28779:// portal if (target.getLocation().equals(new Location(3326, 5469, 0))) { - entity.asPlayer().sendMessage("You can't go back throught his portal."); + entity.asPlayer().sendMessage("You can't go back through this portal."); return true; } teleport(entity.asPlayer(), target.asScenery()); @@ -230,8 +233,9 @@ public final class ChaosTunnelZone extends MapZone implements Plugin { * @param player the player. */ private void teleport(Player player, Scenery object) { - if (object.getLocation().getX() == 3142 || object.getLocation().getY() == 5545) { - commenceBorkBattle(player); + if (object.getLocation().getX() == 3142 && object.getLocation().getY() == 5545) { + if (hasRequirement(player, Quests.WHAT_LIES_BELOW)) + commenceBorkBattle(player); return; } Location loc = getLocation(object.getLocation()); @@ -263,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/CorpAreaController.kt b/Server/src/main/content/region/wilderness/handlers/CorpAreaController.kt index 3d168f98d..265a60c9b 100644 --- a/Server/src/main/content/region/wilderness/handlers/CorpAreaController.kt +++ b/Server/src/main/content/region/wilderness/handlers/CorpAreaController.kt @@ -2,6 +2,7 @@ package content.region.wilderness.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 content.global.skill.summoning.familiar.Familiar import core.game.world.map.zone.ZoneBorders @@ -11,7 +12,7 @@ import core.tools.secondsToTicks class CorpAreaController : MapArea, TickListener { companion object { var activePlayers = ArrayList() - var corpBeast: CorporealBeastNPC? = null + var corpBeast: NPC? = null var borders = ZoneBorders(2974, 4369, 3007, 4400) } @@ -26,11 +27,14 @@ class CorpAreaController : MapArea, TickListener { override fun areaEnter(entity: Entity) { if (entity is Player) { activePlayers.add(entity) + if(entity.familiarManager.hasFamiliar()) { + entity.familiarManager.familiar.call() + } } else if (entity is Familiar) { entity.setAttribute("corp-time-remaining", secondsToTicks(10)) //Familiars last about 10 seconds, based on https://www.youtube.com/watch?v=kOd6q5Q5ZKI } - else if (entity is CorporealBeastNPC) { + else if (entity is NPC && entity.behavior is CorporealBeastNPC) { corpBeast = entity } } @@ -45,9 +49,10 @@ class CorpAreaController : MapArea, TickListener { if (activePlayers.size == 0) { corpBeast?.let { it.skills.lifepoints = it.skills.maximumLifepoints - if (it.darkEnergyCore != null) { - it.darkEnergyCore.clear() - it.darkEnergyCore = null + val behavior = it.behavior as CorporealBeastNPC + if (behavior.darkEnergyCore != null) { + behavior.darkEnergyCore.clear() + behavior.darkEnergyCore = null } } } @@ -66,4 +71,4 @@ class CorpAreaController : MapArea, TickListener { } } } -} \ No newline at end of file +} diff --git a/Server/src/main/content/region/wilderness/handlers/CorporealBeastNPC.java b/Server/src/main/content/region/wilderness/handlers/CorporealBeastNPC.java index fe46c728e..67ad9c876 100644 --- a/Server/src/main/content/region/wilderness/handlers/CorporealBeastNPC.java +++ b/Server/src/main/content/region/wilderness/handlers/CorporealBeastNPC.java @@ -2,26 +2,24 @@ 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.*; import core.game.node.entity.combat.ImpactHandler.HitsplatType; 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; import core.game.node.entity.skill.Skills; 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.game.world.update.flag.context.Graphics; import core.plugin.Initializable; -import core.plugin.Plugin; import core.tools.RandomFunction; -import core.game.node.entity.combat.CombatSwingHandler; -import core.game.node.entity.combat.MultiSwingHandler; -import core.game.world.GameWorld; +import org.rs09.consts.NPCs; import java.util.ArrayList; import java.util.List; @@ -29,116 +27,116 @@ import java.util.List; /** * Handles the Corporeal beast NPC. * @author Emperor - * */ @Initializable -public final class CorporealBeastNPC extends AbstractNPC { +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() { - this(8133, Location.create(2993, 4380, 2)); - } - - /** - * Constructs a new {@code CorporealBeastNPC} {@code Object}. - * @param id The NPC id. - * @param location The location. - */ - public CorporealBeastNPC(int id, Location location) { - super(id, location); - } - - @Override - public void init() { - super.init(); - configureBossData(); + super(new int[] { NPCs.CORPOREAL_BEAST_8133 }); } @Override - public CombatSwingHandler getSwingHandler(boolean swing) { + public void onCreation(NPC self) { + self.configureBossData(); + } + + @Override + public CombatSwingHandler getSwingHandlerOverride(NPC self, CombatSwingHandler original) { return combatHandler; } @Override - public AbstractNPC construct(int id, Location location, Object... objects) { - return new CorporealBeastNPC(id, location); - } - - @Override - public double getFormattedHit(BattleState state, int hit) { - if (hit > 100) { - hit = 100; + 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); } - return super.getFormattedHit(state, hit); } @Override - public int[] getIds() { - return new int[] { 8133 }; - } - - @Override - public void finalizeDeath(Entity killer) { - super.finalizeDeath(killer); - BossKillCounter.addtoKillcount((Player) killer, this.getId()); + public void onDeathFinished(NPC self, Entity killer) { + BossKillCounter.addtoKillcount((Player) killer, NPCs.CORPOREAL_BEAST_8133); if (darkEnergyCore != null) { darkEnergyCore.clear(); darkEnergyCore = null; } } - - @Override - public Plugin newInstance(Object arg) throws Throwable { - init(); - return super.newInstance(arg); - } /** * 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((CorporealBeastNPC) entity, 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(); @@ -152,27 +150,28 @@ public final class CorporealBeastNPC extends AbstractNPC { } 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(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 = npc.getSkills().getMaximumLifepoints() * (0.3 + (npc.getViewport().getCurrentPlane().getPlayers().size() * 0.05)); - if (npc.getSkills().getLifepoints() > max) { + if (!RandomFunction.roll(8)) { return; } Location l = RegionManager.getTeleportLocation(victim.getLocation(), 3); - npc.darkEnergyCore = NPC.create(8127, l, npc); + npc.darkEnergyCore = NPC.create(8127, l, corp); npc.darkEnergyCore.setActive(true); - Projectile.create(npc.getLocation().transform(2, 2, 0), l, 1828, 60, 0, 0, 60, 20, 0).send(); - GameWorld.getPulser().submit(new Pulse(2, npc) { + Projectile.create(corp.getLocation().transform(2, 2, 0), l, 1828, 60, 0, 0, 60, 20, 0).send(); + GameWorld.getPulser().submit(new Pulse(2, corp) { @Override public boolean pulse() { + if (npc.darkEnergyCore == null) + return true; npc.darkEnergyCore.init(); return true; } @@ -192,6 +191,7 @@ public final class CorporealBeastNPC extends AbstractNPC { boolean secondStage = false; List players = RegionManager.getLocalPlayers(entity); Location[] locations = null; + @Override public boolean pulse() { if (!secondStage) { @@ -223,11 +223,14 @@ public final class CorporealBeastNPC extends AbstractNPC { 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); } @@ -260,7 +263,7 @@ public final class CorporealBeastNPC extends AbstractNPC { } return false; } - + @Override public void adjustBattleState(Entity entity, Entity victim, BattleState state) { super.adjustBattleState(entity, victim, state); @@ -270,10 +273,9 @@ public final class CorporealBeastNPC extends AbstractNPC { 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 { @@ -286,11 +288,13 @@ public final class CorporealBeastNPC extends AbstractNPC { } } } - + @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 fe4fceb46..5568d5460 100644 --- a/Server/src/main/content/region/wilderness/handlers/CorporealBeastWarningInterface.kt +++ b/Server/src/main/content/region/wilderness/handlers/CorporealBeastWarningInterface.kt @@ -4,6 +4,8 @@ import core.game.component.Component 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 @@ -15,6 +17,8 @@ class CorporealBeastWarningInterface : InterfaceListener { override fun defineInterfaceListeners() { on(COMPONENT_ID,17){player,component,_,_,_,_ -> + 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) } player.setAttribute("corp-beast-cave-delay", GameWorld.ticks + 5) @@ -33,4 +37,4 @@ class CorporealBeastWarningInterface : InterfaceListener { fun close(player: Player,component: Component){ player.interfaceManager.close(component) } -} \ No newline at end of file +} diff --git a/Server/src/main/content/region/wilderness/handlers/DarkEnergyCoreNPC.java b/Server/src/main/content/region/wilderness/handlers/DarkEnergyCoreNPC.java index 3b35a1e36..0695955ba 100644 --- a/Server/src/main/content/region/wilderness/handlers/DarkEnergyCoreNPC.java +++ b/Server/src/main/content/region/wilderness/handlers/DarkEnergyCoreNPC.java @@ -6,17 +6,19 @@ 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.state.EntityState; 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; +import static core.api.ContentAPIKt.*; + /** * Handles the Dark Energy Core NPC. - * @author Emperor - * + * @author Emperor, Player Name */ @Initializable public final class DarkEnergyCoreNPC extends AbstractNPC { @@ -30,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. */ @@ -58,19 +61,20 @@ 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++; - boolean poisoned = getStateManager().hasState(EntityState.POISONED); - if (getStateManager().hasState(EntityState.STUNNED) || isInvisible()) { + boolean poisoned = isPoisoned(this); + if (isStunned(this) || isInvisible()) { return; } if (fails == 0 && poisoned && (ticks % 100) != 0) { @@ -90,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; @@ -118,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/KingBlackDragonNPC.java b/Server/src/main/content/region/wilderness/handlers/KingBlackDragonNPC.java index d7dd2c7b5..ab5559616 100644 --- a/Server/src/main/content/region/wilderness/handlers/KingBlackDragonNPC.java +++ b/Server/src/main/content/region/wilderness/handlers/KingBlackDragonNPC.java @@ -20,6 +20,8 @@ import core.plugin.Initializable; import core.plugin.Plugin; import core.tools.RandomFunction; +import static core.api.ContentAPIKt.calculateDragonfireMaxHit; + /** * Represents the King Black Dragon NPC. * @author Emperor @@ -28,236 +30,216 @@ import core.tools.RandomFunction; @Initializable public final class KingBlackDragonNPC extends AbstractNPC { - /** - * The default spawn location. - */ - private static final Location DEFAULT_SPAWN = Location.create(2273, 4698, 0); + /** + * The default spawn location. + */ + private static final Location DEFAULT_SPAWN = Location.create(2273, 4698, 0); - /** - * The combat swing handler. - */ - private CombatSwingHandler combatHandler = new KBDCombatSwingHandler(); + /** + * The combat swing handler. + */ + private CombatSwingHandler combatHandler = new KBDCombatSwingHandler(); - - public KingBlackDragonNPC() { - super(-1,null); - } + + public KingBlackDragonNPC() { + super(-1,null); + } - @Override - public void finalizeDeath(Entity killer) { - super.finalizeDeath(killer); - BossKillCounter.addtoKillcount((Player) killer, this.getId()); - } + @Override + public void finalizeDeath(Entity killer) { + super.finalizeDeath(killer); + BossKillCounter.addtoKillcount((Player) killer, this.getId()); + } - /** - * Constructs a new {@code KingBlackDragonNPC} {@code Object}. - * @param id the id. - * @param l the l. - */ - public KingBlackDragonNPC(int id, Location l) { - super(id, l); - } + /** + * Constructs a new {@code KingBlackDragonNPC} {@code Object}. + * @param id the id. + * @param l the l. + */ + public KingBlackDragonNPC(int id, Location l) { + super(id, l); + } - @Override - public void init() { - super.init(); - configureBossData(); - } + @Override + public void init() { + super.init(); + configureBossData(); + } - @Override - public AbstractNPC construct(int id, Location location, Object... objects) { - return new KingBlackDragonNPC(id, location); - } + @Override + public AbstractNPC construct(int id, Location location, Object... objects) { + return new KingBlackDragonNPC(id, location); + } - @Override - public int getDragonfireProtection(boolean fire) { - return 0x2 | 0x4 | 0x8; - } + @Override + public int getDragonfireProtection(boolean fire) { + return 0x2 | 0x4 | 0x8; + } - @Override - public int[] getIds() { - return new int[] { 50 }; - } + @Override + public int[] getIds() { + return new int[] { 50 }; + } - @Override - public CombatSwingHandler getSwingHandler(boolean swing) { - return combatHandler; - } + @Override + public CombatSwingHandler getSwingHandler(boolean swing) { + return combatHandler; + } - @Override - public Plugin newInstance(Object arg) throws Throwable { - return super.newInstance(arg); - } + @Override + public Plugin newInstance(Object arg) throws Throwable { + return super.newInstance(arg); + } - /** - * Handles the King Black Dragon's combat swings. - * @author Emperor - */ - static class KBDCombatSwingHandler extends CombatSwingHandler { + /** + * Handles the King Black Dragon's combat swings. + * @author Emperor + */ + static class KBDCombatSwingHandler extends CombatSwingHandler { - /** - * The style. - */ - private CombatStyle style = CombatStyle.RANGE; - - /** - * Dragonfire. - */ - private static final DragonfireSwingHandler DRAGONFIRE = new DragonfireSwingHandler(false, 56, null, true); - - /** - * The melee attack animation. - */ - private static final Animation MELEE_ATTACK = new Animation(80, Priority.HIGH); - - /** - * The fire type. - */ - private FireType fireType = FireType.FIERY_BREATH; - - /** - * Constructs a new {@code KBDCombatSwingHandler} {@Code Object}. + /** + * The style. */ - public KBDCombatSwingHandler() { - super(CombatStyle.RANGE); - } + private CombatStyle style = CombatStyle.RANGE; - @Override - public void adjustBattleState(Entity entity, Entity victim, BattleState state) { - if (style == CombatStyle.RANGE) { - fireType.getTask().exec(victim, entity); - state.setStyle(null); - DRAGONFIRE.adjustBattleState(entity, victim, state); - state.setStyle(CombatStyle.RANGE); - return; - } - style.getSwingHandler().adjustBattleState(entity, victim, state); - } + /** + * Dragonfire. + */ + private static final DragonfireSwingHandler DRAGONFIRE = new DragonfireSwingHandler(false, 56, null, true); - @Override - public int calculateAccuracy(Entity entity) { - if (style == CombatStyle.MELEE) { - return style.getSwingHandler().calculateAccuracy(entity); - } - return CombatStyle.MAGIC.getSwingHandler().calculateAccuracy(entity); - } + /** + * The melee attack animation. + */ + private static final Animation MELEE_ATTACK = new Animation(80, Priority.HIGH); - @Override - public int calculateDefence(Entity victim, Entity attacker) { - if (style == CombatStyle.MELEE) { - return style.getSwingHandler().calculateDefence(victim, attacker); - } - return CombatStyle.MAGIC.getSwingHandler().calculateDefence(victim, attacker); - } + /** + * The fire type. + */ + private FireType fireType = FireType.FIERY_BREATH; - @Override - public int calculateHit(Entity entity, Entity victim, double modifier) { - if (style == CombatStyle.MELEE) { - return style.getSwingHandler().calculateHit(entity, victim, modifier); - } - int max = 56; - int damage = max; - if (victim instanceof Player) { - int val = victim.getDragonfireProtection(true); - if ((val & 0x2) != 0) { - damage *= 0.5; - } - if ((val & 0x4) != 0) { - damage -= (int) (damage * 0.85); - } - if ((val & 0x8) != 0) { - damage *= 0.6; - } - if (damage < 3) { - damage = 3; - } - } - if (fireType != FireType.FIERY_BREATH) { - damage += 11; - } - return damage; - } + /** + * Constructs a new {@code KBDCombatSwingHandler} {@Code Object}. + */ + public KBDCombatSwingHandler() { + super(CombatStyle.RANGE); + } - @Override - public InteractionType canSwing(Entity entity, Entity victim) { - if (!isProjectileClipped(entity, victim, false)) { - return InteractionType.NO_INTERACT; - } - if (victim.getCenterLocation().withinDistance(entity.getCenterLocation(), getCombatDistance(entity, victim, 9)) && super.canSwing(entity, victim) == InteractionType.STILL_INTERACT) { - entity.getWalkingQueue().reset(); - return InteractionType.STILL_INTERACT; - } - return InteractionType.NO_INTERACT; - } + @Override + public void adjustBattleState(Entity entity, Entity victim, BattleState state) { + if (style == CombatStyle.RANGE) { + fireType.getTask().exec(victim, entity); + state.setStyle(null); + DRAGONFIRE.adjustBattleState(entity, victim, state); + state.setStyle(CombatStyle.RANGE); + return; + } + style.getSwingHandler().adjustBattleState(entity, victim, state); + } - @Override - public ArmourSet getArmourSet(Entity e) { - return style.getSwingHandler().getArmourSet(e); - } + @Override + public int calculateAccuracy(Entity entity) { + if (style == CombatStyle.MELEE) { + return style.getSwingHandler().calculateAccuracy(entity); + } + return CombatStyle.MAGIC.getSwingHandler().calculateAccuracy(entity); + } - @Override - public double getSetMultiplier(Entity e, int skillId) { - return style.getSwingHandler().getSetMultiplier(e, skillId); - } + @Override + public int calculateDefence(Entity victim, Entity attacker) { + if (style == CombatStyle.MELEE) { + return style.getSwingHandler().calculateDefence(victim, attacker); + } + return CombatStyle.MAGIC.getSwingHandler().calculateDefence(victim, attacker); + } - @Override - public void impact(Entity entity, Entity victim, BattleState state) { - style.getSwingHandler().impact(entity, victim, state); - } + @Override + public int calculateHit(Entity entity, Entity victim, double modifier) { + if (style == CombatStyle.MELEE) { + return style.getSwingHandler().calculateHit(entity, victim, modifier); + } + return calculateDragonfireMaxHit(victim, 56, false, fireType != FireType.FIERY_BREATH ? 10 : 0, true); + } - @Override - public int swing(Entity entity, Entity victim, BattleState state) { - style = CombatStyle.RANGE; - int hit = 0; - int ticks = 1; - if (victim.getCenterLocation().withinDistance(entity.getCenterLocation(), getCombatDistance(entity, victim, 1)) && RandomFunction.random(10) < 7) { - style = CombatStyle.MELEE; - } else { - ticks += (int) Math.ceil(entity.getLocation().getDistance(victim.getLocation()) * 0.3); - } - fireType = FireType.values()[RandomFunction.random(FireType.values().length)]; - state.setStyle(style); - if (isAccurateImpact(entity, victim)) { - int max = calculateHit(entity, victim, 1.0); - state.setMaximumHit(max); - hit = RandomFunction.random(max); - } - state.setEstimatedHit(hit); - ((NPC) entity).getAggressiveHandler().setPauseTicks(2); - return ticks; - } + @Override + public InteractionType canSwing(Entity entity, Entity victim) { + if (!isProjectileClipped(entity, victim, false)) { + return InteractionType.NO_INTERACT; + } + if (victim.getCenterLocation().withinMaxnormDistance(entity.getCenterLocation(), getCombatDistance(entity, victim, 9)) && super.canSwing(entity, victim) == InteractionType.STILL_INTERACT) { + entity.getWalkingQueue().reset(); + return InteractionType.STILL_INTERACT; + } + return InteractionType.NO_INTERACT; + } - @Override - public void visualize(Entity entity, Entity victim, BattleState state) { - switch (style) { - case MELEE: - entity.animate(MELEE_ATTACK); - break; - case RANGE: - Projectile.ranged(entity, victim, fireType.getProjectileId(), 20, 36, 50, 15).send(); - entity.animate(fireType.getAnimation()); - break; - default: - break; - } - } + @Override + public ArmourSet getArmourSet(Entity e) { + return style.getSwingHandler().getArmourSet(e); + } - @Override - public void visualizeImpact(Entity entity, Entity victim, BattleState state) { - if (style != CombatStyle.MELEE) { - DRAGONFIRE.visualizeImpact(entity, victim, state); - } else { - style.getSwingHandler().visualizeImpact(entity, victim, state); - } - } + @Override + public double getSetMultiplier(Entity e, int skillId) { + return style.getSwingHandler().getSetMultiplier(e, skillId); + } - /** - * Gets the fire type. - * @return the type. - */ - public FireType getFireType() { - return fireType; - } + @Override + public void impact(Entity entity, Entity victim, BattleState state) { + style.getSwingHandler().impact(entity, victim, state); + } - } + @Override + public int swing(Entity entity, Entity victim, BattleState state) { + style = CombatStyle.RANGE; + int hit = 0; + int ticks = 1; + if (victim.getCenterLocation().withinMaxnormDistance(entity.getCenterLocation(), getCombatDistance(entity, victim, 1)) && RandomFunction.random(10) < 7) { + style = CombatStyle.MELEE; + } else { + ticks += (int) Math.ceil(entity.getLocation().getDistance(victim.getLocation()) * 0.3); + } + fireType = FireType.values()[RandomFunction.random(FireType.values().length)]; + state.setStyle(style); + if (isAccurateImpact(entity, victim)) { + int max = calculateHit(entity, victim, 1.0); + state.setMaximumHit(max); + hit = RandomFunction.random(max + 1); + } + state.setEstimatedHit(hit); + ((NPC) entity).getAggressiveHandler().setPauseTicks(2); + return ticks; + } + + @Override + public void visualize(Entity entity, Entity victim, BattleState state) { + switch (style) { + case MELEE: + entity.animate(MELEE_ATTACK); + break; + case RANGE: + Projectile.ranged(entity, victim, fireType.getProjectileId(), 20, 36, 50, 15).send(); + entity.animate(fireType.getAnimation()); + break; + default: + break; + } + } + + @Override + public void visualizeImpact(Entity entity, Entity victim, BattleState state) { + if (style != CombatStyle.MELEE) { + DRAGONFIRE.visualizeImpact(entity, victim, state); + } else { + style.getSwingHandler().visualizeImpact(entity, victim, state); + } + } + + /** + * Gets the fire type. + * @return the type. + */ + public FireType getFireType() { + return fireType; + } + + } } diff --git a/Server/src/main/content/region/wilderness/handlers/SkulledState.kt b/Server/src/main/content/region/wilderness/handlers/SkulledState.kt deleted file mode 100644 index a23fba064..000000000 --- a/Server/src/main/content/region/wilderness/handlers/SkulledState.kt +++ /dev/null @@ -1,45 +0,0 @@ -package content.region.wilderness.handlers - -import core.game.node.entity.player.Player -import core.game.system.task.Pulse -import org.json.simple.JSONObject -import core.game.node.entity.state.PlayerState -import core.game.node.entity.state.State - -@PlayerState("skull") -class SkulledState(player: Player? = null) : State(player) { - var ticksLeft = 2000 - - override fun save(root: JSONObject) { - root.put("ticksLeft",ticksLeft) - } - - override fun parse(_data: JSONObject) { - if(_data.containsKey("ticksLeft")){ - ticksLeft = _data["ticksLeft"].toString().toInt() - } - } - - override fun newInstance(player: Player?): State { - return SkulledState(player) - } - - override fun createPulse() { - player ?: return - if(ticksLeft <= 0) return - player.skullManager.setSkullIcon(0) - player.skullManager.isSkulled = true - pulse = object : Pulse(){ - override fun pulse(): Boolean { - ticksLeft-- - if(ticksLeft <= 0) { - player.skullManager.reset() - pulse = null - return true - } - return false - } - } - } - -} \ 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 new file mode 100644 index 000000000..2f0ed6a1f --- /dev/null +++ b/Server/src/main/content/region/wilderness/handlers/WildernessGateHandler.kt @@ -0,0 +1,73 @@ +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.Node +import core.game.global.action.DoorActionHandler +import core.game.dialogue.* +import core.tools.* + +class WildernessGateHandler : InteractionListener { + val gates = intArrayOf(1597, 1596) + + override fun defineListeners() { + on (gates, IntType.SCENERY, "open", handler = ::handleGate) + } + + 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 { + DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) + player.skullManager.isDeepWilderness = false + } + } + } else DoorActionHandler.handleAutowalkDoor(player, node.asScenery()) + + return true + } + + class GateDialogue(val callback: (Player) -> Unit, val firstLine: String) : DialogueFile() { + override fun handle (interfaceId: Int, buttonId: Int) { + when (stage) { + 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, "I wish to proceed, and don't show this warning again.", 11, true), + Topic(FacialExpression.NEUTRAL, "Never mind.", END_DIALOGUE, true) + ) + 10 -> { + end() + 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/WildernessInterfacePlugin.java b/Server/src/main/content/region/wilderness/handlers/WildernessInterfacePlugin.java index 6fb35af71..6500ccfef 100644 --- a/Server/src/main/content/region/wilderness/handlers/WildernessInterfacePlugin.java +++ b/Server/src/main/content/region/wilderness/handlers/WildernessInterfacePlugin.java @@ -12,6 +12,9 @@ import core.game.node.scenery.Scenery; 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.playAudio; /** * Represents the wilderness interface plugin. @@ -67,7 +70,7 @@ public final class WildernessInterfacePlugin extends ComponentPlugin { ForceMovement.run(player, Location.create(l.getX() - 1, y, 0), Location.create(l.getX() + 2, y, 0), ANIMATION, 20).setEndAnimation(null); } } - player.getAudioManager().send(new Audio(2462, 10, 30)); + playAudio(player, Sounds.JUMP2_2462, 30); /*if (GameWorld.getSettings().isPvp()) { ActivityManager.register(new BountyHunterActivity(CraterType.MID_LEVEL)); ActivityManager.register(new BountyHunterActivity(CraterType.HIGH_LEVEL)); @@ -92,7 +95,7 @@ public final class WildernessInterfacePlugin extends ComponentPlugin { ForceMovement.run(player, Location.create(l.getX() - 1, y, 0), Location.create(l.getX() + 2, y, 0), ANIMATION, 20).setEndAnimation(null); } } - player.getAudioManager().send(new Audio(2462, 10, 30)); + playAudio(player, Sounds.JUMP2_2462, 30); /*if (GameWorld.getSettings().isPvp()) { ActivityManager.register(new BountyHunterActivity(CraterType.MID_LEVEL)); ActivityManager.register(new BountyHunterActivity(CraterType.HIGH_LEVEL)); diff --git a/Server/src/main/content/region/wilderness/handlers/WildernessObeliskPlugin.java b/Server/src/main/content/region/wilderness/handlers/WildernessObeliskPlugin.java index 6c9b66448..267a5b8cb 100644 --- a/Server/src/main/content/region/wilderness/handlers/WildernessObeliskPlugin.java +++ b/Server/src/main/content/region/wilderness/handlers/WildernessObeliskPlugin.java @@ -16,11 +16,15 @@ import core.game.world.update.flag.context.Graphics; import core.plugin.Initializable; import core.plugin.Plugin; import core.tools.RandomFunction; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.playAudio; /** * Represents the wilderness obelisk plugin. * @author 'Vexia - * @version 1.0 + * @author Player Name + * @version 1.2 */ @Initializable public final class WildernessObeliskPlugin extends OptionHandler { @@ -43,6 +47,12 @@ public final class WildernessObeliskPlugin extends OptionHandler { if (stationObelisk == null) { return false; } + if (player.getSkullManager().isDeepWilderness()) { + if (player.getProperties().getCombatPulse().isInCombat()) { + player.sendMessage("You can't use this while in combat."); + return false; + } + } for (int i = 0; i < 4; i++) { int x = stationObelisk.getLocation().getX(); int y = stationObelisk.getLocation().getY(); @@ -70,7 +80,7 @@ public final class WildernessObeliskPlugin extends OptionHandler { break; } } - player.getAudioManager().send(204); + playAudio(player, Sounds.WILDERNESS_TELEPORT_204); GameWorld.getPulser().submit(new Pulse(6, player) { @Override public boolean pulse() { @@ -84,19 +94,26 @@ public final class WildernessObeliskPlugin extends OptionHandler { } return true; } - int index = RandomFunction.random(Obelisk.values().length); - Obelisk newObelisk = Obelisk.values()[index]; - if (newObelisk == stationObelisk) { - newObelisk = Obelisk.values()[index++ % Obelisk.values().length]; + // Determine new obelisk + Obelisk[] newObelisks = Obelisk.values(); + for (int i = 0; i < newObelisks.length; i++) { + // Find our current obelisk and remove it from the candidate set by replacing it with the last obelisk + if (newObelisks[i] == stationObelisk) { + newObelisks[i] = newObelisks[newObelisks.length - 1]; + break; + } } - for (Player player : RegionManager.getLocalPlayers(center, 1)) { - player.getPacketDispatch().sendMessage("Ancient magic teleports you somewhere in the wilderness."); - int xDif = stationObelisk.getLocation().getX() - player.getLocation().getX(); - int yDif = stationObelisk.getLocation().getY() - player.getLocation().getY(); - if (xDif > 0 || yDif > 0) { - player.getTeleporter().send(Location.create(newObelisk.getLocation().getX() - xDif, newObelisk.getLocation().getY() - yDif, 0), TeleportType.OBELISK, 2); + int index = RandomFunction.random(0, newObelisks.length - 1); //cutting out the last one that is now duplicated + Obelisk newObelisk = newObelisks[index]; + // Teleport players standing within a 3-by-3 bounding box + for (Player player : RegionManager.getLocalPlayersBoundingBox(center, 1, 1)) { + if (player.timers.getTimer("teleblock") == null) { + player.getPacketDispatch().sendMessage("Ancient magic teleports you somewhere in the wilderness."); + int xOffset = player.getLocation().getX() - center.getX(); + int yOffset = player.getLocation().getY() - center.getY(); + player.getTeleporter().send(Location.create(newObelisk.getLocation().getX() + xOffset, newObelisk.getLocation().getY() + yOffset, 0), TeleportType.OBELISK, 2); } else { - player.getTeleporter().send(Location.create(newObelisk.getLocation().getX() + xDif, newObelisk.getLocation().getY() + yDif, 0), TeleportType.OBELISK, 2); + player.getPacketDispatch().sendMessage("A magical force has stopped you from teleporting."); } } super.setDelay(1); diff --git a/Server/src/main/content/region/wilderness/handlers/WildernessPlugin.java b/Server/src/main/content/region/wilderness/handlers/WildernessPlugin.java index 45543b45e..26bb09c1a 100644 --- a/Server/src/main/content/region/wilderness/handlers/WildernessPlugin.java +++ b/Server/src/main/content/region/wilderness/handlers/WildernessPlugin.java @@ -6,12 +6,17 @@ 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.entity.player.link.TeleportManager; import core.game.node.entity.player.link.TeleportManager.TeleportType; import core.game.node.scenery.Scenery; 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.Sounds; + +import static core.api.ContentAPIKt.*; +import content.data.Quests; /** * Represents a plugin used to handle wilderness nodes. @@ -45,6 +50,8 @@ 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, Quests.DEFENDER_OF_VARROCK)) + break; ClimbActionHandler.climb(player, ClimbActionHandler.CLIMB_DOWN, Location.create(3241, 9991, 0), "You descend into the cavern below."); break; case 37928: @@ -66,7 +73,8 @@ public final class WildernessPlugin extends OptionHandler { * Represents the plugin used to handle kbd nodes. * * @author 'Vexia - * @version 1.0 + * @author Player Name + * @version 1.1 */ public static final class KBDPlugin extends OptionHandler { @@ -116,13 +124,17 @@ public final class WildernessPlugin extends OptionHandler { switch (id) { case 1816:// kbd lever. if (player.getLocation().withinDistance(LOCATIONS[5])) { + animate(player, 2140, false); + playAudio(player, Sounds.LEVER_2400); player.getPacketDispatch().sendMessage("You pull the lever..."); - player.getTeleporter().send(LOCATIONS[4], TeleportType.NORMAL); + player.getTeleporter().send(LOCATIONS[4], TeleportType.NORMAL, TeleportManager.WILDY_TELEPORT); player.getPacketDispatch().sendMessage("... and teleport into the lair of the King Black Dragon!", 5); } break; case 1817: if (player.getLocation().withinDistance(LOCATIONS[4])) { + animate(player, 2140, false); + playAudio(player, Sounds.LEVER_2400); player.getPacketDispatch().sendMessage("You pull the lever..."); player.getTeleporter().send(LOCATIONS[5], TeleportType.NORMAL); player.getPacketDispatch().sendMessage("... and teleport out of the lair of the King Black Dragon!", 5); 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 3beb7a61f..01040043e 100644 --- a/Server/src/main/content/region/wilderness/handlers/revenants/RevenantCombatHandler.java +++ b/Server/src/main/content/region/wilderness/handlers/revenants/RevenantCombatHandler.java @@ -7,12 +7,14 @@ import core.game.node.entity.combat.equipment.SwitchAttack; import core.game.node.entity.impl.Projectile; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.prayer.PrayerType; -import core.game.node.entity.state.EntityState; 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 core.game.node.entity.combat.MultiSwingHandler; import core.game.world.GameWorld; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.*; /** * Handles the multi swing combat handler for revenants. @@ -43,7 +45,7 @@ public class RevenantCombatHandler extends MultiSwingHandler { SwitchAttack attack = getCurrent(); if (attack != null) { if (attack.getStyle() == CombatStyle.RANGE) { - victim.asPlayer().getAudioManager().send(4061, true); + playGlobalAudio(victim.getLocation(), 4061, 0, 1, 10); } } } @@ -55,24 +57,25 @@ public class RevenantCombatHandler extends MultiSwingHandler { if (victim instanceof Player) { SwitchAttack attack = getCurrent(); if (attack != null) { - if (attack.getStyle() == CombatStyle.RANGE && victim.getAttribute("freeze_immunity", -1) < GameWorld.getTicks()) { - victim.getStateManager().set(EntityState.FROZEN, 16, "The icy darts freeze your muscles!"); - victim.asPlayer().getAudioManager().send(4059, true); + if (attack.getStyle() == CombatStyle.RANGE && !hasTimerActive(victim, "frozen") && !hasTimerActive(victim, "frozen:immunity")) { + registerTimer(victim, spawnTimer("frozen", 16, true)); + sendMessage((Player) victim, "The icy darts freeze your muscles!"); + playGlobalAudio(victim.getLocation(), 4059, 0, 1, 10); } else if (attack.getStyle() == CombatStyle.MAGIC) { int ticks = 500; if (victim.asPlayer().getPrayer().get(PrayerType.PROTECT_FROM_MAGIC)) { ticks /= 2; } - if (victim.getStateManager().hasState(EntityState.TELEBLOCK)) { - victim.asPlayer().getAudioManager().send(4064, true); + if (hasTimerActive(victim, "teleblock")) { + playGlobalAudio(victim.getLocation(), 4064, 0, 1, 10); } else { - victim.getStateManager().set(EntityState.TELEBLOCK, ticks); + registerTimer (victim, spawnTimer("teleblock", ticks)); } } } } - if (!victim.getStateManager().hasState(EntityState.POISONED) && (WildernessZone.getWilderness(entity) >= 50 || entity.getId() == 6998)) { - victim.getStateManager().register(EntityState.POISONED, false, 68, entity); + if (!isPoisoned(victim) && (WildernessZone.getWilderness(entity) >= 50 || entity.getId() == 6998)) { + applyPoison(victim, entity, 6); } super.impact(entity, victim, state); } @@ -84,9 +87,9 @@ public class RevenantCombatHandler extends MultiSwingHandler { SwitchAttack attack = getCurrent(); if (attack != null) { if (attack.getStyle() == CombatStyle.MAGIC) { - victim.asPlayer().getAudioManager().send(202, true); + playGlobalAudio(entity.getLocation(), Sounds.TELEPORTBLOCK_CAST_202, 0, 1, 10); } else if (attack.getStyle() == CombatStyle.RANGE) { - victim.asPlayer().getAudioManager().send(4062, true); + playGlobalAudio(entity.getLocation(), 4062, 0, 1, 10); } } } diff --git a/Server/src/main/content/region/wilderness/handlers/revenants/RevenantController.kt b/Server/src/main/content/region/wilderness/handlers/revenants/RevenantController.kt index 3d15eb0bc..1a1bdc055 100644 --- a/Server/src/main/content/region/wilderness/handlers/revenants/RevenantController.kt +++ b/Server/src/main/content/region/wilderness/handlers/revenants/RevenantController.kt @@ -1,9 +1,5 @@ package content.region.wilderness.handlers.revenants -import core.api.Commands -import core.api.TickListener -import core.api.poofClear -import core.api.teleport import core.game.interaction.MovementPulse import core.game.node.entity.npc.NPC import core.game.node.entity.player.link.TeleportManager @@ -12,10 +8,13 @@ import core.game.world.map.Location import core.game.world.update.flag.context.Graphics import core.tools.RandomFunction import core.ServerConstants +import core.api.* import core.tools.SystemLogger import core.game.system.command.Privilege import core.game.world.GameWorld +import core.game.world.map.path.Pathfinder import core.game.world.repository.Repository +import core.tools.Log import java.lang.Integer.min import kotlin.collections.ArrayList import kotlin.collections.HashMap @@ -36,11 +35,12 @@ class RevenantController : TickListener, Commands { Repository.RENDERABLE_NPCS.add(revenantNPC) } - @JvmStatic fun unregisterRevenant(revenantNPC: RevenantNPC) { + @JvmStatic fun unregisterRevenant(revenantNPC: RevenantNPC, removeRender: Boolean = true) { trackedRevenants.remove(revenantNPC) taskTimeRemaining.remove(revenantNPC) currentTask.remove(revenantNPC) - Repository.RENDERABLE_NPCS.remove(revenantNPC) + if (removeRender) + Repository.RENDERABLE_NPCS.remove(revenantNPC) } val routes = listOf( @@ -94,10 +94,10 @@ class RevenantController : TickListener, Commands { define("listrevs", Privilege.ADMIN) {player, strings -> for (rev in trackedRevenants) { - SystemLogger.logInfo(this::class.java, "REV ${rev.id}-${rev.name} @ ${rev.location.toString()}") + log(this::class.java, Log.FINE, "REV ${rev.id}-${rev.name} @ ${rev.location.toString()}") } - SystemLogger.logInfo(this::class.java, "Total of ${trackedRevenants.size} revenants spawned.") + log(this::class.java, Log.FINE, "Total of ${trackedRevenants.size} revenants spawned.") } define("clearrevs", Privilege.ADMIN) {_, _ -> @@ -110,24 +110,14 @@ class RevenantController : TickListener, Commands { NONE { override fun execute(revenantNPC: RevenantNPC) {} }, - INTENTIONAL_IDLE { - private val MAX_IDLE_TIME: Int = 50 - - override fun execute(revenantNPC: RevenantNPC) { - if (taskTimeRemaining[revenantNPC] == 0) currentTask[revenantNPC] = NONE - } - - override fun assign(revenantNPC: RevenantNPC) { - taskTimeRemaining[revenantNPC] = RandomFunction.random(MAX_IDLE_TIME) - } - }, RANDOM_ROAM { private val MAX_ROAM_TICKS: Int = 250 override fun execute(revenantNPC: RevenantNPC) { if (!canMove(revenantNPC)) return - revenantNPC.pulseManager.run(object : MovementPulse(revenantNPC, getNextLocation(revenantNPC)) { + val nextLoc = getNextLocation(revenantNPC) + revenantNPC.pulseManager.run(object : MovementPulse(revenantNPC, nextLoc, Pathfinder.SMART) { override fun pulse(): Boolean { if (taskTimeRemaining[revenantNPC]!! <= 0) currentTask[revenantNPC] = NONE return true @@ -141,6 +131,7 @@ class RevenantController : TickListener, Commands { fun canMove(revenantNPC: RevenantNPC) : Boolean { return !revenantNPC.walkingQueue.isMoving + && !revenantNPC.pulseManager.hasPulseRunning() && !revenantNPC.properties.combatPulse.isAttacking && !revenantNPC.properties.combatPulse.isInCombat } @@ -214,7 +205,7 @@ class RevenantController : TickListener, Commands { RandomFunction.random(-pathVariance, pathVariance), 0 ) - revenantNPC.pulseManager.run(object : MovementPulse(revenantNPC, nextLoc) { + revenantNPC.pulseManager.run(object : MovementPulse(revenantNPC, nextLoc, Pathfinder.SMART) { override fun pulse(): Boolean { return true } @@ -237,4 +228,4 @@ class RevenantController : TickListener, Commands { abstract fun execute(revenantNPC: RevenantNPC) open fun assign(revenantNPC: RevenantNPC) {} } -} \ No newline at end of file +} 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 34f921773..6e0d0e6fb 100644 --- a/Server/src/main/content/region/wilderness/handlers/revenants/RevenantNPC.java +++ b/Server/src/main/content/region/wilderness/handlers/revenants/RevenantNPC.java @@ -19,10 +19,14 @@ import core.tools.RandomFunction; import core.game.node.entity.combat.CombatSwingHandler; import core.game.system.config.NPCConfigParser; import core.game.world.GameWorld; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.playAudio; +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 { @@ -84,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 @@ -99,32 +104,38 @@ public class RevenantNPC extends AbstractNPC { @Override public void clear() { super.clear(); - RevenantController.unregisterRevenant(this); + RevenantController.unregisterRevenant(this, true); } @Override public void finalizeDeath(Entity killer) { super.finalizeDeath(killer); - if (killer instanceof Player) { - killer.asPlayer().getAudioManager().send(4063, true); - } + playGlobalAudio(killer.getLocation(),4063); } @Override 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)) { - p.getAudioManager().send(2393); - } - setAttribute("eat-delay", GameWorld.getTicks() + 6); } + 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); + } + } + behavior.tick(this); if (aggressiveHandler != null && aggressiveHandler.selectTarget()) { return; } @@ -193,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/content/region/wilderness/handlers/revenants/RevenantType.java b/Server/src/main/content/region/wilderness/handlers/revenants/RevenantType.java index fe582d1cf..0e7bfb000 100644 --- a/Server/src/main/content/region/wilderness/handlers/revenants/RevenantType.java +++ b/Server/src/main/content/region/wilderness/handlers/revenants/RevenantType.java @@ -1,5 +1,10 @@ package content.region.wilderness.handlers.revenants; +import core.cache.def.impl.NPCDefinition; + +import java.util.ArrayList; +import java.util.List; + /** * A revenant type. * @author Vexia @@ -58,4 +63,20 @@ public enum RevenantType { public int getMaxHit() { return maxHit; } + + public static RevenantType getClosestHigherOrEqual (int combatLevel) { + for (RevenantType t : values()) { + NPCDefinition def = NPCDefinition.forId(t.ids[0]); + if (def.getCombatLevel() >= combatLevel) return t; + } + return RevenantType.DRAGON; + } + + public static List getAllIds() { + ArrayList ids = new ArrayList<>(); + for (RevenantType t : values()) { + for (int id : t.ids) ids.add(id); + } + return ids; + } } diff --git a/Server/src/main/core/Server.kt b/Server/src/main/core/Server.kt index 09c36c40a..80f29a8cf 100644 --- a/Server/src/main/core/Server.kt +++ b/Server/src/main/core/Server.kt @@ -1,20 +1,23 @@ 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 @@ -41,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. @@ -51,27 +56,27 @@ object Server { @JvmStatic fun main(args: Array) { if (args.isNotEmpty()) { - SystemLogger.logInfo(this::class.java, "Using config file: ${args[0]}") + log(this::class.java, Log.INFO, "Using config file: ${args[0]}") ServerConfigParser.parse(args[0]) } else { - SystemLogger.logInfo(this::class.java, "Using config file: ${"worldprops" + File.separator + "default.conf"}") + log(this::class.java, Log.INFO, "Using config file: ${"worldprops" + File.separator + "default.conf"}") ServerConfigParser.parse("worldprops" + File.separator + "default.conf") } startTime = System.currentTimeMillis() val t = TimeStamp() GameWorld.prompt(true) Runtime.getRuntime().addShutdownHook(ServerConstants.SHUTDOWN_HOOK) - SystemLogger.logInfo(this::class.java, "Starting networking...") + log(this::class.java, Log.INFO, "Starting networking...") try { reactor = NioReactor.configure(43594 + GameWorld.settings?.worldId!!) reactor!!.start() } catch (e: BindException) { - SystemLogger.logErr(this::class.java, "Port " + (43594 + GameWorld.settings?.worldId!!) + " is already in use!") + log(this::class.java, Log.ERR, "Port " + (43594 + GameWorld.settings?.worldId!!) + " is already in use!") throw e } //WorldCommunicator.connect() - SystemLogger.logInfo(this::class.java, GameWorld.settings?.name + " flags " + GameWorld.settings?.toString()) - SystemLogger.logInfo(this::class.java, GameWorld.settings?.name + " started in " + t.duration(false, "") + " milliseconds.") + log(this::class.java, Log.INFO, GameWorld.settings?.name + " flags " + GameWorld.settings?.toString()) + log(this::class.java, Log.INFO, GameWorld.settings?.name + " started in " + t.duration(false, "") + " milliseconds.") val scanner = Scanner(System.`in`) running = true @@ -80,7 +85,7 @@ object Server { val command = scanner.nextLine() when(command){ "stop" -> exitProcess(0) - "players" -> System.out.println("Players online: " + (Repository.LOGGED_IN_PLAYERS.size)) + "update" -> SystemManager.flag(SystemState.UPDATING) "help","commands" -> printCommands() "restartworker" -> SystemManager.flag(SystemState.ACTIVE) @@ -93,9 +98,14 @@ 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) { - SystemLogger.logErr(this::class.java, "Triggering reboot due to heartbeat timeout") - SystemLogger.logErr(this::class.java, "Creating thread dump...") + log(this::class.java, Log.ERR, "Triggering reboot due to heartbeat timeout") + log(this::class.java, Log.ERR, "Creating thread dump...") val dump = threadDump(true, true) withContext(Dispatchers.IO) { @@ -113,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 f8889d41b..30a6ab377 100644 --- a/Server/src/main/core/ServerConstants.kt +++ b/Server/src/main/core/ServerConstants.kt @@ -2,264 +2,347 @@ package core import core.game.system.SystemShutdownHook import core.game.world.map.Location +import core.tools.LogLevel 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. * @author Ceikry */ class ServerConstants { - companion object { - var NOAUTH_DEFAULT_ADMIN: Boolean = true + companion object { + var NOAUTH_DEFAULT_ADMIN: Boolean = true @JvmField - var DAILY_ACCOUNT_LIMIT = 3 + var CURRENT_SAVEFILE_VERSION = 4 @JvmField - var REVENANT_POPULATION: Int = 30 + var DAILY_ACCOUNT_LIMIT = 3 @JvmField - var BOTS_INFLUENCE_PRICE_INDEX = true + var REVENANT_POPULATION: Int = 30 @JvmField - var SHUTDOWN_HOOK: Thread = Thread(SystemShutdownHook()) - - @JvmField - var ALLOW_GUI: Boolean = false + var BOTS_INFLUENCE_PRICE_INDEX = true @JvmField - var DATA_PATH: String? = null + var SHUTDOWN_HOOK: Thread = Thread(SystemShutdownHook()) - //path to the cache - @JvmField - var CACHE_PATH: String? = null + @JvmField + var ALLOW_GUI: Boolean = false - //path for the server store - @JvmField - var STORE_PATH: String? = null + @JvmField + var DATA_PATH: String? = null - //path for player saves - @JvmField - var PLAYER_SAVE_PATH: String? = null + //path to the cache + @JvmField + var CACHE_PATH: String? = null - @JvmField - var PLAYER_ATTRIBUTE_PATH = "ish"; + //path for the server store + @JvmField + var STORE_PATH: String? = null - //path to the various config files, such as npc_spawns.json - var CONFIG_PATH: String? = null + //path for player saves + @JvmField + var PLAYER_SAVE_PATH: String? = null - @JvmField - var GRAND_EXCHANGE_DATA_PATH: String? = null + @JvmField + var PLAYER_ATTRIBUTE_PATH = "ish" - @JvmField - var RDT_DATA_PATH: String? = null + //path to the various config files, such as npc_spawns.json + var CONFIG_PATH: String? = null - @JvmField - var OBJECT_PARSER_PATH: String? = null + @JvmField + var GRAND_EXCHANGE_DATA_PATH: String? = null - @JvmField - var SCRIPTS_PATH: String? = null + @JvmField + var RDT_DATA_PATH: String? = null - @JvmField - var DIALOGUE_SCRIPTS_PATH: String? = null + @JvmField + var OBJECT_PARSER_PATH: String? = null - @JvmField - var LOGS_PATH: String? = null + @JvmField + var SCRIPTS_PATH: String? = null - @JvmField - var BOT_DATA_PATH: String? = null + @JvmField + var DIALOGUE_SCRIPTS_PATH: String? = null - @JvmField - var CELEDT_DATA_PATH: String? = null + @JvmField + var LOGS_PATH: String? = null - @JvmField - var USDT_DATA_PATH: String? = null + @JvmField + var BOT_DATA_PATH: String? = null - @JvmField - var HDT_DATA_PATH: String? = null + @JvmField + var CELEDT_DATA_PATH: String? = null - @JvmField - var GDT_DATA_PATH: String? = null + @JvmField + var USDT_DATA_PATH: String? = null - @JvmField - var RSDT_DATA_PATH: String? = null + @JvmField + var HDT_DATA_PATH: String? = null - @JvmField - var ASDT_DATA_PATH: String? = null + @JvmField + var GDT_DATA_PATH: String? = null - //the max number of players. - @JvmField - var MAX_PLAYERS = 2000 + @JvmField + var RSDT_DATA_PATH: String? = null - //the max number of NPCs - @JvmField - var MAX_NPCS = 32000 + @JvmField + var ASDT_DATA_PATH: String? = null - //the location where new players are placed on login. - @JvmField - var START_LOCATION: Location? = null + @JvmField + var GRAFANA_PATH: String? = null - //Location for all home teleports/respawn location - @JvmField - var HOME_LOCATION: Location? = null + //the max number of players. + @JvmField + var MAX_PLAYERS = 2000 - //the name for the database - @JvmField - var DATABASE_NAME: String? = null + //the max number of NPCs + @JvmField + var MAX_NPCS = 32000 - //username for the database - @JvmField - var DATABASE_USER: String? = null + //the location where new players are placed on login. + @JvmField + var START_LOCATION: Location? = null - //password for the database - @JvmField - var DATABASE_PASS: String? = null + //Location for all home teleports/respawn location + @JvmField + var HOME_LOCATION: Location? = null - //address for the database - @JvmField - var DATABASE_ADDRESS: String? = null + //the name for the database + @JvmField + var DATABASE_NAME: String? = null - @JvmField - var DATABASE_PORT: String? = null + //username for the database + @JvmField + var DATABASE_USER: String? = null - @JvmField - var WRITE_LOGS: Boolean = false + //password for the database + @JvmField + var DATABASE_PASS: String? = null - @JvmField - var BANK_SIZE: Int = 496 + //address for the database + @JvmField + var DATABASE_ADDRESS: String? = null - @JvmField - var BANK_BOOTH_QUICK_OPEN: Boolean = false + @JvmField + var DATABASE_PORT: String? = null - @JvmField - var BANK_BOOTH_NOTE_ENABLED: Boolean = true + @JvmField + var WRITE_LOGS: Boolean = false - @JvmField - var BANK_BOOTH_NOTE_UIM: Boolean = true + @JvmField + var BANK_SIZE: Int = 496 - @JvmField - var GE_AUTOSAVE_FREQUENCY = secondsToTicks(3600) //1 hour + @JvmField + var BANK_BOOTH_QUICK_OPEN: Boolean = false - @JvmField - var GE_AUTOSTOCK_ENABLED = false + @JvmField + var BANK_BOOTH_NOTE_ENABLED: Boolean = true - @JvmField - var MS_SECRET_KEY = "" + @JvmField + var BANK_BOOTH_NOTE_UIM: Boolean = true - @JvmField - var LOG_CUTSCENE = true + @JvmField + var GE_AUTOSAVE_FREQUENCY = secondsToTicks(3600) //1 hour - @JvmField - var RULES_AND_INFO_ENABLED = true + @JvmField + var GE_AUTOSTOCK_ENABLED = false - @JvmField - var WATCHDOG_ENABLED = true + @JvmField + var MS_SECRET_KEY = "" - @JvmField - var I_AM_A_CHEATER = false + @JvmField + var LOG_CUTSCENE = true - //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"), - arrayOf(Location.create(1945, 4959, 0), "blast furnace", "blast"), - arrayOf(Location.create(2408, 4449, 0), "zanaris"), - arrayOf(Location.create(3656, 3517, 0), "ectofuntus", "ecto"), - arrayOf(Location.create(2408, 4449, 0), "tower of life lower"), - arrayOf(Location.create(2894, 4756, 0), "test area"), - arrayOf(Location.create(2722, 4886, 0), "quest the golem 1"), - arrayOf(Location.create(2704, 5349, 0), "dorgeshuun", "dorg"), - arrayOf(Location.create(2711, 10132, 0), "brine rats"), - arrayOf(Location.create(2328, 3677, 0), "piscatoris"), - 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(0, 0, 0), ""), + @JvmField + var RULES_AND_INFO_ENABLED = true - @JvmField - var DATABASE: Database? = null + @JvmField + var WATCHDOG_ENABLED = true - //if SQL is enabled - @JvmField - var MYSQL = true + @JvmField + var I_AM_A_CHEATER = false - //the server name - @JvmField - var SERVER_NAME: String = "" + @JvmField + var LOG_LEVEL = LogLevel.VERBOSE - //the server's grand exchange name - @JvmField - var SERVER_GE_NAME: String = "" + @JvmField + var JAVA_VERSION = 8 - //The RSA_KEY for the server. - @JvmField - var EXPONENT = BigInteger("52317200263721308660411803146360972546561037484450290559823448967617618536819222494429186211525706853703641369936136465589036631055945454547936148730495933263344792588795811788941129493188907621550836988152620502378278134421731002382361670176785306598134280732756356458964850508114958769985438054979422820241") + @JvmField + var GRAFANA_LOGGING = true - //The MODULUS for the server. - @JvmField - var MODULUS = BigInteger("96982303379631821170939875058071478695026608406924780574168393250855797534862289546229721580153879336741968220328805101128831071152160922518190059946555203865621183480223212969502122536662721687753974815205744569357388338433981424032996046420057284324856368815997832596174397728134370577184183004453899764051") + @JvmField + var GRAFANA_TTL_DAYS = 7 - @JvmField - var DAILY_RESTART = 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"), + arrayOf(Location.create(1945, 4959, 0), "blast furnace", "blast"), + arrayOf(Location.create(2408, 4449, 0), "zanaris"), + arrayOf(Location.create(3656, 3517, 0), "ectofuntus", "ecto"), + arrayOf(Location.create(2408, 4449, 0), "tower of life lower"), + arrayOf(Location.create(2894, 4756, 0), "test area"), + arrayOf(Location.create(2722, 4886, 0), "quest the golem 1"), + arrayOf(Location.create(2704, 5349, 0), "dorgeshuun", "dorg"), + arrayOf(Location.create(2711, 10132, 0), "brine rats"), + arrayOf(Location.create(2328, 3677, 0), "piscatoris"), + 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") + ) - @JvmField - var DISCORD_GE_WEBHOOK = "" + @JvmField + var DATABASE: Database? = null - @JvmField - var DISCORD_MOD_WEBHOOK = "" + //if SQL is enabled + @JvmField + var MYSQL = true - @JvmField - var PRELOAD_MAP = false + //the server name + @JvmField + var SERVER_NAME: String = "" - @JvmField - var USE_AUTH = false + //the server's grand exchange name + @JvmField + var SERVER_GE_NAME: String = "" - @JvmField - var PERSIST_ACCOUNTS = false - } + //The RSA_KEY for the server. + @JvmField + var EXPONENT = BigInteger("52317200263721308660411803146360972546561037484450290559823448967617618536819222494429186211525706853703641369936136465589036631055945454547936148730495933263344792588795811788941129493188907621550836988152620502378278134421731002382361670176785306598134280732756356458964850508114958769985438054979422820241") + + //The MODULUS for the server. + @JvmField + var MODULUS = BigInteger("96982303379631821170939875058071478695026608406924780574168393250855797534862289546229721580153879336741968220328805101128831071152160922518190059946555203865621183480223212969502122536662721687753974815205744569357388338433981424032996046420057284324856368815997832596174397728134370577184183004453899764051") + + @JvmField + var DAILY_RESTART = false + + @JvmField + var DISCORD_GE_WEBHOOK = "" + + @JvmField + var DISCORD_MOD_WEBHOOK = "" + + @JvmField + var DISCORD_OPENRSC_HOOK = "" + + @JvmField + var PRELOAD_MAP = false + + @JvmField + var USE_AUTH = false + + @JvmField + var PERSIST_ACCOUNTS = false + + @JvmField + var DRAGON_AXE_USE_OSRS_SPEC = false + + @JvmField + var ENABLE_GLOBALCHAT = false + + @JvmField + var MAX_PATHFIND_DISTANCE = 25 + + @JvmField + var IRONMAN_ICONS = false + + @JvmField + var PLAYER_STOCK_CLEAR_INTERVAL = 1 + + @JvmField + var PLAYER_STOCK_RECIRCULATE = true + + @JvmField + var BOTSTOCK_LIMIT = 5000 + + @JvmField + var BETTER_AGILITY_PYRAMID_GP = true + + @JvmField + var BETTER_DFS = true + + @JvmField + var NEW_PLAYER_ANNOUNCEMENT = true + + @JvmField + var HOLIDAY_EVENT_RANDOMS = true + + @JvmField + var FORCE_HALLOWEEN_EVENTS = false + + @JvmField + var FORCE_CHRISTMAS_EVENTS = false + + @JvmField + var FORCE_EASTER_EVENTS = false + + @JvmField + var RUNECRAFTING_FORMULA_REVISION = 581 + + @JvmField + var ENHANCED_DEEP_WILDERNESS = 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 9535d15be..52df7fb52 100644 --- a/Server/src/main/core/ServerStore.kt +++ b/Server/src/main/core/ServerStore.kt @@ -2,8 +2,10 @@ package core import core.api.PersistWorld import core.api.getItemName +import core.api.log import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player +import core.tools.Log import org.json.simple.JSONArray import org.json.simple.JSONObject import org.json.simple.parser.JSONParser @@ -38,7 +40,7 @@ class ServerStore : PersistWorld { fileMap[key] = data counter++ } catch (e: Exception){ - SystemLogger.logErr(this::class.java, "Failed parsing ${storeFile.name} - stack trace below.") + log(this::class.java, Log.ERR, "Failed parsing ${storeFile.name} - stack trace below.") e.printStackTrace() return@forEach } @@ -99,30 +101,30 @@ class ServerStore : PersistWorld { } @JvmStatic - fun JSONObject.getInt(key: String): Int { + fun JSONObject.getInt(key: String, default: Int = 0): Int { return when(val value = this[key]){ is Long -> value.toInt() is Double -> value.toInt() is Float -> value.toInt() is Int -> value - is Nothing -> 0 - else -> 0 + is Nothing -> default + else -> default } } @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/Util.java b/Server/src/main/core/Util.java index 861df7c69..5bb17aaac 100644 --- a/Server/src/main/core/Util.java +++ b/Server/src/main/core/Util.java @@ -34,6 +34,9 @@ public class Util { public static double clamp(double input, double min, double max) { return Math.max(Math.min(input, max), min); } + public static int clamp(int input, int min, int max) { + return Math.max(Math.min(input, max), min); + } public static long nextMidnight(long currentTime) { Date date = new Date(); diff --git a/Server/src/main/core/api/ApiExtensions.kt b/Server/src/main/core/api/ApiExtensions.kt index cdd664c19..fc8efda5a 100644 --- a/Server/src/main/core/api/ApiExtensions.kt +++ b/Server/src/main/core/api/ApiExtensions.kt @@ -4,14 +4,9 @@ import core.game.node.item.Item import java.util.* import kotlin.collections.ArrayList -fun IntRange.toIntArray(): IntArray { - if (last < first) - return IntArray(0) - - val result = IntArray(last - first + 1) - var index = 0 - for (element in this) - result[index++] = element +fun IntProgression.toIntArray(): IntArray { + val result = IntArray((last - first) / step + 1) + forEachIndexed { index, i -> result[index] = i } return result } @@ -57,7 +52,15 @@ fun IntArray.isNextLast(element: Int) : Boolean { return this.isLast(this.getNext(element)) } -fun LinkedList.tryPop(default: T) : T { +fun LinkedList.tryPop(default: T?) : T? { this.peek() ?: return default return this.pop() +} + +inline fun > parseEnumEntry(name: String) : E? { + return try { + enumValueOf(name) + } catch (e: Exception) { + null + } } \ No newline at end of file diff --git a/Server/src/main/core/api/Commands.kt b/Server/src/main/core/api/Commands.kt index 33305875e..231adc129 100644 --- a/Server/src/main/core/api/Commands.kt +++ b/Server/src/main/core/api/Commands.kt @@ -4,6 +4,7 @@ import core.game.node.entity.player.Player import core.game.system.command.Command import core.game.system.command.CommandMapping import core.game.system.command.Privilege +import core.tools.Log import core.tools.colorize /** @@ -32,7 +33,9 @@ interface Commands : ContentInterface { * Glorified player.sendMessage with black text coloring and an arrow. Use this when you need to * notify/inform a player of some information from within the command without ending execution. */ - fun notify(player: Player, message: String){ + fun notify(player: Player, message: String, logToConsole: Boolean = false){ + if (logToConsole) + log (this::class.java, Log.DEBUG, message) player.sendMessage(colorize("-->$message")) } diff --git a/Server/src/main/core/api/ContentAPI.kt b/Server/src/main/core/api/ContentAPI.kt index d9242a991..55bc7c460 100644 --- a/Server/src/main/core/api/ContentAPI.kt +++ b/Server/src/main/core/api/ContentAPI.kt @@ -1,36 +1,63 @@ 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.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.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.entity.state.EntityState 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,28 +66,22 @@ 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.Animation -import core.game.world.update.flag.context.Graphics +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 core.game.activity.Cutscene -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 org.rs09.consts.Sounds +import java.io.* +import java.util.regex.* +import kotlin.math.* /** * Gets a skilling tool which the player has the level to use and is in their inventory. @@ -94,17 +115,6 @@ fun hasLevelStat(player: Player, skill: Int, level: Int): Boolean { return player.skills.getStaticLevel(skill) >= level } -/** - * Check if an item exists in a player's inventory - * @param player the player whose inventory to check - * @param item the ID of the item to check for - * @param amount the amount to check for - * @return true if the player has >= the given item in the given amount, false otherwise. - */ -fun inInventory(player: Player, item: Int, amount: Int = 1): Boolean { - return player.inventory.contains(item, amount) -} - /** * Check the amount of a given item in the player's inventory * @param player the player whose inventory to check @@ -119,10 +129,25 @@ fun amountInInventory(player: Player, id: Int): Int { * Check the amount of a given item in the player's bank * @param player the player to check * @param id the ID of the item to check for + * @param includeSecondary if the secondary bank should be included in the search * @return the amount of the ID in the player's bank. */ fun amountInBank(player: Player, id: Int, includeSecondary: Boolean = true): Int { - return player.bank.getAmount(id) + if (includeSecondary) player.bankSecondary.getAmount(id) else 0 + return getAmountInBank(player, id) + if (includeSecondary) getAmountInBank(player, id, true) else 0 +} + +/** + * More performant way to check the amount of a given item in the player's bank + * Uses the alwaysStack property of banks to short circuit the search. + * @param player the player to check + * @param id the ID of the item to check for + * @param secondary if the secondary bank should be searched instead + * @return the amount of the ID in the player's bank. + */ +private fun getAmountInBank(player: Player, id: Int, secondary: Boolean = false): Int { + val bank = if (secondary) player.bankSecondary.toArray() else player.bankPrimary.toArray() + bank.forEach { if (it?.id == id) return it.amount } + return 0 } /** @@ -132,22 +157,75 @@ fun amountInBank(player: Player, id: Int, includeSecondary: Boolean = true): Int * @return the amount of the ID in the player's equipment. */ fun amountInEquipment(player: Player, id: Int): Int { - return player.equipment.getAmount(id) + val slot = itemDefinition(id).getConfiguration(ItemConfigParser.EQUIP_SLOT, -1) + val equipped = player.equipment[slot] ?: return 0 + return if (equipped.id == id) equipped.amount else 0 } /** - * Check that an item is equipped by the given player + * Check if an item exists in a player's inventory + * @param player the player whose inventory to check + * @param id the ID of the item to check for + * @param amount the amount to check for + * @return true if the player has >= the given item in the given amount, false otherwise. */ -fun isEquipped(player: Player, id: Int): Boolean { - return amountInEquipment(player, id) > 0 +fun inInventory(player: Player, id: Int, amount: Int = 1): Boolean { + return player.inventory.contains(id, amount) +} + +/** + * Check if an item exists in a player's bank + * @param player the player whose bank to check + * @param id the ID of the item to check for + * @param amount the amount to check for, defaults to 1 + * @return true if the item exists in the given amount in the player's bank + */ +fun inBank(player: Player, id: Int, amount: Int = 1): Boolean { + return amountInBank(player, id) >= amount +} + +/** + * Check if an item exists in a player's equipment + * @param player the player whose equipment to check + * @param id the ID of the item to check for + * @param amount the amount to check for, defaults to 1 + * @return true if the item exists in the given amount in the player's equipment + */ +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 + * @param id the ID of the item to check for + * @param amount the amount to check for, defaults to 1 + * @return true if the item exists in the given amount in the player's equipment or inventory + */ +fun inEquipmentOrInventory(player: Player, id: Int, amount: Int = 1): Boolean { + // Proper, but slower implementation. Use faster unless need to check amounts split between equip/inv + //return amountInEquipment(player, id) + amountInInventory(player, id) >= amount + return inEquipment(player, id, amount) || inInventory(player, id, amount) } /** * Check that a set of items is equipped by the given player */ -fun areEquipped(player: Player, vararg ids: Int): Boolean { +fun allInEquipment(player: Player, vararg ids: Int): Boolean { return ids.all { id -> - amountInEquipment(player, id) > 0 + inEquipment(player, id) } } @@ -157,12 +235,22 @@ fun areEquipped(player: Player, vararg ids: Int): Boolean { * @param ids the set of item ids to check * @return true if the player has at least one of the items equipped, false if none are equipped */ -fun areAnyEquipped(player: Player, vararg ids: Int): Boolean { +fun anyInEquipment(player: Player, vararg ids: Int): Boolean { return ids.any { id -> - amountInEquipment(player, id) > 0 + inEquipment(player, id) } } +/** + * Gets the item in the given equipment slot for the given player + * @param player the player whose equipment to pull from + * @param slot the Equipment slot to use, EquipmentSlot enum contains the options. + * @return the Item in the given slot, or null if none. + */ +fun getItemFromEquipment(player: Player, slot: EquipmentSlot): Item? { + return player.equipment.get(slot.ordinal) +} + class ContainerisedItem(val container: core.game.container.Container?, val itemId: Int) { fun remove() : Boolean { return this.container?.remove(this.itemId.asItem()) ?: false @@ -173,14 +261,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) } } } @@ -194,7 +308,7 @@ fun hasAnItem(player: Player, vararg ids: Int): ContainerisedItem { * @return true if the player has an item corresponding to the given god, false otherwise */ fun hasGodItem(player: Player, god: God): Boolean { - god.validItems.forEach { if (amountInEquipment(player, it) > 0) return true } + god.validItems.forEach { if (inEquipment(player, it)) return true } return false } @@ -205,7 +319,16 @@ fun hasGodItem(player: Player, god: God): Boolean { */ fun shouldRemoveNothings(player: Player) : Boolean { val ring = getItemFromEquipment(player, EquipmentSlot.RING) - return ring != null && ring.id in Items.RING_OF_WEALTH_14638..Items.RING_OF_WEALTH4_14646 + if (getAttribute(player, "ringofwealth:unlocked", false)){ + player.sendMessage("Your Wealth enchantment coaxes a rare drop from your opponent.") + return true + } else if (ring != null && ring.id in Items.RING_OF_WEALTH_14638..Items.RING_OF_WEALTH4_14646) { + player.sendMessage("Your Ring of Wealth coaxes a rare drop from your opponent.") + player.sendMessage("The magic in the ring reminds you of the High Level Alchemy spell for some reason.") + return true + } else { + return false + } } /** @@ -252,6 +375,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. */ @@ -262,12 +386,18 @@ fun replaceSlot(player: Player, slot: Int, item: Item, currentItem: Item? = null Container.BANK -> player.bank } + if (item.id == 65535 || item.amount <= 0) { + return cont.replace(null, slot) + } + if (currentItem == null) { return cont.replace(item, slot) } - if (cont.remove(currentItem)) + if (cont.remove(currentItem, slot, true)) { return cont.replace(item, slot) + } + PlayerMonitor.log(player, LogType.DUPE_ALERT, "Potential slot-replacement-based dupe attempt, slot: $slot, item: $item") val other = when (container) { Container.INVENTORY -> Container.EQUIPMENT @@ -278,6 +408,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 @@ -286,7 +458,34 @@ fun replaceSlot(player: Player, slot: Int, item: Item, currentItem: Item? = null */ fun addItemOrDrop(player: Player, id: Int, amount: Int = 1) { val item = Item(id, amount) - if (!player.inventory.add(item)) GroundItemManager.create(item, player) + if(amount == 1 || item.definition.isStackable) { + if (!player.inventory.add(item)) GroundItemManager.create(item, player) + } else { + val singleItem = Item(id, 1) + for(i in 0 until amount) { + if(!player.inventory.add(singleItem)) GroundItemManager.create(singleItem, player) + } + } +} + +/** + * 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!)")) + } + } } /** @@ -308,28 +507,6 @@ fun poofClear(npc: NPC) { }) } -/** - * Check if an item exists in a player's bank - * @param player the player whose bank to check - * @param item the ID of the item to check for - * @param amount the amount to check for, defaults to 1 - * @return true if the item exists in the given amount in the player's bank - */ -fun inBank(player: Player, item: Int, amount: Int = 1): Boolean { - return player.bank.contains(item, amount) || player.bankSecondary.contains(item, amount) -} - -/** - * Check if an item exists in a player's equipment - * @param player the player whose equipment to check - * @param item the ID of the item to check for - * @param amount the amount to check for, defaults to 1 - * @return true if the item exists in the given amount in the player's equipment - */ -fun inEquipment(player: Player, item: Int, amount: Int = 1): Boolean { - return player.equipment.contains(item, amount) -} - /** * Get number of free slots in a player's inventory * @param player the player to check @@ -375,6 +552,11 @@ fun animationDuration(animation: Animation): Int { return animation.definition.durationTicks } +fun animationCycles (animation: Int) : Int { + val def = AnimationDefinition.forId(animation) + return def.cycles +} + /** * Give a player some amount of experience in a specific skill * @param player the player to award XP to @@ -387,33 +569,59 @@ fun rewardXP(player: Player, skill: Int, amount: Double) { /** * Replace an object with the given revert timer - * @param toReplace the GameObject instance we are replacing - * @param with the ID of the GameObject we wish to replace toReplace with - * @param for_ticks the number of ticks the object should be replaced for. Use -1 for permanent. + * @param toReplace the Scenery instance we are replacing + * @param with the ID of the Scenery we wish to replace toReplace with + * @param forTicks the number of ticks the object should be replaced for. Use -1 for permanent. * @param loc the location to move the new object to if necessary. Defaults to null. */ -fun replaceScenery(toReplace: Scenery, with: Int, for_ticks: Int, loc: Location? = null) { +fun replaceScenery(toReplace: Scenery, with: Int, forTicks: Int, loc: Location? = null) { val newLoc = when (loc) { null -> toReplace.location else -> loc } - if (for_ticks == -1) { + if (forTicks == -1) { SceneryBuilder.replace(toReplace, toReplace.transform(with, toReplace.rotation, newLoc)) } else { - SceneryBuilder.replace(toReplace, toReplace.transform(with, toReplace.rotation, newLoc), for_ticks) + SceneryBuilder.replace(toReplace, toReplace.transform(with, toReplace.rotation, newLoc), forTicks) } toReplace.isActive = false } +/** + * Add a scenery to the world + * @param sceneryId the ID of the scenery to add + * @param location the location to place it at + * @param rotation the rotation of the scenery (default 0) + * @param type the type of the scenery (default 22) + * @return the created scenery +*/ +fun addScenery (sceneryId: Int, location: Location, rotation: Int = 0, type: Int = 22) : Scenery { + val scenery = Scenery(sceneryId, location, type, rotation) + SceneryBuilder.add(scenery) + return scenery +} + +fun addScenery (scenery: Scenery) { + SceneryBuilder.add(scenery) +} + +/** + * Remove a scenery from the world + * @param scenery the Scenery object to remove. +*/ +fun removeScenery (scenery: Scenery) { + SceneryBuilder.remove(scenery) +} + /** * Replace an object with the given revert timer with the given rotation - * @param toReplace the GameObject instance we are replacing - * @param with the ID of the GameObject we wish to replace toReplace with - * @param for_ticks the number of ticks the object should be replaced for. Use -1 for permanent. + * @param toReplace the Scenery instance we are replacing + * @param with the ID of the Scenery we wish to replace toReplace with + * @param forTicks the number of ticks the object should be replaced for. Use -1 for permanent. * @Param rotation the Direction of the rotation it should use. Direction.NORTH, Direction.SOUTH, etc * @param loc the location to move the new object to if necessary. Defaults to null. */ -fun replaceScenery(toReplace: Scenery, with: Int, for_ticks: Int, rotation: Direction, loc: Location? = null) { +fun replaceScenery(toReplace: Scenery, with: Int, forTicks: Int, rotation: Direction, loc: Location? = null) { val newLoc = when (loc) { null -> toReplace.location else -> loc @@ -428,10 +636,10 @@ fun replaceScenery(toReplace: Scenery, with: Int, for_ticks: Int, rotation: Dire Direction.SOUTH_WEST -> 5 Direction.WEST -> 3 } - if (for_ticks == -1) { + if (forTicks == -1) { SceneryBuilder.replace(toReplace, toReplace.transform(with, rot, newLoc)) } else { - SceneryBuilder.replace(toReplace, toReplace.transform(with, rot, newLoc), for_ticks) + SceneryBuilder.replace(toReplace, toReplace.transform(with, rot, newLoc), forTicks) } toReplace.isActive = false } @@ -480,10 +688,12 @@ fun getWorldTicks(): Int { } /** - * Gets an Audio object with specified id, volume, etc + * Plays a jingle by id + * @param player the player to play the jingle for + * @param jingleId the jingle to play */ -fun getAudio(id: Int, volume: Int = 10, delay: Int = 1): Audio { - return Audio(id, volume, delay) +fun playJingle(player: Player, jingleId: Int) { + PacketRepository.send(MusicPacket::class.java, MusicContext(player, jingleId, true)) } /** @@ -494,6 +704,7 @@ fun getAudio(id: Int, volume: Int = 10, delay: Int = 1): Audio { */ fun impact(entity: Entity, amount: Int, type: ImpactHandler.HitsplatType = ImpactHandler.HitsplatType.NORMAL) { entity.impactHandler.manualHit(entity, amount, type) + if (entity is Player) playHurtAudio(entity) } /** @@ -620,6 +831,15 @@ fun face(entity: Entity, toFace: Node, duration: Int = -1) { } } +/** + * Causes the given entity to reset its face direction. + * @param entity the entity to reset. +*/ +fun resetFace (entity: Entity) { + entity.face(null) + entity.faceLocation(entity.location) +} + /** * Opens the given interface for the given player * @param player the player to open the interface for @@ -657,10 +877,15 @@ fun emote(entity: Entity, emote: Emotes) { /** * Sends a message to the given player. + * + * The message will be split on word boundaries into multiple lines so + * that none of the lines will overflow the player's message box. + * * @param player the player to send the message to. + * @param message the message to send. */ fun sendMessage(player: Player, message: String) { - player.sendMessage(message) + player.sendMessages(*splitLines(message, 86)) } /** @@ -668,8 +893,13 @@ fun sendMessage(player: Player, message: String) { * @param entity the entity to send the chat for * @param message the message to display */ -fun sendChat(entity: Entity, message: String) { - entity.sendChat(message) +fun sendChat(entity: Entity, message: String, delay: Int = -1) { + if (delay > -1) { + queueScript(entity, delay, QueueStrength.SOFT) { + entity.sendChat(message) + return@queueScript stopExecuting(entity) + } + } else entity.sendChat(message) } /** @@ -721,13 +951,48 @@ fun animate(entity: Entity, anim: T, forced: Boolean = false) { } /** - * Plays the given Audio for the given Entity - * @param player the player to play the audio for - * @param audio the Audio to play - * @param global if other nearby entities should be able to hear it + * Plays audio for the player + * @param player the player to play the defined audio for + * @param id the audio id to play + * @param delay the delay in client cycles (50 cycles = 1 second) + * @param loops the number of times to loop audio (for some audio ids it is used to define how many times to loop the audio) + * @param location the location where the audio will play from (The sound will fade with distance) + * @param radius the distance the audio can be heard from the defined location (default = 8 tiles if undefined) */ -fun playAudio(player: Player, audio: Audio, global: Boolean = false) { - player.audioManager.send(audio, global) +@JvmOverloads +fun playAudio(player: Player, id: Int, delay: Int = 0, loops: Int = 1, location: Location? = null, radius: Int = Audio.defaultAudioRadius) { + PacketRepository.send(AudioPacket::class.java, DefaultContext(player, Audio(id, delay, loops, radius), location)) +} + +/** + * Plays audio for players near a defined location + * @param location the location where the audio will play from (The sound will fade with distance) + * @param id the audio id to play + * @param delay the delay in client cycles (50 cycles = 1 second) + * @param loops the number of times to loop audio (for some audio ids it is used to define how many times to loop the audio) + * @param radius the distance the audio can be heard from the defined location (default = 8 tiles if undefined) + */ +@JvmOverloads +fun playGlobalAudio(location: Location, id: Int, delay: Int = 0, loops: Int = 1, radius: Int = Audio.defaultAudioRadius) { + val nearbyPlayers = RegionManager.getLocalPlayers(location, radius) + for (player in nearbyPlayers) { + PacketRepository.send(AudioPacket::class.java, DefaultContext(player, Audio(id, delay, loops, radius), location)) + } +} + +/** + * Plays a random hurt audio for the player based on gender + * @param player the player to play hurt audio for + * @param delay the delay in client cycles (50 cycles = 1 second) + */ +fun playHurtAudio(player: Player, delay: Int = 0) { + val maleHurtAudio = intArrayOf(Sounds.HUMAN_HIT4_516, Sounds.HUMAN_HIT5_517, Sounds.HUMAN_HIT_518, Sounds.HUMAN_HIT_6_522) + val femaleHurtAudio = intArrayOf(Sounds.FEMALE_HIT_506, Sounds.FEMALE_HIT_507, Sounds.FEMALE_HIT2_508, Sounds.FEMALE_HIT_2_510) + if (player.isMale) { + playAudio(player, maleHurtAudio.random(), delay) + } else { + playAudio(player, femaleHurtAudio.random(), delay) + } } /** @@ -742,10 +1007,18 @@ fun openDialogue(player: Player, dialogue: Any, vararg args: Any) { is Int -> player.dialogueInterpreter.open(dialogue, *args) is DialogueFile -> player.dialogueInterpreter.open(dialogue, *args) is SkillDialogueHandler -> dialogue.open() - else -> SystemLogger.logErr(ContentAPI::class.java, "Invalid object type passed to openDialogue() -> ${dialogue.javaClass.simpleName}") + else -> log(ContentAPI::class.java, Log.ERR, "Invalid object type passed to openDialogue() -> ${dialogue.javaClass.simpleName}") } } +/** + * 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 @@ -793,6 +1066,17 @@ fun findLocalNPC(entity: Entity, id: Int): NPC? { return RegionManager.getLocalNpcs(entity).firstOrNull { it.id == id } } +/** + * Gets an NPC with the given ID in the same general area as the given Entity + * @param location The location to search around. + * @param distance The maximum distance to the entity. + * @returns an NPC matching the given ID or null if none is found + */ +fun findLocalNPCs(location: Location, distance: Int): MutableList { + return RegionManager.getLocalNpcs(location, distance) +} + + /** * Gets a list of nearby NPCs that match the given IDs. * @param entity the entity to check around @@ -848,6 +1132,131 @@ fun removeAttributes(entity: Entity, vararg attributes: String) { for (attribute in attributes) removeAttribute(entity, attribute) } +/** + * Adds the given timer to the entity's active timers. + * @param entity the entity whose timers are being added to + * @param timer the timer being added +**/ +fun registerTimer (entity: Entity, timer: RSTimer?) { + if (timer == null) return + entity.timers.registerTimer (timer) +} + +/** + * Used to fetch the existing, active, non-abstract and non-anonymous timer with the given identifier, or start a new timer if none exists and return that. + * @param entity the entity whose timers we're retrieving + * @param identifier the identifier of the timer, refer to the individual timer class for this token. + * @param args various args to pass to the initialization of the timer, if applicable. + * @return Either the existing active timer, or a new timer initialized with the passed args if none exists yet. +**/ +fun getOrStartTimer (entity: Entity, identifier: String, vararg args: Any) : RSTimer? { + val existing = getTimer (entity, identifier) + if (existing != null) + return existing + return spawnTimer (identifier, *args).also { registerTimer (entity, it) } +} + +/** + * Used to fetch the existing, active, non-abstract and non-anonymous timer with the given type, or start a new timer if none exists and return that. + * @param entity the entity whose timer we're retrieving + * @param T the type of timer we are fetching + * @param args the various args to pass to the initialization of the timer, if applicable. + * @return Either the existing, active timer or a new timer initialized with the passed args if none exists yet. +**/ +inline fun getOrStartTimer (entity: Entity, vararg args: Any) : T { + val existing = getTimer (entity) + if (existing != null) + return existing + return spawnTimer (*args).also { registerTimer (entity, it) } +} + +/** + * Used to fetch a new instance of a registered (see: non-anonymous, non-abstract) timer with the given configuration args + * @param identifier the string identifier for the timer. e.g. poison's is "poison" + * @param args various arbitrary arguments to be passed to the timer's constructor. Refer to the timer in question for what the args are expected to be. + * @return a timer instance configured with your given args, or null if no timer with the given key exists in the registry. +**/ +fun spawnTimer (identifier: String, vararg args: Any) : RSTimer? { + return TimerRegistry.getTimerInstance (identifier, *args) +} + +/** + * Used to fetch a new instance of a registered (see: non-anonymous, non-abstract) timer with the given configuration args + * @param T the type of the timer you're trying to retrieve. The timer registry will be searched for a timer of this type. + * @param args various arbitrary arguments to be passed to the timer's constructor. Refer to the timer in question for what the args are expected to be. + * @return a timer instance configured with your given args, or null if the timer is not listed in the registry (if this happens, your timer is either abstract or anonymous.) +**/ +inline fun spawnTimer (vararg args: Any) : T { + return TimerRegistry.getTimerInstance (*args)!! +} + +/** + * Used to check if a timer of the given type is registered and active in the entity's timers. + * @param T the type of timer + * @param entity the entity whose timers are being checked + * @return true if there is a timer registered and active with the given type. +**/ +inline fun hasTimerActive (entity: Entity) : Boolean { + return getTimer(entity) != null +} + +/** + * Used to get the active instance of a timer with the given type from the entity's timers. + * @param T the type of timer + * @param entity the entity whose timers we are checking + * @return the active instance of the given type in the entity's timers, or null. +*/ +inline fun getTimer (entity: Entity) : T? { + return entity.timers.getTimer() +} + +/** + * Removes any active timers of the given type from the entity's active timers. This will remove ALL matching instances. + * @param T the type of timer + * @param entity the entity whose timers are being checked +**/ +inline fun removeTimer (entity: Entity) { + entity.timers.removeTimer() +} + +/** + * Used to check if a timer with the given identifier string is registered and active in the entity's timers. + * @param identifier the identifier token assigned to the timer. You'll have to refer to the actual timer class for this string. + * @param entity the entity whose timers are being checked + * @return true if there's a timer with the given identifier active in the entity's timers +**/ +fun hasTimerActive (entity: Entity, identifier: String) : Boolean { + return getTimer (entity, identifier) != null +} + +/** + * Used to get the active instance of a timer with the given identifier from the entity's timers. + * @param identifier the string identifier of the timer we are looking for. You'll have to refer to the actual timer class for this string. + * @param entity the entity whose timers are being checked + * @return the instance of the active timer if found, null otherwise. +**/ +fun getTimer (entity: Entity, identifier: String) : RSTimer? { + return entity.timers.getTimer(identifier) +} + +/** + * Removes any active timers with the given identifier from the entity's active timers. This will remove ALL matching instances. + * @param identifier the identifier token assigned to the timer. You'll have to refer to the actual timer class for this string. + * @param entity the entity whose timers are being checked +**/ +fun removeTimer (entity: Entity, identifier: String) { + entity.timers.removeTimer(identifier) +} + +/** + * Removes the given timer from the entity's active timers. Note this variant will only work with a reference to the same exact timer you want to remove. + * @param entity the entity whose timers we are modifying + * @param timer the timer to remove +**/ +fun removeTimer (entity: Entity, timer: RSTimer) { + entity.timers.removeTimer(timer) +} + /** * Locks the given entity for the given number of ticks * @param entity the entity to lock @@ -918,61 +1327,66 @@ fun heal(entity: Entity, amount: Int) { entity.skills.heal(amount) } -/** - * Sets the given varbit for the given player - * @param player the player to set the varbit for - * @param varpIndex the index of the VARP that contains the desired varbit. - * @param offset the offset of the desired varbit inside the varp. - * @param value the value to set the varbit to - */ -fun setVarbit(player: Player, varpIndex: Int, offset: Int, value: Int, save: Boolean = false) { - player.varpManager.get(varpIndex).setVarbit(offset, value).send(player) - if (save) player.varpManager.flagSave(varpIndex) +fun getVarp (player: Player, varpIndex: Int) : Int { + return player.varpMap[varpIndex] ?: 0 } -/** - * Sets the given varbit for the given player. - * @param player the player to set the varbit for. - * @param varbitId the ID of the varbit to set. - * @param value the value to set the varbit to. - * @param save whether or not we should save this setting (default false) - */ -fun setVarbit(player: Player, varbitId: Int, value: Int, save: Boolean = false) { - player.varpManager.setVarbit(varbitId, value) - if (save) { - val def = VarbitDefinition.forId(varbitId) - player.varpManager.flagSave(def.varpId) +fun getVarbit (player: Player, def: VarbitDefinition) : Int { + val mask = def.mask + val current = getVarp (player, def.varpId) + return (current shr def.startBit) and mask +} + +fun getVarbit (player: Player, varbitId: Int) : Int { + val def = VarbitDefinition.forId(varbitId) + return getVarbit (player, def) +} + +@JvmOverloads +fun setVarp (player: Player, varpIndex: Int, value: Int, save: Boolean = false) { + player.varpMap[varpIndex] = value + 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 + val current = getVarp (player, def.varpId) and (mask shl def.startBit).inv() + val newValue = (value and mask) shl def.startBit + setVarp (player, def.varpId, current or newValue, save) +} + +@JvmOverloads +fun setVarbit (player: Player, varbitId: Int, value: Int, save: Boolean = false) { + val def = VarbitDefinition.forId(varbitId) + + if (def == null) { + logWithStack (ContentAPI::class.java, Log.ERR, "Trying to setVarbit $varbitId, which doesn't seem to exist.") + return } + + setVarbit (player, def, value, save) } -/** - * Clears all bits for a given varp index - * @param player the player to clear for - * @param varpIndex the index of the varp to clear - */ -fun clearVarp(player: Player, varpIndex: Int) { - player.varpManager.get(varpIndex).clear() +fun setVarc (player: Player, varc: Int, value: Int) +{ + player.packetDispatch.sendVarcUpdate(varc.toShort(), value) } -/** - * Gets the value of all bits collected together from a given varp - * @param player the player to get the varp for - * @param varpIndex the index of the varp to calculate the value of - * @return the value of the varp - */ -fun getVarpValue(player: Player, varpIndex: Int): Int { - return player.varpManager.get(varpIndex).getValue() -} - -/** - * Gets the value of a specific varbit - * @param player the player to get the value for - * @param varpIndex the index of th varp containinig the desired varbit - * @param offset the offset of the varbit inside the varp - * @return the value of the given varbit - */ -fun getVarbitValue(player: Player, varpIndex: Int, offset: Int): Int { - return player.varpManager.get(varpIndex).getVarbitValue(offset) ?: 0 +fun reinitVarps (player: Player) { + for ((index, value) in player.varpMap) { + setVarp(player, index, value, player.saveVarp[index] ?: false) + } } /** @@ -994,6 +1408,84 @@ fun forceWalk(entity: Entity, dest: Location, type: String) { path.walk(entity) } +/** + * Returns a location truncated to the appropriate pathfinding limit +**/ +fun truncateLoc (mover: Entity, destination: Location) : Pair { + val vector = Vector.betweenLocs(mover.location, destination) + val normVec = vector.normalized() + val mag = vector.magnitude() + + var multiplier = if (mover is NPC) 14.0 else ServerConstants.MAX_PATHFIND_DISTANCE.toDouble() + var clampedMultiplier = min(multiplier, mag) + + var truncated = multiplier == clampedMultiplier + + return Pair(truncated, mover.location.transform(normVec * clampedMultiplier)) +} + +/** + * Force a player to move from the start location to the dest location + * @param player the player we are moving + * @param start the start location + * @param dest the end location + * @param startArrive the number of client cycles to take moving the player to the start location + * @param destArrive the number of client cycles to take moving the player to the end location from the start location + * @param direction (optional) the direction to face the player during the movement + * @param anim (optional) the animation to use throughout the movement + * @param callback (optional) a callback called when the forced movement completes + * @see NOTE: There are 30 client cycles per second. +*/ +fun forceMove (player: Player, start: Location, dest: Location, startArrive: Int, destArrive: Int, dir: Direction? = null, anim: Int = 819, callback: (()->Unit)? = null) { + var direction: Direction + + if (dir == null) { + var delta = Location.getDelta(start, dest) + var x = abs(delta.x) + var y = abs(delta.y) + + if (x > y) + direction = Direction.getDirection(delta.x, 0) + else + direction = Direction.getDirection(0, delta.y) + } else direction = dir + + val startLoc = Location.create(start) + val destLoc = Location.create(dest) + var startArriveTick = getWorldTicks() + cyclesToTicks (startArrive) + 1 + var destArriveTick = startArriveTick + cyclesToTicks (destArrive) + var maskSet = false + + delayEntity(player, (destArriveTick - getWorldTicks()) + 1) + queueScript (player, 0, QueueStrength.SOFT) { stage: Int -> + if (!finishedMoving(player)) + return@queueScript keepRunning(player) + if (!maskSet) { + var ctx = ForceMoveCtx (startLoc, destLoc, startArrive, destArrive, direction) + player.updateMasks.register(EntityFlag.ForceMove, ctx) + maskSet = true + } + + var tick = getWorldTicks() + if (tick < startArriveTick) { + return@queueScript keepRunning(player) + } else if (tick < destArriveTick) { + if (animationFinished(player)) + animate (player, anim) + return@queueScript keepRunning(player) + } else if (tick >= destArriveTick) { + try { + callback?.invoke() + } catch (e: Exception) { + e.printStackTrace() + } + player.properties.teleportLocation = dest + return@queueScript stopExecuting(player) + } + return@queueScript stopExecuting(player) + } +} + /** * Interrupts a given entity's walking queue * @param entity the entity to interrupt @@ -1002,16 +1494,6 @@ fun stopWalk(entity: Entity) { entity.walkingQueue.reset() } -/** - * Gets the item in the given equipment slot for the given player - * @param player the player whose equipment to pull from - * @param slot the Equipment slot to use, EquipmentSlot enum contains the options. - * @return the Item in the given slot, or null if none. - */ -fun getItemFromEquipment(player: Player, slot: EquipmentSlot): Item? { - return player.equipment.get(slot.ordinal) -} - /** * Returns a list of all valid children IDs for a given scenery ID */ @@ -1028,7 +1510,7 @@ fun adjustCharge(node: Node, amount: Int) { when (node) { is Item -> node.charge += amount is Scenery -> node.charge += amount - else -> SystemLogger.logErr(ContentAPI::class.java, "Attempt to adjust the charge of invalid type: ${node.javaClass.simpleName}") + else -> log(ContentAPI::class.java, Log.ERR, "Attempt to adjust the charge of invalid type: ${node.javaClass.simpleName}") } } @@ -1041,7 +1523,7 @@ fun getCharge(node: Node): Int { when (node) { is Item -> return node.charge is Scenery -> return node.charge - else -> SystemLogger.logErr(ContentAPI::class.java, "Attempt to get charge of invalid type: ${node.javaClass.simpleName}") + else -> log(ContentAPI::class.java, Log.ERR, "Attempt to get charge of invalid type: ${node.javaClass.simpleName}") .also { return -1 } } } @@ -1055,7 +1537,7 @@ fun setCharge(node: Node, charge: Int) { when (node) { is Item -> node.charge = charge is Scenery -> node.charge = charge - else -> SystemLogger.logErr(ContentAPI::class.java, "Attempt to set the charge of invalid type: ${node.javaClass.simpleName}") + else -> log(ContentAPI::class.java, Log.ERR, "Attempt to set the charge of invalid type: ${node.javaClass.simpleName}") } } @@ -1096,7 +1578,7 @@ fun visualize(entity: Entity, anim: A, gfx: G) { * @param pulse the Pulse object to submit */ fun submitWorldPulse(pulse: Pulse) { - GameWorld.Pulser.submit(pulse) + Pulser.submit(pulse) } /** @@ -1206,6 +1688,17 @@ fun setInterfaceText(player: Player, string: String, iface: Int, child: Int) { player.packetDispatch.sendString(string, iface, child) } +/** + * Allows you to hide or show specific children in an interface + * @param player the player to send the packet to + * @param iface the ID of the interface to use + * @param child the index of the child to hide or show + * @param hide if the child should be hidden or not + */ +fun setComponentVisibility(player: Player, iface: Int, child: Int, hide: Boolean) { + player.packetDispatch.sendInterfaceConfig(iface, child, hide) +} + /** * Closes any open (non-chat) interfaces for the player * @param player the player to close the interface for @@ -1222,14 +1715,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)) } /** @@ -1243,14 +1747,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) } /** @@ -1294,6 +1812,18 @@ fun sendItemOnInterface(player: Player, iface: Int, child: Int, item: Int, amoun player.packetDispatch.sendItemOnInterface(item, amount, iface, child) } +/** + * Sends a zoomed item to a specific child on an interface + * @param player the player to send the packet to + * @param iface the ID of the interface to send the item onto + * @Param child the index of the child on the interface to send the item onto + * @param item the ID of the item to send + * @param zoom the amount of zoom to apply to the item - defaults to 230 + */ +fun sendItemZoomOnInterface(player: Player, iface: Int, child: Int, item: Int, zoom: Int = 230) { + player.packetDispatch.sendItemZoomOnInterface(item, zoom, iface, child) +} + /** * Sends a dialogue box with a single item and some text * @param player the player to send it to @@ -1319,8 +1849,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, message) + is Int -> player.dialogueInterpreter.sendDoubleItemMessage(item1, item2 as Int, message) + } } /** @@ -1359,6 +1892,7 @@ fun sendInputDialogue(player: Player, type: InputType, prompt: String, handler: } player.setAttribute("runscript", handler) + player.setAttribute("input-type", type) } /** @@ -1403,37 +1937,54 @@ fun runTask(entity: Entity, delay: Int = 0, repeatTimes: Int = 1, task: () -> Un * @param player the player to get the QP for * @return the number of QP the player has */ -fun getQP(player: Player): Int { +fun getQuestPoints(player: Player): Int { return player.questRepository.points } /** * Gets the stage for the given quest for the given player */ -fun questStage(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 + */ +fun isQuestInProgress(player: Player, quest: Quests, startStage: Int, endStage: Int): Boolean { + return player.questRepository.getStage(quest) in startStage..endStage +} + /** * 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 } +/** + * Get the quest for a player. + * @param player The player to get the quest for. + * @param quest The quest name string + * @return the quest object + */ +fun getQuest(player: Player, quest: Quests): Quest { + return player.questRepository.getQuest(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 @@ -1444,7 +1995,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) } @@ -1507,14 +2058,101 @@ fun sendGraphics(gfx: G, location: Location) { } } +/** + * Instructs the given player's client to execute a given CS2 script with the given arguments + * @param player the Player + * @param scriptId the ID of the CS2 script to execute + * @param arguments the various arguments passed to the script. +**/ +fun runcs2 (player: Player, scriptId: Int, vararg arguments: Any) { + var typeString = StringBuilder() + var finalArgs = Array (arguments.size) { null } + try { + for (i in 0 until arguments.size) { + val arg = arguments[i] + if (arg is Int) + typeString.append("i") + else if (arg is String) + typeString.append("s") + else + throw IllegalArgumentException("Argument at index $i ($arg) is not an acceptable type! Only string and int are accepted.") + finalArgs[arguments.size - i - 1] = arg + } + player.packetDispatch.sendRunScript(scriptId, typeString.toString(), *finalArgs) + } catch (e: Exception) { + e.printStackTrace() + } +} + +/** + * Opens a generic item selection prompt with a glowing background, with your own callback to handle the selection. + * @param player the player we are openinig the prompt for + * @param options the right-click options the items should have + * @param keepAlive whether or not the selection prompt should remain open for multiple interactions + * @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) { + 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)) + scriptArgs[6 + i] = options[i] + runcs2(player, 150, *scriptArgs) + val settings = IfaceSettingsBuilder() + .enableOptions(0 until 9) + .build() + player.packetDispatch.sendIfaceSettings(settings, 18, 12, 0, 28) + setAttribute(player, "itemselect-callback", callback) + setAttribute(player, "itemselect-keepalive", keepAlive) +} 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); + sendNews("${player.username} has just received: ${item.amount} x ${item.name}.") + PlayerStatsCounter.incrementRareDrop(player, item) } } +fun hasRequirement(player: Player, req: QuestReq, message: Boolean = true) : Boolean { + var (isMet, unmetReqs) = req.evaluate(player) + val messageList = ArrayList() + + var totalSoftQp = 0 + var totalHardQp = 0 + + for (req in unmetReqs) { + when (req) { + is QPCumulative -> totalSoftQp += req.amount + is QPReq -> if (req.amount > totalHardQp) totalHardQp = req.amount + } + } + + var neededQp = min(max(totalSoftQp, totalHardQp), player.questRepository.getAvailablePoints()) + + isMet = isMet && neededQp <= player.questRepository.getPoints() + + if (isMet) return true + + if (unmetReqs.size == 2 && unmetReqs[0] is QuestReq) { + 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.quest} to access this.") + messageList.add ("Please check the page in your quest journal for more info.") + } + + if (message) + for (message in messageList) + sendMessage(player, message) + + return false +} + +@JvmOverloads +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) +} + /** * Generates a list of skill names which the player has mastered * @param player the player @@ -1567,10 +2205,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++ } } @@ -1680,8 +2321,9 @@ fun unnote(item: Item): Item { * @return True if Seal of Passage present, false otherwise. */ fun hasSealOfPassage(player: Player): Boolean { - return isEquipped(player, Items.SEAL_OF_PASSAGE_9083) - || inInventory(player, Items.SEAL_OF_PASSAGE_9083) + // Snowscape: modified to include checking the bank as well as equipment or inventory. + return player.hasItem(Item(Items.SEAL_OF_PASSAGE_9083)) + //return inEquipmentOrInventory(player, Items.SEAL_OF_PASSAGE_9083) } /** @@ -1706,15 +2348,17 @@ fun getServerConfig(): Toml { } fun getPathableRandomLocalCoordinate(target: Entity, radius: Int, center: Location, maxAttempts: Int = 3): Location { - val swCorner = center.transform(-radius, -radius, center.z) - val neCorner = center.transform(radius, radius, center.z) + var maxRadius = Vector.deriveWithEqualComponents(ServerConstants.MAX_PATHFIND_DISTANCE.toDouble()).x - 1 + var effectiveRadius = min(radius, maxRadius.toInt()) + val swCorner = center.transform(-effectiveRadius, -effectiveRadius, center.z) + val neCorner = center.transform(effectiveRadius, effectiveRadius, center.z) val borders = ZoneBorders(swCorner.x, swCorner.y, neCorner.x, neCorner.y, center.z) var attempts = maxAttempts var success: Boolean while (attempts-- > 0) { val dest = borders.randomLoc - val path = Pathfinder.find(target, dest) + val path = Pathfinder.find(center, dest, target.size()) success = path.isSuccessful && !path.isMoveNear if (success) return dest } @@ -1722,6 +2366,23 @@ fun getPathableRandomLocalCoordinate(target: Entity, radius: Int, center: Locati return target.location } +/** + * Returns a pathable cardinal location in a 1 tile radius in order of west, south, east, north. + * @param entity the entity used to check if the loc is pathable + * @param center the center location + */ +fun getPathableCardinal(target: Entity, center: Location) : Location { + var tiles = center.cardinalTiles + + for (tile in tiles) { + val path = Pathfinder.find(center, tile, target.size()) + if (path.isSuccessful && !path.isMoveNear) + return tile + } + + return center +} + /** * Returns the player's active slayer task. * @author bushtail @@ -1729,7 +2390,7 @@ fun getPathableRandomLocalCoordinate(target: Entity, radius: Int, center: Locati * @return the slayer task. */ fun getSlayerTask(player: Player): Tasks? { - return SlayerManager.getInstance(player).task + return SlayerManager.getInstance(player).activeTask } /** @@ -1787,7 +2448,7 @@ fun getSlayerMasterLocation(player: Player): String { */ fun getSlayerTip(player: Player): Array { return if (hasSlayerTask(player)) { - SlayerManager.getInstance(player).task?.tip!! + getSlayerTask(player)?.tip!! } else { arrayOf("You need something new to hunt.") } @@ -2185,52 +2846,21 @@ 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 } -/** - * Determines whether or not a specified entity has a state - * @param entity the entity whose state we are checking - * @param state the state to check for - * @return whether or not the entity has the provided state - */ -fun hasState(entity: Entity, state: EntityState) : Boolean { - return entity.stateManager.hasState(state) -} - -/** - * Adds a state to the entity - * @param entity the entity whose state we are adding - * @param state the state to add - * @param override whether or not it's to override another state - */ -fun addState(entity: Entity, state: EntityState, override: Boolean, vararg args: Any?) { - if(!entity.stateManager.hasState(state)) { - entity.stateManager.register(state, override, *args) - } -} - -/** - * Removes a state from the entity - * @param entity the entity whose state we are removing - * @param state the state to remove - */ -fun removeState(entity: Entity, state: EntityState) { - entity.stateManager.remove(state) -} - /** * Determines whether or not specified node is a player * @param entity the node whom we are checking @@ -2249,4 +2879,328 @@ fun addDialogueAction(player: Player, action: core.game.dialogue.DialogueAction) player.dialogueInterpreter.addAction(action) } -private class ContentAPI \ No newline at end of file +/** + * Logs a message to the server console + * @param origin simply put (Kotlin) this::class.java or (Java) this.getClass() + * @param type the type of log: Log.FINE (default, visible on VERBOSE), Log.INFO (visible on DETAILED), Log.WARN (visible on CAUTIOUS), Log.ERR (always visible) + * @param message the actual message to log. + */ +fun log(origin: Class<*>, type: Log, message: String) { + SystemLogger.processLogEntry(origin, type, message) +} + +/** + * Logs a message to the server console along with a stack trace leading up to it. + * @param origin simply put (Kotlin) this::class.java or (Java) this.getClass() + * @param type the type of log: Log.FINE (default, visible on VERBOSE), Log.INFO (visible on DETAILED), Log.WARN (visible on CAUTIOUS), Log.ERR (always visible) + * @param message the actual message to log. +*/ +fun logWithStack(origin: Class<*>, type: Log, message: String) { + try { + throw Exception(message) + } catch (e: Exception) { + log(origin, type, "${exceptionToString(e)}") + } +} + +/** + * Takes an exception as an argument, and sends back the complete exception with stack trace as a standard string. Useful for various things. + * @param e The exception you wish to convert. + * @return a string with the full stack trace of the exception +**/ +fun exceptionToString (e: Exception) : String { + val sw = StringWriter() + val pw = PrintWriter(sw) + e.printStackTrace(pw) + return sw.toString() +} + +/** + * Used by content handlers to check if the entity is done moving yet + */ +fun finishedMoving(entity: Entity) : Boolean { + return entity.clocks[Clocks.MOVEMENT] < GameWorld.ticks +} + + +/** + * Delay the execution of the currently running script + */ +fun delayScript(entity: Entity, ticks: Int): Boolean { + entity.scripts.getActiveScript()?.let { it.nextExecution = GameWorld.ticks + ticks } + return false +} + +/** + * Set the global delay for the entity, pausing execution of all queues/scripts until passed. + */ +fun delayEntity(entity: Entity, ticks: Int) { + entity.scripts.delay = GameWorld.ticks + ticks + lock(entity, ticks) //TODO: REMOVE WHEN EVERYTHING IMPORTANT USES PROPER QUEUES - THIS IS INCORRECT BEHAVIOR +} + +fun apRange(entity: Entity, apRange: Int) { + entity.scripts.apRange = apRange + entity.scripts.apRangeCalled = true +} + +fun hasLineOfSight(entity: Entity, target: Node) : Boolean { + return CombatSwingHandler.isProjectileClipped (entity, target, false) +} + +fun animationFinished(entity: Entity) : Boolean { + return entity.clocks[Clocks.ANIMATION_END] < GameWorld.ticks +} + +fun clearScripts(entity: Entity) : Boolean { + entity.scripts.reset() + return true +} + +fun restartScript(entity: Entity) : Boolean { + if (entity.scripts.getActiveScript()?.persist != true) { + log(entity.scripts.getActiveScript()!!::class.java, Log.ERR, "Tried to call restartScript on a non-persistent script! Either use stopExecuting() or make the script persistent.") + return clearScripts(entity) + } + return true +} + +fun keepRunning(entity: Entity) : Boolean { + entity.scripts.getActiveScript()?.nextExecution = getWorldTicks() + 1 + return false +} + +fun stopExecuting(entity: Entity) : Boolean { + if (entity.scripts.getActiveScript()?.persist == true) { + log(entity.scripts.getActiveScript()!!::class.java, Log.ERR, "Tried to call stopExecuting() on a persistent script! To halt execution of a persistent script, you MUST call clearScripts()!") + return clearScripts(entity) + } + return true +} + +fun queueScript(entity: Entity, delay: Int = 1, strength: QueueStrength = QueueStrength.WEAK, persist: Boolean = false, script: (stage: Int) -> Boolean) { + val s = QueuedScript(script, strength, persist) + s.nextExecution = getWorldTicks() + delay + entity.scripts.addToQueue(s, strength) +} + +/** + * Sets the clock to the value of WORLD_TICSK + ticks. + * @param entity the entity whose clock we are updating + * @param clock the clock we are updating. Please use [core.game.interaction.Clocks] for this argument. + * @param ticks the number of ticks to delay by + * @return always returns false so this can be used as a script return value. +**/ +fun delayClock(entity: Entity, clock: Int, ticks: Int) : Boolean { + entity.clocks[clock] = getWorldTicks() + ticks + return false +} + +/** + * Checks if a clock is ready (have we elapsed any delay put into it) + * @param entity the entity whose clock we are checking + * @param clock the clock we are checking. Please use [core.game.interaction.Clocks] for this argument. + * @return true if we have elapsed the clock's wait +**/ +fun clockReady(entity: Entity, clock: Int) : Boolean { + return entity.clocks[clock] <= getWorldTicks() +} + +fun delayAttack(entity: Entity, ticks: Int) { + entity.properties.combatPulse.delayNextAttack(3) + entity.clocks[Clocks.NEXT_ATTACK] = getWorldTicks() + ticks +} + +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) + entity.clocks[Clocks.STUN] = getWorldTicks() + ticks + entity.graphics(Graphics(80, 96)) + if (entity is Player) { + playAudio(entity.asPlayer(), Sounds.STUNNED_2727) + entity.animate(Animation(424, Animator.Priority.VERY_HIGH)) + if (sendMessage) { + sendMessage(entity, "You have been stunned!") + } + } +} + +fun isStunned(entity: Entity) : Boolean { + return entity.clocks[Clocks.STUN] >= getWorldTicks() +} + +/** + * Applies poison to the target. (In other words, creates and starts a poison timer.) + * @param entity the entity who will be receiving the poison damage. + * @param source the entity to whom credit for the damage should be awarded (the attacker.) You should award credit to the victim if the poison is sourceless (e.g. from a trap or plant or something) + * @param severity the severity of the poison damage. Severity is not a 1:1 representation of damage, rather the formula `floor((severity + 4) / 5)` is used. Severity is decreased by 1 with each application of the poison, and ends when it reaches 0. + * @see To those whe ask "why severity instead of plain damage?" to which the answer is: severity is how it works authentically, and allows for scenarios where, e.g. a poison should hit 6 once, and then drop to 5 immediately. +**/ +fun applyPoison (entity: Entity, source: Entity, severity: Int) { + if(hasTimerActive(entity)) { + return + } + val existingTimer = getTimer(entity) + + if (existingTimer != null) { + existingTimer.severity = severity + existingTimer.damageSource = source + } else { + registerTimer(entity, spawnTimer(source, severity)) + } +} + +fun isPoisoned (entity: Entity) : Boolean { + return getTimer(entity) != null +} + +fun curePoison (entity: Entity) { + if (!hasTimerActive(entity)) + return + removeTimer(entity) + if (entity is Player) + sendMessage(entity, "Your poison has been cured.") +} + +fun setCurrentScriptState(entity: Entity, state: Int) { + val script = entity.scripts.getActiveScript() + if (script == null) { + log(ContentAPI::class.java, Log.WARN, "Tried to set a script state when no script was being ran!") + if (GameWorld.settings?.isDevMode != true) return + throw IllegalStateException("Script execution mistake - check stack trace and above warning log!") + } + script.state = state - 1 //set it to 1 below the state so that on next execution the state is at the expected value. +} + +/** + * Modifies prayer points by value + * @param player the player to modify prayer points + * @param amount the amount of points to modify by (positive for increment, negative for decrement) + */ +fun modPrayerPoints(player: Player, amount: Double) { + if(amount > 0) player.skills.incrementPrayerPoints(amount) + else if(amount < 0) player.skills.decrementPrayerPoints(amount.absoluteValue) + else return +} + +/** + * Iterates a container and "decants" every potion type in the container. (Decanting is the process of condensing multiple variable-dose potions into the minimum number of max-dose potions) + * @param container the container to iterate + * @return a pair where the first element is the list of items to be removed, and the second element is the list of items to add. +*/ +fun decantContainer (container: core.game.container.Container) : Pair, List> { + val doseCount = HashMap() + val toRemove = ArrayList() + val toAdd = ArrayList() + val doseRegex = Pattern.compile("(\\([0-9]\\))") //Matches "(2)" in "Defense potion (2)" + + for (item in container.toArray()) { + if (item == null) continue + val potionType = Consumables.getConsumableById(item.id)?.consumable as? Potion ?: continue + val matcher = doseRegex.matcher(item.name) + if (!matcher.find()) continue + val dosage = matcher.group(1).replace("(","").replace(")","").toIntOrNull() ?: continue + doseCount[potionType] = (doseCount[potionType] ?: 0) + dosage + toRemove.add(item) + } + + for ((consumable, count) in doseCount) { + val maxDose = consumable.ids.size + val totalMaxDosePotions = count / maxDose + val remainderDose = count % maxDose + if (totalMaxDosePotions > 0) + toAdd.add(Item(consumable.ids[0], totalMaxDosePotions)) + if (remainderDose > 0) + toAdd.add(Item(consumable.ids[consumable.ids.size - remainderDose])) + } + + val emptyVials = toRemove.size - toAdd.sumBy { it.amount } + if (emptyVials > 0) + toAdd.add(Item(229, emptyVials)) + return Pair,List>(toRemove, toAdd) +} + +/** + * Checks whether the user has a valid anti-dragonfire shield equipped. + * @param player the player whose shield we are checking. + * @param wyvern an optional parameter (default false) whether or not we are checking against wyvern fire, which ignores normal anti-dragon-shields, but accepts elemental/mind shields, etc. + * @return whether or not the player is protected by their shield. + * @see Source +**/ +fun hasDragonfireShieldProtection(player: Player, wyvern: Boolean = false): Boolean { + val shield = getItemFromEquipment(player, EquipmentSlot.SHIELD) ?: return false + return when (shield.id) { + Items.ELEMENTAL_SHIELD_2890, Items.MIND_SHIELD_9731 -> wyvern + Items.ANTI_DRAGON_SHIELD_1540 -> !wyvern + Items.DRAGONFIRE_SHIELD_11283, Items.DRAGONFIRE_SHIELD_11284 -> true + else -> false + } +} + +/** + * Calculates the expected max hit of dragonfire after checking/applying valid protections. + * @param entity the entity whose protection we are checking. + * @param maxDamage the max damage the dragonfire can do without protection. + * @param wyvern an optional parameter (default false) that defines whether or not this is wyvern fire. Wyvern fire ignores anti-fire potions and regular anti-dragon shield. + * @param unprotectableDamage an optional parameter (default 0) that defines how much damage cannot be protected against. Think of it as a minimum-max-hit. + * @param sendMessage an optional parameter (default false) whether or not to send a message notifying the player of the damage being blocked. + * @return the maximum amount of damage that can be done to the player. + * @see Source +**/ +fun calculateDragonfireMaxHit(entity: Entity, maxDamage: Int, wyvern: Boolean = false, unprotectableDamage: Int = 0, sendMessage: Boolean = false): Int { + val hasShield: Boolean + val hasPotion: Boolean + val hasPrayer: Boolean + + if (entity is Player) { + hasShield = hasDragonfireShieldProtection(entity, wyvern) + hasPotion = !wyvern && hasTimerActive(entity) + hasPrayer = entity.prayer.get(PrayerType.PROTECT_FROM_MAGIC) + + if (sendMessage) { + var message = "You are horribly burnt by the ${if (wyvern) "icy" else "fiery"} breath." + if (hasShield && hasPotion) + message = "Your potion and shield fully absorb the ${if (wyvern) "icy" else "fiery"} breath." + else if (hasShield) + message = "Your shield absorbs most of the ${if (wyvern) "icy" else "fiery"} breath." + else if (hasPotion) + message = "Your potion absorbs some of the fiery breath." + else if (hasPrayer) + message = "Your prayer absorbs some of the ${if (wyvern) "icy" else "fiery"} breath." + sendMessage(entity, message) + } + } else { + val dragonfireTokens = entity.getDragonfireProtection(!wyvern) + hasShield = dragonfireTokens and 0x2 != 0 + hasPotion = dragonfireTokens and 0x4 != 0 + hasPrayer = dragonfireTokens and 0x8 != 0 + } + + var effectiveDamage = maxDamage.toDouble() + if (hasPrayer && !hasShield && !hasPotion) + effectiveDamage -= 0.6 * maxDamage + else { + if (hasShield) + effectiveDamage -= 0.9 * maxDamage + if (hasPotion) + effectiveDamage -= 0.1 * maxDamage + } + + return Math.max(unprotectableDamage, effectiveDamage.toInt()) +} + +/** + * Opens a single interface tab from given ID. + * @param player the player to open interface for + * @param component the component ID to open + */ +fun openSingleTab(player: Player, component: Int) { + player.interfaceManager.openSingleTab(Component(component)) +} + +private class ContentAPI diff --git a/Server/src/main/core/api/DialUtils.kt b/Server/src/main/core/api/DialUtils.kt index 87e30b754..1149906cd 100644 --- a/Server/src/main/core/api/DialUtils.kt +++ b/Server/src/main/core/api/DialUtils.kt @@ -2,12 +2,20 @@ package core.api import java.util.* import kotlin.math.ceil +object DialUtils { + val tagRegex = "<([A-Za-z0-9=/]+)>".toRegex() + + fun removeMatches(message: String, regex: Regex): String { + return regex.replace(message, "") + } +} + /** * Automatically split a single continuous string into multiple comma-separated lines. * Should this not work out for any reason, you should fallback to standard npc and player methods for dialogue. */ -fun splitLines(message: String, perLineLimit: Int = 54) : Array { - var lines = Array(ceil(message.length / perLineLimit.toFloat()).toInt()) {""} +fun splitLines(message: String, perLineLimit: Int = 54): Array { + var lines = Array(ceil(DialUtils.removeMatches(message, DialUtils.tagRegex).length / perLineLimit.toFloat()).toInt()) { "" } //short circuit when possible because it's cheaper. if (lines.size == 1) { @@ -20,8 +28,28 @@ fun splitLines(message: String, perLineLimit: Int = 54) : Array { val line = StringBuilder() var accumulator = 0 + val openTags = LinkedList() + fun pushLine() { if (line.isEmpty()) return + + // find all tags that were opened or closed in the line + for (lineTag in DialUtils.tagRegex.findAll(line)) { + if (lineTag.value.get(1) == '/') { + // closing tag encountered; remove it from the list of open tags + for (openTag in openTags.descendingIterator()) { + val lineTagName = lineTag.value.substring(2, lineTag.value.length - 1) + val openTagName = openTag.substring(1, lineTag.value.length - 2) + if (lineTagName == openTagName) { + openTags.remove(openTag) + break + } + } + } else { + openTags.add(lineTag.value) + } + } + //allow array to be resized - there are specific edgecases where it becomes necessary. (Check the unit test for example) if (lines.size == index) lines = lines.plus(line.toString()) @@ -30,11 +58,15 @@ fun splitLines(message: String, perLineLimit: Int = 54) : Array { index++ line.clear() accumulator = 0 + + // if any unclosed tags remain, add them to the beginning of the new line + for (tag in openTags) line.append(tag) + openTags.clear() } while (!tokenQueue.isEmpty()) { - val shouldSpace = line.isNotEmpty() - accumulator += tokenQueue.peek().length + val shouldSpace = DialUtils.removeMatches(line.toString(), DialUtils.tagRegex).isNotEmpty() + accumulator += DialUtils.removeMatches(tokenQueue.peek(), DialUtils.tagRegex).length if (shouldSpace) accumulator += 1 if (accumulator > perLineLimit) { @@ -47,5 +79,12 @@ fun splitLines(message: String, perLineLimit: Int = 54) : Array { } pushLine() + + // If there's 5 lines, merge lines 4 and 5 into line 4. + if (lines.size > 4) { + lines[3] = lines[3] + "
" + lines[4] + lines = lines.sliceArray(0..3) + } + return lines -} \ No newline at end of file +} diff --git a/Server/src/main/core/api/EquipmentSlot.kt b/Server/src/main/core/api/EquipmentSlot.kt index 53a60fcf6..0b09e3751 100644 --- a/Server/src/main/core/api/EquipmentSlot.kt +++ b/Server/src/main/core/api/EquipmentSlot.kt @@ -1,9 +1,9 @@ package core.api enum class EquipmentSlot { - HAT, + HEAD, CAPE, - AMULET, + NECK, WEAPON, CHEST, SHIELD, @@ -14,5 +14,30 @@ enum class EquipmentSlot { FEET, HIDDEN_3, RING, - AMMO + AMMO; + + companion object { + private val slotMap = HashMap() + + init { + slotMap["head"] = HEAD; slotMap["helm"] = HEAD; slotMap ["helmet"] = HEAD + slotMap["cape"] = CAPE; slotMap["back"] = CAPE + slotMap["neck"] = NECK; slotMap["amulet"] = NECK + slotMap["weapon"] = WEAPON; slotMap["main"] = WEAPON + slotMap["chest"] = CHEST; slotMap["body"] = CHEST; slotMap["torso"] = CHEST + slotMap["shield"] = SHIELD; slotMap["off"] = SHIELD + slotMap["legs"] = LEGS; slotMap["leg"] = LEGS + slotMap["hands"] = HANDS; slotMap["hand"] = HANDS; slotMap["brace"] = HANDS; slotMap["bracelet"] = HANDS + slotMap["feet"] = FEET + slotMap["ring"] = RING + slotMap["ammo"] = AMMO; slotMap["ammunition"] = AMMO + } + + /** + * Return the equipment slot by name. Return null if matching no slot. + */ + fun slotByName(input: String): EquipmentSlot? { + return slotMap[input.lowercase()] + } + } } \ No newline at end of file diff --git a/Server/src/main/core/api/Event.kt b/Server/src/main/core/api/Event.kt index d608ad716..1c1bc1ea1 100644 --- a/Server/src/main/core/api/Event.kt +++ b/Server/src/main/core/api/Event.kt @@ -5,8 +5,10 @@ import core.game.event.* object Event { @JvmStatic val ResourceProduced = ResourceProducedEvent::class.java @JvmStatic val NPCKilled = NPCKillEvent::class.java + @JvmStatic val BoneBuried = BoneBuryEvent::class.java @JvmStatic val Teleported = TeleportEvent::class.java @JvmStatic val FireLit = LitFireEvent::class.java + @JvmStatic val LightSourceLit = LitLightSourceEvent::class.java @JvmStatic val Interacted = InteractionEvent::class.java @JvmStatic val ButtonClicked = ButtonClickEvent::class.java @JvmStatic val DialogueOpened = DialogueOpenEvent::class.java @@ -33,4 +35,6 @@ object Event { @JvmStatic val SummoningPointsRecharged = SummoningPointsRechargeEvent::class.java @JvmStatic val PrayerPointsRecharged = PrayerPointsRechargeEvent::class.java @JvmStatic val XpGained = XPGainEvent::class.java -} \ No newline at end of file + @JvmStatic val PrayerActivated = PrayerActivatedEvent::class.java + @JvmStatic val PrayerDeactivated = PrayerDeactivatedEvent::class.java +} diff --git a/Server/src/main/core/api/God.kt b/Server/src/main/core/api/God.kt index 72815bc97..c2e16c32d 100644 --- a/Server/src/main/core/api/God.kt +++ b/Server/src/main/core/api/God.kt @@ -1,10 +1,125 @@ package core.api +import org.rs09.consts.Items + enum class God(vararg val validItems: Int) { - ARMADYL(87, 11694, 11718, 11720, 11722, 12670, 12671, 14671), - BANDOS(11061, 11696, 11724, 11726, 11728), - SARADOMIN(1718, 2412, 2415, 2661, 2663, 2665, 2667, 3479, 3675, 3489, 3840, 4682, 6762, 8055, 10384, 10386, 10388, 10390, 10440, 10446, 10452, 10458, 10464, 10470, 11181, 11698, 11730,542,544), - ZAMORAK(11716, 11700, 1724, 2414, 2417, 2653, 2655, 2657, 2659, 3478, 3674, 3841, 3842, 3852, 4683, 6764, 8056, 10368, 10370, 10372, 10374, 10444, 10450, 10456, 10460, 10468, 10474, 10776, 10786, 10790, 14662), - GUTHIX, - ZAROS -} \ No newline at end of file + ARMADYL( + Items.ARMADYL_CHESTPLATE_11720, + Items.ARMADYL_GODSWORD_11694, + Items.ARMADYL_HELMET_11718, + Items.ARMADYL_HELMET_CHARGED_12671, + Items.ARMADYL_HELMET_E_12670, + Items.ARMADYL_PENDANT_87, + Items.ARMADYL_PLATESKIRT_11722, + ), + BANDOS( + Items.ANCIENT_MACE_11061, + Items.BANDOS_BOOTS_11728, + Items.BANDOS_CHESTPLATE_11724, + Items.BANDOS_GODSWORD_11696, + Items.BANDOS_TASSETS_11726, + ), + SARADOMIN( + Items.ANCIENT_SYMBOL_11181, + Items.GILDED_KITESHIELD_3489, + 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, + Items.SARADOMIN_CLOAK_10446, + Items.SARADOMIN_COIF_10390, + Items.SARADOMIN_CROZIER_10440, + Items.SARADOMIN_DHIDE_10386, + Items.SARADOMIN_FULL_HELM_2665, + Items.SARADOMIN_GODSWORD_11698, + Items.SARADOMIN_KITESHIELD_2667, + Items.SARADOMIN_MITRE_10452, + Items.SARADOMIN_MJOLNIR_6762, + 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, + Items.ZAMORAK_CLOAK_10450, + Items.ZAMORAK_COIF_10374, + Items.ZAMORAK_CROZIER_10444, + Items.ZAMORAK_DHIDE_10370, + Items.ZAMORAK_DHIDE_10790, + Items.ZAMORAK_FULL_HELM_2657, + Items.ZAMORAK_GODSWORD_11700, + Items.ZAMORAKIAN_SPEAR_11716, + Items.ZAMORAK_KITESHIELD_2659, + Items.ZAMORAK_MITRE_10456, + Items.ZAMORAK_MJOLNIR_6764, + Items.ZAMORAK_PLATEBODY_10776, + 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.GUTHIX_BRACERS_10376, + Items.GUTHIX_CAPE_10720, + Items.GUTHIX_CHAPS_10380, + Items.GUTHIX_CLOAK_10448, + Items.GUTHIX_COIF_10382, + 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, + ) +} diff --git a/Server/src/main/core/api/MapArea.kt b/Server/src/main/core/api/MapArea.kt index d7f12f06d..28631f54d 100644 --- a/Server/src/main/core/api/MapArea.kt +++ b/Server/src/main/core/api/MapArea.kt @@ -14,10 +14,10 @@ import core.game.world.map.zone.ZoneRestriction interface MapArea : ContentInterface { var zone: MapZone get(){ - return zoneMaps[this.javaClass.simpleName + "MapArea"]!! + return zoneMaps[this::class.java.simpleName + "MapArea"]!! } set(value) { - zoneMaps[this.javaClass.simpleName + "MapArea"] = value + zoneMaps[this::class.java.simpleName + "MapArea"] = value } fun defineAreaBorders() : Array diff --git a/Server/src/main/core/api/SnowscapeAPI.kt b/Server/src/main/core/api/SnowscapeAPI.kt new file mode 100644 index 000000000..62f397272 --- /dev/null +++ b/Server/src/main/core/api/SnowscapeAPI.kt @@ -0,0 +1,31 @@ +package core.api + +import core.game.node.Node +import core.game.node.entity.Entity +import core.game.node.entity.npc.NPC + +/** + Separate API for Snowscape customizations to reduce impact on upstream code. + +*/ + + + +/** Checks if a given entity is classified as undead. Used for Crumble Undead. + Includes more undead than the default game, notably the Barrows Brothers. +*/ +fun isUndead (entity: Entity): Boolean { + if (entity.getId() in 2025..2030) return true // Barrows brothers + + // Includes any enemy that can be part of an undead slayer task. + val slayerTask = (entity as NPC).getTask() + if (slayerTask != null && slayerTask.undead) return true + + // Final slower check using name. Most cases should be caught by the slayer tasks, but this should guarantee the rest. + val entityName = entity.getName().lowercase() + val undeadNames = arrayOf("zombie","spirit","revenant","ghast","ghost","mummy","shade","skeleton","skogre","zogre","undead","tortured soul","spectre","ankou","banshee","crawling hand") + for (undeadName in undeadNames) { + if (entityName.contains(undeadName)) return true + } + return false +} \ No newline at end of file diff --git a/Server/src/main/core/api/regionspec/RegionSpecification.kt b/Server/src/main/core/api/regionspec/RegionSpecification.kt index 78dde9ce4..863c98024 100644 --- a/Server/src/main/core/api/regionspec/RegionSpecification.kt +++ b/Server/src/main/core/api/regionspec/RegionSpecification.kt @@ -3,7 +3,6 @@ package core.api.regionspec import core.api.regionspec.contracts.* import core.game.world.map.Region import core.game.world.map.RegionChunk -import core.game.world.map.RegionPlane import core.game.world.map.build.DynamicRegion class RegionSpecification(val regionContract: RegionSpecContract = EmptyRegionContract(), vararg val chunkContracts: ChunkSpecContract = arrayOf(EmptyChunkContract())) { diff --git a/Server/src/main/core/api/regionspec/contracts/UseExistingRegionContract.kt b/Server/src/main/core/api/regionspec/contracts/UseExistingRegionContract.kt index d6a30a3f7..24e4bded4 100644 --- a/Server/src/main/core/api/regionspec/contracts/UseExistingRegionContract.kt +++ b/Server/src/main/core/api/regionspec/contracts/UseExistingRegionContract.kt @@ -4,6 +4,6 @@ import core.game.world.map.build.DynamicRegion class UseExistingRegionContract(val region: DynamicRegion) : RegionSpecContract { override fun instantiateRegion(): DynamicRegion { - return region; + return region } } \ No newline at end of file diff --git a/Server/src/main/core/api/utils/CameraUtils.kt b/Server/src/main/core/api/utils/CameraUtils.kt index f242b5b49..d2d95f672 100644 --- a/Server/src/main/core/api/utils/CameraUtils.kt +++ b/Server/src/main/core/api/utils/CameraUtils.kt @@ -2,4 +2,22 @@ package core.api.utils object CameraUtils { +} + +/* + * All camera shake types were found by facing the camera North using + * these values: + * Type [0-4] Jit: 0 Amp: 0 Freq: 128 Speed: 2 + * + * **See Also:** [This forum post](https://forum.2009scape.org/viewtopic.php?t=173-in-game-camera-movement-documentation-server-sided) + * + * WARNING: Playing around with camera shake values may potentially trigger seizures for people with photosensitive epilepsy. + * Please use care when discovering camera values. + */ +enum class CameraShakeType { + TRUCK, // camera movement from left to right + PEDESTAL, // camera movement vertically up to down, fixated on one location + DOLLY, // camera movement forwards to backwards + PAN, // camera movement horizontally, fixed on a certain point + TILT // camera movement vertically, fixed on a certain point } \ No newline at end of file diff --git a/Server/src/main/core/api/utils/Exceptions.kt b/Server/src/main/core/api/utils/Exceptions.kt new file mode 100644 index 000000000..4dd71a813 --- /dev/null +++ b/Server/src/main/core/api/utils/Exceptions.kt @@ -0,0 +1,3 @@ +package core.api.utils + +class ConfigParseException (message: String) : Exception (message) 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 b89f82eea..000000000 --- a/Server/src/main/core/api/utils/GlobalKillCounter.kt +++ /dev/null @@ -1,118 +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.game.node.item.Item -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){ - SystemLogger.logErr(this::class.java, "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/NPCDropTable.kt b/Server/src/main/core/api/utils/NPCDropTable.kt index 05e1f96be..defe06ea6 100644 --- a/Server/src/main/core/api/utils/NPCDropTable.kt +++ b/Server/src/main/core/api/utils/NPCDropTable.kt @@ -5,17 +5,23 @@ import core.game.node.item.Item class NPCDropTable : WeightBasedTable() { private val charmDrops = WeightBasedTable() + private val tertiaryDrops = WeightBasedTable() fun addToCharms(element: WeightedItem): Boolean { return charmDrops.add(element) } - override fun roll(receiver: Entity?): ArrayList { + fun addToTertiary(element: WeightedItem) : Boolean { + return tertiaryDrops.add(element) + } + + override fun roll(receiver: Entity?, times: Int): ArrayList { val items = ArrayList() // Charms table is always rolled, and should contain explicit "Nothing" // entries at the data level to account for the chance to not drop a charm. - items.addAll(charmDrops.roll()) - items.addAll(super.roll(receiver)) + items.addAll(charmDrops.roll(receiver, times)) + items.addAll(tertiaryDrops.roll(receiver, times)) + items.addAll(super.roll(receiver, times)) return items } 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..d6b248c55 --- /dev/null +++ b/Server/src/main/core/api/utils/Permadeath.kt @@ -0,0 +1,141 @@ +package core.api.utils + +import content.global.skill.construction.HouseLocation +import content.minigame.blastfurnace.BFPlayerState +import content.minigame.blastfurnace.BlastFurnace +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() + + //Snowscape custom data + target.plainSatchel.clear() + target.greenSatchel.clear() + target.redSatchel.clear() + target.goldSatchel.clear() + target.runeSatchel.clear() + target.blackSatchel.clear() + target.summoningPouches.clear() + target.costumeRoomBank.clear() + + // 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) + } + } + } + + // 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/PlayerCamera.kt b/Server/src/main/core/api/utils/PlayerCamera.kt index a775cc7ae..8be7bed91 100644 --- a/Server/src/main/core/api/utils/PlayerCamera.kt +++ b/Server/src/main/core/api/utils/PlayerCamera.kt @@ -5,6 +5,16 @@ import core.net.packet.PacketRepository import core.net.packet.context.CameraContext import core.net.packet.out.CameraViewPacket +/** + * Player camera + * + * @property player + * @constructor Create empty Player camera + * **See Also:** [This forum post](https://forum.2009scape.org/viewtopic.php?t=173-in-game-camera-movement-documentation-server-sided) + * + * WARNING: Playing around with camera values may potentially trigger seizures for people with photosensitive epilepsy. + * Please use care when using camera values. + */ class PlayerCamera(val player: Player?) { var ctx: CameraContext? = null @@ -29,4 +39,14 @@ class PlayerCamera(val player: Player?) { ctx = CameraContext(player,CameraContext.CameraType.POSITION,x,y,height,speed,1) PacketRepository.send(CameraViewPacket::class.java,ctx) } + fun shake(cameraType: Int, jitter: Int, amplitude: Int, frequency: Int, speed: Int){ + player ?: return + ctx = CameraContext(player,CameraContext.CameraType.SHAKE,cameraType,jitter,amplitude,frequency,speed) + PacketRepository.send(CameraViewPacket::class.java,ctx) + } + fun reset(){ + player ?: return + ctx = CameraContext(player,CameraContext.CameraType.RESET, -1, -1, -1, -1, -1) + PacketRepository.send(CameraViewPacket::class.java,ctx) + } } \ No newline at end of file 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/Vector.kt b/Server/src/main/core/api/utils/Vector.kt new file mode 100644 index 000000000..eb0688ff9 --- /dev/null +++ b/Server/src/main/core/api/utils/Vector.kt @@ -0,0 +1,75 @@ +package core.api.utils + +import core.game.world.map.Direction +import core.game.world.map.Location +import kotlin.math.* + +class Vector (val x: Double, val y: Double) { + fun normalized() : Vector { + val magnitude = magnitude() + val xComponent = x / magnitude + val yComponent = y / magnitude + return Vector(xComponent, yComponent) + } + + fun magnitude() : Double { + return sqrt(x.pow(2.0) + y.pow(2.0)) + } + + operator fun Vector.unaryMinus() = Vector(-x, -y) + + operator fun times (other: Double) : Vector { + return Vector(this.x * other, this.y * other) + } + + operator fun times (other: Int) : Vector { + return Vector(this.x * other, this.y * other) + } + + operator fun plus (other: Vector) : Vector { + return Vector(this.x + other.x, this.y + other.y) + } + + operator fun minus (other: Vector) : Vector { + return Vector(this.x - other.x, this.y - other.y) + } + + override fun toString() : String { + return "{$x,$y}" + } + + fun invert() : Vector { + return -this + } + + fun toLocation (plane: Int = 0) : Location { + return Location.create(floor(x).toInt(), floor(y).toInt(), plane) + } + + fun toDirection() : Direction { + val norm = normalized() + + if (norm.x >= 0.85) return Direction.EAST + else if (norm.x <= -0.85) return Direction.WEST + + if (norm.y > 0) { + if (norm.y >= 0.85) return Direction.NORTH + return if (norm.x > 0) Direction.NORTH_EAST else Direction.NORTH_WEST + } else { + if (norm.y <= -0.85) return Direction.SOUTH + return if (norm.x > 0) Direction.SOUTH_EAST else Direction.SOUTH_WEST + } + } + + companion object { + @JvmStatic fun betweenLocs (from: Location, to: Location) : Vector { + val xDiff = to.x - from.x + val yDiff = to.y - from.y + return Vector (xDiff.toDouble(), yDiff.toDouble()) + } + @JvmStatic fun deriveWithEqualComponents (magnitude: Double) : Vector { + var sideLength = sqrt(magnitude.pow(2.0) / 2) + return Vector(sideLength, sideLength) + } + } +} diff --git a/Server/src/main/core/api/utils/WeightBasedTable.kt b/Server/src/main/core/api/utils/WeightBasedTable.kt index 2f58cc0ee..79466bffe 100644 --- a/Server/src/main/core/api/utils/WeightBasedTable.kt +++ b/Server/src/main/core/api/utils/WeightBasedTable.kt @@ -10,6 +10,8 @@ import content.data.tables.HerbDropTable import content.data.tables.GemDropTable import content.data.tables.RareSeedDropTable import content.data.tables.AllotmentSeedDropTable +import content.global.handlers.item.equipment.fistofguthixgloves.FOGGlovesManager +import core.api.inEquipment import core.game.node.entity.Entity import core.game.node.entity.player.Player import core.game.node.item.Item @@ -25,24 +27,37 @@ open class WeightBasedTable : ArrayList() { guaranteedItems.add(element) } else { totalWeight += element.weight + val randIndex = RandomFunction.random(0, size) + val end = this.size super.add(element) + + val temp = this[randIndex] + this[randIndex] = element + this[end] = temp + true } } - open fun roll(receiver: Entity? = null): ArrayList{ - val items = ArrayList() - items.addAll(guaranteedItems) + open fun roll(receiver: Entity? = null) : ArrayList { + return roll(receiver, 1) + } - if (size == 1) { - items.add(get(0)) - } - else if (isNotEmpty()) { - var rngWeight = RandomFunction.randomDouble(totalWeight) - for (item in shuffled()) { - rngWeight -= item.weight - if (rngWeight <= 0) { - items.add(item) - break + open fun roll(receiver: Entity? = null, times: Int = 1): ArrayList{ + val items = ArrayList((guaranteedItems.size + 1) * times) + + for (i in 0 until times) { + items.addAll(guaranteedItems) + + if (size == 1) { + items.add(get(0)) + } else if (isNotEmpty()) { + var rngWeight = RandomFunction.randomDouble(totalWeight) + for (item in this) { + rngWeight -= item.weight + if (rngWeight <= 0) { + items.add(item) + break + } } } } @@ -60,7 +75,26 @@ open class WeightBasedTable : ArrayList() { SLOT_RDT -> RareDropTable.retrieve(receiver) SLOT_CELEDT -> CELEMinorTable.retrieve() SLOT_USDT -> UncommonSeedDropTable.retrieve() - SLOT_HDT -> HerbDropTable.retrieve() + SLOT_HDT -> { + if (RandomFunction.nextBool() && receiver is Player) { + if (inEquipment(receiver, Items.IRIT_GLOVES_12856)) { + FOGGlovesManager.updateCharges(receiver) + Item(Items.GRIMY_IRIT_209) + } else if (inEquipment(receiver, Items.AVANTOE_GLOVES_12857)) { + FOGGlovesManager.updateCharges(receiver) + Item(Items.GRIMY_AVANTOE_211) + } else if (inEquipment(receiver, Items.KWUARM_GLOVES_12858)) { + FOGGlovesManager.updateCharges(receiver) + Item(Items.GRIMY_KWUARM_213) + } else if (inEquipment(receiver, Items.CADANTINE_GLOVES_12859)) { + FOGGlovesManager.updateCharges(receiver) + Item(Items.GRIMY_CADANTINE_215) + } else + HerbDropTable.retrieve() + } else { + HerbDropTable.retrieve() + } + } SLOT_GDT -> GemDropTable.retrieve() SLOT_RSDT -> RareSeedDropTable.retrieve() SLOT_ASDT -> AllotmentSeedDropTable.retrieve() @@ -158,4 +192,4 @@ open class WeightBasedTable : ArrayList() { } return builder.toString() } -} \ No newline at end of file +} diff --git a/Server/src/main/core/api/utils/WeightedTable.kt b/Server/src/main/core/api/utils/WeightedTable.kt new file mode 100644 index 000000000..a399b8e91 --- /dev/null +++ b/Server/src/main/core/api/utils/WeightedTable.kt @@ -0,0 +1,61 @@ +package core.api.utils + +import core.tools.* + +/** + * Implementation of a weighted table that supports generic (particularly non-Item) types. +**/ +class WeightedTable : ArrayList>() { + var totalWeight: Double = 0.0 + + fun add (element: T?, weight: Double) : Boolean { + totalWeight += weight + return super.add(Pair(element, weight)) + } + + fun remove (element: T?) : Boolean { + var index = -1 + for ((i, pair) in this.withIndex()) { + val (elem, _) = pair + if (element == elem) { + index = i + break + } + } + if (index == -1) return false + + this.removeAt(index) + return true + } + + override fun removeAt (index: Int) : Pair { + val (_, weight) = this[index] + totalWeight -= weight + return super.removeAt(index) + } + + fun roll() : T? { + if (this.size == 1) return this[0].component1() + else if (this.size == 0) return null + + var shuffled = this.shuffled() + var randWeight = RandomFunction.random(0.0, totalWeight) + + for ((element, weight) in shuffled) { + randWeight -= weight + if (randWeight <= 0) + return element + } + + return null + } + + companion object { + fun create (vararg elements: Pair) : WeightedTable { + var table = WeightedTable() + for ((element, weight) in elements) + table.add (element, weight) + return table + } + } +} diff --git a/Server/src/main/core/cache/Cache.java b/Server/src/main/core/cache/Cache.java index 1751622f7..ce4ed3c25 100644 --- a/Server/src/main/core/cache/Cache.java +++ b/Server/src/main/core/cache/Cache.java @@ -10,8 +10,11 @@ import core.cache.def.impl.GraphicDefinition; import core.cache.def.impl.ItemDefinition; import core.cache.def.impl.NPCDefinition; import core.cache.def.impl.SceneryDefinition; +import core.tools.Log; import core.tools.SystemLogger; +import static core.api.ContentAPIKt.log; + /** * A cache reader. * @@ -48,7 +51,7 @@ public final class Cache { * @throws Throwable When an exception occurs. */ public static void init(String path) throws Throwable { - SystemLogger.logInfo(Cache.class, "Initializing cache..."); + log(Cache.class, Log.FINE, "Initializing cache..."); byte[] cacheFileBuffer = new byte[520]; RandomAccessFile containersInformFile = new RandomAccessFile(path + File.separator + "main_file_cache.idx255", "r"); RandomAccessFile dataFile = new RandomAccessFile(path + File.separator + "main_file_cache.dat2", "r"); @@ -61,7 +64,7 @@ public final class Cache { CacheFile cacheFile = new CacheFile(i, new RandomAccessFile(f, "r"), dataFile, 1000000, cacheFileBuffer); cacheFileManagers[i] = new CacheFileManager(cacheFile, true); if (cacheFileManagers[i].getInformation() == null) { - SystemLogger.logErr(Cache.class, "Error loading cache index " + i + ": no information."); + log(Cache.class, Log.ERR, "Error loading cache index " + i + ": no information."); cacheFileManagers[i] = null; } } @@ -93,7 +96,7 @@ public final class Cache { public static ByteBuffer getArchiveData(int index, int archive, boolean priority, int encryptionValue) { byte[] data = index == 255 ? referenceFile.getContainerData(archive) : cacheFileManagers[index].getCacheFile().getContainerData(archive); if (data == null || data.length < 1) { - SystemLogger.logErr(Cache.class, "Invalid JS-5 request - " + index + ", " + archive + ", " + priority + ", " + encryptionValue + "!"); + log(Cache.class, Log.ERR, "Invalid JS-5 request - " + index + ", " + archive + ", " + priority + ", " + encryptionValue + "!"); return null; } int compression = data[0] & 0xff; diff --git a/Server/src/main/core/cache/def/impl/AnimationDefinition.java b/Server/src/main/core/cache/def/impl/AnimationDefinition.java index 9f54a1d61..1f56d91cd 100644 --- a/Server/src/main/core/cache/def/impl/AnimationDefinition.java +++ b/Server/src/main/core/cache/def/impl/AnimationDefinition.java @@ -88,6 +88,14 @@ public final class AnimationDefinition { return duration; } + public int getCycles() { + if (durations == null) return 0; + int duration = 0; + for (int i : durations) + duration += i; + return duration; + } + /** * Gets the duration of this animation in (600ms) ticks. * @return The duration in ticks. @@ -150,7 +158,7 @@ public final class AnimationDefinition { } else if (opcode == 14) { aBoolean2141 = true; } else { - System.out.println("Unhandled animation opcode " + opcode); + } } else anInt2162 = buffer.get() & 0xFF; diff --git a/Server/src/main/core/cache/def/impl/ClothDefinition.java b/Server/src/main/core/cache/def/impl/ClothDefinition.java index cf133443a..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; @@ -77,11 +76,9 @@ public final class ClothDefinition { e.printStackTrace(); } int length = Cache.getIndexes()[2].getFilesSize(3); - System.out.println("Definition size: " + length + "."); + for (int i = 0; i < length; i++) { ClothDefinition def = forId(i); - if (def.unknownBool) - System.out.println("Clothing " + i + ": " + def.equipmentSlot + ", " + def.unknownBool + ", " + Arrays.toString(def.modelIds) + ", " + Arrays.toString(def.models)); } } diff --git a/Server/src/main/core/cache/def/impl/ComponentType.java b/Server/src/main/core/cache/def/impl/ComponentType.java new file mode 100644 index 000000000..d92e84212 --- /dev/null +++ b/Server/src/main/core/cache/def/impl/ComponentType.java @@ -0,0 +1,14 @@ +package core.cache.def.impl; + +public enum ComponentType { + SCROLLABLE, + UNKNOWN_1, + UNKNOWN_2, + UNKNOWN_3, + TEXT, + SPRITE, + MODEL, + UNKNOWN_7, + UNKNOWN_8, + UNKNOWN_9 +} diff --git a/Server/src/main/core/cache/def/impl/DataMap.java b/Server/src/main/core/cache/def/impl/DataMap.java index 06cfce47b..bdf4c82cc 100644 --- a/Server/src/main/core/cache/def/impl/DataMap.java +++ b/Server/src/main/core/cache/def/impl/DataMap.java @@ -3,12 +3,15 @@ package core.cache.def.impl; import core.cache.Cache; import core.cache.misc.buffer.ByteBufferUtils; import core.tools.CP1252; +import core.tools.Log; import core.tools.SystemLogger; import java.nio.ByteBuffer; import java.util.HashMap; import java.util.Map; +import static core.api.ContentAPIKt.log; + public class DataMap { /** @@ -37,7 +40,7 @@ public class DataMap { public int getInt(int key){ if(!dataStore.containsKey(key)){ - SystemLogger.logErr(this.getClass(), "Invalid value passed for key: " + key + " map: " + id); + log(this.getClass(), Log.ERR, "Invalid value passed for key: " + key + " map: " + id); return -1; } return (int) dataStore.get(key); diff --git a/Server/src/main/core/cache/def/impl/GraphicDefinition.java b/Server/src/main/core/cache/def/impl/GraphicDefinition.java index d6512c10f..866aa8050 100644 --- a/Server/src/main/core/cache/def/impl/GraphicDefinition.java +++ b/Server/src/main/core/cache/def/impl/GraphicDefinition.java @@ -70,14 +70,14 @@ public class GraphicDefinition { } // 5046 - 5050 are related anims & 2148 GraphicDefinition d = GraphicDefinition.forId(803); - System.out.println("Graphic " + d.graphicsId + " anim id = " + d.animationId + ", " + d.defaultModel + "."); + for (int i = 0; i < 5000; i++) { GraphicDefinition def = GraphicDefinition.forId(i); if (def == null) { continue; } if ((def.animationId > 2000 && def.animationId < 2200) || (def.defaultModel >= 1300 && def.defaultModel < 1500)) { - System.out.println("Possible match [id=" + i + ", anim=" + def.animationId + "]."); + } } } diff --git a/Server/src/main/core/cache/def/impl/IfaceDefinition.java b/Server/src/main/core/cache/def/impl/IfaceDefinition.java new file mode 100644 index 000000000..90b32c492 --- /dev/null +++ b/Server/src/main/core/cache/def/impl/IfaceDefinition.java @@ -0,0 +1,554 @@ +package core.cache.def.impl; + +import core.cache.Cache; +import core.net.packet.IoBuffer; +import core.net.packet.PacketHeader; + +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Optional; + +public class IfaceDefinition { + private static HashMap defCache = new HashMap<>(); + public int id; + public ComponentType type; + public int version; + public int clientCode; + public int baseX, baseY; + public int baseWidth, baseHeight; + public int dynWidth, dynHeight; + public int xMode, yMode; + public int overlayer; + public boolean hidden; + public int scrollMaxH, scrollMaxV; + public boolean noClickThrough; + public int spriteId, activeSpriteId; + public int angle2d; + public boolean hasAlpha; + public boolean spriteTiling; + public int alpha; + public int outlineThickness; + public int shadowColor; + public boolean hFlip, vFlip; + public int modelType, activeModelType; + public int modelId, activeModelId; + public int unknownModelProp_1; + public int unknownModelProp_2; + public int modelXAngle, modelYAngle; + public int modelYOffset; + public int modelZoom; + public int modelAnimId, activeModelAnimId; + public boolean modelOrtho; + public int unknownModelProp_3; + public int unknownModelProp_4; + public boolean unknownModelProp_5; + public int unknownModelProp_6; + public int unknownModelProp_7; + public int font; + public String text, activeText; + public int vPadding; + public int halign, valign; + public boolean shadowed; + public int color, activeColor, overColor, unknownColor; + public boolean filled; + public int lineWidth; + public boolean unknownProp_8; + public int[] unknownIntArray_1; + public int[] unknownIntArray_2; + public byte[] unknownByteArray_1; + public byte[] unknownByteArray_2; + public String optionBase; + public String[] ops; + public int dragDeadzone; + public int dragDeadtime; + public boolean dragRenderBehavior; + public String opCircumfix, opSuffix, option; + public int unknownProp_9, unknownProp_10, unknownProp_11; + public int[] cs1ComparisonOperands, cs1ComparisonOpcodes; + public int[][] cs1Scripts; + public int[] objCounts; + public int[] objTypes; + public int invMarginX, invMarginY; + public int[] invOffsetX, invOffsetY; + public int[] invSprite; + public int buttonType; + public String[] invOptions; + public LinkedScripts scripts; + public ScriptTriggers triggers; + + public IfaceDefinition[] children; + public int parent; + + @Override + public String toString() { + if (parent == id) { + return "[IF " + id + " (P)]"; + } else { + return "[IF " + (id >> 16) + ", CH " + (id & 0xFF) + " (" + type.name() + ")]"; + } + } + + public static IfaceDefinition forId (int id) { + if (defCache.containsKey(id)) { + return defCache.get(id); + } + IfaceDefinition def = loadAndParse(id); + defCache.put(id, def); + return def; + } + + public static IfaceDefinition forId (int id, int child) { + if (defCache.containsKey(child + (id << 16))) + return defCache.get(child + (id << 16)); + IfaceDefinition def = forId(id); + return def.children[child]; + } + + private static IfaceDefinition loadAndParse (int id) { + IfaceDefinition def = new IfaceDefinition(); + int childCount = Cache.getIndexes()[3].getFilesSize(id); + def.children = new IfaceDefinition[childCount]; + def.id = id; + def.parent = id; + for (int i = 0; i < childCount; i++) { + def.children[i] = parseChild(id, i); + } + return def; + } + + private static IfaceDefinition parseChild (int id, int childIndex) { + IfaceDefinition def = new IfaceDefinition(); + def.id = childIndex + (id << 16); + def.parent = id; + defCache.put(def.id, def); + + byte[] dataRaw = Cache.getIndexes()[3].getFileData(id, childIndex); + if (dataRaw == null) return def; + + IoBuffer data = new IoBuffer(-1, PacketHeader.NORMAL, ByteBuffer.wrap(dataRaw)); + + if (dataRaw[0] == -1) + decodeIf3 (def, data); + else + decodeIf1 (def, data); + + return def; + } + + private static void decodeIf1 (IfaceDefinition def, IoBuffer data) { + data.g1(); + def.version = 1; + def.type = ComponentType.values()[data.g1()]; + def.buttonType = data.g1(); + def.clientCode = data.g2(); + def.baseX = data.g2b(); + def.baseY = data.g2b(); + def.baseWidth = data.g2(); + def.baseHeight = data.g2(); + def.dynWidth = def.dynHeight = 0; + def.xMode = def.yMode = 0; + def.alpha = data.g1(); + def.overlayer = data.g2(); + if (def.overlayer == 65535) + def.overlayer = -1; + else def.overlayer += def.id & 0xFFFF0000; + def.unknownProp_11 = data.g2(); + + int unknownLocal_1 = data.g1(); + if (unknownLocal_1 > 0) { + def.cs1ComparisonOpcodes = new int[unknownLocal_1]; + def.cs1ComparisonOperands = new int[unknownLocal_1]; + for (int i = 0; i < unknownLocal_1; i++) { + def.cs1ComparisonOpcodes[i] = data.g1(); + def.cs1ComparisonOperands[i] = data.g2(); + } + } + + int unknownLocal_2 = data.g1(); + int unknownLocal_3; + if (unknownLocal_2 > 0) { + def.cs1Scripts = new int[unknownLocal_2][]; + for (int i = 0; i < unknownLocal_2; i++) { + unknownLocal_3 = data.g2(); + def.cs1Scripts[i] = new int[unknownLocal_3]; + for (int j = 0; j < unknownLocal_3; j++) { + def.cs1Scripts[i][j] = data.g2(); + if (def.cs1Scripts[i][j] == 65535) + def.cs1Scripts[i][j] = -1; + } + } + } + + int flags = 0; + if (def.type == ComponentType.SCROLLABLE) { + def.scrollMaxV = data.g2(); + def.hidden = data.g1() == 1; + } + if (def.type == ComponentType.UNKNOWN_1) { + data.g2(); + data.g1(); + } + if (def.type == ComponentType.UNKNOWN_2) { + def.dynHeight = 3; + def.objCounts = new int[def.baseWidth * def.baseHeight]; + def.objTypes = new int[def.baseWidth * def.baseHeight]; + def.dynWidth = 3; + int unknownLocal_4 = data.g1(); + int unknownLocal_5 = data.g1(); + if (unknownLocal_4 == 1) + flags = 268435456; + int unknownLocal_6 = data.g1(); + if (unknownLocal_5 == 1) + flags |= 0x40000000; + if (unknownLocal_6 == 1) + flags |= Integer.MAX_VALUE; + int unknownLocal_7 = data.g1(); + if (unknownLocal_7 == 1) + flags |= 0x20000000; + def.invMarginX = data.g1(); + def.invMarginY = data.g1(); + def.invOffsetX = new int[20]; + def.invOffsetY = new int[20]; + def.invSprite = new int[20]; + for (int i = 0; i < 20; i++) { + int hasSprite = data.g1(); + if (hasSprite == 1) { + def.invOffsetX[i] = data.g2b(); + def.invOffsetY[i] = data.g2b(); + def.invSprite[i] = data.g4(); + } else { + def.invSprite[i] = -1; + } + } + def.invOptions = new String[5]; + for (int i = 0; i < 5; i++) { + String option = data.getJagString(); + if (option.length() > 0) { + def.invOptions[i] = option; + flags |= 0x1 << i + 23; + } + } + } + if (def.type == ComponentType.UNKNOWN_3) { + def.filled = data.g1() == 1; + } + if (def.type == ComponentType.TEXT || def.type == ComponentType.UNKNOWN_1) { + def.halign = data.g1(); + def.valign = data.g1(); + def.vPadding = data.g1(); + def.font = data.g2(); + if (def.font == 65535) + def.font = -1; + def.shadowed = data.g1() == 1; + } + if (def.type == ComponentType.TEXT) { + def.text = data.getJagString(); + def.activeText = data.getJagString(); + } + if (def.type == ComponentType.UNKNOWN_1 || def.type == ComponentType.UNKNOWN_3 || def.type == ComponentType.TEXT) { + def.color = data.g4(); + } + if (def.type == ComponentType.UNKNOWN_3 || def.type == ComponentType.TEXT) { + def.activeColor = data.g4(); + def.overColor = data.g4(); + def.unknownColor = data.g4(); + } + if (def.type == ComponentType.SPRITE) { + def.spriteId = data.g4(); + def.activeSpriteId = data.g4(); + } + if (def.type == ComponentType.MODEL) { + def.modelType = 1; + def.modelId = data.g2(); + if (def.modelId == 65535) + def.modelId = -1; + def.activeModelType = 1; + def.activeModelId = data.g2(); + if (def.activeModelId == 65535) + def.activeModelId = -1; + def.modelAnimId = data.g2(); + if (def.modelAnimId == 65535) + def.modelAnimId = -1; + def.activeModelAnimId = data.g2(); + if (def.activeModelAnimId == 65535) + def.activeModelAnimId = -1; + def.modelZoom = data.g2(); + def.modelXAngle = data.g2(); + def.modelYAngle = data.g2(); + } + if (def.type == ComponentType.UNKNOWN_7) { + def.dynHeight = def.dynWidth = 3; + def.objCounts = new int [def.baseHeight * def.baseWidth]; + def.objTypes = new int [def.baseHeight * def.baseWidth]; + def.halign = data.g1(); + def.font = data.g2(); + if (def.font == 65535) + def.font = -1; + def.shadowed = data.g1() == 1; + def.color = data.g4(); + def.invMarginX = data.g2b(); + def.invMarginY = data.g2b(); + int invHasOptions = data.g1(); + if (invHasOptions == 1) { + flags |= 0x40000000; + } + def.invOptions = new String[5]; + for (int i = 0; i < 5; i++) { + String option = data.getJagString(); + if (option.length() > 0) { + def.invOptions[i] = option; + flags |= 0x1 << i + 23; + } + } + } + if (def.type == ComponentType.UNKNOWN_8) + def.text = data.getJagString(); + if (def.buttonType == 2 || def.type == ComponentType.UNKNOWN_2) { + def.opCircumfix = data.getJagString(); + def.opSuffix = data.getJagString(); + int unknownLocal_4 = data.g2() & 0x3F; + flags |= unknownLocal_4 << 11; + } + if (def.buttonType == 1 || def.buttonType == 4 || def.buttonType == 5 || def.buttonType == 6) { + def.option = data.getJagString(); + if (def.option.length() == 0) { + switch (def.buttonType) { + case 1: + def.option = "Ok"; + break; + case 4: + case 5: + def.option = "Select"; + break; + case 6: + def.option = "Continue"; + break; + } + } + } + if (def.buttonType == 1 || def.buttonType == 4 || def.buttonType == 5) { + flags |= 0x400000; + } + if (def.buttonType == 6) { + flags |= 0x1; + } + } + + private static void decodeIf3 (IfaceDefinition def, IoBuffer data) { + data.g1(); + def.version = 3; + int type = data.g1(); + if ((type & 0x80) != 0) { + type &= 0x7F; + data.getJagString(); + } + def.type = ComponentType.values()[type]; + def.clientCode = data.g2(); + def.baseX = data.g2b(); + def.baseY = data.g2b(); + def.baseWidth = data.g2(); + def.baseHeight = data.g2(); + def.dynWidth = data.g1b(); + def.dynHeight = data.g1b(); + def.yMode = data.g1b(); + def.xMode = data.g1b(); + def.overlayer = data.g2(); + if (def.overlayer == 65535) { + def.overlayer = -1; + } else { + def.overlayer = (def.id & 0xFFFF0000) + def.overlayer; + } + def.hidden = data.g1() == 1; + parseIf3Type (def, data); + + int unknownLocal_1 = data.g3(); + int unknownLocal_2 = data.g1(); + int unknownLocal_3; + if (unknownLocal_2 != 0) { + def.unknownIntArray_1 = new int[10]; + def.unknownByteArray_1 = new byte[10]; + def.unknownByteArray_2 = new byte[10]; + + while (unknownLocal_2 != 0) { + unknownLocal_3 = (unknownLocal_2 >> 4) - 1; + unknownLocal_2 = data.g1() | unknownLocal_2 << 8; + unknownLocal_2 &= 0xFFF; + if (unknownLocal_2 == 4095) { + def.unknownIntArray_1[unknownLocal_3] = -1; + } else { + def.unknownIntArray_1[unknownLocal_3] = unknownLocal_2; + } + def.unknownByteArray_2[unknownLocal_3] = (byte) data.g1b(); + def.unknownByteArray_1[unknownLocal_3] = (byte) data.g1b(); + unknownLocal_2 = data.g1(); + } + } + + def.optionBase = data.getJagString(); + unknownLocal_2 = data.g1(); + int opCount = unknownLocal_2 & 0xF; + if (opCount > 0) { + def.ops = new String[opCount]; + for (int i = 0; i < opCount; i++) { + def.ops[i] = data.getJagString(); + } + } + + int unknownLocal_5; + int unknownLocal_6 = unknownLocal_2 >> 4; + if (unknownLocal_6 > 0) { + unknownLocal_5 = data.g1(); + def.unknownIntArray_2 = new int[unknownLocal_5 + 1]; + for (int i = 0; i < def.unknownIntArray_2.length; i++) + def.unknownIntArray_2[i] = -1; + def.unknownIntArray_2[unknownLocal_5] = data.g2(); + } + if (unknownLocal_6 > 1) { + unknownLocal_5 = data.g1(); + def.unknownIntArray_2[unknownLocal_5] = data.g2(); + } + def.dragDeadzone = data.g1(); + def.dragDeadtime = data.g1(); + def.dragRenderBehavior = data.g1() == 1; + unknownLocal_5 = -1; + def.opCircumfix = data.getJagString(); + + if ((unknownLocal_1 >> 11 & 0x7F) != 0) { + unknownLocal_5 = data.g2(); + def.unknownProp_9 = data.g2(); + if (unknownLocal_5 == 65535) + unknownLocal_5 = -1; + if (def.unknownProp_9 == 65535) + def.unknownProp_9 = -1; + def.unknownProp_10 = data.g2(); + if (def.unknownProp_10 == 65535) + def.unknownProp_10 = -1; + } + + def.scripts = new LinkedScripts(); + def.scripts.unknown = parseIf3ScriptArgs(data); + def.scripts.onMouseOver = parseIf3ScriptArgs(data); + def.scripts.onMouseLeave = parseIf3ScriptArgs(data); + def.scripts.onUseWith = parseIf3ScriptArgs(data); + def.scripts.onUse = parseIf3ScriptArgs(data); + def.scripts.onVarpTransmit = parseIf3ScriptArgs(data); + def.scripts.onInvTransmit = parseIf3ScriptArgs(data); + def.scripts.onStatTransmit = parseIf3ScriptArgs(data); + def.scripts.onTimer = parseIf3ScriptArgs(data); + def.scripts.onOptionClick = parseIf3ScriptArgs(data); + def.scripts.onMouseRepeat = parseIf3ScriptArgs(data); + def.scripts.onClickRepeat = parseIf3ScriptArgs(data); + def.scripts.onDrag = parseIf3ScriptArgs(data); + def.scripts.onRelease = parseIf3ScriptArgs(data); + def.scripts.onHold = parseIf3ScriptArgs(data); + def.scripts.onDragStart = parseIf3ScriptArgs(data); + def.scripts.onDragRelease = parseIf3ScriptArgs(data); + def.scripts.onScroll = parseIf3ScriptArgs(data); + def.scripts.onVarcTransmit = parseIf3ScriptArgs(data); + def.scripts.onVarcstrTransmit = parseIf3ScriptArgs(data); + + def.triggers = new ScriptTriggers(); + def.triggers.varpTriggers = parseIf3Triggers(data); + def.triggers.inventoryTriggers = parseIf3Triggers(data); + def.triggers.statTriggers = parseIf3Triggers(data); + def.triggers.varcTriggers = parseIf3Triggers(data); + def.triggers.varcstrTriggers = parseIf3Triggers(data); + } + + private static ScriptArgs parseIf3ScriptArgs (IoBuffer data) { + int argCount = data.g1(); + if (argCount == 0) + return null; + Object[] argArray = new Object[argCount]; + for (int i = 0; i < argCount; i++) { + int type = data.g1(); + if (type == 0) { + int datum = Integer.valueOf(data.g4()); + argArray[i] = datum; + } else if (type == 1) { + String datum = data.getJagString(); + argArray[i] = datum; + } + } + return new ScriptArgs((int) argArray[0], Arrays.copyOfRange(argArray, 1, argArray.length)); + } + + private static int[] parseIf3Triggers (IoBuffer data) { + int triggerCount = data.g1(); + if (triggerCount == 0) + return null; + int[] triggers = new int[triggerCount]; + for (int i = 0; i < triggerCount; i++) { + triggers[i] = data.g4(); + } + return triggers; + } + + private static void parseIf3Type (IfaceDefinition def, IoBuffer data) { + switch (def.type) { + case SCROLLABLE: + def.scrollMaxH = data.g2(); + def.scrollMaxV = data.g2(); + def.noClickThrough = data.g1() == 1; + break; + case SPRITE: + def.spriteId = data.g4(); + def.angle2d = data.g2(); + int spriteFlags = data.g1(); + def.hasAlpha = (spriteFlags & 0x2) != 0; + def.spriteTiling = (spriteFlags & 0x1) != 0; + def.alpha = data.g1(); + def.outlineThickness = data.g1(); + def.shadowColor = data.g4(); + def.vFlip = data.g1() == 1; + def.hFlip = data.g1() == 1; + break; + case MODEL: + def.modelType = 1; + def.modelId = data.g2(); + if (def.modelId == 65535) + def.modelId = -1; + def.unknownModelProp_1 = data.g2b(); + def.unknownModelProp_2 = data.g2b(); + def.modelXAngle = data.g2(); + def.modelYAngle = data.g2(); + def.modelYOffset = data.g2(); + def.modelZoom = data.g2(); + def.modelAnimId = data.g2(); + if (def.modelAnimId == 65535) + def.modelAnimId = -1; + def.modelOrtho = data.g1() == 1; + def.unknownModelProp_3 = data.g2(); + def.unknownModelProp_4 = data.g2(); + def.unknownModelProp_5 = data.g1() == 1; + if (def.dynWidth != 0) + def.unknownModelProp_6 = data.g2(); + if (def.dynHeight != 0) + def.unknownModelProp_7 = data.g2(); + break; + case TEXT: + def.font = data.g2(); + if (def.font == 65535) + def.font = -1; + def.text = data.getJagString(); + def.vPadding = data.g1(); + def.halign = data.g1(); + def.valign = data.g1(); + def.shadowed = data.g1() == 1; + def.color = data.g4(); + break; + case UNKNOWN_3: + def.color = data.g4(); + def.filled = data.g1() == 1; + def.alpha = data.g1(); + break; + case UNKNOWN_9: + def.lineWidth = data.g1(); + def.color = data.g4(); + def.unknownProp_8 = data.g1() == 1; + break; + } + } + +} diff --git a/Server/src/main/core/cache/def/impl/ItemDefinition.java b/Server/src/main/core/cache/def/impl/ItemDefinition.java index 36fddfdb6..0474e11f8 100644 --- a/Server/src/main/core/cache/def/impl/ItemDefinition.java +++ b/Server/src/main/core/cache/def/impl/ItemDefinition.java @@ -1,37 +1,33 @@ package core.cache.def.impl; +import content.global.skill.summoning.familiar.BurdenBeast; import core.api.EquipmentSlot; import core.cache.Cache; import core.cache.def.Definition; import core.cache.misc.buffer.ByteBufferUtils; import core.game.container.Container; -import core.game.global.action.DropItemHandler; import core.game.interaction.OptionHandler; -import core.game.node.Node; import core.game.node.entity.player.Player; import core.game.node.entity.skill.Skills; import core.game.node.item.Item; import core.game.node.item.ItemPlugin; -import core.net.packet.PacketRepository; -import core.net.packet.out.WeightUpdate; -import core.plugin.Plugin; +import core.game.world.GameWorld; +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; import java.util.Arrays; import static core.api.ContentAPIKt.equipSlot; +import static core.api.ContentAPIKt.log; /** * Represents an item's definitions. - * @author Jagex * @author Emperor */ public class ItemDefinition extends Definition { @@ -59,12 +55,12 @@ public class ItemDefinition extends Definition { /** * The model rotation. */ - private int modelRotationY; + private int modelRotationX; /** * The model rotation. */ - private int modelRotationX; + private int modelRotationY; /** * The model offset. @@ -187,7 +183,7 @@ public class ItemDefinition extends Definition { private int colourEquip2; /** - * The note item. + * The note item id if un-noted. The un-noted item id if noted. -1 if no noted counterpart. */ private int noteId = -1; @@ -259,32 +255,6 @@ public class ItemDefinition extends Definition { options = new String[] { null, null, null, null, "drop" }; } - /** - * Initialize the default option handlers. - */ - static { - // TODO: Move this crap in a plugin. - OptionHandler handler = new OptionHandler() { - @Override - public Plugin newInstance(Object arg) throws Throwable { - return this; - } - - @Override - public boolean handle(final Player player, Node node, String option) { - return DropItemHandler.handle(player, node, option); - } - - @Override - public boolean isWalk() { - return false; - } - }; - setOptionHandler("destroy", handler); - setOptionHandler("dissolve", handler); - setOptionHandler("drop", handler); - } - /** * Parses the item definitions. */ @@ -297,12 +267,12 @@ public class ItemDefinition extends Definition { } ItemDefinition def = ItemDefinition.parseDefinition(itemId, ByteBuffer.wrap(data)); if (def == null) { - SystemLogger.logErr(ItemDefinition.class, "Could not load item definitions for id " + itemId + " - no definitions found!"); + log(ItemDefinition.class, Log.ERR, "Could not load item definitions for id " + itemId + " - no definitions found!"); return ; } if(itemId == 14958) def.setStackable(true); - + ItemDefinition.getDefinitions().put(itemId, def); } ItemDefinition.defineTemplates(); @@ -344,9 +314,9 @@ public class ItemDefinition extends Definition { } else if (opcode == 4) { def.modelZoom = buffer.getShort() & 0xFFFF; } else if (opcode == 5) { - def.modelRotationY = buffer.getShort() & 0xFFFF; - } else if (opcode == 6) { def.modelRotationX = buffer.getShort() & 0xFFFF; + } else if (opcode == 6) { + def.modelRotationY = buffer.getShort() & 0xFFFF; } else if (opcode == 7) { def.modelOffsetX = buffer.getShort() & 0xFFFF; if (def.modelOffsetX > 32767) @@ -476,7 +446,7 @@ public class ItemDefinition extends Definition { def.clientScriptData.put(key, value); } } else { - System.out.println("Unhandled item definition opcode - opcode: " + opcode); + break; } } @@ -519,11 +489,11 @@ public class ItemDefinition extends Definition { modelOffsetY = templateReference.modelOffsetY; textureColour1 = templateReference.textureColour1; value = reference.value; - modelRotationX = templateReference.modelRotationX; + modelRotationY = templateReference.modelRotationY; stackable = true; unnoted = false; modifiedModelColors = templateReference.modifiedModelColors; - modelRotationY = templateReference.modelRotationY; + modelRotationX = templateReference.modelRotationX; modelZoom = templateReference.modelZoom; textureColour1 = templateReference.textureColour1; handlers.put(ItemConfigParser.TRADEABLE, true); @@ -542,8 +512,8 @@ public class ItemDefinition extends Definition { textureColour2 = reference.textureColour2; groundActions = reference.groundActions; unknownArray1 = reference.unknownArray1; - modelRotationY = templateReference.modelRotationY; modelRotationX = templateReference.modelRotationX; + modelRotationY = templateReference.modelRotationY; originalModelColors = reference.originalModelColors; name = reference.name; maleWornModelId1 = reference.maleWornModelId1; @@ -572,7 +542,7 @@ public class ItemDefinition extends Definition { public void transferRecolourDefinition(ItemDefinition reference, ItemDefinition templateReference) { femaleWornModelId2 = reference.femaleWornModelId2; options = new String[5]; - modelRotationX = templateReference.modelRotationX; + modelRotationY = templateReference.modelRotationY; name = reference.name; maleWornModelId1 = reference.maleWornModelId1; modelOffsetY = templateReference.modelOffsetY; @@ -581,7 +551,7 @@ public class ItemDefinition extends Definition { modelOffsetX = templateReference.modelOffsetX; unknownArray1 = reference.unknownArray1; stackable = reference.stackable; - modelRotationY = templateReference.modelRotationY; + modelRotationX = templateReference.modelRotationX; textureColour1 = reference.textureColour1; colourEquip1 = reference.colourEquip1; textureColour2 = reference.textureColour2; @@ -635,12 +605,20 @@ 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; + if (player.getFamiliarManager().hasFamiliar() && player.getFamiliarManager().getFamiliar().isBurdenBeast()) { + containers = new Container[] { player.getInventory(), player.getEquipment(), ((BurdenBeast) player.getFamiliarManager().getFamiliar()).getContainer() }; + } else { + containers = new Container[] { player.getInventory(), player.getEquipment() }; + } + for (Container c : containers) { for (Item i : c.toArray()) { if (i == null) { continue; } + if (GameWorld.getSettings().getSkillcape_perks() && i.getId() == Items.RUNECRAFT_CAPE_9765 || i.getId() == Items.RUNECRAFT_CAPET_9766) { + continue; + } if (!i.getDefinition().isAllowedOnEntrana()) { return false; } @@ -679,7 +657,18 @@ public class ItemDefinition extends Definition { Items.REGEN_BRACELET_11133, Items.WARLOCK_CLOAK_14081, Items.WARLOCK_LEGS_14077, - Items.WARLOCK_TOP_14076 + Items.WARLOCK_TOP_14076, + Items.MONKS_ROBE_542, + Items.MONKS_ROBE_544, + Items.HAM_SHIRT_4298, + Items.HAM_ROBE_4300, + 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 )); private static final HashSet entranaBannedItems = new HashSet(Arrays.asList( /**Items.BUTTERFLY_NET_10010, easing the restriction until barehanded implementation**/ @@ -734,7 +723,7 @@ public class ItemDefinition extends Definition { Integer level = requirements.get(skillId); return level == null ? 0 : level; } - + /** * Gets the wielding animation id (render animation id). * @return The wield animation id. @@ -815,32 +804,32 @@ public class ItemDefinition extends Definition { * Gets the modelRotation1. * @return The modelRotation1. */ - public int getModelRotation1() { - return modelRotationY; + public int getModelRotationX() { + return modelRotationX; } /** * Sets the modelRotation1. * @param modelRotation1 The modelRotation1 to set. */ - public void setModelRotation1(int modelRotation1) { - this.modelRotationY = modelRotation1; + public void setModelRotationX(int modelRotation1) { + this.modelRotationX = modelRotation1; } /** * Gets the modelRotation2. * @return The modelRotation2. */ - public int getModelRotation2() { - return modelRotationX; + public int getModelRotationY() { + return modelRotationY; } /** * Sets the modelRotation2. * @param modelRotation2 The modelRotation2 to set. */ - public void setModelRotation2(int modelRotation2) { - this.modelRotationX = modelRotation2; + public void setModelRotationY(int modelRotation2) { + this.modelRotationY = modelRotation2; } /** @@ -899,6 +888,35 @@ public class ItemDefinition extends Definition { return value; } + /** + * Gets whether the Item has a value in a custom currency + * @param currency the configuration string of the currency in the item definition + * @return {@code True} if so. + */ + public boolean hasShopCurrencyValue(String currency) { + return getHandlers().getOrDefault(currency, "0") != "0"; + } + + /** + * Gets whether shops value the Item (even if the value is 0) in a currency + * @param currency the ID of the currency + * @return {@code True} if so. + */ + public boolean hasShopCurrencyValue(int currency) { + switch (currency) { + case Items.COINS_995: + return isTradeable(); + case Items.TOKKUL_6529: + return hasShopCurrencyValue(ItemConfigParser.TOKKUL_PRICE); + case Items.ARCHERY_TICKET_1464: + return hasShopCurrencyValue(ItemConfigParser.ARCHERY_TICKET_PRICE); + case Items.CASTLE_WARS_TICKET_4067: + return hasShopCurrencyValue(ItemConfigParser.CASTLE_WARS_TICKET_PRICE); + default: + return false; + } + } + /** * @return The value. */ @@ -1374,6 +1392,17 @@ public class ItemDefinition extends Definition { return getConfiguration(ItemConfigParser.LOW_ALCHEMY, (int)Math.rint(value * 0.4)); } + /** + * Checks if the item is alchemizable. + * @return {@code True} if so. + */ + public boolean isAlchemizable() { + if (!getConfiguration(ItemConfigParser.ALCHEMIZABLE, false)) { + return false; + } + return true; + } + /** * Checks if the item is tradeable. * @return {@code True} if so. @@ -1500,7 +1529,7 @@ public class ItemDefinition extends Definition { ItemDefinition def = forId(nodeId); if (def == null) { if (nodeId == 22937) - SystemLogger.logErr(ItemDefinition.class, "[ItemDefinition] No definition for item id " + nodeId + "!"); + log(ItemDefinition.class, Log.ERR, "[ItemDefinition] No definition for item id " + nodeId + "!"); return null; } OptionHandler handler = def.getConfiguration("option:" + name); @@ -1523,7 +1552,6 @@ public class ItemDefinition extends Definition { if (!player.getAttribute("equip_stats_open", false)) { return; } - PacketRepository.send(WeightUpdate.class, player.getPacketDispatch().getContext()); int index = 0; int[] bonuses = player.getProperties().getBonuses(); for (int i = 36; i < 50; i++) { @@ -1535,8 +1563,6 @@ public class ItemDefinition extends Definition { player.getPacketDispatch().sendString(BONUS_NAMES[index++] + bonusValue, 667, i); } player.getPacketDispatch().sendString("Attack bonus", 667, 34); - DecimalFormat dec = new DecimalFormat("#.#"); - player.getPacketDispatch().sendString(dec.format(player.getSettings().getWeight())+" kg", 667, 32); } /** @@ -1609,7 +1635,7 @@ public class ItemDefinition extends Definition { public void setItemType(int itemType) { this.itemType = itemType; } - + /** * Gets the femaleWornModelId3 value. * @return The femaleWornModelId3. diff --git a/Server/src/main/core/cache/def/impl/LinkedScripts.java b/Server/src/main/core/cache/def/impl/LinkedScripts.java new file mode 100644 index 000000000..933d9e9aa --- /dev/null +++ b/Server/src/main/core/cache/def/impl/LinkedScripts.java @@ -0,0 +1,24 @@ +package core.cache.def.impl; + +public class LinkedScripts { + public ScriptArgs unknown; + public ScriptArgs onMouseOver; + public ScriptArgs onMouseLeave; + public ScriptArgs onUseWith; + public ScriptArgs onUse; + public ScriptArgs onVarpTransmit; + public ScriptArgs onInvTransmit; + public ScriptArgs onStatTransmit; + public ScriptArgs onTimer; + public ScriptArgs onOptionClick; + public ScriptArgs onMouseRepeat; + public ScriptArgs onClickRepeat; + public ScriptArgs onDrag; + public ScriptArgs onRelease; + public ScriptArgs onHold; + public ScriptArgs onDragStart; + public ScriptArgs onDragRelease; + public ScriptArgs onScroll; + public ScriptArgs onVarcTransmit; + public ScriptArgs onVarcstrTransmit; +} diff --git a/Server/src/main/core/cache/def/impl/NPCDefinition.java b/Server/src/main/core/cache/def/impl/NPCDefinition.java index 4c6a851a1..dbf3bdf1d 100644 --- a/Server/src/main/core/cache/def/impl/NPCDefinition.java +++ b/Server/src/main/core/cache/def/impl/NPCDefinition.java @@ -17,6 +17,8 @@ import java.nio.ByteBuffer; import java.util.HashMap; import java.util.Map; +import static core.api.ContentAPIKt.*; + /** * Debug NPC Opcodes Line 483, Uncomment */ @@ -202,7 +204,7 @@ public final class NPCDefinition extends Definition { byte[] data = Cache.getIndexes()[18].getFileData(id >>> 7, id & 0x7f); if (data == null) { if (id != -1) { - // System.out.println("Failed loading NPC " + id + "."); + } } else { def.parse(ByteBuffer.wrap(data)); @@ -214,12 +216,12 @@ public final class NPCDefinition extends Definition { public static void main(String... args) throws Throwable { GameWorld.prompt(false); - System.out.println("Roar: " + NPCDefinition.forId(2329).standAnimation); + // for (int i = 0; i < 11000; i++) { // ItemDefinition def = ItemDefinition.forId(i); // if (def.getMaleWornModelId1() >= 1250 && def.getMaleWornModelId1() <= // 1550) { - // System.out.println(def.getName() + " " + i + ": " + + // def.getMaleWornModelId1()); // } // } @@ -239,7 +241,7 @@ public final class NPCDefinition extends Definition { if (configFileId != -1) { configValue = VarbitDefinition.forNPCID(configFileId).getValue(player); } else if (configId != -1) { - configValue = player.getConfigManager().get(configId); + configValue = getVarp(player, configId); } } else { configValue = 0; @@ -1114,4 +1116,4 @@ public final class NPCDefinition extends Definition { public void setRenderAnimationId(int renderAnimationId) { this.renderAnimationId = renderAnimationId; } -} \ No newline at end of file +} diff --git a/Server/src/main/core/cache/def/impl/RenderAnimationDefinition.java b/Server/src/main/core/cache/def/impl/RenderAnimationDefinition.java index 8e1621a31..881e4040d 100644 --- a/Server/src/main/core/cache/def/impl/RenderAnimationDefinition.java +++ b/Server/src/main/core/cache/def/impl/RenderAnimationDefinition.java @@ -1,7 +1,7 @@ package core.cache.def.impl; import core.cache.Cache; -import core.tools.SystemLogger; +import core.tools.Log; import core.game.world.GameWorld; import java.lang.reflect.Array; @@ -9,6 +9,8 @@ import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.nio.ByteBuffer; +import static core.api.ContentAPIKt.log; + /** * Holds definitions for render animations. * @author Jagex @@ -72,7 +74,7 @@ public class RenderAnimationDefinition { if (data != null) { defs.parse(ByteBuffer.wrap(data)); } else { - SystemLogger.logErr(RenderAnimationDefinition.class, "No definitions found for render animation " + animId + ", size=" + Cache.getIndexes()[2].getFilesSize(32) + "!"); + log(RenderAnimationDefinition.class, Log.ERR, "No definitions found for render animation " + animId + ", size=" + Cache.getIndexes()[2].getFilesSize(32) + "!"); } return defs; } @@ -291,7 +293,7 @@ public class RenderAnimationDefinition { public static void main(String...args) throws Throwable { GameWorld.prompt(false); RenderAnimationDefinition def = RenderAnimationDefinition.forId(1426); - System.out.println("size: " + def.getClass().getDeclaredFields().length); + for (Field f : def.getClass().getDeclaredFields()) { if (!Modifier.isStatic(f.getModifiers())) { if (f.getType().isArray()) { @@ -305,7 +307,7 @@ public class RenderAnimationDefinition { continue; } } - System.out.println(f.getName() + ", " + f.get(def)); + } } for (Field f : def.getClass().getSuperclass().getDeclaredFields()) { @@ -321,7 +323,7 @@ public class RenderAnimationDefinition { continue; } } - System.out.println(f.getName() + ", " + f.get(def)); + } } } diff --git a/Server/src/main/core/cache/def/impl/SceneryDefinition.java b/Server/src/main/core/cache/def/impl/SceneryDefinition.java index 50b277171..6172d7828 100644 --- a/Server/src/main/core/cache/def/impl/SceneryDefinition.java +++ b/Server/src/main/core/cache/def/impl/SceneryDefinition.java @@ -6,13 +6,16 @@ import core.cache.misc.buffer.ByteBufferUtils; import core.game.interaction.OptionHandler; import core.game.node.entity.player.Player; import core.game.node.scenery.Scenery; -import core.tools.SystemLogger; +import core.tools.Log; import core.game.world.GameWorld; import java.nio.ByteBuffer; import java.util.HashMap; import java.util.Map; +import static core.api.ContentAPIKt.log; +import static core.api.ContentAPIKt.getVarp; + /** * Represents an object's definition. * @author Emperor @@ -520,14 +523,14 @@ public class SceneryDefinition extends Definition { // for (int id = 0; id <= 27325; id++) { // ObjectDefinition def = ObjectDefinition.forId(id); // if (def.mapIcon > 69) { - // System.out.println(id + " - " + def.getName() + " has map icon " + + // def.mapIcon); // } // } // return; 2105 // } /*ObjectDefinition def = ObjectDefinition.forId(2105); - System.out.println("size: " + def.getClass().getDeclaredFields().length); + for (Field f : def.getClass().getDeclaredFields()) { if (!Modifier.isStatic(f.getModifiers())) { if (f.getType().isArray()) { @@ -538,11 +541,11 @@ public class SceneryDefinition extends Definition { for (int i = 0; i < length; i++) { System.out.print(Array.get(object, i) + (i < (length - 1) ? ", " : "]")); } - System.out.println(); + continue; } } - System.out.println(f.getName() + ", " + f.get(def)); + } } for (Field f : def.getClass().getSuperclass().getDeclaredFields()) { @@ -555,11 +558,11 @@ public class SceneryDefinition extends Definition { for (int i = 0; i < length; i++) { System.out.print(Array.get(object, i) + (i < (length - 1) ? ", " : "]")); } - System.out.println(); + continue; } } - System.out.println(f.getName() + ", " + f.get(def)); + } }*/ } @@ -615,7 +618,7 @@ public class SceneryDefinition extends Definition { // SystemLogger.logErr("----------------------------------------------------\n\n\n"); while (true) { if (!buffer.hasRemaining()) { - SystemLogger.logErr(SceneryDefinition.class, "Buffer empty for " + objectId); + log(SceneryDefinition.class, Log.ERR, "Buffer empty for " + objectId); break; } int opcode = buffer.get() & 0xFF; @@ -801,7 +804,7 @@ public class SceneryDefinition extends Definition { } } else { if (opcode != 0) { - SystemLogger.logErr(SceneryDefinition.class, "Unhandled object definition opcode: " + opcode); + log(SceneryDefinition.class, Log.ERR, "Unhandled object definition opcode: " + opcode); } break; } @@ -887,20 +890,28 @@ public class SceneryDefinition extends Definition { configValue = def.getValue(player); } } else if (configId != -1) { - configValue = player.getConfigManager().get(configId); + configValue = getVarp(player, configId); } } else { configValue = 0; } - if (configValue < 0 || configValue >= childrenIds.length - 1 || childrenIds[configValue] == -1) { + SceneryDefinition childDef = getChildObjectAtIndex(configValue); + if (childDef != null) childDef.configFileId = this.configFileId; + return childDef; + } + + public SceneryDefinition getChildObjectAtIndex (int index) { + if (childrenIds == null || childrenIds.length < 1) { + return this; + } + if (index < 0 || index >= childrenIds.length - 1 || childrenIds[index] == -1) { int objectId = childrenIds[childrenIds.length - 1]; if (objectId != -1) { return forId(objectId); } return this; } - forId(childrenIds[configValue]).configFileId = this.configFileId; - return forId(childrenIds[configValue]); + return forId(childrenIds[index]); } /** diff --git a/Server/src/main/core/cache/def/impl/ScriptArgs.java b/Server/src/main/core/cache/def/impl/ScriptArgs.java new file mode 100644 index 000000000..27b037328 --- /dev/null +++ b/Server/src/main/core/cache/def/impl/ScriptArgs.java @@ -0,0 +1,11 @@ +package core.cache.def.impl; + +public class ScriptArgs { + public int id; + public Object[] args; + + public ScriptArgs(int id, Object[] args) { + this.id = id; + this.args = args; + } +} diff --git a/Server/src/main/core/cache/def/impl/ScriptTriggers.java b/Server/src/main/core/cache/def/impl/ScriptTriggers.java new file mode 100644 index 000000000..dbc165a00 --- /dev/null +++ b/Server/src/main/core/cache/def/impl/ScriptTriggers.java @@ -0,0 +1,9 @@ +package core.cache.def.impl; + +public class ScriptTriggers { + public int[] varpTriggers; + public int[] inventoryTriggers; + public int[] statTriggers; + public int[] varcTriggers; + public int[] varcstrTriggers; +} diff --git a/Server/src/main/core/cache/def/impl/Struct.java b/Server/src/main/core/cache/def/impl/Struct.java index bd3d0a38e..92081ade0 100644 --- a/Server/src/main/core/cache/def/impl/Struct.java +++ b/Server/src/main/core/cache/def/impl/Struct.java @@ -2,12 +2,15 @@ package core.cache.def.impl; import core.cache.Cache; import core.cache.misc.buffer.ByteBufferUtils; +import core.tools.Log; import core.tools.SystemLogger; import java.nio.ByteBuffer; import java.util.HashMap; import java.util.Map; +import static core.api.ContentAPIKt.log; + public class Struct { /** @@ -28,7 +31,7 @@ public class Struct { public int getInt(int key){ if(!dataStore.containsKey(key)){ - SystemLogger.logErr(this.getClass(), "Invalid value passed for key: " + key + " struct: " + id); + log(this.getClass(), Log.ERR, "Invalid value passed for key: " + key + " struct: " + id); return -1; } return (int) dataStore.get(key); diff --git a/Server/src/main/core/cache/def/impl/VarbitDefinition.java b/Server/src/main/core/cache/def/impl/VarbitDefinition.java index 10c20ae14..9fb4f6b15 100644 --- a/Server/src/main/core/cache/def/impl/VarbitDefinition.java +++ b/Server/src/main/core/cache/def/impl/VarbitDefinition.java @@ -7,6 +7,8 @@ import java.nio.ByteBuffer; import java.util.HashMap; import java.util.Map; +import static core.api.ContentAPIKt.*; + /** * Handles config definition reading. * @author Emperor @@ -126,12 +128,7 @@ public final class VarbitDefinition { * @return The config value. */ public int getValue(Player player) { - int size = BITS[endBit - startBit]; - int bitValue = player.varpManager.get(getVarpId()).getBitRangeValue(getStartBit(), getStartBit() + (endBit - startBit)); - if(bitValue != 0){ - return size & (bitValue >>> startBit); - } - return size & (player.getConfigManager().get(varpId) >>> startBit); + return getVarbit(player, id); } /** @@ -162,8 +159,15 @@ public final class VarbitDefinition { return endBit; } + public int getMask() { + int mask = 0; + for (int i = startBit; i <= endBit; i++) + mask |= (1 << (i - startBit)); + return mask; + } + @Override public String toString() { return "ConfigFileDefinition [id=" + id + ", configId=" + varpId + ", bitShift=" + startBit + ", bitSize=" + endBit + "]"; } -} \ No newline at end of file +} diff --git a/Server/src/main/core/game/activity/ActivityManager.java b/Server/src/main/core/game/activity/ActivityManager.java index 1f5ea3260..15ab8ff39 100644 --- a/Server/src/main/core/game/activity/ActivityManager.java +++ b/Server/src/main/core/game/activity/ActivityManager.java @@ -1,12 +1,15 @@ package core.game.activity; import core.game.node.entity.player.Player; +import core.tools.Log; import core.tools.SystemLogger; import core.game.world.GameWorld; import java.util.HashMap; import java.util.Map; +import static core.api.ContentAPIKt.log; + /** * Manages the activities. * @author Emperor @@ -50,7 +53,7 @@ public final class ActivityManager { ActivityPlugin plugin = ACTIVITIES.get(name); if (plugin == null) { if (GameWorld.getSettings().isDevMode()) { - SystemLogger.logErr(ActivityManager.class, "Unhandled activity - " + name + "!"); + log(ActivityManager.class, Log.ERR, "Unhandled activity - " + name + "!"); } return false; } diff --git a/Server/src/main/core/game/activity/Cutscene.kt b/Server/src/main/core/game/activity/Cutscene.kt index 8de3fe6f4..5cf9584ec 100644 --- a/Server/src/main/core/game/activity/Cutscene.kt +++ b/Server/src/main/core/game/activity/Cutscene.kt @@ -22,9 +22,11 @@ import core.net.packet.out.MinimapState import org.rs09.consts.Components import core.ServerConstants import core.api.Event +import core.api.utils.CameraShakeType import core.api.utils.PlayerCamera -import core.tools.SystemLogger +import core.game.system.timer.impl.AntiMacro import core.game.world.GameWorld +import core.tools.Log /** * A utility class for making cutscenes. @@ -55,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. */ @@ -111,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 @@ -221,13 +256,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) @@ -237,31 +278,24 @@ abstract class Cutscene(val player: Player) { player.lock() player.hook(Event.SelfDeath, CUTSCENE_DEATH_HOOK) player.logoutListeners["cutscene"] = {player -> player.location = exitLocation; player.getCutscene()?.end() } - content.global.ame.RandomEventManager.getInstance(player)!!.enabled = false + 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 -> { - try { - endActions?.invoke() - } catch (e: Exception) { - SystemLogger.logErr(this::class.java, "There's some bad nasty code in ${this::class.java.simpleName} end actions!") - e.printStackTrace() - } + 0 -> player.properties.teleportLocation = exitLocation + 1 -> { return true } } @@ -280,8 +314,62 @@ abstract class Cutscene(val player: Player) { clearNPCs() player.unhook(CUTSCENE_DEATH_HOOK) player.logoutListeners.remove("cutscene") - content.global.ame.RandomEventManager.getInstance(player)?.enabled = true + 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() + } + } + }) + } + + /** + * 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() + } } }) } @@ -312,6 +400,42 @@ abstract class Cutscene(val player: Player) { camera.rotateTo(globalLoc.x, globalLoc.y, height, speed) } + /** + * Rotates the camera to face the given difference of the X and Y coordinates + * @param regionX the difference X value to rotate the camera to (0-63) + * @param regionY the difference Y value to rotate the camera to (0-63) + * @param height (optional) the height of the camera, defaults to 300. + * @param speed (optional) the speed of the camera transition, defaults to 100. + */ + fun rotateCameraBy(diffX: Int, diffY: Int, diffHeight: Int = 300, diffSpeed: Int = 100) + { + camera.rotateBy(diffX, diffY, diffHeight, diffSpeed) + } + + /** + * Manipulates the camera using different camera types. Also known as "shake" + * @param cameraType the "type" of shake: TRUCK, PEDESTAL, DOLLY, PAN, TILT + * @param jitter (optional) the amount to rock the camera while looping + * @param amplitude (optional) the maximum extent of camera vibration + * @param frequency (optional) the rate at which jitter and amplitude are repeated + * @param speed (optional) the rate at which the whole camera "shake" loop is repeated + * + * WARNING: Playing around with camera shake values may potentially trigger seizures for people with photosensitive epilepsy. + * Please use care when using the "shake" camera values. + */ + fun shakeCamera(cameraType: CameraShakeType, jitter: Int = 0, amplitude: Int = 0, frequency: Int = 128, speed: Int = 2) + { + camera.shake(cameraType.ordinal, jitter, amplitude, frequency, speed) + } + + /** + * Resets the camera to the current player position + */ + fun resetCamera() + { + camera.reset() + } + /** * Sets the location the player is placed at when the cutscene ends. */ @@ -347,7 +471,7 @@ abstract class Cutscene(val player: Player) { fun logCutscene(message: String) { if(ServerConstants.LOG_CUTSCENE) - SystemLogger.logInfo(this::class.java, "$message") + log(this::class.java, Log.FINE, "$message") } fun clearNPCs() { diff --git a/Server/src/main/core/game/bots/AIPlayer.java b/Server/src/main/core/game/bots/AIPlayer.java index 288a1a9bb..079875dec 100644 --- a/Server/src/main/core/game/bots/AIPlayer.java +++ b/Server/src/main/core/game/bots/AIPlayer.java @@ -217,7 +217,7 @@ public class AIPlayer extends Player { } } } catch (FileNotFoundException e) { - System.out.println("Missing " + fileName); + e.printStackTrace(); } } @@ -349,7 +349,7 @@ public class AIPlayer extends Player { int meX = this.getLocation().getX(); int meY = this.getLocation().getY(); //int meX2 = this.getLocation().getX(); - //System.out.println("local " + meX + " real x? " + meX2 ); + ArrayList nodes = new ArrayList(); for (NPC npc : RegionManager.getLocalNpcs(this, range)) { if (entrys.contains(npc.getId())) @@ -381,7 +381,7 @@ public class AIPlayer extends Player { public Node getClosestNodeWithEntryAndDirection(int range, int entry, Direction direction) { ArrayList nodeList = getNodeInRange(range, entry); if (nodeList.isEmpty()) { - //System.out.println("nodelist empty"); + return null; } Node node = getClosestNodeinNodeListWithDirection(nodeList, direction); @@ -391,7 +391,7 @@ public class AIPlayer extends Player { public Node getClosestNodeWithEntry(int range, int entry) { ArrayList nodeList = getNodeInRange(range, entry); if (nodeList.isEmpty()) { - //System.out.println("nodelist empty"); + return null; } Node node = getClosestNodeinNodeList(nodeList); @@ -401,7 +401,7 @@ public class AIPlayer extends Player { public Node getClosestNodeWithEntry(int range, List entrys) { ArrayList nodeList = getNodeInRange(range, entrys); if (nodeList.isEmpty()) { - //System.out.println("nodelist empty"); + return null; } Node node = getClosestNodeinNodeList(nodeList); @@ -453,7 +453,7 @@ public class AIPlayer extends Player { private Node getClosestNodeinNodeListWithDirection(ArrayList nodes, Direction direction) { if (nodes.isEmpty()) { - //System.out.println("nodelist empty"); + return null; } @@ -471,7 +471,7 @@ public class AIPlayer extends Player { private Node getClosestNodeinNodeList(ArrayList nodes) { if (nodes.isEmpty()) { - //System.out.println("nodelist empty"); + return null; } @@ -490,7 +490,7 @@ public class AIPlayer extends Player { @Override public void clear() { botMapping.remove(uid); - super.clear(true); + super.clear(); } @Override @@ -528,7 +528,7 @@ public class AIPlayer extends Player { Repository.getPlayers().remove(player); return; } - //SystemLogger.logErr(this.getClass(), "Could not deregister AIP#" + uid + ": UID not added to the mapping!"); + //log(this.getClass(), Log.ERR, "Could not deregister AIP#" + uid + ": UID not added to the mapping!"); } @Override diff --git a/Server/src/main/core/game/bots/AIRepository.kt b/Server/src/main/core/game/bots/AIRepository.kt index ea17452ad..3faef5166 100644 --- a/Server/src/main/core/game/bots/AIRepository.kt +++ b/Server/src/main/core/game/bots/AIRepository.kt @@ -3,6 +3,7 @@ package core.game.bots import core.game.node.entity.player.Player import core.game.node.item.GroundItem import core.game.ge.GrandExchangeOffer +import content.global.bots.* /** * A repository for bots to make use of that can contain any info that might be useful to them. @@ -43,11 +44,26 @@ class AIRepository { } @JvmStatic fun clearAllBots() { - PulseRepository.forEach { (_, it) -> - it.stop(); - it.botScript.bot.clear(); + PulseRepository.toList().forEach { (_, it) -> + it.stop() + it.botScript.bot.clear() AIPlayer.deregister((it.botScript.bot as AIPlayer).uid) } } + + @JvmStatic fun sendBotInfo (player: Player, bot: AIPlayer) { + val pulse = PulseRepository[bot.username.lowercase()] ?: return + bot.setAttribute("tracked", true) + player.debug("[Bot: ${bot.username}][${pulse.botScript::class.simpleName}]=================") + player.debug("PM Pulse Running? ${if(bot.pulseManager.hasPulseRunning()) bot.pulseManager.getCurrent()::class.java.name else "No"}") + player.debug("Interaction Running? ${bot.scripts.getActiveScript() != null} ${bot.scripts.getActiveScript()?.let { " : " + it.execution!!::class.java.name } ?: ""}") + player.debug("Botscript Running? ${pulse.botScript.running}") + player.debug("Random Delay? ${pulse.randomDelay}") + player.debug("Delayed? ${bot.scripts.delay}") + if (pulse.botScript is Adventurer) { + player.debug("State: ${pulse.botScript.state.name}") + } + player.debug("==========================================") + } } } diff --git a/Server/src/main/core/game/bots/CombatBot.kt b/Server/src/main/core/game/bots/CombatBot.kt index bf2c35bdd..a5b5c6780 100644 --- a/Server/src/main/core/game/bots/CombatBot.kt +++ b/Server/src/main/core/game/bots/CombatBot.kt @@ -43,7 +43,7 @@ class CombatBot(location: Location) : AIPlayer(location) { this.lock(3) //this.animate(new Animation(829)); val food = inventory.getItem(foodItem) - var consumable: Consumable? = Consumables.getConsumableById(food.id) + var consumable: Consumable? = Consumables.getConsumableById(food.id)?.consumable if (consumable == null) { consumable = Food(IntArray(food.id), HealingEffect(1)) } diff --git a/Server/src/main/core/game/bots/CombatBotAssembler.kt b/Server/src/main/core/game/bots/CombatBotAssembler.kt index fa0a7fde5..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) @@ -422,6 +422,7 @@ class CombatBotAssembler { bot.skills.setStaticLevel(Skills.RANGE,1) bot.skills.setStaticLevel(Skills.MAGIC,1) } + else -> {} } bot.skills.addExperience(Skills.HITPOINTS, (totalXPAdd / skillAmt) * 0.2) diff --git a/Server/src/main/core/game/bots/GeneralBotCreator.kt b/Server/src/main/core/game/bots/GeneralBotCreator.kt index 5aa7147b9..7e3cb5478 100644 --- a/Server/src/main/core/game/bots/GeneralBotCreator.kt +++ b/Server/src/main/core/game/bots/GeneralBotCreator.kt @@ -7,6 +7,15 @@ import core.game.world.map.Location import core.tools.RandomFunction import core.Server import content.global.bots.Idler +import core.api.* +import core.game.interaction.MovementPulse +import core.tools.Log + +class GBCTick : TickListener { + override fun tick() { + GeneralBotCreator.botPulsesTriggeredThisTick = 0 + } +} class GeneralBotCreator { //org/crandor/net/packet/in/InteractionPacket.java <<< This is a very useful class for learning to code bots @@ -28,19 +37,57 @@ class GeneralBotCreator { player.setAttribute("botting:script",pulse) } - inner class BotScriptPulse(public val botScript: Script, val isPlayer: Boolean = false) : Pulse(1){ + companion object { + var botPulsesTriggeredThisTick = 0 + } + + inner class BotScriptPulse(public val botScript: Script, val isPlayer: Boolean = false) : Pulse(1) { var ticks = 0 init { botScript.init(isPlayer) } var randomDelay = 0 - + var lastBotLocation: Location = botScript.bot.location.transform(0,0,0) + var lastBotMoveTicks = getWorldTicks() override fun pulse(): Boolean { if(randomDelay > 0){ randomDelay -= 1 return false } - if (!botScript.bot.pulseManager.hasPulseRunning()) { + if (botScript.bot.pulseManager.hasPulseRunning()) { + if (botScript.bot.pulseManager.current is MovementPulse) { + if (botScript.bot.location != lastBotLocation) { + lastBotLocation = botScript.bot.location.transform(0,0,0) + lastBotMoveTicks = getWorldTicks() + } + if (lastBotLocation == botScript.bot.location && getWorldTicks() - lastBotMoveTicks > 5) { + botScript.bot.pulseManager.current.stop() + } + } + } + + /* + * Chatboxes and interfaces will cause the authentic interaction subsystem + * to pause any currently running authentically-implemented interactions. + * + * When this happens, if the interfaces are not handled by the script and closed, + * execution will remain paused as the game believes the bot is still doing something + * (because they still have an authentic interaction in the queue, that is not advancing + * because it is paused) and so the script does not execute, but the pulse is waiting on botscript input. + * + * This deadlock can be worked around by just closing these. + * + * Set endDialogue to FALSE if you want + * to avoid automatic dialogue termination (useful for, for example, boat travel) + */ + if (botScript.bot.scripts.getActiveScript() != null && botScript.bot.hasModalOpen() && botScript.endDialogue) { + botScript.bot.interfaceManager.closeChatbox() + botScript.bot.interfaceManager.openChatbox(137) + botScript.bot.interfaceManager.close() + botScript.bot.dialogueInterpreter.close() + } + + if (!botScript.bot.pulseManager.hasPulseRunning() && botScript.bot.scripts.getActiveScript() == null) { /*if (ticks++ >= RandomFunction.random(90000,120000)) { AIPlayer.deregister(botScript.bot.uid) @@ -51,10 +98,12 @@ class GeneralBotCreator { }*/ if(!botScript.running) return true //has to be separated this way or it double-submits the respawn pulse. + if (botPulsesTriggeredThisTick++ >= 75) + return false - val idleRoll = RandomFunction.random(50) + val idleRoll = RandomFunction.random(10) if(idleRoll == 2 && botScript !is Idler){ - randomDelay += RandomFunction.random(2,20) + randomDelay += RandomFunction.random(20,50) return false } botScript.tick() @@ -77,4 +126,4 @@ class GeneralBotCreator { return true } } -} \ No newline at end of file +} diff --git a/Server/src/main/core/game/bots/PvMBots.java b/Server/src/main/core/game/bots/PvMBots.java index 7f7a87f47..6aef6e01e 100644 --- a/Server/src/main/core/game/bots/PvMBots.java +++ b/Server/src/main/core/game/bots/PvMBots.java @@ -133,10 +133,10 @@ public class PvMBots extends AIPlayer { //this.animate(new Animation(829)); Item food = this.getInventory().getItem(foodItem); - Consumable consumable = Consumables.getConsumableById(food.getId()); + Consumable consumable = Consumables.getConsumableById(food.getId()).getConsumable(); if (consumable == null) { - consumable = new Food(new int[] {food.getId()}, new HealingEffect(1)); + return; } consumable.consume(food, this); diff --git a/Server/src/main/core/game/bots/Script.java b/Server/src/main/core/game/bots/Script.java index 0c622998f..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,11 +14,13 @@ 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 Player bot; public boolean running = true; + public boolean endDialogue = true; public void init(boolean isPlayer) { @@ -25,6 +28,13 @@ public abstract class Script { scriptAPI = new ScriptAPI(bot); if(!isPlayer) { + // Skills and quests need to be set before equipment in case equipment has level or quest requirements + for (Map.Entry skill : skills.entrySet()) { + setLevel(skill.getKey(), skill.getValue()); + } + for (Quests quest : quests) { + bot.getQuestRepository().setStage(bot.getQuestRepository().getQuest(quest), 100); + } for (Item i : equipment) { bot.getEquipment().add(i, true, false); } @@ -32,9 +42,6 @@ public abstract class Script { for (Item i : inventory) { bot.getInventory().add(i); } - for (Map.Entry skill : skills.entrySet()) { - setLevel(skill.getKey(), skill.getValue()); - } } } @@ -54,4 +61,4 @@ public abstract class Script { // This does not get called and all implementations should be removed public abstract Script newInstance(); -} \ No newline at end of file +} diff --git a/Server/src/main/core/game/bots/ScriptAPI.kt b/Server/src/main/core/game/bots/ScriptAPI.kt index 3a70ef9f7..5a3306e4c 100644 --- a/Server/src/main/core/game/bots/ScriptAPI.kt +++ b/Server/src/main/core/game/bots/ScriptAPI.kt @@ -1,6 +1,9 @@ package core.game.bots import core.api.* +import core.game.interaction.NodeUsageEvent +import core.game.interaction.PluginInteractionManager +import core.game.interaction.UseWithHandler import core.cache.def.impl.ItemDefinition import core.game.component.Component import core.game.consumable.Consumable @@ -26,7 +29,7 @@ import core.game.world.map.path.Pathfinder import core.game.world.update.flag.context.Animation import core.game.world.update.flag.context.ChatMessage import core.game.world.update.flag.context.Graphics -import core.game.world.update.flag.player.ChatFlag +import core.game.world.update.flag.* import core.tools.RandomFunction import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch @@ -42,12 +45,16 @@ import core.tools.SystemLogger import core.game.system.config.ItemConfigParser import core.game.world.GameWorld import core.game.world.repository.Repository +import core.tools.Log import core.tools.colorize import java.util.* import java.util.concurrent.CountDownLatch import kotlin.math.max 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) @@ -78,34 +85,46 @@ class ScriptAPI(private val bot: Player) { val opt: Option? = node.interaction.options.filter {it != null && it.name.equals(option, true) }.firstOrNull() if(opt == null){ - SystemLogger.logWarn(this::class.java, "Invalid option name provided: $option") + log(this::class.java, Log.WARN, "Invalid option name provided: $option") return } if(!InteractionListeners.run(node.id, type, option, bot, node)) node.interaction.handle(bot, opt) } - /** - * Gets the nearest node with name entityName - * @param entityName the name of the node to look for - * @return the nearest node with a matching name or null - * @author Ceikry - */ - fun getNearestNode(entityName: String): Node? { - var entity: Node? = null - var minDistance = Double.MAX_VALUE - for (node in RegionManager.forId(bot.location.regionId).planes[bot.location.z].entities) { - if (node != null && node.name == entityName && distance(bot, node) < minDistance && !Pathfinder.find(bot, node).isMoveNear) { - entity = node - minDistance = distance(bot, node) - } + fun useWith(bot: Player, itemId: Int, node: Node?) { + if(node == null) return + + val type = when(node){ + is Scenery -> IntType.SCENERY + is NPC -> IntType.NPC + is Item -> IntType.ITEM + else -> null + } ?: return + + val item = bot.inventory.getItem(Item(itemId)) + + val childNode = node.asScenery()?.getChild(bot) + + if (InteractionListeners.run(item, node, type, bot)) + return + if (childNode != null && childNode.id != node.id) { + if (InteractionListeners.run(item, childNode, type, bot)) + return } - return entity + val flipped = type == IntType.ITEM && item.id < node.id + val event = if (flipped) + NodeUsageEvent(bot, 0, node, item) + else + NodeUsageEvent(bot, 0, item, childNode ?: node) + if (PluginInteractionManager.handle(bot, event)) + return + UseWithHandler.run(event) } fun sendChat(message: String) { bot.sendChat(message) - bot.updateMasks.register(ChatFlag(ChatMessage(bot, message, 0, 0))) + bot.updateMasks.register(EntityFlag.Chat, ChatMessage(bot, message, 0, 0)) } /** @@ -114,33 +133,11 @@ class ScriptAPI(private val bot: Player) { * @return the nearest node with a matching name or null * @author Ceikry */ - fun getNearestNodeFromList(acceptedNames: List, isObject: Boolean): Node? { - if (isObject) { - var entity: Node? = null - var minDistance = Double.MAX_VALUE - for (objects in RegionManager.forId(bot.location.regionId).planes[bot.location.z].objects) { - for(e in objects) { - val name = e?.name - if (e != null && acceptedNames.contains(name) && distance(bot, e) < minDistance && !Pathfinder.find(bot, e).isMoveNear && e.isActive) { - entity = e - minDistance = distance(bot, e) - } - } - } - return if(entity == null) null else entity as Scenery - } else { - var entity: Node? = null - var minDistance = Double.MAX_VALUE - for (e in RegionManager.forId(bot.location.regionId).planes[bot.location.z].entities) { - val name = e?.name - if (e != null && acceptedNames.contains(name) && distance(bot, e) < minDistance && !Pathfinder.find(bot, e).isMoveNear) { - entity = e - minDistance = distance(bot, e) - } - } - return entity - } + if (isObject) + return processEvaluationList(RegionManager.forId(bot.location.regionId).planes[bot.location.z].objectList, acceptedName = acceptedNames) + else + return processEvaluationList(RegionManager.forId(bot.location.regionId).planes[bot.location.z].entities, acceptedName = acceptedNames) } /** @@ -150,30 +147,21 @@ class ScriptAPI(private val bot: Player) { * @return the closest node with matching id or null. * @author Ceikry */ - fun getNearestNode(id: Int, `object`: Boolean): Node? { - if (`object`) { - var entity: Node? = null - var minDistance = Double.MAX_VALUE - for (objects in RegionManager.forId(bot.location.regionId).planes[bot.location.z].objects) { - for(e in objects) { - if (e != null && e.id == id && distance(bot, e) < minDistance && !Pathfinder.find(bot, e).isMoveNear && e.isActive) { - entity = e - minDistance = distance(bot, e) - } - } - } - return if(entity == null) null else entity as Scenery - } else { - var entity: Node? = null - var minDistance = Double.MAX_VALUE - for (e in RegionManager.forId(bot.location.regionId).planes[bot.location.z].entities) { - if (e != null && e.id == id && distance(bot, e) < minDistance && !Pathfinder.find(bot, e).isMoveNear) { - entity = e - minDistance = distance(bot, e) - } - } - return entity - } + fun getNearestNode(id: Int, isObject: Boolean): Node? { + if (isObject) + return processEvaluationList(RegionManager.forId(bot.location.regionId).planes[bot.location.z].objectList, acceptedId = id) + else + return processEvaluationList(RegionManager.forId(bot.location.regionId).planes[bot.location.z].entities, acceptedId = id) + } + + /** + * Gets the nearest node with name entityName + * @param entityName the name of the node to look for + * @return the nearest node with a matching name or null + * @author Ceikry + */ + fun getNearestNode(entityName: String): Node? { + return processEvaluationList(RegionManager.forId(bot.location.regionId).planes[bot.location.z].entities, acceptedName = listOf(entityName)) } /** @@ -183,32 +171,48 @@ class ScriptAPI(private val bot: Player) { * @return the nearest matching node or null. * @author Ceikry */ - fun getNearestNode(name: String, `object`: Boolean): Node? { - if (`object`) { - var entity: Node? = null - var minDistance = Double.MAX_VALUE - for (objects in RegionManager.forId(bot.location.regionId).planes[bot.location.z].objects) { - for(e in objects) { - if (e != null && e.name.toLowerCase() == name.toLowerCase() && distance(bot, e) < minDistance && !Pathfinder.find(bot, e).isMoveNear && e.isActive) { - entity = e - minDistance = distance(bot, e) - } - } - } - return if(entity == null) null else entity as Scenery + fun getNearestNode(name: String, isObject: Boolean): Node? { + if (isObject) + return processEvaluationList(RegionManager.forId(bot.location.regionId).planes[bot.location.z].objectList, acceptedName = listOf(name)) + else + return processEvaluationList(RegionManager.forId(bot.location.regionId).planes[bot.location.z].entities, acceptedName = listOf(name)) + } + + fun getNearestObjectByPredicate(predicate: (Node?) -> Boolean): Node? { + return processEvaluationList(RegionManager.forId(bot.location.regionId).planes[bot.location.z].objectList, acceptedPredicate = predicate) + } + + fun evaluateViability (e: Node?, minDistance: Double, maxDistance: Double, acceptedNames: List? = null, acceptedId: Int = -1, acceptedPredicate: ((Node?) -> Boolean)? = null): Boolean { + if (e == null || !e.isActive) + return false + if (acceptedId != -1 && e.id != acceptedId) + return false + + val dist = distance(bot, e) + if (dist > maxDistance || dist > minDistance) + return false + + if (acceptedPredicate != null) { + return acceptedPredicate(e) && !Pathfinder.find(bot, e).isMoveNear } else { - var entity: Node? = null - var minDistance = Double.MAX_VALUE - for (e in RegionManager.forId(bot.location.regionId).planes[bot.location.z].entities) { - if (e != null && e.name.toLowerCase() == name.toLowerCase() && distance(bot, e) < minDistance && !Pathfinder.find(bot, e).isMoveNear) { - entity = e - minDistance = distance(bot, e) - } - } - return entity + val name = e?.name + return (acceptedNames?.stream()?.anyMatch({ s -> s.equals(name, true) }) ?: true && !Pathfinder.find(bot, e).isMoveNear) } } + fun processEvaluationList (list: List, acceptedName: List? = null, acceptedId: Int = -1, acceptedPredicate: ((Node?) -> Boolean)? = null): Node? { + var entity: Node? = null + var minDistance = Double.MAX_VALUE + val maxDistance = ServerConstants.MAX_PATHFIND_DISTANCE.toDouble() + for (e in list) { + if (evaluateViability(e, minDistance, maxDistance, acceptedName, acceptedId, acceptedPredicate)) { + entity = e + minDistance = distance(bot, e) + } + } + return entity + } + /** * Gets the nearest ground item with matching ID from the list in AIRepository. * @param id the id of the ground item we are looking for. @@ -349,9 +353,7 @@ class ScriptAPI(private val bot: Player) { */ fun walkTo(loc: Location){ if(!bot.walkingQueue.isMoving) { - GlobalScope.launch { - walkToIterator(loc) - } + walkToIterator(loc) } } @@ -390,14 +392,25 @@ class ScriptAPI(private val bot: Player) { */ fun randomWalkTo(loc: Location, radius: Int) { if(!bot.walkingQueue.isMoving) { - GlobalScope.launch { - var newloc = loc.transform(RandomFunction.random(radius,-radius), - RandomFunction.random(radius,-radius), 0) - walkToIterator(newloc) - } + var newloc = loc.transform(RandomFunction.random(radius,-radius),RandomFunction.random(radius,-radius), 0) + walkToIterator(newloc) } } + /** + * @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. @@ -407,15 +420,12 @@ class ScriptAPI(private val bot: Player) { private fun walkToIterator(loc: Location){ var diffX = loc.x - bot.location.x var diffY = loc.y - bot.location.y - while(!bot.location.transform(diffX, diffY, 0).withinDistance(bot.location)) { - diffX /= 2 - diffY /= 2 - } - GameWorld.Pulser.submit(object : MovementPulse(bot, bot.location.transform(diffX, diffY, 0), Pathfinder.SMART) { - override fun pulse(): Boolean { - return true - } - }) + + val vec = Vector.betweenLocs(bot.location, loc) + val norm = vec.normalized() + val tiles = kotlin.math.min(kotlin.math.floor(vec.magnitude()).toInt(), ServerConstants.MAX_PATHFIND_DISTANCE - 1) + val loc = bot.location.transform(norm * tiles) + bot.pulseManager.run(object : MovementPulse(bot, loc) { override fun pulse() : Boolean { return true } }) } /** @@ -468,6 +478,7 @@ class ScriptAPI(private val bot: Player) { override fun pulse(): Boolean { bot.unlock() bot.properties.teleportLocation = location + bot.pulseManager.clear() bot.animator.reset() return true } @@ -491,7 +502,6 @@ class ScriptAPI(private val bot: Player) { } val canSell = GrandExchange.addBotOffer(actualId, itemAmt) if (canSell && saleIsBigNews(actualId, itemAmt)) { - SystemLogger.logAI(this::class.java, "Offered $itemAmt of $actualId on the GE.") Repository.sendNews(SERVER_GE_NAME + " just offered " + itemAmt + " " + ItemDefinition.forId(actualId).name.toLowerCase() + " on the GE.") } bot.bank.remove(Item(id, itemAmt)) @@ -558,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) @@ -567,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()) + } } /** @@ -578,7 +616,8 @@ class ScriptAPI(private val bot: Player) { * @author Gexja */ fun saleIsBigNews(itemID: Int, amount: Int): Boolean { - return ItemDefinition.forId(itemID).getAlchemyValue(true) * amount >= 500 + return ItemDefinition.forId(itemID).getAlchemyValue(true) * amount >= (GameWorld.settings?.ge_announcement_limit + ?: 500) } /** @@ -598,6 +637,7 @@ class ScriptAPI(private val bot: Player) { override fun pulse(): Boolean { bot.unlock() bot.properties.teleportLocation = location + bot.pulseManager.clear() bot.animator.reset() return true } @@ -627,15 +667,21 @@ class ScriptAPI(private val bot: Player) { * @param none * @author cfunnyman joe */ - fun bankAll(){ + fun bankAll(onComplete: (() -> Unit)? = null){ class BankingPulse() : Pulse(20){ override fun pulse(): Boolean { for(item in bot.inventory.toArray()){ - var itemAmount = bot.inventory.getAmount(item) + if(item != null) { + var itemAmount = bot.inventory.getAmount(item) - bot.inventory.remove(item) - bot.bank.add(item) + if(bot.inventory.remove(item)) { + bot.bank.add(item) + } + } } + if(onComplete != null) { + onComplete?.invoke() + } return true } } @@ -653,7 +699,7 @@ class ScriptAPI(private val bot: Player) { bot.lock(3) //this.animate(new Animation(829)); val food = bot.inventory.getItem(foodItem) - var consumable: Consumable? = Consumables.getConsumableById(foodId) + var consumable: Consumable? = Consumables.getConsumableById(foodId)?.consumable if (consumable == null) { consumable = Food(intArrayOf(food.id), HealingEffect(1)) } @@ -673,7 +719,7 @@ class ScriptAPI(private val bot: Player) { bot.lock(3) //this.animate(new Animation(829)); val food = bot.inventory.getItem(foodItem) - var consumable: Consumable? = Consumables.getConsumableById(foodId) + var consumable: Consumable? = Consumables.getConsumableById(foodId)?.consumable if (consumable == null) { consumable = Food(intArrayOf(foodId), HealingEffect(1)) } diff --git a/Server/src/main/core/game/bots/SkillingBotAssembler.kt b/Server/src/main/core/game/bots/SkillingBotAssembler.kt index 2586504d3..4e47773fa 100644 --- a/Server/src/main/core/game/bots/SkillingBotAssembler.kt +++ b/Server/src/main/core/game/bots/SkillingBotAssembler.kt @@ -21,8 +21,9 @@ class SkillingBotAssembler { val item = Item(i) val configs = item.definition.handlers val slot = configs["equipment_slot"] ?: continue - bot.equipment.add(item, slot as Int, - false,false) + if(bot.inventory.get(slot as Int) == null) { + bot.equipment.add(item, slot as Int,false,false) + } val reqs = configs["requirements"] if(reqs != null) for(req in configs["requirements"] as HashMap) @@ -41,4 +42,4 @@ class SkillingBotAssembler { val POORSETS = arrayOf(listOf(542,544), listOf(581), listOf(6654,6655,6656), listOf(6654,6656), listOf(636,646), listOf(638,648), listOf(), listOf(), listOf()) val AVGSETS = arrayOf(listOf(2649,342,344), listOf(2651,542,544), listOf(6654,6655,6656), listOf(6139,6141), listOf(9923,9924,9925), listOf(10400,10402,2649), listOf(10404,10406), listOf(12971,12978)) val RICHSETS = arrayOf(listOf(10330,10332,2649), listOf(12873,12880,1046), listOf(13858,13861,13864), listOf(13887,13893), listOf(3481,3483), listOf(2653,2655), listOf(2661,2663), listOf(2591,2593), listOf(14490,14492)) -} \ No newline at end of file +} diff --git a/Server/src/main/core/game/component/InterfaceType.java b/Server/src/main/core/game/component/InterfaceType.java index 937012ec6..59dd77e2a 100644 --- a/Server/src/main/core/game/component/InterfaceType.java +++ b/Server/src/main/core/game/component/InterfaceType.java @@ -53,7 +53,7 @@ public enum InterfaceType { /** * Wilderness overlay */ - WILDERNESS_OVERLAY(Components.TOPLEVEL_548, Components.TOPLEVEL_FULLSCREEN_746, 11, 3); + OVERLAY_B(Components.TOPLEVEL_548, Components.TOPLEVEL_FULLSCREEN_746, 11, 3); /** * The fixed window pane id. diff --git a/Server/src/main/core/game/consumable/Cake.java b/Server/src/main/core/game/consumable/Cake.java index 5f8bedc55..bd7286240 100644 --- a/Server/src/main/core/game/consumable/Cake.java +++ b/Server/src/main/core/game/consumable/Cake.java @@ -23,7 +23,7 @@ public class Cake extends Food { player.getInventory().remove(item); } final int initialLifePoints = player.getSkills().getLifepoints(); - Consumables.getConsumableById(item.getId()).effect.activate(player); + Consumables.getConsumableById(item.getId()).getConsumable().effect.activate(player); sendMessages(player, initialLifePoints, item, messages); } diff --git a/Server/src/main/core/game/consumable/Consumable.java b/Server/src/main/core/game/consumable/Consumable.java index 970ddb1c7..f30084740 100644 --- a/Server/src/main/core/game/consumable/Consumable.java +++ b/Server/src/main/core/game/consumable/Consumable.java @@ -11,7 +11,7 @@ import core.plugin.Plugin; /** * Represents any item that has a consumption option such as 'Eat' or 'Drink'. */ -public abstract class Consumable implements Plugin { +public abstract class Consumable { /** * Represents the item IDs of all the variants of a consumable where the last one is often the empty container, if it has any. @@ -50,15 +50,16 @@ public abstract class Consumable implements Plugin { executeConsumptionActions(player); final int nextItemId = getNextItemId(item.getId()); - if(item.getAmount() > 1){ - removeItem(player, item.getId(), Container.INVENTORY); - } else removeItem(player, item, Container.INVENTORY); - - if (nextItemId != -1) { - addItem(player, nextItemId, 1, Container.INVENTORY); + // STACKABLE + NON-RETURN + if (ids.length == 1) { + replaceSlot(player, item.getSlot(), new Item(item.getId(), (item.getAmount() - 1)), item, Container.INVENTORY); + } else { + // ITEM HAS RETURN + replaceSlot(player, item.getSlot(), new Item(nextItemId, 1), item, Container.INVENTORY); } + final int initialLifePoints = player.getSkills().getLifepoints(); - Consumables.getConsumableById(item.getId()).effect.activate(player); + Consumables.getConsumableById(item.getId()).getConsumable().effect.activate(player); sendMessages(player, initialLifePoints, item, messages); } @@ -100,17 +101,6 @@ public abstract class Consumable implements Plugin { return item.getName().replace("(4)", "").replace("(3)", "").replace("(2)", "").replace("(1)", "").trim().toLowerCase(); } - @Override - public Plugin newInstance(Object arg) throws Throwable { - Consumables.add(this); - return this; - } - - @Override - public Object fireEvent(String identifier, Object... args) { - return null; - } - public int getHealthEffectValue(Player player) { return effect.getHealthEffectValue(player); } diff --git a/Server/src/main/core/game/consumable/Drink.java b/Server/src/main/core/game/consumable/Drink.java index ee9b6c165..e5b8611ab 100644 --- a/Server/src/main/core/game/consumable/Drink.java +++ b/Server/src/main/core/game/consumable/Drink.java @@ -1,13 +1,12 @@ package core.game.consumable; import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.audio.Audio; import core.game.node.item.Item; import core.game.world.update.flag.context.Animation; -public class Drink extends Consumable { +import static core.api.ContentAPIKt.playAudio; - public static final Audio SOUND = new Audio(4580, 1, 1); +public class Drink extends Consumable { public Drink(int[] ids, ConsumableEffect effect, String... messages) { super(ids, effect, messages); @@ -26,7 +25,7 @@ public class Drink extends Consumable { @Override protected void executeConsumptionActions(Player player) { player.animate(animation); - player.getAudioManager().send(SOUND); + playAudio(player, 4580); } @Override diff --git a/Server/src/main/core/game/consumable/Food.java b/Server/src/main/core/game/consumable/Food.java index a8f31e1e3..d5b55bb82 100644 --- a/Server/src/main/core/game/consumable/Food.java +++ b/Server/src/main/core/game/consumable/Food.java @@ -1,14 +1,14 @@ package core.game.consumable; import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.audio.Audio; import core.game.node.item.Item; import core.game.world.update.flag.context.Animation; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.playAudio; public class Food extends Consumable { - public static final Audio SOUND = new Audio(2393, 1, 1); - public Food(final int[] ids, final ConsumableEffect effect, final String... messages) { super(ids, effect, messages); animation = new Animation(829); @@ -30,6 +30,6 @@ public class Food extends Consumable { } private void playEatingSound(Player player) { - player.getAudioManager().send(SOUND); + playAudio(player, Sounds.EAT_2393); } } diff --git a/Server/src/main/core/game/consumable/Potion.java b/Server/src/main/core/game/consumable/Potion.java index 8e8c03615..60afb00c3 100644 --- a/Server/src/main/core/game/consumable/Potion.java +++ b/Server/src/main/core/game/consumable/Potion.java @@ -1,10 +1,12 @@ package core.game.consumable; import content.data.consumables.Consumables; -import content.region.kandarin.barcrawl.BarcrawlManager; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.audio.Audio; import core.game.node.item.Item; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.playAudio; public class Potion extends Drink { @@ -22,15 +24,13 @@ public class Potion extends Drink { final int nextItemId = getNextItemId(item.getId()); if (nextItemId != -1) { player.getInventory().replace(new Item(nextItemId), item.getSlot()); - } else { - if (BarcrawlManager.getInstance(player).isFinished()) { - player.getInventory().remove(item); - } else { - player.getInventory().replace(new Item(VIAL), item.getSlot()); - } } + else { + player.getInventory().replace(new Item(VIAL), item.getSlot()); + } + final int initialLifePoints = player.getSkills().getLifepoints(); - Consumables.getConsumableById(item.getId()).effect.activate(player); + Consumables.getConsumableById(item.getId()).getConsumable().effect.activate(player); if (messages.length == 0) { sendDefaultMessages(player, item); } else { @@ -42,7 +42,7 @@ public class Potion extends Drink { @Override protected void executeConsumptionActions(Player player) { player.animate(animation); - player.getAudioManager().send(SOUND); + playAudio(player, Sounds.LIQUID_2401); } @Override @@ -56,15 +56,17 @@ public class Potion extends Drink { final int dosesLeft = ids.length - consumedDoses; player.getPacketDispatch().sendMessage("You drink some of your " + getFormattedName(item) + "."); if (dosesLeft > 1) { - player.getPacketDispatch().sendMessage("You have " + dosesLeft + " doses of your potion left."); + player.getPacketDispatch().sendMessage("You have " + dosesLeft + " doses of potion left."); } else if (dosesLeft == 1) { - player.getPacketDispatch().sendMessage("You have 1 dose of your potion left."); + player.getPacketDispatch().sendMessage("You have 1 dose of potion left."); } else { player.getPacketDispatch().sendMessage("You have finished your potion."); } } - public static int getDose(Item potion){ + public int getDose(Item potion){ + for (int i = 0; i < ids.length; i++) + if (ids[i] == potion.getId()) return ids.length - i; return Integer.parseInt(potion.getName().replaceAll("[^\\d.]","")); } diff --git a/Server/src/main/core/game/container/Container.java b/Server/src/main/core/game/container/Container.java index 4265e0d76..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; @@ -330,6 +329,8 @@ public class Container { */ @CheckReturnValue public boolean remove(Item item, int slot, boolean fireListener) { + if (!contains(item.getId(), item.getAmount())) + return false; Item oldItem = items[slot]; if (oldItem == null || oldItem.getId() != item.getId()) { return false; @@ -372,6 +373,50 @@ public class Container { return true; } + /** + * Removes all items from the container that have the passed in IDs + * + * @param ids Array of IDs to remove + * @return {@code True} if all the items got removed,
{@code false} if some of the + * found items were null + */ + public boolean removeAll(int[] ids) { + boolean removedAll = true; + for (int id : ids) { + if (!removeAll(id)) { + removedAll = false; + } + } + update(); + return removedAll; + } + + /** + * Removes all items from the container that have the passed in ID + * + * @param id Item ID to remove all instances of + * @return {@code True} if all the items got removed,
{@code false} if some of the + * found items were null + */ + public boolean removeAll(int id) { + ArrayList matchingIdItems = new ArrayList<>(); + for (Item item : this.items) { + // If the item is not null and the item ID matches the ID we're looking for + if (item != null && item.getId() == id) { + // Add the item to the list + matchingIdItems.add(item); + } + } + boolean res = true; + // Remove all the items from the container + for (Item item : matchingIdItems) { + if (!remove(item, false)) { + res = false; + } + } + return res; + } + /** * Replaces the item on the given slot with the argued item. * @@ -514,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 -> { @@ -555,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. * @@ -802,6 +799,26 @@ public class Container { return -1; } + /** + * Gets the item slot, taking into account the item's whole hash rather than just the ID part. + * + * @param item The item. + * @return The slot of the item in this container. + */ + public int getSlotHash(Item item) { + if (item == null) { + return -1; + } + int idHash = item.getIdHash(); + for (int i = 0; i < items.length; i++) { + Item it = items[i]; + if (it != null && it.getIdHash() == idHash) { + return i; + } + } + return -1; + } + /** * Gets the item instance. * @@ -820,6 +837,21 @@ public class Container { return null; } + /** + * Gets all instances of an item, and returns them. + * @author Player Name + * @param item the item. + * @return a list of all items that were found (can be length 0). + */ + public ArrayList getAll(Item item){ + ArrayList ret = new ArrayList(); + for(Item i : items){ + if(i == null) continue; + if(item.getId() == i.getId()) ret.add(i); + } + return ret; + } + /** * Gets the next free slot. * diff --git a/Server/src/main/core/game/container/impl/BankContainer.java b/Server/src/main/core/game/container/impl/BankContainer.java index 0e949f8b8..8937c1e8d 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; @@ -18,6 +19,9 @@ import core.net.packet.out.ContainerPacket; import java.nio.ByteBuffer; +import static core.api.ContentAPIKt.*; + + /** * Represents the bank container. * @author Emperor @@ -39,6 +43,11 @@ public final class BankContainer extends Container { */ private Player player; + /** + * Snowscape. The player reference. + */ + private Player owner; + /** * The bank listener. */ @@ -72,6 +81,7 @@ public final class BankContainer extends Container { super(SIZE, ContainerType.ALWAYS_STACK, SortType.HASH); super.register(listener = new BankListener(player)); this.player = player; + this.owner = player; } /** @@ -106,6 +116,18 @@ public final class BankContainer extends Container { ); } + /** + * Snowscape custom. Sets the current player. Used for clan banks. + * @param newplayer The new player assigned to the bank. + */ + public void setPlayer(Player newPlayer) { + // Only modify if the bank is not open, otherwise the player who is accessing it will suddenly be unable to deposit/withdraw + if (!isOpen()) { + this.player = newPlayer; + this.listener.player = newPlayer; + } + } + /** * Open the bank. */ @@ -121,20 +143,18 @@ public final class BankContainer extends Container { return; } player.getInterfaceManager().openComponent(762).setCloseEvent((player, c) -> { + removeAttribute(player, "inCostumeRoomBank"); BankContainer.this.close(); return true; }); player.getInterfaceManager().openSingleTab(new Component(763)); super.refresh(); - player.getInventory().getListeners().add(listener); player.getInventory().refresh(); - player.varpManager.get(1249).setVarbit(0,lastAmountX).send(player); + 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) { @@ -149,6 +169,7 @@ public final class BankContainer extends Container { return; } player.getInterfaceManager().openComponent(762).setCloseEvent((player1, c) -> { + removeAttribute(player, "inCostumeRoomBank"); BankContainer.this.close(); return true; }); @@ -156,34 +177,13 @@ public final class BankContainer extends Container { player.getInterfaceManager().openSingleTab(new Component(763)); player.getInventory().getListeners().add(player.getBank().listener); player.getInventory().refresh(); - player.varpManager.get(1249).setVarbit(0,lastAmountX).send(player); + 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); + } /** @@ -195,6 +195,8 @@ public final class BankContainer extends Container { player.getInterfaceManager().closeSingleTab(); player.removeAttribute("search"); player.getPacketDispatch().sendRunScript(571, ""); + //Snowscape. Set the player back to the owner of the account. + setPlayer(this.owner); } /** @@ -239,7 +241,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); @@ -247,21 +249,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 @@ -297,17 +287,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(); } /** @@ -316,7 +303,7 @@ public final class BankContainer extends Container { */ public void updateLastAmountX(int amount) { this.lastAmountX = amount; - player.varpManager.get(1249).setVarbit(0,amount).send(player); + setVarp(player, 1249, amount); } /** @@ -383,8 +370,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()); } /** @@ -408,47 +394,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; - player.getConfigManager().set(1246, value); - value = getItemsInTab(4); - value += getItemsInTab(5) << 10; - value += getItemsInTab(6) << 20; - player.getConfigManager().set(1247, value); - value = -2013265920; - value += (134217728 * (tabIndex == 10 ? 0 : tabIndex)); - value += getItemsInTab(7); - value += getItemsInTab(8) << 10; - player.getConfigManager().set(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; - player.getConfigManager().set(1246, value); - value = getItemsInTab(4); - value += getItemsInTab(5) << 10; - value += getItemsInTab(6) << 20; - player.getConfigManager().set(1247, value); - value = -2013265920; - value += (134217728 * (tabIndex == 10 ? 0 : tabIndex)); - value += getItemsInTab(7); - value += getItemsInTab(8) << 10; - player.getConfigManager().set(1248, value); - } - + /** * Gets the amount of items in one tab. * @param tabId The tab index. @@ -480,7 +436,7 @@ public final class BankContainer extends Container { * @return If items have to be noted {@code true}. */ public boolean isNoteItems() { - return player.varpManager.getVarbit(Vars.VARBIT_IFACE_BANK_NOTE_MODE) == 1; + return getVarbit(player, Vars.VARBIT_IFACE_BANK_NOTE_MODE) == 1; } /** @@ -488,8 +444,7 @@ public final class BankContainer extends Container { * @param noteItems If items have to be noted {@code true}. */ public void setNoteItems(boolean noteItems) { - player.varpManager.flagSave(Vars.VARBIT_IFACE_BANK_NOTE_MODE, true); - player.varpManager.setVarbit(Vars.VARBIT_IFACE_BANK_NOTE_MODE, noteItems ? 1 : 0); + setVarbit(player, Vars.VARBIT_IFACE_BANK_NOTE_MODE, noteItems ? 1 : 0, true); } /** @@ -513,14 +468,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); } /** @@ -528,8 +478,7 @@ public final class BankContainer extends Container { * @param insertItems The insert items value. */ public void setInsertItems(boolean insertItems) { - player.varpManager.setVarbit(Vars.VARBIT_IFACE_BANK_INSERT_MODE, insertItems ? 1 : 0); - player.varpManager.flagSave(Vars.VARBIT_IFACE_BANK_INSERT_MODE, true); + setVarbit(player, Vars.VARBIT_IFACE_BANK_INSERT_MODE, insertItems ? 1 : 0, true); } /** @@ -537,7 +486,7 @@ public final class BankContainer extends Container { * @return {@code True} if inserting items mode is enabled. */ public boolean isInsertItems() { - return player.varpManager.getVarbit(Vars.VARBIT_IFACE_BANK_INSERT_MODE) == 1; + return getVarbit(player, Vars.VARBIT_IFACE_BANK_INSERT_MODE) == 1; } /** diff --git a/Server/src/main/core/game/container/impl/EquipmentContainer.java b/Server/src/main/core/game/container/impl/EquipmentContainer.java index a059a0be9..1e5eba34b 100644 --- a/Server/src/main/core/game/container/impl/EquipmentContainer.java +++ b/Server/src/main/core/game/container/impl/EquipmentContainer.java @@ -8,7 +8,6 @@ import core.game.node.entity.combat.equipment.WeaponInterface; import core.game.node.entity.player.Player; import core.game.node.entity.skill.Skills; import core.game.node.item.Item; -import core.game.world.update.flag.player.AppearanceFlag; import core.net.packet.PacketRepository; import core.net.packet.context.ContainerContext; import core.net.packet.out.ContainerPacket; @@ -20,6 +19,8 @@ import core.game.system.config.ItemConfigParser; import java.util.ArrayList; +import static core.api.ContentAPIKt.*; + /** * Represents the equipment container. * @author Emperor @@ -302,11 +303,11 @@ public final class EquipmentContainer extends Container { player.removeAttribute("dfs_spec"); player.getProperties().getCombatPulse().setHandler(null); if (!player.getSettings().isSpecialToggled()) { - player.getConfigManager().set(301, 0); + setVarp(player, 301, 0); } } player.getAppearance().setAnimations(); - player.getUpdateMasks().register(new AppearanceFlag(player)); + player.updateAppearance(); player.getSettings().updateWeight(); updateBonuses(player); } @@ -322,9 +323,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]; } } @@ -362,7 +360,6 @@ public final class EquipmentContainer extends Container { if (!player.getInterfaceManager().hasMainComponent(667)) { return; } - PacketRepository.send(WeightUpdate.class, player.getPacketDispatch().getContext()); int index = 0; int[] bonuses = player.getProperties().getBonuses(); for (int i = 36; i < 50; i++) { diff --git a/Server/src/main/core/game/container/impl/InventoryListener.java b/Server/src/main/core/game/container/impl/InventoryListener.java index 33ffa5b19..4ef874f4a 100644 --- a/Server/src/main/core/game/container/impl/InventoryListener.java +++ b/Server/src/main/core/game/container/impl/InventoryListener.java @@ -45,7 +45,7 @@ public final class InventoryListener implements ContainerListener { } } player.getFamiliarManager().setHasPouch(pouch); - if (hadPouch != pouch && player.getSkullManager().isWilderness()) { + if (hadPouch != pouch) { player.getAppearance().sync(); } } diff --git a/Server/src/main/core/game/dialogue/DialogueBuilder.kt b/Server/src/main/core/game/dialogue/DialogueBuilder.kt index ca685b22c..5b1f542c5 100644 --- a/Server/src/main/core/game/dialogue/DialogueBuilder.kt +++ b/Server/src/main/core/game/dialogue/DialogueBuilder.kt @@ -1,13 +1,52 @@ 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 -abstract class DialogueBuilderFile : core.game.dialogue.DialogueFile() { - var data: ArrayList = ArrayList() +val DEBUG_DIALOGUE = false +val NUMBER_PATTERN1 = Pattern.compile("^(\\d+) \\[label", Pattern.MULTILINE) +val NUMBER_PATTERN2 = Pattern.compile("(\\d+) -> (\\d+)") + +abstract class DialogueBuilderFile : DialogueFile() { + var data: ArrayList = ArrayList() //var stages: ArrayList = ArrayList() - abstract fun create(b: core.game.dialogue.DialogueBuilder) + abstract fun create(b: DialogueBuilder) init { - create(core.game.dialogue.DialogueBuilder(this)) + create(DialogueBuilder(this)) + if(DEBUG_DIALOGUE) { + var graphSb = StringBuilder("digraph {\n") + var nodeIndex = 0 + for((i, clause) in data.iterator().withIndex()) { + var clauseSb = StringBuilder() + for(j in 0 until clause.nodes.size) { + if(!(clause.nodes[j] is OptionsDispatchNode)) { + clauseSb.append("${j} ${clause.nodes[j].toString().replace("@indexPlus", "${j+1}").replace("@index", "${j}")}\n") + } + } + graphSb.append("subgraph cluster_${i} {\n") + var tmpSb = StringBuilder() + var m = NUMBER_PATTERN1.matcher(clauseSb) + while(m.find()) { + var x = Integer.valueOf(m.group(1)) + m.appendReplacement(tmpSb, "${x+nodeIndex} [label") + } + m.appendTail(tmpSb) + m = NUMBER_PATTERN2.matcher(tmpSb) + while(m.find()) { + var x = Integer.valueOf(m.group(1)) + var y = Integer.valueOf(m.group(2)) + m.appendReplacement(graphSb, "${x+nodeIndex} -> ${y+nodeIndex}") + } + m.appendTail(graphSb) + graphSb.append("}\n") + nodeIndex += clause.nodes.size + } + graphSb.append("}\n") + System.out.println(graphSb.toString()) + } } override fun handle(componentID: Int, buttonID: Int) { for((i, clause) in data.iterator().withIndex()) { @@ -23,46 +62,140 @@ abstract class DialogueBuilderFile : core.game.dialogue.DialogueFile() { } interface DialogueNode { - fun handle(df: core.game.dialogue.DialogueFile, componentID: Int, buttonID: Int, stage: Int): Int + fun handle(df: DialogueFile, componentID: Int, buttonID: Int, stage: Int): Int } -class NpcLNode(val value: String): core.game.dialogue.DialogueNode { - override fun handle(df: core.game.dialogue.DialogueFile, componentID: Int, buttonID: Int, stage: Int): Int { - df.npcl(value) +class NpcLNode(val expression: FacialExpression, val value: String): DialogueNode { + override fun toString(): String { + return "[label=\"npcl(FacialExpression.${expression.name}, \\\"${value}\\\")\"]; @index -> @indexPlus;" + } + override fun handle(df: DialogueFile, componentID: Int, buttonID: Int, stage: Int): Int { + df.npcl(expression, value) return stage + 1 } } -class NpcNode(val values: Array): core.game.dialogue.DialogueNode { - override fun handle(df: core.game.dialogue.DialogueFile, componentID: Int, buttonID: Int, stage: Int): Int { - df.npc(*values) +class NpcNode(val expression: FacialExpression, val values: Array): DialogueNode { + override fun toString(): String { + return "[label=\"npc(FacialExpression.${expression.name}, \\\"${values.contentDeepToString()}\\\")\"]; @index -> @indexPlus;" + } + override fun handle(df: DialogueFile, componentID: Int, buttonID: Int, stage: Int): Int { + df.npc(expression, *values) return stage + 1 } } -class PlayerLNode(val value: String): core.game.dialogue.DialogueNode { - override fun handle(df: core.game.dialogue.DialogueFile, componentID: Int, buttonID: Int, stage: Int): Int { - df.playerl(value) +class ItemNode(val item: Int, val values: Array): DialogueNode { + override fun toString(): String { + return "[label=\"item(${item}, \\\"${values.contentDeepToString()}\\\")\"]; @index -> @indexPlus;" + } + override fun handle(df: DialogueFile, componentID: Int, buttonID: Int, stage: Int): Int { + df.interpreter!!.sendItemMessage(item, *values) return stage + 1 } } -class ClosureNode(val f: (Player) -> Int): core.game.dialogue.DialogueNode { - override fun handle(df: core.game.dialogue.DialogueFile, componentID: Int, buttonID: Int, stage: Int): Int { - return f(df.player!!) +class PlayerLNode(val expression: FacialExpression, val value: String): DialogueNode { + override fun toString(): String { + return "[label=\"playerl(FacialExpression.${expression.name}, \\\"${value}\\\")\"]; @index -> @indexPlus;" + } + override fun handle(df: DialogueFile, componentID: Int, buttonID: Int, stage: Int): Int { + df.playerl(expression, value) + return stage + 1 + } +} +class PlayerNode(val expression: FacialExpression, val values: Array): DialogueNode { + override fun toString(): String { + return "[label=\"player(FacialExpression.${expression.name}, \\\"${values.contentDeepToString()}\\\")\"]; @index -> @indexPlus;" + } + override fun handle(df: DialogueFile, componentID: Int, buttonID: Int, stage: Int): Int { + df.player(expression, *values) + return stage + 1 + } +} +class BetweenStageNode(val f: (DialogueFile, Player, Int, Int) -> Unit): DialogueNode { + override fun toString(): String { + return "[label=\"betweenstage(${(f as java.lang.Object).getClass().getName()})\"]; @index -> @indexPlus" + } + override fun handle(df: DialogueFile, componentID: Int, buttonID: Int, stage: Int): Int { + df.stage = stage + 1 // Simulates moving to this stage. + f(df, df.player!!, componentID, buttonID) // Calls callback function. + df.handle(componentID, buttonID) // Handles the current stage + return stage + 2 // Skips over the current stage. + } +} +class ManualStageNode(val f: (DialogueFile, Player, Int, Int) -> Unit): DialogueNode { + override fun toString(): String { + return "[label=\"manualstage(${(f as java.lang.Object).getClass().getName()})\"]; @index -> @indexPlus" + } + override fun handle(df: DialogueFile, componentID: Int, buttonID: Int, stage: Int): Int { + f(df, df.player!!, componentID, buttonID) + return stage + 1 + } +} +class ManualStageWithGotoNode(val f: (DialogueFile, Player, Int, Int, Int) -> Int, g: (Int) -> Int): DialogueNode { + override fun toString(): String { + return "[label=\"manualstagewithgoto(${(f as java.lang.Object).getClass().getName()})\"]; @index -> @indexPlus" + } + override fun handle(df: DialogueFile, componentID: Int, buttonID: Int, stage: Int): Int { + return f(df, df.player!!, componentID, buttonID, stage) + } +} + +class PlaceholderNode(val dbf: DialogueBuilderFile, val clauseIndex: Int, var targetStage: Int): DialogueNode { + override fun toString(): String { + return "[label=\"placeholder(${targetStage})\"]; @index -> ${targetStage}" + } + override fun handle(df: DialogueFile, componentID: Int, buttonID: Int, stage: Int): Int { + df.stage = targetStage + df.handle(componentID, buttonID) + return df.stage + } + + fun builder(): DialogueBuilder { + targetStage = dbf.data[clauseIndex].nodes.size + return DialogueBuilder(dbf, clauseIndex) + } +} + +class GotoNode(val node: PlaceholderNode): DialogueNode { + override fun toString(): String { + return "[label=\"goto(${node.targetStage})\"]; @index -> ${node.targetStage}" + } + override fun handle(df: DialogueFile, componentID: Int, buttonID: Int, stage: Int): Int { + df.stage = node.targetStage + df.handle(componentID, buttonID) + return df.stage } } class OptionEntry(val text: String, val nextStage: Int, val predicate: (Player) -> Boolean = { _ -> true }) {} +class OptionsData(val header: String, val entries: ArrayList, var attr: String? = null) {} -class OptionsNode(var options: ArrayList): core.game.dialogue.DialogueNode { - fun optionNames(player: Player): Array { - return options.asSequence().filter({ it.predicate(player) }).map({ it.text }).toList().toTypedArray() +class OptionsNode(var options: OptionsData): DialogueNode { + override fun toString(): String { + var ret = "[label=\"options(\\\"${options.header}\\\")\"]; " + for(entry in options.entries) { + ret += "@index -> ${entry.nextStage} [label=\"\\\"${entry.text}\\\"\"]; " + } + return ret } - override fun handle(df: core.game.dialogue.DialogueFile, componentID: Int, buttonID: Int, stage: Int): Int { + fun optionNames(player: Player): Array { + return options.entries.asSequence().filter({ it.predicate(player) }).map({ it.text }).toList().toTypedArray() + } + override fun handle(df: DialogueFile, componentID: Int, buttonID: Int, stage: Int): Int { val tmp: Array = optionNames(df.player!!) - if(tmp.size > 1) { - df.options(*tmp) + if(DEBUG_DIALOGUE) { + System.out.println("OptionsNode: ${tmp.size}") + for(i in 0 until tmp.size) { + System.out.println("tmp[${i}]: ${tmp[i]}") + } + } + if(tmp.size > 5) { + System.out.printf("Only a maximum of 5 options are supported by the interface") + return END_DIALOGUE + } else if(tmp.size > 1) { + df.interpreter!!.sendOptions(options.header, *tmp) return stage + 1 } else if(tmp.size == 1) { - val tmp: List = options.asSequence().filter({ it.predicate(df.player!!) }).toList() + val tmp: List = options.entries.asSequence().filter({ it.predicate(df.player!!) }).toList() df.stage = tmp[0].nextStage df.handle(componentID, 0) return df.stage @@ -71,17 +204,26 @@ class OptionsNode(var options: ArrayList): core. } } } -class OptionsDispatchNode(var options: ArrayList): core.game.dialogue.DialogueNode { - override fun handle(df: core.game.dialogue.DialogueFile, componentID: Int, buttonID: Int, stage: Int): Int { - val tmp: List = options.asSequence().filter({ it.predicate(df.player!!) }).toList() +class OptionsDispatchNode(var options: OptionsData): DialogueNode { + override fun toString(): String { + return "[label=\"OptionsDispatchNode\"]" + } + override fun handle(df: DialogueFile, componentID: Int, buttonID: Int, stage: Int): Int { + val tmp: List = options.entries.asSequence().filter({ it.predicate(df.player!!) }).toList() + if(DEBUG_DIALOGUE) { + System.out.println("OptionsDispatchNode: ${tmp.size}") + } + if(options.attr != null) { + df.player!!.setAttribute(options.attr, buttonID-1) + } df.stage = tmp[buttonID-1].nextStage df.handle(componentID, 0) return df.stage } } -class DialogueClause(val predicate: (player: Player) -> Boolean, val nodes: ArrayList) { - fun handle(df: core.game.dialogue.DialogueFile, componentID: Int, buttonID: Int, stage: Int): Int { +class DialogueClause(val predicate: (player: Player) -> Boolean, val nodes: ArrayList) { + fun handle(df: DialogueFile, componentID: Int, buttonID: Int, stage: Int): Int { if(stage < nodes.size) { return nodes[stage].handle(df, componentID, buttonID, stage) } else { @@ -90,59 +232,197 @@ class DialogueClause(val predicate: (player: Player) -> Boolean, val nodes: Arra } } -class DialogueOptionsBuilder(var target: core.game.dialogue.DialogueBuilderFile, val clauseIndex: Int, var options: ArrayList) { - fun option(value: String): core.game.dialogue.DialogueBuilder { - options.add(core.game.dialogue.OptionEntry(value, target.data[clauseIndex].nodes.size)) - return core.game.dialogue.DialogueBuilder(target, clauseIndex) +class DialogueOptionsBuilder(var target: DialogueBuilderFile, val clauseIndex: Int, var options: OptionsData) { + fun option(value: String): DialogueBuilder { + options.entries.add(OptionEntry(value, target.data[clauseIndex].nodes.size)) + return DialogueBuilder(target, clauseIndex) } - fun optionIf(value: String, predicate: (Player) -> Boolean): core.game.dialogue.DialogueBuilder { - options.add(core.game.dialogue.OptionEntry(value, target.data[clauseIndex].nodes.size, predicate)) - return core.game.dialogue.DialogueBuilder(target, clauseIndex) + fun optionIf(value: String, predicate: (Player) -> Boolean): DialogueBuilder { + options.entries.add(OptionEntry(value, target.data[clauseIndex].nodes.size, predicate)) + return DialogueBuilder(target, clauseIndex) + } + + fun option_playerl(value: String): DialogueBuilder { + return option(value).playerl(value) + } + + fun recordAttribute(name: String): DialogueOptionsBuilder { + options.attr = name + return this } } -class DialogueBuilder(var target: core.game.dialogue.DialogueBuilderFile, var clauseIndex: Int = -1) { - fun onPredicate(predicate: (player: Player) -> Boolean): core.game.dialogue.DialogueBuilder { - target.data.add(core.game.dialogue.DialogueClause(predicate, ArrayList())) +class BranchesData(val branches: HashMap, val f: (Player) -> Int) {} +class BranchNode(val branches: BranchesData): DialogueNode { + override fun toString(): String { + var ret = "[label=\"branch(\\\"${(branches.f as Object).getClass().getName()}\\\")\"]; " + for(entry in branches.branches) { + ret += "@index -> ${entry.value} [label=\"\\\"${entry.key}\\\"\"]; " + } + return ret + } + override fun handle(df: DialogueFile, componentID: Int, buttonID: Int, stage: Int): Int { + df.stage = branches.branches[branches.f(df.player!!)] ?: END_DIALOGUE + df.handle(componentID, buttonID) + return df.stage + } +} +class DialogueBranchBuilder(var target: DialogueBuilderFile, val clauseIndex: Int, var branches: BranchesData) { + fun onValue(value: Boolean): DialogueBuilder { + return onValue(if(value) { 1 } else { 0 }) + } + fun onValue(value: Int): DialogueBuilder { + val index = target.data[clauseIndex].nodes.size + branches.branches.put(value, index) + return DialogueBuilder(target, clauseIndex) + } +} + +class EndWithNode(val f: (DialogueFile, Player) -> Unit): DialogueNode { + override fun toString(): String { + return "[label=\"endWith(${(f as java.lang.Object).getClass().getName()})\"]"; + } + override fun handle(df: DialogueFile, componentID: Int, buttonID: Int, stage: Int): Int { + f(df, df.player!!) + return END_DIALOGUE + } +} + +class DialogueBuilder(var target: DialogueBuilderFile, var clauseIndex: Int = -1) { + companion object DialogueBuilderStatic { + @JvmStatic + fun endWithNoop(df: DialogueFile, player: Player) { + } + } + + /** + * 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 return this } - fun onQuestStages(name: String, vararg stages: Int): core.game.dialogue.DialogueBuilder { + fun defaultDialogue(): DialogueBuilder { + return onPredicate({ _ -> return@onPredicate true}) + } + fun onQuestStages(name: Quests, vararg stages: Int): DialogueBuilder { return onPredicate() { player -> val questStage = player.questRepository.getStage(name) return@onPredicate stages.contains(questStage) } } - fun playerl(value: String): core.game.dialogue.DialogueBuilder { - target.data[clauseIndex].nodes.add(core.game.dialogue.PlayerLNode(value)) + fun playerl(value: String): DialogueBuilder { + target.data[clauseIndex].nodes.add(PlayerLNode(FacialExpression.NEUTRAL, value)) return this } - fun npcl(value: String): core.game.dialogue.DialogueBuilder { - target.data[clauseIndex].nodes.add(core.game.dialogue.NpcLNode(value)) + fun playerl(expression: FacialExpression, value: String): DialogueBuilder { + target.data[clauseIndex].nodes.add(PlayerLNode(expression, value)) return this } - fun npc(vararg values: String): core.game.dialogue.DialogueBuilder { - target.data[clauseIndex].nodes.add(core.game.dialogue.NpcNode(values as Array)) + fun player(vararg values: String): DialogueBuilder { + target.data[clauseIndex].nodes.add(PlayerNode(FacialExpression.NEUTRAL, values as Array)) return this } - fun endWith(f: (Player) -> Unit) { - target.data[clauseIndex].nodes.add(core.game.dialogue.ClosureNode({ player -> - f(player) - return@ClosureNode END_DIALOGUE - })) + fun player(expression: FacialExpression, vararg values: String): DialogueBuilder { + target.data[clauseIndex].nodes.add(PlayerNode(expression, values as Array)) + return this + } + fun npcl(value: String): DialogueBuilder { + target.data[clauseIndex].nodes.add(NpcLNode(FacialExpression.NEUTRAL, value)) + return this + } + fun npcl(expression: FacialExpression, value: String): DialogueBuilder { + target.data[clauseIndex].nodes.add(NpcLNode(expression, value)) + return this + } + fun npc(vararg values: String): DialogueBuilder { + target.data[clauseIndex].nodes.add(NpcNode(FacialExpression.NEUTRAL, values as Array)) + return this + } + fun npc(expression: FacialExpression, vararg values: String): DialogueBuilder { + target.data[clauseIndex].nodes.add(NpcNode(expression, values as Array)) + return this + } + fun item(item: Int, vararg values: String): DialogueBuilder { + target.data[clauseIndex].nodes.add(ItemNode(item, values as Array)) + return this + } + fun iteml(item: Int, value: String): DialogueBuilder { + return item(item, *splitLines(value)) + } + fun line(vararg values: String): DialogueBuilder { + return manualStage() { df, player, _, _ -> + df.interpreter!!.sendDialogue(*(values as Array)) + } + } + fun linel(value: String): DialogueBuilder { + return manualStage() { df, player, _, _ -> + df.interpreter!!.sendDialogue(*splitLines(value)) + } + } + fun endWith(f: (DialogueFile, Player) -> Unit) { + target.data[clauseIndex].nodes.add(EndWithNode(f)) } fun end() { - target.data[clauseIndex].nodes.add(core.game.dialogue.ClosureNode({ _ -> - return@ClosureNode END_DIALOGUE - })) + target.data[clauseIndex].nodes.add(EndWithNode(::endWithNoop)) } - fun options(): core.game.dialogue.DialogueOptionsBuilder { - var options: ArrayList = ArrayList() - val node = core.game.dialogue.OptionsNode(options) - val dispatchNode = core.game.dialogue.OptionsDispatchNode(options) + /** Runs block without the need for a DialogueFile df.something call. */ + fun betweenStage(f: (DialogueFile, Player, Int, Int) -> Unit): DialogueBuilder { + target.data[clauseIndex].nodes.add(BetweenStageNode(f)) + return this + } + fun manualStage(f: (DialogueFile, Player, Int, Int) -> Unit): DialogueBuilder { + target.data[clauseIndex].nodes.add(ManualStageNode(f)) + return this + } + fun manualStageWithGoto(f: (DialogueFile, Player, Int, Int, Int) -> Int, g: (Int) -> Int): DialogueBuilder { + target.data[clauseIndex].nodes.add(ManualStageWithGotoNode(f, g)) + return this + } + fun options(): DialogueOptionsBuilder { + return options("Select an Option") + } + fun options(header: String): DialogueOptionsBuilder { + val options: ArrayList = ArrayList() + val optionsData = OptionsData(header, options) + val node = OptionsNode(optionsData) + val dispatchNode = OptionsDispatchNode(optionsData) target.data[clauseIndex].nodes.add(node) target.data[clauseIndex].nodes.add(dispatchNode) - return core.game.dialogue.DialogueOptionsBuilder(target, clauseIndex, options) + return DialogueOptionsBuilder(target, clauseIndex, optionsData) + } + + fun placeholder(): PlaceholderNode { + val node = PlaceholderNode(target, clauseIndex, END_DIALOGUE) + target.data[clauseIndex].nodes.add(node) + return node + } + + fun goto(node: PlaceholderNode): DialogueBuilder { + target.data[clauseIndex].nodes.add(GotoNode(node)) + return this + } + + fun branch(f: (Player) -> Int): DialogueBranchBuilder { + var branches = BranchesData(HashMap(), f) + target.data[clauseIndex].nodes.add(BranchNode(branches)) + return DialogueBranchBuilder(target, clauseIndex, branches) + } + + fun branchStages(f: (Player) -> Int): DialogueBranchBuilder { + var branches = BranchesData(HashMap(), f) + target.data[clauseIndex].nodes.add(BranchNode(branches)) + return DialogueBranchBuilder(target, clauseIndex, branches) + } + + fun branchBoolAttribute(attrName: String, defaultVal: Boolean): DialogueBranchBuilder { + return branch({ player -> return@branch if(player.getAttribute(attrName, defaultVal)) { 1 } else { 0 } }) } } diff --git a/Server/src/main/core/game/dialogue/DialogueFile.kt b/Server/src/main/core/game/dialogue/DialogueFile.kt index 7fcf7ccd2..226c962f2 100644 --- a/Server/src/main/core/game/dialogue/DialogueFile.kt +++ b/Server/src/main/core/game/dialogue/DialogueFile.kt @@ -1,6 +1,7 @@ package core.game.dialogue import core.api.splitLines +import core.cache.def.impl.NPCDefinition import core.game.component.Component import core.game.node.entity.Entity import core.game.node.entity.npc.NPC @@ -10,16 +11,17 @@ import core.tools.START_DIALOGUE abstract class DialogueFile { var player: Player? = null var npc: NPC? = null - var interpreter: core.game.dialogue.DialogueInterpreter? = null + var interpreter: DialogueInterpreter? = null open var stage = START_DIALOGUE - var dialoguePlugin: core.game.dialogue.DialoguePlugin? = null + var dialoguePlugin: DialoguePlugin? = null abstract fun handle(componentID: Int, buttonID: Int) - fun load(player: Player, npc: NPC?, interpreter: core.game.dialogue.DialogueInterpreter): DialogueFile { + fun load(player: Player, npc: NPC?, interpreter: DialogueInterpreter): DialogueFile { this.player = player this.npc = npc this.interpreter = interpreter + interpreter.activeTopics.clear() return this } @@ -28,7 +30,7 @@ abstract class DialogueFile { if (npc != null) { return interpreter!!.sendDialogues( npc, - if (npc!!.id > 8591) core.game.dialogue.FacialExpression.OLD_NORMAL else core.game.dialogue.FacialExpression.FRIENDLY, + if (npc!!.id > 8591) FacialExpression.OLD_NORMAL else FacialExpression.FRIENDLY, *messages ) } @@ -36,20 +38,31 @@ abstract class DialogueFile { } open fun npc(id: Int, vararg messages: String?): Component? { - return interpreter!!.sendDialogues(id, core.game.dialogue.FacialExpression.FRIENDLY, *messages) + return interpreter!!.sendDialogues(id, FacialExpression.FRIENDLY, *messages) } - open fun npc(expression: core.game.dialogue.FacialExpression?, vararg messages: String?): Component? { + open fun npc(expression: FacialExpression?, vararg messages: String?): Component? { return if (npc == null) { interpreter!!.sendDialogues(0, expression, *messages) } else interpreter!!.sendDialogues(npc, expression, *messages) } + open fun npc(id: Int, expression: FacialExpression?, vararg messages: String?): Component? { + val chatBoxComponent = interpreter!!.sendDialogues(id, expression, *messages) + return chatBoxComponent + } + + open fun npc(id: Int, title: String, expression: FacialExpression?, vararg messages: String?): Component? { + val chatBoxComponent = interpreter!!.sendDialogues(id, expression, *messages) + player!!.packetDispatch.sendString(title, chatBoxComponent.id, 3) + return chatBoxComponent + } + open fun player(vararg messages: String?): Component? { return interpreter!!.sendDialogues(player, null, *messages) } - open fun player(expression: core.game.dialogue.FacialExpression?, vararg messages: String?): Component? { + open fun player(expression: FacialExpression?, vararg messages: String?): Component? { return interpreter!!.sendDialogues(player, expression, *messages) } @@ -58,10 +71,18 @@ abstract class DialogueFile { * @param expr the FacialExpression to use, located in the FacialExpression enum. * @param msg the message for the NPC to say */ - open fun npcl(expr: core.game.dialogue.FacialExpression?, msg: String?): Component? { + open fun npcl(expr: FacialExpression?, msg: String?): Component? { return npc(expr, *splitLines(msg!!)) } + open fun npcl(id: Int, expr: FacialExpression?, msg: String?): Component? { + return npc(id, expr, *splitLines(msg!!)) + } + + open fun npcl(id: Int, title: String, expr: FacialExpression?, msg: String?): Component? { + return npc(id, title, expr, *splitLines(msg!!)) + } + open fun npcl(msg: String?): Component? { return npc(*splitLines(msg!!)) } @@ -71,7 +92,7 @@ abstract class DialogueFile { * @param expr the FacialExpression to use, located in the FacialExpression enum. * @param msg the message for the player to say */ - open fun playerl(expr: core.game.dialogue.FacialExpression?, msg: String?): Component? { + open fun playerl(expr: FacialExpression?, msg: String?): Component? { return player(expr, *splitLines(msg!!)) } @@ -83,12 +104,12 @@ abstract class DialogueFile { if(interpreter != null) interpreter!!.close() } - open fun sendNormalDialogue(entity: Entity?, expression: core.game.dialogue.FacialExpression?, vararg messages: String?) { + open fun sendNormalDialogue(entity: Entity?, expression: FacialExpression?, vararg messages: String?) { 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) } /** @@ -130,13 +151,16 @@ abstract class DialogueFile { player?.dialogueInterpreter?.sendDialogue(*messages) } - fun showTopics(vararg topics: Topic<*>) { + 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) validTopics.add(topic.text) } - if (validTopics.size == 1) { + if(validTopics.size == 0) { + return true + } + else if (validTopics.size == 1) { val topic = topics[0] if(topic.toStage is DialogueFile) { val topicFile = topic.toStage as DialogueFile @@ -146,7 +170,10 @@ abstract class DialogueFile { } player(topic.text) interpreter!!.activeTopics.clear() + return false + } + else { options(*validTopics.toTypedArray(), title = title) + return false } - else options(*validTopics.toTypedArray()) } } diff --git a/Server/src/main/core/game/dialogue/DialogueInterpreter.java b/Server/src/main/core/game/dialogue/DialogueInterpreter.java index a78a4e93d..b37a610db 100644 --- a/Server/src/main/core/game/dialogue/DialogueInterpreter.java +++ b/Server/src/main/core/game/dialogue/DialogueInterpreter.java @@ -10,9 +10,12 @@ 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; import core.net.packet.out.ContainerPacket; +import core.net.packet.out.RepositionChild; import core.plugin.PluginManifest; import core.plugin.PluginType; import core.game.system.config.ItemConfigParser; @@ -21,6 +24,8 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import static core.tools.DialogueConstKt.END_DIALOGUE; @@ -142,8 +147,9 @@ public final class DialogueInterpreter { close(); return; } + DialogueFile file = dialogue.file; - if (!activeTopics.isEmpty()) { + if (!activeTopics.isEmpty() && buttonId >= 2) { Topic topic = activeTopics.get(buttonId - 2); if (!topic.getSkipPlayer()) @@ -260,8 +266,6 @@ public final class DialogueInterpreter { * @return The component. */ public Component sendDestroyItem(int id, String message) { - player.getInterfaceManager().openChatbox(94); - PacketRepository.send(ContainerPacket.class, new ContainerContext(player, 94, 93, 93, new Item[] { new Item(id) }, 1, false)); String text = ItemDefinition.forId(id).getConfiguration(ItemConfigParser.DESTROY_MESSAGE, "Are you sure you want to destroy this object?"); if (text.length() > 200) { String[] words = text.split(" "); @@ -276,11 +280,11 @@ public final class DialogueInterpreter { } text = sb.toString(); } - player.getPacketDispatch().sendString("Are you sure you want to destroy this object?", 94, 2); - player.getPacketDispatch().sendString("Yes.", 94, 3); - player.getPacketDispatch().sendString("No.", 94, 4); player.getPacketDispatch().sendString(text, 94, 7); player.getPacketDispatch().sendString(ItemDefinition.forId(id).getName(), 94, 8); + player.getPacketDispatch().sendItemOnInterface(id, 1, 94, 9); + + player.getInterfaceManager().openChatbox(94); return player.getInterfaceManager().getChatbox(); } @@ -322,13 +326,61 @@ public final class DialogueInterpreter { * @param itemId The item id. */ public Component sendItemMessage(int itemId, String... messages) { - player.getInterfaceManager().openChatbox(131); - String message = messages[0]; - for (int i = 1; i < messages.length; i++) { - message += "
" + messages[i]; + 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; + } } - player.getPacketDispatch().sendString(message, 131, 1); - player.getPacketDispatch().sendItemOnInterface(itemId, 1, 131, 2); return player.getInterfaceManager().getChatbox(); } @@ -336,14 +388,7 @@ public final class DialogueInterpreter { * Send a message with an item next to it. */ public Component sendItemMessage(final Item item, String... messages) { - player.getInterfaceManager().openChatbox(131); - String message = messages[0]; - for (int i = 1; i < messages.length; i++) { - message += "
" + messages[i]; - } - player.getPacketDispatch().sendString(message, 131, 1); - player.getPacketDispatch().sendItemOnInterface(item.getId(), item.getAmount(), 131, 2); - return player.getInterfaceManager().getChatbox(); + return sendItemMessage(item.getId(), messages); } /** @@ -389,7 +434,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); } /** @@ -397,12 +442,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); } /** @@ -413,34 +457,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); } /** @@ -451,9 +479,22 @@ 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; + while(m.find()) { + m.appendReplacement(sb, m.group(index)); + } + m.appendTail(sb); + return sb.toString(); + } /** * Send dialogues based on the amount of specified messages. * @param npcId The npc id. @@ -462,15 +503,30 @@ 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().sendAnimationInterface(expression, interfaceId, 2); + player.getPacketDispatch().sendItemOnInterface(-1, 1, interfaceId, 1); if (npc) { player.getPacketDispatch().sendNpcOnInterface(npcId, interfaceId, 2); player.getPacketDispatch().sendString(NPCDefinition.forId(npcId).getName(), interfaceId, 3); @@ -479,7 +535,7 @@ public final class DialogueInterpreter { player.getPacketDispatch().sendString(player.getUsername(), interfaceId, 3); } for (int i = 0; i < messages.length; i++) { - player.getPacketDispatch().sendString(messages[i].toString().replace("@name", player.getUsername()), 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); @@ -572,4 +628,4 @@ public final class DialogueInterpreter { public List getActions() { return actions; } -} \ No newline at end of file +} 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..6a48b86ae --- /dev/null +++ b/Server/src/main/core/game/dialogue/DialogueLabeller.kt @@ -0,0 +1,360 @@ +package core.game.dialogue + +import core.api.InputType +import core.api.face +import core.api.openDialogue +import core.api.splitLines +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 function to create an individual stage for each of the dialogue stages. */ + private fun assignIndividualStage(callback: () -> Unit) { + if (startingStage == null) { startingStage = 0 } + if (stage == dialogueCounter && jumpTo == null) { // Run this stage when the stage equals to the dialogueCounter of this dialogue + callback() // CALLBACK FUNCTION + 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(callback: (player: Player, npc: NPC) -> Unit) { + assignIndividualStage { callback(player!!, npc!!) } + } + + /** Dialogue player/playerl. Shows player chathead with text. **/ + fun player(chatAnim: ChatAnim = ChatAnim.NEUTRAL, vararg messages: String) { + assignIndividualStage { interpreter!!.sendDialogues(player, chatAnim, *formatMessages(messages)) } + } + /** 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) { + assignIndividualStage { interpreter!!.sendDialogues(npc, chatAnim, *formatMessages(messages)) } + } + /** Dialogue npc/npcl. Shows npcId chathead with text. **/ + fun npc(chatAnim: ChatAnim = ChatAnim.NEUTRAL, npcId: Int = npc!!.id, vararg messages: String) { + assignIndividualStage { interpreter!!.sendDialogues(NPC(npcId), chatAnim, *formatMessages(messages)) } + } + /** Dialogue npc/npcl. Shows npcId chathead with text. **/ + fun npc(npcId: Int = npc!!.id, vararg messages: String) { + assignIndividualStage { interpreter!!.sendDialogues(NPC(npcId), ChatAnim.NEUTRAL, *formatMessages(messages)) } + } + /** 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 = "") { + assignIndividualStage { interpreter!!.sendItemMessage(item, *formatMessages(messages, message)) } + } + @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 = "") { + assignIndividualStage { interpreter!!.sendDoubleItemMessage(item, item2, formatMessages(messages, message).joinToString(" ")) } + } + + /** Dialogue line/linel. Simply shows text. **/ + fun line(vararg messages: String) { + assignIndividualStage { interpreter!!.sendDialogue(*messages) } + } + @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") { + // 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 + assignIndividualStage { interpreter!!.sendOptions(title, *filteredOptions.map{ it.option }.toTypedArray()) } + // Stage Part 2: Show spoken text. + var opt = if (buttonID != null && buttonID in 1..filteredOptions.size) { filteredOptions[buttonID!! - 1] } else { null } + assignIndividualStage { + if (opt?.skipPlayer == true) { + jumpTo = stage + 1 + } else { + interpreter!!.sendDialogues(player, opt?.expression ?: ChatAnim.NEUTRAL, *(splitLines(opt?.spokenText ?: " "))) + } + optButton = buttonID // transfer the buttonID to a temp memory for the next stage + } + // 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) + } + } + /** 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) } + + /** 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) { + assignIndividualStage { core.api.openDialogue(player, dialogue, *args) } + } + + /** 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 (!stageHit) { end() } // If a dialogue stage is not hit, end the dialogues. + } +} + +/* +// 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 26fb26129..635b41c2b 100644 --- a/Server/src/main/core/game/dialogue/DialoguePlugin.java +++ b/Server/src/main/core/game/dialogue/DialoguePlugin.java @@ -7,11 +7,13 @@ import core.game.node.entity.player.Player; import core.plugin.Plugin; import core.plugin.PluginManifest; import core.plugin.PluginType; +import core.tools.Log; import core.tools.SystemLogger; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; +import static core.api.ContentAPIKt.log; import static core.api.DialUtilsKt.splitLines; import static core.tools.DialogueConstKt.DIALOGUE_INITIAL_OPTIONS_HANDLE; import static core.tools.DialogueConstKt.START_DIALOGUE; @@ -192,12 +194,13 @@ public abstract class DialoguePlugin implements Plugin { * @return {@code True} if the dialogue plugin succesfully opened. */ public boolean open(Object... args) { + player.getDialogueInterpreter().activeTopics.clear(); if (args.length > 0 && args[0] instanceof NPC) { npc = (NPC)args[0]; } if (npc == null) { - SystemLogger.logWarn(this.getClass(), + log(this.getClass(), Log.WARN, args[0].getClass().getSimpleName() + "Is not assigning an NPC. Whoever did that should fix it." ); @@ -362,7 +365,7 @@ public abstract class DialoguePlugin implements Plugin { return player(expr, splitLines(msg, 54)); } - public void showTopics(Topic... topics) { + public boolean showTopics(Topic... topics) { ArrayList validTopics = new ArrayList<>(); for(Topic topic : topics) { @@ -370,21 +373,25 @@ public abstract class DialoguePlugin implements Plugin { interpreter.activeTopics.add(topic); validTopics.add(topic.getText()); } - - if (validTopics.size() == 1) { - Topic topic = interpreter.activeTopics.get(0); - - if(topic.getToStage() instanceof DialogueFile) { - DialogueFile topicFile = (DialogueFile) topic.getToStage(); - interpreter.getDialogue().loadFile(topicFile); - } else if(topic.getToStage() instanceof Integer) { - stage = (Integer) topic.getToStage(); - } - - player(topic.getText()); - interpreter.activeTopics.clear(); + if(validTopics.size() == 0) { + return true; + } + else if (validTopics.size() == 1) { + Topic topic = interpreter.activeTopics.get(0); + if(topic.getToStage() instanceof DialogueFile) { + DialogueFile topicFile = (DialogueFile) topic.getToStage(); + interpreter.getDialogue().loadFile(topicFile); + } + else if(topic.getToStage() instanceof Integer) { + stage = (Integer) topic.getToStage(); + } + player(topic.getText()); + interpreter.activeTopics.clear(); + return false; + } + else { + options(validTopics.toArray(new String[0])); + return false; } - - options(validTopics.toArray(new String[0])); } } diff --git a/Server/src/main/core/game/dialogue/DialogueTopic.kt b/Server/src/main/core/game/dialogue/DialogueTopic.kt index 5f7cff79c..3ef290a0b 100644 --- a/Server/src/main/core/game/dialogue/DialogueTopic.kt +++ b/Server/src/main/core/game/dialogue/DialogueTopic.kt @@ -4,9 +4,11 @@ package core.game.dialogue * Topic/IfTopic system backported from my personal project * @author Ceikry */ -open class Topic(val expr: core.game.dialogue.FacialExpression, val text: String, val toStage: T, val skipPlayer: Boolean = false) { - constructor(text: String, toStage: T, skipPlayer: Boolean = false) : this(core.game.dialogue.FacialExpression.ASKING, text, toStage, skipPlayer) +open class Topic @JvmOverloads constructor(val expr: FacialExpression, val text: String, val toStage: T, val skipPlayer: Boolean = false) { + @JvmOverloads + constructor(text: String, toStage: T, skipPlayer: Boolean = false) : this(FacialExpression.ASKING, text, toStage, skipPlayer) } -class IfTopic(expr: core.game.dialogue.FacialExpression, text: String, toStage: T, val showCondition: Boolean, skipPlayer: Boolean = false) : Topic(expr, text, toStage, skipPlayer) { +class IfTopic @JvmOverloads constructor(expr: FacialExpression, text: String, toStage: T, val showCondition: Boolean, skipPlayer: Boolean = false) : Topic(expr, text, toStage, skipPlayer) { + @JvmOverloads constructor(text: String, toStage: T, showCondition: Boolean, skipPlayer: Boolean = false) : this(core.game.dialogue.FacialExpression.ASKING, text, toStage, showCondition, skipPlayer) -} \ No newline at end of file +} diff --git a/Server/src/main/core/game/dialogue/EmptyPlugin.kt b/Server/src/main/core/game/dialogue/EmptyPlugin.kt index 572fd63e6..79857654e 100644 --- a/Server/src/main/core/game/dialogue/EmptyPlugin.kt +++ b/Server/src/main/core/game/dialogue/EmptyPlugin.kt @@ -4,8 +4,8 @@ import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player import core.tools.START_DIALOGUE -class EmptyPlugin(player: Player? = null,val file: DialogueFile?) : core.game.dialogue.DialoguePlugin(player) { - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { +class EmptyPlugin(player: Player? = null,val file: DialogueFile?) : DialoguePlugin(player) { + override fun newInstance(player: Player?): DialoguePlugin { return EmptyPlugin(player,null) } 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/SimpleEntityMessage.java b/Server/src/main/core/game/dialogue/SimpleEntityMessage.java deleted file mode 100644 index cfc718e91..000000000 --- a/Server/src/main/core/game/dialogue/SimpleEntityMessage.java +++ /dev/null @@ -1,46 +0,0 @@ -package core.game.dialogue; - -import core.plugin.Initializable; -import core.game.node.entity.player.Player; - -/** - * Handles the SimpleEntityMessage dialogue. - * @author 'Vexia - */ -@Initializable -public class SimpleEntityMessage extends DialoguePlugin { - - public SimpleEntityMessage() { - - } - - public SimpleEntityMessage(Player player) { - super(player); - } - - @Override - public int[] getIds() { - return new int[] { 8000 }; - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - end(); - return true; - } - - @Override - public DialoguePlugin newInstance(Player player) { - - return new SimpleEntityMessage(player); - } - - @Override - public boolean open(Object... args) { - String[] messages = new String[args.length]; - for (int i = 0; i < messages.length; i++) - messages[i] = (String) args[i]; - interpreter.sendDialogues(player, FacialExpression.HALF_GUILTY, messages); - return true; - } -} diff --git a/Server/src/main/core/game/dialogue/SkillDialogueHandler.kt b/Server/src/main/core/game/dialogue/SkillDialogueHandler.kt index e38896310..ada3a4232 100644 --- a/Server/src/main/core/game/dialogue/SkillDialogueHandler.kt +++ b/Server/src/main/core/game/dialogue/SkillDialogueHandler.kt @@ -123,6 +123,42 @@ open class SkillDialogueHandler( } } }, + MAKE_SET_ONE_OPTION(582, 4, 1) { + override fun display(player: Player, handler: SkillDialogueHandler) { + val item = handler.data[0] as Item + + // Send item + item name to interface + player.packetDispatch.sendItemZoomOnInterface(item.id, 160, 582, 2) + player.packetDispatch.sendString("



" + item.name, 582, 5) + + // Re-format this interface because it is not formatted properly for the chat-box + // Swords + PacketRepository.send(RepositionChild::class.java, ChildPositionContext(player, 582, 0, 12, 15)) + PacketRepository.send(RepositionChild::class.java, ChildPositionContext(player, 582, 1, 431, 15)) + // "How many would you like to make?" + PacketRepository.send(RepositionChild::class.java, ChildPositionContext(player, 582, 6, 0, 12)) + // Item displayed + PacketRepository.send(RepositionChild::class.java, ChildPositionContext(player, 582, 2, 207, 23)) + // Right click context menu boxes + PacketRepository.send(RepositionChild::class.java, ChildPositionContext(player, 582, 3, 58, 27)) + PacketRepository.send(RepositionChild::class.java, ChildPositionContext(player, 582, 4, 58, 27)) + PacketRepository.send(RepositionChild::class.java, ChildPositionContext(player, 582, 5, 58, 27)) + } + + override fun getAmount(handler: SkillDialogueHandler, buttonId: Int): Int { + return when(buttonId){ + // "Continue" Option + 5 -> 1 + // "Make 1 set" Option + 4 -> 1 + // "Make 5 sets" Option + 3 -> 5 + // "Make 10 sets" Option + 2 -> 30 + else -> 30 + } + } + }, TWO_OPTION(303, 7, 2) { override fun display(player: Player, handler: SkillDialogueHandler) { var item: Item @@ -146,7 +182,7 @@ open class SkillDialogueHandler( when (buttonId) { 6, 10 -> return 1 5, 9 -> return 5 - 4, 8 -> return 10 + 4, 8 -> return 30 3, 7 -> return -1 } return 1 @@ -175,7 +211,7 @@ open class SkillDialogueHandler( when (buttonId) { 7, 11, 15 -> return 1 6, 10, 14 -> return 5 - 5, 9, 13 -> return 10 + 5, 9, 13 -> return 30 4, 8, 12 -> return -1 } return 1 @@ -205,7 +241,7 @@ open class SkillDialogueHandler( when (buttonId) { 8, 12, 16, 20 -> return 1 7, 11, 15, 19 -> return 5 - 6, 10, 14, 18 -> return 10 + 6, 10, 14, 18 -> return 30 5, 9, 13, 17 -> return -1 } return 1 @@ -242,7 +278,7 @@ open class SkillDialogueHandler( when (buttonId) { 9, 13, 17, 21, 25 -> return 1 8, 12, 16, 20, 24 -> return 5 - 7, 11, 15, 19, 23 -> return 10 + 7, 11, 15, 19, 23 -> return 30 6, 10, 14, 18, 22 -> return -1 } return 1 diff --git a/Server/src/main/core/game/dialogue/book/Book.java b/Server/src/main/core/game/dialogue/book/Book.java deleted file mode 100644 index ce15fd6e6..000000000 --- a/Server/src/main/core/game/dialogue/book/Book.java +++ /dev/null @@ -1,197 +0,0 @@ -package core.game.dialogue.book; - -import core.game.component.Component; -import core.game.dialogue.DialoguePlugin; -import core.game.dialogue.DialoguePlugin; -import core.game.node.entity.player.Player; -import core.plugin.PluginManifest; -import core.plugin.PluginType; - -/** - * Represents a dialogue book. - * @author 'Vexia - * @date 1/1/14 - */ -@PluginManifest(type = PluginType.DIALOGUE) -public abstract class Book extends DialoguePlugin { - - /** - * Represents the component interface of the book. - */ - private static final Component INTERFACE = new Component(49); - - /** - * Represents the red string. - */ - protected static final String RED = ""; - - /** - * Represents the blue string. - */ - protected static final String BLUE = ""; - - /** - * Represents the name of the book. - */ - protected String name; - - /** - * Represents the id of this book. - */ - protected int id; - - /** - * Represents the pages of this book. - */ - protected PageSet[] sets; - - /** - * Represents the index of the page set we're at. - */ - protected int index = -1; - - /** - * Constructs a new {@code Book} {@code Object}. - * @param name the name. - * @param id the id. - */ - public Book(final Player player, final String name, final int id, final PageSet... sets) { - this.player = player; - this.name = name; - this.id = id; - this.sets = sets; - } - - /** - * Constructs a new {@code Book} {@code Object}. - */ - public Book() { - /** - * empty. - */ - } - - @Override - public boolean open(Object... args) { - return open(player); - } - - @Override - public boolean handle(int interfaceId, int buttonId) { - switch (buttonId) { - case 112: - player.getInterfaceManager().close(); - break; - case 66: - case 52: - next(); - break; - case 64: - case 50: - previous(); - break; - } - return true; - } - - @Override - public Object fireEvent(String identifier, Object... args) { - return this; - } - - /** - * Method used to open a book. - * @return True if succesfully opened. - */ - public boolean open(final Player player) { - next(); - return true; - } - - /** - * Method used to write the next dialogue. - */ - public void next() { - player.lock(); - index++; - if (index > sets.length - 1) { - player.unlock(); - player.getInterfaceManager().close(); - return; - } - final Page[] set = sets[index].getPages(); - display(set); - player.unlock(); - } - - /** - * Method used to write the previous dialogue. - */ - public void previous() { - player.lock(); - index--; - if (index < 0) { - index = 0; - } - final Page[] set = sets[index].getPages(); - display(set); - player.unlock(); - } - - /** - * Method used to display a set of pages. - * @param set the set. - */ - public void display(Page[] set) { - // TODO: It seems like different subclasses instantiate this by - // copy-paste, this should probably be made final, and should switch between a few - // implementations by component id - } - - /** - * Method used when the book is finished. - */ - public void finish() { - } - - /** - * Gets the name. - * @return The name. - */ - public String getName() { - return name; - } - - /** - * Gets the id. - * @return The id. - */ - public int getId() { - return id; - } - - /** - * Gets the index. - * @return The index. - */ - public int getIndex() { - return index; - } - - /** - * Sets the index. - * @param index The index to set. - */ - public void setIndex(int index) { - this.index = index; - } - - /** - * Gets the interface component. - * @return the component. - */ - public Component getInterface() { - return INTERFACE; - } - -} diff --git a/Server/src/main/core/game/dialogue/book/BookLine.java b/Server/src/main/core/game/dialogue/book/BookLine.java deleted file mode 100644 index 101446508..000000000 --- a/Server/src/main/core/game/dialogue/book/BookLine.java +++ /dev/null @@ -1,46 +0,0 @@ -package core.game.dialogue.book; - -/** - * Represents a book line. - * @author 'Vexia - * @date 31/12/2013 - */ -public class BookLine { - - /** - * Represents the message to display. - */ - private final String message; - - /** - * Represents the child if of the line. - */ - private final int child; - - /** - * Constructs a new {@code Page} {@code Object}. - * @param message the message. - * @param child the child. - */ - public BookLine(final String message, final int child) { - this.message = message; - this.child = child; - } - - /** - * Gets the message. - * @return The message. - */ - public String getMessage() { - return message; - } - - /** - * Gets the child. - * @return The child. - */ - public int getChild() { - return child; - } - -} diff --git a/Server/src/main/core/game/dialogue/book/Page.java b/Server/src/main/core/game/dialogue/book/Page.java deleted file mode 100644 index e6196c964..000000000 --- a/Server/src/main/core/game/dialogue/book/Page.java +++ /dev/null @@ -1,31 +0,0 @@ -package core.game.dialogue.book; - -/** - * Represents a page on a book. - * @author 'Vexia - * @date 1/1/14 - */ -public class Page { - - /** - * Represents the lines on a page. - */ - private final BookLine[] lines; - - /** - * Constructs a new {@code Page} {@code Object}. - * @param lines the lines. - */ - public Page(BookLine... lines) { - this.lines = lines; - } - - /** - * Gets the lines. - * @return The lines. - */ - public BookLine[] getLines() { - return lines; - } - -} diff --git a/Server/src/main/core/game/dialogue/book/PageSet.java b/Server/src/main/core/game/dialogue/book/PageSet.java deleted file mode 100644 index 5e4793345..000000000 --- a/Server/src/main/core/game/dialogue/book/PageSet.java +++ /dev/null @@ -1,29 +0,0 @@ -package core.game.dialogue.book; - -/** - * Represents a set of pages on a book. - * @author 'Vexia - */ -public class PageSet { - - /** - * Represents the set of pages. - */ - private final Page[] pages; - - /** - * Constructs a new {@code PageSet} {@code Object}. - * @param pages the pages. - */ - public PageSet(final Page... pages) { - this.pages = pages; - } - - /** - * Gets the pages. - * @return The pages. - */ - public Page[] getPages() { - return pages; - } -} diff --git a/Server/src/main/core/game/diary/DiaryEventHookBase.kt b/Server/src/main/core/game/diary/DiaryEventHookBase.kt index 30e08f41a..4fb85b0cd 100644 --- a/Server/src/main/core/game/diary/DiaryEventHookBase.kt +++ b/Server/src/main/core/game/diary/DiaryEventHookBase.kt @@ -51,6 +51,7 @@ abstract class DiaryEventHookBase(private val diaryType: DiaryType) : MapArea, L player.hook(Event.NPCKilled, EventHandler(this, ::onNpcKilled)) player.hook(Event.Teleported, EventHandler(this, ::onTeleported)) player.hook(Event.FireLit, EventHandler(this, ::onFireLit)) + player.hook(Event.LightSourceLit, EventHandler(this, ::onLightSourceLit)) player.hook(Event.Interacted, EventHandler(this, ::onInteracted)) player.hook(Event.ButtonClicked, EventHandler(this, ::onButtonClicked)) player.hook(Event.DialogueOpened, EventHandler(this, ::onDialogueOpened)) @@ -207,6 +208,7 @@ abstract class DiaryEventHookBase(private val diaryType: DiaryType) : MapArea, L protected open fun onNpcKilled(player: Player, event: NPCKillEvent) {} protected open fun onTeleported(player: Player, event: TeleportEvent) {} protected open fun onFireLit(player: Player, event: LitFireEvent) {} + protected open fun onLightSourceLit(player: Player, event: LitLightSourceEvent) {} protected open fun onInteracted(player: Player, event: InteractionEvent) {} protected open fun onButtonClicked(player: Player, event: ButtonClickEvent) {} protected open fun onDialogueOpened(player: Player, event: DialogueOpenEvent) {} diff --git a/Server/src/main/core/game/event/EventHook.kt b/Server/src/main/core/game/event/EventHook.kt index ce77835a2..728fa46b8 100644 --- a/Server/src/main/core/game/event/EventHook.kt +++ b/Server/src/main/core/game/event/EventHook.kt @@ -2,6 +2,6 @@ package core.game.event import core.game.node.entity.Entity -interface EventHook { +interface EventHook { fun process(entity: Entity, event: T) -} \ No newline at end of file +} diff --git a/Server/src/main/core/game/event/Events.kt b/Server/src/main/core/game/event/Events.kt index f399a67ce..900b41629 100644 --- a/Server/src/main/core/game/event/Events.kt +++ b/Server/src/main/core/game/event/Events.kt @@ -7,6 +7,7 @@ import core.game.node.entity.Entity import core.game.node.entity.npc.NPC import core.game.node.entity.player.link.SpellBookManager.SpellBook import core.game.node.entity.player.link.TeleportManager.TeleportType +import core.game.node.entity.player.link.prayer.PrayerType import core.game.node.item.Item import core.game.world.map.Location import content.global.activity.jobs.JobType @@ -15,8 +16,10 @@ import content.global.skill.magic.TeleportMethod data class ResourceProducedEvent(val itemId: Int, val amount: Int, val source: Node, val original: Int = -1) : Event data class NPCKillEvent(val npc: NPC) : Event +data class BoneBuryEvent(val boneId: Int) : Event data class TeleportEvent(val type: TeleportType, val method: TeleportMethod, val source: Any, val location: Location) : Event data class LitFireEvent(val logId: Int) : Event +data class LitLightSourceEvent(val litLightSourceId: Int) : Event data class InteractionEvent(val target: Node, val option: String) : Event data class ButtonClickEvent(val iface: Int, val buttonId: Int) : Event data class DialogueOpenEvent(val dialogue: DialoguePlugin) : Event @@ -30,7 +33,7 @@ data class InterfaceOpenEvent(val component: Component) : Event data class InterfaceCloseEvent(val component: Component) : Event data class AttributeSetEvent(val entity: Entity, val attribute: String, val value: Any) : Event data class AttributeRemoveEvent(val entity: Entity, val attribute: String) : Event -data class SpellCastEvent(val spellBook: SpellBook, val spellId: Int) : Event +data class SpellCastEvent(val spellBook: SpellBook, val spellId: Int, val target: Node? = null) : Event data class ItemAlchemizationEvent(val itemId: Int, val isHigh: Boolean) : Event data class ItemEquipEvent(val itemId: Int, val slotId: Int) : Event data class ItemUnequipEvent(val itemId: Int, val slotId: Int) : Event @@ -42,4 +45,6 @@ data class VarbitUpdateEvent(val offset: Int, val value: Int) : Event data class DynamicSkillLevelChangeEvent(val skillId: Int, val oldValue: Int, val newValue: Int): Event data class SummoningPointsRechargeEvent(val obelisk: Node) : Event data class PrayerPointsRechargeEvent(val altar: Node) : Event -data class XPGainEvent(val skillId: Int, val amount: Double) : Event \ No newline at end of file +data class XPGainEvent(val skillId: Int, val amount: Double) : Event +data class PrayerActivatedEvent (val type: PrayerType) : Event +data class PrayerDeactivatedEvent (val type: PrayerType) : Event diff --git a/Server/src/main/core/game/ge/GEDB.kt b/Server/src/main/core/game/ge/GEDB.kt index 6db3ca4dc..3fedd1e2d 100644 --- a/Server/src/main/core/game/ge/GEDB.kt +++ b/Server/src/main/core/game/ge/GEDB.kt @@ -1,71 +1,33 @@ package core.game.ge +import core.ServerConstants import core.cache.def.impl.ItemDefinition +import core.integrations.sqlite.SQLiteProvider import org.json.simple.JSONArray import org.json.simple.JSONObject -import org.json.simple.parser.JSONParser -import org.sqlite.SQLiteDataSource -import core.ServerConstants -import java.io.File -import java.io.FileReader import java.sql.Connection -import java.util.concurrent.TimeUnit -import java.util.concurrent.locks.ReentrantLock /** * Collection of methods for interacting with the grand exchange databases * @author Ceikry */ object GEDB { - private var pathString = "" - public var connection: Connection? = null - private var initialized = false - private var connectionRefs = 0 - private var obtainConnectionLock = ReentrantLock() - private var dbRunLock = ReentrantLock() + lateinit var db: SQLiteProvider fun init() { - init(File(ServerConstants.GRAND_EXCHANGE_DATA_PATH + "grandexchange.db").absolutePath) + init (ServerConstants.GRAND_EXCHANGE_DATA_PATH + "grandexchange.db") } - //This needs to be a separate method, so we can call it after the server config has been parsed - fun init(path: String) - { - if(initialized) return - pathString = path - //Check if the grandexchange.db file already exists. If not, create it and create the tables. - if(!File(path).exists()) - generateAndTransfer() + fun init (path: String) { + db = SQLiteProvider(path, expectedTables) + db.initTables { createdTable: String -> + if (createdTable == "price_index") + populateInitialPriceIndex() + } } @JvmStatic fun run(closure: (conn: Connection) -> Unit) { - dbRunLock.tryLock(10000L, TimeUnit.MILLISECONDS) - - connectionRefs++ - val con = connect() - closure.invoke(con) - connectionRefs-- - - if(connectionRefs == 0) { - con.close() - } - - dbRunLock.unlock() - } - - private fun connect(): Connection - { - obtainConnectionLock.tryLock(10000L, TimeUnit.MILLISECONDS) - - if (connection == null || connection!!.isClosed) - { - val ds = SQLiteDataSource() - ds.url = "jdbc:sqlite:$pathString" - connection = ds.connection - } - - obtainConnectionLock.unlock() - return connection!! + db.run(closure) } private fun convertJsonArray(arr: JSONArray): String @@ -85,107 +47,48 @@ object GEDB { return sb.toString() } - private fun generateAndTransfer() + var expectedTables = hashMapOf( + "player_offers" to "CREATE TABLE player_offers(" + + "uid INTEGER PRIMARY KEY ASC," + + "player_uid INTEGER," + + "item_id INTEGER," + + "amount_total INTEGER," + + "amount_complete INTEGER," + + "offered_value INTEGER," + + "time_stamp INTEGER," + + "offer_state INTEGER," + + "is_sale INTEGER," + + "withdraw_items STRING ," + + "slot_index INTEGER," + + "total_coin_xc INTEGER)", + "bot_offers" to "CREATE TABLE bot_offers(" + + "item_id INTEGER," + + "amount INTEGER)", + "price_index" to "CREATE TABLE price_index(" + + "item_id INTEGER," + + "value INTEGER," + + "total_value INTEGER," + + "unique_trades INTEGER," + + "last_update INTEGER)" + ) + + private fun populateInitialPriceIndex() { - val conn = connect() //for sqlite jdbc, attempting to connect to a non-existing .db file creates it. - val statement = conn.createStatement() - - //table for tracking player offers - replaces offer_dispatch.json - statement.execute( - "CREATE TABLE player_offers(" + - "uid INTEGER PRIMARY KEY ASC," + - "player_uid INTEGER," + - "item_id INTEGER," + - "amount_total INTEGER," + - "amount_complete INTEGER," + - "offered_value INTEGER," + - "time_stamp INTEGER," + - "offer_state INTEGER," + - "is_sale INTEGER," + - "withdraw_items STRING ," + - "slot_index INTEGER," + - "total_coin_xc INTEGER)" - ) - - //table for tracking bot offers - replaces bot_offers.json - statement.execute( - "CREATE TABLE bot_offers(" + - "item_id INTEGER," + - "amount INTEGER)" - ) - - //table for tracking price index data - replaces gedb.xml - statement.execute( - "CREATE TABLE price_index(" + - "item_id INTEGER," + - "value INTEGER," + - "total_value INTEGER," + - "unique_trades INTEGER," + - "last_update INTEGER)" - ) - - //check if the old .json and .xml files exist, and if they do, read them into the sqlite database and remove them. - val playerJson = ServerConstants.GRAND_EXCHANGE_DATA_PATH + "offer_dispatch.json" - val botJson = ServerConstants.GRAND_EXCHANGE_DATA_PATH + "bot_offers.json" - - if(File(playerJson).exists()) - { - val parser = JSONParser() - val reader = FileReader(playerJson) - val saveFile = parser.parse(reader) as JSONObject - - if (saveFile.containsKey("offers")) { - val offers = saveFile["offers"] as JSONArray - - for (offer in offers) { - val o = offer as JSONObject - statement.execute("insert into player_offers(player_uid,item_id,amount_total,amount_complete,offered_value,time_stamp,offer_state,is_sale,withdraw_items,total_coin_xc,slot_index) " + - "values(" + - "${o["playerUID"]}," + - "${o["itemId"]}," + - "${o["amount"]}," + - "${o["completedAmount"]}," + - "${o["offeredValue"]}," + - "${o["timeStamp"]}," + - "${o["offerState"]}," + - "${if (o["sale"] as Boolean) 1 else 0}," + - "'" + convertJsonArray(o["withdrawItems"] as JSONArray) + "'," + - "${o["totalCoinExchange"]}," + - "-1)" - ) - - } - } - - reader.close() - //File(playerJson).delete() - } - - if(File(botJson).exists()) - { - val parser = JSONParser() - val reader = FileReader(botJson) - val saveFile = parser.parse(reader) as JSONObject - - if (saveFile.containsKey("offers")) { - val offers = saveFile["offers"] as JSONArray - - for (offer in offers) { - val o = offer as JSONObject - statement.execute("insert into bot_offers " + - "values(${o["item"]},${o["qty"]})") - } - } - reader.close() - } - statement.close() - - //price index isn't worth transferring, so we're just going to make a new one. - ItemDefinition.getDefinitions().values.forEach { def -> - if(def.isTradeable){ - PriceIndex.allowItem(def.id) + run { + with (it.prepareStatement(PriceIndex.INSERT_QUERY)) { + ItemDefinition.getDefinitions().values.forEach { def -> + if(def.isTradeable) { + setInt(1, def.id) + setInt(2, def.getAlchemyValue(true)) + setInt(3, def.getAlchemyValue(true)) + setInt(4, 0) + setInt(5, 0) + execute() + } + } } } + } } \ 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 86eb9293c..5b763bf71 100644 --- a/Server/src/main/core/game/ge/GrandExchange.kt +++ b/Server/src/main/core/game/ge/GrandExchange.kt @@ -1,17 +1,19 @@ package core.game.ge +import core.ServerConstants import core.api.* +import core.cache.def.impl.ItemDefinition import core.game.node.entity.player.Player import core.game.node.entity.player.info.PlayerDetails -import core.game.node.entity.player.link.audio.Audio -import core.game.system.task.Pulse -import core.ServerConstants -import core.tools.SystemLogger 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 @@ -202,7 +204,9 @@ class GrandExchange : StartupListener, Commands { if (!PriceIndex.canTrade(itemID)) return false - val offer = GrandExchangeOffer.createBotOffer(itemID, amount) + // noted offers can not be bought. + val itemDef = ItemDefinition.forId(itemID) + val offer = GrandExchangeOffer.createBotOffer(if (itemDef.isUnnoted) itemID else itemDef.noteId, amount) pendingOffers.addLast(offer) return true @@ -218,7 +222,7 @@ class GrandExchange : StartupListener, Commands { if ( offer.offerState != OfferState.PENDING || offer.uid != 0L ) { - SystemLogger.logWarn(this::class.java, "[GE] DISPATCH FAILURE: ${offer.offerState.name}, UID: ${offer.uid}") + log(this::class.java, Log.WARN, "[GE] DISPATCH FAILURE: ${offer.offerState.name}, UID: ${offer.uid}") return false } @@ -275,7 +279,7 @@ class GrandExchange : StartupListener, Commands { buyer.completedAmount += amount if(seller.amountLeft < 1 && seller.player != null) - seller.player!!.audioManager.send(Audio(4042,1,1)) + playAudio(seller.player!!, Sounds.GE_COLLECT_COINS_4042) seller.addWithdrawItem(995, amount * if(sellerBias) buyer.offeredValue else seller.offeredValue) buyer.addWithdrawItem(seller.itemID, amount) @@ -316,7 +320,7 @@ class GrandExchange : StartupListener, Commands { private fun canUpdatePriceIndex(seller: GrandExchangeOffer, buyer: GrandExchangeOffer): Boolean { if(seller.playerUID == buyer.playerUID) return false - if(!ServerConstants.BOTS_INFLUENCE_PRICE_INDEX && (seller.isBot || buyer.isBot)) return false; + if(!ServerConstants.BOTS_INFLUENCE_PRICE_INDEX && (seller.isBot || buyer.isBot)) return false return true } @@ -355,10 +359,24 @@ class GrandExchange : StartupListener, Commands { return offers } + fun getBotstockForId(itemId: Int): Int { + var total = 0 + GEDB.run { conn -> + val stmt = conn.prepareStatement("SELECT sum(amount) FROM bot_offers WHERE amount > 0 AND item_id = ?") + stmt.setInt(1, itemId) + + val results = stmt.executeQuery() + while (results.next()) { + total += results.getInt(1) + } + } + return total + } + } override fun startup(){ GEDB.init() boot() } -} \ No newline at end of file +} diff --git a/Server/src/main/core/game/ge/GrandExchangeOffer.kt b/Server/src/main/core/game/ge/GrandExchangeOffer.kt index d27f7f431..919427567 100644 --- a/Server/src/main/core/game/ge/GrandExchangeOffer.kt +++ b/Server/src/main/core/game/ge/GrandExchangeOffer.kt @@ -11,7 +11,9 @@ import core.net.packet.out.ContainerPacket import core.net.packet.out.GrandExchangePacket import core.integrations.discord.Discord import core.game.world.repository.Repository +import kotlin.math.min import java.sql.ResultSet +import core.ServerConstants /** @@ -22,7 +24,6 @@ import java.sql.ResultSet class GrandExchangeOffer() { var itemID = 0 - var amount = 0 var completedAmount = 0 var offeredValue = 0 var index = 0 @@ -37,6 +38,8 @@ class GrandExchangeOffer() { var isLimitation = false var isBot = false + var amount: Int = 0 + get() = if (isBot) min(field, ServerConstants.BOTSTOCK_LIMIT) else field /** * Gets the total amount of money entered. * @return The total value. @@ -49,7 +52,7 @@ class GrandExchangeOffer() { * @return The amount. */ val amountLeft: Int - get() = amount - completedAmount + get() = if (isBot) min(ServerConstants.BOTSTOCK_LIMIT, amount - completedAmount) else amount - completedAmount /** * Checks if this offer is still active for dispatching. @@ -237,4 +240,22 @@ class GrandExchangeOffer() { return o } } + + fun cacheValue(): Int { + var value = 0 + if(sell) { + // count the cache value of the unsold items + value += ItemDefinition.forId(itemID).getValue() * amountLeft + } else { + // count the number of coins that haven't yet been spent + value += offeredValue * amountLeft + } + // for both the buyer and seller, the already completed portion is present in withdraw + for(item in withdraw) { + if(item != null) { + value += item.definition.value * item.amount + } + } + return value + } } diff --git a/Server/src/main/core/game/ge/GrandExchangeRecords.kt b/Server/src/main/core/game/ge/GrandExchangeRecords.kt index cf31406b0..63aa20848 100644 --- a/Server/src/main/core/game/ge/GrandExchangeRecords.kt +++ b/Server/src/main/core/game/ge/GrandExchangeRecords.kt @@ -212,7 +212,8 @@ class GrandExchangeRecords(private val player: Player? = null) : PersistPlayer, } } if (updated) { - sendMessage(player!!, "You have items from the Grand Exchange waiting in your collection box.") + sendMessage(player!!, "You have items waiting in your Grand Exchange collection box!") + playJingle(player, 284) } } @@ -251,4 +252,4 @@ class GrandExchangeRecords(private val player: Player? = null) : PersistPlayer, return player?.getAttribute("ge-records", GrandExchangeRecords()) ?: GrandExchangeRecords() } } -} \ No newline at end of file +} 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/DigSpadeHandler.java b/Server/src/main/core/game/global/action/DigSpadeHandler.java index 265fd30c1..31735e452 100644 --- a/Server/src/main/core/game/global/action/DigSpadeHandler.java +++ b/Server/src/main/core/game/global/action/DigSpadeHandler.java @@ -3,6 +3,7 @@ package core.game.global.action; import core.game.node.entity.player.Player; import core.game.system.communication.CommunicationInfo; import content.global.handlers.item.SpadeDigListener; +import core.tools.Log; import core.tools.SystemLogger; import core.game.system.task.Pulse; import core.game.world.GameWorld; @@ -12,6 +13,8 @@ import core.game.world.update.flag.context.Animation; import java.util.HashMap; import java.util.Map; +import static core.api.ContentAPIKt.log; + /** * Handles digging with a spade. * @author Emperor @@ -63,7 +66,7 @@ public final class DigSpadeHandler { */ public static boolean register(Location location, DigAction action) { if (ACTIONS.containsKey(location)) { - SystemLogger.logErr(CommunicationInfo.class, "Already contained dig reward for location " + location + "."); + log(CommunicationInfo.class, Log.ERR, "Already contained dig reward for location " + location + "."); return false; } ACTIONS.put(location, action); diff --git a/Server/src/main/core/game/global/action/DoorActionHandler.java b/Server/src/main/core/game/global/action/DoorActionHandler.java index 5c51ea0d5..02234e136 100644 --- a/Server/src/main/core/game/global/action/DoorActionHandler.java +++ b/Server/src/main/core/game/global/action/DoorActionHandler.java @@ -1,8 +1,8 @@ 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.audio.Audio; import core.game.node.entity.player.link.diary.DiaryType; import core.game.node.scenery.Constructed; import core.game.node.scenery.Scenery; @@ -15,6 +15,10 @@ import core.game.world.map.path.Pathfinder; import kotlin.Unit; import core.game.system.config.DoorConfigLoader; import core.game.world.GameWorld; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.hasRequirement; +import static core.api.ContentAPIKt.playAudio; import java.awt.*; @@ -40,7 +44,14 @@ public final class DoorActionHandler { final Scenery second = (object.getId() == 1530 || object.getId() == 1531) ? null : getSecondDoor(object, player); Scenery o = null; if (object instanceof Constructed && (o = ((Constructed) object).getReplaced()) != null) { - player.getAudioManager().send(new Audio(43)); + DoorConfigLoader.Door d = DoorConfigLoader.Companion.forId(object.getId()); + if (d != null && d.isMetal()) { + playAudio(player, Sounds.IRON_DOOR_CLOSE_70); + } else if (d != null && d.isFence()){ + playAudio(player, Sounds.GATE_CLOSE_66); + } else { + playAudio(player, 60); + } SceneryBuilder.replace(object, o); if (second instanceof Constructed && (o = ((Constructed) second).getReplaced()) != null) { SceneryBuilder.replace(second, o); @@ -51,11 +62,20 @@ public final class DoorActionHandler { if (object.getDefinition().hasAction("close")) { if (second != null) { player.getPacketDispatch().sendMessage("The doors appear to be stuck."); + playAudio(player, Sounds.DOOR_CREAK_61); return; } DoorConfigLoader.Door d = DoorConfigLoader.Companion.forId(object.getId()); + if (d != null && d.isMetal()) { + playAudio(player, Sounds.IRON_DOOR_OPEN_71); + } else if (d != null && d.isFence()){ + playAudio(player, Sounds.GATE_OPEN_67); + } else { + playAudio(player, Sounds.DOOR_OPEN_62); + } if (d == null) { player.getPacketDispatch().sendMessage("The door appears to be stuck."); + playAudio(player, Sounds.DOOR_CREAK_61); return; } int firstDir = (object.getRotation() + 3) % 4; @@ -65,14 +85,16 @@ public final class DoorActionHandler { return; } DoorConfigLoader.Door d = DoorConfigLoader.Companion.forId(object.getId()); - if (d == null) { + if (d == null || d.isAutoWalk()) { handleAutowalkDoor(player, object); return; } if (d.isMetal()) { - player.getAudioManager().send(new Audio(71)); + playAudio(player, Sounds.IRON_DOOR_OPEN_71); + } else if (d.isFence()){ + playAudio(player, Sounds.GATE_OPEN_67); } else { - player.getAudioManager().send(new Audio(81)); + playAudio(player, Sounds.DOOR_OPEN_62); } if (second != null) { DoorConfigLoader.Door s = DoorConfigLoader.Companion.forId(second.getId()); @@ -93,11 +115,23 @@ 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) { - ((Player) entity).getAudioManager().send(new Audio(3419)); + DoorConfigLoader.Door d = DoorConfigLoader.Companion.forId(object.getId()); + if (d != null && d.isMetal()) { + playAudio(entity.asPlayer(), Sounds.IRON_DOOR_OPEN_71); + playAudio(entity.asPlayer(), Sounds.IRON_DOOR_CLOSE_70, 60); + } else if (d != null && d.isFence()){ + playAudio(entity.asPlayer(), Sounds.GATE_OPEN_67); + playAudio(entity.asPlayer(), Sounds.GATE_CLOSE_66, 60); + } else { + playAudio(entity.asPlayer(), Sounds.DOOR_OPEN_62); + playAudio(entity.asPlayer(), 60, 60); + } entity.asPlayer().logoutListeners.put("autowalk", player -> { player.setLocation(loc); return Unit.INSTANCE; @@ -512,4 +546,4 @@ public final class DoorActionHandler { } return rotations; } -} \ No newline at end of file +} diff --git a/Server/src/main/core/game/global/action/DropItemHandler.java b/Server/src/main/core/game/global/action/DropItemHandler.java deleted file mode 100644 index 64d29a7f4..000000000 --- a/Server/src/main/core/game/global/action/DropItemHandler.java +++ /dev/null @@ -1,75 +0,0 @@ -package core.game.global.action; - -import core.game.node.Node; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.info.login.PlayerParser; -import core.game.node.entity.player.link.audio.Audio; -import core.game.node.item.GroundItemManager; -import core.game.node.item.Item; -import core.game.node.entity.combat.graves.GraveController; -import core.tools.SystemLogger; -import core.game.system.config.ItemConfigParser; -import core.game.world.GameWorld; - -/** - * Handles the dropping of an item. - * @author Vexia - */ -public final class DropItemHandler { - - /** - * Handles the droping of an item. - * @param player the player. - * @param node the node. - * @param option the option. - * @return {@code True} if so. - */ - public static boolean handle(final Player player, Node node, String option) { - Item item = (Item) node; - if (item.getSlot() == -1) { - player.getPacketDispatch().sendMessage("Invalid slot!"); - return false; - } - switch (option) { - case "drop": - case "destroy": - case "dissolve": - if (!player.getInterfaceManager().close()) { - return true; - } - player.getDialogueInterpreter().close(); - player.getPulseManager().clear(); - if (option.equalsIgnoreCase("destroy") || option.equalsIgnoreCase("dissolve") || (boolean) item.getDefinition().getHandlers().getOrDefault(ItemConfigParser.DESTROY,false)) { - player.getDialogueInterpreter().open(9878, item); - return true; - } - if (GraveController.hasGraveAt(player.getLocation())) { - player.sendMessage("You cannot drop items on top of graves!"); - return false; - } - if (player.getAttribute("equipLock:" + item.getId(), 0) > GameWorld.getTicks()) { - SystemLogger.logAlert(DropItemHandler.class, player + ", tried to do the drop & equip dupe."); - return true; - } - if (player.getInventory().replace(null, item.getSlot()) == item) { - item = item.getDropItem(); - player.getAudioManager().send(new Audio(item.getId() == 995 ? 10 : 2739, 1, 0));//2739 ACTUAL DROP SOUND - GroundItemManager.create(item, player.getLocation(), player); - PlayerParser.save(player); - } - player.setAttribute("droppedItem:" + item.getId(), GameWorld.getTicks() + 2); - return true; - } - return false; - } - - /** - * Drops an item. - * @param player the player. - * @param item the item. - * @return - */ - public static boolean drop(Player player, Item item) { - return handle(player, item, item.getDefinition().hasDestroyAction() ? "destroy" : "drop"); - } -} diff --git a/Server/src/main/core/game/global/action/DropListener.kt b/Server/src/main/core/game/global/action/DropListener.kt new file mode 100644 index 000000000..254b582ff --- /dev/null +++ b/Server/src/main/core/game/global/action/DropListener.kt @@ -0,0 +1,83 @@ +package core.game.global.action + +import core.api.* +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.combat.graves.GraveController +import core.game.node.entity.player.Player +import core.game.node.entity.player.info.login.PlayerParser +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 + +/** + * Represents the item drop/destroy/dissolve handler. + * @author Ceikry, ovenbreado + */ +class DropListener : InteractionListener { + override fun defineListeners() { + on(IntType.ITEM, "drop", "destroy", "dissolve", handler = ::handleDropAction) + } + + companion object { + private val DROP_COINS_SOUND = Sounds.EYEGLO_COIN_10 + private val DROP_ITEM_SOUND = Sounds.PUT_DOWN_2739 + private val DESTROY_ITEM_SOUND = Sounds.DESTROY_OBJECT_2381 + @JvmStatic fun drop(player: Player, item: Item) : Boolean { + return handleDropAction(player, item) + } + private fun handleDropAction(player: Player, node: Node) : Boolean { + val option = getUsedOption(player) + val item = node as? Item ?: return false + if (option == "drop") { + if (Pets.forId(item.id) != null) { + player.familiarManager.summon(item, true, true) + return true + } + if (GraveController.hasGraveAt(player.location)) { + sendMessage(player, "You cannot drop items on top of graves!") + return false + } + if (player.locks.equipmentLock != null) { + return false + } + + 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) + setAttribute(player, "droppedItem:${droppedItem.id}", getWorldTicks() + 2) + PlayerParser.save(player) + return@queueScript stopExecuting(player) + } + } else if (option == "destroy" || option == "dissolve" || item.definition.handlers.getOrDefault(ItemConfigParser.DESTROY, false) as Boolean) { + player.dialogueInterpreter.sendDestroyItem(item.id, item.name) + addDialogueAction(player) { player, button -> + if (button == 3) { + if (removeItem(player, item)) { + playAudio(player, DESTROY_ITEM_SOUND) + } + } + } + } + return true + } + } +} diff --git a/Server/src/main/core/game/global/action/EquipHandler.kt b/Server/src/main/core/game/global/action/EquipHandler.kt index dc0059990..29299e238 100644 --- a/Server/src/main/core/game/global/action/EquipHandler.kt +++ b/Server/src/main/core/game/global/action/EquipHandler.kt @@ -5,6 +5,7 @@ import core.game.event.ItemUnequipEvent import core.game.container.impl.EquipmentContainer import content.global.handlers.item.equipment.brawling_gloves.BrawlingGlovesManager import content.global.skill.slayer.SlayerEquipmentFlags +import core.api.playAudio import core.game.node.Node import core.game.node.entity.combat.equipment.WeaponInterface import core.game.node.entity.player.Player @@ -14,6 +15,7 @@ import core.game.interaction.InteractionListener import core.game.interaction.IntType import core.game.interaction.InteractionListeners import core.game.system.config.ItemConfigParser +import org.rs09.consts.Sounds /** * Represents the equipment equipping handler plugin. @@ -31,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) @@ -43,7 +53,7 @@ class EquipHandler : InteractionListener { return } } - if (!InteractionListeners.run(node.id, player, node, true)) { + if (!InteractionListeners.run(node.id, player, item, true)) { return } @@ -65,12 +75,16 @@ class EquipHandler : InteractionListener { player.dialogueInterpreter.close() /* TODO: Send different equip sound based on what is being equip.*/ - player.audioManager.send(item.definition.getConfiguration(ItemConfigParser.EQUIP_AUDIO, 2244)) + playAudio(player, item.definition.getConfiguration(ItemConfigParser.EQUIP_AUDIO, 2244)) + if (player.properties.autocastSpell != null) { - player.properties.autocastSpell = null - val wif = player.getExtension(WeaponInterface::class.java) - wif.selectAutoSpell(-1, true) - wif.openAutocastSelect() + + if (itemEquipmentSlot == EquipmentContainer.SLOT_WEAPON) { + player.properties.autocastSpell = null + val wif = player.getExtension(WeaponInterface::class.java) + wif.selectAutoSpell(-1, true) + wif.openAutocastSelect() + } } if (SlayerEquipmentFlags.isSlayerEq(item.id)) { @@ -120,7 +134,7 @@ class EquipHandler : InteractionListener { } if (player.equipment.remove(item)) { /* TODO: Send different unequip sound based on what is being unequipped.*/ - player.audioManager.send(Audio(2238, 10, 1)) + playAudio(player, Sounds.EQUIP_FUN_2238) player.dialogueInterpreter.close() player.inventory.add(item) diff --git a/Server/src/main/core/game/global/action/PickupHandler.kt b/Server/src/main/core/game/global/action/PickupHandler.kt index 0f9249f62..ec42b1858 100644 --- a/Server/src/main/core/game/global/action/PickupHandler.kt +++ b/Server/src/main/core/game/global/action/PickupHandler.kt @@ -4,8 +4,8 @@ import core.game.event.PickUpEvent import core.api.getItemName import content.data.GodType import core.game.node.entity.player.Player -import core.game.node.entity.player.link.audio.Audio import content.global.skill.runecrafting.RunePouch +import core.api.playAudio import core.game.node.entity.player.info.LogType import core.game.node.item.GroundItem import core.game.node.item.GroundItemManager @@ -13,9 +13,9 @@ import core.game.node.item.Item import core.game.world.map.RegionManager import core.game.world.update.flag.context.Animation import core.game.node.entity.player.info.PlayerMonitor -import core.tools.SystemLogger import core.game.system.config.GroundSpawnLoader import core.game.world.GameWorld +import org.rs09.consts.Sounds /** * A class used to handle the picking up of ground items. @@ -39,7 +39,6 @@ object PickupHandler { return true } if (player.getAttribute("droppedItem:" + item.id, 0) > GameWorld.ticks) { //Splinter - SystemLogger.logAlert(this::class.java, "$player tried to do the drop & quick pick-up Ground Item dupe.") return true } if (item !is GroundSpawnLoader.GroundSpawn && item.isRemainPrivate && !item.droppedBy(player)) { @@ -67,7 +66,7 @@ object PickupHandler { } */ - player.audioManager.send(Audio(2582, 10, 1)) + playAudio(player, Sounds.PICK2_2582) player.dispatch(PickUpEvent(item.id)) } return true diff --git a/Server/src/main/core/game/global/action/SpecialLadders.java b/Server/src/main/core/game/global/action/SpecialLadders.java index c69b6e81a..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,24 +19,58 @@ 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)), DRAYNOR_SEWER_SOUTHEAST_DOWN(new Location(3118, 3244, 0), new Location(3118, 9643, 0)), DRAYNOR_SEWER_SOUTHEAST_UP(new Location(3118, 9643, 0), new Location(3118, 3243, 0)), DRAYNOR_SEWER_NORTHWEST_DOWN(new Location(3084, 3272, 0), new Location(3085, 9672, 0)), DRAYNOR_SEWER_NORTHWEST_UP(new Location(3084, 9672, 0), new Location(3084, 3271, 0)), + BURTHORPE_HEROES_GUILD_STAIRS_UP(new Location(2895,3513,0), new Location(2897, 3513,1)), + + TREE_GNOME_STRONGHOLD_WEST_BAR_STAIRS_UP( new Location(2417, 3490, 0), new Location(2418, 3492,1)), + TREE_GNOME_STRONGHOLD_WEST_BAR_STAIRS_DOWN(new Location(2418, 3491,1), new Location(2419, 3491, 0)), + + WATERBIRTH_ISLAND_DUNGEON_SUBLEVEL_2_WALLASALKI_3_LADDER_DOWN(new Location(1799,4387,2), new Location(1799,4388,1)), + + FALADOR_WHITE_KNIGHT_CASTLE_WEST_TOWER_STAIRS_UP(new Location(2960, 3338,1), new Location(2959,3339,2)), + FALADOR_WHITE_KNIGHT_CASTLE_WEST_TOWER_STAIRS_DOWN(new Location(2960,3339,2), new Location(2960,3340,1)), + + CASTLEWARS_SARADOMIN_MAIN_FLOOR_STAIRS_DOWN(Location.create(2419, 3080, 1), Location.create(2419, 3077, 0)), + CASTLEWARS_SARADOMIN_MAIN_FLOOR_STAIRS_UP(Location.create(2428, 3081, 1), Location.create(2430, 3080, 2)), + CASTLEWARS_SARADOMIN_OUTER_WALL_STAIRS_UP(Location.create(2417, 3077, 0), Location.create(2416, 3075, 0)), + CASTLEWARS_SARADOMIN_OUTER_WALL_STAIRS_DOWN(Location.create(2417, 3075, 0), Location.create(2417, 3078, 0)), + + CASTLEWARS_ZAMORAK_TOP_FLOOR_DOWN(Location.create(2374, 3133, 3), Location.create(2374, 3130, 2)), + CASTLEWARS_ZAMORAK_MAIN_FLOOR_STAIRS_UP(Location.create(2380, 3129, 0), Location.create(2379, 3127, 1)), + 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) { @@ -43,8 +78,19 @@ public enum SpecialLadders implements LadderAchievementCheck { } }, PORT_SARIM_RAT_PITS_UP(new Location(2962,9651,0), new Location(3018,3231,0)), + ENTRANA_GLASSBLOWING_PIPE_HOUSE_DOWN(new Location(2816, 3352, 1), new Location(2817, 3352, 0)), + PATERDOMUS_TEMPLE_STAIRCASE_NORTH_UP(new Location(3415, 3490, 0), new Location(3415, 3491, 1)), + PATERDOMUS_TEMPLE_STAIRCASE_NORTH_DOWN(new Location(3415, 3491, 1), new Location(3414,3491,0)), + 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/global/report/AbuseReport.java b/Server/src/main/core/game/global/report/AbuseReport.java index 0a47d41f1..dee08a06f 100644 --- a/Server/src/main/core/game/global/report/AbuseReport.java +++ b/Server/src/main/core/game/global/report/AbuseReport.java @@ -51,7 +51,7 @@ public final class AbuseReport { CommandMapping.INSTANCE.get("mute").attemptHandling(player, new String[] {"mute", victim, "48h"}); } player.getPacketDispatch().sendMessage("Thank-you, your abuse report has been received."); - Discord.INSTANCE.postPlayerAlert(victim, "Abuse Report - " + rule.name()); + Discord.postPlayerAlert(victim, "Abuse Report - " + rule.name()); } /** diff --git a/Server/src/main/core/game/interaction/Clocks.kt b/Server/src/main/core/game/interaction/Clocks.kt new file mode 100644 index 000000000..950ef844a --- /dev/null +++ b/Server/src/main/core/game/interaction/Clocks.kt @@ -0,0 +1,12 @@ +package core.game.interaction + +object Clocks { + @JvmStatic val MOVEMENT = 0 + @JvmStatic val ANIMATION_END = 1 + @JvmStatic val NEXT_EAT = 2 + @JvmStatic val NEXT_CONSUME = 3 + @JvmStatic val NEXT_DRINK = 4 + @JvmStatic val NEXT_ATTACK = 5 + @JvmStatic val STUN = 6 + @JvmStatic val SKILLING = 7 +} diff --git a/Server/src/main/core/game/interaction/DestinationFlag.java b/Server/src/main/core/game/interaction/DestinationFlag.java index cbaa15045..a5d9a0165 100644 --- a/Server/src/main/core/game/interaction/DestinationFlag.java +++ b/Server/src/main/core/game/interaction/DestinationFlag.java @@ -115,7 +115,7 @@ public class DestinationFlag { if (dir != null) { return getDestination(mover, object, sizeX, sizeY, dir, 3); } - // System.out.println(dir); + return getDestination(mover, object, sizeX, sizeY, Direction.getLogicalDirection(object.getLocation(), mover.getLocation()), 0); } diff --git a/Server/src/main/core/game/interaction/Interaction.java b/Server/src/main/core/game/interaction/InteractPlugin.java similarity index 93% rename from Server/src/main/core/game/interaction/Interaction.java rename to Server/src/main/core/game/interaction/InteractPlugin.java index c96de2c10..d5057980e 100644 --- a/Server/src/main/core/game/interaction/Interaction.java +++ b/Server/src/main/core/game/interaction/InteractPlugin.java @@ -9,18 +9,20 @@ 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.update.flag.player.FaceLocationFlag; import core.net.packet.PacketRepository; import core.net.packet.context.InteractionOptionContext; import core.net.packet.out.InteractionOption; +import core.tools.Log; import core.tools.SystemLogger; import core.game.world.GameWorld; +import static core.api.ContentAPIKt.log; + /** * Handles interaction between nodes. * @author Emperor */ -public class Interaction { +public class InteractPlugin { /** * The current options. @@ -41,7 +43,7 @@ public class Interaction { * Constructs a new {@code Interaction} {@code Object}. * @param node The node reference. */ - public Interaction(Node node) { + public InteractPlugin(Node node) { this.node = node; } @@ -85,7 +87,7 @@ public class Interaction { player.dispatch(new InteractionEvent(node, option.getName().toLowerCase())); } catch (Exception e){ e.printStackTrace(); - SystemLogger.logErr(this.getClass(), this.getClass().getName() + e.getMessage()); + log(this.getClass(), Log.ERR, this.getClass().getName() + e.getMessage()); } } @@ -118,7 +120,7 @@ public class Interaction { } } catch (Exception e){ e.printStackTrace(); - SystemLogger.logErr(this.getClass(), this.getClass().getName() + e.getMessage()); + log(this.getClass(), Log.ERR, this.getClass().getName() + e.getMessage()); } return true; } @@ -142,14 +144,14 @@ public class Interaction { @Override public boolean pulse() { try { - player.faceLocation(FaceLocationFlag.getFaceLocation(player, node)); + player.faceLocation(node.getFaceLocation(player.getLocation())); if (player.getLocks().isInteractionLocked() || player.getZoneMonitor().interact(node, option)) { return true; } player.getPacketDispatch().sendMessage("Nothing interesting happens."); } catch (Exception e){ e.printStackTrace(); - SystemLogger.logErr(this.getClass(), this.getClass().getName() + e.getMessage()); + log(this.getClass(), Log.ERR, this.getClass().getName() + e.getMessage()); } return true; } @@ -178,7 +180,7 @@ public class Interaction { @Override public boolean pulse() { try { - player.faceLocation(FaceLocationFlag.getFaceLocation(player, node)); + player.faceLocation(node.getFaceLocation(player.getLocation())); if (player.getLocks().isInteractionLocked() || player.getZoneMonitor().interact(node, option)) { return true; } @@ -187,7 +189,7 @@ public class Interaction { } } catch (Exception e){ e.printStackTrace(); - SystemLogger.logErr(this.getClass(), this.getClass().getName() + e.getMessage()); + log(this.getClass(), Log.ERR, this.getClass().getName() + e.getMessage()); } return true; } @@ -341,4 +343,4 @@ public class Interaction { this.initialized = initialized; } -} \ No newline at end of file +} diff --git a/Server/src/main/core/game/interaction/InteractionListener.kt b/Server/src/main/core/game/interaction/InteractionListener.kt index ef7963650..fff396633 100644 --- a/Server/src/main/core/game/interaction/InteractionListener.kt +++ b/Server/src/main/core/game/interaction/InteractionListener.kt @@ -23,7 +23,7 @@ interface InteractionListener : ContentInterface{ fun on(ids: IntArray, type: IntType, vararg option: String, handler: (player: Player, node: Node) -> Boolean){ InteractionListeners.add(ids, type.ordinal, option, handler) } - fun on(option: String, type: IntType, handler: (player: Player, node: Node) -> Boolean){ + @Deprecated("Don't use") fun on(option: String, type: IntType, handler: (player: Player, node: Node) -> Boolean){ InteractionListeners.add(option, type.ordinal, handler) } fun on(type: IntType, vararg option: String, handler: (player: Player, node: Node) -> Boolean){ @@ -82,5 +82,16 @@ interface InteractionListener : ContentInterface{ InteractionListeners.instantClasses.add(name) } + fun defineInteraction(type: IntType, ids: IntArray, vararg options: String, persistent: Boolean = false, allowedDistance: Int = 1, handler: (player: Player, node: Node, state: Int) -> Boolean) { + InteractionListeners.addMetadata(ids, type, options, InteractionMetadata(handler, allowedDistance, persistent)) + } + + fun defineInteraction(type: IntType, vararg options: String, persist: Boolean = false, allowedDistance: Int = 1, handler: (player: Player, node: Node, state: Int) -> Boolean) { + InteractionListeners.addGenericMetadata(options, type, InteractionMetadata(handler, allowedDistance, persist)) + } + + data class InteractionMetadata(val handler: (player: Player, node: Node, state: Int) -> Boolean, val distance: Int, val persist: Boolean) + data class UseWithMetadata(val handler: (player: Player, used: Node, with: Node, state: Int) -> Boolean, val distance: Int, val persist: Boolean) + fun defineListeners() } diff --git a/Server/src/main/core/game/interaction/InteractionListeners.kt b/Server/src/main/core/game/interaction/InteractionListeners.kt index 0d65d40c5..c28113500 100644 --- a/Server/src/main/core/game/interaction/InteractionListeners.kt +++ b/Server/src/main/core/game/interaction/InteractionListeners.kt @@ -14,6 +14,8 @@ object InteractionListeners { private val useWithWildcardListeners = HashMap Boolean, (Player, Node, Node) -> Boolean>>>(10) private val destinationOverrides = HashMap Location>(100) private val equipListeners = HashMap Boolean>(10) + private val interactions = HashMap() + private val useWithInteractions = HashMap() val instantClasses = HashSet() @JvmStatic @@ -214,8 +216,8 @@ object InteractionListeners { } else { if(flipped) player.dispatch(UseWithEvent(with.id, used.id)) else player.dispatch(UseWithEvent(used.id, with.id)) - if(flipped) method.invoke(player,with,used) - else method.invoke(player,used,with) + if(flipped) return method.invoke(player,with,used) + else return method.invoke(player,used,with) } return true } @@ -232,32 +234,37 @@ object InteractionListeners { if(player.locks.isInteractionLocked) return false - val method = get(id,type.ordinal,option) ?: get(option,type.ordinal) ?: return false - val destOverride = getOverride(type.ordinal, id, option) ?: getOverride(type.ordinal,node.id) ?: getOverride(type.ordinal,option.toLowerCase()) + val method = get(id,type.ordinal,option) ?: get(option,type.ordinal) - player.setAttribute("interact:option", option) + player.setAttribute("interact:option", option.lowercase()) + player.dispatch(InteractionEvent(node, option.toLowerCase())) - if(option.toLowerCase() == "attack") //Attack needs special handling >.> - { - player.dispatch(InteractionEvent(node, option.toLowerCase())) - method.invoke(player, node) + if (method == null) { + val inter = interactions["${type.ordinal}:$id:${option.lowercase()}"] ?: interactions["${type.ordinal}:${option.lowercase()}"] ?: return false + val script = Interaction(inter.handler, inter.distance, inter.persist) + player.scripts.setInteractionScript(node, script) + player.pulseManager.run(object : MovementPulse(player, node, flag) { + override fun pulse(): Boolean { + return true + } + }) return true } + val destOverride = getOverride(type.ordinal, id, option) ?: getOverride(type.ordinal,node.id) ?: getOverride(type.ordinal,option.toLowerCase()) + if(type != IntType.ITEM && !isInstant(method)) { if(player.locks.isMovementLocked) return false player.pulseManager.run(object : MovementPulse(player, node, flag, destOverride) { override fun pulse(): Boolean { if(player.zoneMonitor.interact(node, Option(option, 0))) return true player.faceLocation(node.location) - player.dispatch(InteractionEvent(node, option.toLowerCase())) method.invoke(player,node) return true } }) } else { method.invoke(player,node) - player.dispatch(InteractionEvent(node, option.toLowerCase())) } return true } @@ -285,4 +292,29 @@ object InteractionListeners { val className = handler.javaClass.name.substringBefore("$") return instantClasses.contains(className) } + + fun addMetadata (ids: IntArray, type: IntType, options: Array, metadata: InteractionListener.InteractionMetadata) { + for (id in ids) + for (opt in options) + interactions["${type.ordinal}:$id:${opt.lowercase()}"] = metadata + } + + fun addMetadata (id: Int, type: IntType, options: Array, metadata: InteractionListener.InteractionMetadata) { + for (opt in options) + interactions["${type.ordinal}:$id:${opt.lowercase()}"] = metadata + } + + fun addGenericMetadata (options: Array, type: IntType, metadata: InteractionListener.InteractionMetadata) { + for (opt in options) + interactions["${type.ordinal}:$opt"] = metadata + } + + fun addMetadata (used: Int, with: IntArray, type: IntType, metadata: InteractionListener.UseWithMetadata) { + for (id in with) + useWithInteractions["${type.ordinal}:$used:$with"] = metadata + } + + fun addMetadata (used: Int, with: Int, type: IntType, metadata: InteractionListener.UseWithMetadata) { + useWithInteractions["${type.ordinal}:$used:$with"] = metadata + } } diff --git a/Server/src/main/core/game/interaction/MovementPulse.java b/Server/src/main/core/game/interaction/MovementPulse.java index 9f819f091..04c9d2a8d 100644 --- a/Server/src/main/core/game/interaction/MovementPulse.java +++ b/Server/src/main/core/game/interaction/MovementPulse.java @@ -4,8 +4,10 @@ import core.game.node.Node; import core.game.node.entity.Entity; import core.game.node.entity.impl.WalkingQueue; 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.task.Pulse; +import core.game.world.GameWorld; import core.game.world.map.Direction; import core.game.world.map.Location; import core.game.world.map.Point; @@ -15,7 +17,13 @@ import core.net.packet.PacketRepository; import core.net.packet.context.PlayerContext; import core.net.packet.out.ClearMinimapFlag; import kotlin.jvm.functions.Function2; +import kotlin.Pair; import core.tools.SystemLogger; +import core.api.utils.Vector; + +import content.region.wilderness.handlers.revenants.RevenantNPC; + +import static core.api.ContentAPIKt.*; import java.util.Deque; @@ -78,6 +86,8 @@ public abstract class MovementPulse extends Pulse { private Function2 overrideMethod; + private Location previousLoc; + /** * Constructs a new {@code MovementPulse} {@code Object}. * @@ -167,6 +177,11 @@ public abstract class MovementPulse extends Pulse { if (pathfinder == null) { if (mover instanceof Player) { this.pathfinder = Pathfinder.SMART; + } else if (mover instanceof NPC) { + NPC npc = (NPC)mover; + NPCBehavior behavior = npc.behavior; + Pathfinder pf = behavior != null ? behavior.getPathfinderOverride(npc) : null; + this.pathfinder = pf != null ? pf : Pathfinder.DUMB; } else { this.pathfinder = Pathfinder.DUMB; } @@ -174,26 +189,53 @@ public abstract class MovementPulse extends Pulse { this.pathfinder = pathfinder; } this.forceRun = forceRun; + + if (destination instanceof NPC || destination instanceof Player) + destinationFlag = DestinationFlag.ENTITY; + + if (mover.currentMovement != null) { + mover.currentMovement.stop(); + mover.getWalkingQueue().reset(); + } + mover.currentMovement = this; + } + + private void clearInferiorScripts() { + mover.scripts.removeWeakScripts(); + mover.scripts.removeNormalScripts(); } @Override public boolean update() { - mover.face(null); - if (mover == null || destination == null || mover.getViewport().getRegion() == null) { + if (!mover.getViewport().getRegion().isActive()) return false; - } - if (hasInactiveNode() || !mover.getViewport().getRegion().isActive()) { + if (!isRunning()) return true; + + if (!validate()) { stop(); return true; } - if (!isRunning()) { + + clearInferiorScripts(); + + mover.face(null); + updatePath(); + + if (tryInteract()) { + stop(); return true; } - findPath(); + + return false; + } + + private boolean tryInteract() { Location ml = mover.getLocation(); // Allow being within 1 square of moving entities to interact with them. int radius = destination instanceof Entity && ((Entity)destination).getWalkingQueue().hasPath() ? 1 : 0; + if (interactLocation == null) + return false; if (Math.max(Math.abs(ml.getX() - interactLocation.getX()), Math.abs(ml.getY() - interactLocation.getY())) <= radius) { try { if (near || pulse()) { @@ -214,6 +256,13 @@ public abstract class MovementPulse extends Pulse { return false; } + private boolean validate() { + if (mover == null || destination == null || mover.getViewport().getRegion() == null || hasInactiveNode()) { + return false; + } + return isRunning(); + } + @Override public void stop() { super.stop(); @@ -226,76 +275,95 @@ public abstract class MovementPulse extends Pulse { /** * Finds a path to the destination, if necessary. */ - public void findPath() { + private boolean usingTruncatedPath = false; + private boolean isMoveNearSet = false; + public void updatePath() { if (mover instanceof NPC && mover.asNpc().isNeverWalks()) { return; } - if(destination.getLocation() == null){ - SystemLogger.logAlert(this.getClass(), destination.getId() + " < ID"); - SystemLogger.logAlert(this.getClass(), destination.getName() + " < NAME"); - SystemLogger.logAlert(this.getClass(), "ASDAD"); + if(destination == null || destination.getLocation() == null){ return; } - boolean inside = isInsideEntity(mover.getLocation()); -/* This appears to have been a premature optimization that lead to a bug that would cause both entities - to completely stop moving mid-combat/mid-follow-dance/etc - if (last != null && last.equals(destination.getLocation()) && !inside) { - return; - } -*/ Location loc = null; - if (destinationFlag != null && overrideMethod == null) { + if (optionHandler != null) { + loc = optionHandler.getDestination(mover, destination); + } + else if (useHandler != null) { + loc = useHandler.getDestination((Player) mover, destination); + } + else if (isInsideEntity(mover.getLocation())) { + loc = findBorderLocation(); + } + + if (loc == null && destinationFlag != null && overrideMethod == null) { loc = destinationFlag.getDestination(mover, destination); } - else if(overrideMethod != null){ + else if(loc == null && overrideMethod != null){ loc = overrideMethod.invoke(mover,destination); - if(loc == destination.getLocation()) loc = destinationFlag.getDestination(mover,destination); + if(loc == destination.getLocation() && destinationFlag != null) loc = destinationFlag.getDestination(mover,destination); + else if (loc == destination.getLocation()) loc = null; } - if (loc == null) { - if (optionHandler != null) { - loc = optionHandler.getDestination(mover, destination); - } - else if (useHandler != null) { - loc = useHandler.getDestination((Player) mover, destination); - } - else if (inside) { - loc = findBorderLocation(); - } - } + if (destination instanceof NPC && mover.getProperties().getCombatPulse().getVictim() != destination) + loc = checkForEntityPathInterrupt(loc != null ? loc : destination.getLocation()); - if (destination == null) { - return; - } + if (interactLocation == null) + interactLocation = 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 && Math.max(Math.abs(ml.getX() - dl.getX()), Math.abs(ml.getY() - dl.getY())) > 1) { - WalkingQueue wq = ((Entity)destination).getWalkingQueue(); - if(wq.hasPath()) { - Point[] points = wq.getQueue().toArray(new Point[0]); - if(points.length > 0) { - Point p = points[0]; - for(int i=0; i 0) || (usingTruncatedPath && destination.getLocation().getDistance(mover.getLocation()) < 14)) { + if (!checkAllowMovement()) + return; + if (destination instanceof Entity && previousLoc != null && previousLoc.equals(loc) && mover.getWalkingQueue().hasPath()) + return; + + Path path; + Pair truncation = truncateLoc(mover, loc != null ? loc : destination.getLocation()); + if (truncation.getFirst()) { + path = Pathfinder.find(mover, truncation.getSecond(), true, pathfinder); + usingTruncatedPath = true; + } else { + path = Pathfinder.find(mover, loc != null ? loc : destination, true, pathfinder); + interactLocation = null; //reset interactLocation so the below code can set it to the properly-pathfound last bit of path. + usingTruncatedPath = false; + } + near = !path.isSuccessful() || path.isMoveNear(); + + if (!path.getPoints().isEmpty()) { + Point point = path.getPoints().getLast(); + if (forceRun) { + mover.getWalkingQueue().reset(forceRun); + } else { + mover.getWalkingQueue().reset(); + } + int size = path.getPoints().toArray().length; + Deque points = path.getPoints(); + for (int i = 0; i < size; i++) { + point = path.getPoints().pop(); + mover.getWalkingQueue().addPath(point.getX(), point.getY()); + if (destination instanceof Entity) { + mover.face((Entity) destination); + } else { + mover.face(null); } - loc.setX(p.getX()); - loc.setY(p.getY()); + + if (i == size - 1 && interactLocation == null) + interactLocation = Location.create(point.getX(), point.getY(), mover.getLocation().getZ()); } } + previousLoc = loc; } - Path path = Pathfinder.find(mover, loc != null ? loc : destination, true, pathfinder); - near = !path.isSuccessful() || path.isMoveNear(); - interactLocation = mover.getLocation(); + last = destination.getLocation(); + if (mover instanceof Player && mover.getAttribute("draw-intersect", false)) { + clearHintIcon((Player) mover); + registerHintIcon((Player) mover, interactLocation, 5); + } + } + + private boolean checkAllowMovement() { boolean canMove = true; - if (destination instanceof NPC || destination instanceof Player) { + if (destination instanceof Entity) { Entity e = (Entity) destination; Location l = e.getLocation(); Deque npcPath = e.getWalkingQueue().getQueue(); @@ -309,27 +377,58 @@ public abstract class MovementPulse extends Pulse { } } } - if (!path.getPoints().isEmpty() && canMove) { - Point point = path.getPoints().getLast(); - interactLocation = Location.create(point.getX(), point.getY(), mover.getLocation().getZ()); - if (forceRun) { - mover.getWalkingQueue().reset(forceRun); - } else { - mover.getWalkingQueue().reset(); - } - int size = path.getPoints().toArray().length; - Deque points = path.getPoints(); - for (int i = 0; i < size; i++) { - point = path.getPoints().pop(); - mover.getWalkingQueue().addPath(point.getX(), point.getY()); - if (destination instanceof Entity) { - mover.face((Entity) destination); - } else { - mover.face(null); + 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> 1, size >> 1, 0); + Location centerDest = centerDestLoc.transform(size >> 1, size >> 1, 0); Location center = mover.getLocation().transform(mover.size() >> 1, mover.size() >> 1, 0); Direction direction = Direction.getLogicalDirection(centerDest, center); - Location delta = Location.getDelta(destination.getLocation(), mover.getLocation()); + Location delta = Location.getDelta(centerDestLoc, mover.getLocation()); main: for (int i = 0; i < 4; i++) { int amount = 0; diff --git a/Server/src/main/core/game/interaction/Option.java b/Server/src/main/core/game/interaction/Option.java index 9e5a47065..8c99a1b57 100644 --- a/Server/src/main/core/game/interaction/Option.java +++ b/Server/src/main/core/game/interaction/Option.java @@ -88,7 +88,7 @@ public final class Option { if (node instanceof Item) { return ItemDefinition.getOptionHandler(nodeId, name); } - System.out.println("Unhandled node type " + node + "!"); + return null; } diff --git a/Server/src/main/core/game/interaction/OptionHandler.java b/Server/src/main/core/game/interaction/OptionHandler.java index 3528289cd..057bdacdb 100644 --- a/Server/src/main/core/game/interaction/OptionHandler.java +++ b/Server/src/main/core/game/interaction/OptionHandler.java @@ -3,6 +3,7 @@ package core.game.interaction; import core.cache.def.impl.SceneryDefinition; import core.game.node.Node; import core.game.node.entity.player.Player; +import core.tools.Log; import core.tools.SystemLogger; import core.game.world.map.Location; import core.plugin.Plugin; @@ -10,6 +11,8 @@ import core.plugin.Plugin; import java.util.ArrayList; import java.util.List; +import static core.api.ContentAPIKt.log; + /** * Handles an interaction option. * @author Emperor @@ -71,7 +74,7 @@ public abstract class OptionHandler implements Plugin { final SceneryDefinition definition = SceneryDefinition.forId(wrapper); final List list = new ArrayList<>(20); if (definition.getChildrenIds() == null) { - SystemLogger.logErr(this.getClass(), "Null child wrapper in option handler wrapperId=" + wrapper); + log(this.getClass(), Log.ERR, "Null child wrapper in option handler wrapperId=" + wrapper); return new int[] { wrapper }; } for (int child : definition.getChildrenIds()) { diff --git a/Server/src/main/core/game/interaction/Script.kt b/Server/src/main/core/game/interaction/Script.kt new file mode 100644 index 000000000..895011970 --- /dev/null +++ b/Server/src/main/core/game/interaction/Script.kt @@ -0,0 +1,25 @@ +package core.game.interaction + +import core.game.node.Node +import core.game.node.entity.player.Player + +typealias UseWithExecutor = (Player, Node, Node, Int) -> Boolean +typealias InteractExecutor = (Player, Node, Int) -> Boolean +typealias VoidExecutor = (Int) -> Boolean + +enum class QueueStrength { + WEAK, + NORMAL, + STRONG, + SOFT +} + +open class Script (val execution: T, val persist: Boolean) { + var state: Int = 0 + var nextExecution = 0 +} + +class Interaction(execution: InteractExecutor, val distance: Int, persist: Boolean) : Script(execution, persist) +class UseWithInteraction(execution: UseWithExecutor, val distance: Int, persist: Boolean, val used: Node, val with: Node) : Script(execution, persist) +class QueuedScript(executor: VoidExecutor, val strength: QueueStrength, persist: Boolean) : Script(executor, persist) +class QueuedUseWith(executor: UseWithExecutor, val strength: QueueStrength, persist: Boolean, val used: Node, val with: Node) : Script(executor, persist) \ No newline at end of file diff --git a/Server/src/main/core/game/interaction/ScriptProcessor.kt b/Server/src/main/core/game/interaction/ScriptProcessor.kt new file mode 100644 index 000000000..52c86b1e8 --- /dev/null +++ b/Server/src/main/core/game/interaction/ScriptProcessor.kt @@ -0,0 +1,303 @@ +package core.game.interaction + +import core.api.* +import core.game.node.Node +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.GroundItem +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 java.lang.Integer.max +import java.io.* + +class ScriptProcessor(val entity: Entity) { + private var apScript: Script<*>? = null + private var opScript: Script<*>? = null + private var interactTarget: Node? = null + private var currentScript: Script<*>? = null + private val queue = ArrayList>() + + var delay = 0 + var interacted = false + var apRangeCalled = false + var apRange = 10 + var persistent = false + var targetDestination: Location? = null + + fun preMovement() { + var allSkipped = false + while (!allSkipped) { + allSkipped = processQueue() + } + + if (isStunned(entity)) return + if (entity.delayed()) return + + var canProcess = !entity.delayed() + if (entity is Player && entity !is AIPlayer) + canProcess = canProcess && !entity.hasModalOpen() + + if (entity !is Player) return + if (!entity.delayed() && canProcess && interactTarget != null) { + if (opScript != null && inOperableDistance()) { + face(entity, interactTarget?.getFaceLocation(entity.location) ?: return reset()) + processInteractScript(opScript ?: return reset()) + } + else if (apScript != null && inApproachDistance(apScript ?: return reset())) { + face(entity, interactTarget?.getFaceLocation(entity.location) ?: return reset()) + processInteractScript(apScript ?: return reset()) + } + else if (apScript == null && opScript == null && inOperableDistance()) { + sendMessage(entity, "Nothing interesting happens.") + } + } + } + + fun postMovement(didMove: Boolean) { + if (didMove) + entity.clocks[Clocks.MOVEMENT] = GameWorld.ticks + if (entity.walkingQueue.isRunning) 0 else 1 + var canProcess = !entity.delayed() + if (entity is Player && entity !is AIPlayer) + canProcess = canProcess && !entity.interfaceManager.isOpened && !entity.interfaceManager.hasChatbox() + + if (entity !is Player) return + if (!entity.delayed() && canProcess && interactTarget != null && !interacted) { + if (opScript != null && inOperableDistance()) { + face(entity, interactTarget?.centerLocation ?: return reset()) + processInteractScript(opScript ?: return reset()) + } + else if (apScript != null && inApproachDistance(apScript ?: return reset())) { + face(entity, interactTarget?.centerLocation ?: return reset()) + processInteractScript(apScript ?: return reset()) + } + else if (apScript == null && opScript == null && inOperableDistance()) { + sendMessage(entity, "Nothing interesting happens.") + } + } + if (canProcess && (apScript != null || opScript != null)) { + if (!interacted && !didMove && finishedMoving(entity)) { + sendMessage(entity, "I can't reach that!") + reset() + } + } + if (interacted && !apRangeCalled && !persistent) reset() + if (interactTarget != null && interactTarget?.isActive != true) reset() + } + + fun processQueue() : Boolean { + var strongInQueue = false + var softInQueue = false + var anyExecuted = false + strongInQueue = hasTypeInQueue(QueueStrength.STRONG) + softInQueue = hasTypeInQueue(QueueStrength.SOFT) + + if (strongInQueue) { + if (entity is Player) { + closeAllInterfaces(entity) + } + } + + if (strongInQueue) { + removeWeakScripts() + } + + val toRemove = ArrayList>() + + for (i in 0 until queue.size) { + when (val script = queue[i]) { + is QueuedScript -> { + if (entity.delayed() && script.strength != QueueStrength.SOFT) + continue + if (script.nextExecution > GameWorld.ticks) + continue + if ((script.strength == QueueStrength.STRONG) && entity is Player) { + closeAllInterfaces(entity) + } + script.nextExecution = GameWorld.ticks + 1 + val finished = executeScript(script) + script.state++ + if (finished && !script.persist) + toRemove.add(script) + else if (finished) + script.state = 0 + anyExecuted = true + } + is QueuedUseWith -> { + if (entity.delayed() && script.strength != QueueStrength.SOFT) + continue + if (entity !is Player) { + toRemove.add(script) + log(this::class.java, Log.ERR, "Tried to queue an item UseWith interaction for a non-player!") + continue + } + if (script.nextExecution > GameWorld.ticks) + continue + if ((script.strength == QueueStrength.STRONG)) { + closeAllInterfaces(entity) + } + script.nextExecution = GameWorld.ticks + 1 + val finished = executeScript(script) + script.state++ + if (finished && !script.persist) + toRemove.add(script) + else if (finished) + script.state = 0 + anyExecuted = true + } + } + } + + queue.removeAll(toRemove.toSet()) + return !anyExecuted + } + + fun isPersist (script: Script<*>) : Boolean { + return script.persist + } + + fun processInteractScript(script: Script<*>) { + if (interactTarget == null || !interactTarget!!.isActive) { + log(this::class.java, Log.FINE, "Interact target $interactTarget no longer active, cancelling interaction.") + reset() + } + if (script.nextExecution < GameWorld.ticks) { + val finished = executeScript(script) + script.state++ + if (finished && isPersist(script)) + script.state = 0 + interacted = true + } + } + + fun executeScript(script: Script<*>) : Boolean { + currentScript = script + try { + when (script) { + is Interaction -> return script.execution.invoke(entity as? Player ?: return true, interactTarget ?: return true, script.state) + is UseWithInteraction -> return script.execution.invoke(entity as? Player ?: return true, script.used, script.with, script.state) + is QueuedScript -> return script.execution.invoke(script.state) + is QueuedUseWith -> return script.execution.invoke(entity as? Player ?: return true, script.used, script.with, script.state) + } + } catch (e: Exception) { + val sw = StringWriter() + val pw = PrintWriter(sw) + e.printStackTrace(pw) + log(this::class.java, Log.ERR, "Error processing ${script::class.java.simpleName} - stopping the script. Exception follows: $sw") + reset() + } + currentScript = null + return true + } + + fun removeWeakScripts() { + queue.removeAll(queue.filter { it is QueuedScript && it.strength == QueueStrength.WEAK || it is QueuedUseWith && it.strength == QueueStrength.WEAK }.toSet()) + } + + fun removeNormalScripts() { + queue.removeAll(queue.filter { it is QueuedScript && it.strength == QueueStrength.NORMAL || it is QueuedUseWith && it.strength == QueueStrength.NORMAL }.toSet()) + } + + fun inApproachDistance(script: Script<*>) : Boolean { + val distance = when (script) { + is Interaction -> script.distance + is UseWithInteraction -> script.distance + else -> 10 + } + targetDestination?.let { + return it.location.getDistance(entity.location) <= distance && hasLineOfSight(entity, it) + } + return false + } + + fun inOperableDistance() : Boolean { + targetDestination?.let { + return it.cardinalTiles.any {loc -> loc == entity.location} && hasLineOfSight(entity, it) + } + return false + } + + fun reset() { + apScript = null + opScript = null + currentScript = null + apRangeCalled = false + interacted = false + apRange = 10 + interactTarget = null + persistent = false + targetDestination = null + resetAnimator(entity as? Player ?: return) + } + + fun setInteractionScript(target: Node, script: Script<*>?) { + if (apScript != null && script != null && script.execution == apScript!!.execution) return + if (opScript != null && script != null && script.execution == opScript!!.execution) return + reset() + interactTarget = target + if (script != null) { + apRange = when(script) { + is Interaction -> script.distance + is UseWithInteraction -> script.distance + else -> 10 + } + persistent = script.persist + if (apRange == -1) + opScript = script + else + apScript = script + targetDestination = when (interactTarget) { + is NPC -> DestinationFlag.ENTITY.getDestination(entity, interactTarget) + is Scenery -> { + val basicPath = Pathfinder.find(entity, interactTarget) + val path = basicPath.points.lastOrNull() + if (basicPath.isMoveNear) { + target.location + return + } + if (path == null) { + clearScripts(entity) + return + } + Location.create(path.x, path.y, entity.location.z) + } + is GroundItem -> DestinationFlag.ITEM.getDestination(entity, interactTarget) + else -> target.location + } + } + } + + fun addToQueue(script: Script<*>, strength: QueueStrength) { + if (script !is QueuedScript && script !is QueuedUseWith) { + log(this::class.java, Log.ERR, "Tried to queue ${script::class.java.simpleName} as a queueable script but it's not!") + return + } + if (strength == QueueStrength.STRONG && entity is Player) { + closeAllInterfaces(entity) + } + script.nextExecution = max(GameWorld.ticks + 1, script.nextExecution) + queue.add(script) + } + + fun getActiveScript() : Script<*>? { + return currentScript ?: getActiveInteraction() + } + + private fun getActiveInteraction() : Script<*>? { + return opScript ?: apScript + } + + fun hasTypeInQueue (type: QueueStrength) : Boolean { + for (script in queue) { + if (script is QueuedScript && script.strength == type) + return true + else if (script is QueuedUseWith && script.strength == type) + return true + } + return false + } +} diff --git a/Server/src/main/core/game/interaction/UseWithHandler.java b/Server/src/main/core/game/interaction/UseWithHandler.java index fa2b5e393..4c5a6f414 100644 --- a/Server/src/main/core/game/interaction/UseWithHandler.java +++ b/Server/src/main/core/game/interaction/UseWithHandler.java @@ -8,10 +8,10 @@ import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.game.node.item.Item; import core.game.node.scenery.Scenery; +import core.tools.Log; import core.tools.SystemLogger; import core.game.system.task.Pulse; import core.game.world.map.Location; -import core.game.world.update.flag.player.FaceLocationFlag; import core.plugin.Plugin; import java.util.ArrayList; @@ -19,6 +19,8 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import static core.api.ContentAPIKt.log; + /** * Handles the "use {@code node a} with {@code node b}" option. * @@ -132,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; } @@ -172,7 +176,7 @@ public abstract class UseWithHandler implements Plugin { @Override public boolean pulse() { event.getPlayer().dispatch(new UseWithEvent(event.getUsed().getId(), event.getUsedWith().getId())); - event.getPlayer().faceLocation(FaceLocationFlag.getFaceLocation(event.getPlayer(), event.getUsedWith())); + event.getPlayer().faceLocation(event.getUsedWith().getFaceLocation(event.getPlayer().getLocation())); boolean handled = false; Item used = (Item) event.getUsed(); for (UseWithHandler h : handlers) { @@ -210,7 +214,7 @@ public abstract class UseWithHandler implements Plugin { final SceneryDefinition definition = SceneryDefinition.forId(wrapper); final List list = new ArrayList<>(20); if (definition.getChildrenIds() == null) { - SystemLogger.logErr(this.getClass(), "Null child wrapper in option handler wrapperId=" + wrapper); + log(this.getClass(), Log.ERR, "Null child wrapper in option handler wrapperId=" + wrapper); return new int[]{wrapper}; } for (int child : definition.getChildrenIds()) { @@ -270,4 +274,4 @@ public abstract class UseWithHandler implements Plugin { public boolean isDynamic() { return false; } -} \ No newline at end of file +} diff --git a/Server/src/main/core/game/node/Node.java b/Server/src/main/core/game/node/Node.java index 493ec9239..2e93e6049 100644 --- a/Server/src/main/core/game/node/Node.java +++ b/Server/src/main/core/game/node/Node.java @@ -1,7 +1,7 @@ package core.game.node; import core.game.interaction.DestinationFlag; -import core.game.interaction.Interaction; +import core.game.interaction.InteractPlugin; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.game.node.item.Item; @@ -9,9 +9,10 @@ import core.game.node.scenery.Scenery; import core.game.world.map.Direction; import core.game.world.map.Location; import core.tools.StringUtils; +import core.api.utils.Vector; /** - * Represents a node which is anything that is interactable in Keldagrim. + * Represents a node which is anything that is interactable in 2009Scape. * @author Emperor */ public abstract class Node { @@ -49,7 +50,7 @@ public abstract class Node { /** * The interaction instance. */ - protected Interaction interaction; + protected InteractPlugin interactPlugin; /** * The destination flag. @@ -111,6 +112,14 @@ public abstract class Node { return this instanceof NPC ? ((NPC) this).getId() : this instanceof Scenery ? ((Scenery) this).getId() : this instanceof Item ? ((Item) this).getId() : -1; } + /** + * Gets the node id hash (only relevant if the node is an item). + * @return the id hash. + */ + public int getIdHash() { + return this instanceof Item ? ((Item) this).getIdHash() : -1; + } + /** * Gets the center location. * @return The center location. @@ -120,6 +129,21 @@ public abstract class Node { return location.transform(offset, offset, 0); } + public Vector getMathematicalCenter() { + Location topRight = location.transform(size - 1, size - 1, 0); + double x = ((double) location.getX() + (double) topRight.getX()) / 2.0; + double y = ((double) location.getY() + (double) topRight.getY()) / 2.0; + return new Vector(x, y); + } + + public Location getFaceLocation (Location fromLoc) { + Vector center = getMathematicalCenter(); + Vector fromVec = new Vector((double) fromLoc.getX(), (double) fromLoc.getY()); + Vector difference = fromVec.minus(center); + Vector end = center.plus(difference.invert()); + return Location.create((int)end.getX(), (int)end.getY(), fromLoc.getZ()); + } + /** * Gets the name of this node. * @return The name. @@ -181,6 +205,8 @@ public abstract class Node { * @param direction The direction to set. */ public void setDirection(Direction direction) { + if (direction == null) + return; this.direction = direction; } @@ -220,19 +246,19 @@ public abstract class Node { * Gets the interaction. * @return The interaction. */ - public Interaction getInteraction() { - if (interaction != null && !interaction.isInitialized()) { - interaction.setDefault(); + public InteractPlugin getInteraction() { + if (interactPlugin != null && !interactPlugin.isInitialized()) { + interactPlugin.setDefault(); } - return interaction; + return interactPlugin; } /** * Sets the interaction. - * @param interaction The interaction to set. + * @param interactPlugin The interaction to set. */ - public void setInteraction(Interaction interaction) { - this.interaction = interaction; + public void setInteraction(InteractPlugin interactPlugin) { + this.interactPlugin = interactPlugin; } /** @@ -266,4 +292,4 @@ public abstract class Node { public void setRenderable(boolean renderable) { this.renderable = renderable; } -} \ No newline at end of file +} diff --git a/Server/src/main/core/game/node/entity/Entity.java b/Server/src/main/core/game/node/entity/Entity.java index 2bdc10d75..b566cccb0 100644 --- a/Server/src/main/core/game/node/entity/Entity.java +++ b/Server/src/main/core/game/node/entity/Entity.java @@ -1,7 +1,7 @@ package core.game.node.entity; import core.game.event.*; -import core.game.interaction.DestinationFlag; +import core.game.interaction.*; import core.game.node.Node; import core.game.node.entity.combat.BattleState; import core.game.node.entity.combat.CombatStyle; @@ -9,14 +9,14 @@ import core.game.node.entity.combat.DeathTask; import core.game.node.entity.combat.ImpactHandler; import core.game.node.entity.combat.equipment.ArmourSet; import core.game.node.entity.impl.*; +import core.game.node.entity.impl.Properties; import core.game.node.entity.lock.ActionLocks; import core.game.node.entity.npc.NPC; 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.node.entity.state.EntityState; -import core.game.node.entity.state.StateManager; import core.game.system.task.Pulse; +import core.game.world.map.zone.ZoneRestriction; import org.jetbrains.annotations.NotNull; import core.game.world.GameWorld; import core.game.world.map.Location; @@ -26,13 +26,13 @@ import core.game.world.map.path.Pathfinder; import core.game.world.map.zone.ZoneMonitor; import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Graphics; +import core.game.world.update.flag.*; import core.game.node.entity.combat.CombatSwingHandler; import core.game.world.update.UpdateMasks; +import core.game.system.timer.TimerManager; +import core.game.system.timer.TimerRegistry; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; /** * An entity is a movable node, such as players and NPCs. @@ -48,7 +48,7 @@ public abstract class Entity extends Node { /** * The entity's update masks. */ - private final UpdateMasks updateMasks = new UpdateMasks(); + private final UpdateMasks updateMasks = new UpdateMasks(this); /** * The walking queue. @@ -100,28 +100,29 @@ public abstract class Entity extends Node { */ private final ZoneMonitor zoneMonitor = new ZoneMonitor(this); - /** - * The state manager. - */ - private final StateManager stateManager = new StateManager(this); - /** * The reward locks. */ private final ActionLocks locks = new ActionLocks(); + public ScriptProcessor scripts = new ScriptProcessor(this); + public final int[] clocks = new int[10]; + + public MovementPulse currentMovement; /** * The mapping of event types to event hooks */ private HashMap, ArrayList> hooks = new HashMap<>(); - + public TimerManager timers = new TimerManager(this); /** * If the entity is invisible. */ private boolean invisible; + + /** * Constructs a new {@code Entity} {@code Object}. * @param name The name of the entity. @@ -162,7 +163,8 @@ public abstract class Entity extends Node { { if(this.hooks.containsKey(event.getClass())) { - this.hooks.get(event.getClass()).forEach((hook) -> { hook.process(this, event); }); + ArrayList processList = new ArrayList(this.hooks.get(event.getClass())); + processList.forEach((hook) -> { hook.process(this, event); }); } } @@ -193,7 +195,8 @@ public abstract class Entity extends Node { { hookList = new ArrayList(); } - hookList.add(hook); + if (!hookList.contains(hook)) + hookList.add(hook); hooks.put(event, hookList); } @@ -202,15 +205,20 @@ public abstract class Entity extends Node { */ public void init() { active = true; + TimerRegistry.addAutoTimers (this); } /** * This methods gets called before the {@link #update()} method. */ public void tick() { + scripts.preMovement(); dispatch(new TickEvent(GameWorld.getTicks())); skills.pulse(); + Location old = location != null ? location.transform(0, 0, 0) : Location.create(0,0,0); walkingQueue.update(); + scripts.postMovement(!Objects.equals(location, old)); + timers.processTimers(); updateMasks.prepare(this); } @@ -251,6 +259,8 @@ public abstract class Entity extends Node { */ public void fullRestore() { skills.restore(); + timers.removeTimer("poison"); + timers.removeTimer("poison:immunity"); } /** @@ -269,7 +279,7 @@ public abstract class Entity extends Node { skills.rechargePrayerPoints(); impactHandler.getImpactQueue().clear(); impactHandler.setDisabledTicks(10); - stateManager.reset(); + timers.onEntityDeath(); removeAttribute("combat-time"); face(null); //Check if it's a Loar shade and transform back into the shadow version. @@ -293,6 +303,9 @@ public abstract class Entity extends Node { * combat zone. */ public boolean isIgnoreMultiBoundaries(Entity victim) { + if (this instanceof NPC) { + return ((NPC) this).behavior.shouldIgnoreMultiRestrictions((NPC) this, victim); + } return false; } @@ -329,6 +342,9 @@ public abstract class Entity extends Node { public void onImpact(final Entity entity, BattleState state) { if (DeathTask.isDead(this)) state.neutralizeHits(); + if (this instanceof NPC) { + ((NPC) this).behavior.afterDamageReceived((NPC) this, entity, state); + } if (properties.isRetaliating() && !properties.getCombatPulse().isAttacking() && !getLocks().isInteractionLocked() && properties.getCombatPulse().getNextAttack() < GameWorld.getTicks()) { if (!getWalkingQueue().hasPath() && !getPulseManager().isMovingPulse() || (this instanceof NPC)) { properties.getCombatPulse().attack(entity); @@ -473,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. @@ -616,21 +632,39 @@ public abstract class Entity extends Node { * @param entity The entity to face. * @return {@code True} if succesful. */ - public abstract boolean face(Entity entity); + public boolean face(Entity entity) { + if (entity == null) { + int ordinal = EntityFlags.getOrdinal(EFlagType.of(this), EntityFlag.FaceEntity); + if (getUpdateMasks().unregisterSynced(ordinal)) { + return getUpdateMasks().register(EntityFlag.FaceEntity, null); + } + return true; + } + return getUpdateMasks().register(EntityFlag.FaceEntity, entity, true); + } /** * Registers a new face location update flag to the update masks. * @param location The location to face. * @return {@code True} if succesful. */ - public abstract boolean faceLocation(Location location); + public boolean faceLocation(Location location) { + if (location == null) { + int ordinal = EntityFlags.getOrdinal(EFlagType.of(this), EntityFlag.FaceLocation); + getUpdateMasks().unregisterSynced(ordinal); + return true; + } + return getUpdateMasks().register(EntityFlag.FaceLocation, location, true); + } /** * 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 abstract boolean sendChat(String string); + public boolean sendChat(String string) { + return getUpdateMasks().register(EntityFlag.ForceChat, string); + } /** * Gets the current combat swing handler. @@ -882,14 +916,6 @@ public abstract class Entity extends Node { return zoneMonitor; } - /** - * Gets the stateManager. - * @return The stateManager. - */ - public StateManager getStateManager() { - return stateManager; - } - /** * Checks if we have fire resistance. * @return {@code True} if so. @@ -903,7 +929,7 @@ public abstract class Entity extends Node { * @return {@code True} if so. */ public boolean isTeleBlocked() { - return stateManager.hasState(EntityState.TELEBLOCK); + return timers.getTimer("teleblock") != null || getLocks().isTeleportLocked() || getZoneMonitor().isRestricted(ZoneRestriction.TELEPORT); } /** @@ -952,4 +978,12 @@ public abstract class Entity extends Node { } return occupied; } + + public boolean delayed() { + return scripts.getDelay() > GameWorld.getTicks(); + } + + public boolean isTeleporting() { + return getAttribute("tele-pulse", null) != null || properties.getTeleportLocation() != null; + } } 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 009df9029..7e0475e3d 100644 --- a/Server/src/main/core/game/node/entity/combat/CombatPulse.kt +++ b/Server/src/main/core/game/node/entity/combat/CombatPulse.kt @@ -1,5 +1,6 @@ package core.game.node.entity.combat +import content.global.ame.RandomEventNPC import content.global.handlers.item.equipment.special.SalamanderSwingHandler import core.game.container.impl.EquipmentContainer import core.game.interaction.MovementPulse @@ -9,14 +10,15 @@ import core.game.node.entity.combat.equipment.WeaponInterface 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.player.link.audio.Audio import core.game.node.entity.skill.Skills -import core.game.node.entity.state.EntityState 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.tools.RandomFunction +import core.api.* +import core.game.interaction.DestinationFlag +import core.game.system.timer.impl.* /** * The combat-handling pulse implementation. @@ -131,7 +133,7 @@ class CombatPulse( if (handler == null) { handler = entity.getSwingHandler(true) } - if (!v.isAttackable(entity, handler!!.type, true)) { + if (!v.isAttackable(entity, handler!!.type, true) && entity != getAttribute(v, AntiMacro.EVENT_NPC, null)) { return true } if (!swing(entity, victim, handler)) { @@ -147,7 +149,7 @@ class CombatPulse( } else if (entity.properties.attackStyle.style == WeaponInterface.STYLE_RAPID || (salamander && entity.properties.attackStyle.style == WeaponInterface.STYLE_RANGE_ACCURATE)) { speed-- } - if (!magic && entity.stateManager.hasState(EntityState.MIASMIC)) { + if (!magic && hasTimerActive(entity)) { speed = (speed * 1.5).toInt() } setNextAttack(speed) @@ -215,7 +217,7 @@ class CombatPulse( if (entity == null || victim == null || entity.locks.isMovementLocked) { return false } - movement.findPath() + movement.updatePath() return type == InteractionType.MOVE_INTERACT } @@ -294,6 +296,7 @@ class CombatPulse( } setVictim(victim) entity.onAttack(victim as Entity?) + victim.scripts.removeWeakScripts() if (!isAttacking) entity.pulseManager.run(this) @@ -351,6 +354,7 @@ class CombatPulse( override fun start() { super.start() entity!!.face(victim) + entity.walkingQueue.reset() } override fun stop() { @@ -438,17 +442,17 @@ class CombatPulse( if (DeathTask.isDead(victim) || DeathTask.isDead(entity)) { return true } + if (entity is NPC) + entity.asNpc().behavior.beforeAttackFinalized(entity, victim, state) if (impact || getDelay() == 0) { - if (state.estimatedHit != 0 && victim is NPC && entity is Player) { + if (state.estimatedHit != 0 && victim is NPC) { val n = victim.asNpc() val audio = n.getAudio(1) if (audio != null) { - entity.asPlayer().audioManager.send(audio, true) + playGlobalAudio(victim.location, audio.id) } } else if (state.estimatedHit != 0 && victim is Player) { - val sounds = intArrayOf(516, 517, 518) - val audio = Audio(sounds[RandomFunction.random(sounds.size)]) - audio.send(victim.asPlayer(), true) + playHurtAudio(victim.asPlayer()) } handler.impact(entity, victim, state) handler.onImpact(entity, victim, state) 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 6b148d8e4..748e258d5 100644 --- a/Server/src/main/core/game/node/entity/combat/CombatSwingHandler.kt +++ b/Server/src/main/core/game/node/entity/combat/CombatSwingHandler.kt @@ -11,6 +11,8 @@ import core.game.node.entity.player.link.audio.Audio import core.game.node.entity.player.link.prayer.PrayerType import core.game.node.entity.skill.Skills import content.global.skill.summoning.familiar.Familiar +import core.api.log +import core.api.playGlobalAudio import core.game.world.map.Direction import core.game.world.map.Location import core.game.world.map.RegionManager @@ -19,8 +21,9 @@ import core.game.world.map.path.Pathfinder import core.game.world.map.path.Pathfinder.* import core.game.world.update.flag.context.Animation import core.tools.RandomFunction -import core.tools.SystemLogger import core.game.system.config.ItemConfigParser +import core.tools.Log +import org.rs09.consts.Sounds import java.util.* import kotlin.math.floor @@ -28,8 +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: ArrayList = ArrayList(SwingHandlerFlag.values().size) + constructor(type: CombatStyle?, vararg flags: SwingHandlerFlag) : this(type) { + this.flags = arrayListOf(*flags) + } + /** * The mapping of the special attack handlers. */ @@ -168,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") SystemLogger.logInfo(this::class.java, "Should hit: $it") } + return Math.random() < chance } /** @@ -226,7 +230,6 @@ abstract class CombatSwingHandler(var type: CombatStyle?) { entity.attack(victim) entity.removeAttribute("autocast_component") } - weapEx?.setAttackStyle(1) weapEx?.updateInterface() entity.debug("Adjusting attack style") } @@ -323,18 +326,15 @@ abstract class CombatSwingHandler(var type: CombatStyle?) { if (protection and 0x2 != 0) { return "Your potion and shield fully protects you from the dragon's $fireName." } - return if (protection and 0x8 != 0) { - "Your prayer and shield absorbs most of the dragon's $fireName." - } else "Your shield absorbs most of the dragon's $fireName." + return "Your shield absorbs most of the dragon's $fireName." } if (protection and 0x2 != 0) { - return if (protection and 0x8 != 0) { - "Your prayer and potion absorbs most of the dragon's $fireName." - } else "Your antifire potion helps you defend the against the dragon's $fireName." + return "Your antifire potion helps you defend the against the dragon's $fireName." } - return if (protection and 0x8 != 0) { - "Your magic prayer absorbs some of the dragon's $fireName." - } else "You are horribly burnt by the dragon's $fireName." + if (protection and 0x8 != 0) { + return "Your magic prayer absorbs some of the dragon's $fireName." + } + return "You are horribly burnt by the dragon's $fireName." } /** @@ -357,15 +357,16 @@ abstract class CombatSwingHandler(var type: CombatStyle?) { if (audio == null || audio.id == 0) { audio = audios[0] } - entity.asPlayer().audioManager.send(audio, true, entity.location) + playGlobalAudio(entity.location, audio.id) } - } else { - entity.asPlayer().audioManager.send(2564) + } else if (type == CombatStyle.MELEE) { + //plays a punching sound when no weapon is equipped + playGlobalAudio(entity.location, Sounds.HUMAN_ATTACK_2564) } - } else if (entity is NPC && victim is Player) { + } else if (entity is NPC) { val npc = entity.asNpc() val audio = npc.getAudio(0) - audio?.send(victim.asPlayer(), true, entity.location) + if (audio != null) playGlobalAudio(entity.location, audio.id) } } @@ -437,7 +438,7 @@ abstract class CombatSwingHandler(var type: CombatStyle?) { if (!(state.targets.size == 1 && state.targets[0] == state)) { for (s in state.targets) { if (s != null && s != state) { - adjustBattleState(entity, victim, s); + adjustBattleState(entity, victim, s) } } } @@ -485,10 +486,13 @@ abstract class CombatSwingHandler(var type: CombatStyle?) { is Player -> {player = entity; attStyle = entity.properties.attackStyle.style} else -> return } + if (victim is NPC) EXPERIENCE_MOD *= victim.behavior.getXpMultiplier(victim, player) if (state != null) { val hit = state.totalDamage if (entity is Player) { - player.skills.addExperience(Skills.HITPOINTS, hit * 1.33, true) + var experience = hit * 1.33 + if (victim is NPC) experience *= victim.behavior.getXpMultiplier(victim, player) + player.skills.addExperience(Skills.HITPOINTS, experience, true) } var skill = -1 @@ -515,8 +519,10 @@ abstract class CombatSwingHandler(var type: CombatStyle?) { WeaponInterface.STYLE_CAST -> skill = Skills.MAGIC WeaponInterface.STYLE_DEFENSIVE_CAST -> { - player.skills.addExperience(Skills.MAGIC, hit * 1.33, true) - player.skills.addExperience(Skills.DEFENCE, hit.toDouble(), true) + var experience = hit.toDouble() + if (victim is NPC) experience *= victim.behavior.getXpMultiplier(victim, player) + player.skills.addExperience(Skills.MAGIC, experience * 1.33, true) + player.skills.addExperience(Skills.DEFENCE, experience, true) return } } @@ -601,7 +607,7 @@ abstract class CombatSwingHandler(var type: CombatStyle?) { specialHandlers = HashMap() } if (specialHandlers!!.containsKey(itemId)) { - SystemLogger.logErr(this::class.java, "Already contained special attack handler for item " + itemId + " - [old=" + specialHandlers!![itemId]!!::class.java.simpleName + ", new=" + handler.javaClass.simpleName + "].") + log(this::class.java, Log.ERR, "Already contained special attack handler for item " + itemId + " - [old=" + specialHandlers!![itemId]!!::class.java.simpleName + ", new=" + handler.javaClass.simpleName + "].") return false } return specialHandlers!!.put(itemId, handler) == null @@ -644,7 +650,7 @@ abstract class CombatSwingHandler(var type: CombatStyle?) { for(x2 in 0 until victim!!.size()) { for(y2 in 0 until victim!!.size()) { val dst = victim!!.location.transform(x2, y2, 0) - val path = Pathfinder.PROJECTILE.find(src, 1, dst, 1, 1, 0, 0, 0, false, RegionManager::getClippingFlag) + val path = PROJECTILE.find(src, 1, dst, 1, 1, 0, 0, 0, false, RegionManager::getClippingFlag) if(path.isSuccessful && (!checkClose || path.points.size <= 1)) { return true } @@ -657,3 +663,11 @@ abstract class CombatSwingHandler(var type: CombatStyle?) { } } + +enum class SwingHandlerFlag { + IGNORE_STAT_BOOSTS_DAMAGE, + IGNORE_STAT_BOOSTS_ACCURACY, + IGNORE_PRAYER_BOOSTS_DAMAGE, + 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 99d697ce2..da308856d 100644 --- a/Server/src/main/core/game/node/entity/combat/DeathTask.java +++ b/Server/src/main/core/game/node/entity/combat/DeathTask.java @@ -18,6 +18,8 @@ import core.game.system.task.Pulse; import core.game.world.GameWorld; import core.game.world.map.Location; +import static core.api.ContentAPIKt.playGlobalAudio; + /** * Handles an entity death task. * @author Emperor @@ -47,12 +49,9 @@ public final class DeathTask extends NodeTask { Entity killer = n.length > 0 ? (Entity) n[0] : e; if (e instanceof NPC) { killer.removeAttribute("combat-time"); - if (killer instanceof Player) { - Player p = killer.asPlayer(); - Audio audio = e.asNpc().getAudio(2); - if (audio != null) { - audio.send(p, true); - } + Audio audio = e.asNpc().getAudio(2); + if (audio != null) { + playGlobalAudio(e.getLocation(), audio.id); } } e.graphics(Animator.RESET_G); @@ -83,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)); } @@ -156,7 +155,7 @@ public final class DeathTask extends NodeTask { if (killer == null) { killer = entity; } - Pulse pulse = SINGLETON.schedule(entity, killer); + Pulse pulse = new DeathTask().schedule(entity, killer); entity.getPulseManager().run(pulse, PulseType.STRONG); } } @@ -179,4 +178,4 @@ public final class DeathTask extends NodeTask { return SINGLETON; } -} \ No newline at end of file +} 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..4065a0d80 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,47 @@ public final class ImpactHandler { if (entity instanceof Player) { 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 +409,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 13d0164b8..1838c72e1 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 @@ -15,8 +16,8 @@ import kotlin.math.floor * @author Emperor * @author Ceikry, Kotlin conversion + cleanup */ -open class MagicSwingHandler - : CombatSwingHandler(CombatStyle.MAGIC) { +open class MagicSwingHandler (vararg flags: SwingHandlerFlag) + : CombatSwingHandler(CombatStyle.MAGIC, *flags) { override fun canSwing(entity: Entity, victim: Entity): InteractionType? { if (!isProjectileClipped(entity, victim, false)) { @@ -24,9 +25,9 @@ open class MagicSwingHandler } var distance = 10 var type = InteractionType.STILL_INTERACT - var goodRange = victim.centerLocation.withinDistance(entity.centerLocation, getCombatDistance(entity, victim, distance)) + var goodRange = victim.getClosestOccupiedTile(entity.location).withinDistance(entity.centerLocation, getCombatDistance(entity, victim, distance)) if (victim.walkingQueue.isMoving && !goodRange) { - goodRange = victim.centerLocation.withinDistance(entity.centerLocation, getCombatDistance(entity, victim, ++distance)) + goodRange = victim.getClosestOccupiedTile(entity.location).withinDistance(entity.centerLocation, getCombatDistance(entity, victim, ++distance)) type = InteractionType.MOVE_INTERACT } if (goodRange && isAttackable(entity, victim) != InteractionType.NO_INTERACT) { @@ -61,7 +62,7 @@ open class MagicSwingHandler 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,28 +137,31 @@ open class MagicSwingHandler } 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) { - prayer += entity.prayer.getSkillBonus(Skills.MAGIC) - } - val additional = getSetMultiplier(entity, Skills.MAGIC); - val effective = floor(level * prayer * additional + spellBonus) - val bonus = entity.properties.bonuses[WeaponInterface.BONUS_MAGIC] - return floor((effective + 8) * (bonus + 64) / 10).toInt() + + return 0 } override fun calculateHit(entity: Entity?, victim: Entity?, modifier: Double): Int { @@ -180,14 +184,31 @@ open class MagicSwingHandler } 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 b54533a6b..c70d760f9 100644 --- a/Server/src/main/core/game/node/entity/combat/MeleeSwingHandler.kt +++ b/Server/src/main/core/game/node/entity/combat/MeleeSwingHandler.kt @@ -13,9 +13,9 @@ import core.game.node.entity.combat.equipment.WeaponInterface 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.entity.state.EntityState import core.game.world.map.path.Pathfinder import core.tools.RandomFunction +import org.rs09.consts.Items import kotlin.math.ceil import kotlin.math.floor @@ -24,11 +24,11 @@ import kotlin.math.floor * @author Emperor * @author Ceikry, Kotlin conversion + cleanup */ -open class MeleeSwingHandler +open class MeleeSwingHandler (vararg flags: SwingHandlerFlag) /** * Constructs a new `MeleeSwingHandler` {@Code Object}. */ - : CombatSwingHandler(CombatStyle.MELEE) { + : CombatSwingHandler(CombatStyle.MELEE, *flags) { override fun canSwing(entity: Entity, victim: Entity): InteractionType? { //Credits wolfenzi, https://www.rune-server.ee/2009scape-development/rs2-server/snippets/608720-arios-hybridding-improve.html var distance = if (usingHalberd(entity)) 2 else 1 @@ -64,13 +64,23 @@ open class MeleeSwingHandler 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) + + // Snowscape: Give slower weapons a minimum damage on hit to compensate for their lower DPS + // While the exact math varies due to enemy defence, this brings the average time to kill for slow weapons closer to the scimitar + // This formula is copied to MiscCommandSet.kt for the ::calcmaxhit command. + var speedMod = (entity.getProperties().getAttackSpeed() - 4) + var min = floor(speedMod * 0.1 * max).toInt() + + hit = RandomFunction.random(min, max + 1) } state.estimatedHit = hit if(victim != null) { @@ -118,7 +128,7 @@ open class MeleeSwingHandler damage = 48 } if (damage > -1 && RandomFunction.random(10) < 4) { - victim.stateManager.register(EntityState.POISONED, false, damage, entity) + applyPoison (victim, entity, damage) } } } @@ -128,60 +138,79 @@ open class MeleeSwingHandler 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) 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) - effectiveAttackLevel *= (entity.properties.bonuses[entity.properties.attackStyle.bonusType] + 64) - val amuletName = (if(entity is Player) getItemFromEquipment(entity, EquipmentSlot.AMULET)?.name ?: "null" else "null").toLowerCase() - val victimName = entity.properties.combatPulse.getVictim()?.name ?: "none" + 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 - if(entity is Player && SlayerManager.getInstance(entity).task?.ids?.contains((entity.properties.combatPulse?.getVictim()?.id ?: 0)) == true) - effectiveAttackLevel *= SlayerEquipmentFlags.getDamAccBonus(entity) //Slayer Helm/ Black Mask/ Slayer cape + val victimName = entity.properties.combatPulse.getVictim()?.name ?: "none" - else if (entity is Player //Salve amulet - && (amuletName.contains("salve")) - && checkUndead(victimName) - ) { - effectiveAttackLevel *= 1.2 + // 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)?.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 + } + + 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) { - 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)?.ids?.contains((entity.properties.combatPulse?.getVictim()?.id ?: 0)) == 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() - - cumulativeStr *= getSetMultiplier(entity, Skills.STRENGTH) - - if(entity is Player && SlayerManager.getInstance(entity).task?.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 { @@ -189,19 +218,19 @@ open class MeleeSwingHandler 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 } } @@ -211,7 +240,7 @@ open class MeleeSwingHandler override fun getSetMultiplier(e: Entity?, skillId: Int): Double { if (e!!.properties.armourSet === ArmourSet.DHAROK && skillId == Skills.STRENGTH) { -// System.out.println("Fiist number -> " + 1.0 + ((e.getSkills().getMaximumLifepoints() - e.getSkills().getLifepoints()) * 0.01)); + return 1.0 + (e!!.skills.maximumLifepoints - e.skills.lifepoints) * 0.01 } if(e is Player && e.isWearingVoid(CombatStyle.MELEE) && (skillId == Skills.ATTACK || skillId == Skills.STRENGTH)) { @@ -300,7 +329,7 @@ open class MeleeSwingHandler } return victim.getSwingHandler(false).type == CombatStyle.MELEE && e.withinDistance(victim.location, 1) && victim.properties.combatPulse.getVictim() === entity && entity.index < victim.index } - return entity.centerLocation.withinDistance(victim.centerLocation, distance + (size shr 1) + (victim.size() shr 1)) + return entity.centerLocation.withinDistance(victim.getClosestOccupiedTile(entity.location), distance + (size shr 1) + (victim.size() shr 1)) } } } 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 64d4784c7..ead12b6c3 100644 --- a/Server/src/main/core/game/node/entity/combat/RangeSwingHandler.kt +++ b/Server/src/main/core/game/node/entity/combat/RangeSwingHandler.kt @@ -1,6 +1,7 @@ package core.game.node.entity.combat import content.global.skill.skillcapeperks.SkillcapePerks +import core.api.* import core.game.container.impl.EquipmentContainer import core.game.node.entity.Entity import core.game.node.entity.combat.equipment.* @@ -9,31 +10,31 @@ 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.skill.Skills -import core.game.node.entity.state.EntityState 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.Log import core.tools.RandomFunction -import core.tools.SystemLogger -import core.game.world.GameWorld import java.util.* import kotlin.math.ceil import kotlin.math.floor +import content.data.Quests /** * Handles the range combat swings. * @author Emperor * @author Ceikry, conversion to Kotlin + cleanup */ -open class RangeSwingHandler +open class RangeSwingHandler (vararg flags: SwingHandlerFlag) : CombatSwingHandler(CombatStyle.RANGE, *flags) { /** * Constructs a new `RangeSwingHandler` {@Code Object}. */ - : CombatSwingHandler(CombatStyle.RANGE) { override fun canSwing(entity: Entity, victim: Entity): InteractionType? { if (!isProjectileClipped(entity, victim, false)) { return InteractionType.NO_INTERACT @@ -46,16 +47,16 @@ open class RangeSwingHandler distance += 2 } if (entity is Player) { - val rw = RangeWeapon.get(entity.getEquipment().getNew(EquipmentContainer.SLOT_WEAPON).getId()); + val rw = RangeWeapon.get(entity.getEquipment().getNew(EquipmentContainer.SLOT_WEAPON).getId()) if(rw != null && (rw.weaponType == WeaponType.DOUBLE_SHOT || rw.weaponType == WeaponType.DEGRADING)) { // Dark bow and crystal bow have a 10-square range, independent of whether longrange stance is used distance = 10 } } - var goodRange = victim.centerLocation.withinDistance(entity.centerLocation, getCombatDistance(entity, victim, distance)) + var goodRange = victim.getClosestOccupiedTile(entity.location).withinDistance(entity.centerLocation, getCombatDistance(entity, victim, distance)) var type = InteractionType.STILL_INTERACT if (victim.walkingQueue.isMoving && !goodRange) { - goodRange = victim.centerLocation.withinDistance(entity.centerLocation, getCombatDistance(entity, victim, ++distance)) + goodRange = victim.getClosestOccupiedTile(entity.location).withinDistance(entity.centerLocation, getCombatDistance(entity, victim, ++distance)) type = InteractionType.MOVE_INTERACT } if (goodRange && super.canSwing(entity, victim) != InteractionType.NO_INTERACT) { @@ -74,15 +75,24 @@ open class RangeSwingHandler return -1 } var hit = 0 - if (isAccurateImpact(entity, victim, CombatStyle.RANGE)) { - val max = calculateHit(entity, victim, 1.0).also { if(entity?.name?.toLowerCase() == "test10") SystemLogger.logInfo(this::class.java, "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 } @@ -106,7 +116,7 @@ open class RangeSwingHandler if (entity is Player) { val rw = RangeWeapon.get(entity.equipment.getNew(3).id) if (rw == null) { - SystemLogger.logErr(this::class.java, "Unhandled range weapon used - [item id=" + entity.equipment.getNew(3).id + "].") + log(this::class.java, Log.ERR, "Unhandled range weapon used - [item id=" + entity.equipment.getNew(3).id + "].") return } w = Weapon(entity.equipment[3], rw.ammunitionSlot, entity.equipment.getNew(rw.ammunitionSlot)) @@ -123,7 +133,7 @@ open class RangeSwingHandler if (state.ammunition != null && entity is Player) { val damage = state.ammunition.poisonDamage if (state.estimatedHit > 0 && damage > 8 && RandomFunction.random(10) < 4) { - victim.stateManager.register(EntityState.POISONED, false, damage, entity) + applyPoison(victim, entity, damage) } } super.adjustBattleState(entity, victim, state) @@ -160,7 +170,7 @@ open class RangeSwingHandler } 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 @@ -182,49 +192,98 @@ open class RangeSwingHandler override fun calculateAccuracy(entity: Entity?): Int { entity ?: return 0 - var effectiveRangedLevel = entity.skills.getLevel(Skills.RANGE).toDouble() - if(entity is Player) 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) - effectiveRangedLevel *= (entity.properties.bonuses[entity.properties.attackStyle.bonusType] + 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) { - prayer += entity.prayer.getSkillBonus(Skills.RANGE) + entity ?: return 0 + + 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((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() + } } - var cumulativeStr = floor(level * prayer) - if (entity.properties.attackStyle.style == WeaponInterface.STYLE_RANGE_ACCURATE) { - cumulativeStr += 3.0 - } - cumulativeStr *= getSetMultiplier(entity, Skills.RANGE) - cumulativeStr *= (bonus + 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 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 @@ -282,7 +341,6 @@ open class RangeSwingHandler val type = state.weapon.type val amount = if (type == WeaponType.DOUBLE_SHOT) 2 else 1 if (type == WeaponType.DEGRADING) { - degrade(e, state, amount) return } val ammo = state.weapon.ammunition @@ -302,7 +360,7 @@ open class RangeSwingHandler dropLocation = null } if (dropLocation != null && state.rangeWeapon.isDropAmmo) { - val rate = 5 * (1.0 + e.skills.getLevel(Skills.RANGE) * 0.01) * dropRate + val rate = 5 * dropRate if (RandomFunction.randomize(rate.toInt()) != 0) { val drop = GroundItemManager.increase(Item(ammo.id, amount), dropLocation, e) if (drop != null) { @@ -324,53 +382,26 @@ open class RangeSwingHandler */ private fun getDropRate(e: Entity?): Double { if (e is Player) { - val cape = e.equipment[EquipmentContainer.SLOT_CAPE] + //val cape = e.equipment[EquipmentContainer.SLOT_CAPE] val weapon = e.equipment[EquipmentContainer.SLOT_WEAPON] - if (cape != null && (cape.id == 10498 || cape.id == 10499) && weapon != null && weapon.id != 10034 && weapon.id != 10033) { + //if (cape != null && (cape.id == 10498 || cape.id == 10499) && weapon != null && weapon.id != 10034 && weapon.id != 10033) { + if (hasRequirement(e, Quests.ANIMAL_MAGNETISM, false) && weapon != null && weapon.id != 10034 && weapon.id != 10033) { val rate = 80 if (RandomFunction.random(100) < rate) { + /* val torso = e.equipment[EquipmentContainer.SLOT_CHEST] val modelId = torso?.definition?.maleWornModelId1 ?: -1 if (modelId == 301 || modelId == 306 || modelId == 3379) { e.packetDispatch.sendMessage("Your armour interferes with Ava's device.") return 1.0 } + */ return (-1).toDouble() } - return 0.33 + return 0.0 } } return 1.0 } - - /** - * Degrades the player's range weapon used. - * @param p The player. - * @param state The battle state. - * @param amount The amount of shots to degrade. - */ - private fun degrade(p: Player, state: BattleState, amount: Int) { - if (state.weapon.item.id == 4212) { // New crystal bow. - p.packetDispatch.sendMessage("Your crystal bow has degraded!") - p.equipment.replace(Item(4214, 1, 996), 3) - return - } - val charge = state.weapon.item.charge - amount * 4 - state.weapon.item.charge = charge - if (charge < 1) { - val id = state.weapon.id + 1 - if (id < 4224) { - p.packetDispatch.sendMessage("Your crystal bow has degraded!") - p.equipment.replace(Item(id, 1, 999), 3) - } else { - var replace: Item? = null - if (!p.inventory.add(Item(4207))) { - replace = Item(4207) - } - p.equipment.replace(replace, 3) - p.packetDispatch.sendMessage("Your crystal bow has degraded to a small crystal seed.") - } - } - } } } 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 7b6d5275b..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 @@ -7,12 +7,13 @@ import core.game.node.entity.combat.ImpactHandler.HitsplatType; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.audio.Audio; -import core.game.node.entity.state.EntityState; import core.game.world.GameWorld; import core.game.world.update.flag.context.Graphics; import core.tools.RandomFunction; import org.rs09.consts.NPCs; +import static core.api.ContentAPIKt.*; + /** * Represents a bolt effect. * @author Vexia @@ -20,7 +21,6 @@ import org.rs09.consts.NPCs; */ 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)); @@ -32,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) { @@ -58,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)); @@ -107,12 +105,11 @@ public enum BoltEffect { EMERALD(9241, new Graphics(752), new Audio(2919)) { @Override public void impact(BattleState state) { - state.getVictim().getStateManager().register(EntityState.POISONED, false, 68, state.getAttacker()); + 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); @@ -134,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)); @@ -164,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); @@ -228,10 +216,10 @@ public enum BoltEffect { if (sound != null) { Entity attacker = state.getAttacker(); if (attacker instanceof Player) { - sound.send(attacker.asPlayer(), true); + playGlobalAudio(attacker.getLocation(), sound.id); } if (victim instanceof Player) { - sound.send(victim.asPlayer(), true); + playGlobalAudio(victim.getLocation(), sound.id); } } if (graphics != null) { @@ -269,5 +257,4 @@ public enum BoltEffect { public int getItemId() { return itemId; } - } diff --git a/Server/src/main/core/game/node/entity/combat/equipment/FireType.java b/Server/src/main/core/game/node/entity/combat/equipment/FireType.java index d4d18d910..4e006d648 100644 --- a/Server/src/main/core/game/node/entity/combat/equipment/FireType.java +++ b/Server/src/main/core/game/node/entity/combat/equipment/FireType.java @@ -4,11 +4,12 @@ import core.game.node.Node; import core.game.node.entity.Entity; import core.game.node.entity.impl.Animator.Priority; import core.game.node.entity.player.Player; -import core.game.node.entity.state.EntityState; import core.game.system.task.NodeTask; import core.game.world.update.flag.context.Animation; import core.tools.RandomFunction; +import static core.api.ContentAPIKt.*; + /** * The fire types. * @author Emperor @@ -47,7 +48,7 @@ public enum FireType { TOXIC_BREATH(new Animation(82, Priority.HIGH), 394, new NodeTask(0) { @Override public boolean exec(Node node, Node... n) { - ((Entity) node).getStateManager().register(EntityState.POISONED, false, 80, (Entity) n[0]); + applyPoison ((Entity) node, (Entity) n[0], 40); return true; } }), @@ -58,7 +59,7 @@ public enum FireType { ICY_BREATH(new Animation(83, Priority.HIGH), 395, new NodeTask(0) { @Override public boolean exec(Node node, Node... n) { - ((Entity) node).getStateManager().set(EntityState.FROZEN, 7); + registerTimer((Entity) node, spawnTimer ("frozen", 7, true)); return true; } }); @@ -112,4 +113,4 @@ public enum FireType { return task; } -} \ No newline at end of file +} diff --git a/Server/src/main/core/game/node/entity/combat/equipment/RangeWeapon.java b/Server/src/main/core/game/node/entity/combat/equipment/RangeWeapon.java index e82601410..13265303a 100644 --- a/Server/src/main/core/game/node/entity/combat/equipment/RangeWeapon.java +++ b/Server/src/main/core/game/node/entity/combat/equipment/RangeWeapon.java @@ -95,7 +95,7 @@ public final class RangeWeapon { return false; } NodeList nodeList = doc.getDocumentElement().getChildNodes(); - System.out.println("Loading range weapon info..."); + for (short i = 1; i < nodeList.getLength(); i += 2) { Node n = nodeList.item(i); if (n != null) { @@ -131,7 +131,7 @@ public final class RangeWeapon { } } } - System.out.println("Loaded " + RANGE_WEAPONS.size() + " range weapon definitions."); + return true; } diff --git a/Server/src/main/core/game/node/entity/combat/equipment/WeaponInterface.java b/Server/src/main/core/game/node/entity/combat/equipment/WeaponInterface.java index b9e0de4c0..6a38729fe 100644 --- a/Server/src/main/core/game/node/entity/combat/equipment/WeaponInterface.java +++ b/Server/src/main/core/game/node/entity/combat/equipment/WeaponInterface.java @@ -20,6 +20,9 @@ import core.net.packet.out.StringPacket; import org.rs09.consts.Components; import core.game.system.config.ItemConfigParser; +import static core.api.ContentAPIKt.*; + + /** * Represents the weapon interface component. @@ -43,10 +46,20 @@ public final class WeaponInterface extends Component { private static final int[] SLAYER_STAFF_SPELL_IDS = { 22, 31, 45, 48, 52, 55 }; /** - * The void staff spell ids + * The void and guthix staff spell ids */ private static final int[] VOID_STAFF_SPELL_IDS = { 22, 42, 45, 48, 52, 55 }; + /** + * The saradomin staff spell ids + */ + private static final int[] SARADOMIN_STAFF_SPELL_IDS = { 22, 41, 45, 48, 52, 55 }; + + /** + * The zamorak staff spell ids + */ + private static final int[] ZAMORAK_STAFF_SPELL_IDS = { 22, 43, 45, 48, 52, 55 }; + /** * The default attack animations. */ @@ -318,9 +331,9 @@ public final class WeaponInterface extends Component { return; } boolean defensive = slot == 3; - player.getConfigManager().set(439, defensive ? -5 : 0); + setVarp(player, 439, defensive ? -5 : 0); if (slot > 2) { - player.getConfigManager().set(43, defensive ? -1 : 3); + setVarp(player, 43, defensive ? -1 : 3); } }; @@ -330,13 +343,23 @@ public final class WeaponInterface extends Component { * @return The component id for the autocast select tab. */ public int getAutospellId(int spellId) { - boolean modern = player.getSpellBookManager().getSpellBook() == Components.MAGIC_192; - int[] data = modern ? MODERN_SPELL_IDS : ANCIENT_SPELL_IDS; - if (modern && player.getEquipment().getNew(3).getName().equalsIgnoreCase("Slayer's staff")) { + //boolean modern = player.getSpellBookManager().getSpellBook() == Components.MAGIC_192; + boolean modern = !player.getEquipment().getNew(3).getName().equalsIgnoreCase("Ancient Staff") && !player.getEquipment().getNew(3).getName().contains("uriel's staff"); + int[] data = MODERN_SPELL_IDS; + if (player.getEquipment().getNew(3).getName().equalsIgnoreCase("Ancient Staff") || player.getEquipment().getNew(3).getName().contains("uriel's staff")) { + data = ANCIENT_SPELL_IDS; + } + if (player.getEquipment().getNew(3).getName().equalsIgnoreCase("Slayer's staff")) { data = SLAYER_STAFF_SPELL_IDS; } - if (modern && player.getEquipment().getNew(3).getName().equalsIgnoreCase("Void knight mace")) { + if (player.getEquipment().getNew(3).getName().equalsIgnoreCase("Void knight mace") || player.getEquipment().getNew(3).getName().equalsIgnoreCase("Guthix Staff")) { data = VOID_STAFF_SPELL_IDS; + } + if (player.getEquipment().getNew(3).getName().equalsIgnoreCase("Saradomin Staff")) { + data = SARADOMIN_STAFF_SPELL_IDS; + } + if (player.getEquipment().getNew(3).getName().equalsIgnoreCase("Zamorak Staff")) { + data = ZAMORAK_STAFF_SPELL_IDS; } for (int i = 0; i < data.length; i++) { if (data[i] == spellId) { @@ -352,14 +375,24 @@ public final class WeaponInterface extends Component { * @param adjustAttackStyle If the attack style should be adjusted. */ public void selectAutoSpell(int buttonId, boolean adjustAttackStyle) { - boolean modern = player.getSpellBookManager().getSpellBook() == Components.MAGIC_192; - int[] data = modern ? MODERN_SPELL_IDS : ANCIENT_SPELL_IDS; - if (modern && player.getEquipment().getNew(3).getName().equalsIgnoreCase("Slayer's staff")) { + //boolean modern = player.getSpellBookManager().getSpellBook() == Components.MAGIC_192; + boolean modern = !player.getEquipment().getNew(3).getName().equalsIgnoreCase("Ancient Staff") && !player.getEquipment().getNew(3).getName().contains("uriel's staff"); + int[] data = MODERN_SPELL_IDS; + if (player.getEquipment().getNew(3).getName().equalsIgnoreCase("Ancient staff") || player.getEquipment().getNew(3).getName().contains("uriel's staff")) { + data = ANCIENT_SPELL_IDS; + } + if (player.getEquipment().getNew(3).getName().equalsIgnoreCase("Slayer's staff")) { data = SLAYER_STAFF_SPELL_IDS; } - if (modern && player.getEquipment().getNew(3).getName().equalsIgnoreCase("Void knight mace")) { + if (player.getEquipment().getNew(3).getName().equalsIgnoreCase("Void knight mace") || modern && player.getEquipment().getNew(3).getName().equalsIgnoreCase("Guthix Staff")) { data = VOID_STAFF_SPELL_IDS; } + if (player.getEquipment().getNew(3).getName().equalsIgnoreCase("Saradomin Staff")) { + data = SARADOMIN_STAFF_SPELL_IDS; + } + if (player.getEquipment().getNew(3).getName().equalsIgnoreCase("Zamorak Staff")) { + data = ZAMORAK_STAFF_SPELL_IDS; + } CombatSpell current = player.getProperties().getAutocastSpell(); if (buttonId >= data.length) { return; @@ -452,15 +485,20 @@ public final class WeaponInterface extends Component { return; } player.setAttribute("autocast_select", true); - int id = player.getSpellBookManager().getSpellBook() == 193 ? 797 : 319; + //int id = player.getSpellBookManager().getSpellBook() == 193 ? 797 : 319; + int id = 319; + boolean ancient = player.getEquipment().getNew(3).getName().equalsIgnoreCase("Ancient staff") || player.getEquipment().getNew(3).getName().contains("uriel's staff"); boolean slayer = player.getEquipment().getNew(3).getName().equalsIgnoreCase("Slayer's staff"); - boolean mace = player.getEquipment().getNew(3).getName().equalsIgnoreCase("Void knight mace"); + boolean mace = player.getEquipment().getNew(3).getName().equalsIgnoreCase("Void knight mace") || player.getEquipment().getNew(3).getName().equalsIgnoreCase("Guthix Staff") || player.getEquipment().getNew(3).getName().equalsIgnoreCase("Saradomin Staff") || player.getEquipment().getNew(3).getName().equalsIgnoreCase("Zamorak Staff"); if (slayer) { id = 310; } if (mace) { id = 406; } + if (ancient) { + id = 797; + } Component component = new Component(id); component.getDefinition().setTabIndex(0); component.getDefinition().setType(InterfaceType.TAB); @@ -477,7 +515,7 @@ public final class WeaponInterface extends Component { if (current != WeaponInterfaces.STAFF) { return false; } - if (player.getSpellBookManager().getSpellBook() == SpellBookManager.SpellBook.LUNAR.getInterfaceId()) { + /* if (player.getSpellBookManager().getSpellBook() == SpellBookManager.SpellBook.LUNAR.getInterfaceId()) { if (message) { player.getPacketDispatch().sendMessage("You can't autocast Lunar magic."); } @@ -489,7 +527,7 @@ public final class WeaponInterface extends Component { player.getPacketDispatch().sendMessage("You can only autocast ancient magicks with an Ancient or Zuriel's staff."); } return false; - } + } */ return true; } @@ -729,4 +767,4 @@ public final class WeaponInterface extends Component { return specialBar; } -} \ No newline at end of file +} 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..2118ab7dc 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 @@ -49,7 +49,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 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 1bff66e63..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 @@ -6,7 +6,6 @@ import core.game.node.entity.combat.ImpactHandler import core.game.node.entity.player.Player import core.game.node.entity.player.info.Rights import core.game.node.entity.player.link.IronmanMode -import core.game.node.entity.player.link.audio.Audio import core.game.node.entity.skill.Skills import core.game.node.item.Item import core.game.system.task.Pulse @@ -18,12 +17,14 @@ 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 import core.game.world.repository.Repository import core.tools.secondsToTicks import core.tools.colorize +import org.rs09.consts.Sounds import java.util.Map import kotlin.math.min @@ -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) + } } } @@ -75,6 +74,7 @@ class GraveController : PersistWorld, TickListener, InteractionListener, Command when (grave.type) { in GraveType.SMALL_GS..GraveType.ANGEL_DEATH -> isGraniteBackground = true + else -> {} } if (isGraniteBackground) @@ -105,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 @@ -121,10 +127,9 @@ class GraveController : PersistWorld, TickListener, InteractionListener, Command player.skills.prayerPoints -= blessAmount setAttribute(g, "blessed", true) - playAudio(player, Audio(2674)) + 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.")) } @@ -154,7 +159,7 @@ class GraveController : PersistWorld, TickListener, InteractionListener, Command player.skills.prayerPoints -= restoreAmount setAttribute(g, "repaired", true) - playAudio(player, Audio(2674)) + playAudio(player, Sounds.PRAYER_RECHARGE_2674) animate(player, 645) return true } @@ -243,6 +248,7 @@ class GraveController : PersistWorld, TickListener, InteractionListener, Command fun serializeToServerStore() { val archive = ServerStore.getArchive("active-graves") + archive.clear() for ((uid,grave) in activeGraves) { val g = JSONObject() g["ticksRemaining"] = grave.ticksRemaining @@ -288,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..fb1da695c 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,21 +1,22 @@ 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) { - 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."), - ORNATE_GS(NPCs.GRAVESTONE_6574, 5000, 3, false, text = SMALL_GS.text), - FONT_OF_LIFE(NPCs.GRAVESTONE_6577, 50000, 4, true, text = "In your travels,
pause awhile to remember @name,
who passed away at this spot."), - STELE(NPCs.STELE_6580, 50000, 4, true, text = FONT_OF_LIFE.text), - 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."), - 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."); +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, 5, false, text = "In memory of @name,
who died here."), + FLAG(NPCs.GRAVE_MARKER_6568, 50, 10, false, text = MEM_PLAQUE.text), + SMALL_GS(NPCs.GRAVESTONE_6571, 500, 15, false, text = "In loving memory of our dear friend @name,
who died in this place @mins ago."), + ORNATE_GS(NPCs.GRAVESTONE_6574, 5000, 30, false, text = SMALL_GS.text), + FONT_OF_LIFE(NPCs.GRAVESTONE_6577, 50000, 45, true, text = "In your travels,
pause awhile to remember @name,
who passed away at this spot."), + STELE(NPCs.STELE_6580, 50000, 45, true, text = FONT_OF_LIFE.text), + SARA_SYMBOL(NPCs.SARADOMIN_SYMBOL_6583, 50000, 45, true, text = "@name,
an enlightened servant of Saradomin,
perished in this place."), + ZAM_SYMBOL(NPCs.ZAMORAK_SYMBOL_6586, 50000, 45, true, text = "@name,
a most bloodthirsty follower of Zamorak,
perished in this place."), + GUTH_SYMBOL(NPCs.GUTHIX_SYMBOL_6589, 50000, 45, true, text = "@name,
who walked with the Balance of Guthix,
perished in this place."), + BAND_SYMBOL(NPCs.BANDOS_SYMBOL_6592, 50000, 45, 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, 45, 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, 45, true, requiredQuest = Quests.DESERT_TREASURE, text = "@name,
servant of the Unknown Power,
perished in this place."), + ANGEL_DEATH(NPCs.MEMORIAL_STONE_6601, 500000, 60, 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 { val ids = values().fold(ArrayList()) {list, type -> 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 225855b11..0db21b192 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 @@ -15,10 +15,13 @@ import core.game.node.item.Item; import core.game.world.map.RegionManager; import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Graphics; +import org.rs09.consts.Sounds; import java.util.ArrayList; import java.util.List; +import static core.api.ContentAPIKt.playGlobalAudio; + /** * Represents a combat magic spell. * @author Emperor @@ -132,18 +135,13 @@ public abstract class CombatSpell extends MagicSpell { * @param state The battle state. */ public void visualizeImpact(Entity entity, Entity target, BattleState state) { - boolean combatVictim = target == entity.getProperties().getCombatPulse().getVictim(); if (state.getEstimatedHit() == -1) { - if (combatVictim) { - sendAudio(target, new Audio(227, 1, 20)); - } + playGlobalAudio(target.getLocation(), Sounds.SPELLFAIL_227, 20); target.graphics(SPLASH_GRAPHIC); return; } target.graphics(endGraphic); - if (combatVictim) { - sendAudio(target, new Audio(impactAudio, 1, 20)); - } + playGlobalAudio(target.getLocation(), impactAudio, 20); } @Override @@ -158,10 +156,20 @@ public abstract class CombatSpell extends MagicSpell { } if (entity.getProperties().getAutocastSpell() == this && (entity instanceof Player || animation == null)) { Player p = entity.asPlayer(); - if (p.getProperties().getAutocastSpell().getSpellId() == 31) { - entity.animate(new Animation(1576)); - } else { - entity.animate(AUTOCAST_ANIMATION); + switch(p.getProperties().getAutocastSpell().getSpellId()) { + case 31: + entity.animate(new Animation(1576, Priority.HIGH)); + break; + case 41: + case 42: + case 43: + entity.animate(new Animation(811, Priority.HIGH)); + break; + case 29: + entity.animate(new Animation(708, Priority.HIGH)); + break; + default: + entity.animate(AUTOCAST_ANIMATION); } } else { if (entity instanceof NPC) { @@ -175,7 +183,7 @@ public abstract class CombatSpell extends MagicSpell { entity.animate(animation); } } - sendAudio(entity); + playGlobalAudio(entity.getLocation(), audio.id, 20); } @Override diff --git a/Server/src/main/core/game/node/entity/combat/spell/MagicSpell.java b/Server/src/main/core/game/node/entity/combat/spell/MagicSpell.java index 53d017c75..aee3cbf05 100644 --- a/Server/src/main/core/game/node/entity/combat/spell/MagicSpell.java +++ b/Server/src/main/core/game/node/entity/combat/spell/MagicSpell.java @@ -1,6 +1,7 @@ package core.game.node.entity.combat.spell; import core.game.component.Component; +import core.game.event.SpellCastEvent; import core.game.node.entity.skill.Skills; import core.game.node.Node; import core.game.node.entity.Entity; @@ -11,8 +12,6 @@ import core.game.node.entity.player.link.SpellBookManager.SpellBook; import core.game.node.entity.player.link.audio.Audio; import core.game.node.item.Item; import core.game.world.GameWorld; -import core.game.world.map.MapDistance; -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.Plugin; @@ -21,6 +20,9 @@ import core.tools.RandomFunction; import java.util.ArrayList; import java.util.List; +import static core.api.ContentAPIKt.playGlobalAudio; +import static core.api.ContentAPIKt.inEquipmentOrInventory; + /** * Represents a magic spell. * @author Emperor @@ -131,6 +133,7 @@ public abstract class MagicSpell implements Plugin { if (p.getAttribute("magic-delay", 0) <= GameWorld.getTicks()) { p.setAttribute("magic-delay", GameWorld.getTicks() + spell.getDelay()); } + p.dispatch(new SpellCastEvent(book, spellId, target)); return true; } return false; @@ -158,7 +161,7 @@ public abstract class MagicSpell implements Plugin { public void visualize(Entity entity, Node target) { entity.graphics(graphic); entity.animate(animation); - sendAudio(entity); + playGlobalAudio(entity.getLocation(), audio.id, 20); } /** @@ -199,6 +202,7 @@ public abstract class MagicSpell implements Plugin { if (!checkLevelRequirement(caster, message)) { return false; } + /* Snowscape custom. Removing this allows guthix staff to autocast claws of guthix. if (caster instanceof Player) { CombatSpell spell = ((Player) caster).getProperties().getAutocastSpell(); if (spell != null) { @@ -210,20 +214,20 @@ public abstract class MagicSpell implements Plugin { } } } + */ + /* Snowscape custom: removing forced delay on binding spells. If possible, reimplement this for pvp scenarios if((spellId == 12 || spellId == 30 || spellId == 56) && caster instanceof Player){ if (caster.getAttribute("entangleDelay", 0) > GameWorld.getTicks()) { caster.asPlayer().sendMessage("You have recently cast a binding spell."); return false; } } + */ if (caster instanceof Player) { Player p = (Player) caster; - if(p.getEquipment().get(3) != null && p.getEquipment().get(3).getId() == 14726){ - if(RandomFunction.getRandom(100) < 13){ - p.sendMessage("Your staff negates the rune requirement of the spell."); - return true; - } - } + //if(p.getEquipment().get(3) != null && p.getEquipment().get(3).getId() == 14726){ + + //} if (runes == null) { return true; } @@ -233,9 +237,16 @@ public abstract class MagicSpell implements Plugin { return false; } } + //Snowscape custom: only consume runes 33% of the time + if(this instanceof CombatSpell && RandomFunction.getRandom(100) > 33){ + //p.sendMessage("Your staff negates the rune requirement of the spell."); + return true; + } if (remove) { toRemove.forEach(i -> { - p.getInventory().remove(i); + if (!p.getInventory().remove(i)){ + p.runeSatchel.remove(i); + } }); } return true; @@ -267,9 +278,55 @@ public abstract class MagicSpell implements Plugin { * @param message the message. * @return {@code True} if so. */ + // Snowscape modifications: replaced entire "hasrune" function to only check the inventory once per rune, so the rune satchel has less overhead. public boolean hasRune(Player p, Item item, List toRemove, boolean message) { if (!usingStaff(p, item.getId())) { - boolean hasBaseRune = p.getInventory().contains(item.getId(),item.getAmount()); + boolean hasBaseRune = p.getInventory().contains(item.getId(),item.getAmount()) || (inEquipmentOrInventory(p, 10882,1) && p.runeSatchel.contains(item.getId(),item.getAmount())); + if(!hasBaseRune){ + int baseAmt = Math.max(p.getInventory().getAmount(item.getId()),((inEquipmentOrInventory(p, 10882,1)) ? p.runeSatchel.getAmount(item.getId()) : 0)); + if(baseAmt > 0){ + toRemove.add(new Item(item.getId(),Math.min(baseAmt,item.getAmount()))); + } + int amtRemaining = item.getAmount() - baseAmt; + List possibleComboRunes = CombinationRune.eligibleFor(Runes.forId(item.getId())); + for(CombinationRune r : possibleComboRunes){ + // Check if this combination rune was already added for a different element + for(Item alreadyListed : toRemove){ + if (alreadyListed.getId() == r.id){ + amtRemaining -= alreadyListed.getAmount(); + } + } + + int amt = Math.max(p.getInventory().getAmount(r.id),((inEquipmentOrInventory(p, 10882,1)) ? p.runeSatchel.getAmount(r.id) : 0)); + if(amt > 0 && amtRemaining > 0){ + + if(amtRemaining < amt){ + toRemove.add(new Item(r.id,amtRemaining)); + amtRemaining = 0; + continue; + } + amtRemaining -= amt; + toRemove.add(new Item(r.id,amt)); + } + } + if(amtRemaining <= 0){ + return true; + } else { + p.getPacketDispatch().sendMessage("You don't have enough " + item.getName() + "s to cast this spell."); + return false; + } + } + toRemove.add(item); + return true; + } + return true; + } + +/* Snowscape: Original function left here for easier merging of updates. + public boolean hasRune(Player p, Item item, List toRemove, boolean message) { + if (!usingStaff(p, item.getId())) { + // Snowscape modification: check the rune satchel for the base runes if the player is carrying one + boolean hasBaseRune = p.getInventory().contains(item.getId(),item.getAmount()) || (inEquipmentOrInventory(p, 10882,1) && p.runeSatchel.contains(item.getId(),item.getAmount())); if(!hasBaseRune){ int baseAmt = p.getInventory().getAmount(item.getId()); if(baseAmt > 0){ @@ -301,6 +358,7 @@ public abstract class MagicSpell implements Plugin { } return true; } +*/ /** * Adds the experience for casting this spell. @@ -314,11 +372,12 @@ public abstract class MagicSpell implements Plugin { } entity.getSkills().addExperience(Skills.HITPOINTS, hit * 1.33, true); if (entity.getProperties().getAttackStyle().getStyle() == WeaponInterface.STYLE_DEFENSIVE_CAST) { - entity.getSkills().addExperience(Skills.DEFENCE, hit, true); - entity.getSkills().addExperience(Skills.MAGIC, 1.33 * hit, true); + double baseXpReward = (CombatSwingHandler.EXPERIENCE_MOD * hit) / 2.0; + entity.getSkills().addExperience(Skills.DEFENCE, baseXpReward, true); + entity.getSkills().addExperience(Skills.MAGIC, baseXpReward, true); return; } - entity.getSkills().addExperience(Skills.MAGIC, hit * (CombatSwingHandler.EXPERIENCE_MOD / 2), true); + entity.getSkills().addExperience(Skills.MAGIC, hit * (CombatSwingHandler.EXPERIENCE_MOD), true); } /** @@ -329,30 +388,6 @@ public abstract class MagicSpell implements Plugin { return level; } - /** - * Sends the Audio packet for all players to hear (in a distance specified - * by ). - * @param entity The entity from where this Audio comes from. - */ - public void sendAudio(Entity entity) { - sendAudio(entity, audio); - } - - /** - * Sends the Audio packet for all players to hear (in a distance specified - * by ). - * @param entity The entity from where this Audio comes from. - * @param audio The Audio to send. - */ - public void sendAudio(Entity entity, Audio audio) { - if (audio == null || audio.getId() < 0) { - return; - } - for (Player p : RegionManager.getLocalPlayers(entity, MapDistance.SOUND.getDistance())) { - p.getAudioManager().send(audio); - } - } - @Override public Object fireEvent(String identifier, Object... args) { return null; @@ -423,4 +458,4 @@ public abstract class MagicSpell implements Plugin { public int getLevel() { return level; } -} \ 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/combat/spell/SpellType.java b/Server/src/main/core/game/node/entity/combat/spell/SpellType.java index 508400c17..cd0fb91e8 100644 --- a/Server/src/main/core/game/node/entity/combat/spell/SpellType.java +++ b/Server/src/main/core/game/node/entity/combat/spell/SpellType.java @@ -5,9 +5,11 @@ import core.game.node.entity.skill.Skills; 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.state.EntityState; import core.game.node.item.Item; +import static core.api.ContentAPIKt.*; +import static core.api.SnowscapeAPIKt.*; + /** * Represents the spell types. * @author Emperor @@ -46,7 +48,12 @@ public enum SpellType { CRUMBLE_UNDEAD(1.2) { @Override public int getImpactAmount(Entity e, Entity victim, int base) { - return 15; // Hits as high as Earth blast + // The check in CrumbleUndead.cast() only fires when manual casting. Check again here so that no damage is dealt when autocasting. + if (isUndead(victim)) { + return 12 + (e.getSkills().getLevel(Skills.MAGIC) / 10); + } + ((Player) e).sendMessage("This spell only affects the undead."); + return 0; } }, @@ -107,10 +114,10 @@ public enum SpellType { BARRAGE(1.4) { @Override public int getImpactAmount(Entity e, Entity victim, int base) { - System.out.println("Attacker - >" + e.getName()); - System.out.println("Victim - >" + victim.getName()); - System.out.println("Accuracy Mod -> " + getAccuracyMod()); - System.out.println("Base Mod -> " + base); + + + + return 26 + base; } }, @@ -152,7 +159,7 @@ public enum SpellType { @Override public int getImpactAmount(Entity e, Entity victim, int base) { if(!(e instanceof Player)) return 20; - if (e.asPlayer().hasActiveState("godcharge")) { + if (hasTimerActive(e, "magic:spellcharge")) { Item cape = ((Player) e).getEquipment().getNew(EquipmentContainer.SLOT_CAPE); if (cape.getId() == 2412 || cape.getId() == 2413 || cape.getId() == 2414) { return 30; @@ -252,4 +259,4 @@ public enum SpellType { return accuracyMod; } -} \ No newline at end of file +} 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 f52c8dc14..3afafb41b 100644 --- a/Server/src/main/core/game/node/entity/impl/Animator.java +++ b/Server/src/main/core/game/node/entity/impl/Animator.java @@ -1,14 +1,12 @@ 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; -import core.game.world.update.flag.npc.NPCAnimation; -import core.game.world.update.flag.npc.NPCGraphic; -import core.game.world.update.flag.player.AnimationFlag; -import core.game.world.update.flag.player.GraphicFlag; +import core.game.world.update.flag.EntityFlag; /** * Handles the animating of an Entity. @@ -121,13 +119,18 @@ public final class Animator { animation.setId(-1); } this.animation = animation; - ticks = GameWorld.getTicks() + animation.getDuration(); - entity.getUpdateMasks().register(entity instanceof NPC ? new NPCAnimation(animation) : new AnimationFlag(animation)); + if (animation.getId() != -1) { + ticks = GameWorld.getTicks() + animation.getDuration(); + } else { + ticks = 0; + } + entity.clocks[Clocks.getANIMATION_END()] = ticks; + entity.getUpdateMasks().register(EntityFlag.Animate, animation); priority = animation.getPriority(); } if (graphic != null) { this.graphics = graphic; - entity.getUpdateMasks().register(entity instanceof NPC ? new NPCGraphic(graphic) : new GraphicFlag(graphic)); + entity.getUpdateMasks().register(EntityFlag.SpotAnim, graphic); } return true; } @@ -151,6 +154,8 @@ public final class Animator { */ public void reset() { animate(RESET_A); + entity.clocks[Clocks.getANIMATION_END()] = 0; + ticks = 0; } /** @@ -158,7 +163,7 @@ public final class Animator { * @return {@code True} if so. */ public boolean isAnimating() { - return animation != null && ticks > GameWorld.getTicks(); + return animation != null && animation.getId() != -1 && ticks > GameWorld.getTicks(); } /** @@ -204,4 +209,4 @@ public final class Animator { public void setPriority(Priority priority) { this.priority = priority; } -} \ No newline at end of file +} diff --git a/Server/src/main/core/game/node/entity/impl/ForceMovement.java b/Server/src/main/core/game/node/entity/impl/ForceMovement.java index e3ef1eb1e..8cfc7efd6 100644 --- a/Server/src/main/core/game/node/entity/impl/ForceMovement.java +++ b/Server/src/main/core/game/node/entity/impl/ForceMovement.java @@ -6,8 +6,8 @@ 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.player.ForceMovementFlag; +import core.game.world.update.flag.context.*; +import core.game.world.update.flag.*; /** * The force movement handler. @@ -91,8 +91,9 @@ public class ForceMovement extends Pulse { * @param commenceSpeed The commencing speed. * @param pathSpeed The path speed. * @param unlockAfter Whether to unlock the entity after the ForceMovement completes - * + * @deprecated this is no longer the preferred way to use force movement. Use the ContentAPI forceMove method instead, please. */ + @Deprecated public ForceMovement(Entity e, Location start, Location destination, Animation startAnim, Animation animation, Direction direction, int commenceSpeed, int pathSpeed, boolean unlockAfter) { super(1, e); this.entity = e; @@ -113,11 +114,17 @@ public class ForceMovement extends Pulse { * @param end the destination. * @param animation the animation. * @param speed The path speed. + * @deprecated this is no longer the preferred way to use force movement. Use the ContentAPI forceMove method instead, please. */ + @Deprecated public ForceMovement(Entity e, Location start, Location end, Animation animation, int speed) { this(e, start, end, WALK_ANIMATION, animation, direction(start, end), WALKING_SPEED, speed, true); } + /** + * @deprecated this is no longer the preferred way to use force movement. Use the ContentAPI forceMove method instead, please. + */ + @Deprecated public ForceMovement(Entity e, Location destination, int startSpeed, int animSpeed){ this(e,e.getLocation(),destination,WALK_ANIMATION,WALK_ANIMATION,direction(e.getLocation(),destination),startSpeed,animSpeed, true); } @@ -128,7 +135,9 @@ public class ForceMovement extends Pulse { * @param start the start location. * @param destination the destination. * @param animation the animation. + * @deprecated this is no longer the preferred way to use force movement. Use the ContentAPI forceMove method instead, please. */ + @Deprecated public ForceMovement(Entity e, Location start, Location destination, Animation animation) { this(e, start, destination, WALK_ANIMATION, animation, direction(start, destination), WALKING_SPEED, WALKING_SPEED, true); } @@ -138,7 +147,9 @@ public class ForceMovement extends Pulse { * @param start the start loc. * @param destination the destination. * @param animation the animation. + * @deprecated this is no longer the preferred way to use force movement. Use the ContentAPI forceMove method instead, please. */ + @Deprecated public ForceMovement(Location start, Location destination, Animation animation) { this(null, start, destination, WALK_ANIMATION, animation, direction(start, destination), WALKING_SPEED, WALKING_SPEED, true); } @@ -148,7 +159,9 @@ public class ForceMovement extends Pulse { * @param e The entity. * @param destination The destination location. * @return The created ForceMovement object. + * @deprecated this is no longer the preferred way to use force movement. Use the ContentAPI forceMove method instead, please. */ + @Deprecated public static ForceMovement run(Entity e, Location destination) { return run(e, e.getLocation(), destination, WALK_ANIMATION, WALK_ANIMATION, direction(e.getLocation(), destination), WALKING_SPEED, WALKING_SPEED, true); } @@ -159,7 +172,9 @@ public class ForceMovement extends Pulse { * @param start The start location. * @param destination The destination location. * @return The created ForceMovement object. + * @deprecated this is no longer the preferred way to use force movement. Use the ContentAPI forceMove method instead, please. */ + @Deprecated public static ForceMovement run(Entity e, Location start, Location destination) { return run(e, start, destination, WALK_ANIMATION, WALK_ANIMATION, direction(e.getLocation(), destination), WALKING_SPEED, WALKING_SPEED, true); } @@ -171,7 +186,9 @@ public class ForceMovement extends Pulse { * @param destination The destination location. * @param animation The animation. * @return The created ForceMovement object. + * @deprecated this is no longer the preferred way to use force movement. Use the ContentAPI forceMove method instead, please. */ + @Deprecated public static ForceMovement run(Entity e, Location start, Location destination, Animation animation) { return run(e, start, destination, WALK_ANIMATION, animation, direction(start, destination), WALKING_SPEED, WALKING_SPEED, true); } @@ -184,7 +201,9 @@ public class ForceMovement extends Pulse { * @param animation The animation. * @param speed The path speed. * @return The created ForceMovement object. + * @deprecated this is no longer the preferred way to use force movement. Use the ContentAPI forceMove method instead, please. */ + @Deprecated public static ForceMovement run(Entity e, Location start, Location destination, Animation animation, int speed) { return run(e, start, destination, WALK_ANIMATION, animation, direction(start, destination), WALKING_SPEED, speed, true); } @@ -196,7 +215,9 @@ public class ForceMovement extends Pulse { * @param destination The destination location. * @param animation The animation. * @return The created ForceMovement object. + * @deprecated this is no longer the preferred way to use force movement. Use the ContentAPI forceMove method instead, please. */ + @Deprecated public static ForceMovement run(Entity e, Location start, Location destination, Animation startAnim, Animation animation) { return run(e, start, destination, startAnim, animation, direction(start, destination), WALKING_SPEED, WALKING_SPEED, true); } @@ -209,7 +230,9 @@ public class ForceMovement extends Pulse { * @param animation The animation. * @param direction The direction. * @return The created ForceMovement object. + * @deprecated this is no longer the preferred way to use force movement. Use the ContentAPI forceMove method instead, please. */ + @Deprecated public static ForceMovement run(Entity e, Location start, Location destination, Animation startAnim, Animation animation, Direction direction) { return run(e, start, destination, startAnim, animation, direction, WALKING_SPEED, WALKING_SPEED, true); } @@ -223,11 +246,17 @@ public class ForceMovement extends Pulse { * @param direction The direction. * @param pathSpeed The speed (in ticks). * @return The created ForceMovement object. + * @deprecated this is no longer the preferred way to use force movement. Use the ContentAPI forceMove method instead, please. */ + @Deprecated public static ForceMovement run(Entity e, Location start, Location destination, Animation startAnim, Animation animation, Direction direction, int pathSpeed) { return run(e, start, destination, startAnim, animation, direction, WALKING_SPEED, pathSpeed, true); } + /** + * @deprecated this is no longer the preferred way to use force movement. Use the ContentAPI forceMove method instead, please. + */ + @Deprecated public static ForceMovement run(Entity e, Location start, Location destination, Animation startAnim, Animation animation, Direction direction, int commenceSpeed, int pathSpeed) { return run(e, start, destination, startAnim, animation, direction, commenceSpeed, pathSpeed, true); } @@ -240,7 +269,9 @@ public class ForceMovement extends Pulse { * @param animation The animation. * @param direction The direction. * @return The created ForceMovement object. + * @deprecated this is no longer the preferred way to use force movement. Use the ContentAPI forceMove method instead, please. */ + @Deprecated public static ForceMovement run(Entity e, Location start, Location destination, Animation startAnim, Animation animation, Direction direction, int commenceSpeed, int pathSpeed, boolean unlockAfter) { if (startAnim != null) { startAnim.setPriority(Animator.Priority.VERY_HIGH); @@ -254,7 +285,10 @@ public class ForceMovement extends Pulse { GameWorld.getPulser().submit(fm); return fm; } - + /* + * @deprecated this is no longer the preferred way to use force movement. Use the ContentAPI forceMove method instead, please. + */ + @Deprecated public static ForceMovement run(Entity e, Location destination, int commenceSpeed, int pathSpeed){ return run(e,e.getLocation(),destination,WALK_ANIMATION,WALK_ANIMATION,direction(e.getLocation(),destination),commenceSpeed,pathSpeed, true); } @@ -262,7 +296,9 @@ public class ForceMovement extends Pulse { /** * Method used to run the force movement. * @param e the entity. + * @deprecated this is no longer the preferred way to use force movement. Use the ContentAPI forceMove method instead, please. */ + @Deprecated public void run(final Entity e, final int speed) { this.entity = e; int commence = (int) start.getDistance(e.getLocation()); @@ -321,7 +357,7 @@ public class ForceMovement extends Pulse { } int ticks = 1 + commenceSpeed + pathSpeed; entity.getImpactHandler().setDisabledTicks(ticks); - entity.getUpdateMasks().register(new ForceMovementFlag(this)); + entity.getUpdateMasks().register(EntityFlag.ForceMove, new ForceMoveCtx(start, destination, commenceSpeed * 30, pathSpeed * 30, direction)); if(entity instanceof Player) { entity.getWalkingQueue().updateRegion(destination, false); } diff --git a/Server/src/main/core/game/node/entity/impl/Properties.java b/Server/src/main/core/game/node/entity/impl/Properties.java index ab6044614..78e4f8807 100644 --- a/Server/src/main/core/game/node/entity/impl/Properties.java +++ b/Server/src/main/core/game/node/entity/impl/Properties.java @@ -10,7 +10,9 @@ import core.game.node.entity.combat.equipment.WeaponInterface; 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.game.world.map.Location; +import core.game.world.update.flag.EntityFlag; import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Graphics; import core.game.node.entity.combat.CombatPulse; @@ -31,7 +33,7 @@ public final class Properties { /** * The entity's combat pulse. */ - private final CombatPulse combatPulse; + private CombatPulse combatPulse; /** * If the entity is retaliating. @@ -231,8 +233,12 @@ public final class Properties { public int getCurrentCombatLevel() { if (entity instanceof Player) { Player player = (Player) entity; - if (player.getFamiliarManager().isUsingSummoning() || !player.getSkullManager().isWilderness()) { - return player.getFamiliarManager().getSummoningCombatLevel() + combatLevel; + if ((GameWorld.getSettings().isPvp() || (GameWorld.getSettings().getWild_pvp_enabled() && player.getSkullManager().isWilderness())) + && !player.getFamiliarManager().isUsingSummoning()) { + //TODO: Split combat levels should also be used for Bounty Hunter + return combatLevel; + } else { + return combatLevel + player.getFamiliarManager().getSummoningCombatLevel(); } } return combatLevel; @@ -326,6 +332,14 @@ public final class Properties { return combatPulse; } + /** + * Sets the combatPulse. + * @return The void. + */ + public void setCombatPulse(CombatPulse combatPulse) { + this.combatPulse = (CombatPulse)combatPulse; + } + /** * Gets the retaliating. * @return The retaliating. @@ -565,4 +579,4 @@ public final class Properties { this.protectStyle = protectStyle; } -} \ No newline at end of file +} diff --git a/Server/src/main/core/game/node/entity/impl/WalkingQueue.java b/Server/src/main/core/game/node/entity/impl/WalkingQueue.java index 5b81e2aef..f20768b70 100644 --- a/Server/src/main/core/game/node/entity/impl/WalkingQueue.java +++ b/Server/src/main/core/game/node/entity/impl/WalkingQueue.java @@ -4,13 +4,21 @@ import content.global.skill.skillcapeperks.SkillcapePerks; 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.node.item.GroundItem; 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.update.flag.chunk.ItemUpdateFlag; +import core.tools.Log; +import core.tools.SystemLogger; import java.util.Deque; -import java.util.LinkedList; +import java.util.ArrayDeque; +import java.util.ArrayList; + +import static core.api.ContentAPIKt.*; /** * The walking queue. @@ -21,7 +29,7 @@ public final class WalkingQueue { /** * The walking queue. */ - private final Deque walkingQueue = new LinkedList(); + private final Deque walkingQueue = new ArrayDeque(); /** * The entity. @@ -54,6 +62,8 @@ public final class WalkingQueue { */ private Location footPrint; + public ArrayList routeItems = new ArrayList(); + /** * Constructs a new {@code WalkingQueue} {@code Object}. * @param entity The entity. @@ -79,9 +89,20 @@ public final class WalkingQueue { if (isPlayer && updateRegion(entity.getLocation(), true)) { return; } + if (hasTimerActive(entity, "frozen")) + return; Point point = walkingQueue.poll(); + boolean drawPath = entity.getAttribute("routedraw", false); if (point == null) { updateRunEnergy(false); + if (isPlayer && drawPath) { + for (GroundItem item : routeItems) { + if (item != null) { + RegionManager.getRegionPlane(item.getLocation()).remove(item); + } + } + routeItems.clear(); + } return; } if (isPlayer && ((Player) entity).getSettings().getRunEnergy() < 1.0) { @@ -169,6 +190,12 @@ public final class WalkingQueue { if (player.getSettings().getWeight() > 0.0) { rate *= 1 + (player.getSettings().getWeight() / 100); } + if (hasTimerActive(player, "hamstrung")) { + rate *= 4; + } + //Snowscape custom: gain agility exp when running. getLevel returns int so we convert to double for the math + double experience = (Double.valueOf(player.getSkills().getLevel(Skills.AGILITY)) + 10)/50; + player.getSkills().addExperience(Skills.AGILITY, experience); return rate; } @@ -296,6 +323,13 @@ public final class WalkingQueue { if (point == null) { return; } + boolean drawRoute = entity.getAttribute("routedraw", false); + if (drawRoute && entity instanceof Player) { + Player p = (Player) entity; + GroundItem item = new GroundItem(new Item(13444), Location.create(x, y, p.getLocation().getZ()), p); + routeItems.add (item); + RegionManager.getRegionPlane(item.getLocation()).add(item); + } int diffX = x - point.getX(), diffY = y - point.getY(); int max = Math.max(Math.abs(diffX), Math.abs(diffY)); for (int i = 0; i < max; i++) { @@ -337,6 +371,7 @@ public final class WalkingQueue { * {@code false} if not. */ public boolean isRunningBoth() { + if (isRunDisabled()) return false; if (entity instanceof Player && ((Player) entity).getSettings().isRunToggled()) { return true; } @@ -374,7 +409,7 @@ public final class WalkingQueue { Location loc = entity.getLocation(); if (loc == null) { - throw new IllegalStateException( + log(this.getClass(), Log.ERR, "The entity location provided was null." + "Are you sure anything down the stack trace isn't providing an NPC with a null location?" ); diff --git a/Server/src/main/core/game/node/entity/lock/ActionLocks.java b/Server/src/main/core/game/node/entity/lock/ActionLocks.java index af76575a1..e8a816a88 100644 --- a/Server/src/main/core/game/node/entity/lock/ActionLocks.java +++ b/Server/src/main/core/game/node/entity/lock/ActionLocks.java @@ -64,7 +64,6 @@ public final class ActionLocks { */ public void lock(int ticks) { lockMovement(ticks); - lockTeleport(ticks); lockInteractions(ticks); } @@ -73,7 +72,6 @@ public final class ActionLocks { */ public void unlock() { unlockMovement(); - unlockTeleport(); unlockInteraction(); } diff --git a/Server/src/main/core/game/node/entity/npc/AbstractNPC.java b/Server/src/main/core/game/node/entity/npc/AbstractNPC.java index b0731b45b..b98656677 100644 --- a/Server/src/main/core/game/node/entity/npc/AbstractNPC.java +++ b/Server/src/main/core/game/node/entity/npc/AbstractNPC.java @@ -1,5 +1,6 @@ package core.game.node.entity.npc; +import core.tools.Log; import core.tools.SystemLogger; import core.game.world.map.Location; import core.plugin.Plugin; @@ -7,6 +8,8 @@ import core.plugin.Plugin; import java.util.HashMap; import java.util.Map; +import static core.api.ContentAPIKt.log; + /** * Used as superclass for "special NPC" plugins. * @author Emperor @@ -38,32 +41,13 @@ public abstract class AbstractNPC extends NPC implements Plugin { super.setWalks(autowalk); } - /** - * Configures default boss data. - */ - public void configureBossData() { - getProperties().setNPCWalkable(true); - getProperties().setCombatTimeOut(2); - if (!isAggressive()) { - setAggressive(true); - setDefaultBehavior(); - } - if (getAggressiveHandler() != null) { - getAggressiveHandler().setChanceRatio(6); - getAggressiveHandler().setAllowTolerance(false); - getAggressiveHandler().setTargetSwitching(true); - getAggressiveHandler().setRadius(64); - } - walkRadius = 64; - } - @Override public Plugin newInstance(Object arg) throws Throwable { for (int id : getIds()) { if (mapping.containsKey(id)) { String name = mapping.get(id).getClass().getSimpleName(); if (name != getClass().getSimpleName()) { - SystemLogger.logErr(this.getClass(), "[" + getClass().getSimpleName() + "] - Warning: Mapping already contained NPC id " + id + "! (" + name + ")"); + log(this.getClass(), Log.ERR, "[" + getClass().getSimpleName() + "] - Warning: Mapping already contained NPC id " + id + "! (" + name + ")"); continue; } } @@ -101,4 +85,4 @@ public abstract class AbstractNPC extends NPC implements Plugin { return mapping.get(npcId); } -} \ No newline at end of file +} diff --git a/Server/src/main/core/game/node/entity/npc/IdleAbstractNPC.java b/Server/src/main/core/game/node/entity/npc/IdleAbstractNPC.java index 7612724ba..a2047b8b5 100644 --- a/Server/src/main/core/game/node/entity/npc/IdleAbstractNPC.java +++ b/Server/src/main/core/game/node/entity/npc/IdleAbstractNPC.java @@ -61,7 +61,7 @@ public abstract class IdleAbstractNPC extends AbstractNPC { if (isInvisible()) { return false; } - if (getTask() != null && entity instanceof Player && getTask().levelReq > entity.getSkills().getStaticLevel(Skills.SLAYER)) { + if (getTask() != null && entity instanceof Player && getTask().levelReq > entity.getSkills().getLevel(Skills.SLAYER)) { if(message) { ((Player) entity).getPacketDispatch().sendMessage("You need a higher slayer level to know how to wound this monster."); } 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 82d9fd4c9..b1d4aba99 100644 --- a/Server/src/main/core/game/node/entity/npc/NPC.java +++ b/Server/src/main/core/game/node/entity/npc/NPC.java @@ -3,7 +3,7 @@ package core.game.node.entity.npc; import core.game.event.NPCKillEvent; import core.cache.def.impl.NPCDefinition; import core.game.dialogue.DialoguePlugin; -import core.game.interaction.Interaction; +import core.game.interaction.InteractPlugin; import core.game.interaction.MovementPulse; import core.game.node.entity.Entity; import core.game.node.entity.combat.BattleState; @@ -26,12 +26,10 @@ import core.game.world.map.build.DynamicRegion; import core.game.world.map.path.Pathfinder; import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Graphics; -import core.game.world.update.flag.npc.NPCFaceEntity; -import core.game.world.update.flag.npc.NPCFaceLocation; -import core.game.world.update.flag.npc.NPCForceChat; -import core.game.world.update.flag.npc.NPCSwitchId; +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; import core.game.system.config.NPCConfigParser; @@ -142,6 +140,10 @@ public class NPC extends Entity { */ private String forceTalk; + public NPCBehavior behavior; + + public boolean isRespawning = false; + /** * Constructs a new {@code NPC} {@code Object}. * @param id The NPC id. @@ -162,7 +164,8 @@ public class NPC extends Entity { this.definition = NPCDefinition.forId(id); super.size = definition.size; super.direction = direction; - super.interaction = new Interaction(this); + super.interactPlugin = new InteractPlugin(this); + this.behavior = NPCBehavior.forId(id); } /** @@ -184,6 +187,7 @@ public class NPC extends Entity { */ public static NPC create(int id, Location location, Direction direction, Object... objects) { NPC n = AbstractNPC.forId(id); + if (n != null) { n = ((AbstractNPC) n).construct(id, location, objects); } @@ -213,18 +217,22 @@ public class NPC extends Entity { if (getViewport().getRegion().isActive()) { Repository.addRenderableNPC(this); } - interaction.setDefault(); + interactPlugin.setDefault(); configure(); setDefaultBehavior(); if (definition.childNPCIds != null) { children = new NPC[definition.childNPCIds.length]; for (int i = 0; i < children.length; i++) { NPC npc = children[i] = new NPC(definition.childNPCIds[i]); - npc.interaction.setDefault(); + npc.interactPlugin.setDefault(); npc.index = index; npc.size = size; } } + behavior.onCreation(this); + // FIXME: hack around MovementPulse's constructor getting run while behavior is null when behavior is set between NPC constructor and init. + // FIXME: Commented out as a fix. most npcs were not being able to attack with range/magic due to setting the combat pulse. + // getProperties().setCombatPulse(new CombatPulse(this)); } @Override @@ -233,9 +241,29 @@ public class NPC extends Entity { Repository.removeRenderableNPC(this); Repository.getNpcs().remove(this); getViewport().setCurrentPlane(null); + behavior.onRemoval(this); // getViewport().setRegion(null); } + /** + * Configures default boss data. + */ + public void configureBossData() { + getProperties().setNPCWalkable(true); + getProperties().setCombatTimeOut(2); + if (!isAggressive()) { + setAggressive(true); + setDefaultBehavior(); + } + if (getAggressiveHandler() != null) { + getAggressiveHandler().setChanceRatio(6); + getAggressiveHandler().setAllowTolerance(false); + getAggressiveHandler().setTargetSwitching(true); + getAggressiveHandler().setRadius(64); + } + walkRadius = 64; + } + /** * Initializes the configurations. */ @@ -360,21 +388,24 @@ public class NPC extends Entity { public void checkImpact(BattleState state) { super.checkImpact(state); Entity entity = state.getAttacker(); - if (task != null && entity instanceof Player && task.levelReq > entity.getSkills().getStaticLevel(Skills.SLAYER)) { + behavior.beforeDamageReceived(this, entity, state); + if (task != null && entity instanceof Player && task.levelReq > entity.getSkills().getLevel(Skills.SLAYER)) { state.neutralizeHits(); } } @Override public boolean isAttackable(Entity entity, CombatStyle style, boolean message) { - if ((entity instanceof Player && !definition.hasAction("attack")) || isInvisible()) { + if (isInvisible()) { return false; } - if (task != null && entity instanceof Player && task.levelReq > entity.getSkills().getStaticLevel(Skills.SLAYER)) { + if (task != null && entity instanceof Player && task.levelReq > entity.getSkills().getLevel(Skills.SLAYER)) { if(message) { ((Player) entity).getPacketDispatch().sendMessage("You need a higher slayer level to know how to wound this monster."); } } + if (!behavior.canBeAttackedBy(this, entity, style, message)) + return false; return super.isAttackable(entity, style, message); } @@ -394,8 +425,11 @@ public class NPC extends Entity { teleport(getProperties().getSpawnLocation()); return; } - if (respawnTick == GameWorld.getTicks()) { + if (isRespawning && respawnTick <= GameWorld.getTicks()) { + behavior.onRespawn(this); onRespawn(); + fullRestore(); + isRespawning = false; } handleTickActions(); super.tick(); @@ -410,15 +444,21 @@ public class NPC extends Entity { * Handles the automatic actions of the NPC. */ public void handleTickActions() { + if (!behavior.tick(this)) + return; if (!getLocks().isInteractionLocked()) { if (!getLocks().isMovementLocked()) { if ( !pathBoundMovement && walkRadius > 0 + && walkRadius <= 20 && !getLocation().withinDistance(getProperties().getSpawnLocation(), (int)(walkRadius * 1.5)) && !getAttribute("no-spawn-return", false) ) { + MovementPulse current = getAttribute("return-to-spawn-pulse"); + if (current != null && current.isRunning()) return; + if(!isNeverWalks()){ if(walkRadius == 0) walkRadius = 3; @@ -430,7 +470,8 @@ public class NPC extends Entity { getLocks().lockMovement(100); getImpactHandler().setDisabledTicks(100); setAttribute("return-to-spawn", true); - GameWorld.getPulser().submit(new MovementPulse(this, getProperties().getSpawnLocation(), Pathfinder.SMART) { + + MovementPulse returnPulse = new MovementPulse(this, getProperties().getSpawnLocation(), Pathfinder.SMART) { @Override public boolean pulse() { getProperties().getCombatPulse().stop(); @@ -438,9 +479,13 @@ public class NPC extends Entity { fullRestore(); getImpactHandler().setDisabledTicks(0); removeAttribute("return-to-spawn"); + removeAttribute("return-to-spawn-pulse"); return true; } - }); + }; + + setAttribute("return-to-spawn-pulse", returnPulse); + GameWorld.getPulser().submit(returnPulse); return; } if (dialoguePlayer == null || !dialoguePlayer.isActive() || !dialoguePlayer.getInterfaceManager().hasChatbox()) { @@ -477,6 +522,9 @@ public class NPC extends Entity { return false; } + public int getNextWalk() { + return nextWalk; + } /** * Sets the next walk. */ @@ -496,6 +544,14 @@ public class NPC extends Entity { getWalkingQueue().reset(); getPulseManager().clear(); getUpdateMasks().reset(); + if (getAttribute("return-to-spawn", false)) { + this.location = getProperties().getSpawnLocation(); + MovementPulse returnPulse = getAttribute("return-to-spawn-pulse"); + if (returnPulse != null) { + returnPulse.pulse(); + returnPulse.stop(); + } + } Repository.removeRenderableNPC(this); if (getViewport().getRegion() instanceof DynamicRegion) { clear(); @@ -513,16 +569,27 @@ public class NPC extends Entity { if (getZoneMonitor().handleDeath(killer)) { return; } - setRespawnTick(GameWorld.getTicks() + definition.getConfiguration(NPCConfigParser.RESPAWN_DELAY, 17)); 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; + behavior.onDeathFinished(this, killer); killer.dispatch(new NPCKillEvent(this)); + setRespawnTick(GameWorld.getTicks() + definition.getConfiguration(NPCConfigParser.RESPAWN_DELAY, 17)); + } + + public void setRespawnTicks (int ticks) { + definition.getHandlers().put(NPCConfigParser.RESPAWN_DELAY, ticks); + } + + @Override + public void commenceDeath(Entity killer) { + behavior.onDeathStarted(this, killer); } /** @@ -553,34 +620,10 @@ public class NPC extends Entity { super.reset(); } - @Override - public boolean face(Entity entity) { - if (entity == null) { - if (getUpdateMasks().unregisterSynced(NPCFaceEntity.getOrdinal())) { - return getUpdateMasks().register(new NPCFaceEntity(null)); - } - return true; - } - return getUpdateMasks().register(new NPCFaceEntity(entity), true); - } - - @Override - public boolean faceLocation(Location location) { - if (location == null) { - getUpdateMasks().unregisterSynced(NPCFaceLocation.getOrdinal()); - return true; - } - return getUpdateMasks().register(new NPCFaceLocation(location), true); - } - - @Override - public boolean sendChat(String string) { - return getUpdateMasks().register(new NPCForceChat(string)); - } - @Override public CombatSwingHandler getSwingHandler(boolean swing) { - return getProperties().getCombatPulse().getStyle().getSwingHandler(); + CombatSwingHandler original = getProperties().getCombatPulse().getStyle().getSwingHandler(); + return behavior.getSwingHandlerOverride(this, original); } /** @@ -592,7 +635,7 @@ public class NPC extends Entity { int[] audios = getDefinition().getConfiguration(NPCConfigParser.COMBAT_AUDIO, null); if (audios != null) { Audio audio = new Audio(audios[index]); - if (audio != null && audio.getId() != 0) { + if (audio != null && audio.id != 0) { return audio; } } @@ -671,14 +714,15 @@ public class NPC extends Entity { this.definition = NPCDefinition.forId(id); super.name = definition.getName(); super.size = definition.size; - super.interaction = new Interaction(this); + super.interactPlugin = new InteractPlugin(this); initConfig(); configure(); - interaction.setDefault(); + interactPlugin.setDefault(); if (id == originalId) { - getUpdateMasks().unregisterSynced(NPCSwitchId.getOrdinal()); + int ordinal = EntityFlags.getOrdinal (EFlagType.NPC, EntityFlag.TypeSwap); + getUpdateMasks().unregisterSynced(ordinal); } - getUpdateMasks().register(new NPCSwitchId(id), id != originalId); + getUpdateMasks().register(EntityFlag.TypeSwap, id, id != originalId); return this; } @@ -700,16 +744,11 @@ public class NPC extends Entity { if (!pathBoundMovement || movementPath == null || movementPath.length < 1) { Location returnToSpawnLocation = getProperties().getSpawnLocation().transform(-5 + RandomFunction.random(getWalkRadius()), -5 + RandomFunction.random(getWalkRadius()), 0); int dist = (int) Location.getDistance(location, returnToSpawnLocation); - int pathLimit = 15; - if(dist > pathLimit || dist < -pathLimit) - { - int diffX = returnToSpawnLocation.getX() - location.getX(); - int diffY = returnToSpawnLocation.getY() - location.getY(); - returnToSpawnLocation = location.transform( - Math.min(Math.max(-pathLimit, diffX), pathLimit), //Try to path no more than pathLimit and no less than -pathLimit tiles away in the X and Y directions - Math.min(Math.max(-pathLimit, diffY), pathLimit), - 0); - } + int pathLimit = 14; + if (dist > pathLimit) { + Vector normalizedDir = Vector.betweenLocs(this.location, returnToSpawnLocation).normalized(); + returnToSpawnLocation = this.location.transform (normalizedDir.times(pathLimit)); + } return returnToSpawnLocation; } Location l = movementPath[movementIndex++]; diff --git a/Server/src/main/core/game/node/entity/npc/NPCBehavior.kt b/Server/src/main/core/game/node/entity/npc/NPCBehavior.kt new file mode 100644 index 000000000..55f9b99e6 --- /dev/null +++ b/Server/src/main/core/game/node/entity/npc/NPCBehavior.kt @@ -0,0 +1,161 @@ +package core.game.node.entity.npc + +import core.game.node.item.Item +import core.api.ContentInterface +import core.game.node.entity.Entity +import core.game.world.map.RegionManager +import core.game.node.entity.player.Player +import core.game.node.entity.combat.BattleState +import core.game.node.entity.combat.CombatStyle +import core.game.node.entity.combat.CombatSwingHandler +import core.game.world.map.path.ClipMaskSupplier +import core.game.world.map.path.Pathfinder + +open class NPCBehavior(vararg val ids: Int = intArrayOf()) : ContentInterface { + companion object { + private val idMap = HashMap() + private val defaultBehavior = NPCBehavior() + + @JvmStatic fun forId(id: Int) : NPCBehavior { + return idMap[id] ?: defaultBehavior + } + fun register(ids: IntArray, behavior: NPCBehavior){ + ids.forEach { idMap[it] = behavior } + } + } + + object StandardClipMaskSupplier : ClipMaskSupplier { + override fun getClippingFlag (z: Int, x: Int, y: Int) : Int { + return RegionManager.getClippingFlag(z,x,y) + } + } + + /** + * Called every tick, before the base NPC tick() method. + * @param self the NPC instance this behavior belongs to + * @return whether we should proceed with the base NPC tick() method - e.g. returning false means we do not proceed with a normal NPC tick. + */ + open fun tick(self: NPC): Boolean { + return true + } + + /** + * Called before this NPC receives damage, and allows you to adjust the battlestate if needed. + * @param self the NPC instance this behavior belongs to + * @param attacker the entity attacking this NPC + * @param state the current state of the combat between this NPC and the attacker. + */ + open fun beforeDamageReceived(self: NPC, attacker: Entity, state: BattleState) {} + + /** + * Called after this NPC receives damage, and allows you to adjust the battlestate if needed. + * @param self the NPC instance this behavior belongs to + * @param attacker the entity attacking this NPC + * @param state the current state of the combat between this NPC and the attacker. + */ + open fun afterDamageReceived(self: NPC, attacker: Entity, state: BattleState) {} + + /** + * Called after this NPC's basic attack has been calculated, but before it is finalized, so adjustments can be made. + * @param self the NPC instance this behavior belongs to + * @param victim the entity this NPC is attacking + * @param state the state of combat between this NPC and the victim. + */ + open fun beforeAttackFinalized(self: NPC, victim: Entity, state: BattleState) {} + + /** + * Called when this NPC is being removed from the game world. + * Note: This is not the same as death. Death does not remove an NPC, unless that NPC cannot respawn. + * @param self the NPC instance this behavior belongs to + */ + open fun onRemoval(self: NPC) {} + + /** + * Called when this NPC is first created and spawned into the game world. + * Note: This is not the same as respawning. + * @param self the NPC instance this behavior belongs to + */ + open fun onCreation(self: NPC) {} + + /** + * Called when this NPC respawns after being killed. + * @param self the NPC instance this behavior belongs to + */ + open fun onRespawn(self: NPC) {} + + /** + * Called immediately when the NPC first begins to die (on the same tick that the death animation begins) + * @param self the NPC instance this behavior belongs to + * @param killer the entity which killed this NPC. + */ + open fun onDeathStarted(self: NPC, killer: Entity) {} + + /** + * Called immediately after the death animation of this NPC has finished, on the same tick that drop tables are rolled. + * @param self the NPC instance this behavior belongs to + * @param killer the entity which killed this NPC. + */ + open fun onDeathFinished(self: NPC, killer: Entity) {} + + /** + * Called after this NPC's drop table is rolled, but before the items are actually dropped, so the list can be manipulated. + * @param self the NPC instance this behavior belongs to + * @param killer the killer of the npc + * @param drops the generated list of drops for this roll of the table + */ + open fun onDropTableRolled(self: NPC, killer: Entity, drops: ArrayList) {} + + /** + * Called by combat-related code to check if this NPC can be attacked by the `attacker` entity. + * @param self the NPC instance this behavior belongs to + * @param attacker the entity attempting to attack this NPC + * @param style the combat style the attacker is attempting to use + * @param shouldSendMessage whether the core combat code believes you should send a message e.g. "You can't attack this NPC with that weapon" + * @return whether the attacker should be able to attack this NPC. + */ + open fun canBeAttackedBy(self: NPC, attacker: Entity, style: CombatStyle, shouldSendMessage: Boolean) : Boolean { + if (attacker is Player && !self.definition.hasAction("attack")) + return false + return true + } + + /** + * Called by combat-related code to check if this NPC should ignore multi-combat rules when attempting to attack the given victim. + * @param self the NPC instance this behavior belongs to + * @param victim the entity that is being considered for attack. + * @return whether we should ignore the rules of multi-way combat for the given entity. + */ + open fun shouldIgnoreMultiRestrictions(self: NPC, victim: Entity) : Boolean {return false} + + /** + * Called by combat-related code to allow the combat handler to be overridden + * @param self the NPC instance this behavior belongs to + * @param original the default swing handler this NPC would have used + * @return the SwingHandler instance to be used for this cycle of combat + */ + open fun getSwingHandlerOverride(self: NPC, original: CombatSwingHandler) : CombatSwingHandler {return original} + + /** + * Called by MovementPulse to determine if a non-default Pathfinder should be used (e.g. whether this npc should intelligently walk around obstacles) + */ + open fun getPathfinderOverride(self: NPC): Pathfinder? { + return null + } + + /** + * Called by pathfinding code to determine the clipping mask supplier this NPC should use. You can use this to ignore water, etc. + */ + open fun getClippingSupplier(self: NPC) : ClipMaskSupplier? { + return StandardClipMaskSupplier + } + + /** + * Modifies the combat experience gained from killing an entity + * @param self the NPC instance this behavior belongs to + * @param attacker entity attacking the npc + * @return multiplier for final xp gain + */ + open fun getXpMultiplier(self: NPC, attacker: Entity) : Double { + return 1.0 + } +} 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..ae740b8f4 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 @@ -10,6 +10,9 @@ import core.game.world.map.RegionManager; import java.util.ArrayList; import java.util.List; +import core.game.node.entity.player.link.prayer.PrayerType; +import static core.api.ContentAPIKt.allInEquipment; + /** * Handles an NPC's aggressive behaviour. * @author Emperor @@ -41,6 +44,9 @@ public class AggressiveBehavior { return false; } if (target instanceof Player) { + if (ignorePlayer((Player) target)) { + return false; + } return ((Player) target).getSkullManager().isWilderness(); } return true; @@ -70,11 +76,14 @@ public class AggressiveBehavior { return false; } if (entity instanceof NPC && target instanceof Player) { + if (ignorePlayer((Player) target)) { + return false; + } 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,7 +93,19 @@ public class AggressiveBehavior { return true; } - public boolean ignoreCombatLevelDifference() { + public boolean ignoreCombatLevelDifference() { + return false; + } + + //Snowscape: allow players to hide from aggressive mobs with the protect from summoning prayer, or full ghostly robe set + public boolean ignorePlayer(Player player) { + if (player.getPrayer().get(PrayerType.PROTECT_FROM_SUMMONING)) { + return true; + } + //Check if player is wearing full ghostly robe set + if (allInEquipment(player, 6106, 6107, 6108, 6109, 6110, 6111)) { + return true; + } return false; } @@ -132,9 +153,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 5a7291c25..dc3b2aacd 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 @@ -23,6 +23,12 @@ import core.api.utils.NPCDropTable; import core.game.ge.GrandExchange; import core.game.world.repository.Repository; +import content.global.skill.summoning.familiar.BurdenBeast; +import content.global.skill.summoning.familiar.Forager; +import content.global.skill.summoning.SummoningPouch; +import content.global.skill.summoning.familiar.PackYakNPC; +import content.global.handlers.item.SnowscapeSatchelListener; + import java.util.ArrayList; import java.util.List; @@ -42,7 +48,7 @@ public final class NPCDropTables { */ public static final int[] MESSAGE_NPCS = { 50, 7133, 7134, 2881, 2882, 2883, 3200, 3340, 6247, 6203, 6260, 6222, 2745, 1160, 8133, 8610, 8611, 8612, 8613, 8614, 6204, 6206, 6208, 6261, 6263, 6265, 6223, 6225, 6227 }; - public final NPCDropTable table = new NPCDropTable(); + public NPCDropTable table = new NPCDropTable(); /** * The NPC definitions. @@ -74,7 +80,15 @@ public final class NPCDropTables { */ public void drop(NPC npc, Entity looter) { Player p = looter instanceof Player ? (Player) looter : null; - table.roll(looter).forEach(item -> createDrop(item,p,npc,npc.getDropLocation())); + ArrayList drops = table.roll(looter, 1); + npc.behavior.onDropTableRolled(npc, looter, drops); + drops.forEach(item -> createDrop(item,p,npc,npc.getDropLocation())); + } + + public List roll (NPC npc, Entity looter, int times) { + ArrayList drops = table.roll(looter, times); + npc.behavior.onDropTableRolled(npc, looter, drops); + return drops; } /** @@ -91,6 +105,15 @@ public final class NPCDropTables { if (handleBoneCrusher(player, item)) { return; } + boolean blocked = isBlockedItem(player, item); + if (!blocked) { + if (handleSatchel(player, item)) { + return; + } + if (handleCurrency(player, item)) { + return; + } + } if (item.hasItemPlugin() && player != null) { if (!item.getPlugin().createDrop(item, player, npc, l)) { return; @@ -98,9 +121,17 @@ public final class NPCDropTables { item = item.getPlugin().getItem(item, npc); } if (!item.getDefinition().isStackable() && item.getAmount() > 1) { + if (!blocked && hasValidFamiliar(player)) { + for (int i = 0; i < item.getAmount(); i++) { + if (!addItemFamiliar(player, new Item(item.getId()))) { + GroundItemManager.create(new Item(item.getId()), l, player); + } + } + } else { for (int i = 0; i < item.getAmount(); i++) { GroundItemManager.create(new Item(item.getId()), l, player); } + } return; } announceIfRare(player, item); @@ -113,7 +144,11 @@ public final class NPCDropTables { GroundItemManager.create(item, l); } } else { - GroundItem groundItem = GroundItemManager.create(item, l, getLooter(player, npc, item)); + Player looter = getLooter(player, npc, item); + if (!blocked && hasValidFamiliar(looter) && addItemFamiliar(looter, item)) { + return; + } + GroundItem groundItem = GroundItemManager.create(item, l, looter); if(player instanceof AIPlayer) { AIRepository.addItem(groundItem); } @@ -126,6 +161,37 @@ public final class NPCDropTables { } } + /** Snowscape: custom looting + * Check if the player has a valid familiar that can loot. + * @param player The player + * @return true if they have a valid familiar. + */ + private boolean hasValidFamiliar(Player player) { + if (!(player instanceof AIPlayer) && player.getFamiliarManager().hasFamiliar() && player.getFamiliarManager().getFamiliar().isBurdenBeast() && !(player.getFamiliarManager().getFamiliar() instanceof Forager) && !SummoningPouch.get(player.getFamiliarManager().getFamiliar().getPouchId()).abyssal){ + return true; + } else { + return false; + } + } + /** Snowscape: custom looting + * Attempt adding item to familiar inventory. + * @param player The player + * @return true if item was successfully added. + */ + private boolean addItemFamiliar(Player player, Item item) { + if ((player.getFamiliarManager().getFamiliar() instanceof PackYakNPC) && ((BurdenBeast) player.getFamiliarManager().getFamiliar()).getContainer().contains(12435, 1) && player.getBank().add(item)) { + ((BurdenBeast) player.getFamiliarManager().getFamiliar()).getContainer().remove(new Item(12435, 1)); + player.sendMessage("Your familiar picked up and banked " + item.getAmount() + " " + item.getName() + "."); + return true; + } + if (((BurdenBeast) player.getFamiliarManager().getFamiliar()).getContainer().add(item)) { + player.sendMessage("Your familiar picked up " + item.getAmount() + " " + item.getName() + "."); + return true; + } else { + return false; + } + } + /** * Gets the looting player. * @param player the player. @@ -139,7 +205,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); } } @@ -193,17 +259,64 @@ public final class NPCDropTables { * @param item The item * @return true if successfully added experience. */ - private boolean handleBoneCrusher(Player player, Item item) { + public static boolean handleBoneCrusher(Player player, Item item) { Bones bone = Bones.forId(item.getId()); if (bone == null) { return false; } - if (!player.getGlobalData().isEnableBoneCrusher()) { + if (!inEquipmentOrInventory(player,2963, 1) || !player.getAttribute("bonecrusher:enabled", false)) { return false; } player.getSkills().addExperience(Skills.PRAYER, item.getAmount() * bone.getExperience()); return true; } + + //Snowscape modification: loot certain items into the satchels if carried + private boolean handleSatchel(Player player, Item item) { + for (int satchelId = 10877; satchelId <= 10882; satchelId++) { + if (inEquipmentOrInventory(player,satchelId,1) && SnowscapeSatchelListener.Companion.isAllowed(player,satchelId,unnote(item).getId())){ + if (satchelId == 10877 && !player.plainSatchel.contains(unnote(item).getId(),1)) { + continue; + } + if (SnowscapeSatchelListener.Companion.getSatchel(player, satchelId).add(unnote(item))) { + player.sendMessage("Your " + new Item(satchelId).getName() + " stashed " + item.getAmount() + " " + item.getName() + "."); + return true; + } + } + } + return false; + } + + /** + * Snowscape custom: Automatically loot coins and tokkul if wearing a ring of wealth. + * @param player The player + * @param item The item + * @return true if successfully added items to player. + */ + private boolean handleCurrency(Player player, Item item) { + if (item.getId() == 995 || item.getId() == 6529) { + Item ring = player.getEquipment().get(12); + if (ring != null && ring.getId() >= 14638 && ring.getId() <=14646) { + if (player.getInventory().add(item)) { + player.sendMessage("Your ring collected " + item.getAmount() + " " + item.getName() + "."); + return true; + } + } + } + return false; + } + + //Snowscape: check if the item is in the player's ignore list + private boolean isBlockedItem(Player player, Item item) { + // Format the item name to match the block list formatting. Then check if the item name starts with any of the blocked names. + String formattedName = item.getName().toLowerCase().replace(" ","_"); + for (String blocked : player.getCommunication().getBlocked()) { + if (blocked.length() > 0 && formattedName.startsWith(blocked)) { + return true; + } + } + return false; + } /** * Gets the ratio for stabilizing NPC combat difficulty & drop rates. 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 9a4f28dec..883a188a4 100644 --- a/Server/src/main/core/game/node/entity/player/Player.java +++ b/Server/src/main/core/game/node/entity/player/Player.java @@ -2,6 +2,8 @@ package core.game.node.entity.player; import content.global.handlers.item.equipment.special.SalamanderSwingHandler; import content.global.skill.runecrafting.PouchManager; +import core.api.ContentAPIKt; +import core.api.EquipmentSlot; import core.game.component.Component; import core.game.container.Container; import core.game.container.ContainerType; @@ -9,7 +11,9 @@ import core.game.container.impl.BankContainer; import core.game.container.impl.EquipmentContainer; import core.game.container.impl.InventoryListener; import core.game.dialogue.DialogueInterpreter; -import core.game.interaction.Interaction; +import core.game.interaction.InteractPlugin; +import core.game.interaction.InteractionListeners; +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; @@ -19,9 +23,9 @@ import content.global.handlers.item.equipment.special.ChinchompaSwingHandler; import core.game.node.entity.npc.NPC; import core.game.node.entity.player.info.*; import core.game.node.entity.player.info.login.LoginConfiguration; +import core.game.node.entity.player.info.login.PlayerParser; import core.game.node.entity.player.link.*; import core.game.node.entity.player.link.appearance.Appearance; -import core.game.node.entity.player.link.audio.AudioManager; import core.game.node.entity.player.link.diary.AchievementDiaryManager; import core.game.node.entity.player.link.emote.EmoteManager; import core.game.node.entity.player.link.music.MusicPlayer; @@ -33,6 +37,7 @@ import core.game.node.entity.player.link.skillertasks.SkillerTasks; import core.game.node.entity.skill.Skills; import content.global.skill.construction.HouseManager; import content.global.skill.summoning.familiar.FamiliarManager; +import core.game.node.item.GroundItem; import core.game.node.item.GroundItemManager; import core.game.node.item.Item; import core.game.system.communication.CommunicationInfo; @@ -40,13 +45,10 @@ 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.context.Animation; -import core.game.world.update.flag.context.Graphics; -import core.game.world.update.flag.player.FaceEntityFlag; -import core.game.world.update.flag.player.FaceLocationFlag; -import core.game.world.update.flag.player.ForceChatFlag; +import core.game.world.update.flag.*; import core.net.IoSession; import core.net.packet.PacketRepository; import core.net.packet.context.DynamicSceneContext; @@ -55,12 +57,12 @@ import core.net.packet.context.SkillContext; import core.net.packet.out.BuildDynamicScene; import core.net.packet.out.SkillLevel; import core.net.packet.out.UpdateSceneGraph; -import core.tools.RandomFunction; -import core.tools.StringUtils; -import core.tools.TickUtilsKt; +import core.plugin.Plugin; +import core.tools.*; import kotlin.Unit; import kotlin.jvm.functions.Function1; import org.rs09.consts.Items; +import org.rs09.consts.Sounds; import proto.management.ClanLeaveNotification; import proto.management.PlayerStatusUpdate; import core.GlobalStats; @@ -69,25 +71,27 @@ 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.tools.SystemLogger; import core.game.world.GameWorld; import core.game.world.repository.Repository; import core.game.world.update.MapChunkRenderer; import core.game.world.update.NPCRenderer; import core.game.world.update.PlayerRenderer; import core.game.world.update.UpdateSequence; +import core.game.ge.GrandExchangeRecords; +import core.game.ge.GrandExchangeOffer; +import core.cache.def.impl.ItemDefinition; import core.worker.ManagementEvents; +import core.game.world.update.flag.context.*; 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. @@ -117,10 +121,14 @@ public class Player extends Entity { public VarpManager varpManager = new VarpManager(this); - public HashMap states = new HashMap<>(); + public HashMap varpMap = new HashMap<>(); + + public HashMap saveVarp = new HashMap<>(); public HashMap> logoutListeners = new HashMap<>(); + public Boolean isAfkLogout; + /** * The inventory. */ @@ -146,21 +154,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. */ @@ -214,7 +207,7 @@ public class Player extends Entity { /** * The quest repository. */ - private QuestRepository questRepository = new QuestRepository(this); + public QuestRepository questRepository = new QuestRepository(this); /** * The prayer manager. @@ -231,15 +224,10 @@ public class Player extends Entity { */ private final FamiliarManager familiarManager = new FamiliarManager(this); - /** - * The config manager. - */ - private final ConfigurationManager configManager = new ConfigurationManager(this); - /** * The saved data. */ - private SavedData savedData = new SavedData(this); + public SavedData savedData = new SavedData(this); /** * The request manager. @@ -265,11 +253,6 @@ public class Player extends Entity { */ private final HouseManager houseManager = new HouseManager(); - /** - * The audio manager. - */ - private final AudioManager audioManager = new AudioManager(this); - /** * The bank pin manager. */ @@ -314,9 +297,33 @@ 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; + /** + * The save file version. + */ + public int version = ServerConstants.CURRENT_SAVEFILE_VERSION; + + /** + * 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; + + // Snowscape additions + // The satchel containers. + public final Container plainSatchel = new Container(6, ContainerType.ALWAYS_STACK); + public final Container greenSatchel = new Container(30, ContainerType.ALWAYS_STACK); + public final Container redSatchel = new Container(30, ContainerType.ALWAYS_STACK); + public final Container blackSatchel = new Container(30, ContainerType.ALWAYS_STACK); + public final Container goldSatchel = new Container(30, ContainerType.ALWAYS_STACK); + public final Container runeSatchel = new Container(6, ContainerType.ALWAYS_STACK); + // The summoning pouch storage + public final Container summoningPouches = new Container(30); + // The costume room bank + public final BankContainer costumeRoomBank = new BankContainer(this); + /** * Constructs a new {@code Player} {@code Object}. * @param details The player's details. @@ -324,36 +331,39 @@ public class Player extends Entity { public Player(PlayerDetails details) { super(details.getUsername(), ServerConstants.START_LOCATION); super.active = false; - super.interaction = new Interaction(this); + super.interactPlugin = new InteractPlugin(this); this.details = details; this.direction = Direction.SOUTH; } @Override public void init() { - if(!artificial) - SystemLogger.logInfo(this.getClass(), getUsername() + " initialising..."); if (!artificial) { - getProperties().setSpawnLocation(ServerConstants.HOME_LOCATION); + log(this.getClass(), Log.INFO, getUsername() + " initialising..."); getDetails().getSession().setObject(this); } super.init(); LoginConfiguration.configureLobby(this); + setAttribute("logged-in-fully", true); } @Override public void clear() { - clear(false); + if (isArtificial()) { + finishClear(); + return; + } + Repository.getDisconnectionQueue().remove(getName()); + Repository.getDisconnectionQueue().add(this, true); + details.save(); } /** * Clears the player from the game. - * @param force If we should force removal, a player engaged in combat will otherwise remain active until out of combat. + * You should NEVER call this manually. This can only be called by the DisconnectionQueue doing its job. + * If you think you need to call this manually, you're wrong. Stop. Turn around. Go back. Here be monsters. */ - public void clear(boolean force) { - if (force) { - Repository.getDisconnectionQueue().remove(getName()); - } + public void finishClear() { if (!isArtificial()) GameWorld.getLogoutListeners().forEach((it) -> it.logout(this)); setPlaying(false); @@ -368,11 +378,10 @@ public class Player extends Entity { interfaceManager.closeSingleTab(); super.clear(); getZoneMonitor().clear(); + houseManager.expelGuests(this); HouseManager.leave(this); UpdateSequence.getRenderablePlayers().remove(this); - details.save(); sendLogoutEvents(); - Repository.getDisconnectionQueue().add(this); checkForWealthUpdate(true); } @@ -439,12 +448,12 @@ public class Player extends Entity { int time = getAttribute("fire:immune",0) - GameWorld.getTicks(); if(time == TickUtilsKt.secondsToTicks(30)){ sendMessage(colorize("%RYou have 30 seconds remaining on your antifire potion.")); - getAudioManager().send(3120); + playAudio(this, Sounds.CLOCK_TICK_1_3120, 0, 3); } if(time == 0){ sendMessage(colorize("%RYour antifire potion has expired.")); removeAttribute("fire:immune"); - getAudioManager().send(2607); + playAudio(this, Sounds.DRAGON_POTION_FINISHED_2607); } } if(getAttribute("poison:immunity",0) > 0){ @@ -452,23 +461,37 @@ public class Player extends Entity { debug(time + ""); if(time == TickUtilsKt.secondsToTicks(30)){ sendMessage(colorize("%RYou have 30 seconds remaining on your antipoison potion.")); - getAudioManager().send(3120); + playAudio(this, Sounds.CLOCK_TICK_1_3120, 0, 3); } if(time == 0){ sendMessage(colorize("%RYour antipoison potion has expired.")); removeAttribute("poison:immunity"); - getAudioManager().send(2607); + playAudio(this, Sounds.DRAGON_POTION_FINISHED_2607); } } if(getAttribute("infinite-special", false)) { 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. + // Only runs when the player is not movement/interaction-locked, so that original-loc does not get wiped e.g. in the middle of the player teleporting to their POH. + if (GameWorld.getTicks() % 10 == 0 && !getLocks().isMovementLocked() && !getLocks().isInteractionLocked()) { + if (ContentAPIKt.getAttribute(this, "/save:original-loc", null) != null) { + 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) { @@ -491,6 +514,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(); + } + } + + // 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); @@ -521,31 +559,11 @@ public class Player extends Entity { PacketRepository.send(SkillLevel.class, new SkillContext(this, Skills.HITPOINTS)); getSkills().setLifepointsUpdate(false); } - } - - @Override - public boolean face(Entity entity) { - if (entity == null) { - if (getUpdateMasks().unregisterSynced(FaceEntityFlag.getOrdinal())) { - return getUpdateMasks().register(new FaceEntityFlag(entity)); - } - return true; + if (getAttribute("flagged-for-save", false)) { + PlayerParser.saveImmediately(this); + removeAttribute("flagged-for-save"); } - return getUpdateMasks().register(new FaceEntityFlag(entity), true); - } - - @Override - public boolean faceLocation(Location location) { - if (location == null) { - getUpdateMasks().unregisterSynced(FaceLocationFlag.getOrdinal()); - return true; - } - return getUpdateMasks().register(new FaceLocationFlag(location), true); - } - - @Override - public boolean sendChat(String string) { - return getUpdateMasks().register(new ForceChatFlag(string)); + Arrays.fill(opCounts, (byte) 0); } @Override @@ -553,6 +571,16 @@ 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 CombatSwingHandler getSwingHandler(boolean swing) { CombatStyle style = getProperties().getCombatPulse().getStyle(); @@ -580,6 +608,7 @@ public class Player extends Entity { @Override public void commenceDeath(Entity killer) { + if (!isPlaying()) return; super.commenceDeath(killer); if (prayer.get(PrayerType.RETRIBUTION)) { prayer.startRetribution(killer); @@ -588,6 +617,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. GlobalStats.incrementDeathCount(); settings.setSpecialEnergy(100); settings.updateRunEnergy(settings.getRunEnergy() - 100); @@ -601,45 +631,44 @@ public class Player extends Entity { if (this.isArtificial() && killer instanceof NPC) { return; } + if (killer instanceof Player && killer.getName() != getName() /* happens if you died via typeless damage from an external cause, e.g. bugs in a dark cave without a light source */ && 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()); + } getPacketDispatch().sendMessage("Oh dear, you are dead!"); incrementAttribute("/save:"+STATS_BASE+":"+STATS_DEATHS); 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); - new PlayerSaver(this).save(); - clear(true); + 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()) { + if (i == null) continue; + InteractionListeners.run(i.getId(), this, i, false); + Plugin equipPlugin = i.getDefinition().getConfiguration("equipment", null); + if (equipPlugin != null) equipPlugin.fireEvent("unequip"); + } + boolean canCreateGrave = GraveController.allowGenerate(this); if (canCreateGrave) { Grave g = GraveController.produceGrave(GraveController.getGraveType(this)); g.initialize(this, location, Arrays.stream(c[1].toArray()).filter(Objects::nonNull).toArray(Item[]::new)); //note: the amount of code required to filter nulls from an array in Java is atrocious. } else { StringBuilder itemsLost = new StringBuilder(); + int coins = 0; for (Item item : c[1].toArray()) { + boolean stayPrivate = false; if (item == null) continue; if (killer instanceof Player) itemsLost.append(getItemName(item.getId())).append("(").append(item.getAmount()).append("), "); @@ -647,8 +676,20 @@ public class Player extends Entity { continue; if (GraveController.shouldRelease(item.getId())) continue; + if (!item.getDefinition().isTradeable()) { + if (killer instanceof Player) { + int value = item.getDefinition().getAlchemyValue(true); + if (getStatLevel(killer, Skills.MAGIC) < 55) value /= 2; + coins += Math.max(0, value - 250); + continue; + } else stayPrivate = true; + } item = GraveController.checkTransform(item); - GroundItemManager.create(item, location, killer instanceof Player ? (Player) killer : this); + GroundItem gi = GroundItemManager.create(item, location, killer instanceof Player ? (Player) killer : this); + gi.setRemainPrivate(stayPrivate); + } + if (coins > 0) { + GroundItemManager.create(new Item(Items.COINS_995, coins), location, (Player) killer); } if (killer instanceof Player) PlayerMonitor.log((Player) killer, LogType.PK, "Killed " + name + ", who dropped: " + itemsLost); @@ -663,6 +704,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()) { @@ -716,12 +761,24 @@ 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; + } return super.isAttackable(entity, style, message); } @Override public boolean isPoisonImmune() { - return getAttribute("poison:immunity", -1) > GameWorld.getTicks(); + return timers.getTimer("poison:immunity") != null; } @Override @@ -761,7 +818,6 @@ public class Player extends Entity { getPulseManager().clear(); getZoneMonitor().getZones().clear(); getViewport().setCurrentPlane(RegionManager.forId(66666).getPlanes()[3]); - configManager.reset(); playerFlags.setLastSceneGraph(null); playerFlags.setUpdateSceneGraph(false); playerFlags.setLastViewport(new RegionChunk[Viewport.CHUNK_SIZE][Viewport.CHUNK_SIZE]); @@ -906,7 +962,7 @@ public class Player extends Entity { * @return {@code True} if so. */ public boolean allowRemoval() { - return inCombat() || getSkills().getLifepoints() < 1 || DeathTask.isDead(this); + return !(inCombat() || getSkills().getLifepoints() < 1 || DeathTask.isDead(this) || isTeleporting() || scripts.hasTypeInQueue(QueueStrength.SOFT)); } /** @@ -991,11 +1047,23 @@ public class Player extends Entity { return equipment; } - /** + /** Snowscape modifications: Added check for clan bank * Gets the current active bank. * @return Current active bank. */ public BankContainer getBank() { + if (getAttribute("inCostumeRoomBank", false)) { + return costumeRoomBank; + } + if (getAttribute("clanbank:enabled",false)) { + Player target = Repository.getPlayerByName(this.getCommunication().getCurrentClan()); + if (target != null) { + target.getBankPrimary().setPlayer(this); + return target.getBankPrimary(); + } + } + //The primary bank.player is changed back to the owner when a player closes it, but if something else, like dialogue, runs a getbank() function then it's never changed back. So this manually changes it when checking our own bank. + bank.setPlayer(this); return useSecondaryBank ? bankSecondary : bank; } @@ -1103,6 +1171,28 @@ public class Player extends Entity { return interfaceManager; } + public boolean hasModalOpen() { + int[] excludedIds = new int[] {372, 421, InterfaceManager.DEFAULT_CHATBOX}; //excludes plain message, plain message with scrollbar, and normal chatbox + Component openedIface = interfaceManager.getOpened(); + Component openChatbox = interfaceManager.getChatbox(); + + boolean hasModal = false; + + if (openedIface != null) { + for (int i = 0; i < excludedIds.length; i++) + if (excludedIds[i] == openedIface.getId()) break; + else if (i == excludedIds.length - 1) hasModal = true; + } + + if (openChatbox != null) { + for (int i = 0; i < excludedIds.length; i++) + if (excludedIds[i] == openChatbox.getId()) break; + else if (i == excludedIds.length - 1) hasModal = true; + } + + return hasModal; + } + /** * Gets the dialogue interpreter. * @return The dialogue interpreter. @@ -1154,14 +1244,6 @@ public class Player extends Entity { return familiarManager; } - /** - * Gets the configManager. - * @return The configManager. - */ - public ConfigurationManager getConfigManager() { - return configManager; - } - /** * Gets the communication. * @return The communication. @@ -1224,14 +1306,6 @@ public class Player extends Entity { return houseManager; } - /** - * Gets the audioManager. - * @return the audioManager - */ - public AudioManager getAudioManager() { - return audioManager; - } - /** * Gets the bankPinManager. * @return the bankPinManager @@ -1298,7 +1372,6 @@ public class Player extends Entity { return "Player [name=" + name + ", getRights()=" + getRights() + "]"; } - public String getCustomState() { return customState; } @@ -1324,25 +1397,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; - } - return false; + public void updateAppearance() { + getUpdateMasks().register(EntityFlag.Appearance, this); } - 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(); + 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."); } - states.remove(key); } } diff --git a/Server/src/main/core/game/node/entity/player/Varp.kt b/Server/src/main/core/game/node/entity/player/Varp.kt deleted file mode 100644 index a6c33687d..000000000 --- a/Server/src/main/core/game/node/entity/player/Varp.kt +++ /dev/null @@ -1,54 +0,0 @@ -package core.game.node.entity.player - -/** - * A class that represents Varps and aids in modifying/calculating them - * @author Ceikry - */ -class Varp(val index: Int) { - val varbits = HashMap() - var save = false - - fun setVarbit(offset: Int, value: Int): Varp { - varbits[offset] = value - return this - } - - fun getValue(): Int{ - var config = 0 - for((index,value) in varbits.entries){ - config = config or (value shl index) - } - return config - } - - fun getVarbit(offset: Int): Int { - return getVarbitValue(offset) - } - - fun getVarbitValue(offset: Int): Int { - return varbits[offset] ?: 0 - } - - fun send(player: Player){ - player.packetDispatch.sendVarp(this) - } - - fun clear(): Varp { - varbits.clear() - return this - } - - fun clearBitRange(first : Int, last : Int){ - for(i in first..last) varbits.remove(i) - } - - fun getBitRangeValue(first: Int, last: Int): Int{ - var product = 0 - for (i in first..last) - { - val value = varbits[i] ?: 0 - product = product or (value shl i) - } - return product - } -} \ No newline at end of file diff --git a/Server/src/main/core/game/node/entity/player/VarpManager.kt b/Server/src/main/core/game/node/entity/player/VarpManager.kt index 34cb5d23e..96067b1c7 100644 --- a/Server/src/main/core/game/node/entity/player/VarpManager.kt +++ b/Server/src/main/core/game/node/entity/player/VarpManager.kt @@ -12,111 +12,23 @@ import org.json.simple.JSONObject * @author Ceikry */ class VarpManager(val player: Player) { - val varps = Array(3500) { Varp(it) } - - fun get(index: Int): Varp { - return varps[index] - } - - fun send(index: Int){ - player.packetDispatch.sendVarp(get(index)) - } - - fun set(index: Int, value: Int){ - get(index).varbits.clear() - get(index).varbits[0] = value - get(index).send(player) - } - - fun setVarbit(def: VarbitDefinition, value: Int){ - get(def.varpId).setVarbit(def.startBit,value).send(player) - - player.dispatch(VarbitUpdateEvent(def.id, value)) - } - - fun setVarbit(varbitIndex: Int, value: Int){ - val def = VarbitDefinition.forId(varbitIndex) - get(def.varpId).setVarbit(def.startBit, value) - get(def.varpId).send(player) - - player.dispatch(VarbitUpdateEvent(varbitIndex, value)) - } - - fun getVarbit(varbitIndex: Int): Int{ - val def = VarbitDefinition.forId(varbitIndex) - return get(def.varpId).getVarbit(def.startBit) - } - - fun flagSave(index: Int, isVarbit: Boolean = false){ - if (isVarbit) { - val def = VarbitDefinition.forId(index) - flagSave(def.varpId, false) - } else { - get(index).save = true - } - } - - fun unflagSave(index: Int, isVarbit: Boolean = false){ - if (isVarbit) { - val def = VarbitDefinition.forId(index) - unflagSave(def.varpId, false) - } - else { - get(index).save = false - } - } - - fun sendAllVarps(){ - for(varp in varps){ - if(varp.getValue() != 0){ - player.packetDispatch.sendVarp(varp) - } - } - } - - fun getSavedVarps(): ArrayList{ - val list = ArrayList() - for(varp in varps){ - if(varp.save) list.add(varp) - } - return list - } - fun save(root: JSONObject){ - val varps = JSONArray() - for(varp in getSavedVarps()){ - val varpobj = JSONObject() - varpobj.put("index",varp.index.toString()) - val bitArray = JSONArray() - for(varbit in varp.varbits){ - val varbitobj = JSONObject() - varbitobj.put("offset",varbit.key.toString()) - varbitobj.put("value",varbit.value.toString()) - bitArray.add(varbitobj) - } - varpobj.put("bitArray",bitArray) - varps.add(varpobj) - } - root.put("varps",varps) } fun parse(data: JSONArray){ - val patch_varps = FarmingPatch.values().map { it.varpIndex }.toIntArray() - val bin_varps = FarmingPatch.values().map { it.varpIndex }.toIntArray() for(varpobj in data){ val vobj = varpobj as JSONObject val index = vobj["index"].toString().toInt() - if(patch_varps.contains(index)) continue - if(bin_varps.contains(index)) continue - val varp = get(index) val bits = vobj["bitArray"] as JSONArray + var total = 0 for(vbit in bits){ val varbit = vbit as JSONObject val offset = varbit["offset"].toString().toInt() val value = varbit["value"].toString().toInt() - varp.setVarbit(offset,value) - varp.save = true + total = total or (value shl offset) } + player.varpMap[index] = total + player.saveVarp[index] = true } } -} \ No newline at end of file +} diff --git a/Server/src/main/core/game/node/entity/player/info/PlayerMonitor.kt b/Server/src/main/core/game/node/entity/player/info/PlayerMonitor.kt index 2b5f6ed7b..112d51a3c 100644 --- a/Server/src/main/core/game/node/entity/player/info/PlayerMonitor.kt +++ b/Server/src/main/core/game/node/entity/player/info/PlayerMonitor.kt @@ -1,15 +1,15 @@ package core.game.node.entity.player.info +import core.ServerConstants import core.api.getItemName import core.game.container.Container import core.game.node.entity.player.Player import core.game.node.entity.skill.Skills import core.integrations.discord.Discord -import kotlinx.coroutines.* -import org.sqlite.SQLiteDataSource -import core.ServerConstants -import core.tools.SystemLogger -import java.io.File +import core.integrations.sqlite.SQLiteProvider +import core.tools.Log +import kotlinx.coroutines.Job +import kotlinx.coroutines.cancel import java.sql.Connection import java.util.concurrent.LinkedBlockingQueue import kotlin.math.abs @@ -17,6 +17,25 @@ import kotlin.math.abs object PlayerMonitor { private val eventQueue = LinkedBlockingQueue() private var activeTask: Job? = null + private lateinit var db: SQLiteProvider + + var expectedTables = hashMapOf( + "chat_logs" to """ + CREATE TABLE "chat_logs" ( "player" TEXT, "uid" INTEGER, "type" TEXT, "message" TEXT, "timestamp" NUMERIC ); + """, + "misc_logs" to """ + CREATE TABLE "misc_logs" ( "player" TEXT, "uid" INTEGER, "type" TEXT, "details" TEXT , "timestamp" NUMERIC); + """, + "trade_logs" to """ + CREATE TABLE "trade_logs" ( "player_a" TEXT, "player_b" TEXT, "uid_a" INTEGER, "uid_b" INTEGER, "items_a" TEXT, "items_b" TEXT, "timestamp" NUMERIC ); + """, + "xp_gains" to """ + CREATE TABLE "xp_gains" ( "player" TEXT, "uid" INTEGER, "attack" INTEGER, "defence" INTEGER, "strength" INTEGER, "hitpoints" INTEGER, "ranged" INTEGER, "prayer" INTEGER, "magic" INTEGER, "cooking" INTEGER, "woodcutting" INTEGER, "fletching" INTEGER, "fishing" INTEGER, "firemaking" INTEGER, "crafting" INTEGER, "smithing" INTEGER, "mining" INTEGER, "herblore" INTEGER, "agility" INTEGER, "thieving" INTEGER, "slayer" INTEGER, "farming" INTEGER, "runecrafting" INTEGER, "hunter" INTEGER, "construction" INTEGER, "summoning" INTEGER , "timestamp" NUMERIC) + """, + "wealth_logs" to """ + CREATE TABLE "wealth_logs" ( "player" TEXT, "uid" INTEGER, "total" NUMERIC, "diff" NUMERIC, "timestamp" NUMERIC ) + """ + ) @JvmStatic fun logWealthChange(player: Player, totalWealth: Long, diff: Long) { val event = LogEvent.WealthLog( @@ -119,72 +138,34 @@ object PlayerMonitor { } } - fun processQueuedEvents() { - val path = ServerConstants.LOGS_PATH + "playerlogs.db" - if (activeTask?.isActive == true) - return - if (!File(path).exists()) { - createSqliteDatabase(path) - } - activeTask = GlobalScope.launch { - val conn = connect(path) - conn.use { - while (eventQueue.isNotEmpty()) { - val event = withContext(Dispatchers.IO) { eventQueue.take() } - process(event, it) - } - } + fun init () { + if (!this::db.isInitialized) { + var path = ServerConstants.LOGS_PATH + "playerlogs.db" + db = SQLiteProvider(path, expectedTables) + db.initTables() } } - @JvmStatic fun flushRemainingEventsImmediately() { - SystemLogger.logInfo(this::class.java, "Flushing player log events...") - val path = ServerConstants.LOGS_PATH + "playerlogs.db" - if (!File(path).exists()) { - createSqliteDatabase(path) - } - if (activeTask != null) - activeTask?.cancel("Interrupted by shutdown. This is probably fine.") - val conn = connect(path) - conn.use { + fun processQueuedEvents() { + init() + if (activeTask?.isActive == true) + return + + activeTask = db.runAsync { while (eventQueue.isNotEmpty()) process(eventQueue.take(), it) } } - private fun connect(path: String) : Connection { - val source = SQLiteDataSource() - source.url = "jdbc:sqlite:$path" - return source.connection - } - - private fun createSqliteDatabase(path: String) { - if (!File(ServerConstants.LOGS_PATH).exists()) - File(ServerConstants.LOGS_PATH).mkdirs() - val connection = connect(path) - val stmt = connection.createStatement() - stmt.execute( - """ - CREATE TABLE "chat_logs" ( "player" TEXT, "uid" INTEGER, "type" TEXT, "message" TEXT, "timestamp" NUMERIC ); - """) - stmt.execute( - """ - CREATE TABLE "misc_logs" ( "player" TEXT, "uid" INTEGER, "type" TEXT, "details" TEXT , "timestamp" NUMERIC); - """) - stmt.execute( - """ - CREATE TABLE "trade_logs" ( "player_a" TEXT, "player_b" TEXT, "uid_a" INTEGER, "uid_b" INTEGER, "items_a" TEXT, "items_b" TEXT, "timestamp" NUMERIC ); - """) - stmt.execute( - """ - CREATE TABLE "xp_gains" ( "player" TEXT, "uid" INTEGER, "attack" INTEGER, "defence" INTEGER, "strength" INTEGER, "hitpoints" INTEGER, "ranged" INTEGER, "prayer" INTEGER, "magic" INTEGER, "cooking" INTEGER, "woodcutting" INTEGER, "fletching" INTEGER, "fishing" INTEGER, "firemaking" INTEGER, "crafting" INTEGER, "smithing" INTEGER, "mining" INTEGER, "herblore" INTEGER, "agility" INTEGER, "thieving" INTEGER, "slayer" INTEGER, "farming" INTEGER, "runecrafting" INTEGER, "hunter" INTEGER, "construction" INTEGER, "summoning" INTEGER , "timestamp" NUMERIC) - """) - stmt.execute( - """ - CREATE TABLE "wealth_logs" ( "player" TEXT, "uid" INTEGER, "total" NUMERIC, "diff" NUMERIC, "timestamp" NUMERIC ) - """) - stmt.close() - connection.close() + @JvmStatic fun flushRemainingEventsImmediately() { + core.api.log(this::class.java, Log.FINE, "Flushing player log events...") + init() + if (activeTask != null) + activeTask?.cancel("Interrupted by shutdown. This is probably fine.") + db.run { + while (eventQueue.isNotEmpty()) + process(eventQueue.take(), it) + } } private fun process(event: LogEvent, conn: 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 cd9b28e59..c50de0e95 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 @@ -1,6 +1,7 @@ package core.game.node.entity.player.info; import core.game.node.entity.player.Player; +import core.ServerConstants; /** * Represent the rights of a player. @@ -24,9 +25,11 @@ public enum Rights { if (c != Rights.REGULAR_PLAYER && c != null) { return c.toInteger(); } - if (player.getIronmanManager().isIronman()) { - return player.getIronmanManager().getMode().getIcon(); - } + if (ServerConstants.IRONMAN_ICONS) { + 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 0340ce749..0f8771092 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 @@ -6,7 +6,6 @@ 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.world.map.RegionManager; -import core.game.world.update.flag.player.AppearanceFlag; import core.net.packet.PacketRepository; import core.net.packet.context.InterfaceContext; import core.net.packet.out.Interface; @@ -14,10 +13,11 @@ import core.plugin.Plugin; import core.ServerConstants; import core.game.interaction.InteractionListeners; import content.global.handlers.iface.RulesAndInfo; -import core.tools.SystemLogger; +import core.tools.Log; 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 java.util.ArrayList; import java.util.Date; @@ -26,6 +26,10 @@ import java.util.Random; import java.util.concurrent.TimeUnit; import java.util.stream.IntStream; +import static core.api.ContentAPIKt.*; +import static core.tools.GlobalsKt.colorize; +import content.data.Quests; + /** * Sends the login configuration packets. @@ -108,7 +112,7 @@ public final class LoginConfiguration { 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 2009scape?
Visit the github using the link below!", 378, 230); + 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)); @@ -126,28 +130,49 @@ public final class LoginConfiguration { * @param player The player. */ public static void configureGameWorld(final Player player) { - player.getConfigManager().reset(); sendGameConfiguration(player); Repository.getLobbyPlayers().remove(player); player.setPlaying(true); UpdateSequence.getRenderablePlayers().add(player); RegionManager.move(player); player.getMusicPlayer().init(); - player.getUpdateMasks().register(new AppearanceFlag(player)); + player.updateAppearance(); player.getPlayerFlags().setUpdateSceneGraph(true); - player.getStateManager().init(); player.getPacketDispatch().sendInterfaceConfig(226, 1, true); + if(player.getGlobalData().getTestStage() == 3 && !player.getEmoteManager().isUnlocked(Emotes.SAFETY_FIRST)){ player.getEmoteManager().unlock(Emotes.SAFETY_FIRST); } - player.varpManager.sendAllVarps(); + for (Item item : player.getEquipment().toArray()) { //Run equip hooks for all items equipped on login. //We should have already been doing this. //Frankly, I don't even want to imagine the number of bugs us *not* doing this has caused. if (item == null) continue; +//Snowscape: removed the re-equip section, as that cleared the autocast spell on login and I don't care about catching the odd item requirement change. InteractionListeners.run(item.getId(), player, item, true); +/* + player.getEquipment().remove(item); + if (!InteractionListeners.run(item.getId(), player, item, true) || !player.getEquipment().add(item, true, false)) { + 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, 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, 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(ServerConstants.RULES_AND_INFO_ENABLED) RulesAndInfo.openFor(player); /*if (GameWorld.getSettings().isPvp()) { @@ -209,7 +234,7 @@ public final class LoginConfiguration { */ public static final void config(final Player player) { if(!player.isArtificial()) - SystemLogger.logInfo(LoginConfiguration.class, "configuring player " + player.getUsername()); + log(LoginConfiguration.class, Log.INFO, "configuring player " + player.getUsername()); player.getInventory().refresh(); player.getEquipment().refresh(); player.getSkills().refresh(); @@ -220,13 +245,12 @@ public final class LoginConfiguration { player.getFamiliarManager().login(); player.getInterfaceManager().openDefaultTabs(); player.getPacketDispatch().sendString("Friends List - World " + GameWorld.getSettings().getWorldId(), 550, 3); - player.getConfigManager().init(); player.getQuestRepository().syncronizeTab(player); player.getInterfaceManager().close(); player.getEmoteManager().refresh(); player.getInterfaceManager().openInfoBars(); if(!player.isArtificial()) - SystemLogger.logInfo(LoginConfiguration.class, "finished configuring player " + player.getUsername()); + log(LoginConfiguration.class, Log.INFO, "finished configuring player " + player.getUsername()); } /** @@ -300,4 +324,4 @@ public final class LoginConfiguration { return LOGIN_PLUGINS; } -} \ No newline at end of file +} 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 9c28fed57..75eb6e4c6 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.api.LoginListener +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,6 +37,7 @@ 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) @@ -45,6 +46,7 @@ class LoginParser(val details: PlayerDetails) { } else { flag(AuthResponse.Success) player.init() + reinitVarps(player) } } else { Repository.removePlayer(player) @@ -70,7 +72,7 @@ class LoginParser(val details: PlayerDetails) { player.isActive = true flag(AuthResponse.Success) player.updateSceneGraph(true) - player.configManager.init() + reinitVarps(player) LoginConfiguration.configureGameWorld(player) } @@ -99,4 +101,4 @@ class LoginParser(val details: PlayerDetails) { details.session.write(response, true) return response == AuthResponse.Success } -} \ No newline at end of file +} diff --git a/Server/src/main/core/game/node/entity/player/info/login/PlayerParser.java b/Server/src/main/core/game/node/entity/player/info/login/PlayerParser.java index bb19f1b31..6eb56ff4c 100644 --- a/Server/src/main/core/game/node/entity/player/info/login/PlayerParser.java +++ b/Server/src/main/core/game/node/entity/player/info/login/PlayerParser.java @@ -2,10 +2,13 @@ package core.game.node.entity.player.info.login; import core.game.node.entity.player.Player; import core.ServerConstants; +import core.tools.Log; import core.tools.SystemLogger; import java.io.*; +import static core.api.ContentAPIKt.log; + /** * Class used to abstract the process of loading a player save. * @author Ceikry @@ -31,6 +34,10 @@ public final class PlayerParser { * @param player The player. */ public static void save(Player player) { + player.setAttribute("flagged-for-save", true); + } + + public static void saveImmediately(Player player) { new PlayerSaver(player).save(); } @@ -57,7 +64,7 @@ public final class PlayerParser { assert os != null; os.close(); } catch (Exception f){ - SystemLogger.logWarn(PlayerParser.class, "Unable to close file copiers in PlayerParser.java line 216."); + log(PlayerParser.class, Log.ERR, "Unable to close file copiers in PlayerParser.java line 216."); f.printStackTrace(); } } 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 a86fe536e..38ec991fb 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 @@ -10,14 +8,16 @@ import core.game.node.entity.player.link.IronmanMode import core.game.node.entity.player.link.SpellBookManager import core.game.node.entity.player.link.emote.Emotes import core.game.node.entity.player.link.music.MusicEntry +import core.game.world.map.Location import org.json.simple.JSONArray import org.json.simple.JSONObject import org.json.simple.parser.JSONParser 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 import java.io.FileReader import java.util.* @@ -36,49 +36,45 @@ class PlayerSaveParser(val player: Player) { var saveFile: JSONObject? = null var read = true - val patch_varps = FarmingPatch.values().map { it.varpIndex }.toIntArray() - val bin_varps = CompostBins.values().map { it.varpIndex }.toIntArray() - - init { + fun parse() { val JSON = File(ServerConstants.PLAYER_SAVE_PATH + player.name + ".json") if(JSON.exists()) { reader = FileReader(JSON) } - reader ?: SystemLogger.logWarn(this::class.java, "Couldn't find save file for ${player.name}, or save is corrupted.").also { read = false } + 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 + parseData() } } - fun parse() { - if (read) { - parseCore() - parseAttributes() - parseSkills() - parseSettings() - parseQuests() - parseAppearance() - parseGrave() - parseVarps() - parseStates() - parseSpellbook() - parseSavedData() - parseAutocastSpell() - parseFarming() - parseConfigs() - parseMonitor() - parseMusic() - parseFamiliars() - parseBankPin() - parseHouse() - parseIronman() - parseEmoteManager() - parseStatistics() - parseAchievements() - parsePouches() - parsePouches() - } + fun parseData() { + parseVersion() + parseCore() + parseAttributes() + parseSkills() + parseSettings() + parseQuests() + parseAppearance() + parseGrave() + parseVarps() + parseSpellbook() + parseSavedData() + parseAutocastSpell() + parseFarming() + parseConfigs() + parseMonitor() + parseMusic() + parseFamiliars() + parseBankPin() + parseHouse() + parseIronman() + parseEmoteManager() + parseStatistics() + parseAchievements() + parsePouches() + parseSnowscapeData() } fun runContentHooks() @@ -120,7 +116,8 @@ class PlayerSaveParser(val player: Player) { "long" -> attr["value"].toString().toLong() "bool" -> attr["value"] as Boolean "byte" -> Base64.getDecoder().decode(attr["value"].toString())[0] - else -> null.also { SystemLogger.logWarn(this::class.java, "Invalid data type for key: $key in PlayerSaveParser.kt Line 115") } + "location" -> Location.fromString(attr["value"].toString()) + else -> null.also { log(this::class.java, Log.WARN, "Invalid data type for key: $key in PlayerSaveParser.kt Line 115") } }) } } else { @@ -177,23 +174,6 @@ class PlayerSaveParser(val player: Player) { player.bankPinManager.parse(bpData) } - fun parseStates() { - player.states.clear() - SystemLogger.logErr(this::class.java, "Parsing states") - 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) @@ -212,15 +192,13 @@ class PlayerSaveParser(val player: Player) { } fun parseConfigs() { - val configs = saveFile!!["configs"] as JSONArray + val configs = saveFile!!["configs"] as? JSONArray ?: return for (config in configs) { val c = config as JSONObject val index = (c.get("index") as String).toInt() if(index == 1048) continue - if(patch_varps.contains(index)) continue - if(bin_varps.contains(index)) continue val value = (c.get("value") as String).toInt() - player.configManager.savedConfigurations[index] = value + player.varpMap[index] = value } } @@ -300,6 +278,8 @@ class PlayerSaveParser(val player: Player) { val bBars = coreData["blastBars"] as? JSONArray val bOre = coreData["blastOre"] as? JSONArray val bCoal = coreData["blastCoal"] as? JSONArray + val varpData = coreData["varp"] as? JSONArray + val timerData = coreData["timers"] as? JSONObject val location = coreData["location"] as String val bankTabData = coreData["bankTabs"] if (bankTabData != null) { @@ -328,10 +308,21 @@ 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) { + for (e in varpData) { + e ?: continue + val varp = e as JSONObject + val index = varp["index"]?.toString()?.toInt() ?: continue + val value = varp["value"]?.toString()?.toInt() ?: continue + player.varpMap[index] = value + player.saveVarp[index] = true + } + } + + if (timerData != null) + player.timers.parseTimers(timerData) } fun parseSkills() { @@ -339,15 +330,17 @@ 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() + player.skills.experienceMultiplier = saveFile!!["exp_multiplier"].toString().toDouble() +/* Snowscape: removing this function since it causes the default exp rate to override player selection, and not allow 10x or higher multipliers if (GameWorld.settings?.default_xp_rate != 5.0) { - player.skills.experienceMutiplier = GameWorld.settings?.default_xp_rate!! + player.skills.experienceMultiplier = GameWorld.settings?.default_xp_rate!! } 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")) { val milestone: JSONObject = saveFile!!["milestone"] as JSONObject player.skills.combatMilestone = (milestone.get("combatMilestone")).toString().toInt() @@ -361,5 +354,56 @@ 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() + } + } -} \ No newline at end of file + //Load the custom data added by snowscape + fun parseSnowscapeData() { + saveFile ?: return + if (!(saveFile!!.containsKey("snowscape_data"))) return + + val snowscapeData = saveFile!!["snowscape_data"] as JSONObject + + val plainSatchel = snowscapeData["plainSatchel"] + if (plainSatchel != null) player.plainSatchel.parse(plainSatchel as JSONArray) + + val greenSatchel = snowscapeData["greenSatchel"] + if (greenSatchel != null) player.greenSatchel.parse(greenSatchel as JSONArray) + + val redSatchel = snowscapeData["redSatchel"] + if (redSatchel != null) player.redSatchel.parse(redSatchel as JSONArray) + + val blackSatchel = snowscapeData["blackSatchel"] + if (blackSatchel != null) player.blackSatchel.parse(blackSatchel as JSONArray) + + val goldSatchel = snowscapeData["goldSatchel"] + if (goldSatchel != null) player.goldSatchel.parse(goldSatchel as JSONArray) + + val runeSatchel = snowscapeData["runeSatchel"] + if (runeSatchel != null) player.runeSatchel.parse(runeSatchel as JSONArray) + + val summoningPouches = snowscapeData["summoningPouches"] + if (summoningPouches != null) player.summoningPouches.parse(summoningPouches as JSONArray) + + val costumeRoomBankTabs = snowscapeData["costumeRoomBankTabs"] + if (costumeRoomBankTabs != null) { + val tabData = costumeRoomBankTabs as JSONArray + for (i in tabData) { + i ?: continue + val tab = i as JSONObject + val index = tab["index"].toString().toInt() + val startSlot = tab["startSlot"].toString().toInt() + player.costumeRoomBank.tabStartSlot[index] = startSlot + } + } + + val costumeRoomBank = snowscapeData["costumeRoomBank"] + if (costumeRoomBank != null) player.costumeRoomBank.parse(costumeRoomBank as JSONArray) + + } +} 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 59d00d48f..56d018d9a 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 @@ -1,23 +1,26 @@ package core.game.node.entity.player.info.login +import content.global.skill.summoning.familiar.BurdenBeast +import content.global.skill.summoning.pet.Pet +import core.ServerConstants import core.api.PersistPlayer +import core.api.log import core.game.container.Container import core.game.node.entity.player.Player import core.game.node.entity.player.link.IronmanMode import core.game.node.entity.skill.Skills -import content.global.skill.summoning.familiar.BurdenBeast -import content.global.skill.summoning.pet.Pet +import core.game.world.map.Location +import core.tools.Log +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.withContext import org.json.simple.JSONArray import org.json.simple.JSONObject -import core.ServerConstants -import core.tools.SystemLogger import java.io.File import java.io.FileWriter import java.io.IOException -import java.lang.Math.ceil -import java.util.* import javax.script.ScriptEngineManager +import java.util.* /** @@ -30,22 +33,20 @@ class PlayerSaver (val player: Player){ companion object { val contentHooks = ArrayList() } - private fun populate(): JSONObject { + fun populate(): JSONObject { val saveFile = JSONObject() + saveVersion(saveFile) saveCoreData(saveFile) saveSkills(saveFile) saveSettings(saveFile) saveQuests(saveFile) saveAppearance(saveFile) saveSpellbook(saveFile) - saveVarps(saveFile) saveSavedData(saveFile) saveAutocast(saveFile) - saveConfigs(saveFile) savePlayerMonitor(saveFile) saveMusicPlayer(saveFile) saveFamiliarManager(saveFile) - saveStateManager(saveFile) saveBankPinData(saveFile) saveHouseData(saveFile) saveAchievementData(saveFile) @@ -54,50 +55,58 @@ class PlayerSaver (val player: Player){ saveStatManager(saveFile) saveAttributes(saveFile) savePouches(saveFile) + saveSnowscapeData(saveFile) contentHooks.forEach { it.savePlayer(player, saveFile) } return saveFile } fun save() = runBlocking { if (!player.details.saveParsed) return@runBlocking - val manager = ScriptEngineManager() - val scriptEngine = manager.getEngineByName("JavaScript") - if (scriptEngine == null) { - SystemLogger.logErr(this::class.java, "Cannot save: Failed to load ScriptEngineManager, this is a known issue on non Java-11 versions. Set your Java version to 11 to avoid further bugs!") - return@runBlocking - } - scriptEngine.put("jsonString", populate().toJSONString()) - scriptEngine.eval("result = JSON.stringify(JSON.parse(jsonString), null, 2)") - val prettyPrintedJson = scriptEngine["result"] as String + val json: String + if (ServerConstants.JAVA_VERSION < 11) { + val manager = ScriptEngineManager() + val scriptEngine = manager.getEngineByName("JavaScript") + if (scriptEngine == null) { + log(this::class.java, Log.ERR, "Cannot save: Failed to load ScriptEngineManager, this is a known issue on non Java-11 versions. Set your Java version to 11 to avoid further bugs!") + return@runBlocking + } + scriptEngine.put("jsonString", populate().toJSONString()) + scriptEngine.eval("result = JSON.stringify(JSON.parse(jsonString), null, 2)") + json = scriptEngine["result"] as String + } else json = populate().toJSONString() try { if(!File("${ServerConstants.PLAYER_SAVE_PATH}${player.name}.json").exists()){ File("${ServerConstants.PLAYER_SAVE_PATH}").mkdirs() - File("${ServerConstants.PLAYER_SAVE_PATH}${player.name}.json").createNewFile() + withContext(Dispatchers.IO) { + File("${ServerConstants.PLAYER_SAVE_PATH}${player.name}.json").createNewFile() + } } - FileWriter("${ServerConstants.PLAYER_SAVE_PATH}${player.name}.json").use { file -> - file.write(prettyPrintedJson) - file.flush() + withContext(Dispatchers.IO) { + FileWriter("${ServerConstants.PLAYER_SAVE_PATH}${player.name}.json").use { file -> + file.write(json) + file.flush() + } } } catch (e: IOException) { e.printStackTrace() } } - fun saveVarps(root: JSONObject){ - - player.varpManager.save(root) - } fun savePouches(root: JSONObject){ 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() for(key in player.gameAttributes.savedAttributes){ val value = player.gameAttributes.attributes[key] value ?: continue - val isExpirable = player.gameAttributes.keyExpirations.containsKey(key); + val isExpirable = player.gameAttributes.keyExpirations.containsKey(key) val attr = JSONObject() val type = when(value){ is Int -> "int" @@ -106,7 +115,8 @@ class PlayerSaver (val player: Player){ is Short -> "short" is String -> "str" is Byte -> "byte" - else -> "null".also { SystemLogger.logWarn(this::class.java, "Invalid attribute type for key: $key in PlayerSaver.kt Line 115") } + is Location -> "location" + else -> "null".also { log(this::class.java, Log.WARN, "Invalid attribute type for key: $key in PlayerSaver.kt Line 115") } } attr.put("type",type) attr.put("key",key) @@ -181,6 +191,7 @@ class PlayerSaver (val player: Player){ fun saveHouseData(root: JSONObject){ val manager = player.houseManager val houseData = JSONObject() + if (manager.isLocked) houseData.put("locked", true) houseData.put("location",manager.location.ordinal.toString()) houseData.put("style",manager.style.ordinal.toString()) if(manager.hasServant()){ @@ -252,33 +263,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()) - detail.put("stage",it.value.stage.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).pet.babyItemId.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()) @@ -307,21 +308,6 @@ class PlayerSaver (val player: Player){ } - fun saveConfigs(root: JSONObject){ - val configs = JSONArray() - var idx = 0 - player.configManager.savedConfigurations.map { - if(it != 0){ - val config = JSONObject() - config.put("index",idx.toString()) - config.put("value",it.toString()) - configs.add(config) - } - idx++ - } - root.put("configs",configs) - } - fun saveAutocast(root: JSONObject){ player.properties.autocastSpell ?: return val spell = JSONObject() @@ -567,19 +553,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()) @@ -614,15 +600,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() @@ -650,6 +627,48 @@ class PlayerSaver (val player: Player){ val locStr = "${loctemp.x},${loctemp.y},${loctemp.z}" coreData.put("location",locStr) + val varpData = JSONArray() + for ((index, value) in player.varpMap) { + if (!(player.saveVarp[index] ?: false)) continue + if (value == 0) continue + + val varpObj = JSONObject() + varpObj["index"] = index.toString() + varpObj["value"] = value.toString() + varpData.add(varpObj) + } + coreData.put("varp", varpData) + + val timerData = JSONObject() + player.timers.saveTimers(timerData) + coreData.put("timers", timerData) + root.put("core_data",coreData) } + + // Function to save the custom data added by snowscape + fun saveSnowscapeData(root: JSONObject){ + val snowscapeData = JSONObject() + + snowscapeData.put("plainSatchel", saveContainer(player.plainSatchel)) + snowscapeData.put("greenSatchel", saveContainer(player.greenSatchel)) + snowscapeData.put("redSatchel", saveContainer(player.redSatchel)) + snowscapeData.put("blackSatchel", saveContainer(player.blackSatchel)) + snowscapeData.put("goldSatchel", saveContainer(player.goldSatchel)) + snowscapeData.put("runeSatchel", saveContainer(player.runeSatchel)) + + snowscapeData.put("summoningPouches", saveContainer(player.summoningPouches)) + + val costumeRoomBankTabs = JSONArray() + for(i in player.costumeRoomBank.tabStartSlot.indices){ + val tab = JSONObject() + tab.put("index",i.toString()) + tab.put("startSlot",player.costumeRoomBank.tabStartSlot[i].toString()) + costumeRoomBankTabs.add(tab) + } + snowscapeData.put("costumeRoomBankTabs",costumeRoomBankTabs) + snowscapeData.put("costumeRoomBank", saveContainer(player.costumeRoomBank)) + + root.put("snowscape_data",snowscapeData) + } } 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 4d36a8b0e..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 @@ -14,6 +14,7 @@ import core.net.packet.out.RepositionChild; import core.net.packet.out.StringPacket; import core.tools.RandomFunction; import org.json.simple.JSONObject; +import org.rs09.consts.Sounds; import java.util.ArrayList; import java.util.Arrays; @@ -21,6 +22,9 @@ import java.util.Collections; import java.util.List; import java.util.concurrent.TimeUnit; +import static core.api.ContentAPIKt.*; + + /** * Manages the players bank pin. * @author Vexia @@ -145,7 +149,7 @@ public class BankPinManager { player.getInterfaceManager().close(); tempPin = null; sendMessages(messages); - player.getAudioManager().send(1040); + playAudio(player, Sounds.PIN_PENDING_1040); player.getInterfaceManager().open(new Component(14)); } @@ -216,36 +220,36 @@ public class BankPinManager { public void handleConfirmInterface(int button) { boolean confirm = button != 91; switch (status) { - case NO_PIN: - if (!confirm) { - toggleConfirmInterface(false); - player.getInterfaceManager().close(); - openSettings("No changes made."); - break; - } - openPin(); - break; - case PENDING: - if (confirm) { - unlock(); - } else { - cancelPin("The PIN has been cancelled", "and will NOT be set.", "", "You still do not have a Bank", "PIN."); - } - break; - case ACTIVE: - if (confirm) { - if (unlocked) { - cancelPin("Your Bank PIN has now been", "deleted.", "", "This means that there is no", "PIN protection on your bank", "account."); - } else { - deleting = true; - openPin(); + case NO_PIN: + if (!confirm) { + toggleConfirmInterface(false); + player.getInterfaceManager().close(); + openSettings("No changes made."); + break; } - } else { - openSettings("No changes made."); - } - break; - default: - break; + openPin(); + break; + case PENDING: + if (confirm) { + unlock(); + } else { + cancelPin("The PIN has been cancelled", "and will NOT be set.", "", "You still do not have a Bank", "PIN."); + } + break; + case ACTIVE: + if (confirm) { + if (unlocked) { + cancelPin("Your Bank PIN has now been", "deleted.", "", "This means that there is no", "PIN protection on your bank", "account."); + } else { + deleting = true; + openPin(); + } + } else { + openSettings("No changes made."); + } + break; + default: + break; } } @@ -265,7 +269,7 @@ public class BankPinManager { for (int i = 60; i < 66; i++) { player.getPacketDispatch().sendInterfaceConfig(14, i, show); } - player.getAudioManager().send(1040); + playAudio(player, Sounds.PIN_PENDING_1040); player.getPacketDispatch().sendInterfaceConfig(14, 89, !show); player.getPacketDispatch().sendInterfaceConfig(14, 91, !show); player.getPacketDispatch().sendInterfaceConfig(14, 87, !show); @@ -338,13 +342,13 @@ public class BankPinManager { public void handlePinStage(int stage) { if (stage == 4) { if (!hasPin() || changeState == 2) { - player.getAudioManager().send(1040); + playAudio(player, Sounds.PIN_PENDING_1040); player.getPacketDispatch().sendString("Now please enter that number again!", 13, 28); shuffleDigits(); return; } else { if (!tempPin.equals(pin)) { - player.getAudioManager().send(1042); + playAudio(player, Sounds.PILLORY_WRONG_2277); player.getInterfaceManager().close(); player.sendMessage("The PIN you entered is incorrect."); if (setTries(getTries() + 1) >= 2) { @@ -364,6 +368,7 @@ public class BankPinManager { return; } unlock(); + playAudio(player, Sounds.PILLORY_SUCCESS_2274); player.sendMessage("You have correctly entered your PIN."); } return; @@ -391,7 +396,7 @@ public class BankPinManager { } shuffleDigits(); if (stage != 0) { - player.getAudioManager().send(1041); + playAudio(player, Sounds.PIN_BEEP_1041); } } @@ -436,8 +441,8 @@ public class BankPinManager { if (stage >= 4) { stage = stage - 4; } - player.getConfigManager().set(562, bitValue); - player.getConfigManager().set(563, digits.get(8) | digits.get(9) << 4 | stage << 26); + setVarp(player, 562, bitValue); + setVarp(player, 563, digits.get(8) | digits.get(9) << 4 | stage << 26); for (int i = 0; i < 9; i++) { int child = (i > 2 ? i + 1 : i) + 11; int positionX = 37 + ((i % 3) * 95) + RandomFunction.random(2, 45); @@ -465,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; - player.getAudioManager().send(1042); - openSettings(messages); } + /** * Checks if the pin is an easy guess. * @return {@code True} if so. @@ -816,4 +829,4 @@ public class BankPinManager { public long getPendingDelay() { return pendingDelay; } -} +} \ No newline at end of file diff --git a/Server/src/main/core/game/node/entity/player/link/ConfigurationManager.java b/Server/src/main/core/game/node/entity/player/link/ConfigurationManager.java deleted file mode 100644 index 992a61ca5..000000000 --- a/Server/src/main/core/game/node/entity/player/link/ConfigurationManager.java +++ /dev/null @@ -1,207 +0,0 @@ -package core.game.node.entity.player.link; - -import core.game.node.entity.player.Player; - -import core.game.system.task.Pulse; -import core.game.world.GameWorld; -import core.net.packet.PacketRepository; -import core.net.packet.context.ConfigContext; -import core.net.packet.out.Config; - -/** - * Manages a player's configurations. - * @author Emperor - */ -@Deprecated -public final class ConfigurationManager { - - /** - * The amount of configurations. - */ - public static final int SIZE = 3500; - - /** - * The player. - */ - private final Player player; - - /** - * The configurations. - */ - private final int[] configurations = new int[SIZE]; - - /** - * The configurations. - */ - private final int[] savedConfigurations = new int[SIZE]; - - /** - * Constructs a new {@code ConfigurationManager} {@code Object}. - * @param player The player. - */ - public ConfigurationManager(Player player) { - this.player = player; - } - - /** - * Initializes the configurations. - */ - public void init() { - for (int i = 0; i < savedConfigurations.length; i++) { - int value = savedConfigurations[i]; - if (value != 0) { - set(i, value, false); - } - } - } - - /** - * Resets the configurations. - */ - public void reset() { - for (int i = 0; i < configurations.length; i++) { - configurations[i] = 0; - } - } - - /** - * Sets a configuration. - * @param config The configuration. - * @param value The value. - */ - public void set(Configuration config, boolean value) { - set(config.id, value); - } - - /** - * Sets a configuration. - * @param id The configuration id. - * @param value The value. - */ - @Deprecated - public void set(int id, boolean value) { - - player.varpManager.get(id).setVarbit(0, value ? 1 : 0).send(player); - } - - /** - * Sets a configuration. - * @param config The configuration. - * @param value The value. - */ - @Deprecated - public void set(Configuration config, int value) { - player.varpManager.get(config.id).setVarbit(0,value).send(player); - } - - /** - * Sets the configuration. - * @param config The configuration id. - * @param value The value. - * @param saved If the configuration should be saved. - */ - public void set(Configuration config, int value, boolean saved) { - set(config.id, value, saved); - } - - /** - * Sets a configuration. - * @param id The configuration id. - * @param value The value. - */ - public void set(int id, int value) { - set(id, value, false); - } - - /** - * Sets a configuration for a set amount of time. - * @param id the id. - * @param value the value. - * @param delay the delay. - */ - @Deprecated - public void set(final int id, final int value, int delay) { - set(id, value); - player.varpManager.get(id).setVarbit(0,value).send(player); - GameWorld.getPulser().submit(new Pulse(delay, player) { - @Override - public boolean pulse() { - player.varpManager.get(id).setVarbit(0,0).send(player); - return true; - } - }); - } - - /** - * Sets a configuration. - * @param id The configuration id. - * @param value The value. - */ - @Deprecated - public void set(int id, int value, boolean saved) { - player.varpManager.get(id).setVarbit(0,value).send(player); - if (saved) { - player.varpManager.flagSave(id, false); - } - } - - public void forceSet(int id, int value, boolean saved){ - PacketRepository.send(Config.class, new ConfigContext(player,id,value)); - } - - /** - * Sends the configuration without caching. - */ - @Deprecated - public void send(int id, int value) { - PacketRepository.send(Config.class, new ConfigContext(player, id, value)); - } - - /** - * Gets the configuration value. - * @param id The config id. - * @return The value. - */ - @Deprecated - public int get(int id) { - if(player.varpManager.get(id).getValue() != 0){ - return player.varpManager.get(id).getValue(); - } - return configurations[id]; - } - - /** - * Holds the configurations. - * @author Emperor - */ - public enum Configuration { - - BRIGHTNESS(166), MUSIC_VOLUME(168), EFFECT_VOLUME(169), MOUSE_BUTTON(170), CHAT_EFFECT(171), RETALIATE(172), RUNNING(173), SPLIT_PRIVATE(287), ACCEPT_AID(427), PC_PORTALS(719), SURROUNDING_VOLUME(872), CLAN_WAR_DATA(1147), ; - - /** - * The config id. - */ - private final int id; - - /** - * Constructs a new {@code Configuration} {@code Object}. - * @param id The config id. - */ - private Configuration(int id) { - this.id = id; - } - - /** - * Gets the id. - * @return The id. - */ - public int getId() { - return id; - } - - } - - public int[] getSavedConfigurations() { - return savedConfigurations; - } -} \ No newline at end of file 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..29878c2f6 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 @@ -821,8 +821,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/InterfaceManager.java b/Server/src/main/core/game/node/entity/player/link/InterfaceManager.java index 47574f569..eaa9ca5da 100644 --- a/Server/src/main/core/game/node/entity/player/link/InterfaceManager.java +++ b/Server/src/main/core/game/node/entity/player/link/InterfaceManager.java @@ -12,10 +12,12 @@ import core.net.packet.context.WindowsPaneContext; import core.net.packet.out.CloseInterface; import core.net.packet.out.Interface; import core.net.packet.out.WindowsPane; +import core.tools.Log; import org.rs09.consts.Components; import content.region.misc.tutisland.handlers.TutorialStage; -import core.tools.SystemLogger; +import static core.api.ContentAPIKt.log; +import static core.api.ContentAPIKt.*; /** @@ -124,7 +126,7 @@ public final class InterfaceManager { public Component openWindowsPane(Component windowsPane, boolean overlap) { this.windowsPane = windowsPane; if (windowsPane.getDefinition().getType() != InterfaceType.WINDOW_PANE) { - SystemLogger.logErr(this.getClass(), "Set interface type to WINDOW_PANE for component " + windowsPane.getId() + ", definition requires updating!"); + log(this.getClass(), Log.WARN, "Set interface type to WINDOW_PANE for component " + windowsPane.getId() + ", definition requires updating!"); windowsPane.getDefinition().setType(InterfaceType.WINDOW_PANE); } @@ -140,7 +142,7 @@ public final class InterfaceManager { public void openWindowsPane(Component windowsPane, int type) { this.windowsPane = windowsPane; if (windowsPane.getDefinition().getType() != InterfaceType.WINDOW_PANE) { - SystemLogger.logErr(this.getClass(), "Set interface type to WINDOW_PANE for component " + windowsPane.getId() + ", definition requires updating!"); + log(this.getClass(), Log.WARN, "Set interface type to WINDOW_PANE for component " + windowsPane.getId() + ", definition requires updating!"); windowsPane.getDefinition().setType(InterfaceType.SINGLE_TAB); } @@ -203,6 +205,10 @@ public final class InterfaceManager { player.removeAttribute("runscript"); player.getPacketDispatch().sendRunScript(101, ""); } + // Component 333 is an immediate(no-fading) full-screen HD-mode black screen which auto-clears when interrupted. + if (overlay != null && overlay.getId() == 333) { + closeOverlay(); + } if (opened != null && opened.close(player)) { if (opened != null && (!opened.getDefinition().isWalkable() || opened.getId() == 14)) { PacketRepository.send(CloseInterface.class, new InterfaceContext(player, opened.getDefinition().getWindowPaneId(isResizable()), opened.getDefinition().getChildId(isResizable()), opened.getId(), opened.getDefinition().isWalkable())); @@ -268,7 +274,7 @@ public final class InterfaceManager { */ public Component openSingleTab(Component component) { if (component.getDefinition().getType() != InterfaceType.SINGLE_TAB) { - SystemLogger.logErr(this.getClass(), "Set interface type to SINGLE_TAB for component " + component.getId() + ", definition requires updating!"); + log(this.getClass(), Log.WARN, "Set interface type to SINGLE_TAB for component " + component.getId() + ", definition requires updating!"); component.getDefinition().setType(InterfaceType.SINGLE_TAB); } component.open(player); @@ -352,9 +358,9 @@ public final class InterfaceManager { openTab(6, new Component(player.getSpellBookManager().getSpellBook())); // Magic break; case 7: - if (player.getFamiliarManager().hasFamiliar()) { + //if (player.getFamiliarManager().hasFamiliar()) { openTab(7, new Component(662)); - } + //} break; default: openTab(i, new Component(DEFAULT_TABS[i])); @@ -387,9 +393,9 @@ public final class InterfaceManager { openTab(4, new Component(Components.WORNITEMS_387)); // Equipment openTab(5, new Component(Components.PRAYER_271)); // Prayer openTab(6, new Component(player.getSpellBookManager().getSpellBook())); // Magic - if (player.getFamiliarManager().hasFamiliar()) { + //if (player.getFamiliarManager().hasFamiliar()) { openTab(7, new Component(Components.LORE_STATS_SIDE_662)); // summoning. - } + //} openTab(8, new Component(Components.FRIENDS2_550)); // Friends openTab(9, new Component(Components.IGNORE2_551)); // Ignores openTab(10, new Component(Components.CLANJOIN_589)); // Clan chat @@ -453,11 +459,11 @@ public final class InterfaceManager { throw new IllegalStateException("Attack tab can only be instanced as " + WeaponInterface.class.getCanonicalName() + "!"); } if (component.getDefinition().getTabIndex() != slot) { - SystemLogger.logErr(this.getClass(), "Set tab index to " + slot + " for component " + component.getId() + ", definition requires updating!"); + log(this.getClass(), Log.WARN, "Set tab index to " + slot + " for component " + component.getId() + ", definition requires updating!"); component.getDefinition().setTabIndex(slot); } if (component.getDefinition().getType() != InterfaceType.TAB) { - SystemLogger.logErr(this.getClass(), "Set interface type to TAB for component " + component.getId() + ", definition requires updating!"); + log(this.getClass(), Log.WARN, "Set interface type to TAB for component " + component.getId() + ", definition requires updating!"); component.getDefinition().setType(InterfaceType.TAB); } component.open(player); @@ -470,7 +476,7 @@ public final class InterfaceManager { */ public void openTab(Component component) { if (component.getDefinition().getTabIndex() < 0) { - SystemLogger.logErr(this.getClass(), "No component definitions found for tab " + component.getId() + "!"); + log(this.getClass(), Log.WARN, "No component definitions found for tab " + component.getId() + "!"); return; } openTab(component.getDefinition().getTabIndex(), component); @@ -496,11 +502,11 @@ public final class InterfaceManager { PacketRepository.send(Interface.class, new InterfaceContext(player, InterfaceType.CHATBOX.getFixedPaneId(), InterfaceType.CHATBOX.getFixedChildId(), Components.CHATDEFAULT_137, true)); } chatbox = component; - player.getConfigManager().set(334, 1); + setVarp(player, 334, 1); } else { chatbox = component; if (chatbox.getDefinition().getType() != InterfaceType.DIALOGUE && chatbox.getDefinition().getType() != InterfaceType.CHATBOX && chatbox.getDefinition().getType() != InterfaceType.CS_CHATBOX) { - SystemLogger.logErr(this.getClass(), "Set interface type to CHATBOX for component " + component.getId() + ", definition requires updating!"); + log(this.getClass(), Log.WARN, "Set interface type to CHATBOX for component " + component.getId() + ", definition requires updating!"); chatbox.getDefinition().setType(InterfaceType.DIALOGUE); } chatbox.open(player); @@ -567,7 +573,7 @@ public final class InterfaceManager { */ public void setViewedTab(int tabIndex) { if (tabs[tabIndex] == null) { - throw new IllegalStateException("Tab at index " + tabIndex + " is null!"); + return; } currentTabIndex = tabIndex; switch (tabIndex) { @@ -605,31 +611,14 @@ public final class InterfaceManager { return; } overlay = component; - if (overlay.getDefinition().getType() != InterfaceType.OVERLAY) { - SystemLogger.logErr(this.getClass(), "Set interface type to OVERLAY for component " + component.getId() + ", definition requires updating!"); + if (overlay.getDefinition().getType() != InterfaceType.OVERLAY && overlay.getDefinition().getType() != InterfaceType.OVERLAY_B) { + log(this.getClass(), Log.WARN, "Set interface type to OVERLAY for component " + component.getId() + ", definition requires updating!"); overlay.getDefinition().setType(InterfaceType.OVERLAY); overlay.getDefinition().setWalkable(true); } overlay.open(player); } - /** - * Opens the wilderness overlay. - * @param component The component. - */ - public void openWildernessOverlay(Component component) { - if (wildyOverlay != null && !wildyOverlay.close(player)) { - return; - } - wildyOverlay = component; - if (wildyOverlay.getDefinition().getType() != InterfaceType.WILDERNESS_OVERLAY) { - SystemLogger.logErr(this.getClass(), "Set interface type to WILDERNESS_OVERLAY for component " + component.getId() + ", definition requires updating!"); - wildyOverlay.getDefinition().setType(InterfaceType.WILDERNESS_OVERLAY); - wildyOverlay.getDefinition().setWalkable(true); - } - wildyOverlay.open(player); - } - /** * Closes the current overlay. */ @@ -783,4 +772,4 @@ public final class InterfaceManager { packetCount += increment; return count; } -} \ No newline at end of file +} 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 33f6f29e3..53dd6819e 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 @@ -1,8 +1,8 @@ package core.game.node.entity.player.link; -import core.game.node.entity.player.Varp; 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; @@ -11,13 +11,16 @@ import core.game.world.map.RegionManager; import core.game.world.update.flag.chunk.AnimateObjectUpdateFlag; import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Graphics; -import core.game.world.update.flag.player.AnimationFlag; -import core.game.world.update.flag.player.GraphicFlag; +import core.game.world.update.flag.EntityFlag; import core.net.packet.PacketRepository; import core.net.packet.context.*; import core.net.packet.context.DisplayModelContext.ModelType; import core.net.packet.out.*; +import static core.api.ContentAPIKt.log; +import static core.api.ContentAPIKt.*; + + /** * Represents the class used to dispatching packets. * @author Emperor @@ -44,8 +47,8 @@ public final class PacketDispatch { this.context = new PlayerContext(player); } - public void sendVarp(Varp varp){ - PacketRepository.send(Config.class, new ConfigContext(player, varp.getIndex(), varp.getValue())); + public void sendVarp (int index, int value) { + PacketRepository.send(Config.class, new ConfigContext(player, index, value)); } public void sendVarcUpdate(short index, int value) { @@ -64,7 +67,7 @@ public final class PacketDispatch { return; } if (message.length() > 255) { - SystemLogger.logErr(this.getClass(), "Message length out of bounds (" + message + ")!"); + log(this.getClass(), Log.ERR, "Message length out of bounds (" + message + ")!"); message = message.substring(0, 255); } PacketRepository.send(GameMessage.class, new GameMessageContext(player, message)); @@ -199,7 +202,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); } /** @@ -216,6 +224,10 @@ public final class PacketDispatch { PacketRepository.send(DisplayModel.class, new DisplayModelContext(player, ModelType.MODEL, modelID,zoom,interfaceId,childId,new Object())); } + 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)); + } + /** * Send the item on interface packet. * @param itemId The item id. @@ -297,7 +309,7 @@ public final class PacketDispatch { * @param id The animation id. */ public void sendAnimation(int id) { - player.getUpdateMasks().register(new AnimationFlag(new Animation(id))); + player.getUpdateMasks().register(EntityFlag.Animate, new Animation(id)); } /** @@ -306,7 +318,7 @@ public final class PacketDispatch { * @param delay The animation delay. */ public void sendAnimation(int id, int delay) { - player.getUpdateMasks().register(new AnimationFlag(new Animation(id, delay))); + player.getUpdateMasks().register(EntityFlag.Animate, new Animation(id, delay)); } /** @@ -314,7 +326,7 @@ public final class PacketDispatch { * @param id The graphic id. */ public void sendGraphic(int id) { - player.getUpdateMasks().register(new GraphicFlag(new Graphics(id))); + player.getUpdateMasks().register(EntityFlag.SpotAnim, new Graphics(id)); } /** @@ -380,18 +392,18 @@ public final class PacketDispatch { * @param height The graphic height. */ public void sendGraphic(int id, int height) { - player.getUpdateMasks().register(new GraphicFlag(new Graphics(id, height))); + player.getUpdateMasks().register(EntityFlag.SpotAnim, new Graphics(id, height)); } public void sendVarClient(int id, int value, boolean cs2) { PacketRepository.send(Config.class, new ConfigContext(player, id, value, cs2)); } public void sendLeftShiftedVarbit(int varpIndex, int offset, int value){ - player.getConfigManager().set(varpIndex, (value << offset)); + setVarp(player, varpIndex, (value << offset)); } public void sendRightShiftedVarbit(int varpIndex, int offset, int value){ - player.getConfigManager().set(varpIndex, (value >> offset)); + setVarp(player, varpIndex, (value >> offset)); } 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 258c018ea..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,11 +4,10 @@ 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.*; + /** * Holds a player's settings. @@ -128,38 +127,22 @@ public final class Settings { * Updates the settings. */ public void update() { - player.getConfigManager().set(ConfigurationManager.Configuration.BRIGHTNESS, brightness + 1); - player.getConfigManager().set(ConfigurationManager.Configuration.MUSIC_VOLUME, musicVolume); - player.getConfigManager().set(ConfigurationManager.Configuration.EFFECT_VOLUME, soundEffectVolume); - player.getConfigManager().set(ConfigurationManager.Configuration.SURROUNDING_VOLUME, areaSoundVolume); - player.getConfigManager().set(ConfigurationManager.Configuration.MOUSE_BUTTON, singleMouseButton); - player.getConfigManager().set(ConfigurationManager.Configuration.CHAT_EFFECT, disableChatEffects); - player.getConfigManager().set(ConfigurationManager.Configuration.SPLIT_PRIVATE, splitPrivateChat); - player.getConfigManager().set(ConfigurationManager.Configuration.ACCEPT_AID, acceptAid); - player.getConfigManager().set(ConfigurationManager.Configuration.RETALIATE, !player.getProperties().isRetaliating()); - player.getConfigManager().set(ConfigurationManager.Configuration.RUNNING, runToggled); - player.getConfigManager().set(1054, clanChatSetting); - player.getConfigManager().set(1055, assistSetting); - player.getConfigManager().set(300, specialEnergy * 10); - player.getConfigManager().set(43, attackStyleIndex); + setVarp(player, 166, brightness + 1); + setVarp(player, 168, musicVolume); + setVarp(player, 169, soundEffectVolume); + setVarp(player, 872, areaSoundVolume); + setVarp(player, 170, singleMouseButton ? 1 : 0); + setVarp(player, 171, disableChatEffects ? 1 : 0); + setVarp(player, 287, splitPrivateChat ? 1 : 0); + setVarp(player, 427, acceptAid ? 1 : 0); + setVarp(player, 172, !player.getProperties().isRetaliating() ? 1 : 0); + setVarp(player, 173, runToggled ? 1 : 0); + setVarp(player, 1054, clanChatSetting); + setVarp(player, 1055, assistSetting); + setVarp(player, 300, specialEnergy * 10); + setVarp(player, 43, attackStyleIndex); player.getPacketDispatch().sendRunEnergy(); updateChatSettings(); - Pulse pulse = player.getAttribute("energy-restore", null); - if (pulse == null || !pulse.isRunning()) { - pulse = new Pulse(50, player) { - @Override - public boolean pulse() { - if (specialEnergy < 100) { - int heal = 100 - specialEnergy; - setSpecialEnergy(specialEnergy + (heal > 10 ? 10 : heal)); - } - return false; - } - }; - pulse.setTicksPassed(1); - GameWorld.getPulser().submit(pulse); - player.setAttribute("energy-restore", pulse); - } } /** @@ -168,7 +151,7 @@ public final class Settings { */ public void toggleAttackStyleIndex(int index) { this.attackStyleIndex = index; - player.getConfigManager().set(43, attackStyleIndex); + setVarp(player, 43, attackStyleIndex); } /** @@ -212,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()); @@ -301,7 +232,7 @@ public final class Settings { */ public void setSpecialToggled(boolean enable) { specialToggled = !specialToggled; - player.getConfigManager().set(301, specialToggled ? 1 : 0); + setVarp(player, 301, specialToggled ? 1 : 0); } /** @@ -337,7 +268,7 @@ public final class Settings { */ public void setSpecialEnergy(int value) { specialEnergy = value; - player.getConfigManager().set(300, specialEnergy * 10); + setVarp(player, 300, specialEnergy * 10); } /** @@ -353,7 +284,7 @@ public final class Settings { */ public void toggleRetaliating() { player.getProperties().setRetaliating(!player.getProperties().isRetaliating()); - player.getConfigManager().set(172, !player.getProperties().isRetaliating() ? 1 : 0); + setVarp(player, 172, !player.getProperties().isRetaliating() ? 1 : 0); } /** @@ -361,7 +292,7 @@ public final class Settings { */ public void toggleMouseButton() { singleMouseButton = !singleMouseButton; - player.getConfigManager().set(170, singleMouseButton ? 1 : 0); + setVarp(player, 170, singleMouseButton ? 1 : 0); } /** @@ -369,7 +300,7 @@ public final class Settings { */ public void toggleChatEffects() { disableChatEffects = !disableChatEffects; - player.getConfigManager().set(171, disableChatEffects ? 1 : 0); + setVarp(player, 171, disableChatEffects ? 1 : 0); } /** @@ -377,7 +308,7 @@ public final class Settings { */ public void toggleSplitPrivateChat() { splitPrivateChat = !splitPrivateChat; - player.getConfigManager().set(287, splitPrivateChat ? 1 : 0); + setVarp(player, 287, splitPrivateChat ? 1 : 0); } /** @@ -385,7 +316,7 @@ public final class Settings { */ public void toggleAcceptAid() { acceptAid = !acceptAid; - player.getConfigManager().set(427, acceptAid ? 1 : 0); + setVarp(player, 427, acceptAid ? 1 : 0); } /** @@ -401,7 +332,7 @@ public final class Settings { */ public void setRunToggled(boolean enabled) { runToggled = enabled; - player.getConfigManager().set(173, runToggled ? 1 : 0); + setVarp(player, 173, runToggled ? 1 : 0); } /** @@ -441,6 +372,7 @@ public final class Settings { } weight += item.getDefinition().getConfiguration(ItemConfigParser.WEIGHT, 0.0); } + player.getPacketDispatch().sendString((int) weight + " kg", 667, 32); } /** @@ -465,7 +397,7 @@ public final class Settings { */ public void setBrightness(int brightness) { this.brightness = brightness; - player.getConfigManager().set(166, brightness + 1); + setVarp(player, 166, brightness + 1); } /** @@ -482,7 +414,7 @@ public final class Settings { */ public void setMusicVolume(int musicVolume) { this.musicVolume = musicVolume; - player.getConfigManager().set(168, musicVolume); + setVarp(player, 168, musicVolume); } /** @@ -499,7 +431,7 @@ public final class Settings { */ public void setSoundEffectVolume(int soundEffectVolume) { this.soundEffectVolume = soundEffectVolume; - player.getConfigManager().set(169, soundEffectVolume); + setVarp(player, 169, soundEffectVolume); } /** @@ -516,7 +448,7 @@ public final class Settings { */ public void setAreaSoundVolume(int areaSoundVolume) { this.areaSoundVolume = areaSoundVolume; - player.getConfigManager().set(872, areaSoundVolume); + setVarp(player, 872, areaSoundVolume); } /** @@ -610,7 +542,7 @@ public final class Settings { */ public void setClanChatSetting(int clanChatSetting) { this.clanChatSetting = clanChatSetting; - player.getConfigManager().set(1054, clanChatSetting); + setVarp(player, 1054, clanChatSetting); } /** @@ -644,7 +576,7 @@ public final class Settings { */ public void setAssistSetting(int assistSetting) { this.assistSetting = assistSetting; - player.getConfigManager().set(1055, assistSetting); + setVarp(player, 1055, assistSetting); } /** @@ -677,4 +609,4 @@ public final class Settings { this.attackStyleIndex = attackStyleIndex; } -} \ No newline at end of file +} 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 5a6ec9efa..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 @@ -1,19 +1,55 @@ package core.game.node.entity.player.link; +import core.game.container.Container; +import core.game.container.ContainerEvent; +import core.game.container.ContainerListener; import core.game.node.entity.Entity; import core.game.node.entity.player.Player; -import core.game.node.entity.state.EntityState; -import core.game.world.update.flag.player.AppearanceFlag; +import core.game.node.item.Item; +import core.ServerConstants; +import static core.api.ContentAPIKt.*; import java.util.ArrayList; import java.util.List; +import static core.tools.GlobalsKt.colorize; +import static core.game.world.map.zone.impl.WildernessZone.WILDERNESS_PROT_ATTR; + /** * Represents a managing class of the active player skulls. * @author Vexia * @author Emperor */ public final class SkullManager { + public enum SkullIcon { + NONE(-1), + WHITE(0), + RED(1), + BH_RED5(2), + BH_BLUE4(3), + BH_GREEN3(4), + BH_GREY2(5), + BH_BROWN1(6), + SCREAM(7); + public final int id; + SkullIcon(int id) { + this.id = id; + } + public static SkullIcon forId(int id) { + switch(id) { + case 0: return SkullIcon.WHITE; + case 1: return SkullIcon.RED; + case 2: return SkullIcon.BH_RED5; + case 3: return SkullIcon.BH_BLUE4; + case 4: return SkullIcon.BH_GREEN3; + case 5: return SkullIcon.BH_GREY2; + case 6: return SkullIcon.BH_BROWN1; + case 7: return SkullIcon.SCREAM; + default: return SkullIcon.NONE; + } + } + } + /** * The player instance. @@ -50,6 +86,8 @@ public final class SkullManager { */ private boolean skullCheckDisabled; + private boolean deepWilderness; + /** * Constructs a new {@code SkullManager} {@code Object}. * @param player the player. @@ -76,8 +114,8 @@ public final class SkullManager { return; } skullCauses.add(o); - player.clearState("skull"); - player.registerState("skull").init(); + removeTimer (player, "skulled"); + registerTimer (player, spawnTimer("skulled", 2000)); } /** @@ -86,7 +124,7 @@ public final class SkullManager { */ public void setSkullIcon(int skullIcon) { player.getAppearance().setSkullIcon(skullIcon); - player.getUpdateMasks().register(new AppearanceFlag(player)); + player.updateAppearance(); } /** @@ -96,6 +134,7 @@ public final class SkullManager { skullCauses.clear(); setSkullIcon(-1); setSkulled(false); + player.getAppearance().sync(); } /** @@ -119,6 +158,16 @@ public final class SkullManager { * @param level the level to set. */ public void setLevel(int level) { + if (!deepWilderness && level >= 49) + setDeepWilderness(true); + else if (deepWilderness && level < 48) + setDeepWilderness(false); + + if (level > 20) + player.getLocks().lockTeleport (1_000_000); + else + player.getLocks().unlockTeleport(); + this.level = level; } @@ -162,6 +211,10 @@ public final class SkullManager { return wildernessDisabled; } + public boolean hasWildernessProtection() { + return level < 49; + } + /** * Sets the wildernessDisabled. * @param wildernessDisabled The wildernessDisabled to set. @@ -175,9 +228,78 @@ public final class SkullManager { * @return The skulled. */ public boolean isSkulled() { - return skulled; + return skulled || deepWilderness; } + public boolean isDeepWilderness() { + return deepWilderness; + } + + public void setDeepWilderness (boolean deepWildy) { + if(ServerConstants.ENHANCED_DEEP_WILDERNESS && deepWildy) { + updateDWSkullIcon(); + } else { + removeDWSkullIcon(); + } + setSkullCheckDisabled(deepWildy); + deepWilderness = deepWildy; + } + + public static final long DEEP_WILD_DROP_RISK_THRESHOLD = 100000; + public void updateDWSkullIcon() { + if (player.getAttribute("deepwild-value-listener") == null) { + ContainerListener listener = new ContainerListener() { + @Override + public void update(Container c, ContainerEvent event) { + refresh(c); + } + + @Override + public void refresh(Container c) { + updateDWSkullIcon(); + } + }; + player.setAttribute("deepwild-value-listener", listener); + player.getInventory().getListeners().add(listener); + player.getEquipment().getListeners().add(listener); + } + long value = 0; + long maxValue = 0; + for (Item item : player.getInventory().toArray()) { + if (item != null) { + long alchValue = item.getAlchemyValue(); + value += alchValue; + maxValue = Math.max(maxValue, alchValue); + } + } + for (Item item : player.getEquipment().toArray()) { + if (item != null) { + long alchValue = item.getAlchemyValue(); + value += alchValue; + maxValue = Math.max(maxValue, alchValue); + } + } + // Act as if protect item is always active when calculating risk + value -= maxValue; + player.setAttribute("deepwild-value-risk", value); + SkullIcon skull = SkullIcon.BH_BROWN1; + if (value >= DEEP_WILD_DROP_RISK_THRESHOLD) { + skull = SkullIcon.RED; + } + setSkullIcon(skull.id); + } + + public void removeDWSkullIcon() { + setSkullIcon(skulled ? 0 : -1); + ContainerListener listener = player.getAttribute("deepwild-value-listener"); + if (listener != null) { + player.getInventory().getListeners().remove(listener); + player.getEquipment().getListeners().remove(listener); + } + player.removeAttribute("deepwild-value-listener"); + player.removeAttribute("deepwild-value-risk"); + } + /** * Sets the skulled. * @param skulled The skulled to set. 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 f6cbf4f4f..147337cc5 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 @@ -4,12 +4,15 @@ import core.ServerConstants; import core.game.node.entity.Entity; import core.game.node.entity.impl.Animator.Priority; import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.audio.Audio; import core.game.system.task.Pulse; import core.game.world.GameWorld; import core.game.world.map.Location; +import core.game.world.map.zone.ZoneRestriction; import core.game.world.update.flag.context.Animation; import core.game.world.update.flag.context.Graphics; +import org.rs09.consts.Sounds; + +import static core.api.ContentAPIKt.*; /** * Handles the entity teleport. @@ -90,14 +93,17 @@ public class TeleportManager { * @return {@code True} if the player successfully started teleporting. */ public boolean send(Location location, TeleportType type, int teleportType) { - if (teleportType != -1 && entity.isTeleBlocked()) { - if (entity.isPlayer()) { - entity.asPlayer().sendMessage("A magical force has stopped you from teleporting."); + if (teleportType == WILDY_TELEPORT || type == TeleportType.OBELISK) { + if (hasTimerActive(entity, "teleblock")) return false; + } else { + if (!entity.getZoneMonitor().teleport(teleportType, null)) { + return false; + } + if (teleportType != -1 && type != TeleportType.ANCIENT && entity.isTeleBlocked()) { + if (entity.isPlayer()) + entity.asPlayer().sendMessage("A magical force has stopped you from teleporting."); + return false; } - return false; - } - if ((teleportType != WILDY_TELEPORT && type != TeleportType.OBELISK) && !entity.getZoneMonitor().teleport(teleportType, null)) { - return false; } if (teleportType != -1) { if (entity instanceof Player) { @@ -177,6 +183,7 @@ public class TeleportManager { return currentTeleport; } + /** * Represents a NodeType for Teleporter * @author SonicForce41 @@ -189,24 +196,20 @@ public class TeleportManager { NORMAL(new TeleportSettings(8939, 8941, 1576, 1577)) { @Override public Pulse getPulse(final Entity entity, final Location location) { - return new Pulse(1) { + return new TeleportPulse(entity) { int delay = 0; @Override public boolean pulse() { if (delay == 0) { - if (entity instanceof Player) { - entity.asPlayer().getAudioManager().send(new Audio(200), true); - } + playGlobalAudio(entity.getLocation(), Sounds.TELEPORT_ALL_200); entity.getAnimator().forceAnimation(new Animation(getSettings().getStartEmote())); entity.graphics(new Graphics(getSettings().getStartGfx())); } else if (delay == 3) { entity.getProperties().setTeleportLocation(Location.create(location)); fireRandom(entity, location); } else if (delay == 4) { - if (entity instanceof Player) { - entity.asPlayer().getAudioManager().send(new Audio(201), true); - } + playGlobalAudio(entity.getLocation(), Sounds. TELEPORT_REVERSE_201); entity.getAnimator().forceAnimation(new Animation(getSettings().getEndEmote(), Priority.HIGH)); entity.graphics(new Graphics(getSettings().getEndGfx())); return true; @@ -227,12 +230,13 @@ public class TeleportManager { ANCIENT(new TeleportSettings(1979, -1, 392, -1)) { @Override public Pulse getPulse(final Entity entity, final Location location) { - return new Pulse(1) { + return new TeleportPulse(entity) { int delay = 0; @Override public boolean pulse() { if (delay == 0) { + playGlobalAudio(entity.getLocation(), Sounds.BLOCK_TELEPORT_197, 0, 7); entity.getAnimator().forceAnimation(new Animation(getSettings().getStartEmote())); entity.graphics(new Graphics(getSettings().getStartGfx())); } else if (delay == 4) { @@ -260,7 +264,7 @@ public class TeleportManager { LUNAR(new TeleportSettings(1816, -1, 747, -1)) { @Override public Pulse getPulse(final Entity entity, final Location location) { - return new Pulse(1) { + return new TeleportPulse(entity) { int delay = 0; @Override @@ -293,15 +297,13 @@ public class TeleportManager { TELETABS(new TeleportSettings(4731, -1, 678, -1)) { @Override public Pulse getPulse(final Entity entity, final Location location) { - return new Pulse(1) { + return new TeleportPulse(entity) { int delay = 0; @Override public boolean pulse() { if (delay == 0) { - if (entity instanceof Player) { - entity.asPlayer().getAudioManager().send(979); - } + playGlobalAudio(entity.getLocation(), Sounds.POH_TABLET_BREAK_979); entity.getAnimator().forceAnimation(new Animation(4069)); } else if (delay == 2) { entity.getAnimator().forceAnimation(new Animation(getSettings().getStartEmote(), Priority.HIGH)); @@ -331,7 +333,7 @@ public class TeleportManager { HOME(new TeleportSettings(4847, 4857, 800, 804)) { @Override public Pulse getPulse(final Entity entity, final Location location) { - return new Pulse(1) { + return new TeleportPulse(entity) { int count; Player player; @@ -342,7 +344,7 @@ public class TeleportManager { player.getProperties().setTeleportLocation(location); return true; default: - entity.asPlayer().getAudioManager().send(getAudio(count)); + playGlobalAudio(entity.getLocation(), getAudio(count)); player.getPacketDispatch().sendGraphic(HOME_GRAPHICS[count]); player.getPacketDispatch().sendAnimation(HOME_ANIMATIONS[count]); break; @@ -382,7 +384,7 @@ public class TeleportManager { OBELISK(new TeleportSettings(8939, 8941, 661, -1)) { @Override public Pulse getPulse(final Entity entity, final Location location) { - return new Pulse(1) { + return new TeleportPulse(entity) { int delay = 0; @Override @@ -406,15 +408,13 @@ public class TeleportManager { TELE_OTHER(new TeleportSettings(1816, -1, 342, -1)) { @Override public Pulse getPulse(final Entity entity, final Location location) { - return new Pulse(1) { + return new TeleportPulse(entity) { int delay = 0; @Override public boolean pulse() { if (delay == 0) { - if (entity instanceof Player) { - entity.asPlayer().getAudioManager().send(199); - } + playGlobalAudio(entity.getLocation(), Sounds.TELE_OTHER_CAST_199); entity.getAnimator().forceAnimation(new Animation(getSettings().getStartEmote())); entity.graphics(new Graphics(getSettings().getStartGfx())); } else if (delay == 3) { @@ -434,7 +434,7 @@ public class TeleportManager { @Override public Pulse getPulse(final Entity entity, final Location location) { entity.graphics(Graphics.create(569)); - return new Pulse(1, entity) { + return new TeleportPulse(entity) { int delay; @Override @@ -442,9 +442,9 @@ public class TeleportManager { switch (++delay) { case 2: entity.animate(Animation.create(3265)); - if(entity instanceof Player) { - ((Player) entity).getAudioManager().send(1098, 1); - } + if(entity instanceof Player) { + playAudio(entity.asPlayer(), Sounds.FT_FAIRY_TELEPORT_1098); + } break; case 4: entity.animate(Animation.create(3266)); @@ -462,7 +462,7 @@ public class TeleportManager { PURO_PURO(new TeleportSettings(6601, 1118, -1, -1)) { @Override public Pulse getPulse(final Entity entity, final Location location) { - return new Pulse(1) { + return new TeleportPulse(entity) { int delay = 0; @Override @@ -485,24 +485,20 @@ public class TeleportManager { ECTOPHIAL(new TeleportSettings(8939, 8941, 1587, 1588)) { @Override public Pulse getPulse(final Entity entity, final Location location) { - return new Pulse(1) { + return new TeleportPulse(entity) { int delay = 0; @Override public boolean pulse() { if (delay == 0) { - if (entity instanceof Player) { - entity.asPlayer().getAudioManager().send(200); - } + playGlobalAudio(entity.getLocation(), Sounds.TELEPORT_ALL_200); entity.getAnimator().forceAnimation(new Animation(getSettings().getStartEmote())); entity.graphics(new Graphics(getSettings().getStartGfx())); } else if (delay == 3) { entity.getProperties().setTeleportLocation(Location.create(location)); fireRandom(entity, location); } else if (delay == 4) { - if (entity instanceof Player) { - entity.asPlayer().getAudioManager().send(201); - } + playGlobalAudio(entity.getLocation(), Sounds.TELEPORT_REVERSE_201); entity.getAnimator().forceAnimation(new Animation(getSettings().getEndEmote(), Priority.HIGH)); entity.graphics(new Graphics(getSettings().getEndGfx())); return true; @@ -510,37 +506,26 @@ public class TeleportManager { delay++; return false; } - - @Override - public void stop() { - super.stop(); - entity.unlock(); - entity.lock(4); - } }; } }, CHRISTMAS(new TeleportSettings(7534, -1, 1292, -1)) { @Override public Pulse getPulse(final Entity entity, final Location location) { - return new Pulse(1) { + return new TeleportPulse(entity) { int delay = 0; @Override public boolean pulse() { if (delay == 0) { - if (entity instanceof Player) { - entity.asPlayer().getAudioManager().send(200); - } + playGlobalAudio(entity.getLocation(), Sounds.TELEPORT_ALL_200); entity.getAnimator().forceAnimation(new Animation(getSettings().getStartEmote())); entity.graphics(new Graphics(getSettings().getStartGfx())); } else if (delay == 3) { entity.getProperties().setTeleportLocation(Location.create(location)); fireRandom(entity, location); } else if (delay == 4) { - if (entity instanceof Player) { - entity.asPlayer().getAudioManager().send(201); - } + playGlobalAudio(entity.getLocation(), Sounds.TELEPORT_REVERSE_201); entity.getAnimator().forceAnimation(new Animation(getSettings().getEndEmote(), Priority.HIGH)); entity.graphics(new Graphics(getSettings().getEndGfx())); return true; @@ -561,14 +546,14 @@ public class TeleportManager { CABBAGE(new TeleportSettings(9984, 9986, 1731, 1732)) { @Override public Pulse getPulse(final Entity entity, final Location location) { - return new Pulse(1) { + return new TeleportPulse(entity) { int delay = 0; @Override public boolean pulse() { if (delay == 0) { if (entity instanceof Player) { - entity.asPlayer().getAudioManager().send(5036); + playAudio(entity.asPlayer(), 5036); } entity.getAnimator().forceAnimation(new Animation(getSettings().getStartEmote())); entity.graphics(new Graphics(getSettings().getStartGfx())); @@ -576,7 +561,7 @@ public class TeleportManager { entity.getProperties().setTeleportLocation(Location.create(location)); fireRandom(entity, location); if (entity instanceof Player) { - entity.asPlayer().getAudioManager().send(5034); + playAudio(entity.asPlayer(), 5034); } entity.getAnimator().forceAnimation(new Animation(getSettings().getEndEmote(), Priority.HIGH)); entity.graphics(new Graphics(getSettings().getEndGfx())); @@ -598,24 +583,20 @@ public class TeleportManager { ENTRANA_MAGIC_DOOR(new TeleportSettings(10100, 9013, 1745, 1747)) { // @Override public Pulse getPulse(final Entity entity, final Location location) { - return new Pulse(1) { + return new TeleportPulse(entity) { int delay = 0; @Override public boolean pulse() { if (delay == 0) { - if (entity instanceof Player) { - entity.asPlayer().getAudioManager().send(200); - } + playGlobalAudio(entity.getLocation(), Sounds.TELEPORT_ALL_200); entity.getAnimator().forceAnimation(new Animation(getSettings().getStartEmote())); entity.graphics(new Graphics(getSettings().getStartGfx())); } else if (delay == 3) { entity.getProperties().setTeleportLocation(Location.create(location)); fireRandom(entity, location); } else if (delay == 4) { - if (entity instanceof Player) { - entity.asPlayer().getAudioManager().send(201); - } + playGlobalAudio(entity.getLocation(), Sounds.TELEPORT_REVERSE_201); entity.getAnimator().forceAnimation(new Animation(getSettings().getEndEmote(), Priority.HIGH)); entity.graphics(new Graphics(getSettings().getEndGfx())); return true; @@ -636,7 +617,7 @@ public class TeleportManager { MINIGAME(new TeleportSettings(6601, 1118, -1, -1)) { @Override public Pulse getPulse(final Entity entity, final Location location) { - return new Pulse(1) { + return new TeleportPulse(entity) { int delay = 0; Player player; @@ -673,7 +654,7 @@ public class TeleportManager { INSTANT(new TeleportSettings(-1, -1, -1, -1)) { @Override public Pulse getPulse(final Entity entity, final Location location) { - return new Pulse(1) { + return new TeleportPulse(entity) { int delay = 0; @Override @@ -824,3 +805,4 @@ public class TeleportManager { this.teleportType = teleportType; } } + diff --git a/Server/src/main/core/game/node/entity/player/link/TeleportPulse.java b/Server/src/main/core/game/node/entity/player/link/TeleportPulse.java new file mode 100644 index 000000000..31d76f3b0 --- /dev/null +++ b/Server/src/main/core/game/node/entity/player/link/TeleportPulse.java @@ -0,0 +1,24 @@ +package core.game.node.entity.player.link; + +import core.game.system.task.Pulse; +import core.game.node.entity.Entity; + +abstract class TeleportPulse extends Pulse { + private Entity entity; + + public TeleportPulse(Entity e) { this.entity = e; } + + public abstract boolean pulse(); + + @Override + public void start() { + entity.setAttribute("tele-pulse", this); + super.start(); + } + + @Override + public void stop() { + entity.removeAttribute("tele-pulse"); + super.stop(); + } +} diff --git a/Server/src/main/core/game/node/entity/player/link/WarningMessages.java b/Server/src/main/core/game/node/entity/player/link/WarningMessages.java index fad53a42b..11731ff1d 100644 --- a/Server/src/main/core/game/node/entity/player/link/WarningMessages.java +++ b/Server/src/main/core/game/node/entity/player/link/WarningMessages.java @@ -8,6 +8,9 @@ import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; +import static core.api.ContentAPIKt.*; + + /** * Handles the warning messages of a player. * @author Vexia @@ -38,7 +41,7 @@ public final class WarningMessages { * @param player the player. */ private void refresh(final Player player) { - player.getConfigManager().set(CONFIG, getConfigValue(), true); + setVarp(player, CONFIG, getConfigValue(), true); } /** diff --git a/Server/src/main/core/game/node/entity/player/link/appearance/Appearance.java b/Server/src/main/core/game/node/entity/player/link/appearance/Appearance.java index b639477b5..e4c5ecfb4 100644 --- a/Server/src/main/core/game/node/entity/player/link/appearance/Appearance.java +++ b/Server/src/main/core/game/node/entity/player/link/appearance/Appearance.java @@ -8,7 +8,6 @@ import core.game.node.entity.impl.Animator.Priority; import core.game.node.entity.player.Player; import core.game.node.item.Item; import core.game.world.update.flag.context.Animation; -import core.game.world.update.flag.player.AppearanceFlag; import org.json.simple.JSONArray; import org.json.simple.JSONObject; import core.game.system.config.ItemConfigParser; @@ -190,7 +189,7 @@ public final class Appearance { * Method used to sync this appearance with the client. */ public void sync() { - player.getUpdateMasks().register(new AppearanceFlag(player)); + player.updateAppearance(); } /** @@ -546,6 +545,8 @@ public final class Appearance { * @return The render animation id. */ public int getRenderAnimation() { + if (player.getAttribute("render-anim-override") != null) + return player.getAttribute("render-anim-override", renderAnimationId); return renderAnimationId; } diff --git a/Server/src/main/core/game/node/entity/player/link/audio/Audio.java b/Server/src/main/core/game/node/entity/player/link/audio/Audio.java deleted file mode 100644 index 8fdb5e5cd..000000000 --- a/Server/src/main/core/game/node/entity/player/link/audio/Audio.java +++ /dev/null @@ -1,155 +0,0 @@ -package core.game.node.entity.player.link.audio; - -import core.game.node.entity.player.Player; -import core.game.world.map.Location; - -import java.util.List; - -/** - * An audio piece to play. - * @author Vexia - */ -public class Audio { - - /** - * The default volume. - */ - public static final int VOLUME = 10; - - /** - * The default delay. - */ - public static final int DELAY = 0; - - /** - * The default radius in tiles of positional audio in [0,16). - */ - public static final int RADIUS = 15; - - /** - * The id of the audio piece. - */ - private final int id; - - /** - * The volume. - */ - private final int volume; - - /** - * The delay. - */ - private final int delay; - - /** - * The radius. - */ - private final int radius; - - /** - * Constructs a new {@Code Audio} {@Code Object} - * @param id the id. - * @param volume the volume. - * @param delay the delay. - * @param radius the radius. - */ - public Audio(int id, int volume, int delay, int radius) { - this.id = id; - this.volume = volume; - this.delay = delay; - this.radius = radius; - } - - /** - * Constructs a new {@Code Audio} {@Code Object} - * @param id the id. - * @param volume the volume. - * @param delay the delay. - */ - public Audio(int id, int volume, int delay) { - this(id, volume, delay, RADIUS); - } - - /** - * Constructs a new {@Code Audio} {@Code Object} - * @param id the id. - * @param volume the volume. - */ - public Audio(int id, int volume) { - this(id, volume, DELAY); - } - - /** - * Constructs a new {@Code Audio} {@Code Object} - * @param id the id. - */ - public Audio(int id) { - this(id, VOLUME, 0); - } - - /** - * Sends an audio through the manager. - * @param player the player. - * @param global the global. - */ - public void send(Player player, boolean global) { - send(player, global, null); - } - - public void send(Player player, boolean global, Location loc) { - player.getAudioManager().send(this, global, loc); - } - - /** - * Sends this audio. - * @param player the player. - */ - public void send(Player player) { - send(player, false); - } - - /** - * Sends the sound to a list of players. - * @param players the players. - */ - public void send(List players) { - for (Player p : players) { - if (p == null) { - continue; - } - send(p, false); - } - } - - /** - * Gets the id. - * @return the id - */ - public int getId() { - return id; - } - - /** - * Gets the volume. - * @return the volume - */ - public int getVolume() { - return volume; - } - - /** - * Gets the delay. - * @return the delay - */ - public int getDelay() { - return delay; - } - - /** - * Gets the radius. - * @return the radius - */ - public int getRadius() { - return radius; - } -} diff --git a/Server/src/main/core/game/node/entity/player/link/audio/Audio.kt b/Server/src/main/core/game/node/entity/player/link/audio/Audio.kt new file mode 100644 index 000000000..a1e155769 --- /dev/null +++ b/Server/src/main/core/game/node/entity/player/link/audio/Audio.kt @@ -0,0 +1,29 @@ +package core.game.node.entity.player.link.audio + +/** + * An audio object to play. + * @author Zerken + */ +class Audio + @JvmOverloads + constructor( + @JvmField + val id: Int, + @JvmField + val delay: Int = 0, + @JvmField + val loops: Int = 1, + @JvmField + val radius: Int = defaultAudioRadius + ) { + companion object { + /** + * ----Audio Radius Constants---- + * Values which represent the tile distance/radius from which certain groups of audio can be heard from + * Certain sounds can be heard from a further distance than others + * This is a common place where audio distances can be adjusted if needed + */ + // TODO: Add specific groups of audios to have the correct radius (some sounds should be heard from a farther radius than others) + const val defaultAudioRadius = 8 + } + } \ No newline at end of file diff --git a/Server/src/main/core/game/node/entity/player/link/audio/AudioManager.java b/Server/src/main/core/game/node/entity/player/link/audio/AudioManager.java deleted file mode 100644 index b004afee4..000000000 --- a/Server/src/main/core/game/node/entity/player/link/audio/AudioManager.java +++ /dev/null @@ -1,115 +0,0 @@ -package core.game.node.entity.player.link.audio; - -import core.game.node.entity.player.Player; -import core.game.world.map.Location; -import core.game.world.map.MapDistance; -import core.game.world.map.RegionManager; -import core.net.packet.PacketRepository; -import core.net.packet.context.DefaultContext; -import core.net.packet.out.AudioPacket; - -import java.util.List; - -/** - * Manages audio for a player. - * @author Vexia - */ -public class AudioManager { - - /** - * The player instance. - */ - private final Player player; - - /** - * Constructs a new {@Code AudioManager} {@Code Object} - * @param player the player. - */ - public AudioManager(Player player) { - this.player = player; - } - - /** - * Sends an audio packet. - * @param audioId the audio id. - */ - public void send(int audioId) { - send(audioId, false); - } - - /** - * Sends an audio. - * @param audioId the audio id. - * @param global the global. - */ - public void send(int audioId, boolean global) { - send(new Audio(audioId), global); - } - - /** - * Sends an audio packet. - * @param audioId the audio id. - * @param volume the volume. - */ - public void send(int audioId, int volume) { - send(new Audio(audioId, volume), false); - } - - /** - * Sends an audio packet. - * @param audioId the audio id. - * @param volume the volume. - * @param delay the delay. - */ - public void send(int audioId, int volume, int delay) { - send(new Audio(audioId, volume, delay), false); - } - - /** - * Sends an audio packet. - * @param audio the audio. - */ - public void send(Audio audio) { - send(audio, false); - } - - public void send(Audio audio, boolean global) { - send(audio, global, null); - } - - /** - * Sends an audio packet. - * @param audio the audio. - * @param global if globally heard. - */ - public void send(Audio audio, boolean global, Location loc) { - if (global) { - send(audio, RegionManager.getLocalPlayers(player, MapDistance.SOUND.getDistance()), loc); - return; - } - PacketRepository.send(AudioPacket.class, new DefaultContext(player, audio, loc)); - } - - /** - * Sends an audio packet for a bunch of players. - * @param audio the audio. - * @param players the players. - */ - public void send(Audio audio, List players, Location loc) { - for (Player p : players) { - if (p == null) { - continue; - } - p.getAudioManager().send(audio, false, loc); - } - } - - /** - * Gets the player. - * @return the player - */ - public Player getPlayer() { - return player; - } - -} 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..ddcdddeaf 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); } /** @@ -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/emote/EmoteManager.java b/Server/src/main/core/game/node/entity/player/link/emote/EmoteManager.java index 7f2e691f9..e9c991d80 100644 --- a/Server/src/main/core/game/node/entity/player/link/emote/EmoteManager.java +++ b/Server/src/main/core/game/node/entity/player/link/emote/EmoteManager.java @@ -7,6 +7,8 @@ import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; +import static core.api.ContentAPIKt.setVarp; + /** * Manages the players unlocked/locked emotes. * @author Vexia @@ -52,7 +54,7 @@ public class EmoteManager { if (isUnlocked(Emotes.SLAP_HEAD)) { value1 += 2; } - player.getConfigManager().set(802, value1); // stronghold of // security + setVarp(player, 802, value1, false); int value2 = 0; if (isUnlocked(Emotes.GLASS_BOX)) { value2 += 2; @@ -103,12 +105,12 @@ public class EmoteManager { if (isUnlocked(Emotes.GIVE_THANKS)) { value2 += 16384; } - player.getConfigManager().set(313, value2); // events emotes + setVarp(player, 313, value2, false); int value3 = 0; if (isUnlocked(Emotes.ZOMBIE_HAND)) { value3 = 12; } - player.getConfigManager().set(1085, value3);//zombie hand. + setVarp(player, 1085, value3, false); } /** 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..00f281939 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 @@ -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 1ef960fab..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,10 @@ 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.*; + /** * Handles a music playing for a player. @@ -33,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. @@ -74,7 +75,7 @@ public final class MusicPlayer { */ public void init() { refreshList(); - player.getConfigManager().set(19, looping ? 1 : 0); + setVarp(player, 19, looping ? 1 : 0); int value = 0; for (int i = 0; i < CONFIG_IDS.length; i++) { value |= 2 << i; @@ -92,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. @@ -129,15 +137,14 @@ 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) { - System.out.println("Index out of range [index=" + index + ", list=" + listIndex + ", id=" + entry.getId() + ", name=" + entry.getName() + "]."); continue; } - values[index] |= 1 << (listIndex - (index * 32)); + values[index] |= 1 << (listIndex & 31); } for (int i = 0; i < CONFIG_IDS.length; i++) { - player.getConfigManager().set(CONFIG_IDS[i], values[i]); + setVarp(player, CONFIG_IDS[i], values[i]); } } @@ -166,10 +173,12 @@ public final class MusicPlayer { * @param entry The song. */ public void play(MusicEntry entry) { - PacketRepository.send(MusicPacket.class, new MusicContext(player, entry.getId())); - PacketRepository.send(StringPacket.class, new StringContext(player, entry.getName() + (player.isDebug() ? (" (" + entry.getId() + ")") : ""), 187, 14)); - currentMusicId = entry.getId(); - playing = true; + if (!looping || currentMusicId == entry.getId()) { + PacketRepository.send(MusicPacket.class, new MusicContext(player, entry.getId())); + PacketRepository.send(StringPacket.class, new StringContext(player, entry.getName() + (player.isDebug() ? (" (" + entry.getId() + ")") : ""), 187, 14)); + currentMusicId = entry.getId(); + playing = true; + } } /** @@ -188,10 +197,9 @@ public final class MusicPlayer { public void unlock(int id, boolean play) { MusicEntry entry = MusicEntry.forId(id); if (entry == null) { -// System.out.println("Could not find music entry for id " + id + "!"); 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(); @@ -213,7 +221,7 @@ public final class MusicPlayer { if(GameWorld.getTicks() % 20 == 0){ if(!isPlaying()){ try { - play((MusicEntry) unlocked.values().toArray()[new Random().nextInt(unlocked.values().size())]); + PacketRepository.send(MusicPacket.class, new MusicContext(player, currentMusicId)); } catch (Exception e){} } } @@ -224,7 +232,7 @@ public final class MusicPlayer { */ public void toggleLooping() { looping = !looping; - player.getConfigManager().set(19, looping ? 1 : 0); + setVarp(player, 19, looping ? 1 : 0); } /** @@ -289,6 +297,6 @@ public final class MusicPlayer { */ public void setLooping(boolean looping) { this.looping = looping; - player.getConfigManager().set(19, looping ? 1 : 0); + setVarp(player, 19, looping ? 1 : 0); } -} \ No newline at end of file +} diff --git a/Server/src/main/core/game/node/entity/player/link/prayer/Prayer.java b/Server/src/main/core/game/node/entity/player/link/prayer/Prayer.java index a339c9382..8dcdf8265 100644 --- a/Server/src/main/core/game/node/entity/player/link/prayer/Prayer.java +++ b/Server/src/main/core/game/node/entity/player/link/prayer/Prayer.java @@ -14,11 +14,15 @@ 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.game.event.*; import core.game.world.GameWorld; +import org.rs09.consts.Sounds; import java.util.ArrayList; import java.util.List; +import static core.api.ContentAPIKt.*; + /** * Represents a managing class of a players prayers. * @author Vexia @@ -43,10 +47,6 @@ public final class Prayer { */ public Prayer(Player player) { this.player = 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. - player.varpManager.get(1050).setVarbit(1, 8); } /** @@ -64,26 +64,28 @@ public final class Prayer { * Method used to reset this prayer managers cached prayers. */ public void reset() { + // Immediately clear the lights on the client interface and terminate any bonuses for (PrayerType type : getActive()) { - player.getConfigManager().set(type.getConfig(), 0); + setVarp(player, type.getConfig(), 0, false); + player.dispatch(new PrayerDeactivatedEvent(type)); } getActive().clear(); - // Clear the overhead prayer icon a tick later - GameWorld.getPulser().submit(new Pulse(1) { - @Override - public boolean pulse() { - player.getAppearance().setHeadIcon(-1); - player.getAppearance().sync(); - return true; - } - }); + // Clear the overhead prayer icon a tick later + GameWorld.getPulser().submit(new Pulse(1) { + @Override + public boolean pulse() { + player.getAppearance().setHeadIcon(-1); + player.getAppearance().sync(); + return true; + } + }); } /** * Starts the redemption effect. */ public void startRedemption() { - player.getAudioManager().send(2681); + playAudio(player, Sounds.REDEMPTION_HEAL_2681); player.graphics(Graphics.create(436)); player.getSkills().heal((int) (player.getSkills().getStaticLevel(Skills.PRAYER) * 0.25)); player.getSkills().setPrayerPoints(0.0); @@ -103,7 +105,7 @@ public final class Prayer { } } } - player.getAudioManager().send(159); + playAudio(player, Sounds.FIREBREATH_159); player.graphics(Graphics.create(437)); int maximum = (int) (player.getSkills().getStaticLevel(Skills.PRAYER) * 0.25) - 1; if (killer != player && killer.getLocation().withinDistance(player.getLocation(), 1)) { @@ -132,7 +134,8 @@ public final class Prayer { if(prayerActiveTicks > 0 && prayerActiveTicks % 2 == 0){ if(getPlayer().getSkills().getPrayerPoints() == 0){ - getPlayer().getAudioManager().send(2672); + playAudio(getPlayer(), Sounds.PRAYER_DRAIN_2672); + getPlayer().sendMessage("You have run out of prayer points; you must recharge at an altar."); reset(); return; } 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 8f4e138a3..5b8b3e8a1 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 @@ -2,15 +2,19 @@ package core.game.node.entity.player.link.prayer; import core.game.node.entity.player.link.diary.DiaryType; import core.game.node.entity.skill.SkillBonus; -import core.game.node.entity.skill.SkillRestoration; import core.game.node.entity.skill.Skills; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.audio.Audio; import core.game.world.map.zone.ZoneBorders; import core.tools.StringUtils; +import core.game.event.*; +import org.rs09.consts.Sounds; import java.util.List; +import static core.api.ContentAPIKt.*; + + /** * Represents a prayer type. * @author Vexia @@ -18,33 +22,33 @@ import java.util.List; * @author jamix77 */ public enum PrayerType { - THICK_SKIN(1, 12, 83, 5, PrayerCategory.BABY_BLUE, 10000, new SkillBonus(Skills.DEFENCE, 0.05)), - BURST_OF_STRENGTH(4, 12, 84, 7, PrayerCategory.GREEN, 2689, new SkillBonus(Skills.STRENGTH, 0.05)), - CLARITY_OF_THOUGHT(7, 12, 85, 9, PrayerCategory.PINK, 2664, new SkillBonus(Skills.ATTACK, 0.05)), - SHARP_EYE(8, 12, 862, 11, PrayerCategory.LIME_GREEN, 2685, new SkillBonus(Skills.RANGE, 0.05)), - MYSTIC_WILL(9, 12, 863, 13, PrayerCategory.LIME_GREEN, 2670, new SkillBonus(Skills.MAGIC, 0.05)), - ROCK_SKIN(10, 6, 86, 15, PrayerCategory.BABY_BLUE, 2684, new SkillBonus(Skills.DEFENCE, 0.1)), - SUPERHUMAN_STRENGTH(13, 6, 87, 17, PrayerCategory.GREEN, 2689, new SkillBonus(Skills.STRENGTH, 0.1)), - IMPROVED_REFLEXES(16, 6, 88, 19, PrayerCategory.PINK, 2662, new SkillBonus(Skills.ATTACK, 0.1)), - RAPID_RESTORE(19, 26, 89, 21, PrayerCategory.PURPLE, 2679), - RAPID_HEAL(22, 18, 90, 23, PrayerCategory.PURPLE, 2678), - PROTECT_ITEMS(25, 18, 91, 25, PrayerCategory.DARK_GREEN, 1982), - HAWK_EYE(26, 6, 864, 27, PrayerCategory.LIME_GREEN, 2666, new SkillBonus(Skills.RANGE, 0.1)), - MYSTIC_LORE(27, 6, 865, 29, PrayerCategory.LIME_GREEN, 2668, new SkillBonus(Skills.MAGIC, 0.1)), - STEEL_SKIN(28, 3, 92, 31, PrayerCategory.BABY_BLUE, 2687, new SkillBonus(Skills.DEFENCE, 0.15)), - ULTIMATE_STRENGTH(31, 3, 93, 33, PrayerCategory.GREEN, 2691, new SkillBonus(Skills.STRENGTH, 0.15)), - INCREDIBLE_REFLEXES(34, 3, 94, 35, PrayerCategory.PINK, 2667, new SkillBonus(Skills.ATTACK, 0.15)), - PROTECT_FROM_SUMMONING(35, 2, 1168, 53, PrayerCategory.DARK_BROWN, PrayerCategory.MAGENTA, new Audio(4262)), - PROTECT_FROM_MAGIC(37, 3, 95, 37, PrayerCategory.LIGHT_BROWN, 2675), - PROTECT_FROM_MISSILES(40, 3, 96, 39, PrayerCategory.LIGHT_BROWN, 2677), - PROTECT_FROM_MELEE(43, 4, 97, 41, PrayerCategory.LIGHT_BROWN, 2676), - EAGLE_EYE(44, 3, 866, 43, PrayerCategory.LIME_GREEN, 2666, new SkillBonus(Skills.RANGE, 0.15)), - MYSTIC_MIGHT(45, 3, 867, 45, PrayerCategory.LIME_GREEN, 2669, new SkillBonus(Skills.MAGIC, 0.15)), - RETRIBUTION(46, 12, 98, 47, PrayerCategory.LIGHT_BROWN, PrayerCategory.MAGENTA, new Audio(10000)), - REDEMPTION(49, 6, 99, 49, PrayerCategory.LIGHT_BROWN, PrayerCategory.MAGENTA, new Audio(2678)), - SMITE(52, 2, 100, 51, PrayerCategory.LIGHT_BROWN, PrayerCategory.MAGENTA, new Audio(2685)), - CHIVALRY(60, 2, 1052, 55, PrayerCategory.PINK, 3826, 65, new SkillBonus(Skills.DEFENCE, 0.2), new SkillBonus(Skills.STRENGTH, 0.18), new SkillBonus(Skills.ATTACK, 0.15)), - PIETY(70, 2, 1053, 57, PrayerCategory.PINK, 3825, 70, new SkillBonus(Skills.DEFENCE, 0.25), new SkillBonus(Skills.STRENGTH, 0.23), new SkillBonus(Skills.ATTACK, 0.2)); + THICK_SKIN(1, 30, 83, 5, PrayerCategory.BABY_BLUE, Sounds.THICK_SKIN_2690, new SkillBonus(Skills.DEFENCE, 0.05)), + BURST_OF_STRENGTH(4, 60, 84, 7, PrayerCategory.GREEN, Sounds.STRENGTH_BURST_2688, new SkillBonus(Skills.STRENGTH, 0.05)), + CLARITY_OF_THOUGHT(7, 60, 85, 9, PrayerCategory.PINK, Sounds.CLARITY_2664, new SkillBonus(Skills.ATTACK, 0.05)), + SHARP_EYE(8, 30, 862, 11, PrayerCategory.LIME_GREEN, Sounds.SHARP_EYE_2685, new SkillBonus(Skills.RANGE, 0.05)), + MYSTIC_WILL(9, 30, 863, 13, PrayerCategory.LIME_GREEN, Sounds.MYSTIC_WILL_2670, new SkillBonus(Skills.MAGIC, 0.05)), + ROCK_SKIN(10, 15, 86, 15, PrayerCategory.BABY_BLUE, Sounds.ROCK_SKIN_2684, new SkillBonus(Skills.DEFENCE, 0.1)), + SUPERHUMAN_STRENGTH(13, 30, 87, 17, PrayerCategory.GREEN, Sounds.SUPERHUMAN_STRENGTH_2689, new SkillBonus(Skills.STRENGTH, 0.1)), + IMPROVED_REFLEXES(16, 30, 88, 19, PrayerCategory.PINK, Sounds.IMPROVED_REFLEXES_2662, new SkillBonus(Skills.ATTACK, 0.1)), + RAPID_RESTORE(19, 30, 89, 21, PrayerCategory.PURPLE, Sounds.RAPID_RESTORE_2679), + RAPID_HEAL(22, 20, 90, 23, PrayerCategory.PURPLE, Sounds.RAPID_HEAL_2678), + PROTECT_ITEMS(25, 20, 91, 25, PrayerCategory.DARK_GREEN, Sounds.PROTECT_ITEMS_1982), + HAWK_EYE(26, 15, 864, 27, PrayerCategory.LIME_GREEN, Sounds.HAWK_EYE_2666, new SkillBonus(Skills.RANGE, 0.1)), + MYSTIC_LORE(27, 15, 865, 29, PrayerCategory.LIME_GREEN, Sounds.MYSTIC_2668, new SkillBonus(Skills.MAGIC, 0.1)), + STEEL_SKIN(28, 10, 92, 31, PrayerCategory.BABY_BLUE, Sounds.STEEL_SKIN_2687, new SkillBonus(Skills.DEFENCE, 0.15)), + ULTIMATE_STRENGTH(31, 20, 93, 33, PrayerCategory.GREEN, Sounds.ULTIMATE_STRENGTH_2691, new SkillBonus(Skills.STRENGTH, 0.15)), + INCREDIBLE_REFLEXES(34, 20, 94, 35, PrayerCategory.PINK, Sounds.INCREDIBLE_REFLEXES_2667, new SkillBonus(Skills.ATTACK, 0.15)), + PROTECT_FROM_SUMMONING(35, 3, 1168, 53, PrayerCategory.DARK_BROWN, PrayerCategory.MAGENTA, new Audio(4262)), + PROTECT_FROM_MAGIC(37, 3, 95, 37, PrayerCategory.LIGHT_BROWN, Sounds.PROTECT_FROM_MAGIC_2675), + PROTECT_FROM_MISSILES(40, 3, 96, 39, PrayerCategory.LIGHT_BROWN, Sounds.PROTECT_FROM_MISSILES_2677), + PROTECT_FROM_MELEE(43, 3, 97, 41, PrayerCategory.LIGHT_BROWN, Sounds.PROTECT_FROM_MELEE_2676), + EAGLE_EYE(44, 10, 866, 43, PrayerCategory.LIME_GREEN, Sounds.EAGLE_EYE_2665, new SkillBonus(Skills.RANGE, 0.15)), + MYSTIC_MIGHT(45, 10, 867, 45, PrayerCategory.LIME_GREEN, Sounds.MYSTIC_MIGHT_2669, new SkillBonus(Skills.MAGIC, 0.15)), + RETRIBUTION(46, 12, 98, 47, PrayerCategory.LIGHT_BROWN, PrayerCategory.MAGENTA, new Audio(2682)), + REDEMPTION(49, 6, 99, 49, PrayerCategory.LIGHT_BROWN, PrayerCategory.MAGENTA, new Audio(2680)), + SMITE(52, 2, 100, 51, PrayerCategory.LIGHT_BROWN, PrayerCategory.MAGENTA, new Audio(2686)), + CHIVALRY(60, 4, 1052, 55, PrayerCategory.PINK, Sounds.KR_CHIVALRY_3826, 65, new SkillBonus(Skills.DEFENCE, 0.2), new SkillBonus(Skills.STRENGTH, 0.18), new SkillBonus(Skills.ATTACK, 0.15), new SkillBonus(Skills.MAGIC, 0.15), new SkillBonus(Skills.RANGE, 0.15)), + PIETY(70, 3, 1053, 57, PrayerCategory.PINK, Sounds.KR_PIETY_3825, 70, new SkillBonus(Skills.DEFENCE, 0.25), new SkillBonus(Skills.STRENGTH, 0.23), new SkillBonus(Skills.ATTACK, 0.2), new SkillBonus(Skills.MAGIC, 0.2), new SkillBonus(Skills.RANGE, 0.2)); /** * Represents the a cache of objects related to prayers in order to decide @@ -59,6 +63,7 @@ public enum PrayerType { /** * The drain rate. + * The drain rate X 2 is the number of seconds (not ticks) that it takes to drain 1 point at 0 prayer bonus */ private final int drain; @@ -86,7 +91,7 @@ public enum PrayerType { * The sound. */ private Audio sound; - + /** * The required level of defence to use this prayer. */ @@ -108,9 +113,9 @@ public enum PrayerType { PrayerType(int level, int drain, int config, int button, PrayerCategory restriction, int soundId, SkillBonus... bonuses) { this(level, drain, config, button, restriction, null, new Audio(soundId), bonuses); } - + /** - * + * * Constructs a new @{Code PrayerType} object. * @param level * @param drain @@ -136,9 +141,9 @@ public enum PrayerType { PrayerType(int level, int drain, int config, int button, PrayerCategory restriction, PrayerCategory secondRestriction, Audio sound, SkillBonus... bonuses) { this(level,drain,config,button,restriction,secondRestriction,sound,1,bonuses); } - + /** - * + * * Constructs a new @{Code PrayerType} object. * @param level * @param drain @@ -218,7 +223,7 @@ public enum PrayerType { */ public boolean permitted(final Player player) { if (player.getSkills().getStaticLevel(Skills.PRAYER) < getLevel() || player.getSkills().getStaticLevel(Skills.DEFENCE) < defenceReq) { - player.getAudioManager().send(2673); + 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("_", " ")) + "."); return false; } @@ -231,20 +236,23 @@ public enum PrayerType { * @return True if toggled. */ public boolean toggle(final Player player, final boolean on) { - player.getConfigManager().set(getConfig(), on ? 1 : 0); + setVarp(player, getConfig(), on ? 1 : 0); if (on) { flag(player, this); player.getPrayer().getActive().add(this); iconify(player, getIcon(player, this)); - player.getAudioManager().send(getSound()); + 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)); } else { player.getPrayer().getActive().remove(this); + playAudio(player, Sounds.CANCEL_PRAYER_2663); findNextIcon(player); + 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 1c6aa9efa..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; @@ -7,6 +8,9 @@ import core.plugin.PluginManifest; import core.plugin.PluginType; import java.util.Arrays; +import java.util.Random; + +import static core.api.ContentAPIKt.*; /** * A skeleton plugin for a quest. @@ -21,6 +25,11 @@ public abstract class Quest implements Plugin { */ public static final String RED = ""; + /** + * Represents the bright red string. + */ + public static final String BRIGHT_RED = ""; + /** * Represents the blue string. */ @@ -42,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. @@ -67,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; @@ -108,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); } @@ -117,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) { @@ -129,14 +153,29 @@ public abstract class Quest implements Plugin { player.getQuestRepository().setStage(this, 100); player.getQuestRepository().incrementPoints(getQuestPoints()); player.getQuestRepository().syncronizeTab(player); - player.getConfigManager().set(101, player.getQuestRepository().getPoints()); - 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!"); - player.getPacketDispatch().sendTempMusic(152); + int questJingles[] = {152, 153, 154}; + playJingle(player, questJingles[new Random().nextInt(3)]); } + /** + * Resets the quest. This is called when ::setQuestStage is set to 0. + * Useful to override and reset quest player attributes. + */ + 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. @@ -152,7 +191,7 @@ public abstract class Quest implements Plugin { line++; } } else { - send = send.replace("!!",RED).replace("??",BLUE).replace("---",BLACK + "").replace("/--", BLUE); + send = send.replace("!!", RED).replace("??", BLUE).replace("---", BLACK + "").replace("/--", BLUE).replace("%%", BRIGHT_RED).replace("&&", BLUE); line(player, send, line, false); } } @@ -165,16 +204,31 @@ public abstract class Quest implements Plugin { * @param crossed True if the message should be crossed out. */ public void line(Player player, String message, int line, final boolean crossed) { - String send = message; + String send; if(!crossed){ send = BLUE + "" + message.replace("", "

").replace("", BLUE).replace("", RED); - send = send.replace("!!",RED).replace("??",BLUE); + send = send.replace("!!", RED).replace("??", BLUE).replace("%%", BRIGHT_RED).replace("&&", BLUE); } else { - send = BLUE + "" + message.replace("??","").replace("!!",""); + send = BLACK + "" + message.replace("", "

").replace("", "").replace("", "RED"); + send = send.replace("!!", "").replace("??", "").replace("%%", "").replace("&&", ""); } 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. @@ -202,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]}; } @@ -250,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; } /** @@ -288,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 421d28c1f..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,8 +1,9 @@ package core.game.node.entity.player.link.quest; +import content.data.Quests; import core.game.node.entity.player.Player; -import core.tools.SystemLogger; +import core.tools.Log; import org.json.simple.JSONArray; import org.json.simple.JSONObject; @@ -10,6 +11,10 @@ import java.util.HashMap; import java.util.TreeMap; import java.util.Map; +import static core.api.ContentAPIKt.log; +import static core.api.ContentAPIKt.*; + + /** * Manages the systems/players quest repository. * @@ -20,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. @@ -65,25 +70,22 @@ public final class QuestRepository { * @param player The player. */ public void syncronizeTab(Player player) { - player.getConfigManager().set(101, points); + setVarp(player, 101, points); int[] config = null; - if(!player.getAttribute("quest-varps-converted",false)) { - for (Quest quest : QUESTS.values()) { - config = quest.getConfig(player, getStage(quest)); - player.varpManager.get(config[0]).setVarbit(0,config[1]).send(player); - player.varpManager.flagSave(config[0], false); - player.setAttribute("/save:quest-varps-converted",true); -// System.out.println(quest.getName() + " - > stage = " + getStage(quest) + " - configs = { " + config[0] + " " + config[1] + " }"); - } - } else { - for(Quest quest : QUESTS.values()){ - if(quest.getIndex() == 81 && quest.isCompleted(player)){ - player.varpManager.get(534).setVarbit(15,2).setVarbit(18,2).setVarbit(21,2).setVarbit(24,2).setVarbit(12,2).send(player); - } - config = quest.getConfig(player,getStage(quest)); - player.varpManager.get(config[0]).setVarbit(0,config[1]).send(player); - quest.updateVarps(player); + for(Quest quest : QUESTS.values()){ + config = quest.getConfig(player,getStage(quest)); + + // {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); } } @@ -98,7 +100,7 @@ public final class QuestRepository { if(oldStage < stage) { quests.put(quest.getIndex(), stage); } else { - SystemLogger.logWarn(this.getClass(), 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)); } } @@ -196,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) { - SystemLogger.logErr(this.getClass(), "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) { - SystemLogger.logErr(this.getClass(), "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. */ @@ -249,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); } /** @@ -282,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); } /** @@ -290,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/assist/AssistSession.java b/Server/src/main/core/game/node/entity/player/link/request/assist/AssistSession.java index 9f90705b1..bf82b6548 100644 --- a/Server/src/main/core/game/node/entity/player/link/request/assist/AssistSession.java +++ b/Server/src/main/core/game/node/entity/player/link/request/assist/AssistSession.java @@ -13,6 +13,9 @@ import core.game.world.update.flag.context.Graphics; import java.util.Calendar; import java.util.Date; +import static core.api.ContentAPIKt.*; + + /** * Represents the class used to handle the assist requesting of a {@link Player} * . @@ -151,8 +154,8 @@ public final class AssistSession extends Pulse implements RequestModule { player.animate(ANIMATION); player.graphics(GRAPHIC); partener.animate(ANIMATION); - partener.getAudioManager().send(4010); - player.getAudioManager().send(4010); + playAudio(partener, 4010); + playAudio(player, 4010); toggleIcon(player, false); toggleIcon(partener, false); GameWorld.getPulser().submit(this); @@ -243,8 +246,8 @@ public final class AssistSession extends Pulse implements RequestModule { message = "You've earned the maximum XP from the Assist System within a 24-hour period. You can assist again in " + getTimeLeft() + "."; } player.getPacketDispatch().sendString(message, 301, 10); - player.getConfigManager().set(1087, value); - player.getConfigManager().set(1088, (int) totalXp * 10); + setVarp(player, 1087, value); + setVarp(player, 1088, (int) totalXp * 10); } /** @@ -466,4 +469,4 @@ public final class AssistSession extends Pulse implements RequestModule { this.partener = partener; } -} \ No newline at end of file +} diff --git a/Server/src/main/core/game/node/entity/player/link/request/trade/TradeCloseEvent.java b/Server/src/main/core/game/node/entity/player/link/request/trade/TradeCloseEvent.java index 78b1aa8b0..e2eb3a095 100644 --- a/Server/src/main/core/game/node/entity/player/link/request/trade/TradeCloseEvent.java +++ b/Server/src/main/core/game/node/entity/player/link/request/trade/TradeCloseEvent.java @@ -8,6 +8,9 @@ import core.net.packet.PacketRepository; import core.net.packet.context.ContainerContext; import core.net.packet.out.ContainerPacket; +import static core.api.ContentAPIKt.*; + + /** * Represents the close event invoked at the closing of a trade interface. * @author Vexia @@ -59,8 +62,8 @@ public final class TradeCloseEvent implements CloseEvent { * @param player the player. */ private void end(final Player player) { - player.getConfigManager().set(1043, 0); - player.getConfigManager().set(1042, 0); + setVarp(player, 1043, 0); + setVarp(player, 1042, 0); } /** @@ -76,4 +79,4 @@ public final class TradeCloseEvent implements CloseEvent { player.getInventory().addAll(module.getContainer()); } -} \ No newline at end of file +} 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..b2ce8da3a 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 @@ -49,7 +49,7 @@ public final class TradeContainer extends Container { return; } if (!tradeable(item) && !GameWorld.getSettings().isDevMode()) { - player.getPacketDispatch().sendMessage("You can't trade this item."); + //player.getPacketDispatch().sendMessage("You can't trade this item."); return; } Item remove = new Item(item.getId(), amount); @@ -161,12 +161,15 @@ 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()) { + sendMessage(player,"Cannot trade that item to or from an Ironman."); return false; } +/* Snowscape: removing this section. Not that we use bots, but the IP for every account is 0.0.0.0 so it fails the check every time) if ((playerIsBot || targetIsBot) && (playerIP.equals(targetIP) || playerMac.equals(targetMac) || playerHost.equals(targetHost))){ sendMessage(player, colorize("%RYou can not trade items with your own bot accounts.")); return false; @@ -174,7 +177,12 @@ public final class TradeContainer extends Container { if (item.getName().equals("Coins") && item.getId() != 995) { return false; } - return definition.isTradeable(); +*/ + if (!definition.isTradeable()) { + sendMessage(player, "This item is not normally tradeable. Be careful not to break any quests!"); + } + return true; + //return definition.isTradeable(); } /** diff --git a/Server/src/main/core/game/node/entity/player/link/request/trade/TradeModule.kt b/Server/src/main/core/game/node/entity/player/link/request/trade/TradeModule.kt index c0123e4f2..3d0c84483 100644 --- a/Server/src/main/core/game/node/entity/player/link/request/trade/TradeModule.kt +++ b/Server/src/main/core/game/node/entity/player/link/request/trade/TradeModule.kt @@ -10,6 +10,7 @@ import core.game.node.item.Item import core.game.bots.AIRepository import content.global.bots.DoublingMoney import core.game.node.entity.player.info.PlayerMonitor +import core.api.* import java.text.DecimalFormat import java.util.* @@ -166,10 +167,10 @@ class TradeModule if (stage == 0) { // Main interface if (otherModified) { - player!!.configManager[1043] = 1 + setVarp(player!!, 1043, 1) } if (isModified) { - player!!.configManager[1042] = 1 + setVarp(player!!, 1042, 1) } } else { // Accept interface @@ -511,4 +512,4 @@ class TradeModule init { container = TradeContainer(player) } -} \ No newline at end of file +} diff --git a/Server/src/main/core/game/node/entity/skill/LevelUp.java b/Server/src/main/core/game/node/entity/skill/LevelUp.java index 9d1b08351..c9e23d8ea 100644 --- a/Server/src/main/core/game/node/entity/skill/LevelUp.java +++ b/Server/src/main/core/game/node/entity/skill/LevelUp.java @@ -9,17 +9,16 @@ import core.net.packet.PacketRepository; import core.net.packet.context.MusicContext; import core.net.packet.out.MusicPacket; +import static core.api.ContentAPIKt.*; +import static core.game.node.entity.skill.LevelUpJinglesKt.getSkillJingle; + + /** * Represents a leveling up reward. * @author Emperor */ public final class LevelUp { - - /** - * Level up sound ids. - */ - private static final int[] SOUND_EFFECTS = { 29, 37, 65, 48, 57, 55, 52, 33, 69, 44, 41, 39, 35, 43, 53, 45, 28, 34, 62, 11, 60, 49, 31, 300 }; - + /** * The skillcapes data. */ @@ -115,10 +114,7 @@ public final class LevelUp { if (!player.getAttribute("tutorial:complete", false)) { return; } - int soundId = SOUND_EFFECTS[slot]; - if (soundId > -1) { - PacketRepository.send(MusicPacket.class, new MusicContext(player, soundId, true)); - } + playJingle(player, getSkillJingle(player, slot)); handleMilestones(player, slot, amount); player.getPacketDispatch().sendGraphic(199); player.getPacketDispatch().sendString("Congratulations, you've just advanced a " + Skills.SKILL_NAME[slot] + " level!", 740, 0); @@ -128,9 +124,13 @@ public final class LevelUp { player.getSkills().incrementPrayerPoints(1); } if (player.getSkills().getStaticLevel(slot) == 99 && !player.isArtificial()) { - Repository.sendNews(player.getUsername() + " has just achieved level 99 " + Skills.SKILL_NAME[slot]); + Repository.sendNews(player.getUsername() + " has just achieved level 99 " + Skills.SKILL_NAME[slot] + "!"); Skillcape.trim(player); player.getEmoteManager().unlock(Emotes.SKILLCAPE); + if (player.skills.getTotalLevel() == 2376) + { + sendNews(player.getUsername() + " has just achieved level 99 in all skills!"); + } } player.setAttribute("/save:levelup:" + slot, true); sendFlashingIcons(player, slot); @@ -165,7 +165,7 @@ public final class LevelUp { } value |= player.getSkills().getCombatMilestone() << 23; //Combat level milestone index. value |= player.getSkills().getSkillMilestone() << 27; //Total level milestone index - player.getConfigManager().set(1230, value); + setVarp(player, 1230, value); } /** @@ -184,7 +184,7 @@ public final class LevelUp { value |= SKILL_ICON[slot]; player.getInterfaceManager().openChatbox(new Component(740)); } - player.getConfigManager().set(1179, value); + setVarp(player, 1179, value); } /** @@ -193,4 +193,4 @@ public final class LevelUp { public static int[] getSkillcapes() { return SKILLCAPES; } -} \ No newline at end of file +} diff --git a/Server/src/main/core/game/node/entity/skill/LevelUpJingles.kt b/Server/src/main/core/game/node/entity/skill/LevelUpJingles.kt new file mode 100644 index 000000000..a02a09e06 --- /dev/null +++ b/Server/src/main/core/game/node/entity/skill/LevelUpJingles.kt @@ -0,0 +1,63 @@ +package core.game.node.entity.skill + +import core.api.getStatLevel +import core.game.node.entity.player.Player + +/** + * Skill levels with unlocks + **/ + +private val attack: IntArray = intArrayOf(5, 10, 15, 20, 30, 40, 50, 60, 70, 75 ,78, 99) +private val mining: IntArray = intArrayOf(6, 10, 15, 20, 21, 30, 31, 35, 40, 41, 45, 46, 50, 55, 60, 70, 80, 85, 90, 99) +private val smithing: IntArray = intArrayOf(2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 46, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 68, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99) +private val defence: IntArray = intArrayOf(5, 10, 20, 25, 30, 40, 45, 50, 55, 60, 65, 70, 75, 78, 80, 99) +private val herblore: IntArray = intArrayOf(3, 5, 8, 11, 12, 19, 20, 22, 25, 26, 30, 31, 34, 35, 36, 38, 39, 40, 44, 45, 48, 50, 52, 53, 54, 55, 57, 59, 60, 63, 65, 66, 67, 68, 69, 70, 72, 73, 75, 76, 78, 80, 81, 82, 99) +private val fishing: IntArray = intArrayOf(5, 10, 15, 16, 20, 23, 25, 28, 30, 33, 35, 38, 40, 46, 48, 50, 53, 55, 58, 62, 65, 68, 70, 76, 79, 81, 90, 96, 99) +private val ranged: IntArray = intArrayOf(5, 10, 16, 19, 20, 21, 25, 26, 28, 31, 30, 33, 35, 36, 37, 39, 40, 42, 45, 46, 50, 55, 60, 61, 65, 70, 78, 80, 99) +private val thieving: IntArray = intArrayOf(2, 5, 10, 13, 15, 20, 22, 25, 27, 28, 30, 32, 35, 36, 38, 40, 42, 43, 44, 45, 47, 49, 50, 52, 53, 55, 59, 63, 65, 70, 72, 75, 78, 80, 82, 85, 99) +private val cooking: IntArray = intArrayOf(4, 5, 6, 7, 8, 10, 11, 12, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 28, 29, 30, 31, 32, 33, 34, 35, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 53, 54, 55, 57, 58, 59, 60, 62, 64, 65, 67, 68, 70, 80, 82, 85, 91, 95, 99) +private val prayer: IntArray = intArrayOf(2, 4, 7, 8, 9, 10, 13, 16, 19, 22, 25, 26, 28, 31, 34, 35, 37, 40, 43, 44, 45, 46, 49, 52, 60, 70, 85, 90, 99) +private val crafting: IntArray = intArrayOf(3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 60, 62, 63, 64, 66, 67, 68, 70, 71, 73, 74, 75, 77, 79, 80, 82, 84, 85, 87, 90, 99) +private val firemaking: IntArray = intArrayOf(4, 5, 11, 12, 15, 20, 21, 25, 26, 30, 33, 35, 40, 42, 43, 45, 47, 48, 49, 50, 52, 53, 55, 58, 59, 60, 62, 63, 65, 68, 70, 72, 75, 76, 78, 79, 80, 83, 85, 87, 89, 92, 95, 99) +private val magic: IntArray = intArrayOf(4, 5, 7, 9, 11, 13, 14, 15, 17, 19, 20, 21, 23, 24, 25, 27, 29, 30, 31, 32, 33, 35, 37, 39, 40, 43, 45, 47, 49, 50, 51, 52, 53, 54, 55, 56, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 90, 91, 92, 93, 94, 95, 96, 97, 99) +private val fletching: IntArray = intArrayOf(5, 7, 9, 10, 11, 15, 18, 20, 22, 24, 25, 26, 30, 32, 33, 35, 37, 38, 39, 40, 41, 42, 43, 45, 46, 48, 49, 50, 51, 52, 53, 54, 55, 55, 56, 58, 59, 60, 61, 62, 63, 65, 67, 69, 70, 71, 73, 75, 77, 80, 81, 85, 90, 95, 99) +private val woodcutting: IntArray = intArrayOf(6, 10, 12, 15, 20, 21, 27, 30, 31, 35, 37, 40, 41, 42, 44, 45, 50, 54, 56, 57, 58, 60, 61, 75, 80, 82, 99) +private val runecrafting: IntArray = intArrayOf(2, 5, 6, 9, 10, 11, 13, 14, 15, 19, 20, 22, 23, 26, 27, 28, 33, 35, 38, 40, 42, 44, 46, 52, 54, 55, 56, 57, 59, 65, 66, 70, 74, 76, 77, 78, 82, 84, 91, 92, 95, 98, 99) +private val slayer: IntArray = intArrayOf(5, 7, 10, 15, 17, 20, 22, 25, 30, 32, 33, 35, 37, 39, 40, 42, 45, 47, 50, 51, 52, 55, 56, 57, 58, 59, 60, 63, 65, 68, 70, 72, 75, 80, 83, 85, 90, 99) +private val farming: IntArray = intArrayOf(2, 3, 4, 5, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 36, 38, 39, 42, 44, 45, 47, 48, 50, 51, 52, 53, 55, 56, 57, 59, 60, 62, 63, 67, 68, 70, 72, 73, 75, 79, 83, 85, 99) + +/** + * Gets the jingleId to play based on skill slot id and skill level + * @param player the player + * @param slot the skill slot id + **/ +fun getSkillJingle(player: Player, slot: Int) : Int { + val skillLevel = getStatLevel(player, slot) + when (slot) { + 0 -> return if (attack.contains(skillLevel)) 30 else 29 + 1 -> return if (defence.contains(skillLevel)) 38 else 37 + 2 -> return if (skillLevel < 50) 65 else 66 //Strength + 3 -> return if (skillLevel < 50) 47 else 48 //Hitpoints + 4 -> return if (ranged.contains(skillLevel)) 58 else 57 + 5 -> return if (prayer.contains(skillLevel)) 56 else 55 + 6 -> return if (magic.contains(skillLevel)) 52 else 51 + 7 -> return if (cooking.contains(skillLevel)) 34 else 33 + 8 -> return if (woodcutting.contains(skillLevel)) 70 else 69 + 9 -> return if (fletching.contains(skillLevel)) 44 else 43 + 10 -> return if (fishing.contains(skillLevel)) 42 else 41 + 11 -> return if (firemaking.contains(skillLevel)) 40 else 39 + 12 -> return if (crafting.contains(skillLevel)) 36 else 35 + 13 -> return if (smithing.contains(skillLevel)) 64 else 63 + 14 -> return if (mining.contains(skillLevel)) 54 else 53 + 15 -> return if (herblore.contains(skillLevel)) 46 else 45 + 16 -> return 28 //Agility + 17 -> return if (thieving.contains(skillLevel)) 68 else 67 + 18 -> return if (slayer.contains(skillLevel)) 62 else 61 + 19 -> return if (farming.contains(skillLevel)) 11 else 10 + 20 -> return if (runecrafting.contains(skillLevel)) 60 else 59 + 21 -> return if ((skillLevel % 2 == 0)) 49 else 50 //Hunter + 22 -> return if ((skillLevel % 10 == 0)) 31 else 32 //Construction + 23 -> return if ((skillLevel < 50)) 300 else 301 //Summoning + } + return 40 +} diff --git a/Server/src/main/core/game/node/entity/skill/SkillRestoration.java b/Server/src/main/core/game/node/entity/skill/SkillRestoration.java deleted file mode 100644 index af10b0eb7..000000000 --- a/Server/src/main/core/game/node/entity/skill/SkillRestoration.java +++ /dev/null @@ -1,83 +0,0 @@ -package core.game.node.entity.skill; - -import core.game.container.impl.EquipmentContainer; -import core.game.node.entity.Entity; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.prayer.PrayerType; -import core.game.node.item.Item; -import org.rs09.consts.Items; -import core.game.world.GameWorld; - -/** - * Handles the skill restoration data. - * @author Emperor - */ -public final class SkillRestoration { - - /** - * The skill index. - */ - private final int skillId; - - /** - * Constructs a new {@code SkillRestoration} {@code Object}. - * @param skillId The skill id. - */ - public SkillRestoration(int skillId) { - this.skillId = skillId; - } - - /** - * Restores the skill. - * @param entity The entity. - */ - public void restore(Entity entity) { - Skills skills = entity.getSkills(); - int max = skills.getStaticLevel(skillId); - int tickDivisor = 1; - if(skillId == Skills.HITPOINTS){ - max = skills.getMaximumLifepoints(); - if(entity instanceof Player) { - Player player = entity.asPlayer(); - if(player.getPrayer().getActive().contains(PrayerType.RAPID_HEAL)) { - tickDivisor *= 2; - } - Item gloves = player.getEquipment().get(EquipmentContainer.SLOT_HANDS); - if(gloves != null && gloves.getId() == Items.REGEN_BRACELET_11133) { - tickDivisor *= 2; - } - } - } - if(skillId == Skills.PRAYER) { - return; - } - if(skillId != Skills.HITPOINTS && skillId != Skills.SUMMONING) { - if(entity instanceof Player) { - if(entity.asPlayer().getPrayer().getActive().contains(PrayerType.RAPID_RESTORE)) { - tickDivisor *= 2; - } - } - } - - int ticks = 100 / tickDivisor; - if(GameWorld.getTicks() % ticks == 0){ - if(skillId == Skills.HITPOINTS){ - if(skills.getLifepoints() >= max){ - return; - } - skills.heal(1); - } else { - int current = skills.getLevel(skillId); - skills.updateLevel(skillId,current < max ? 1 : -1,max); - } - } - } - - /** - * Gets the skillId. - * @return The skillId. - */ - public int getSkillId() { - return skillId; - } -} 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 35175282e..d94ccebb1 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; @@ -11,7 +12,6 @@ import core.game.node.entity.npc.NPC; import core.game.node.entity.player.Player; import core.game.node.entity.player.link.request.assist.AssistSession; import core.game.node.item.Item; -import core.game.world.update.flag.player.AppearanceFlag; import core.net.packet.PacketRepository; import core.net.packet.context.SkillContext; import core.net.packet.out.SkillLevel; @@ -23,10 +23,12 @@ import core.game.world.GameWorld; import core.game.world.repository.Repository; 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; +import static core.api.ContentAPIKt.playAudio; import static java.lang.Math.floor; import static java.lang.Math.max; @@ -39,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. @@ -105,11 +107,6 @@ public final class Skills { */ private double experienceGained = 0; - /** - * The restoration pulses. - */ - private final SkillRestoration[] restoration; - /** * If a lifepoints update should occur. */ @@ -125,6 +122,9 @@ public final class Skills { */ private int skillMilestone; + public int lastTrainedSkill = -1; + public int lastXpGain = 0; + /** * Constructs a new {@code Skills} {@code Object}. * @param entity The entity. @@ -134,7 +134,6 @@ public final class Skills { this.experience = new double[24]; this.staticLevels = new int[24]; this.dynamicLevels = new int[24]; - this.restoration = new SkillRestoration[24]; for (int i = 0; i < 24; i++) { this.staticLevels[i] = 1; this.dynamicLevels[i] = 1; @@ -164,15 +163,6 @@ public final class Skills { */ public void configure() { updateCombatLevel(); - int max = 24; - if (entity instanceof NPC) { - max = 7; - } - for (int i = 0; i < max; i++) { - if (i != PRAYER && i != SUMMONING && restoration[i] == null) { - configureRestorationPulse(i); - } - } } /** @@ -182,19 +172,6 @@ public final class Skills { if (lifepoints < 1) { return; } - for (int i = 0; i < restoration.length; i++) { - if (restoration[i] != null) { - restoration[i].restore(entity); - } - } - } - - /** - * Configures a restoration pulse for the given skill id. - * @param skillId The skill id. - */ - private void configureRestorationPulse(final int skillId) { - restoration[skillId] = new SkillRestoration(skillId); } /** @@ -277,10 +254,9 @@ public final class Skills { staticLevels[slot] = newLevel; if (entity instanceof Player) { - if (updateCombatLevel()) { - player.getUpdateMasks().register(new AppearanceFlag(player)); - } - LevelUp.levelup(player, slot, amount); + player.updateAppearance(); + LevelUp.levelup(player, slot, amount); + updateCombatLevel(); } } if (entity instanceof Player) { @@ -299,6 +275,8 @@ public final class Skills { lastUpdateXp = this.experience.clone(); lastUpdate = GameWorld.getTicks(); } + lastTrainedSkill = slot; + lastXpGain = getWorldTicks(); } /** @@ -311,7 +289,8 @@ 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; } @@ -386,41 +365,35 @@ public final class Skills { setLevel(i, staticLevel); } if (entity instanceof Player) { - entity.asPlayer().getAudioManager().send(2674); + 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()); + } + } } } @@ -436,40 +409,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. */ @@ -650,7 +593,7 @@ public final class Skills { Player assister = assist.getPlayer(); int index = assist.getSkillIndex(slot); if (index != -1 && !assist.isRestricted()) { - // System.out.println(index + ", " + + // assist.getSkills()[index] + ", " + SKILL_NAME[slot]); if (assist.getSkills()[index]) { int assistLevel = assister.getSkills().getLevel(slot); @@ -882,14 +825,6 @@ public final class Skills { } } - /** - * Gets the restoration pulses. - * @return The restoration pulse array. - */ - public SkillRestoration[] getRestoration() { - return restoration; - } - /** * Gets the amount of mastered skills. * @return The amount of mastered skills. diff --git a/Server/src/main/core/game/node/entity/state/EntityState.java b/Server/src/main/core/game/node/entity/state/EntityState.java deleted file mode 100644 index e75866246..000000000 --- a/Server/src/main/core/game/node/entity/state/EntityState.java +++ /dev/null @@ -1,66 +0,0 @@ -package core.game.node.entity.state; - -import core.game.node.entity.state.impl.*; - -/** - * Represents the statuses. - * @author Emperor - */ -public enum EntityState { - - /** - * The entity is poisoned. - */ - POISONED(new PoisonStatePulse(null)), - - /** - * The entity is stunned. - */ - STUNNED(new StunStatePulse(null, 0)), - - /** - * The entity is frozen. - */ - FROZEN(new FrozenStatePulse(null, 0)), - - /** - * The entity is skulled. - */ - SKULLED(new SkullStatePulse(null, 0)), - - /** - * The entity is under teleblock. - */ - TELEBLOCK(new TeleblockStatePulse(null, 0, 0)), - - /** - * The entity has decreased weapon speeds. - */ - MIASMIC(new MiasmicStatePulse(null, 0)), - - /** - * The entity is healing over time - */ - HEALOVERTIME(new HealOverTimePulse(null,0,0,0,0)); - - /** - * The state pulse used for this state. - */ - private final StatePulse pulse; - - /** - * Constructs a new {@code EntityState} {@code Object}. - * @param pulse The state pulse. - */ - private EntityState(StatePulse pulse) { - this.pulse = pulse; - } - - /** - * Gets the pulse. - * @return The pulse. - */ - public StatePulse getPulse() { - return pulse; - } -} \ No newline at end of file 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/StateManager.java b/Server/src/main/core/game/node/entity/state/StateManager.java deleted file mode 100644 index 304b1cee0..000000000 --- a/Server/src/main/core/game/node/entity/state/StateManager.java +++ /dev/null @@ -1,138 +0,0 @@ -package core.game.node.entity.state; - -import core.game.node.entity.Entity; - - -import java.nio.ByteBuffer; -import java.util.HashMap; -import java.util.Map; - -/** - * Handles an entity's status (eg. poisoned, stunned, frozen, skulled, ...) - * @author Emperor - */ -public final class StateManager { - - /** - * The entity. - */ - private final Entity entity; - - /** - * The entity's current states. - */ - private final Map states = new HashMap<>(); - - /** - * Constructs a new {@code StateManager} {@code Object}. - * @param entity The entity. - */ - public StateManager(Entity entity) { - this.entity = entity; - } - - /** - * Initializes the pulses. - */ - public void init() { - for (StatePulse pulse : states.values()) { - pulse.run(); - } - } - - /** - * Checks if a save is required. - * @return {@code True} if so. - */ - public boolean isSaveRequired() { - return !states.isEmpty(); - } - - /** - * Registers a state. - * @param state The state. - * @param args The arguments. - */ - public void set(EntityState state, Object... args) { - register(state, true, args); - } - - /** - * Registers a state. - * @param state The state. - * @param override If the previous pulse (if any) should be overriden. - * @param args The arguments. - */ - public void register(EntityState state, boolean override, Object... args) { - if (!state.getPulse().canRun(entity)) { - return; - } - StatePulse pulse = states.get(state); - if (pulse != null) { - if (!override) { - return; - } - pulse.stop(); - } - pulse = state.getPulse().create(entity, args); - pulse.run(); - states.put(state, pulse); - } - - /** - * Resets all the states. - */ - public void reset() { - for (StatePulse pulse : states.values()) { - if (pulse.isRunning()) { - pulse.remove(); - pulse.stop(); - } - } - states.clear(); - } - - /** - * Resets the state pulse. - * @param state The state. - */ - public void remove(EntityState state) { - StatePulse pulse = states.get(state); - if (pulse != null && pulse.isRunning()) { - pulse.remove(); - pulse.stop(); - } - states.remove(state); - } - - /** - * Checks if the entity has a pulse running for the given state. - * @param state The state. - * @return {@code True} if so. - */ - public boolean hasState(EntityState state) { - StatePulse pulse = states.get(state); - return pulse != null && pulse.isRunning(); - } - - /** - * Gets the pulse for the given state. - * @param state The state to get the pulse for. - * @return The state pulse. - */ - public StatePulse get(EntityState state) { - return states.get(state); - } - - /** - * Gets the entity. - * @return The entity. - */ - public Entity getEntity() { - return entity; - } - - public Map getStates() { - return states; - } -} \ 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 9f6419153..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.states[key] != null){ - return null - } - if(state != null){ - val clazz = state.newInstance(player) - player.states[key] = clazz - return clazz - } - return null - } - } -} \ No newline at end of file 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/entity/state/impl/FrozenStatePulse.java b/Server/src/main/core/game/node/entity/state/impl/FrozenStatePulse.java deleted file mode 100644 index ac22619e9..000000000 --- a/Server/src/main/core/game/node/entity/state/impl/FrozenStatePulse.java +++ /dev/null @@ -1,95 +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; - -/** - * Handles the frozen state pulse. - * @author Emperor - */ -public final class FrozenStatePulse extends StatePulse { - - /** - * The amount of ticks immunity lasts. - */ - private static final int IMMUNITY_TICK = 7; - - /** - * The message when frozen. - */ - private final String message; - - /** - * Constructs a new {@code FrozenStatePulse} {@code Object}. - * @param entity The entity. - * @param ticks The ticks to freeze for. - */ - public FrozenStatePulse(Entity entity, String message, int ticks) { - super(entity, ticks); - this.message = message; - } - - /** - * Constructs a new {@code FrozenStatePulse} {@code Object}. - * @param entity The entity. - * @param ticks The ticks to freeze for. - */ - public FrozenStatePulse(Entity entity, int ticks) { - this(entity, "You have been frozen!", ticks); - } - - @Override - public boolean canRun(Entity entity) { - return entity.getAttribute("freeze_immunity", -1) < GameWorld.getTicks(); - } - - @Override - public void start() { - super.start(); - if (entity.getPulseManager().isMovingPulse()) { - entity.getPulseManager().clear(); - } - entity.getWalkingQueue().reset(); - entity.getLocks().lockMovement(getDelay()); - entity.setAttribute("freeze_immunity", GameWorld.getTicks() + getDelay() + IMMUNITY_TICK); - if (entity instanceof Player) { - ((Player) entity).getPacketDispatch().sendMessage(message); - } - } - - @Override - public StatePulse create(Entity entity, Object... args) { - if (args.length > 1) { - return new FrozenStatePulse(entity, (String) args[1], (Integer) args[0]); - } else { - return new FrozenStatePulse(entity, (Integer) args[0]); - } - } - - @Override - public boolean pulse() { - return true; - } - - @Override - public boolean isSaveRequired() { - return false; - } - - @Override - public void save(ByteBuffer buffer) { - /* - * empty. - */ - } - - @Override - public StatePulse parse(Entity entity, ByteBuffer buffer) { - return null; - } - -} diff --git a/Server/src/main/core/game/node/entity/state/impl/HealOverTimePulse.java b/Server/src/main/core/game/node/entity/state/impl/HealOverTimePulse.java deleted file mode 100644 index dba94089e..000000000 --- a/Server/src/main/core/game/node/entity/state/impl/HealOverTimePulse.java +++ /dev/null @@ -1,86 +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.update.flag.context.Graphics; - -import java.nio.ByteBuffer; - -/** - * Method for healing a player a certain amount of HP - * every X amount of ticks for a total of X amount of heals - * over X amount of ticks. - * - * @author phil lips - */ - -public class HealOverTimePulse extends StatePulse { - - /**The total amount of HP to heal*/ - - private int totalToHeal; - - /**How many ticks to spread the heals over*/ - - private int ticksTotal; - - /**How many times to heal the player during those ticks*/ - - private int timesToHeal; - - private int currentTick; - - /** - * The entity. - */ - protected final Entity entity; - - /**Constructs a new HealOverTimePulse object - * @param entity the entity to heal over time - * @param ticks the total time to spread the heal over - * @param totalHeal the total amount to heal for - * @param healInc how many times the total amount to heal is divided to*/ - - public HealOverTimePulse(Entity entity, int ticks, int totalHeal, int healInc, int currentTick){ - super(entity,0); - this.entity = entity; - this.ticksTotal = ticks; - this.totalToHeal = totalHeal; - this.timesToHeal = healInc; - this.currentTick = currentTick; - } - - @Override - public StatePulse create(Entity entity, Object... args) { - return new HealOverTimePulse(entity, (Integer) args[1],(Integer) args[2],(Integer) args[3],1); - } - - /** Checks if it can heal the player every pulse - * the mod is funny math haha :) - */ - @Override - public boolean pulse() { - if(currentTick != 0){ - if(currentTick % (ticksTotal / timesToHeal) == 0){ - entity.getSkills().heal(totalToHeal / timesToHeal); - } - } - currentTick += 1; - return currentTick > ticksTotal; - } - - @Override - public boolean isSaveRequired() { - return true; - } - - @Override - public void save(ByteBuffer buffer) { - } - - @Override - public StatePulse parse(Entity entity, ByteBuffer buffer) { - return null; - } -} diff --git a/Server/src/main/core/game/node/entity/state/impl/MiasmicStatePulse.java b/Server/src/main/core/game/node/entity/state/impl/MiasmicStatePulse.java deleted file mode 100644 index 628934bd8..000000000 --- a/Server/src/main/core/game/node/entity/state/impl/MiasmicStatePulse.java +++ /dev/null @@ -1,90 +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; - -/** - * Handles the Miasmic state pulse. - * @author Emperor - */ -public final class MiasmicStatePulse extends StatePulse { - - /** - * The amount of ticks immunity lasts. - */ - private static final int IMMUNITY_TICK = 7; - - /** - * The message when frozen. - */ - private final String message; - - /** - * Constructs a new {@code MiasmicStatePulse} {@code Object}. - * @param entity The entity. - * @param ticks The ticks to freeze for. - */ - public MiasmicStatePulse(Entity entity, String message, int ticks) { - super(entity, ticks); - this.message = message; - } - - /** - * Constructs a new {@code MiasmicStatePulse} {@code Object}. - * @param entity The entity. - * @param ticks The ticks to freeze for. - */ - public MiasmicStatePulse(Entity entity, int ticks) { - this(entity, null, ticks); - } - - @Override - public boolean canRun(Entity entity) { - return entity.getAttribute("miasmic_immunity", -1) < GameWorld.getTicks(); - } - - @Override - public void start() { - super.start(); - entity.setAttribute("miasmic_immunity", GameWorld.getTicks() + getDelay() + IMMUNITY_TICK); - if (entity instanceof Player) { - ((Player) entity).getPacketDispatch().sendMessage(message); - } - } - - @Override - public StatePulse create(Entity entity, Object... args) { - if (args.length > 1) { - return new MiasmicStatePulse(entity, (String) args[1], (Integer) args[0]); - } else { - return new MiasmicStatePulse(entity, (Integer) args[0]); - } - } - - @Override - public boolean pulse() { - return true; - } - - @Override - public boolean isSaveRequired() { - return false; - } - - @Override - public void save(ByteBuffer buffer) { - /* - * empty. - */ - } - - @Override - public StatePulse parse(Entity entity, ByteBuffer buffer) { - return null; - } - -} \ No newline at end of file diff --git a/Server/src/main/core/game/node/entity/state/impl/PoisonStatePulse.java b/Server/src/main/core/game/node/entity/state/impl/PoisonStatePulse.java deleted file mode 100644 index 26594fb9c..000000000 --- a/Server/src/main/core/game/node/entity/state/impl/PoisonStatePulse.java +++ /dev/null @@ -1,120 +0,0 @@ -package core.game.node.entity.state.impl; - -import core.game.node.entity.Entity; -import core.game.node.entity.combat.ImpactHandler; -import core.game.node.entity.player.Player; -import core.game.node.entity.state.StatePulse; - -import java.nio.ByteBuffer; - -/** - * Handles the poisoned state. - * @author Emperor - */ -public final class PoisonStatePulse extends StatePulse { - - /** - * The current amount of poison damage. - */ - private int damage; - - /** - * The entity poisoning this entity. - */ - private Entity poisoner; - - /** - * Constructs a new {@code PoisonStatePulse} {@code Object}. - * @param entity The entity. - */ - public PoisonStatePulse(Entity entity) { - super(entity, 30); - } - - @Override - public boolean canRun(Entity entity) { - return !entity.isPoisonImmune(); - } - - @Override - public void start() { - super.start(); - if (entity instanceof Player) { - ((Player) entity).getPacketDispatch().sendMessage("You have been poisoned!"); - } - } - - @Override - public boolean pulse() { - if (!poisoner.isActive()) { - poisoner = entity; - } - if (damage / 10 > 0) { - entity.getImpactHandler().manualHit(poisoner, damage / 10, ImpactHandler.HitsplatType.POISON); - } - damage -= 2; - if (damage < 10) { - if (entity instanceof Player) { - ((Player) entity).getPacketDispatch().sendMessage("The poison has wore off."); - } - return true; - } - return false; - } - - @Override - public boolean isSaveRequired() { - return damage > 9; - } - - @Override - public void save(ByteBuffer buffer) { - buffer.put((byte) damage); - } - - @Override - public StatePulse parse(Entity entity, ByteBuffer buffer) { - return create(entity, buffer.get() & 0xFF, entity); - } - - @Override - public StatePulse create(Entity entity, Object... args) { - PoisonStatePulse pulse = new PoisonStatePulse(entity); - pulse.damage = (Integer) args[0]; - pulse.poisoner = (Entity) args[1]; - return pulse; - } - - /** - * Gets the damage. - * @return The damage. - */ - public int getDamage() { - return damage; - } - - /** - * Sets the damage. - * @param damage The damage to set. - */ - public void setDamage(int damage) { - this.damage = damage; - } - - /** - * Gets the poisoner. - * @return The poisoner. - */ - public Entity getPoisoner() { - return poisoner; - } - - /** - * Sets the poisoner. - * @param poisoner The poisoner to set. - */ - public void setPoisoner(Entity poisoner) { - this.poisoner = poisoner; - } - -} \ No newline at end of file diff --git a/Server/src/main/core/game/node/entity/state/impl/SkullStatePulse.java b/Server/src/main/core/game/node/entity/state/impl/SkullStatePulse.java deleted file mode 100644 index 9946b3fbb..000000000 --- a/Server/src/main/core/game/node/entity/state/impl/SkullStatePulse.java +++ /dev/null @@ -1,70 +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 java.nio.ByteBuffer; - -/** - * Handles the skulled state. - * @author Emperor - */ -public final class SkullStatePulse extends StatePulse { - - /** - * The amount of ticks. - */ - public static final int TICKS = 2000; - - /** - * Constructs a new {@code SkullStatePulse} {@code Object}. - * @param entity The entity. - * @param ticks The amount of ticks. - */ - public SkullStatePulse(Entity entity, int ticks) { - super(entity, ticks); - } - - @Override - public void start() { - super.start(); - Player player = (Player) entity; - player.getSkullManager().setSkullIcon(0); - player.getSkullManager().setSkulled(true); - } - - @Override - public boolean isSaveRequired() { - return getTicksPassed() < getDelay(); - } - - @Override - public void save(ByteBuffer buffer) { - buffer.putShort((short) (getDelay() - getTicksPassed())); - } - - @Override - public StatePulse parse(Entity entity, ByteBuffer buffer) { - return create(entity, buffer.getShort() & 0xFFFF); - } - - @Override - public StatePulse create(Entity entity, Object... args) { - int ticks = args.length > 0 ? (Integer) args[0] : TICKS; - return new SkullStatePulse(entity, ticks); - } - - @Override - public boolean pulse() { - setTicksPassed(getDelay()); - remove(); - return true; - } - - @Override - public void remove() { - ((Player) entity).getSkullManager().reset(); - } - -} \ No newline at end of file diff --git a/Server/src/main/core/game/node/entity/state/impl/StunStatePulse.java b/Server/src/main/core/game/node/entity/state/impl/StunStatePulse.java deleted file mode 100644 index 8f0827a61..000000000 --- a/Server/src/main/core/game/node/entity/state/impl/StunStatePulse.java +++ /dev/null @@ -1,96 +0,0 @@ -package core.game.node.entity.state.impl; - -import core.game.node.entity.Entity; -import core.game.node.entity.impl.Animator; -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.audio.Audio; -import core.game.node.entity.state.StatePulse; -import core.game.world.update.flag.context.Animation; -import core.game.world.update.flag.context.Graphics; - -import java.nio.ByteBuffer; - -/** - * Handles the stunned state. - * @author Emperor - */ -public final class StunStatePulse extends StatePulse { - - /** - * The stun graphic. - */ - private static final Graphics STUN_GRAPHIC = new Graphics(80, 96); - - private static final Audio STUN_AUDIO = new Audio(2727, 1, 0); - - private static final Animation STUN_ANIM = new Animation(424, Animator.Priority.VERY_HIGH); - - /** - * The stun message. - */ - private String stunMessage; - - /** - * Constructs a new {@code FrozenStatePulse} {@code Object}. - * @param entity The entity. - * @param ticks The ticks to freeze for. - * @param stunMessage The stun message. - */ - public StunStatePulse(Entity entity, int ticks, String stunMessage) { - super(entity, ticks); - this.stunMessage = stunMessage; - } - - /** - * Constructs a new @{Code StunStatePulse} object. - * @param entity The entity. - * @param ticks The ticks to freeze for. - */ - public StunStatePulse(Entity entity, int ticks) { - this(entity, ticks, "You have been stunned!"); - } - - @Override - public void start() { - super.start(); - entity.getWalkingQueue().reset(); - entity.getLocks().lock(getDelay()); - entity.graphics(STUN_GRAPHIC); - if (entity instanceof Player) { - entity.asPlayer().getAudioManager().send(STUN_AUDIO); - entity.animate(STUN_ANIM); - ((Player) entity).getPacketDispatch().sendMessage(stunMessage); - } - } - - @Override - public StatePulse create(Entity entity, Object... args) { - return new StunStatePulse(entity, (Integer) args[0], args.length > 1 ? (String) args[1] : "You have been stunned!"); - } - - @Override - public boolean pulse() { - if (entity.getAnimator().getGraphics() == STUN_GRAPHIC) { - entity.graphics(Graphics.create(-1)); - } - return true; - } - - @Override - public boolean isSaveRequired() { - return false; - } - - @Override - public void save(ByteBuffer buffer) { - /* - * empty. - */ - } - - @Override - public StatePulse parse(Entity entity, ByteBuffer buffer) { - return null; - } - -} \ No newline at end of file diff --git a/Server/src/main/core/game/node/entity/state/impl/TeleblockStatePulse.java b/Server/src/main/core/game/node/entity/state/impl/TeleblockStatePulse.java deleted file mode 100644 index 21f9e97cf..000000000 --- a/Server/src/main/core/game/node/entity/state/impl/TeleblockStatePulse.java +++ /dev/null @@ -1,105 +0,0 @@ -package core.game.node.entity.state.impl; - -import core.api.PersistPlayer; -import core.game.node.entity.Entity; -import core.game.node.entity.player.Player; -import core.game.node.entity.state.EntityState; -import core.game.node.entity.state.StatePulse; -import org.jetbrains.annotations.NotNull; -import org.json.simple.JSONObject; - -import java.nio.ByteBuffer; - -/** - * Handles the teleblock state pulse. - * @author Vexia - */ -public final class TeleblockStatePulse extends StatePulse implements PersistPlayer { - - /** - * The ticks needed to pass. - */ - public int ticks; - - /** - * The current tick. - */ - public int currentTick; - - /** - * Constructs a new {@Code TeleblockStatePulse} {@Code Object} - * @param entity the entity. - * @param ticks the ticks. - * @param currentTick the current tick. - */ - public TeleblockStatePulse(Entity entity, int ticks, int currentTick) { - super(entity, 1); - this.ticks = ticks; - this.currentTick = currentTick; - } - - //Required to be instantiated as a ContentListener for PersistPlayer - public TeleblockStatePulse() { - super(null, 0); - } - - @Override - public void savePlayer(@NotNull Player player, @NotNull JSONObject save) { - TeleblockStatePulse tbPulse = (TeleblockStatePulse) player.getStateManager().get(EntityState.TELEBLOCK); - - if (tbPulse != null && tbPulse.isSaveRequired()) { - JSONObject tbObj = new JSONObject(); - tbObj.put("tb-state-current", "" + tbPulse.currentTick); - tbObj.put("tb-state-total", "" + tbPulse.ticks); - save.put("teleblock-state", tbObj); - } - } - - @Override - public void parsePlayer(@NotNull Player player, @NotNull JSONObject data) { - if (data.containsKey("teleblock-state")) { - JSONObject tbData = (JSONObject) data.get("teleblock-state"); - int currentTick = Integer.parseInt(tbData.get("tb-state-current").toString()); - int totalTicks = Integer.parseInt(tbData.get("tb-state-total").toString()); - player.getStateManager().set(EntityState.TELEBLOCK, totalTicks, currentTick); - } - } - - @Override - public boolean isSaveRequired() { - return currentTick < ticks; - } - - @Override - public void save(ByteBuffer buffer) { - buffer.putInt(ticks); - buffer.putInt(currentTick); - } - - @Override - public StatePulse parse(Entity entity, ByteBuffer buffer) { - return new TeleblockStatePulse(entity, buffer.getInt(), buffer.getInt()); - } - - @Override - public void start() { - if (currentTick == 0) { - if (entity instanceof Player) { - entity.asPlayer().getAudioManager().send(203, true); - entity.asPlayer().sendMessage("You have been teleblocked."); - } - } - super.start(); - } - - @Override - public boolean pulse() { - return ++currentTick >= ticks; - } - - @Override - public StatePulse create(Entity entity, Object... args) { - return new TeleblockStatePulse(entity, (int) args[0], args.length > 1 ? (int) args[1] : 0); - } - -} diff --git a/Server/src/main/core/game/node/item/GroundItem.java b/Server/src/main/core/game/node/item/GroundItem.java index 01ad07313..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 @@ -80,7 +82,7 @@ public class GroundItem extends Item { super(item.getId(), item.getAmount(), item.getCharge()); super.location = location; super.index = -1; - super.interaction.setDefault(); + super.interactPlugin.setDefault(); this.dropper = player; this.dropperUid = player != null ? player.getDetails().getUid() : -1; this.ticks = GameWorld.getTicks(); @@ -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 9f0a034cc..086784cf6 100644 --- a/Server/src/main/core/game/node/item/Item.java +++ b/Server/src/main/core/game/node/item/Item.java @@ -2,7 +2,7 @@ package core.game.node.item; import core.cache.def.impl.ItemDefinition; import core.game.interaction.DestinationFlag; -import core.game.interaction.Interaction; +import core.game.interaction.InteractPlugin; import core.game.interaction.OptionHandler; import core.game.node.Node; import core.game.node.entity.combat.equipment.DegradableEquipment; @@ -34,7 +34,7 @@ public class Item extends Node{ */ public Item() { super("null", null); - super.interaction = new Interaction(this); + super.interactPlugin = new InteractPlugin(this); this.idHash = -1 << 16 | 1000; } @@ -65,7 +65,7 @@ public class Item extends Node{ super(ItemDefinition.forId(id).getName(), null); super.destinationFlag = DestinationFlag.ITEM; super.index = -1; // Item slot. - super.interaction = new Interaction(this); + super.interactPlugin = new InteractPlugin(this); this.idHash = id << 16 | charge; this.amount = amount; this.definition = ItemDefinition.forId(id); @@ -106,6 +106,14 @@ public class Item extends Node{ return value * getAmount(); } + public long getAlchemyValue() { + long value = 1; + if (definition.getAlchemyValue(true) > value) { + value = definition.getAlchemyValue(true); + } + return value * getAmount(); + } + /** * Gets a copy of the item. * @return The item copy. @@ -188,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. @@ -245,4 +245,4 @@ public class Item extends Node{ return "Item id=" + getId() + ", name=" + getName() + ", amount=" + amount; } -} \ No newline at end of file +} diff --git a/Server/src/main/core/game/node/scenery/Scenery.java b/Server/src/main/core/game/node/scenery/Scenery.java index 27e2e0ea9..9594a7f26 100644 --- a/Server/src/main/core/game/node/scenery/Scenery.java +++ b/Server/src/main/core/game/node/scenery/Scenery.java @@ -3,7 +3,7 @@ package core.game.node.scenery; import core.cache.def.impl.VarbitDefinition; import core.cache.def.impl.SceneryDefinition; import core.game.interaction.DestinationFlag; -import core.game.interaction.Interaction; +import core.game.interaction.InteractPlugin; import core.game.node.Node; import core.game.node.entity.impl.GameAttributes; import core.game.node.entity.player.Player; @@ -14,6 +14,7 @@ import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.List; +import static core.api.ContentAPIKt.*; /** * Represents a scenery. @@ -145,7 +146,7 @@ public class Scenery extends Node { } super.destinationFlag = DestinationFlag.OBJECT; super.direction = Direction.get(rotation); - super.interaction = new Interaction(this); + super.interactPlugin = new InteractPlugin(this); this.rotation = rotation; this.id = id; this.location = location; @@ -232,10 +233,10 @@ public class Scenery extends Node { if (def.getVarbitID() > -1) { VarbitDefinition config = def.getConfigFile(); if (config != null) { - player.varpManager.get(config.getVarpId()).setVarbit(config.getStartBit(),index).send(player); + setVarbit(player, def.getVarbitID(), index); } } else if (def.getConfigId() > -1) { - player.varpManager.get(def.getConfigId()).setVarbit(0,index); + setVarp(player, def.getConfigId(), index); } } diff --git a/Server/src/main/core/game/node/scenery/SceneryBuilder.java b/Server/src/main/core/game/node/scenery/SceneryBuilder.java index 550fb14d1..3dac14bf0 100644 --- a/Server/src/main/core/game/node/scenery/SceneryBuilder.java +++ b/Server/src/main/core/game/node/scenery/SceneryBuilder.java @@ -2,6 +2,7 @@ package core.game.node.scenery; import core.game.node.item.GroundItem; import core.game.node.item.GroundItemManager; +import core.tools.Log; import core.tools.SystemLogger; import core.game.system.task.Pulse; import core.game.world.GameWorld; @@ -10,6 +11,8 @@ import core.game.world.map.RegionManager; import core.game.world.map.build.LandscapeParser; import core.game.world.update.flag.chunk.ObjectUpdateFlag; +import static core.api.ContentAPIKt.log; + /** * An aiding class for object constructing/removing. * @@ -43,9 +46,6 @@ public final class SceneryBuilder { remove = remove.getWrapper(); Scenery current = LandscapeParser.removeScenery(remove); if (current == null) { - if (GameWorld.getSettings().isDevMode()) { - SystemLogger.logErr(SceneryBuilder.class, "Object could not be replaced - object to remove is invalid."); - } return false; } if (current.getRestorePulse() != null) { @@ -118,9 +118,6 @@ public final class SceneryBuilder { remove = remove.getWrapper(); Scenery current = LandscapeParser.removeScenery(remove); if (current == null) { - if (GameWorld.getSettings().isDevMode()) { - SystemLogger.logErr(SceneryBuilder.class, "Object could not be replaced - object to remove is invalid."); - } return false; } if (current.getRestorePulse() != null) { diff --git a/Server/src/main/core/game/requirement/Requirement.kt b/Server/src/main/core/game/requirement/Requirement.kt new file mode 100644 index 000000000..d43b68dac --- /dev/null +++ b/Server/src/main/core/game/requirement/Requirement.kt @@ -0,0 +1,201 @@ +package core.game.requirement + +import core.api.* +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.quest.QuestRepository +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> +} + +open class SkillReq (val skillId: Int, val level: Int, val soft: Boolean = false) : Requirement { + override fun evaluate (player: Player) : Pair> { + val hasLevelRequirement = getStatLevel(player, skillId) >= level //if (soft) getDynLevel(player, skillId) >= level else getStatLevel(player, skillId) >= level + if (hasLevelRequirement) return Pair (true, listOf()) + else return Pair(false, listOf(this)) + } +} + +open class QuestReq (val questReq: QuestRequirements, val stageRequired: Int = 100) : Requirement { + override fun evaluate (player: Player) : Pair> { + val quest = QuestRepository.getQuests()[questReq.quest] + val unmetRequirements = ArrayList() + var isMet = true + if (quest == null) { + for (req in questReq.requirements) { + val (met, reqs) = req.evaluate(player) + isMet = isMet && met + unmetRequirements.addAll(reqs) + } + unmetRequirements.add (QPCumulative(5)) + } else { + isMet = quest.getStage(player) >= stageRequired + if (!isMet) unmetRequirements.add(this) + unmetRequirements.add (QPCumulative(quest.getQuestPoints())) + } + + return Pair (isMet, unmetRequirements) + } +} + +open class QPReq (val amount: Int) : Requirement { + override fun evaluate (player: Player) : Pair> { + val needed = min(amount, player.questRepository.getAvailablePoints()) + val hasNeeded = player.questRepository.getPoints() >= needed + + return Pair (hasNeeded, if (hasNeeded) listOf() else listOf(this)) + } +} + +open class QPCumulative (val amount: Int) : Requirement { + override fun evaluate (player: Player) : Pair> { + return Pair (false, listOf(this)) + } +} + +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 01ef3f6f3..d19a4c2ec 100644 --- a/Server/src/main/core/game/shops/Shop.kt +++ b/Server/src/main/core/game/shops/Shop.kt @@ -1,23 +1,22 @@ package core.game.shops +import core.ServerConstants import core.api.* +import core.game.component.Component +import core.game.container.* +import core.game.container.Container import core.game.event.ItemShopPurchaseEvent import core.game.event.ItemShopSellEvent -import core.game.component.Component -import core.game.container.Container -import core.game.container.ContainerEvent -import core.game.container.ContainerListener -import core.game.container.ContainerType import core.game.node.entity.player.Player import core.game.node.item.Item +import core.game.shops.Shops.Companion.logShop +import core.game.system.config.ItemConfigParser +import core.game.world.GameWorld import core.net.packet.PacketRepository import core.net.packet.context.ContainerContext import core.net.packet.out.ContainerPacket import org.rs09.consts.Components import org.rs09.consts.Items -import core.ServerConstants -import core.game.shops.Shops.Companion.logShop -import core.game.world.GameWorld import java.lang.Integer.max import java.lang.Integer.min import kotlin.math.ceil @@ -37,7 +36,7 @@ class ShopListener(val player: Player) : ContainerListener } } -class Shop(val title: String, val stock: Array, val general: Boolean = false, val currency: Int = Items.COINS_995, val highAlch: Boolean = false) +class Shop(val title: String, val stock: Array, val general: Boolean = false, val currency: Int = Items.COINS_995, val highAlch: Boolean = false, val forceShared: Boolean = false) { val stockInstances = HashMap() val playerStock = if (general) generalPlayerStock else Container(40, ContainerType.SHOP) @@ -45,7 +44,7 @@ class Shop(val title: String, val stock: Array, val general: Boolean = val restockRates = HashMap() init { - if(!getServerConfig().getBoolean(Shops.personalizedShops, false)) + if(!getServerConfig().getBoolean(Shops.personalizedShops, false) || forceShared) stockInstances[ServerConstants.SERVER_NAME.hashCode()] = generateStockContainer() } @@ -77,8 +76,8 @@ class Shop(val title: String, val stock: Array, val general: Boolean = } val settings = IfaceSettingsBuilder() - .enableOptions(0..9) - .build() + .enableOptions(0..9) + .build() player.packetDispatch.sendIfaceSettings(settings, if (main) 23 else 24, Components.SHOP_TEMPLATE_620, 0, cont.capacity()) player.packetDispatch.sendRunScript(150, "IviiiIsssssssss", "", "", "", "", "Buy X", "Buy 10", "Buy 5", "Buy 1", "Value", -1, 0, 4, 10, 92, (620 shl 16) or if (main) 23 else 24) @@ -97,10 +96,10 @@ class Shop(val title: String, val stock: Array, val general: Boolean = public fun getContainer(player: Player) : Container { - val container = if(getServerConfig().getBoolean(Shops.personalizedShops, false)) + val container = if(getServerConfig().getBoolean(Shops.personalizedShops, false) && !forceShared) stockInstances[player.details.uid] ?: generateStockContainer().also { stockInstances[player.details.uid] = it } else - stockInstances[ServerConstants.SERVER_NAME.hashCode()]!! + stockInstances[ServerConstants.SERVER_NAME.hashCode()] ?: generateStockContainer().also { stockInstances[ServerConstants.SERVER_NAME.hashCode()] = it } val listener = listenerInstances[player.details.uid] @@ -135,6 +134,7 @@ class Shop(val title: String, val stock: Array, val general: Boolean = { if(cont[i] == null) continue if(stock.size < i + 1) break + if(stock[i].restockRate == 0) continue if(GameWorld.ticks % stock[i].restockRate != 0) continue if(cont[i].amount < stock[i].amount){ @@ -156,10 +156,17 @@ class Shop(val title: String, val stock: Array, val general: Boolean = val isMainStock = getAttribute(player, "shop-main", true) val cont = if (isMainStock) getAttribute(player, "shop-cont", null) ?: return Item(-1,-1) else playerStock val item = cont[slot] + + if (item == null) { + player.sendMessage("That item doesn't appear to be there anymore. Please try again.") + return Item(-1, -1) + } + val price = when(currency) { - Items.TOKKUL_6529 -> item.definition.getConfiguration("tokkul_price", 1) - Items.ARCHERY_TICKET_1464 -> item.definition.getConfiguration("archery_ticket_price", 1) + 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) } @@ -170,6 +177,13 @@ class Shop(val title: String, val stock: Array, val general: Boolean = { val shopCont = getAttribute(player, "shop-cont", null) ?: return Pair(null, Item(-1,-1)) val item = player.inventory[slot] + + if (item == null) { + player.debug("Inventory slot $slot does not contain an item!") + player.sendMessage("That item doesn't seem to be there anymore. Please try again.") + return Pair(null, Item(-1,-1)) + } + val shopItemId = if (item.definition.isUnnoted) { item.id } @@ -180,26 +194,27 @@ class Shop(val title: String, val stock: Array, val general: Boolean = var (isPlayerStock, shopSlot) = getStockSlot(shopItemId) val stockAmt = - if(isPlayerStock) - 0 - else{ - if(shopSlot != -1) stock[shopSlot].amount - else 0 - } - val currentAmt = - if(isPlayerStock) playerStock.getAmount(shopItemId) - else { - if(shopSlot != -1) shopCont[shopSlot].amount - else { - isPlayerStock = true + if(isPlayerStock) 0 + else{ + if(shopSlot != -1) stock[shopSlot].amount + else 0 + } + val currentAmt = + if(isPlayerStock) playerStock.getAmount(shopItemId) + else { + if(shopSlot != -1) shopCont[shopSlot].amount + else { + isPlayerStock = true + 0 + } } - } val price = when(currency) { - Items.TOKKUL_6529 -> (item.definition.getConfiguration("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("archery_ticket_price", 1) + 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) } @@ -223,9 +238,9 @@ class Shop(val title: String, val stock: Array, val general: Boolean = val price: Int = ceil(item.definition.value * mod.toDouble() / 100.0).toInt() -/* if(player.getVarp(532) == 6529){ - price = 3 * price / 2 - }*/ + /* if(player.getVarp(532) == 6529){ + price = 3 * price / 2 + }*/ return max(price, 1) } @@ -268,7 +283,7 @@ class Shop(val title: String, val stock: Array, val general: Boolean = return TransactionStatus.Failure("Shop item out of stock.") } - if(isMainStock && inStock.amount > stock[slot].amount && !getServerConfig().getBoolean(Shops.personalizedShops, false) && player.ironmanManager.isIronman) + if(isMainStock && inStock.amount > stock[slot].amount && (!getServerConfig().getBoolean(Shops.personalizedShops, false) || forceShared) && player.ironmanManager.isIronman) { sendDialogue(player, "As an ironman, you cannot buy overstocked items from shops.") return TransactionStatus.Failure("Ironman overstock purchase") @@ -283,14 +298,14 @@ class Shop(val title: String, val stock: Array, val general: Boolean = 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 + cost.amount = cost.amount * item.amount } if(inInventory(player, cost.id, cost.amount)) { if(removeItem(player, cost)) { - if (item.amount == 0 && amountInInventory(player, cost.id) == 0) { + if (item.amount == 0) { item.amount = 1 } if(!hasSpaceFor(player, item)) { @@ -321,7 +336,8 @@ class Shop(val title: String, val stock: Array, val general: Boolean = } else { - sendMessage(player, "You don't have enough ${cost.name.toLowerCase()} to buy that many.") + sendMessage(player, "You don't have enough ${cost.name.lowercase()} to buy that many.") + return TransactionStatus.Failure("Not enough money in inventory") } player.dispatch(ItemShopPurchaseEvent(item.id, item.amount, cost)) @@ -355,9 +371,15 @@ class Shop(val title: String, val stock: Array, val general: Boolean = if(amount > player.inventory.getAmount(item.id)) item.amount = player.inventory.getAmount(item.id) + val id = if(!item.definition.isUnnoted) item.noteChange else item.id + val (isPlayerStock, shopSlot) = getStockSlot(id) + + if(isPlayerStock && shopSlot == -1 && generalPlayerStock.freeSlots() == 0) { + sendMessage(player, "The shop is too full to buy any more items") + return TransactionStatus.Failure("Attempt to sell to full shop.") + } + if(currency == Items.COINS_995 && item.amount > 1){ - val id = if(!item.definition.isUnnoted) item.noteChange else item.id - val (isPlayerStock, shopSlot) = getStockSlot(id) var amt = item.amount var inStockAmt = container!![shopSlot]?.amount ?: playerStock.getAmount(id) while(amt-- > 1) @@ -434,4 +456,4 @@ class Shop(val title: String, val stock: Array, val general: Boolean = class Success : TransactionStatus() class Failure(val reason: String) : TransactionStatus() } -} +} \ No newline at end of file diff --git a/Server/src/main/core/game/shops/Shops.kt b/Server/src/main/core/game/shops/Shops.kt index 0bf521d2a..0a649a891 100644 --- a/Server/src/main/core/game/shops/Shops.kt +++ b/Server/src/main/core/game/shops/Shops.kt @@ -1,23 +1,26 @@ package core.game.shops +import content.global.skill.crafting.TanningProduct +import core.ServerConstants import core.api.* +import core.game.ge.GrandExchange +import core.game.interaction.IntType +import core.game.interaction.InteractionListener +import core.game.interaction.InterfaceListener import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player -import content.global.skill.crafting.TanningProduct +import core.game.system.command.Privilege +import core.tools.END_DIALOGUE +import core.tools.Log +import core.tools.secondsToTicks import org.json.simple.JSONArray import org.json.simple.JSONObject import org.json.simple.parser.JSONParser import org.rs09.consts.Components import org.rs09.consts.Items import org.rs09.consts.NPCs -import core.ServerConstants -import core.game.interaction.InteractionListener -import core.game.interaction.IntType -import core.game.interaction.InterfaceListener -import core.tools.SystemLogger -import core.game.system.command.Privilege -import core.tools.END_DIALOGUE import java.io.FileReader +import content.data.Quests /** * The "controller" class for shops. Handles opening shops from various NPC interactions and updating stock, etc. @@ -33,6 +36,7 @@ class Shops : StartupListener, TickListener, InteractionListener, InterfaceListe @JvmStatic val personalizedShops = "world.personalized_shops" @JvmStatic val shopsById = HashMap() @JvmStatic val shopsByNpc = HashMap() + private var lastPlayerStockClear = 0 @JvmStatic fun openId(player: Player, id: Int) { @@ -41,7 +45,7 @@ class Shops : StartupListener, TickListener, InteractionListener, InterfaceListe fun logShop(msg: String) { - SystemLogger.logInfo(this::class.java, "[SHOPS] $msg") + log(this::class.java, Log.FINE, "[SHOPS] $msg") } fun parseStock(stock: String, id: Int): ArrayList{ @@ -51,20 +55,25 @@ class Shops : StartupListener, TickListener, InteractionListener, InterfaceListe return items } stock.split('-').map { - val tokens = it.replace("{", "").replace("}", "").split(",".toRegex()).toTypedArray() - var amount = tokens[1].trim() - if(amount == "inf") - amount = "-1" - val item = tokens[0].toInt() - if(idsInStock[item] != null) { - SystemLogger.logWarn(this::class.java, "[SHOPS] MALFORMED STOCK IN SHOP ID $id FOR ITEM $item") - items.forEach { if(it.itemId == item) { - it.amount += amount.toInt() - return@map - }} - } else { - items.add(ShopItem(item, amount.toInt(), tokens.getOrNull(2)?.toIntOrNull() ?: 100)) - idsInStock[item] = true + try { + val tokens = it.replace("{", "").replace("}", "").split(",".toRegex()).toTypedArray() + var amount = tokens[1].trim() + if(amount == "inf") + amount = "-1" + val item = tokens[0].toInt() + if(idsInStock[item] != null) { + log(this::class.java, Log.WARN, "[SHOPS] MALFORMED STOCK IN SHOP ID $id FOR ITEM $item") + items.forEach { if(it.itemId == item) { + it.amount += amount.toInt() + return@map + }} + } else { + items.add(ShopItem(item, amount.toInt(), tokens.getOrNull(2)?.toIntOrNull() ?: 100)) + idsInStock[item] = true + } + } catch (e: Exception) { + log(this::class.java, Log.WARN, "[SHOPS] MALFORMED STOCK IN SHOP ID $id FOR ITEM $it") + throw e } } return items @@ -90,7 +99,8 @@ class Shops : StartupListener, TickListener, InteractionListener, InterfaceListe val npcs = if(shopData["npcs"].toString().isNotBlank()) shopData["npcs"].toString().split(",").map { it.toInt() }.toIntArray() else intArrayOf() val currency = shopData["currency"].toString().toInt() val highAlch = shopData["high_alch"].toString() == "1" - shop = Shop(title, stock, general, currency, highAlch) + val forceShared = shopData.getOrDefault("force_shared", "false").toString().toBoolean() + shop = Shop(title, stock, general, currency, highAlch, forceShared) npcs.map { shopsByNpc[it] = shop } shopsById[id] = shop @@ -102,13 +112,23 @@ class Shops : StartupListener, TickListener, InteractionListener, InterfaceListe override fun tick() { shopsById.values.forEach(Shop::restock) + + val playerStockClearInterval = secondsToTicks(ServerConstants.PLAYER_STOCK_CLEAR_INTERVAL * 60) + if (getWorldTicks() % playerStockClearInterval == 0) { + val clearToGe = ServerConstants.PLAYER_STOCK_RECIRCULATE + if (clearToGe) { + for (item in Shop.generalPlayerStock.toArray().filter{it != null}) + GrandExchange.addBotOffer(item.id, item.amount) + } + Shop.generalPlayerStock.clear() + } } 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) { @@ -120,7 +140,7 @@ class Shops : StartupListener, TickListener, InteractionListener, InterfaceListe } on(NPCs.SIEGFRIED_ERKLE_933, IntType.NPC, "trade"){ player, node -> - val points = getQP(player) + val points = getQuestPoints(player) if(points < 40){ sendNPCDialogue(player, NPCs.SIEGFRIED_ERKLE_933, "I'm sorry, adventurer, but you need 40 quest points to buy from me.") return@on true @@ -130,13 +150,22 @@ 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) } return@on true } + + on(NPCs.CANDLE_MAKER_562, IntType.NPC, "trade") { player, node -> + if (getQuestStage(player, Quests.MERLINS_CRYSTAL) > 60) { + openId(player, 56) + } else { + shopsByNpc[node.id]?.openFor(player) + } + return@on true + } } override fun defineInterfaceListeners() { @@ -162,11 +191,11 @@ class Shops : StartupListener, TickListener, InteractionListener, InterfaceListe when(opcode) { - OP_VALUE -> sendMessage(player, "${getItemName(if (isMainStock) shop.stock[slot].itemId else shop.playerStock[slot].id)}: This item currently costs ${price.amount} ${price.name.toLowerCase()}.") + OP_VALUE -> sendMessage(player, "${getItemName(if (isMainStock) shop.stock[slot].itemId else shop.playerStock[slot].id)}: This item currently costs ${price.amount} ${price.name.lowercase()}.") OP_BUY_1 -> shop.buy(player, slot, 1) OP_BUY_5 -> shop.buy(player, slot, 5) OP_BUY_10 -> shop.buy(player, slot, 10) - OP_BUY_X -> sendInputDialogue(player, true, "Enter the amount to buy:"){value -> + OP_BUY_X -> sendInputDialogue(player, InputType.AMOUNT, "Enter the amount to buy:"){value -> val amt = value as Int shop.buy(player, slot, amt) } @@ -181,7 +210,7 @@ class Shops : StartupListener, TickListener, InteractionListener, InterfaceListe .enableOptions(0 until 9) .build() player.packetDispatch.sendIfaceSettings(settings, 0, 621, 0, 28) - player.packetDispatch.sendRunScript(150, "IviiiIsssssssss", "", "", "", "", "Sell X", "Sell 10", "Sell 5", "Sell 1", "Value", -1, 0, 7, 4, 93, 621 shl 16); + player.packetDispatch.sendRunScript(150, "IviiiIsssssssss", "", "", "", "", "Sell X", "Sell 10", "Sell 5", "Sell 1", "Value", -1, 0, 7, 4, 93, 621 shl 16) return@onOpen true } @@ -208,14 +237,20 @@ class Shops : StartupListener, TickListener, InteractionListener, InterfaceListe val shop = getAttribute(player, "shop", null) ?: return@on false + val itemInSlot = player.inventory[slot] + if (itemInSlot == null) { + player.sendMessage("That item doesn't appear to be there anymore. Please try again.") + return@on true + } + val (_,price) = shop.getSellPrice(player, slot) val def = itemDefinition(player.inventory[slot].id) val valueMsg = when { (price.amount == -1) - || !def.isTradeable - || def.id in intArrayOf(Items.COINS_995, Items.TOKKUL_6529, Items.ARCHERY_TICKET_1464) - || def.hasDestroyAction() -> "This shop will not buy that item." + || !def.hasShopCurrencyValue(price.id) + || def.id in intArrayOf(Items.COINS_995, Items.TOKKUL_6529, Items.ARCHERY_TICKET_1464, Items.CASTLE_WARS_TICKET_4067) + -> "This shop will not buy that item." else -> "${player.inventory[slot].name}: This shop will buy this item for ${price.amount} ${price.name.lowercase()}." } @@ -225,7 +260,7 @@ class Shops : StartupListener, TickListener, InteractionListener, InterfaceListe OP_SELL_1 -> shop.sell(player, slot, 1) OP_SELL_5 -> shop.sell(player, slot, 5) OP_SELL_10 -> shop.sell(player, slot, 10) - OP_SELL_X -> sendInputDialogue(player, true, "Enter the amount to sell:"){value -> + OP_SELL_X -> sendInputDialogue(player, InputType.AMOUNT, "Enter the amount to sell:"){value -> val amt = value as Int shop.sell(player, slot, amt) } @@ -259,4 +294,4 @@ class Shops : StartupListener, TickListener, InteractionListener, InterfaceListe player.packetDispatch.sendRunScript(25, "vg", arg1, 92) //Run CS2 script 25, with args 868? and 92(our container id) } } -} \ No newline at end of file +} diff --git a/Server/src/main/core/game/system/SystemShutdownHook.java b/Server/src/main/core/game/system/SystemShutdownHook.java index 1736c293e..850de35ed 100644 --- a/Server/src/main/core/game/system/SystemShutdownHook.java +++ b/Server/src/main/core/game/system/SystemShutdownHook.java @@ -1,7 +1,10 @@ package core.game.system; +import core.tools.Log; import core.tools.SystemLogger; +import static core.api.ContentAPIKt.log; + /** * Handles the shutdown hook. * @author Emperor @@ -13,7 +16,7 @@ public final class SystemShutdownHook implements Runnable { if (SystemManager.isTerminated()) { return; } - SystemLogger.logInfo(this.getClass(), "[SystemShutdownHook] Terminating..."); + log(this.getClass(), Log.INFO, "Terminating..."); SystemManager.flag(SystemState.TERMINATED); } } \ No newline at end of file diff --git a/Server/src/main/core/game/system/SystemTermination.java b/Server/src/main/core/game/system/SystemTermination.java index 5c7fb2665..a1557f381 100644 --- a/Server/src/main/core/game/system/SystemTermination.java +++ b/Server/src/main/core/game/system/SystemTermination.java @@ -8,6 +8,7 @@ import core.ServerConstants; import core.ServerStore; import core.game.bots.AIRepository; import core.game.node.entity.player.info.PlayerMonitor; +import core.tools.Log; import core.tools.SystemLogger; import core.game.world.GameWorld; import core.game.world.repository.Repository; @@ -15,6 +16,8 @@ import core.game.world.repository.Repository; import java.io.File; import java.util.Iterator; +import static core.api.ContentAPIKt.log; + /** * Handles the terminating of the system. * @author Emperor @@ -35,14 +38,14 @@ public final class SystemTermination { * Terminates the system safely. */ public void terminate() { - SystemLogger.logInfo(this.getClass(), "Initializing termination sequence - do not shutdown!"); + log(this.getClass(), Log.INFO, "Initializing termination sequence - do not shutdown!"); try { - SystemLogger.logInfo(this.getClass(), "Shutting down networking..."); + log(this.getClass(), Log.INFO, "Shutting down networking..."); Server.setRunning(false); - SystemLogger.logInfo(this.getClass(), "Stopping all bots..."); + log(this.getClass(), Log.INFO, "Stopping all bots..."); AIRepository.clearAllBots(); Server.getReactor().terminate(); - SystemLogger.logInfo(this.getClass(), "Stopping all pulses..."); + log(this.getClass(), Log.INFO, "Stopping all pulses..."); GameWorld.getMajorUpdateWorker().stop(); for (Iterator it = Repository.getPlayers().iterator(); it.hasNext();) { try { @@ -72,7 +75,7 @@ public final class SystemTermination { } catch (Throwable e) { e.printStackTrace(); } - SystemLogger.logInfo(this.getClass(), "Server successfully terminated!"); + log(this.getClass(), Log.INFO, "Server successfully terminated!"); } /** @@ -81,7 +84,7 @@ public final class SystemTermination { */ public void save(String directory) { File file = new File(directory); - SystemLogger.logInfo(this.getClass(), "Saving data [dir="+ file.getAbsolutePath() + "]..."); + log(this.getClass(), Log.INFO, "Saving data [dir="+ file.getAbsolutePath() + "]..."); if (!file.isDirectory()) { file.mkdirs(); } @@ -94,7 +97,6 @@ public final class SystemTermination { } catch (Exception ignored) {} } Repository.getDisconnectionQueue().update(); - SystemLogger.flushLogs(); Repository.getDisconnectionQueue().clear(); } } diff --git a/Server/src/main/core/game/system/SystemUpdate.java b/Server/src/main/core/game/system/SystemUpdate.java index bb5375ea0..e28fe342f 100644 --- a/Server/src/main/core/game/system/SystemUpdate.java +++ b/Server/src/main/core/game/system/SystemUpdate.java @@ -57,7 +57,7 @@ public final class SystemUpdate extends Pulse { */ public void notifyPlayers() { try { - System.out.println("BEGINNING UPDATE SEQUENCE - TIME TIL UPDATE " + getDelay() + " TICKS"); + int time = getDelay() + (createBackup ? BACKUP_TICK : 0); for (Iterator it = Repository.getPlayers().iterator(); it.hasNext();) { Player p = it.next(); 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/MapDumpCommand.kt b/Server/src/main/core/game/system/command/MapDumpCommand.kt index 25fed543b..2743af899 100644 --- a/Server/src/main/core/game/system/command/MapDumpCommand.kt +++ b/Server/src/main/core/game/system/command/MapDumpCommand.kt @@ -50,7 +50,7 @@ class MapDumpCommand : CommandPlugin() { LocationObjectMap[value] = locations } } - return; + return } private fun findObj(player: Player?, args: List?) { @@ -76,7 +76,7 @@ class MapDumpCommand : CommandPlugin() { } } - return; + return } } } \ No newline at end of file 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 a395ca995..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") @@ -232,7 +243,7 @@ class VisualCommand : CommandPlugin() { var shift = 0 override fun pulse(): Boolean { for(i in 0..1999){ - player?.configManager?.forceSet(i, pos shl shift, false) + setVarp(player!!, i, pos shl shift) } player?.sendMessage("$pos shl $shift") if(pos++ >= 63){ @@ -246,34 +257,14 @@ class VisualCommand : CommandPlugin() { submitWorldPulse(object : Pulse(3, player) { var pos = 0 override fun pulse(): Boolean { - player?.configManager?.forceSet(cfg_index, value shl pos, false) + setVarp(player!!, cfg_index, value shl pos) player?.sendMessage("$pos") return pos++ >= 32 } }) } - } - "setbit" -> { - if (args!!.size < 2) { - player!!.debug("syntax error: bit value") - return true - } - var bit = toInteger(args[0]!!) - var value = toInteger(args[1]!!) - var val2 = toInteger(args[2]!!) - player!!.debug("$value $val2") - PacketRepository.send(Varbit::class.java, VarbitContext(player, value, val2)) return true } - "setbits" -> { - args ?: return false - val start = toInteger(args[1]!!) - val end = toInteger(args[2]!!) - val value = toInteger(args[3]!!) - for(i in start until end){ - player?.varpManager?.setVarbit(i, value) - } - } "loop_anim_on_i" -> { var anim = toInteger(args!![1]!!) submitWorldPulse(object : Pulse(3){ @@ -283,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]!!) @@ -342,7 +335,7 @@ class VisualCommand : CommandPlugin() { val value = toInteger(args[3]!!) var i = toInteger(args[1]!!) while (i < toInteger(args[2]!!)) { - player!!.configManager[i] = value + setVarp(player!!, i, value) i++ } return true @@ -437,10 +430,6 @@ class VisualCommand : CommandPlugin() { player!!.impactHandler.manualHit(player, toInteger(args!![1]!!), HitsplatType.NORMAL) return true } - "sound" -> { - player!!.audioManager.send(Audio(args!![1]!!.toInt(), 10, 1)) - return true - } "noclip" -> { player!!.setAttribute("no_clip", !player.getAttribute("no_clip", false)) return true @@ -517,6 +506,6 @@ class VisualCommand : CommandPlugin() { return true }*/ } - return false; + return false } -} \ No newline at end of file +} diff --git a/Server/src/main/core/game/system/command/rottenpotato/RPUseWithPlayerDialogue.kt b/Server/src/main/core/game/system/command/rottenpotato/RPUseWithPlayerDialogue.kt index 8b0ae8fd0..f79fdfcd9 100644 --- a/Server/src/main/core/game/system/command/rottenpotato/RPUseWithPlayerDialogue.kt +++ b/Server/src/main/core/game/system/command/rottenpotato/RPUseWithPlayerDialogue.kt @@ -10,11 +10,11 @@ import core.plugin.Initializable * Rotten Potato -> Player interaction dialogue * @author Ceikry */ -class RPUseWithPlayerDialogue(player: Player? = null) : core.game.dialogue.DialoguePlugin(player) { +class RPUseWithPlayerDialogue(player: Player? = null) : DialoguePlugin(player) { var other: Player? = null val ID = 38575796 - override fun newInstance(player: Player?): core.game.dialogue.DialoguePlugin { + override fun newInstance(player: Player?): DialoguePlugin { return RPUseWithPlayerDialogue(player) } diff --git a/Server/src/main/core/game/system/command/rottenpotato/RottenPotatoOptionHandler.kt b/Server/src/main/core/game/system/command/rottenpotato/RottenPotatoOptionHandler.kt index e3e69ac3e..3d825cac4 100644 --- a/Server/src/main/core/game/system/command/rottenpotato/RottenPotatoOptionHandler.kt +++ b/Server/src/main/core/game/system/command/rottenpotato/RottenPotatoOptionHandler.kt @@ -24,7 +24,7 @@ class RottenPotatoOptionHandler : OptionHandler() { for(option in ROTTEN_POTATO_OPTIONS) { ItemDefinition.forId(Items.ROTTEN_POTATO_5733).handlers["option:${option.toLowerCase()}"] = this } - return this; + return this } override fun handle(player: Player?, node: Node?, option: String?): Boolean { @@ -36,7 +36,6 @@ class RottenPotatoOptionHandler : OptionHandler() { if(player.rights != Rights.ADMINISTRATOR) { removeItem(player, Items.ROTTEN_POTATO_5733) - SystemLogger.logAlert(this::class.java, "Player ${player.username} had a rotten potato. It has been removed.") return false } 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..e1de944d1 100644 --- a/Server/src/main/core/game/system/command/sets/AnimationCommandSet.kt +++ b/Server/src/main/core/game/system/command/sets/AnimationCommandSet.kt @@ -27,6 +27,25 @@ class AnimationCommandSet : CommandSet(Privilege.ADMIN) { player.animate(animation) } + /** + * Force the player to play animation + */ + define("anims", Privilege.ADMIN, "::anim Animation ID", "Plays the animation with the given ID."){ player, args -> + if (args.size < 3) { + reject(player, "Syntax error: ::anim ") + } + val animation = args[1].toInt() + val animationTo = args[2].toInt() + GameWorld.Pulser.submit(object : Pulse(3, player) { + var someId = animation + override fun pulse(): Boolean { + player.animate(Animation.create(someId)) + someId += 1 + return someId >= animationTo + } + }) + } + /** * Force the player to loop animation */ @@ -56,11 +75,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/AudioCommandSet.kt b/Server/src/main/core/game/system/command/sets/AudioCommandSet.kt new file mode 100644 index 000000000..ac8ee0324 --- /dev/null +++ b/Server/src/main/core/game/system/command/sets/AudioCommandSet.kt @@ -0,0 +1,99 @@ +package core.game.system.command.sets + +import core.api.playAudio +import core.api.playGlobalAudio +import core.game.node.entity.player.link.music.MusicEntry +import core.game.system.command.Privilege +import core.game.world.map.Location +import core.game.world.repository.Repository +import core.net.packet.PacketRepository +import core.net.packet.context.MusicContext +import core.net.packet.out.MusicPacket +import core.plugin.Initializable + +@Initializable +class MusicCommandSet : CommandSet(Privilege.STANDARD){ + override fun defineCommands() { + + /** + * Command that lets you play a specific song + */ + define("playsong"){player,args -> + if(args.size < 2){ + reject(player,"Usage: ::playsong songID") + } + val id = args[1].toIntOrNull() + if(id == null){ + reject(player,"Please use a valid integer for the song id.") + } + player.musicPlayer.play(MusicEntry.forId(id!!)) + notify(player,"Now playing song $id") + } + /** + * Command that lets you play a specific jingle + */ + define("playjingle"){player,args -> + if(args.size < 2){ + reject(player,"Usage: ::playjingle jingleID") + } + val id = args[1].toIntOrNull() + if(id == null){ + reject(player,"Please use a valid integer for the jingle id.") + } + PacketRepository.send(MusicPacket::class.java, MusicContext(player, id!!, true)) + notify(player,"Now playing jingle $id") + } + + /** + * Command that lets you play a song via the ID instead of the song name + * this is mostly useful for custom tracks that aren't in the ingame + * music player yet. + */ + define("playid"){player,arg -> + if (arg.size < 2) + reject(player, "Needs more args.") + val id = arg[1].toIntOrNull() + if(id != null){ + PacketRepository.send(MusicPacket::class.java, MusicContext(player, id)) + notify(player,"Now playing song $id") + } + } + + /** + * Command that unlocks all music tracks for the player + * Restricted to ADMIN access only. + */ + define("allmusic", Privilege.ADMIN){ player, _ -> + for (me in MusicEntry.getSongs().values) { + player.musicPlayer.unlock(me.id) + } + } + /** + * Command that lets you play an audio id + */ + define("audio", Privilege.ADMIN, "::audio id loops[optional]", "Plays audio by id") { player, args -> + if (args.size < 2 || args.size > 3) + reject(player, "Usage: ::audio id loops[optional]") + val id = args[1].toInt() + val loops = if (args.size > 2) args[2].toInt() else 1 + playAudio(player, id, 0, loops) + } + /** + * Command that lets you play an audio id globally by playername or coords + */ + define("globalaudio", Privilege.ADMIN, "::globalaudio id radius[max 15] location[player name or x y z]", "Play global audio by id, radius, and location") { player, args -> + if (args.size < 3) + reject(player, "Usage: ::globalaudio id radius[max 15] location[player name or x y z]") + val loc = when (args.size) { + 6 -> Location(args[3].toInt(),args[4].toInt(),args[5].toInt()) + 4 -> Repository.getPlayerByName(args[3])?.location + else -> null + } + if (loc == null) + reject(player, "Invalid player name / location") + val id = args[1].toInt() + val radius = if (args[2].toInt() > 15) 15 else args[2].toInt() + if (loc != null) playGlobalAudio(loc, id, 0, 1, radius) + } + } +} diff --git a/Server/src/main/core/game/system/command/sets/CameraCommandSet.kt b/Server/src/main/core/game/system/command/sets/CameraCommandSet.kt new file mode 100644 index 000000000..e501da9a9 --- /dev/null +++ b/Server/src/main/core/game/system/command/sets/CameraCommandSet.kt @@ -0,0 +1,117 @@ +package core.game.system.command.sets + +import core.api.sendMessage +import core.api.utils.PlayerCamera +import core.game.system.command.Privilege +import core.game.world.map.RegionManager +import core.plugin.Initializable + +/** + * Camera command set + * + * @constructor Create empty Camera command set + * + * WARNING: Playing around with camera values may potentially trigger seizures for people with photosensitive epilepsy. + * Please use care when discovering camera values. + */ +@Initializable +class CameraCommandSet : CommandSet(Privilege.ADMIN) { + + override fun defineCommands() { + define("poscam", Privilege.ADMIN, "::poscam Region X Region Y [Height]", "Positions the camera to the given region-local coordinates.") { player, args -> + val regionX = args[1].toIntOrNull() ?: return@define + val regionY = args[2].toIntOrNull() ?: return@define + var height = 300 + + if (args.size > 3) { + height = args[3].toIntOrNull() ?: return@define + } + + val region = RegionManager.forId(player.location.regionId) + val base = region.baseLocation + + val globalLoc = base.transform(regionX, regionY, 0) + sendMessage(player, "CAMERA POSITION | loc:[$regionX, $regionY] settings:[height:$height]") + PlayerCamera(player).setPosition(globalLoc.x, globalLoc.y, height) + } + + define("movcam", Privilege.ADMIN, "::movcam Region X Region Y [Height Speed]", "Moves the camera to the given region-local coordinates.") { player, args -> + val regionX = args[1].toIntOrNull() ?: return@define + val regionY = args[2].toIntOrNull() ?: return@define + var height = 300 + var speed = 100 + + if (args.size > 3) + height = args[3].toIntOrNull() ?: return@define + + if (args.size > 4) + speed = args[4].toIntOrNull() ?: return@define + + val region = RegionManager.forId(player.location.regionId) + val base = region.baseLocation + + val globalLoc = base.transform(regionX, regionY, 0) + sendMessage(player, "CAMERA MOVE | loc:[$regionX, $regionY] settings:[height:$height, speed:$speed]") + PlayerCamera(player).panTo(globalLoc.x, globalLoc.y, height, speed) + } + + define("rotcam", Privilege.ADMIN, "::rotcam Region X Region Y [Height Speed]", "Rotates the camera to face the given region-local coordinates.") { player, args -> + val regionX = args[1].toIntOrNull() ?: return@define + val regionY = args[2].toIntOrNull() ?: return@define + var height = 300 + var speed = 100 + + if (args.size > 3) { + height = args[3].toIntOrNull() ?: return@define + } + + if (args.size > 4) { + speed = args[4].toIntOrNull() ?: return@define + } + + val region = RegionManager.forId(player.location.regionId) + val base = region.baseLocation + + val globalLoc = base.transform(regionX, regionY, 0) + sendMessage(player, "CAMERA ROTATE | loc:[$regionX, $regionY] settings:[height:$height, speed:$speed]") + PlayerCamera(player).rotateTo(globalLoc.x, globalLoc.y, height, speed) + } + + define("shakecam", Privilege.ADMIN, "::shakecam Camera Movement Type (0-4) [Jitter Amplitude Frequency Speed]", + "Type (0-4) Jitter, Amplitude, Frequency (0-255)") {player, args -> + val cameraMovementType = args[1].toIntOrNull() ?: return@define + if (cameraMovementType < 0 || cameraMovementType > 4) { + return@define + } + // Type [0-4] Jit: 0 Amp: 0 Freq: 128 Speed: 2 + var jitter = 0 + var amplitude = 0 + var frequency = 128 + var speed = 2 + + if (args.size > 2) { + jitter = args[2].toIntOrNull() ?: return@define + } + + if (args.size > 3) { + amplitude = args[3].toIntOrNull() ?: return@define + } + + if (args.size > 4) { + frequency = args[4].toIntOrNull() ?: return@define + } + + if (args.size > 5) { + speed = args[5].toIntOrNull() ?: return@define + } + + sendMessage(player, "CAMERA SHAKE | type:$cameraMovementType settings:[jit:$jitter, amp:$amplitude, freq:$frequency, speed:$speed]") + PlayerCamera(player).shake(cameraMovementType, jitter, amplitude, frequency, speed) + } + + define("resetcam", Privilege.ADMIN, "::resetcam", "Resets the current camera position.") {player, args -> + sendMessage(player, "CAMERA RESET") + PlayerCamera(player).reset() + } + } +} \ No newline at end of file diff --git a/Server/src/main/core/game/system/command/sets/ConfigCommandSet.kt b/Server/src/main/core/game/system/command/sets/ConfigCommandSet.kt index 5f64859d9..25c5fb2e1 100644 --- a/Server/src/main/core/game/system/command/sets/ConfigCommandSet.kt +++ b/Server/src/main/core/game/system/command/sets/ConfigCommandSet.kt @@ -3,6 +3,7 @@ package core.game.system.command.sets import core.plugin.Initializable import core.game.system.command.Command import core.game.system.command.Privilege +import core.api.* @Initializable class ConfigCommandSet : CommandSet(Privilege.ADMIN){ @@ -18,7 +19,7 @@ class ConfigCommandSet : CommandSet(Privilege.ADMIN){ val idlo = args[1].toIntOrNull() ?: reject(player, "INCORRECT ID LOW") val idhi = args[2].toIntOrNull() ?: reject(player, "INCORRECT ID HIGH") for (idsend in (idlo as Int) until (idhi as Int)) { - player.configManager.set(idsend, Integer.MAX_VALUE) + setVarp(player, idsend, Integer.MAX_VALUE) notify(player,"Config: $idsend value: " + Integer.MAX_VALUE) } } @@ -33,7 +34,7 @@ class ConfigCommandSet : CommandSet(Privilege.ADMIN){ val idlo = args[1].toIntOrNull() ?: reject(player, "INCORRECT ID LOW") val idhi = args[2].toIntOrNull() ?: reject(player, "INCORRECT ID HIGH") for (idsend in (idlo as Int) until (idhi as Int)) { - player.configManager.set(idsend, 0) + setVarp(player, idsend, 0) notify(player,"Config: $idsend value: 0") } } 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 c9d7ab19d..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,6 +1,7 @@ package core.game.system.command.sets -import core.api.sendMessage +import content.global.activity.jobs.JobManager +import core.api.* import core.cache.Cache import core.cache.def.impl.DataMap import core.cache.def.impl.NPCDefinition @@ -17,28 +18,56 @@ 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 import java.io.FileWriter +import java.util.Arrays import core.net.packet.PacketWriteQueue +import core.tools.Log +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)) } } + define("cs2", Privilege.ADMIN, "::cs2 id args", "Allows you to call arbitrary cs2 scripts during runtime") {player, args -> + var scriptArgs = ArrayList() + if (args.size == 2) { + runcs2(player, args[1].toIntOrNull() ?: return@define) + return@define + } + else if (args.size > 2) { + for (i in 2 until args.size) { + scriptArgs.add(args[i].toIntOrNull() ?: args[i]) + } + runcs2(player, args[1].toIntOrNull() ?: return@define, *(scriptArgs.toTypedArray().also { player.debug (Arrays.toString(it)) })) + } + } + define("cleardiary", Privilege.ADMIN) { player, _ -> for (type in DiaryType.values()) { val diary = player.achievementDiaryManager.getDiary(type) @@ -51,6 +80,15 @@ class DevelopmentCommandSet : CommandSet(Privilege.ADMIN) { sendMessage(player, "All achievement diaries cleared successfully.") } + define("clearjob", Privilege.ADMIN) { player, _ -> + val playerJobManager = JobManager.getInstance(player) + playerJobManager.job = null + playerJobManager.jobAmount = -1 + playerJobManager.jobOriginalAmount = -1 + + sendMessage(player, "Job cleared successfully.") + } + define("region", Privilege.STANDARD, "", "Prints your current Region ID.") {player, args -> sendMessage(player, "Region ID: ${player.viewport.region.regionId}") } @@ -72,14 +110,14 @@ class DevelopmentCommandSet : CommandSet(Privilege.ADMIN) { val mapId = args[1].toIntOrNull() ?: return@define val def = Struct.get(mapId) - SystemLogger.logInfo(this::class.java, def.toString()) + log(this::class.java, Log.FINE, def.toString()) } define("datamap") {player, args -> val mapId = args[1].toIntOrNull() ?: return@define val def = DataMap.get(mapId) - SystemLogger.logInfo(this::class.java, def.toString()) + log(this::class.java, Log.FINE, def.toString()) } define("dumpstructs", Privilege.ADMIN, "", "Dumps all the cache structs to structs.txt") {player, _ -> @@ -136,13 +174,8 @@ class DevelopmentCommandSet : CommandSet(Privilege.ADMIN) { val amount = args[2].toInt() container.clear() - - for(i in 0..amount) - { - val drops = NPCDefinition.forId(npcId).dropTables.table.roll(player) - for(drop in drops) container.add(drop) - } - + val drops = NPCDefinition.forId(npcId).dropTables.table.roll(player, amount) + for(drop in drops) container.add(drop, false) container.open(player) } @@ -168,5 +201,178 @@ class DevelopmentCommandSet : CommandSet(Privilege.ADMIN) { define("testpacket") { player, _ -> PacketWriteQueue.write(ResetInterface(), PlayerContext(player)) } + + define("npcsearch", Privilege.STANDARD, "npcsearch name", "Searches for NPCs that match the name either in main or children.") {player, strings -> + val name = strings.slice(1 until strings.size).joinToString(" ").lowercase() + for (id in 0 until 9000) { + val def = NPCDefinition.forId(id) + if (def.name.isNotBlank() && (def.name.lowercase().contains(name) || name.contains(def.name.lowercase()))) { + notify(player, "$id - ${def.name}") + } + else { + for ((childId,index) in def.childNPCIds?.withIndex() ?: continue) { + val childDef = NPCDefinition.forId(childId) + if (childDef.name.lowercase().contains(name) || name.contains(childDef.name.lowercase())) { + notify(player, "$childId child($id) index $index - ${childDef.name}") + } + } + } + } + } + + 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() + if (name.contains(itemName) || itemName.contains(name)) + notify(player, "$i: $name") + } + } + + define("runekit", Privilege.ADMIN, "", "Gives 1k of each Rune type" ) { player, args -> + for(item in runeKitItems) { + addItem(player, item, 1000) + } + } + + define("drawchunks", Privilege.ADMIN, "", "Draws the border of the chunk you're standing in") {player, _ -> + 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)) + } + + define("drawroute", Privilege.ADMIN, "", "Visualizes the path your player is taking") {player, _ -> + setAttribute (player, "routedraw", !getAttribute(player, "routedraw", false)) + } + + define ("fmstart", Privilege.ADMIN, "", "") {player, _ -> + setAttribute(player, "fmstart", Location.create(player.location)) + } + + define ("fmend", Privilege.ADMIN, "", "") {player, _ -> + setAttribute(player, "fmend", Location.create(player.location)) + } + + define ("fmspeed", Privilege.ADMIN, "", "") {player, args -> + setAttribute(player, "fmspeed", args[1].toIntOrNull() ?: 10) + } + + define ("fmspeedend", Privilege.ADMIN, "", "") {player, args -> + setAttribute(player, "fmspeedend", args[1].toIntOrNull() ?: 10) + } + + define("testfm", Privilege.ADMIN, "", "") { player, _ -> + val start = getAttribute(player, "fmstart", Location.create(player.location)) + val end = getAttribute(player, "fmend", Location.create(player.location)) + val speed = getAttribute(player, "fmspeed", 10) + val speedEnd = getAttribute(player, "fmspeedend", 10) + val ani = getAttribute(player, "fmanim", -1) + forceMove(player, start, end, speed, speedEnd, anim = ani) + } + + define("fmanim", Privilege.ADMIN, "", "") {player, args -> + setAttribute(player, "fmanim", args[1].toIntOrNull() ?: -1) + } + + define("drawintersect", Privilege.ADMIN, "", "Visualizes the predicted intersection point with an NPC") {player, _ -> + setAttribute(player, "draw-intersect", !getAttribute(player, "draw-intersect", false)) + } + + define("expression", Privilege.ADMIN, "::expression id", "Visualizes chathead animations from ID.") { player, args -> + if(args.size != 2) + reject(player, "Usage: ::expression id") + val id = args[1].toIntOrNull() ?: 9804 + player.dialogueInterpreter.sendDialogues(player, id, "Expression ID: $id") + } + + define("timers", Privilege.ADMIN, "::timers", "Print out timers") { player, args -> + player.sendMessage("Active timers:") + for(timer in player.timers.activeTimers) { + player.sendMessage(" ${timer.identifier} ${timer.nextExecution}") + } + player.sendMessage("New timers:") + for(timer in player.timers.newTimers) { + 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!!) + } } -} \ No newline at end of file +} 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 13cbf910e..ec69cdd72 100644 --- a/Server/src/main/core/game/system/command/sets/FunCommandSet.kt +++ b/Server/src/main/core/game/system/command/sets/FunCommandSet.kt @@ -1,25 +1,30 @@ package core.game.system.command.sets -import core.api.* +import content.global.handlers.item.SpadeDigListener import content.region.misc.tutisland.handlers.iface.CharacterDesign +import core.api.* +import core.game.dialogue.DialogueFile +import core.game.node.entity.combat.ImpactHandler import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player -import core.game.system.task.Pulse -import core.game.world.map.Location -import core.game.world.map.RegionManager -import core.game.world.update.flag.context.Animation -import core.plugin.Initializable -import org.json.simple.JSONObject -import core.game.dialogue.DialogueFile -import content.global.handlers.item.SpadeDigListener import core.game.node.entity.player.info.login.PlayerSaver import core.game.system.command.Privilege +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.repository.Repository.getPlayerByName +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.RandomFunction +import org.json.simple.JSONObject +import org.rs09.consts.Sounds import java.awt.HeadlessException import java.awt.Toolkit import java.awt.datatransfer.StringSelection -import java.util.* +import kotlin.streams.toList @Initializable class FunCommandSet : CommandSet(Privilege.ADMIN) { @@ -31,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("npcareaanim", Privilege.ADMIN, "::npcareaanim Animation ID String") { player, args -> - if (args.size < 3) { - reject(player, "Syntax error: ::npcareaanim ") + 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(2 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) + } + } } } @@ -103,18 +118,29 @@ class FunCommandSet : CommandSet(Privilege.ADMIN) { /** * Go on Mr Bones' Wild Ride */ - define("mrboneswildride"){ player, _ -> - val boneMode = !player.getAttribute("boneMode",false) - player.setAttribute("boneMode", boneMode) - notify(player,"Bone Mode ${if (boneMode) "ENGAGED." else "POWERING DOWN."}") - player.appearance.rideCart(boneMode) - if (player.appearance.isRidingMinecart) { + define("mrboneswildride"){ player, args -> + val p : Player = if(args.size > 2){ + reject(player, "Usage: ::mrboneswildride ") + return@define + } else if(args.size == 1) { + player + } else if(getPlayerByName(args[1]) == null) { + reject(player, "ERROR: Username not found. Usage: ::mrboneswildride ") + return@define + } else { + getPlayerByName(args[1]) ?: return@define + } + val boneMode = !p.getAttribute("boneMode",false) + p.setAttribute("boneMode", boneMode) + notify(p,"Bone Mode ${if (boneMode) "ENGAGED." else "POWERING DOWN."}") + p.appearance.rideCart(boneMode) + if (p.appearance.isRidingMinecart) { var i = 0 GameWorld.Pulser.submit(object : Pulse(1, player) { override fun pulse(): Boolean { - if (i++ % 12 == 0) player.sendChat("I want to get off Mr. Bones Wild Ride.") - player.moveStep() - return !player.appearance.isRidingMinecart + if (i++ % 12 == 0) p.sendChat("I want to get off Mr. Bones Wild Ride.") + p.moveStep() + return !p.appearance.isRidingMinecart } }) } @@ -133,7 +159,7 @@ class FunCommandSet : CommandSet(Privilege.ADMIN) { */ define("dumpappearance", Privilege.MODERATOR){ player, _ -> val json = JSONObject() - PlayerSaver(player).saveAppearance(json); + PlayerSaver(player).saveAppearance(json) val equipJson = PlayerSaver(player).saveContainer(player.equipment) json["equipment"] = equipJson val jsonString = json.toJSONString() @@ -168,6 +194,29 @@ class FunCommandSet : CommandSet(Privilege.ADMIN) { }) } + + define("appearance", Privilege.ADMIN, "", "Allows you to change your appearance."){ player, _ -> + CharacterDesign.reopen(player) + } + + /** + * Cast a weakened version of ice barrage on nearby players within the defined radius. + * This spell will never kill or freeze a player + */ + define("barrage", Privilege.ADMIN, "::barrage radius ", "Cast a weak barrage on all nearby players. Will never kill players") { player, args -> + if (args.size != 2) + reject(player, "Usage: ::barrage radius[max = 50]") + val radius = if (args[1].toInt() > 50) 50 else args[1].toInt() + val nearbyPlayers = RegionManager.getLocalPlayers(player, radius).stream().filter { p: Player -> p.username != player.username }.toList() + animate(player, 1978) + playGlobalAudio(player.location, Sounds.ICE_CAST_171) + for (p in nearbyPlayers) { + playGlobalAudio(p.location, Sounds.ICE_BARRAGE_IMPACT_168, 20) + val impactAmount = if (p.skills.lifepoints < 10 ) 0 else RandomFunction.getRandom(3) + impact(p, impactAmount, ImpactHandler.HitsplatType.NORMAL) + p.graphics(Graphics(369, 0)) + } + } } fun bury(player: Player){ diff --git a/Server/src/main/core/game/system/command/sets/IfCommandSet.kt b/Server/src/main/core/game/system/command/sets/IfCommandSet.kt new file mode 100644 index 000000000..b088b031f --- /dev/null +++ b/Server/src/main/core/game/system/command/sets/IfCommandSet.kt @@ -0,0 +1,61 @@ +package core.game.system.command.sets + +import core.api.Commands +import core.api.getChildren +import core.cache.def.impl.ComponentType +import core.cache.def.impl.IfaceDefinition +import core.game.system.command.Privilege + +class IfCommandSet : Commands { + override fun defineCommands() { + define ("iftriggers", Privilege.ADMIN, "::iftriggers id", "Lists all known triggers for the given interface.") {player, args -> + val id = args.getOrNull(1)?.toIntOrNull() ?: -1 + if (id == -1) reject(player, "Must supply a valid interface ID!") + + val def = IfaceDefinition.forId(id) + notify(player, logToConsole = true, message = "Triggers for $def:") + + for (child in def.children) { + if (child.scripts == null) continue + if (child.triggers == null) continue + + if (child.scripts.onVarpTransmit != null) { + notify(player, logToConsole = true, message = "$child [VARP]:") + notify(player, logToConsole = true, message = " Transmit ${child.triggers.varpTriggers.joinToString(",")} triggers script ${child.scripts.onVarpTransmit.id}") + notify(player, logToConsole = true, message = " Default script args: ${child.scripts.onVarpTransmit.args.joinToString(",")}") + } + if (child.scripts.onVarcTransmit != null) { + notify(player, logToConsole = true, message = "$child [VARC]:") + notify(player, logToConsole = true, message = " Transmit ${child.triggers.varcTriggers.joinToString(",")} triggers script ${child.scripts.onVarcTransmit.id}") + notify(player, logToConsole = true, message = " Default script args: ${child.scripts.onVarcTransmit.args.joinToString(",")}") + } + } + } + + define ("listiftext", Privilege.ADMIN, "::listiftext id", "Prints all text values and their child index on an interface.") {player, args -> + val id = args.getOrNull(1)?.toIntOrNull() ?: -1 + if (id == -1) reject(player, "Must supply a valid interface ID!") + + val def = IfaceDefinition.forId(id) + notify(player, logToConsole = true, message = "Text for $def:") + + for (child in def.children) { + if (child.type != ComponentType.TEXT) continue + notify(player, logToConsole = true, message = "$child - ${child.text} - ${child.activeText}") + } + } + + define ("listifmodels", Privilege.ADMIN, "::listifmodels id", "Prints all default model values and their child index.") {player, args -> + val id = args.getOrNull(1)?.toIntOrNull() ?: -1 + if (id == -1) reject(player, "Must supply a valid interface ID!") + + val def = IfaceDefinition.forId(id) + notify(player, logToConsole = true, message = "Models for $def:") + + for (child in def.children) { + if (child.type != ComponentType.MODEL) continue + notify(player, logToConsole = true, message = "$child - ${child.modelId}/${child.activeModelId} - Anim: ${child.modelAnimId}/${child.activeModelAnimId}") + } + } + } +} \ No newline at end of file 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 5d4c5f450..28f63e1cc 100644 --- a/Server/src/main/core/game/system/command/sets/MiscCommandSet.kt +++ b/Server/src/main/core/game/system/command/sets/MiscCommandSet.kt @@ -1,43 +1,44 @@ 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.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.global.skill.farming.FarmingState -import content.minigame.fishingtrawler.TrawlerLoot -import core.tools.SystemLogger -import core.game.system.command.CommandMapping -import core.game.system.command.Privilege -import core.game.world.repository.Repository -import core.tools.colorize import java.awt.HeadlessException import java.awt.Toolkit import java.awt.datatransfer.StringSelection +import core.game.node.entity.combat.CombatStyle +import core.game.node.entity.combat.equipment.WeaponInterface +import kotlin.math.floor + @Initializable class MiscCommandSet : CommandSet(Privilege.ADMIN){ override fun defineCommands() { @@ -49,10 +50,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,52 +62,31 @@ 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}") } } - define("movcam", Privilege.ADMIN, "::movcam Region X Region Y [Height Speed]", "Moves the camera to the given region-local coordinates.") {player, args -> - val regionX = args[1].toIntOrNull() ?: return@define - val regionY = args[2].toIntOrNull() ?: return@define - var height = 300 - var speed = 100 - - if (args.size > 3) - height = args[3].toIntOrNull() ?: return@define - - if (args.size > 4) - speed = args[4].toIntOrNull() ?: return@define - - val region = RegionManager.forId(player.location.regionId) - val base = region.baseLocation - - val globalLoc = base.transform(regionX, regionY, 0) - PlayerCamera(player).panTo(globalLoc.x, globalLoc.y, height, speed) - } - - define("rotcam", Privilege.ADMIN, "::rotcam Region X Region Y [Height Speed]", "Rotates the camera to face the given region-local coordinates.") {player, args -> - val regionX = args[1].toIntOrNull() ?: return@define - val regionY = args[2].toIntOrNull() ?: return@define - var height = 300 - var speed = 100 - - if (args.size > 3) - height = args[3].toIntOrNull() ?: return@define - - if (args.size > 4) - speed = args[4].toIntOrNull() ?: return@define - - val region = RegionManager.forId(player.location.regionId) - val base = region.baseLocation - - val globalLoc = base.transform(regionX, regionY, 0) - PlayerCamera(player).rotateTo(globalLoc.x, globalLoc.y, height, speed) - } - define("anmacs", Privilege.ADMIN) { player, _ -> 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 */ @@ -118,9 +99,9 @@ class MiscCommandSet : CommandSet(Privilege.ADMIN){ notify(player, "Jagex: ${l.z}_${l.regionId shr 8}_${l.regionId and 0xFF}_${l.localX}_${l.localY}") notify(player,"Object: " + RegionManager.getObject(l).also{obj = it} + ".") notify(player,"Object Varp: " + obj?.definition?.configFile?.varpId + " offset: " + obj?.definition?.configFile?.startBit + " size: " + (obj?.definition?.configFile?.startBit?.minus(obj?.definition?.configFile?.startBit!!))) - SystemLogger.logInfo(this::class.java, "Viewport: " + l.getSceneX(player.playerFlags.lastSceneGraph) + "," + l.getSceneY(player.playerFlags.lastSceneGraph)) + log(this::class.java, Log.FINE, "Viewport: " + l.getSceneX(player.playerFlags.lastSceneGraph) + "," + l.getSceneY(player.playerFlags.lastSceneGraph)) val loc = "Location.create(" + l.x + ", " + l.y + ", " + l.z + ")" - SystemLogger.logInfo(this::class.java, loc + "; " + player.playerFlags.lastSceneGraph + ", " + l.localX + ", " + l.localY) + log(this::class.java, Log.FINE, loc + "; " + player.playerFlags.lastSceneGraph + ", " + l.localX + ", " + l.localY) try { val stringSelection = StringSelection(loc) val clpbrd = Toolkit.getDefaultToolkit().systemClipboard @@ -148,7 +129,14 @@ 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()})") + + // Snowscape: calculate the min damage on hit for melee attacks (see MeleeSwingHandler.swing() for the custom minimum damage). + if (swingHandler.type == CombatStyle.MELEE) { + val speedMod = (player.getProperties().getAttackSpeed() - 4) + val min = floor(speedMod * 0.1 * hit).toInt() + notify(player, "min hit: ${min} (on successful hits)") + } } /** @@ -213,7 +201,9 @@ class MiscCommandSet : CommandSet(Privilege.ADMIN){ * Opens the credit/voting shop */ define("shop", Privilege.STANDARD, "", "Opens the credit shop."){ player, _ -> - player.interfaceManager.open(Component(Components.CREDIT_SHOP)) + if (player.locks.isInteractionLocked || player.locks.isMovementLocked) { + sendMessage(player, "You can't open the shop right now.") + } else player.interfaceManager.open(Component(Components.CREDIT_SHOP)) } /** @@ -228,13 +218,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") } } @@ -258,6 +244,10 @@ class MiscCommandSet : CommandSet(Privilege.ADMIN){ reject(player, "Usage: ::commands page") } + if (page > pages.size) { + 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) } @@ -327,56 +317,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", "Sets SKILL NAME to LEVEL."){player,args -> - if(args.size < 2) 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!") - - player.skills.setStaticLevel(skill,desiredLevel) - player.skills.setLevel(skill,desiredLevel) - player.skills.updateCombatLevel() - } - define("completediaries", Privilege.ADMIN, "", "Completes all diaries."){player,_ -> player.achievementDiaryManager.diarys.forEach { for(level in it.taskCompleted.indices){ @@ -387,6 +327,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() @@ -421,7 +377,7 @@ class MiscCommandSet : CommandSet(Privilege.ADMIN){ } else { args[1].toString().toInt() } - player.bank.add(*TrawlerLoot.getLoot(player.skills.getLevel(Skills.FISHING), rolls, false).toTypedArray()) + TrawlerLoot.addLootAndMessage(player, player.skills.getLevel(Skills.FISHING), rolls, false) } define("fillbank", Privilege.ADMIN, "", "Right as it says on the tin."){player,_ -> @@ -441,7 +397,7 @@ class MiscCommandSet : CommandSet(Privilege.ADMIN){ } val configID = args[1].toString().toInt() val configValue = args[2].toString().toInt() - player.configManager.forceSet(configID,configValue,false) + setVarp(player, configID, configValue) } define("getobjectvarp"){player,args -> @@ -459,77 +415,7 @@ class MiscCommandSet : CommandSet(Privilege.ADMIN){ val varbitID = args[1].toInt() notify(player, "${varbitID}: ${VarbitDefinition.forId(varbitID)}") } - define("togglexp", Privilege.STANDARD){ player, args -> - val enabled = player.varpManager.get(2501).getVarbit(0) == 1 - player.varpManager.get(2501).setVarbit(0,if(enabled) 0 else 1).send(player) - notify(player, "XP drops are now " + colorize("%R" + if(!enabled) "ON." else "OFF.")) - player.varpManager.flagSave(2501) - } - define("xpconfig", Privilege.STANDARD){ player, args -> - if(args.size < 3){ - reject(player,"Usage: ::xpconfig track|mode type", - "Track types: total|recent", - "Mode types: instant|increment", - "Defaults: track - total, mode - increment") - } - - when(args[1]){ - "track" -> when(args[2]){ - "total" -> { - player.varpManager.get(2501).setVarbit(2,0).send(player) - notify(player,"You are now tracking " + colorize("%RTOTAL") + " experience.") - } - "recent" -> { - player.varpManager.get(2501).setVarbit(2,1).send(player) - notify(player,"You are now tracking the " + colorize("%RMOST RECENT") + " skill's experience.") - } - else -> { - reject(player,"Usage: ::xpconfig track|mode type") - reject(player,"Track types: total|recent") - reject(player, "Mode types: instant|increment") - reject(player,"Defaults: track - total, mode - increment") - } - } - - "mode" -> { - when(args[2]){ - "instant" -> { - player.varpManager.get(2501).setVarbit(1,1).send(player) - notify(player,"Your xp tracker now updates " + colorize("%RINSTANTLY") + ".") - } - "increment" -> { - player.varpManager.get(2501).setVarbit(1,0).send(player) - notify(player,"Your xp tracker now updates " + colorize("%RINCREMENTALLY") + ".") - } - else -> { - reject(player,"Usage: ::xpconfig track|mode type") - reject(player,"Track types: total|recent") - reject(player, "Mode types: instant|increment") - reject(player,"Defaults: track - total, mode - increment") - } - } - } - else -> { - reject(player,"Usage: ::xpconfig track|mode type") - reject(player,"Track types: total|recent") - reject(player, "Mode types: instant|increment") - reject(player,"Defaults: track - total, mode - increment") - } - } - player.varpManager.flagSave(2501) - } - - define("toggleslayer", Privilege.STANDARD){ player, _ -> - val disabled = player.varpManager.get(2502).getVarbit(0) == 1 - player.varpManager.get(2502).setVarbit(0,if(disabled) 0 else 1).send(player) - if(!disabled){ - player.varpManager.flagSave(2502) - } else { - player.varpManager.get(2502).save = false - } - notify(player,"Slayer task tracker is now " + (if(disabled) colorize("%RON") else colorize("%ROFF")) + ".") - } define("setvarbit", Privilege.ADMIN, "::setvarbit VARBIT ID VALUE", ""){ player,args -> @@ -543,7 +429,29 @@ class MiscCommandSet : CommandSet(Privilege.ADMIN){ reject(player,"Usage ::setvarbit index offset value") } - player.varpManager.setVarbit(index!!, value!!) + 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) + reject(player, "Usage: ::getvarbit id") + val index = args[1].toIntOrNull() ?: return@define + notify(player, "Varbit $index: Currently ${getVarbit(player, index)}") } define("setvarp", Privilege.ADMIN, "::setvarp VARP ID BIT OFFSET VALUE", "Sets the value starting at the BIT OFFSET of the varp."){ @@ -559,7 +467,7 @@ class MiscCommandSet : CommandSet(Privilege.ADMIN){ reject(player,"Usage ::setvarp index offset value") } - player.varpManager.get(index!!).setVarbit(offset!!, value!!).send(player) + setVarp(player, index!!, value!! shl offset!!) } define("setvarc", Privilege.ADMIN, "::setvarc VARC ID VALUE") { player, args -> @@ -577,44 +485,40 @@ class MiscCommandSet : CommandSet(Privilege.ADMIN){ } define("grow", Privilege.ADMIN, "", "Grows all planted crops by 1 stage."){ player, _ -> - val state: FarmingState = player.states.get("farming") as FarmingState? ?: return@define + val state = getOrStartTimer (player)!! for(patch in state.getPatches()){ - patch.nextGrowth = System.currentTimeMillis() + patch.nextGrowth = System.currentTimeMillis() - 1 } + + state.run (player) } define("finishbins", Privilege.ADMIN, "", "Finishes any in-progress compost bins."){ player, _ -> - val state: FarmingState = player.states.get("farming") as FarmingState? ?: return@define - - for(bin in state.getBins()){ - bin.finishedTime = System.currentTimeMillis() + val bins = getOrStartTimer(player).getBins() + for (bin in bins) { + if (!bin.isFinished && bin.isClosed) bin.finish() } } - define("testlady", Privilege.ADMIN){ player, _ -> - content.global.ame.RandomEventManager.getInstance(player)!!.event = RandomEvents.RIVER_TROLL.npc.create(player) - content.global.ame.RandomEventManager.getInstance(player)!!.event!!.init() + 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("revent", Privilege.ADMIN){ player, _ -> - println(player.pulseManager.current) - content.global.ame.RandomEventManager.getInstance(player)!!.fireEvent() + 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, _ -> player.details.credits += 100 } - define("resetgwdropes", Privilege.STANDARD, "", "OLD: Was used for fixing broken data."){ player, _ -> - player.varpManager.get(1048).clearBitRange(0,31) - player.varpManager.get(1048).send(player) - } - - define("resetmistag", Privilege.STANDARD, "", "OLD: Was used for fixing broken data."){ player, _ -> - player.removeAttribute("mistag-greeted") - } - define("getnpcparent"){player,args -> if(args.size < 2){ reject(player,"Usage: ::getnpcparent npcid") @@ -647,21 +551,22 @@ class MiscCommandSet : CommandSet(Privilege.ADMIN){ else -> reject(player, usageStr) } } - define("allow_aggro", Privilege.ADMIN) { player, args -> - val usageStr = "Usage: ::allow_aggro true | false" + define("allowaggro", Privilege.ADMIN, "allowaggro true | false", "Toggle NPCs aggroing on you") { player, args -> + val usageStr = "Usage: ::allowaggro true | false" if(args.size < 2) { - reject(player, usageStr) + notify(player, "Allow admin aggression is currently ${player.getAttribute("/save:allow_admin_aggression", false)}") + return@define } when(args[1]) { - "true" -> player.setAttribute("allow_admin_aggression", true) - "false" -> player.removeAttribute("allow_admin_aggression") + "true" -> player.setAttribute("/save:allow_admin_aggression", true) + "false" -> player.setAttribute("/save:allow_admin_aggression", false) else -> reject(player, usageStr) - } + notify(player, "Setting aggro ${args[1]}") } define("rules", Privilege.STANDARD, "", "Shows the rules."){ player, _ -> - RulesAndInfo.setBaseRulesAndInfo(player); + RulesAndInfo.setBaseRulesAndInfo(player) player.packetDispatch.sendInterfaceConfig(384, 17, true) openInterface(player, 384) } @@ -679,12 +584,127 @@ class MiscCommandSet : CommandSet(Privilege.ADMIN){ sendDialogue(player, "Thank you!") player.unlock() player.removeAttribute("rules:pin") + if(ServerConstants.NEW_PLAYER_ANNOUNCEMENT) sendNews("A new player has joined. Welcome ${player.username}!") } else { 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]}") + + } + + //Snowscape custom commands + define("dps", Privilege.STANDARD, "::dps NPC ID", "Calculates current DPS against the given NPC target."){ player, args -> + // If NPC ID is specified, save as attribute. If unspecified, use last saved ID (defaults to Chicken) + if (args.size > 1) { + setAttribute(player, "snowscape:dpscalc", args[1].toInt()) + } + val npcId: Int = getAttribute(player, "snowscape:dpscalc", 41) + val npc = NPC(npcId) + npc.initConfig() + + val handler = player.getSwingHandler(false) + var attackSpeed = player.getProperties().getAttackSpeed() + var maxHit = handler.calculateHit(player, npc, 1.0) + var minHit = 0 + if (handler.type == CombatStyle.MELEE) { + minHit = floor((attackSpeed - 4) * 0.1 * maxHit).toInt() + } else if (handler.type == CombatStyle.RANGE) { + if (player.getProperties().getAttackStyle().getStyle() == WeaponInterface.STYLE_RAPID) { + attackSpeed-- + } + } else if (handler.type == CombatStyle.MAGIC) { + attackSpeed = 5 + val spell = player.getProperties().getAutocastSpell() + maxHit = if (spell != null) spell.getMaximumImpact(player, npc, null) else 0 + } + val avgHit = ((maxHit + 1) * maxHit) / 2 / (maxHit - minHit + 1) + + val accuracy = handler.calculateAccuracy(player) + val defence = handler.calculateDefence(npc, player) + + val hitChance: Double = if (accuracy > defence) { + 1.0 - ((defence + 2.0) / (2.0 * (accuracy + 1.0))) + } else { + accuracy / (2.0 * (defence + 1.0)) + } + + val dps = (1/(attackSpeed * 0.6)) * hitChance * avgHit + + + val color = "00fffff" + player.sendMessage("------ DPS vs ${npc.name} ------") + player.sendMessage("Attack Style: ${handler.type}") + player.sendMessage("Attack Speed: ${attackSpeed}") + player.sendMessage("Damage: ${minHit} - ${maxHit}") + player.sendMessage("Chance to hit: ${floor(hitChance*100).toInt()}%") + player.sendMessage("Average DPS: ${floor(dps*100)/100}") + } + } + + 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) @@ -700,17 +720,14 @@ class MiscCommandSet : CommandSet(Privilege.ADMIN){ offerPrice[offer.itemID] = offer.offeredValue } - val entries = offerAmounts.entries.sortedBy({ e -> getItemName(e.key) }) - var lineId = 11 - 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) @@ -726,17 +743,14 @@ class MiscCommandSet : CommandSet(Privilege.ADMIN){ offerPrice[offer.itemID] = offer.offeredValue } - val entries = offerAmounts.entries.sortedBy({ e -> getItemName(e.key) }) - var lineId = 11 - 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){ @@ -758,17 +772,14 @@ class MiscCommandSet : CommandSet(Privilege.ADMIN){ lowestPrice[offer.itemID] = price } - val entries = offerAmounts.entries.sortedBy({ e -> getItemName(e.key) }) - var lineId = 11 - 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){ @@ -790,26 +801,27 @@ class MiscCommandSet : CommandSet(Privilege.ADMIN){ highestPrice[offer.itemID] = price } - val entries = offerAmounts.entries.sortedBy({ e -> getItemName(e.key) }) - var lineId = 11 - 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) @@ -836,38 +848,64 @@ class MiscCommandSet : CommandSet(Privilege.ADMIN){ } } - 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/ModelViewerCommandSet.kt b/Server/src/main/core/game/system/command/sets/ModelViewerCommandSet.kt new file mode 100644 index 000000000..0d9074356 --- /dev/null +++ b/Server/src/main/core/game/system/command/sets/ModelViewerCommandSet.kt @@ -0,0 +1,99 @@ +package core.game.system.command.sets + +import content.global.handlers.iface.BookInterface +import core.api.* +import core.game.node.entity.player.Player +import core.game.system.command.Privilege +import core.plugin.Initializable + +/** To view models in game. */ +@Initializable +class ModelViewerCommandSet : CommandSet(Privilege.ADMIN) { + companion object { + + const val DEF_BOOK = 10216 + const val TITLE = "Model Viewer" + const val ATTRIBUTE_MODEL_NUMBER = "modelNumber" + const val ATTRIBUTE_ZOOM = "modelZoom" + const val ATTRIBUTE_PITCH = "modelPitch" + const val ATTRIBUTE_YAW = "modelYaw" + + private fun display(player: Player, pageNum: Int, buttonID: Int) : Boolean { + BookInterface.clearBookLines(player, BookInterface.FANCY_BOOK_2_27, BookInterface.FANCY_BOOK_2_27_LINE_IDS) + BookInterface.clearButtons(player, BookInterface.FANCY_BOOK_2_27, BookInterface.FANCY_BOOK_2_27_BUTTON_IDS) + BookInterface.setTitle(player, BookInterface.FANCY_BOOK_2_27, BookInterface.FANCY_BOOK_2_27_LINE_IDS, TITLE) + + // Custom button interfaces for model book + // These are non-standard. No pages are "turned" here. + player.packetDispatch.sendInterfaceConfig(BookInterface.FANCY_BOOK_2_27, 114, false) + player.packetDispatch.sendInterfaceConfig(BookInterface.FANCY_BOOK_2_27, 116, false) + player.packetDispatch.sendInterfaceConfig(BookInterface.FANCY_BOOK_2_27, 118, false) + player.packetDispatch.sendInterfaceConfig(BookInterface.FANCY_BOOK_2_27, 122, false) + player.packetDispatch.sendInterfaceConfig(BookInterface.FANCY_BOOK_2_27, 124, false) + player.packetDispatch.sendInterfaceConfig(BookInterface.FANCY_BOOK_2_27, 126, false) + player.packetDispatch.sendInterfaceConfig(BookInterface.FANCY_BOOK_2_27, 128, false) + player.packetDispatch.sendInterfaceConfig(BookInterface.FANCY_BOOK_2_27, 144, false) + player.packetDispatch.sendInterfaceConfig(BookInterface.FANCY_BOOK_2_27, 146, false) + player.packetDispatch.sendInterfaceConfig(BookInterface.FANCY_BOOK_2_27, 148, false) + player.packetDispatch.sendInterfaceConfig(BookInterface.FANCY_BOOK_2_27, 152, false) + player.packetDispatch.sendInterfaceConfig(BookInterface.FANCY_BOOK_2_27, 154, false) + player.packetDispatch.sendInterfaceConfig(BookInterface.FANCY_BOOK_2_27, 156, false) + player.packetDispatch.sendInterfaceConfig(BookInterface.FANCY_BOOK_2_27, 158, false) + player.packetDispatch.sendString("-1 zoom" ,BookInterface.FANCY_BOOK_2_27, 114) + player.packetDispatch.sendString("-1 pitch" ,BookInterface.FANCY_BOOK_2_27, 116) + player.packetDispatch.sendString("-1 yaw" ,BookInterface.FANCY_BOOK_2_27, 118) + player.packetDispatch.sendString("-1" ,BookInterface.FANCY_BOOK_2_27, 122) + player.packetDispatch.sendString("-10" ,BookInterface.FANCY_BOOK_2_27, 124) + player.packetDispatch.sendString("-100" ,BookInterface.FANCY_BOOK_2_27, 126) + player.packetDispatch.sendString("-1000" ,BookInterface.FANCY_BOOK_2_27, 128) + player.packetDispatch.sendString("+1 zoom" ,BookInterface.FANCY_BOOK_2_27, 144) + player.packetDispatch.sendString("+1 pitch" ,BookInterface.FANCY_BOOK_2_27, 146) + player.packetDispatch.sendString("+1 yaw" ,BookInterface.FANCY_BOOK_2_27, 148) + player.packetDispatch.sendString("+1" ,BookInterface.FANCY_BOOK_2_27, 152) + player.packetDispatch.sendString("+10" ,BookInterface.FANCY_BOOK_2_27, 154) + player.packetDispatch.sendString("+100" ,BookInterface.FANCY_BOOK_2_27, 156) + player.packetDispatch.sendString("+1000" ,BookInterface.FANCY_BOOK_2_27, 158) + + // Attach buttons to setAttributes + when (buttonID) { + 114 -> setAttribute(player, ATTRIBUTE_ZOOM, getAttribute(player, ATTRIBUTE_ZOOM, 700) - 100) + 116 -> setAttribute(player, ATTRIBUTE_PITCH, getAttribute(player, ATTRIBUTE_PITCH, 0) - 100) + 118 -> setAttribute(player, ATTRIBUTE_YAW, getAttribute(player, ATTRIBUTE_YAW, 0) - 100) + 122 -> setAttribute(player, ATTRIBUTE_MODEL_NUMBER, getAttribute(player, ATTRIBUTE_MODEL_NUMBER, DEF_BOOK) - 1) + 124 -> setAttribute(player, ATTRIBUTE_MODEL_NUMBER, getAttribute(player, ATTRIBUTE_MODEL_NUMBER, DEF_BOOK) - 10) + 126 -> setAttribute(player, ATTRIBUTE_MODEL_NUMBER, getAttribute(player, ATTRIBUTE_MODEL_NUMBER, DEF_BOOK) - 100) + 128 -> setAttribute(player, ATTRIBUTE_MODEL_NUMBER, getAttribute(player, ATTRIBUTE_MODEL_NUMBER, DEF_BOOK) - 1000) + 144 -> setAttribute(player, ATTRIBUTE_ZOOM, getAttribute(player, ATTRIBUTE_ZOOM, 700) + 100) + 146 -> setAttribute(player, ATTRIBUTE_PITCH, getAttribute(player, ATTRIBUTE_PITCH, 0) + 100) + 148 -> setAttribute(player, ATTRIBUTE_YAW, getAttribute(player, ATTRIBUTE_YAW, 0) + 100) + 152 -> setAttribute(player, ATTRIBUTE_MODEL_NUMBER, getAttribute(player, ATTRIBUTE_MODEL_NUMBER, DEF_BOOK) + 1) + 154 -> setAttribute(player, ATTRIBUTE_MODEL_NUMBER, getAttribute(player, ATTRIBUTE_MODEL_NUMBER, DEF_BOOK) + 10) + 156 -> setAttribute(player, ATTRIBUTE_MODEL_NUMBER, getAttribute(player, ATTRIBUTE_MODEL_NUMBER, DEF_BOOK) + 100) + 158 -> setAttribute(player, ATTRIBUTE_MODEL_NUMBER, getAttribute(player, ATTRIBUTE_MODEL_NUMBER, DEF_BOOK) + 1000) + } + + // Display model number + player.packetDispatch.sendString("No: " + getAttribute(player, ATTRIBUTE_MODEL_NUMBER, DEF_BOOK) + " " + getAttribute(player, ATTRIBUTE_ZOOM, 700) + " " + getAttribute(player, ATTRIBUTE_PITCH, 0) + " " + getAttribute(player, ATTRIBUTE_YAW, 0),BookInterface.FANCY_BOOK_2_27, 38) + player.packetDispatch.sendString("No: " + (getAttribute(player, ATTRIBUTE_MODEL_NUMBER, DEF_BOOK) + 1),BookInterface.FANCY_BOOK_2_27, 53) + + // Display the models in the middle + BookInterface.setModelOnPage(player,0, getAttribute(player, ATTRIBUTE_MODEL_NUMBER, DEF_BOOK), BookInterface.FANCY_BOOK_2_27, BookInterface.FANCY_BOOK_2_27_IMAGE_ENABLE_DRAW_IDS[7], BookInterface.FANCY_BOOK_2_27_IMAGE_DRAW_IDS[7], getAttribute(player, ATTRIBUTE_ZOOM, 700), getAttribute(player, ATTRIBUTE_PITCH, 0), getAttribute(player, ATTRIBUTE_YAW, 0)) + BookInterface.setModelOnPage(player,0, getAttribute(player, ATTRIBUTE_MODEL_NUMBER, DEF_BOOK) + 1, BookInterface.FANCY_BOOK_2_27, BookInterface.FANCY_BOOK_2_27_IMAGE_ENABLE_DRAW_IDS[22], BookInterface.FANCY_BOOK_2_27_IMAGE_DRAW_IDS[22], getAttribute(player, ATTRIBUTE_ZOOM, 700), getAttribute(player, ATTRIBUTE_PITCH, 0), getAttribute(player, ATTRIBUTE_YAW, 0)) + + return true + } + } + override fun defineCommands() { + define("models"){ player, args -> + + // Bad number of args + if(args.size > 2){ + reject(player,"Usage: ::models") + return@define + } + + BookInterface.openBook(player, BookInterface.FANCY_BOOK_2_27, ::display) + return@define + } + } +} \ No newline at end of file diff --git a/Server/src/main/core/game/system/command/sets/ModerationCommandSet.kt b/Server/src/main/core/game/system/command/sets/ModerationCommandSet.kt index 459f22220..13d0d9e79 100644 --- a/Server/src/main/core/game/system/command/sets/ModerationCommandSet.kt +++ b/Server/src/main/core/game/system/command/sets/ModerationCommandSet.kt @@ -40,7 +40,7 @@ class ModerationCommandSet : CommandSet(Privilege.MODERATOR){ define("kick", Privilege.MODERATOR){ player, args -> val playerToKick: Player? = Repository.getPlayerByName(args[1]) if (playerToKick != null) { - playerToKick.clear(true) + playerToKick.clear() notify(player, "Player ${playerToKick.username} was kicked.") } else { reject(player, "ERROR REMOVING PLAYER.") @@ -81,7 +81,7 @@ class ModerationCommandSet : CommandSet(Privilege.MODERATOR){ } playerToKick?.details?.accountInfo?.banEndTime = System.currentTimeMillis() + durationMillis - playerToKick?.clear(true) + playerToKick?.clear() GameWorld.Pulser.submit(object : Pulse(2) { override fun pulse(): Boolean { val info = GameWorld.accountStorage.getAccountInfo(name) @@ -131,7 +131,7 @@ class ModerationCommandSet : CommandSet(Privilege.MODERATOR){ for (p in playersToBan) { val playerToKick = Repository.getPlayerByName(p) playerToKick?.details?.accountInfo?.banEndTime = System.currentTimeMillis() + durationMillis - playerToKick?.clear(true) + playerToKick?.clear() GameWorld.Pulser.submit(object : Pulse(2) { override fun pulse(): Boolean { val info = GameWorld.accountStorage.getAccountInfo(p) @@ -274,7 +274,7 @@ class ModerationCommandSet : CommandSet(Privilege.MODERATOR){ if (p == null) GameWorld.accountStorage.update(info) else - sendMessage(p, "You have been ${if (amount > 0) "granted" else "penalized"} ${abs(amount)} credits.") + sendMessage(p, "You have been ${if (amount > 0) "granted" else "penalized"} ${abs(amount)} credit(s).") notify(player, "Updated $username's credits to ${info.credits} by ${if (amount > 0) "adding" else "removing"} ${abs(amount)}.") } diff --git a/Server/src/main/core/game/system/command/sets/MusicCommandSet.kt b/Server/src/main/core/game/system/command/sets/MusicCommandSet.kt deleted file mode 100644 index 09cfc6c65..000000000 --- a/Server/src/main/core/game/system/command/sets/MusicCommandSet.kt +++ /dev/null @@ -1,53 +0,0 @@ -package core.game.system.command.sets - -import core.game.node.entity.player.link.music.MusicEntry -import core.net.packet.PacketRepository -import core.net.packet.context.MusicContext -import core.net.packet.out.MusicPacket -import core.plugin.Initializable -import core.game.system.command.Command -import core.game.system.command.Privilege - -@Initializable -class MusicCommandSet : CommandSet(Privilege.STANDARD){ - override fun defineCommands() { - - /** - * Command that lets you play a specific song - */ - define("playsong"){player,args -> - if(args.size < 2){ - reject(player,"Usage: ::playsong songID") - } - val id = args[1].toIntOrNull() - if(id == null){ - reject(player,"Please use a valid integer for the song id.") - } - player.musicPlayer.play(MusicEntry.forId(id!!)) - notify(player,"Now playing song $id") - } - - /** - * Command that lets you play a song via the ID instead of the song name - * this is mostly useful for custom tracks that aren't in the ingame - * music player yet. - */ - define("playid"){player,arg -> - val id = arg[1].toIntOrNull() - if(id != null){ - PacketRepository.send(MusicPacket::class.java, MusicContext(player, id)) - notify(player,"Now playing song $id") - } - } - - /** - * Command that unlocks all music tracks for the player - * Restricted to ADMIN access only. - */ - define("allmusic", Privilege.ADMIN){ player, _ -> - for (me in MusicEntry.getSongs().values) { - player.musicPlayer.unlock(me.id) - } - } - } -} \ No newline at end of file diff --git a/Server/src/main/core/game/system/command/sets/PlayerAttributeCommandSet.kt b/Server/src/main/core/game/system/command/sets/PlayerAttributeCommandSet.kt new file mode 100644 index 000000000..fa25eb4b5 --- /dev/null +++ b/Server/src/main/core/game/system/command/sets/PlayerAttributeCommandSet.kt @@ -0,0 +1,99 @@ +package core.game.system.command.sets + +import core.api.getAttribute +import core.api.setAttribute +import core.game.node.entity.player.Player +import core.game.system.command.Privilege +import core.game.world.repository.Repository +import core.plugin.Initializable + +/** + * In game setting of simple attributes. Not for complex objects or enums. + * @author ovenbread + */ +@Initializable +class PlayerAttributeCommandSet : CommandSet(Privilege.ADMIN) { + override fun defineCommands() { + + /** + * Gets the value of an attribute on a player. + */ + define("getattribute") { player, args -> + if (args.size < 2) { + reject(player, "Usage ::getattribute [playername] attributename") + } + var attributeName = "" + + // If stats is called without a username, return self. + var queryPlayer: Player? = null + if(args.size == 2){ + queryPlayer = player + attributeName = args[1] + } + // If stats is called with a username, find the player or set to null. + if(args.size == 3) { + queryPlayer = Repository.getPlayerByName(args[1]) + attributeName = args[2] + } + // If player is not found, return error. + if(queryPlayer == null){ + reject(player,"Invalid player or player not online.") + return@define + } + + notify(player,"Attribute: " + attributeName + " Value: " + getAttribute(queryPlayer!!, attributeName, "").toString()) + return@define + } + + /** + * Sets the value of an attribute on a player. + */ + define("setattribute") { player, args -> + if (args.size < 2) { + reject(player, "Usage ::setattribute [playername] attributename value") + } + var attributeName = "" + var value = "" + + // If stats is called without a username, return self. + var queryPlayer: Player? = null + if(args.size == 3){ + queryPlayer = player + attributeName = args[1] + value = args[2] + } + // If stats is called with a username, find the player or set to null. + if(args.size == 4) { + queryPlayer = Repository.getPlayerByName(args[1]) + attributeName = args[2] + value = args[3] + } + // If player is not found, return error. + if(queryPlayer == null){ + reject(player,"Invalid player or player not online.") + return@define + } + // Please be careful when editing attributes. + // If string input is an integer, convert to an integer and set it to the attribute. + if (value.toIntOrNull() != null){ + setAttribute(queryPlayer!!, attributeName, value.toIntOrNull()) + return@define + } + // If string input is a float(decimal number), convert to a float and set it to the attribute. + if (value.toFloatOrNull() != null){ + setAttribute(queryPlayer!!, attributeName, value.toFloatOrNull()) + return@define + } + // If string matches the text "true", set the attribute value to the boolean true. + if (value == "true"){ + setAttribute(queryPlayer!!, attributeName, true) + return@define + } + // If string matches the text "false", set the attribute value to the boolean false. + if (value == "false"){ + setAttribute(queryPlayer!!, attributeName, false) + return@define + } + } + } +} 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 43fa0077e..69ae5c1c5 100644 --- a/Server/src/main/core/game/system/command/sets/QuestCommandSet.kt +++ b/Server/src/main/core/game/system/command/sets/QuestCommandSet.kt @@ -3,9 +3,9 @@ package core.game.system.command.sets import core.game.component.Component import core.game.node.entity.player.Player import core.game.node.entity.player.link.quest.QuestRepository -import core.game.system.command.Command import core.plugin.Initializable import core.game.system.command.Privilege +import core.game.world.repository.Repository @Initializable class QuestCommandSet : CommandSet(Privilege.ADMIN){ @@ -22,21 +22,43 @@ class QuestCommandSet : CommandSet(Privilege.ADMIN){ /** * Displays the currently implemented quests with debug information */ - define("quest"){player,_ -> - sendQuestsDebug(player) + define("quest"){player,args -> + if (args.size < 3) { + val lookupP = if (args.size == 1) { + player + } else if (Repository.getPlayerByName(args[1]) != null) { + Repository.getPlayerByName(args[1]) ?: return@define + } else { + reject(player, "ERROR: Username not found. Usage: ::quest ") + return@define + } + sendQuestsDebug(player, lookupP) + } else { + reject(player, "Usage: ::quest || ::quest ") + } } /** * 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") - player.questRepository.setStageNonmonotonic(player.questRepository.forIndex(quest as Int), stage as Int) - notify(player, "Setting " + player.questRepository.forIndex(quest).name + " to stage $stage") + val questObject = player.questRepository.forIndex(quest as Int) + 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") + } } /** @@ -60,26 +82,32 @@ 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++) } } /** * Sends the list of quests with debug information - * @param player the player. + * @param admin the player. */ - private fun sendQuestsDebug(player: Player?) { - player!!.interfaceManager.open(Component(275)) + private fun sendQuestsDebug(admin: Player?, lookupUser: Player?) { + admin!!.interfaceManager.open(Component(275)) for (i in 0..310) { - player.packetDispatch.sendString("", 275, i) + admin.packetDispatch.sendString("", 275, i) } var lineId = 11 - player.packetDispatch.sendString("" + "Quests Debug" + "", 275, 2) + admin.packetDispatch.sendString("${lookupUser!!.username}'s Quest Debug", 275, 2) for (q in QuestRepository.getQuests().values) { // Add a space to beginning and end of string for the strikethrough - player.packetDispatch.sendString("${q.name}", 275, lineId++) - player.packetDispatch.sendString("Index: ${q.index} | Stage: ${player.questRepository.getStage(q)}", 275, lineId++) - player.packetDispatch.sendString(" ", 275, lineId++) + val stage = lookupUser.questRepository.getStage(q) + val statusColor = when { + stage >= 100 -> "80ff00" + stage in 1..99 -> "ff8400" + else -> "ff0000" + } + 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 117e06cd0..9be1e0626 100644 --- a/Server/src/main/core/game/system/command/sets/SlayerCommandSet.kt +++ b/Server/src/main/core/game/system/command/sets/SlayerCommandSet.kt @@ -1,9 +1,13 @@ package core.game.system.command.sets +import content.global.skill.slayer.Master import content.global.skill.slayer.SlayerManager +import content.global.skill.slayer.SlayerUtils +import content.global.skill.slayer.Tasks import core.game.node.entity.npc.NPC -import core.plugin.Initializable import core.game.system.command.Privilege +import core.plugin.Initializable +import core.api.* @Initializable class SlayerCommandSet : CommandSet(Privilege.ADMIN){ @@ -35,5 +39,20 @@ class SlayerCommandSet : CommandSet(Privilege.ADMIN){ SlayerManager.getInstance(player).slayerPoints = amount!! notify(player, "Set slayer points to $amount.") } + + define("setslayertask", Privilege.ADMIN, "::setslayertask npc id [amount]", "Set the slayer task to npc. Amount optional.") { player, args -> + if (args.size < 2) reject(player, "Usage: ::setslayertask npc id [amount]") + + 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, masterTask, Master.values().random()) + if (amount != null) slayer.amount = amount + setVarp(player, 2502, slayer.flags.taskFlags shr 4) + } } -} \ No newline at end of file +} 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 ea8886494..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,14 +1,16 @@ 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 +import core.tools.Log import java.awt.Toolkit import java.awt.datatransfer.StringSelection @@ -20,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) } /** @@ -68,7 +91,7 @@ class SpawnCommandSet : CommandSet(Privilege.ADMIN){ } val `object` = if (args.size > 3) Scenery(CommandPlugin.toInteger(args[1]!!), player!!.location, CommandPlugin.toInteger(args[2]!!), CommandPlugin.toInteger(args[3]!!)) else if (args.size == 3) Scenery(CommandPlugin.toInteger(args[1]!!), player!!.location, CommandPlugin.toInteger(args[2]!!)) else Scenery(CommandPlugin.toInteger(args[1]!!), player!!.location) SceneryBuilder.add(`object`) - SystemLogger.logInfo(this::class.java, "object = $`object`") + log(this::class.java, Log.FINE, "object = $`object`") } define("objectgrid") { player, args -> 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 1189dbcfb..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,10 @@ 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" const val FISHING_TRAWLER_GAMES_WON = "fishing-trawler-games" const val FISHING_TRAWLER_LEAKS_PATCHED = "fishing-trawler-leaks-patched" 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 42f03a37e..b9b6e320c 100644 --- a/Server/src/main/core/game/system/command/sets/StatsCommandSet.kt +++ b/Server/src/main/core/game/system/command/sets/StatsCommandSet.kt @@ -1,17 +1,20 @@ package core.game.system.command.sets -import core.game.component.Component import content.data.BossKillCounter import content.global.activity.ttrail.TreasureTrailManager +import content.global.handlers.iface.BookInterface +import content.global.handlers.iface.Page +import content.global.handlers.iface.PageSet import content.global.skill.slayer.SlayerManager +import core.api.setAttribute 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.game.interaction.InterfaceListener +import org.rs09.consts.Items +import org.rs09.consts.NPCs +import core.api.utils.PlayerStatsCounter import core.game.system.command.Privilege import core.game.world.repository.Repository +import java.util.* val TUROTH_IDS = intArrayOf(NPCs.TUROTH_1622, NPCs.TUROTH_1623, NPCs.TUROTH_1626, NPCs.TUROTH_1627, NPCs.TUROTH_1628, NPCs.TUROTH_1629, NPCs.TUROTH_1630) val KURASK_IDS = intArrayOf(NPCs.KURASK_1608, NPCs.KURASK_1609, NPCs.KURASK_4229, NPCs.KURASK_7811) @@ -27,193 +30,169 @@ val STEEL_DRAGON_IDS = intArrayOf(NPCs.STEEL_DRAGON_1592, NPCs.STEEL_DRAGON_3590 val MITHRIL_DRAGON_IDS = intArrayOf(NPCs.MITHRIL_DRAGON_5363, NPCs.MITHRIL_DRAGON_8424) val SKELETAL_WYVERN_IDS = intArrayOf(NPCs.SKELETAL_WYVERN_3068, NPCs.SKELETAL_WYVERN_3069, NPCs.SKELETAL_WYVERN_3070, NPCs.SKELETAL_WYVERN_3071) -val SPACER = " "; -val NUM_PAGES = 3 - -fun sendStats(player: Player, other: Player, page: Int){ - prepareInterface(player, other, page) - val globalData = other.savedData.globalData - for(i in (68..97)) { - when(page) { - 0 -> { - when(i) { - //Various stats - 97 -> sendLine(player,"Easy Clues: ${TreasureTrailManager.getInstance(other).completedClues[0]}",i) - 68 -> sendLine(player,"Medium Clues: ${TreasureTrailManager.getInstance(other).completedClues[1]}",i) - 69 -> sendLine(player,"Hard Clues: ${TreasureTrailManager.getInstance(other).completedClues[2]}",i) - 70 -> sendLine(player, SPACER,i) - 71 -> sendLine(player,"Slayer Tasks: ${SlayerManager.getInstance(other).flags.completedTasks}",i) - 72 -> sendLine(player,"Quest Points: ${other.questRepository.points}",i) - 73 -> sendLine(player,"Ironman Mode: ${other.ironmanManager.mode.name.toLowerCase()}",i) - 74 -> sendLine(player,"Deaths: ${other.getAttribute("$STATS_BASE:$STATS_DEATHS",0)}",i) - 75 -> sendLine(player, SPACER,i) - 76 -> sendLine(player,"Logs Chopped: ${other.getAttribute("$STATS_BASE:$STATS_LOGS",0)}",i) - 77 -> sendLine(player,"Rocks Mined: ${other.getAttribute("$STATS_BASE:$STATS_ROCKS",0)}",i) - 78 -> sendLine(player,"Fish Caught: ${other.getAttribute("$STATS_BASE:$STATS_FISH",0)}",i) - 79 -> sendLine(player, "Essence Crafted: ${other.getAttribute("$STATS_BASE:$STATS_RC",0)}", i) - - //Boss KC - 82 -> sendLine(player, "KBD KC: ${globalData.bossCounters.get(BossKillCounter.KING_BLACK_DRAGON.ordinal)}",i) - 83 -> sendLine(player, "TDs KC: ${globalData.bossCounters.get(BossKillCounter.TORMENTED_DEMONS.ordinal)}",i) - 84 -> sendLine(player, "Supreme KC: ${globalData.bossCounters.get(BossKillCounter.DAGANNOTH_SUPREME.ordinal)}",i) - 85 -> sendLine(player, "Rex KC: ${globalData.bossCounters.get(BossKillCounter.DAGANNOTH_REX.ordinal)}",i) - 86 -> sendLine(player, "Prime KC: ${globalData.bossCounters.get(BossKillCounter.DAGANNOTH_PRIME.ordinal)}",i) - 87 -> sendLine(player, "Barrows KC: ${globalData.barrowsLoots}",i) - 88 -> sendLine(player, "Chaos Ele: ${globalData.bossCounters.get(BossKillCounter.CHAOS_ELEMENTAL.ordinal)}",i) - 89 -> sendLine(player, "Mole KC: ${globalData.bossCounters.get(BossKillCounter.GIANT_MOLE.ordinal)}",i) - 90 -> sendLine(player, "Sara KC: ${globalData.bossCounters.get(BossKillCounter.SARADOMIN.ordinal)}",i) - 91 -> sendLine(player, "Zammy KC: ${globalData.bossCounters.get(BossKillCounter.ZAMORAK.ordinal)}",i) - 92 -> sendLine(player, "Bandos KC: ${globalData.bossCounters.get(BossKillCounter.BANDOS.ordinal)}",i) - 93 -> sendLine(player, "Arma KC: ${globalData.bossCounters.get(BossKillCounter.ARMADYL.ordinal)}",i) - 94 -> sendLine(player, "Jad KC: ${globalData.bossCounters.get(BossKillCounter.JAD.ordinal)}",i) - 95 -> sendLine(player, "KQ KC: ${globalData.bossCounters.get(BossKillCounter.KALPHITE_QUEEN.ordinal)}",i) - 96 -> sendLine(player, "Corp KC: ${globalData.bossCounters.get(BossKillCounter.CORPOREAL_BEAST.ordinal)}",i) - else -> sendLine(player,"",i) - } - } - 1 -> { - when(i) { - 97 -> sendLine(player, "Turoths: ${GlobalKillCounter.getKills(other, TUROTH_IDS)}", i) - 68 -> sendLine(player, "Kurasks: ${GlobalKillCounter.getKills(other, KURASK_IDS)}", i) - 69 -> sendLine(player, "Leaf-bladed swords: ${GlobalKillCounter.getRareDrops(other, Items.LEAF_BLADED_SWORD_13290)}", i) - 70 -> sendLine(player, SPACER,i) - 71 -> sendLine(player, "Gargoyles: ${GlobalKillCounter.getKills(other, GARGOYLE_IDS)}", i) - 72 -> sendLine(player, "Granite mauls: ${GlobalKillCounter.getRareDrops(other, Items.GRANITE_MAUL_4153)}", i) - 73 -> sendLine(player, SPACER,i) - 74 -> sendLine(player, "Spiritual mages: ${GlobalKillCounter.getKills(other, SPIRITUAL_MAGE_IDS)}", i) - 75 -> sendLine(player, "Dragon boots: ${GlobalKillCounter.getRareDrops(other, Items.DRAGON_BOOTS_11732)}", i) - 76 -> sendLine(player, SPACER,i) - 77 -> sendLine(player, "Abyssal demons: ${GlobalKillCounter.getKills(other, NPCs.ABYSSAL_DEMON_1615)}", i) - 78 -> sendLine(player, "Abyssal whips: ${GlobalKillCounter.getRareDrops(other, Items.ABYSSAL_WHIP_4151)}", i) - 79 -> sendLine(player, SPACER,i) - 80 -> sendLine(player, "Dark beasts: ${GlobalKillCounter.getKills(other, NPCs.DARK_BEAST_2783)}", i) - 81 -> sendLine(player, "Dark bows: ${GlobalKillCounter.getRareDrops(other, Items.DARK_BOW_11235)}", i) - - 82 -> sendLine(player, "Green Dragons: ${GlobalKillCounter.getKills(other, GREEN_DRAGON_IDS)}", i) - 83 -> sendLine(player, "Blue Dragons: ${GlobalKillCounter.getKills(other, BLUE_DRAGON_IDS)}", i) - 84 -> sendLine(player, "Red Dragons: ${GlobalKillCounter.getKills(other, RED_DRAGON_IDS)}", i) - 85 -> sendLine(player, "Black Dragons: ${GlobalKillCounter.getKills(other, BLACK_DRAGON_IDS)}", i) - 86 -> sendLine(player, SPACER,i) - 87 -> sendLine(player, "Bronze Dragons: ${GlobalKillCounter.getKills(other, BRONZE_DRAGON_IDS)}", i) - 88 -> sendLine(player, "Iron Dragons: ${GlobalKillCounter.getKills(other, IRON_DRAGON_IDS)}", i) - 89 -> sendLine(player, "Steel Dragons: ${GlobalKillCounter.getKills(other, STEEL_DRAGON_IDS)}", i) - 90 -> sendLine(player, "Mithril Dragons: ${GlobalKillCounter.getKills(other, MITHRIL_DRAGON_IDS)}", i) - 91 -> sendLine(player, "Skeletal Wyverns: ${GlobalKillCounter.getKills(other, SKELETAL_WYVERN_IDS)}", i) - 92 -> sendLine(player, SPACER,i) - 93 -> sendLine(player, "Draconic visages: ${GlobalKillCounter.getRareDrops(other, Items.DRACONIC_VISAGE_11286)}", i) - - else -> sendLine(player,"",i) - } - } - 2 -> { - when(i) { - 97 -> sendLine(player, "Ahrim's hood: ${GlobalKillCounter.getRareDrops(other, Items.AHRIMS_HOOD_4708)}", i) - 68 -> sendLine(player, "Ahrim's staff: ${GlobalKillCounter.getRareDrops(other, Items.AHRIMS_STAFF_4710)}", i) - 69 -> sendLine(player, "Ahrim's robetop: ${GlobalKillCounter.getRareDrops(other, Items.AHRIMS_ROBETOP_4712)}", i) - 70 -> sendLine(player, "Ahrim's robeskirt: ${GlobalKillCounter.getRareDrops(other, Items.AHRIMS_ROBESKIRT_4714)}", i) - 71 -> sendLine(player, SPACER,i) - 72 -> sendLine(player, "Dharok's helm: ${GlobalKillCounter.getRareDrops(other, Items.DHAROKS_HELM_4716)}", i) - 73 -> sendLine(player, "Dharok's greataxe: ${GlobalKillCounter.getRareDrops(other, Items.DHAROKS_GREATAXE_4718)}", i) - 74 -> sendLine(player, "Dharok's platebody: ${GlobalKillCounter.getRareDrops(other, Items.DHAROKS_PLATEBODY_4720)}", i) - 75 -> sendLine(player, "Dharok's platelegs: ${GlobalKillCounter.getRareDrops(other, Items.DHAROKS_PLATELEGS_4722)}", i) - 76 -> sendLine(player, SPACER,i) - 77 -> sendLine(player, "Guthan's helm: ${GlobalKillCounter.getRareDrops(other, Items.GUTHANS_HELM_4724)}", i) - 78 -> sendLine(player, "Guthan's warspear: ${GlobalKillCounter.getRareDrops(other, Items.GUTHANS_WARSPEAR_4726)}", i) - 79 -> sendLine(player, "Guthan's platebody: ${GlobalKillCounter.getRareDrops(other, Items.GUTHANS_PLATEBODY_4728)}", i) - 80 -> sendLine(player, "Guthan's chainskirt: ${GlobalKillCounter.getRareDrops(other, Items.GUTHANS_CHAINSKIRT_4730)}", i) - - 82 -> sendLine(player, "Karil's coif: ${GlobalKillCounter.getRareDrops(other, Items.KARILS_COIF_4732)}", i) - 83 -> sendLine(player, "Karil's crossbow: ${GlobalKillCounter.getRareDrops(other, Items.KARILS_CROSSBOW_4734)}", i) - 84 -> sendLine(player, "Karil's leathertop: ${GlobalKillCounter.getRareDrops(other, Items.KARILS_LEATHERTOP_4736)}", i) - 85 -> sendLine(player, "Karil's leatherskirt: ${GlobalKillCounter.getRareDrops(other, Items.KARILS_LEATHERSKIRT_4738)}", i) - 86 -> sendLine(player, SPACER,i) - 87 -> sendLine(player, "Torag's helm: ${GlobalKillCounter.getRareDrops(other, Items.TORAGS_HELM_4745)}", i) - 88 -> sendLine(player, "Torag's hammers: ${GlobalKillCounter.getRareDrops(other, Items.TORAGS_HAMMERS_4747)}", i) - 89 -> sendLine(player, "Torag's platebody: ${GlobalKillCounter.getRareDrops(other, Items.TORAGS_PLATEBODY_4749)}", i) - 90 -> sendLine(player, "Torag's platelegs: ${GlobalKillCounter.getRareDrops(other, Items.TORAGS_PLATELEGS_4751)}", i) - 91 -> sendLine(player, SPACER,i) - 92 -> sendLine(player, "Verac's helm: ${GlobalKillCounter.getRareDrops(other, Items.VERACS_HELM_4753)}", i) - 93 -> sendLine(player, "Verac's flail: ${GlobalKillCounter.getRareDrops(other, Items.VERACS_FLAIL_4755)}", i) - 94 -> sendLine(player, "Verac's brassard: ${GlobalKillCounter.getRareDrops(other, Items.VERACS_BRASSARD_4757)}", i) - 95 -> sendLine(player, "Verac's plateskirt: ${GlobalKillCounter.getRareDrops(other, Items.VERACS_PLATESKIRT_4759)}", i) - else -> sendLine(player,"",i) - } - } - } - } - player.interfaceManager.open(Component(26)) -} - -fun sendLine(player: Player, line: String, child: Int) { - player.packetDispatch.sendString(line,26,child) -} - -fun prepareInterface(player: Player,other: Player, page: Int){ - player.setAttribute("stats-page-info", StatsPageInfo(other, page)) - if(page == 0) { - player.packetDispatch.sendInterfaceConfig(26,62,true) - player.packetDispatch.sendInterfaceConfig(26,65,true) - } else { - sendLine(player, "Previous", 65) - } - if(page == NUM_PAGES - 1) { - player.packetDispatch.sendInterfaceConfig(26,64,true) - player.packetDispatch.sendInterfaceConfig(26,66,true) - } else { - sendLine(player, "Next", 66) - } - player.packetDispatch.sendString(other.username + "'s Stats - Page ${page + 1}",26,101) -} - -data class StatsPageInfo(val other: Player, val page: Int) {} +val SPACER = " " +val FAKE_CONTENT = arrayOf( + PageSet(Page(), Page()), + PageSet(Page(), Page()), + PageSet(Page(), Page()), +) @Initializable class StatsCommandSet : CommandSet(Privilege.STANDARD) { - override fun defineCommands() { - define("stats"){player,args -> - if(args.size == 1){ - sendStats(player) - return@define + companion object { + private fun display(player: Player, pageNum: Int, buttonId: Int) : Boolean { + val queryPlayer: Player? = player.getAttribute("stats-command-query-player", null) + if (queryPlayer == null) { + return false } + val globalData = queryPlayer.globalData + BookInterface.pageSetup(player, BookInterface.FANCY_BOOK_26,queryPlayer.username + "'s Stats", FAKE_CONTENT) + + for(i in (68..97)) { + when(pageNum) { + 0 -> { + when(i) { + //Various stats + 97 -> sendLine(player,"Easy Clues: ${TreasureTrailManager.getInstance(queryPlayer).completedClues[0]}",i) + 68 -> sendLine(player,"Medium Clues: ${TreasureTrailManager.getInstance(queryPlayer).completedClues[1]}",i) + 69 -> sendLine(player,"Hard Clues: ${TreasureTrailManager.getInstance(queryPlayer).completedClues[2]}",i) + 70 -> sendLine(player, SPACER,i) + 71 -> sendLine(player,"Slayer Tasks: ${SlayerManager.getInstance(queryPlayer).flags.completedTasks}",i) + 72 -> sendLine(player,"Quest Points: ${queryPlayer.questRepository.points}",i) + 73 -> sendLine(player,"Ironman Mode: ${queryPlayer.ironmanManager.mode.name.lowercase(Locale.getDefault())}",i) + 74 -> sendLine(player,"Deaths: ${queryPlayer.getAttribute("$STATS_BASE:$STATS_DEATHS",0)}",i) + 75 -> sendLine(player, SPACER,i) + 76 -> sendLine(player,"Logs Chopped: ${queryPlayer.getAttribute("$STATS_BASE:$STATS_LOGS",0)}",i) + 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) + 83 -> sendLine(player, "TDs KC: ${globalData.bossCounters.get(BossKillCounter.TORMENTED_DEMONS.ordinal)}",i) + 84 -> sendLine(player, "Supreme KC: ${globalData.bossCounters.get(BossKillCounter.DAGANNOTH_SUPREME.ordinal)}",i) + 85 -> sendLine(player, "Rex KC: ${globalData.bossCounters.get(BossKillCounter.DAGANNOTH_REX.ordinal)}",i) + 86 -> sendLine(player, "Prime KC: ${globalData.bossCounters.get(BossKillCounter.DAGANNOTH_PRIME.ordinal)}",i) + 87 -> sendLine(player, "Barrows KC: ${globalData.barrowsLoots}",i) + 88 -> sendLine(player, "Chaos Ele: ${globalData.bossCounters.get(BossKillCounter.CHAOS_ELEMENTAL.ordinal)}",i) + 89 -> sendLine(player, "Mole KC: ${globalData.bossCounters.get(BossKillCounter.GIANT_MOLE.ordinal)}",i) + 90 -> sendLine(player, "Sara KC: ${globalData.bossCounters.get(BossKillCounter.SARADOMIN.ordinal)}",i) + 91 -> sendLine(player, "Zammy KC: ${globalData.bossCounters.get(BossKillCounter.ZAMORAK.ordinal)}",i) + 92 -> sendLine(player, "Bandos KC: ${globalData.bossCounters.get(BossKillCounter.BANDOS.ordinal)}",i) + 93 -> sendLine(player, "Arma KC: ${globalData.bossCounters.get(BossKillCounter.ARMADYL.ordinal)}",i) + 94 -> sendLine(player, "Jad KC: ${globalData.bossCounters.get(BossKillCounter.JAD.ordinal)}",i) + 95 -> sendLine(player, "KQ KC: ${globalData.bossCounters.get(BossKillCounter.KALPHITE_QUEEN.ordinal)}",i) + 96 -> sendLine(player, "Corp KC: ${globalData.bossCounters.get(BossKillCounter.CORPOREAL_BEAST.ordinal)}",i) + else -> sendLine(player,"",i) + } + } + 1 -> { + when(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: ${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: ${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: ${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: ${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: ${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: ${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: ${PlayerStatsCounter.getRareDrops(queryPlayer, Items.DRACONIC_VISAGE_11286)}", i) + + else -> sendLine(player,"",i) + } + } + 2 -> { + when(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: ${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: ${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: ${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: ${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: ${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) + } + } + } + } + return true + } + fun sendLine(player: Player, line: String, child: Int) { + player.packetDispatch.sendString(line ,BookInterface.FANCY_BOOK_26, child) + } + + } + override fun defineCommands() { + define("stats"){ player, args -> + + // Bad number of args if(args.size > 2){ reject(player,"Usage: ::stats playername") return@define } - val other = Repository.getPlayerByName(args[1]) - if(other == null){ + // If stats is called without a username, return self. + var queryPlayer: Player? = null + if(args.size == 1){ + queryPlayer = player + } + // If stats is called with a username, find the player or set to null. + if(args.size == 2) { + queryPlayer = Repository.getPlayerByName(args[1]) + } + // If main player is not found, return error. + if(queryPlayer == null){ reject(player,"Invalid player or player not online.") return@define } - sendStats(player,other, 0) - } - } - - fun sendStats(player: Player){ - sendStats(player,player, 0) - } - -} - -class StatsInterface : InterfaceListener { - val COMPONENT_ID = 26 - - override fun defineInterfaceListeners() { - on(COMPONENT_ID, 61) { player, _, _, _, _, _ -> - val info: StatsPageInfo? = player.getAttribute("stats-page-info", null) - if(info != null) { - sendStats(player, info.other, info.page-1) - } - return@on true - } - on(COMPONENT_ID, 63) { player, _, _, _, _, _ -> - val info: StatsPageInfo? = player.getAttribute("stats-page-info", null) - if(info != null) { - sendStats(player, info.other, info.page+1) - } - return@on true + setAttribute(player, "stats-command-query-player", queryPlayer) + BookInterface.openBook(player, BookInterface.FANCY_BOOK_26, ::display) + return@define } } } diff --git a/Server/src/main/core/game/system/command/sets/SystemCommandSet.kt b/Server/src/main/core/game/system/command/sets/SystemCommandSet.kt index e1803535e..2581bb4bb 100644 --- a/Server/src/main/core/game/system/command/sets/SystemCommandSet.kt +++ b/Server/src/main/core/game/system/command/sets/SystemCommandSet.kt @@ -1,19 +1,17 @@ package core.game.system.command.sets -import core.api.InputType -import core.api.runTask -import core.api.sendDialogue -import core.api.sendInputDialogue +import content.data.ChargedItem +import core.api.* import core.cache.def.impl.ItemDefinition +import core.game.node.entity.player.Player import core.game.node.item.Item import core.game.system.SystemManager import core.game.system.SystemState -import core.plugin.Initializable -import org.rs09.consts.Items -import core.tools.SystemLogger import core.game.system.command.Privilege import core.game.world.GameWorld import core.game.world.repository.Repository +import core.plugin.Initializable +import org.rs09.consts.Items import kotlin.system.exitProcess @Initializable @@ -43,7 +41,6 @@ class SystemCommandSet : CommandSet(Privilege.ADMIN) { define("resetpassword", Privilege.STANDARD, "", "WARNING: Case insensitive due to dialogue limitations.") { player, args -> sendInputDialogue(player, InputType.STRING_SHORT, "Enter Current Password:"){value -> val pass = value.toString() - SystemLogger.logInfo(this::class.java, pass) runTask(player) { if (GameWorld.authenticator.checkPassword(player, pass)) { sendInputDialogue(player, InputType.STRING_SHORT, "Enter New Password:") { value2 -> @@ -237,5 +234,75 @@ class SystemCommandSet : CommandSet(Privilege.ADMIN) { exitProcess(0) } + /** + * Command to get and set the charge of an item. + * Can handle both the internal charge used for things like degradation, and distinct (#) charge that's different items. + * The default behavior without any flags is to get the internal charge of an item. + * param equipment slot name | item id - selects the item, either by using the equipment slot name, or item id. + * param sd - optional flags, where s = set charge, d = distinct charge. + * param charge - the charge to set on the item. Only necessary when the set flag is toggled. + * @author RiL + */ + define("charge", Privilege.ADMIN, "::charge equipment slot name | item id [sd] [charge]", "Get/set the charge of an item. Flags: s = set, d = distinct(#).") { player, args -> + if (args.size < 2) reject( + player, + "Usage: ::charge equipment slot name | item id [sd] [charge]", + "Get internal: ::charge 4718", + "Get distinct: ::charge ring d", + "Set internal: ::charge head s 500", + "Set distinct: ::charge 1704 sd 4" + ) + + @Suppress("UNCHECKED_CAST") + val itemInfo = (args[1].toIntOrNull()?.let { + getItemAndContainer(player, it) ?: reject(player, "Item not found: $it.") + } ?: EquipmentSlot.slotByName(args[1])?.ordinal?.let { + Pair(player.equipment.get(it) ?: reject(player, "No item equipped at slot: ${args[1]}."), player.equipment) + } ?: reject(player, "No slot: ${args[1]}.")) as Pair + + val item = itemInfo.first + + when (val flags = args.getOrElse(2) { "" }) { + "" -> { // get internal charge + notify(player, "${item.name}: ${item.charge} charge.") + } + + "d" -> { // get distinct charge + ChargedItem.forId(item.id)?.run { + notify(player, "${item.name}: (${ChargedItem.getCharge(item.id)}) charge.") + } ?: reject(player, "${item.name} is not a distinct(#) item.") + } + + "s", "sd", "ds" -> { + if (args.size < 4) reject(player, "Must enter a charge value.") + val charge = (args[3].toIntOrNull() ?: reject(player, "Charge must be an integer.")) as Int + if (flags == "s") { // set internal charge + if (charge < 1) reject(player, "Charge must be a positive integer.") + item.charge = charge + notify(player, "Set internal charge of ${item.name} to $charge.") + } else { // set distinct charge + ChargedItem.forId(item.id)?.forCharge(charge)?.let { + itemInfo.second.replace(Item(it), item.slot) + notify(player, "Set distinct charge of ${item.name} to (${ChargedItem.getCharge(it)}).") + } ?: reject(player, "${item.name} is not a distinct(#) item.") + } + } + + else -> { + reject(player, "Please enter valid flags: no flag/empty, s, d, sd.", "Use ::charge for examples.") + } + } + } + } + + /** + * Search for item in player and return item and container if found. Return null if not found + */ + private fun getItemAndContainer(player: Player, id: Int): Pair? { + if (id !in 0 until ItemDefinition.getDefinitions().size) return null + arrayOf(player.inventory, player.equipment, player.bankPrimary, player.bankSecondary).forEach { container -> + container.toArray().firstOrNull { it?.id == id }?.let { return Pair(it, container) } + } + return null } } 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 b9468e139..30e4107c9 100644 --- a/Server/src/main/core/game/system/command/sets/TeleportCommandSet.kt +++ b/Server/src/main/core/game/system/command/sets/TeleportCommandSet.kt @@ -7,8 +7,10 @@ import core.plugin.Initializable import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import core.ServerConstants +import core.api.log import core.game.system.command.Privilege import core.game.world.repository.Repository +import core.tools.Log @Initializable class TeleportCommandSet : CommandSet(Privilege.ADMIN){ @@ -137,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/ClanRepository.java b/Server/src/main/core/game/system/communication/ClanRepository.java index ebdc8a3dc..58ec6708c 100644 --- a/Server/src/main/core/game/system/communication/ClanRepository.java +++ b/Server/src/main/core/game/system/communication/ClanRepository.java @@ -1,5 +1,6 @@ package core.game.system.communication; +import core.ServerConstants; import core.game.component.Component; import core.game.activity.ActivityPlugin; import core.game.node.entity.player.Player; @@ -100,7 +101,7 @@ public final class ClanRepository { * @return {@code True} if the player successfully entered the clan chat. */ public boolean enter(Player player) { - if (!owner.equals("2009scape") && players.size() >= MAX_MEMBERS) { + if (!owner.equals(ServerConstants.SERVER_NAME.toLowerCase()) && players.size() >= MAX_MEMBERS) { player.getPacketDispatch().sendMessage("The channel you tried to join is full.:clan:"); return false; } diff --git a/Server/src/main/core/game/system/communication/CommunicationInfo.java b/Server/src/main/core/game/system/communication/CommunicationInfo.java index eb323277f..8135e7315 100644 --- a/Server/src/main/core/game/system/communication/CommunicationInfo.java +++ b/Server/src/main/core/game/system/communication/CommunicationInfo.java @@ -1,11 +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; @@ -18,10 +17,12 @@ 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; +import static core.api.ContentAPIKt.log; +import static core.api.ContentAPIKt.setVarp; + /** * Holds communication information. * @author Emperor @@ -191,19 +192,19 @@ public final class CommunicationInfo { public void toggleLootshare(final Player player) { if (lootShare) { lootShare = false; - player.getConfigManager().set(1083, 0); + setVarp(player, 1083, 0); } else if (lootSharePulse != null) { lootSharePulse.stop(); lootSharePulse = null; - player.getConfigManager().set(1083, 0); + setVarp(player, 1083, 0); } else if (!lootShare) { - player.getConfigManager().set(1083, 2); + setVarp(player, 1083, 2); lootSharePulse = new Pulse(GameWorld.getSettings().isDevMode() ? 5 : 200, player) { @Override public boolean pulse() { lootShare = true; lootSharePulse = null; - player.getConfigManager().set(1083, 1); + setVarp(player, 1083, 1); return true; } }; @@ -211,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. @@ -253,6 +233,10 @@ public final class CommunicationInfo { */ public static void add(Player player, String contact) { CommunicationInfo info = player.getDetails().getCommunication(); + if (contact.isEmpty()) { + player.sendMessage("Unable to find a player by that name. Please try again."); + return; + } if (WorldCommunicator.isEnabled()) { MSPacketRepository.sendContactUpdate(player.getName(), contact, false, false, null); return; @@ -287,6 +271,10 @@ public final class CommunicationInfo { * @param block If the contact should be removed from the block list. */ public static void remove(Player player, String contact, boolean block) { + if (contact.isEmpty()) { + player.sendMessage("Unable to find a player by that name. Please try again."); + return; + } if (WorldCommunicator.isEnabled()) { MSPacketRepository.sendContactUpdate(player.getName(), contact, true, block, null); return; @@ -322,6 +310,10 @@ public final class CommunicationInfo { * @param contact The contact to block. */ public static void block(Player player, String contact) { + if (contact.isEmpty()) { + player.sendMessage("Unable to find a player by that name. Please try again."); + return; + } if (WorldCommunicator.isEnabled()) { MSPacketRepository.sendContactUpdate(player.getName(), contact, false, true, null); return; @@ -348,10 +340,14 @@ public final class CommunicationInfo { * @param clanRank The clan rank to set. */ public static void updateClanRank(Player player, String contact, ClanRank clanRank) { + if (contact.isEmpty()) { + player.sendMessage("Unable to find a player by that name. Please try again."); + return; + } CommunicationInfo info = player.getDetails().getCommunication(); Contact c = info.contacts.get(contact); if (c == null) { - SystemLogger.logErr(CommunicationInfo.class, "Could not find contact " + contact + " to update clan rank!"); + log(CommunicationInfo.class, Log.ERR, "Could not find contact " + contact + " to update clan rank!"); return; } c.setRank(clanRank); @@ -664,4 +660,4 @@ public final class CommunicationInfo { + "," + kickRequirement.ordinal() + "," + lootRequirement.ordinal(); } -} \ No newline at end of file +} diff --git a/Server/src/main/core/game/system/communication/GlobalChat.kt b/Server/src/main/core/game/system/communication/GlobalChat.kt new file mode 100644 index 000000000..6e8ed2161 --- /dev/null +++ b/Server/src/main/core/game/system/communication/GlobalChat.kt @@ -0,0 +1,41 @@ +package core.game.system.communication + +import core.api.Commands +import core.api.getAttribute +import core.api.sendMessage +import core.api.setAttribute +import core.game.system.command.Privilege +import core.game.world.repository.Repository +import core.game.node.entity.player.info.Rights +import core.tools.colorize + +class GlobalChat : Commands { + override fun defineCommands() { + define("muteglobal", Privilege.STANDARD, "", "Toggles global chat on or off.") {player, _ -> + val original = getAttribute(player, ATTR_GLOBAL_MUTE, false) + setAttribute(player, ATTR_GLOBAL_MUTE, !original) + sendMessage(player, "Global chat is now ${if (original) "ON" else "OFF"}.") + return@define + } + } + + companion object { + val ATTR_GLOBAL_MUTE = "/save:globalmute" + fun process(sender: String, message: String, rights: Int) { + val msgSD = prepare(sender, message, false, rights) + val msgHD = prepare(sender, message, true, rights) + for (player in Repository.players.filter { !getAttribute(it, ATTR_GLOBAL_MUTE, false) }) { + if (player.interfaceManager.isResizable) + sendMessage(player, msgHD) + else + sendMessage(player, msgSD) + } + } + + private fun prepare(sender: String, message: String, isResizable: Boolean, rights: Int): String { + val baseColor = if (isResizable) "%f1b04c" else "%7512ff" + val bracketColor = if (isResizable) "%ffffff" else "%000000" + return colorize("$bracketColor[${baseColor}G$bracketColor] ${if (rights > 0) "" else ""}$sender: ${baseColor}$message") + } + } +} diff --git a/Server/src/main/core/game/system/config/CustomVarbitParser.kt b/Server/src/main/core/game/system/config/CustomVarbitParser.kt index 770117cc9..bf4647479 100644 --- a/Server/src/main/core/game/system/config/CustomVarbitParser.kt +++ b/Server/src/main/core/game/system/config/CustomVarbitParser.kt @@ -5,6 +5,8 @@ import org.json.simple.JSONArray import org.json.simple.JSONObject import org.json.simple.parser.JSONParser import core.ServerConstants +import core.api.log +import core.tools.Log import core.tools.SystemLogger import java.io.FileReader @@ -30,6 +32,6 @@ class CustomVarbitParser { count++ } - SystemLogger.logInfo(this::class.java, "Parsed $count custom varbit definitions.") + log(this::class.java, Log.FINE, "Parsed $count custom varbit definitions.") } } \ No newline at end of file diff --git a/Server/src/main/core/game/system/config/DoorConfigLoader.kt b/Server/src/main/core/game/system/config/DoorConfigLoader.kt index f1bc229b3..04d23c978 100644 --- a/Server/src/main/core/game/system/config/DoorConfigLoader.kt +++ b/Server/src/main/core/game/system/config/DoorConfigLoader.kt @@ -1,6 +1,8 @@ package core.game.system.config import core.ServerConstants +import core.api.log +import core.tools.Log import core.tools.SystemLogger import org.json.simple.JSONArray import org.json.simple.JSONObject @@ -28,10 +30,17 @@ class DoorConfigLoader { door.replaceId = e["replaceId"].toString().toInt() door.isFence = e["fence"].toString().toBoolean() door.isMetal = e["metal"].toString().toBoolean() + door.isAutoWalk = e["autowalk"]?.toString()?.toBoolean() ?: false 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 + DOORS[door.replaceId] = replacedDoor count++ } - SystemLogger.logInfo(this::class.java, "Parsed $count door configs.") + log(this::class.java, Log.FINE, "Parsed $count door configs.") } @@ -92,5 +101,7 @@ class DoorConfigLoader { */ var isMetal = false + var questRequirement = "" + } -} \ No newline at end of file +} diff --git a/Server/src/main/core/game/system/config/DropTableParser.kt b/Server/src/main/core/game/system/config/DropTableParser.kt index 57cf98d79..465a1c6ee 100644 --- a/Server/src/main/core/game/system/config/DropTableParser.kt +++ b/Server/src/main/core/game/system/config/DropTableParser.kt @@ -5,11 +5,13 @@ import org.json.simple.JSONArray import org.json.simple.JSONObject import org.json.simple.parser.JSONParser import core.ServerConstants -import core.api.utils.NPCDropTable -import core.api.utils.WeightedItem +import core.api.* +import core.api.utils.* +import core.tools.Log import core.tools.SystemLogger import java.io.FileReader + class DropTableParser { val parser = JSONParser() var reader: FileReader? = null @@ -20,28 +22,43 @@ class DropTableParser { for(i in obj){ val tab = i as JSONObject val ids = tab["ids"].toString().split(",") - for(n in ids){ - val def = NPCDefinition.forId(n.toInt()).dropTables - def ?: continue - parseTable(tab["main"] as JSONArray, def.table, false) - parseTable(tab["charm"] as JSONArray, def.table, false, true) - parseTable(tab["default"] as JSONArray,def.table,true) - count++ + + try { + val table = NPCDropTable() + parseTable(tab["main"] as JSONArray, table, isAlways = false) + parseTable(tab["charm"] as JSONArray, table, isAlways = false, isCharms = true) + (tab["tertiary"] as? JSONArray)?.let { parseTable(it, table, isAlways = false, isTertiary = true) } + parseTable(tab["default"] as JSONArray, table, true) + + for(n in ids){ + val def = NPCDefinition.forId(n.toInt()).dropTables + def ?: continue + def.table = table + count++ + } + } catch (e: ConfigParseException) { + log (this::class.java, Log.ERR, "Error parsing drop tables for NPC ${NPCDefinition.forId(ids[0].toInt()).name}: ${exceptionToString(e)}") } } - SystemLogger.logInfo(this::class.java, "Parsed $count drop tables.") + log(this::class.java, Log.FINE, "Parsed $count drop tables.") } - private fun parseTable(data: JSONArray, destTable: NPCDropTable, isAlways: Boolean, isCharms: Boolean = false) { + private fun parseTable(data: JSONArray, destTable: NPCDropTable, isAlways: Boolean, isTertiary: Boolean = false, isCharms: Boolean = false) { for(it in data){ val item = it as JSONObject val id = item["id"].toString().toInt() val minAmount = item["minAmount"].toString().toInt() val maxAmount = item["maxAmount"].toString().toInt() + + if (minAmount > maxAmount) { + throw ConfigParseException("Table is invalid! Specified minimum amount is > specified maximum amount.") + } + val weight = item["weight"].toString().toDouble() val newItem = WeightedItem(id,minAmount,maxAmount,weight.toDouble(),isAlways) if(isCharms) destTable.addToCharms(newItem) + else if (isTertiary) destTable.addToTertiary(newItem) else destTable.add(newItem) } } -} \ No newline at end of file +} diff --git a/Server/src/main/core/game/system/config/GroundSpawnLoader.kt b/Server/src/main/core/game/system/config/GroundSpawnLoader.kt index 39ea3df6e..cbf5dabc9 100644 --- a/Server/src/main/core/game/system/config/GroundSpawnLoader.kt +++ b/Server/src/main/core/game/system/config/GroundSpawnLoader.kt @@ -9,11 +9,11 @@ import org.json.simple.JSONArray import org.json.simple.JSONObject import org.json.simple.parser.JSONParser import core.ServerConstants -import core.tools.SystemLogger +import core.api.log import core.game.world.GameWorld import core.game.world.repository.Repository -import java.io.FileReader -import java.nio.ByteBuffer +import core.tools.Log +import java.io.* class GroundSpawnLoader { val parser = JSONParser() @@ -24,17 +24,25 @@ class GroundSpawnLoader { reader = FileReader(ServerConstants.CONFIG_PATH + "ground_spawns.json") var configs = parser.parse(reader) as JSONArray for(config in configs){ - val e = config as JSONObject - val datas = e["loc_data"].toString().split("-") - val id = e["item_id"].toString().toInt() - for(d in datas){ - val tokens = d.replace("{", "").replace("}", "").split(",".toRegex()).toTypedArray() - val spawn = GroundSpawn(tokens[4].toInt(), Item(id, tokens[0].toInt()), Location(Integer.valueOf(tokens[1]), Integer.valueOf(tokens[2]), Integer.valueOf(tokens[3]))) - spawn.init() - count++ + try { + val e = config as JSONObject + val datas = e["loc_data"].toString().split("-") + val id = e["item_id"].toString().toInt() + for(d in datas){ + if (d.isNullOrEmpty()) continue + val tokens = d.replace("{", "").replace("}", "").split(",".toRegex()).toTypedArray() + val spawn = GroundSpawn(tokens[4].toInt(), Item(id, tokens[0].toInt()), Location(Integer.valueOf(tokens[1]), Integer.valueOf(tokens[2]), Integer.valueOf(tokens[3]))) + spawn.init() + count++ + } + } catch (e: Exception) { + val sw = StringWriter() + val pw = PrintWriter(sw) + e.printStackTrace(pw) + log(this::class.java, Log.ERR, "Error parsing config entry ${config.toString()}: $sw") } } - SystemLogger.logInfo(this::class.java, "Initialized $count ground items.") + log(this::class.java, Log.FINE, "Initialized $count ground items.") } class GroundSpawn /** @@ -55,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. */ @@ -116,4 +113,4 @@ class GroundSpawnLoader { } } -} \ No newline at end of file +} diff --git a/Server/src/main/core/game/system/config/InterfaceConfigParser.kt b/Server/src/main/core/game/system/config/InterfaceConfigParser.kt index a31b33dc1..d691bd167 100644 --- a/Server/src/main/core/game/system/config/InterfaceConfigParser.kt +++ b/Server/src/main/core/game/system/config/InterfaceConfigParser.kt @@ -1,7 +1,9 @@ package core.game.system.config import core.ServerConstants +import core.api.log import core.game.component.ComponentDefinition +import core.tools.Log import core.tools.SystemLogger import org.json.simple.JSONArray import org.json.simple.JSONObject @@ -24,6 +26,6 @@ class InterfaceConfigParser{ ComponentDefinition.getDefinitions()[id] = ComponentDefinition().parse(e["interfaceType"].toString(),e["walkable"].toString(),e["tabIndex"].toString()) count++ } - SystemLogger.logInfo(this::class.java, "Parsed $count interface configs.") + log(this::class.java, Log.FINE, "Parsed $count interface configs.") } } \ No newline at end of file diff --git a/Server/src/main/core/game/system/config/ItemConfigParser.kt b/Server/src/main/core/game/system/config/ItemConfigParser.kt index 8bdb39f65..39645961d 100644 --- a/Server/src/main/core/game/system/config/ItemConfigParser.kt +++ b/Server/src/main/core/game/system/config/ItemConfigParser.kt @@ -8,6 +8,8 @@ import org.json.simple.JSONArray import org.json.simple.JSONObject import org.json.simple.parser.JSONParser import core.ServerConstants +import core.api.log +import core.tools.Log import core.tools.SystemLogger import java.io.FileReader @@ -38,6 +40,11 @@ class ItemConfigParser { */ const val TWO_HANDED = "two_handed" + /** + * The alchemisable configuration key. + */ + const val ALCHEMIZABLE = "alchemizable" + /** * The high-alchemy price item configuration key. */ @@ -215,6 +222,11 @@ class ItemConfigParser { */ const val ARCHERY_TICKET_PRICE = "archery_ticket_price" + /** + * The Castle Wars Ticket price of items (in Lanthas' shop). + */ + const val CASTLE_WARS_TICKET_PRICE = "castle_wars_ticket_price" + } val parser = JSONParser() @@ -261,6 +273,7 @@ class ItemConfigParser { "destroy", "lendable", "tradeable" -> configs.put(it.key.toString(),it.value.toString().toBoolean()) + "alchemizable", -> configs.put(it.key.toString(),it.value.toString().toBoolean()) //doubles "weight" -> configs.put(it.key.toString(),it.value.toString().toDouble()) @@ -268,8 +281,9 @@ class ItemConfigParser { //ints "equip_audio", "point_price", - "tokkul_price", - "archery_ticket_price", + TOKKUL_PRICE, + ARCHERY_TICKET_PRICE, + CASTLE_WARS_TICKET_PRICE, "ge_buy_limit", "weapon_interface", "attack_speed", @@ -295,6 +309,6 @@ class ItemConfigParser { } count++ } - SystemLogger.logInfo(this::class.java, "Parsed $count item configs.") + log(this::class.java, Log.FINE, "Parsed $count item configs.") } } \ No newline at end of file diff --git a/Server/src/main/core/game/system/config/MusicConfigLoader.kt b/Server/src/main/core/game/system/config/MusicConfigLoader.kt index ff0136cab..3f5cc60cf 100644 --- a/Server/src/main/core/game/system/config/MusicConfigLoader.kt +++ b/Server/src/main/core/game/system/config/MusicConfigLoader.kt @@ -2,11 +2,13 @@ package core.game.system.config import core.cache.def.impl.DataMap import core.ServerConstants +import core.api.log import core.game.node.entity.player.link.music.MusicEntry import core.game.node.entity.player.link.music.MusicZone import core.tools.SystemLogger import core.game.world.map.RegionManager import core.game.world.map.zone.ZoneBorders +import core.tools.Log import org.json.simple.JSONArray import org.json.simple.JSONObject import org.json.simple.parser.JSONParser @@ -20,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) @@ -33,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() @@ -41,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 @@ -57,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()) { @@ -67,6 +78,6 @@ class MusicConfigLoader { } count++ } - SystemLogger.logInfo(this::class.java, "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/NPCConfigParser.kt b/Server/src/main/core/game/system/config/NPCConfigParser.kt index e3354f637..292c682e7 100644 --- a/Server/src/main/core/game/system/config/NPCConfigParser.kt +++ b/Server/src/main/core/game/system/config/NPCConfigParser.kt @@ -3,10 +3,12 @@ package core.game.system.config import core.ServerConstants import core.cache.def.impl.NPCDefinition import content.global.activity.ttrail.ClueLevel +import core.api.log import core.game.node.entity.combat.CombatStyle import core.game.node.entity.impl.Animator import core.tools.SystemLogger import core.game.world.update.flag.context.Animation +import core.tools.Log import org.json.simple.JSONArray import org.json.simple.JSONObject import org.json.simple.parser.JSONParser @@ -259,12 +261,12 @@ NPCConfigParser { "facing_booth", "can_tolerate", "water_npc"-> configs.put(it.key.toString(), it.value.toString().toBoolean()) - else -> SystemLogger.logWarn(this::class.java, "Unhandled key for npc config: ${it.key.toString()}") + else -> log(this::class.java, Log.WARN, "Unhandled key for npc config: ${it.key.toString()}") } } } count++ } - SystemLogger.logInfo(this::class.java, "Parsed $count NPC configurations") + log(this::class.java, Log.FINE, "Parsed $count NPC configurations") } } \ No newline at end of file diff --git a/Server/src/main/core/game/system/config/NPCSpawner.kt b/Server/src/main/core/game/system/config/NPCSpawner.kt index 9307d71dd..958b3e2d6 100644 --- a/Server/src/main/core/game/system/config/NPCSpawner.kt +++ b/Server/src/main/core/game/system/config/NPCSpawner.kt @@ -1,10 +1,12 @@ package core.game.system.config import core.ServerConstants +import core.api.log import core.game.node.entity.npc.NPC import core.tools.SystemLogger import core.game.world.map.Direction import core.game.world.map.Location +import core.tools.Log import org.json.simple.JSONArray import org.json.simple.JSONObject import org.json.simple.parser.JSONParser @@ -38,6 +40,6 @@ class NPCSpawner { count++ } } - SystemLogger.logInfo(this::class.java, "Spawned $count NPCs") + log(this::class.java, Log.FINE, "Spawned $count NPCs") } } \ No newline at end of file diff --git a/Server/src/main/core/game/system/config/ObjectConfigParser.kt b/Server/src/main/core/game/system/config/ObjectConfigParser.kt index d38ebf8b2..0445909c2 100644 --- a/Server/src/main/core/game/system/config/ObjectConfigParser.kt +++ b/Server/src/main/core/game/system/config/ObjectConfigParser.kt @@ -1,7 +1,9 @@ package core.game.system.config import core.ServerConstants +import core.api.log import core.cache.def.impl.SceneryDefinition +import core.tools.Log import core.tools.SystemLogger import org.json.simple.JSONArray import org.json.simple.JSONObject @@ -35,7 +37,7 @@ class ObjectConfigParser { count++ } } - SystemLogger.logInfo(this::class.java, "Parsed $count object configs.") + log(this::class.java, Log.FINE, "Parsed $count object configs.") } } diff --git a/Server/src/main/core/game/system/config/RangedConfigLoader.kt b/Server/src/main/core/game/system/config/RangedConfigLoader.kt index 57d28cd4c..6c54cbd55 100644 --- a/Server/src/main/core/game/system/config/RangedConfigLoader.kt +++ b/Server/src/main/core/game/system/config/RangedConfigLoader.kt @@ -1,16 +1,17 @@ package core.game.system.config import core.ServerConstants +import core.api.log import core.cache.def.impl.ItemDefinition import core.game.node.entity.combat.equipment.Ammunition 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 +import core.tools.Log import org.json.simple.JSONArray import org.json.simple.JSONObject import org.json.simple.parser.JSONParser @@ -55,7 +56,7 @@ class RangedConfigLoader { Ammunition.getAmmunition().put(Integer.parseInt(e["itemId"].toString()),ammo) count++ } - SystemLogger.logWarn(this::class.java, "Parsed $count ammo configs...") + log(this::class.java, Log.FINE, "Parsed $count ammo configs...") count = 0 reader = FileReader(ServerConstants.CONFIG_PATH + "ranged_weapon_configs.json") @@ -75,6 +76,6 @@ class RangedConfigLoader { RangeWeapon.getRangeWeapons().putIfAbsent(weapon.itemId,weapon) count++ } - SystemLogger.logInfo(this::class.java, "Parsed $count ranged weapon configs...") + log(this::class.java, Log.FINE, "Parsed $count ranged weapon configs...") } } diff --git a/Server/src/main/core/game/system/config/ServerConfigParser.kt b/Server/src/main/core/game/system/config/ServerConfigParser.kt index ffedb1f65..69c660f44 100644 --- a/Server/src/main/core/game/system/config/ServerConfigParser.kt +++ b/Server/src/main/core/game/system/config/ServerConfigParser.kt @@ -1,12 +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.tools.SystemLogger +import core.api.log +import core.api.parseEnumEntry 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 @@ -32,17 +35,25 @@ object ServerConfigParser { private fun parseFromFile(confFile: File?) { if(!confFile!!.canonicalFile.exists()){ - SystemLogger.logErr(this::class.java, "${confFile.canonicalFile} does not exist.") + log(this::class.java, Log.ERR, "${confFile.canonicalFile} does not exist.") exitProcess(0) } else { try { tomlData = Toml().read(confFile) parseServerSettings() parseGameSettings() + val jvmString = System.getProperty("java.version") + if (jvmString.startsWith("1.")) { + ServerConstants.JAVA_VERSION = jvmString.substring(2,3).toInt() + } else if (!jvmString.startsWith("1.")) { + ServerConstants.JAVA_VERSION = jvmString.substring(0,2).toInt() + } else if (!jvmString.contains(".")) { + ServerConstants.JAVA_VERSION = jvmString.toInt() + } + log(this::class.java, Log.FINE, "It seems we are in a Java ${ServerConstants.JAVA_VERSION} environment.") } catch (e: java.lang.IllegalStateException) { - SystemLogger.logErr(this::class.java, "Passed config file is not a TOML file. Path: ${confFile.canonicalPath}") - SystemLogger.logErr(this::class.java, "Exception received: $e") - SystemLogger.logAlert(this::class.java, "Shutting down...") + log(this::class.java, Log.ERR, "Passed config file is not a TOML file. Path: ${confFile.canonicalPath}") + log(this::class.java, Log.ERR, "Exception received: $e") exitProcess(0) } } @@ -78,6 +89,9 @@ object ServerConfigParser { enable_doubling_money_scammers = data.getBoolean("world.enable_doubling_money_scammers", false), wild_pvp_enabled = data.getBoolean("world.wild_pvp_enabled"), jad_practice_enabled = data.getBoolean("world.jad_practice_enabled"), + ge_announcement_limit = data.getLong("world.ge_announcement_limit", 500L).toInt(), + smartpathfinder_bfs = data.getBoolean("world.smartpathfinder_bfs", false), + enable_castle_wars = data.getBoolean("world.enable_castle_wars", false), message_model = data.getString("world.motw_identifier").toInt(), message_string = data.getString("world.motw_text").replace("@name", ServerConstants.SERVER_NAME) ) @@ -124,22 +138,47 @@ object ServerConfigParser { ServerConstants.BANK_BOOTH_QUICK_OPEN = data.getBoolean("world.bank_booth_quick_open", false) ServerConstants.BANK_BOOTH_NOTE_ENABLED = data.getBoolean("world.bank_booth_note_enabled", true) ServerConstants.BANK_BOOTH_NOTE_UIM = data.getBoolean("world.bank_booth_note_uim", true) - ServerConstants.DISCORD_GE_WEBHOOK = data.getString("server.discord_webhook", "") + ServerConstants.DISCORD_GE_WEBHOOK = data.getString("integrations.discord_ge_webhook", "") ServerConstants.WATCHDOG_ENABLED = data.getBoolean("server.watchdog_enabled", true) ServerConstants.I_AM_A_CHEATER = data.getBoolean("world.i_want_to_cheat", false) ServerConstants.USE_AUTH = data.getBoolean("server.use_auth", true) ServerConstants.PERSIST_ACCOUNTS = data.getBoolean("server.persist_accounts", true) ServerConstants.DAILY_ACCOUNT_LIMIT = data.getLong("server.daily_accounts_per_ip", 3L).toInt() - ServerConstants.DISCORD_MOD_WEBHOOK = data.getString("server.moderation_webhook", "") + ServerConstants.DISCORD_MOD_WEBHOOK = data.getString("integrations.discord_moderation_webhook", "") 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.MAX_PATHFIND_DISTANCE = data.getLong("server.max_pathfind_dist", 25L).toInt() + ServerConstants.IRONMAN_ICONS = data.getBoolean("world.ironman_icons", 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.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_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", 581).toInt() + ServerConstants.ENHANCED_DEEP_WILDERNESS = data.getBoolean("world.enhanced_deep_wilderness", 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 } private fun Toml.getPath(key: String): String{ try { - return parsePath(getString(key).replace("@data", ServerConstants.DATA_PATH!!)) + return parsePath(getString(key, "@data/").replace("@data", ServerConstants.DATA_PATH!!)) } catch (e: Exception){ - SystemLogger.logErr(this::class.java, "Error parsing key: $key") + log(this::class.java, Log.ERR, "Error parsing key: $key") exitProcess(0) } } diff --git a/Server/src/main/core/game/system/config/ShopParser.kt b/Server/src/main/core/game/system/config/ShopParser.kt index a81ce02f4..1546e3733 100644 --- a/Server/src/main/core/game/system/config/ShopParser.kt +++ b/Server/src/main/core/game/system/config/ShopParser.kt @@ -4,6 +4,8 @@ import core.game.node.entity.player.Player import org.json.simple.JSONArray import org.json.simple.parser.JSONParser import core.ServerConstants +import core.api.log +import core.tools.Log import core.tools.SystemLogger import java.io.FileReader @@ -17,8 +19,8 @@ class ShopParser{ fun openUid(player: Player,uid: Int): Boolean { /* val shop = UID_SHOPS[uid] ?: return false; shop.open(player);*/ - return true; - } + return true +} } fun load(){ @@ -29,6 +31,6 @@ class ShopParser{ count++ } - SystemLogger.logInfo(this::class.java, "Parsed $count shops.") + log(this::class.java, Log.FINE, "Parsed $count shops.") } } \ No newline at end of file diff --git a/Server/src/main/core/game/system/config/XteaParser.kt b/Server/src/main/core/game/system/config/XteaParser.kt index 7c4d1ec7b..9eb4f569c 100644 --- a/Server/src/main/core/game/system/config/XteaParser.kt +++ b/Server/src/main/core/game/system/config/XteaParser.kt @@ -1,6 +1,8 @@ package core.game.system.config import core.ServerConstants +import core.api.log +import core.tools.Log import core.tools.SystemLogger import org.json.simple.JSONArray import org.json.simple.JSONObject @@ -11,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 - // System.out.println("USING SQL REGION KEYS");//Confirms we have unlocked those regions - return REGION_XTEA[regionId] - ?: // System.out.println("USING DEFAULT REGION KEYS FOR REGION " + regionId);//Used to check for missing regions - 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() @@ -36,6 +34,6 @@ class XteaParser { REGION_XTEA[id] = intArrayOf(keys[0],keys[1],keys[2],keys[3]) count++ } - SystemLogger.logInfo(this::class.java, "Parsed $count region keys.") + log(this::class.java, Log.FINE, "Parsed $count region keys.") } } \ No newline at end of file diff --git a/Server/src/main/core/game/system/security/EncryptionManager.java b/Server/src/main/core/game/system/security/EncryptionManager.java index 0b038fb3b..617daa0fc 100644 --- a/Server/src/main/core/game/system/security/EncryptionManager.java +++ b/Server/src/main/core/game/system/security/EncryptionManager.java @@ -19,15 +19,15 @@ public class EncryptionManager { String pass = "lugi"; String salt = BCrypt.gensalt(workload); - System.out.println("pw=" + SystemManager.getEncryption().hashPassword(pass, salt)); System.out.println("Salt=" + salt); + - // System.out.println(BCrypt.checkpw("yes", + // "$2a$12$N3jCPMPEZTisbMf7PG9O7ePgTQEJjhNXGHOJ9zvovT8ibY3Uk.Xlm")); String hash = "$2a$12$N3jCPMPEZTisbMf7PG9O7ePgTQEJjhNXGHOJ9zvovT8ibY3Uk.Xlm"; salt = hash.substring(0, 29); - System.out.println("salt=" + salt); + String hsh = hash.substring(29, hash.length()); - System.out.println("hash=" + hsh); + } /** @@ -105,8 +105,8 @@ public class EncryptionManager { *

To check whether a plaintext password matches one that has * been hashed previously, use the checkpw method:

if * (BCrypt.checkpw(candidate_password, stored_hash))
- *     System.out.println("It matches");
else
    System.out.println("It does not match");
+ + *

The gensalt() method takes an optional parameter (log_rounds) * that determines the computational complexity of the hashing:

* String strong_salt = BCrypt.gensalt(10)
String stronger_salt = diff --git a/Server/src/main/core/game/system/timer/PersistTimer.kt b/Server/src/main/core/game/system/timer/PersistTimer.kt new file mode 100644 index 000000000..977c57438 --- /dev/null +++ b/Server/src/main/core/game/system/timer/PersistTimer.kt @@ -0,0 +1,23 @@ +package core.game.system.timer + +import core.api.* +import org.json.simple.* +import core.game.node.entity.Entity +import kotlin.reflect.full.createInstance + +/** + * A timer implementation with support for saving and loading arbitrary data. See `RSTimer` for more info on timers themselves. +**/ +abstract class PersistTimer (runInterval: Int, identifier: String, isSoft: Boolean = false, isAuto: Boolean = false, flags: Array = arrayOf()) : RSTimer (runInterval, identifier, isSoft, isAuto, flags) { + open fun save (root: JSONObject, entity: Entity) { + root["ticksLeft"] = (nextExecution - getWorldTicks()).toString() + } + + open fun parse (root: JSONObject, entity: Entity) { + runInterval = root["ticksLeft"].toString().toInt() + } + + override fun retrieveInstance() : RSTimer { + return this::class.createInstance() + } +} diff --git a/Server/src/main/core/game/system/timer/RSTimer.kt b/Server/src/main/core/game/system/timer/RSTimer.kt new file mode 100644 index 000000000..75770b7bc --- /dev/null +++ b/Server/src/main/core/game/system/timer/RSTimer.kt @@ -0,0 +1,57 @@ +package core.game.system.timer + +import core.game.node.entity.Entity +import kotlin.reflect.full.createInstance + +/** + * Class for the timer feature of the engine. If you have some task which should repeat periodically, such as applying poison damage, etc, use a timer. + * If the `isAuto` value of a timer is set to `true`, then the timer is automatically added to an entity on creation and started. This is separate from the + * default PersistTimer behavior, which automatically starts the timer only if there's saved data for that timer present. In truth, there's very few + * timers that should have isAuto true. +**/ +abstract class RSTimer (var runInterval: Int, val identifier: String = "generictimer", val isSoft: Boolean = false, val isAuto: Boolean = false, val flags: Array = arrayOf()) { + + /** + * Executed every time the run interval of the timer elapses. + * Execution will be delayed if this timer has `isSoft` set to false (which 99% of timers should) if the entity has a modal open or is otherwise stalled. + * @return whether the timer should execute again. If false, timer will be unregistered from the entity and stop executing. If true, timer will be scheduled to repeat once the runInterval elapses again. + **/ + abstract fun run (entity: Entity) : Boolean + + /** + * Called by core code to determine the amount of time between timer scheduling and the initial (first) run. + * Returns the runInterval by default, but cases such as Farming require an override to sync with realtime clocks. + **/ + 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) {} + + /** + * Called by core code when the timer is being removed. + */ + open fun onRemoval (entity: Entity) {} + + var lastExecution: Int = 0 + var nextExecution: Int = 0 + + open fun retrieveInstance() : RSTimer { + return this::class.createInstance() + } + + /** + * This is called only when getTimer is called by further up code with arguments, otherwise retrieveInstance() is called if no arguments are passed. + **/ + open fun getTimer (vararg args: Any) : RSTimer { + return retrieveInstance() + } +} diff --git a/Server/src/main/core/game/system/timer/TimerFlag.kt b/Server/src/main/core/game/system/timer/TimerFlag.kt new file mode 100644 index 000000000..dd56d225f --- /dev/null +++ b/Server/src/main/core/game/system/timer/TimerFlag.kt @@ -0,0 +1,5 @@ +package core.game.system.timer + +enum class TimerFlag { + ClearOnDeath +} \ No newline at end of file diff --git a/Server/src/main/core/game/system/timer/TimerManager.kt b/Server/src/main/core/game/system/timer/TimerManager.kt new file mode 100644 index 000000000..92bd50e8c --- /dev/null +++ b/Server/src/main/core/game/system/timer/TimerManager.kt @@ -0,0 +1,143 @@ +package core.game.system.timer + +import core.api.* +import core.tools.* +import java.util.ArrayList +import org.json.simple.JSONObject +import core.game.node.entity.Entity +import core.game.node.entity.player.Player + +class TimerManager (val entity: Entity) { + val activeTimers = ArrayList() + val newTimers = ArrayList() + val toRemoveTimers = ArrayList() + + fun registerTimer (timer: RSTimer) { + timer.beforeRegister(entity) + newTimers.add(timer) + timer.onRegister(entity) + } + + fun processTimers () { + activeTimers.removeAll(toRemoveTimers) + newTimers.removeAll(toRemoveTimers) + toRemoveTimers.clear() + + val canRunNormalTimers = (entity !is Player) || !(entity.asPlayer().hasModalOpen() || entity.scripts.delay > getWorldTicks()) + for (timer in activeTimers) { + if (timer.nextExecution > getWorldTicks()) continue + if (!canRunNormalTimers && !timer.isSoft) continue + + try { + if (timer.run(entity)) { + timer.nextExecution = getWorldTicks() + timer.runInterval + } else removeTimer(timer) + } catch (e: Exception) { + log (this::class.java, Log.ERR, "Prematurely removing timer ${timer::class.java.simpleName} from ${entity.name} because it threw an exception when ran. Exception follows:") + e.printStackTrace() + removeTimer(timer) + } + } + + for (timer in newTimers) { + activeTimers.add(timer) + timer.nextExecution = timer.getInitialRunDelay() + getWorldTicks() + } + + newTimers.clear() + } + + fun clearTimers () { + activeTimers.clear() + newTimers.clear() + toRemoveTimers.clear() + } + + fun onEntityDeath() { + for (timer in activeTimers) { + if (timer.flags.contains(TimerFlag.ClearOnDeath)) + removeTimer(timer) + } + } + + fun saveTimers (root: JSONObject) { + for (timer in activeTimers) { + if (timer !is PersistTimer) continue + val obj = JSONObject() + timer.save(obj, entity) + root [timer.identifier] = obj + } + for (timer in newTimers) { + if (timer !is PersistTimer) continue + val obj = JSONObject() + timer.save(obj, entity) + root [timer.identifier] = obj + } + } + + fun parseTimers (root: JSONObject) { + for ((identifier, dataObj) in root) { + val data = dataObj as JSONObject + val timer = TimerRegistry.getTimerInstance (identifier.toString()) as? PersistTimer + + if (timer == null) { + log (this::class.java, Log.ERR, "Tried to load data for persistent timer identified by $identifier, but no such timer seems to exist.") + continue + } + + timer.parse(data, entity) + registerTimer(entity, timer) + } + } + + inline fun removeTimer () { + for (timer in activeTimers) + if (timer is T) + removeTimer(timer) + for (timer in newTimers) + if (timer is T) + removeTimer(timer) + } + + inline fun getTimer () : T? { + var t: T? = null + for (timer in activeTimers) + if (timer is T) + t = timer + for (timer in newTimers) + if (timer is T) + t = timer + if (t == null) return null + if (toRemoveTimers.contains(t)) + return null + return t + } + + fun getTimer (identifier: String): RSTimer? { + var t: RSTimer? = null + for (timer in activeTimers) + if (timer.identifier == identifier) + t = timer + for (timer in newTimers) + if (timer.identifier == identifier) + t = timer + if (t == null) return null + if (toRemoveTimers.contains(t)) return null + return t + } + + fun removeTimer (identifier: String) { + for (timer in activeTimers) + if (timer.identifier == identifier) + removeTimer(timer) + for (timer in newTimers) + if (timer.identifier == identifier) + removeTimer(timer) + } + + fun removeTimer (timer: RSTimer) { + timer.nextExecution = Int.MAX_VALUE + toRemoveTimers.add(timer) + try { timer.onRemoval(entity) } catch (e: Exception) { e.printStackTrace() } + } +} diff --git a/Server/src/main/core/game/system/timer/TimerRegistry.kt b/Server/src/main/core/game/system/timer/TimerRegistry.kt new file mode 100644 index 000000000..3aaa46878 --- /dev/null +++ b/Server/src/main/core/game/system/timer/TimerRegistry.kt @@ -0,0 +1,49 @@ +package core.game.system.timer + +import java.util.* +import core.api.* +import core.tools.* +import core.game.node.entity.Entity +import core.game.node.entity.player.Player + +object TimerRegistry { + val timerMap = HashMap() + val autoTimers = ArrayList() + + fun registerTimer (timer: RSTimer) { + log (this::class.java, Log.WARN, "Registering timer ${timer::class.java.simpleName}") + if (timerMap.containsKey(timer.identifier.lowercase())) { + log (this::class.java, Log.ERR, "Timer identifier ${timer.identifier} already in use by ${timerMap[timer.identifier.lowercase()]!!::class.java.simpleName}! Not loading ${timer::class.java.simpleName}!") + return + } + timerMap[timer.identifier.lowercase()] = timer + if (timer.isAuto) autoTimers.add(timer) + } + + fun getTimerInstance (identifier: String, vararg args: Any) : RSTimer? { + var t = timerMap[identifier.lowercase()] + if (args.size > 0) + return t?.getTimer(*args) + else + return t?.retrieveInstance() + } + + @JvmStatic + fun addAutoTimers (entity: Entity) { + for (timer in autoTimers) { + if (!hasTimerActive (entity, timer.identifier)) + registerTimer (entity, timer.retrieveInstance()) + } + } + + inline fun getTimerInstance (vararg args: Any) : T? { + for ((_, inst) in timerMap) + if (inst is T) { + if (args.size > 0) + return inst.getTimer(*args) as? T + else + return inst.retrieveInstance() as? T + } + return null + } +} diff --git a/Server/src/main/core/game/system/timer/impl/AntiMacro.kt b/Server/src/main/core/game/system/timer/impl/AntiMacro.kt new file mode 100644 index 000000000..35d3ca2cc --- /dev/null +++ b/Server/src/main/core/game/system/timer/impl/AntiMacro.kt @@ -0,0 +1,187 @@ +package core.game.system.timer.impl + +import content.global.ame.RandomEventNPC +import content.global.ame.RandomEvents +import core.api.* +import core.game.node.entity.Entity +import core.game.node.entity.player.Player +import core.game.node.entity.player.info.Rights +import core.game.node.item.Item +import core.game.system.command.Privilege +import core.game.system.timer.PersistTimer +import core.game.world.map.zone.ZoneRestriction +import core.game.world.repository.Repository +import core.tools.RandomFunction +import core.tools.colorize +import org.json.simple.JSONObject + +class AntiMacro : PersistTimer(0, "antimacro", isAuto = true), Commands { + var paused = true + var nextRandom: RandomEvents? = null + + override fun run(entity: Entity): Boolean { + //Snowscape: prevent random events entirely + entity.timers.removeTimer(this) + return false + + if (entity !is Player) return false + + setNextExecution() + + if (!canSpawn(entity)) return true + val eventSelection = rollEventPool(entity) + val eventNpc = eventSelection.npc.create(entity, eventSelection.loot, eventSelection.type) + if (eventNpc.spawnLocation == null) { + entity.debug("[AntiMacro] Attempted to spawn random, but spawnLoc was null.") + return true + } + + eventNpc.init() + setAttribute(entity, EVENT_NPC, eventNpc) + entity.debug("[AntiMacro] Fired ${eventSelection.name}.") + return true + } + + override fun onRegister(entity: Entity) { + if (entity !is Player || entity.isArtificial) + entity.timers.removeTimer(this) + //if (entity is Player && entity.rights == Rights.ADMINISTRATOR) + if (entity is Player) + paused = true + + if (runInterval == 0) + setNextExecution() + } + + override fun save(root: JSONObject, entity: Entity) { + root["ticksRemaining"] = (nextExecution - getWorldTicks()).toString() + } + + override fun parse(root: JSONObject, entity: Entity) { + runInterval = (root["ticksRemaining"]?.toString()?.toIntOrNull() ?: 0) + nextExecution = getWorldTicks() + runInterval + } + + private fun canSpawn(entity: Entity) : Boolean { + if (entity.zoneMonitor.isRestricted(ZoneRestriction.RANDOM_EVENTS)) + return false + + val current = getAttribute(entity, EVENT_NPC, null) + if (current != null) + return false + + if (paused) + return false + + return true + } + + private fun setNextExecution() { + runInterval = RandomFunction.random(MIN_DELAY_TICKS, MAX_DELAY_TICKS + 1) + nextExecution = getWorldTicks() + runInterval + } + + private fun rollEventPool(entity: Entity) : RandomEvents { + if (nextRandom != null) { + val result = nextRandom!! + nextRandom = null + return result + } + + val skillBasedRandom = RandomEvents.getSkillBasedRandomEvent(entity.skills.lastTrainedSkill) + val normalRandom = RandomEvents.getNonSkillRandom() + val roll = RandomFunction.random(100) + + if (roll >= 65 && skillBasedRandom != null && getWorldTicks() - entity.skills.lastXpGain < 250) + return skillBasedRandom + return normalRandom + } + + override fun defineCommands() { + define("revent", Privilege.ADMIN, "::revent [-p] player name [-e event name]", "Spawns a random event for the target player.
Optional -e parameter to pass a specific event.") {player, args -> + if (args.size == 1) { + val possible = RandomEvents.values() + for (event in possible) { + notify(player, event.name.lowercase()) + } + return@define + } + + val arg = parseCommandArgs(args.joinToString(" ")) + val target = Repository.getPlayerByName(arg.targetPlayer) + if (target == null) + reject(player, "Unable to find user ${arg.targetPlayer}.") + if (target!!.rights == Rights.ADMINISTRATOR) { + unpause(target) + sendMessage(target, colorize("%RAntiMacro timer unpaused until next login.")) + } + + forceEvent(target!!, arg.targetEvent) + } + } + + data class CommandArgs (val targetPlayer: String, val targetEvent: RandomEvents?) + + companion object { + const val EVENT_NPC = "re-npc" + const val MIN_DELAY_TICKS = 3000 + const val MAX_DELAY_TICKS = 9000 + + fun terminateEventNpc (player: Player) { + getEventNpc(player)?.terminate() + } + + fun rollEventLoot (player: Player) : ArrayList { + return getEventNpc(player)?.loot?.roll(player) ?: ArrayList() + } + + fun getEventNpc (player: Player) : RandomEventNPC? { + return getAttribute(player, EVENT_NPC, null) + } + + fun pause (player: Player) { + val timer = getTimer(player) ?: return + timer.paused = true + } + + fun unpause (player: Player) { + val timer = getTimer(player) ?: return + timer.paused = false + } + + fun forceEvent (player: Player, event: RandomEvents? = null) { + val timer = getTimer(player) ?: return + timer.nextExecution = getWorldTicks() + timer.nextRandom = event + } + + fun parseCommandArgs (args: String, commandName: String = "revent") : CommandArgs { + val tokens = args.split(" ") + val modeTokens = arrayOf("-p", "-e") + + var userString = "" + var eventString = "" + var lastMode = "-p" + + for (token in tokens) { + when (token) { + commandName -> continue + in modeTokens -> lastMode = token + else -> when (lastMode) { + "-p" -> userString += "$token " + "-e" -> eventString += "$token " + } + } + } + + val username = userString.trim().lowercase().replace(" ", "_") + val eventName = eventString.trim().uppercase().replace(" ", "_") + + var event: RandomEvents? = null + + try { event = RandomEvents.valueOf(eventName) } catch (_: Exception) {} + + return CommandArgs(username, event) + } + } +} \ No newline at end of file diff --git a/Server/src/main/core/game/system/timer/impl/Disease.kt b/Server/src/main/core/game/system/timer/impl/Disease.kt new file mode 100644 index 000000000..cfc72ac84 --- /dev/null +++ b/Server/src/main/core/game/system/timer/impl/Disease.kt @@ -0,0 +1,46 @@ +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 core.game.node.entity.combat.ImpactHandler +import core.tools.RandomFunction +import org.json.simple.* + +class Disease : PersistTimer (30, "disease", flags = arrayOf(TimerFlag.ClearOnDeath)) { + var hitsLeft = 25 + + override fun save (root: JSONObject, entity: Entity) { + root["hitsLeft"] = hitsLeft.toString() + } + + override fun parse (root: JSONObject, entity: Entity) { + hitsLeft = root["hitsLeft"].toString().toInt() + } + + override fun beforeRegister (entity: Entity) { + if (hasTimerActive(entity)) + removeTimer(entity, this) + else if (entity is Player) + sendMessage(entity, "You have become diseased.") + } + + override fun run (entity: Entity) : Boolean { + var damage = RandomFunction.random(1, 5) + entity.impactHandler.manualHit(entity,damage,ImpactHandler.HitsplatType.DISEASE) + var skillId = RandomFunction.random(24) + if(skillId == 3) skillId-- + entity.skills.updateLevel(skillId,-damage,0) + if (--hitsLeft == 0 && entity is Player) + sendMessage(entity, "The disease has wore off.") + return hitsLeft > 0 + } + + override fun getTimer (vararg args: Any) : RSTimer { + val inst = Disease() + inst.hitsLeft = args.getOrNull(0) as? Int ?: 25 + return inst + } +} 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 new file mode 100644 index 000000000..97fd002f8 --- /dev/null +++ b/Server/src/main/core/game/system/timer/impl/Frozen.kt @@ -0,0 +1,47 @@ +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 + } + } + + 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/FrozenImmunity.kt b/Server/src/main/core/game/system/timer/impl/FrozenImmunity.kt new file mode 100644 index 000000000..250dbfe28 --- /dev/null +++ b/Server/src/main/core/game/system/timer/impl/FrozenImmunity.kt @@ -0,0 +1,38 @@ +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 FrozenImmunity : PersistTimer (1, "frozen:immunity", flags = arrayOf(TimerFlag.ClearOnDeath)) { + var ticksRemaining = 0 + + override fun save (root: JSONObject, entity: Entity) { + root["ticksLeft"] = (nextExecution - getWorldTicks()).toString() + } + + override fun parse (root: JSONObject, entity: Entity) { + runInterval = root["ticksLeft"].toString().toInt() + } + + override fun onRegister (entity: Entity) { + if (hasTimerActive(entity)) { + removeTimer(entity) + } + (entity as? Player)?.debug("Applied frozen immunity for $runInterval ticks.") + } + + override fun run (entity: Entity) : Boolean { + (entity as? Player)?.debug("Removed frozen immunity") + return false + } + + override fun getTimer (vararg args: Any) : RSTimer { + val inst = FrozenImmunity() + inst.runInterval = args.getOrNull(0) as? Int ?: 7 + return inst + } +} diff --git a/Server/src/main/core/game/system/timer/impl/HealOverTime.kt b/Server/src/main/core/game/system/timer/impl/HealOverTime.kt new file mode 100644 index 000000000..da5016e86 --- /dev/null +++ b/Server/src/main/core/game/system/timer/impl/HealOverTime.kt @@ -0,0 +1,47 @@ +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 org.json.simple.* +import kotlin.math.min + +class HealOverTime : PersistTimer (1, "healovertime", flags = arrayOf(TimerFlag.ClearOnDeath)) { + var healRemaining = 0 + var healPerTick = 0 + + override fun run (entity: Entity) : Boolean { + var amt = min (healRemaining, healPerTick) + healRemaining -= amt + entity.skills.heal(amt) + return healRemaining > 0 + } + + override fun save (root: JSONObject, entity: Entity) { + super.save (root, entity) + root["healRemaining"] = healRemaining.toString() + root["healPerTick"] = healPerTick.toString() + } + + override fun parse (root: JSONObject, entity: Entity) { + super.parse (root, entity) + healRemaining = root["healRemaining"].toString().toInt() + healPerTick = root["healPerTick"].toString().toInt() + } + + override fun onRegister (entity: Entity) { + if (hasTimerActive(entity)) + removeTimer(entity, this) + if (hasTimerActive(entity)) + removeTimer(entity, this) + } + + override fun getTimer (vararg args: Any) : RSTimer { + val t = HealOverTime() + t.runInterval = args.getOrNull(0) as? Int ?: 100 + t.healRemaining = args.getOrNull(1) as? Int ?: 10 + t.healPerTick = args.getOrNull(2) as? Int ?: 2 + return t + } +} diff --git a/Server/src/main/core/game/system/timer/impl/Miasmic.kt b/Server/src/main/core/game/system/timer/impl/Miasmic.kt new file mode 100644 index 000000000..9af8ee3ad --- /dev/null +++ b/Server/src/main/core/game/system/timer/impl/Miasmic.kt @@ -0,0 +1,30 @@ +package core.game.system.timer.impl + +import core.api.hasTimerActive +import core.api.registerTimer +import core.api.removeTimer +import core.api.spawnTimer +import core.game.node.entity.Entity +import core.game.system.timer.PersistTimer +import core.game.system.timer.RSTimer +import core.game.system.timer.TimerFlag + +class Miasmic : PersistTimer (1, "miasmic", flags = arrayOf(TimerFlag.ClearOnDeath)) { + override fun run (entity: Entity) : Boolean { + registerTimer (entity, spawnTimer(entity, 7)) + return false + } + + override fun beforeRegister (entity: Entity) { + if (hasTimerActive(entity)) + removeTimer(entity, this) + if (hasTimerActive(entity)) + removeTimer(entity, this) + } + + override fun getTimer (vararg args: Any) : RSTimer { + val t = Miasmic() + t.runInterval = args.getOrNull(0) as? Int ?: 100 + return t + } +} diff --git a/Server/src/main/core/game/system/timer/impl/MiasmicImmunity.kt b/Server/src/main/core/game/system/timer/impl/MiasmicImmunity.kt new file mode 100644 index 000000000..2ce41cb34 --- /dev/null +++ b/Server/src/main/core/game/system/timer/impl/MiasmicImmunity.kt @@ -0,0 +1,25 @@ +package core.game.system.timer.impl + +import core.api.hasTimerActive +import core.api.removeTimer +import core.game.node.entity.Entity +import core.game.system.timer.PersistTimer +import core.game.system.timer.RSTimer +import core.game.system.timer.TimerFlag + +class MiasmicImmunity : PersistTimer (1, "miasmic:immunity", flags = arrayOf(TimerFlag.ClearOnDeath)) { + override fun run (entity: Entity) : Boolean { + return false + } + + override fun onRegister (entity: Entity) { + if (hasTimerActive(entity)) + removeTimer(entity) + } + + override fun getTimer (vararg args: Any) : RSTimer { + val t = MiasmicImmunity() + t.runInterval = args.getOrNull(0) as? Int ?: 100 + return t + } +} diff --git a/Server/src/main/core/game/system/timer/impl/Poison.kt b/Server/src/main/core/game/system/timer/impl/Poison.kt new file mode 100644 index 000000000..6f87d40a9 --- /dev/null +++ b/Server/src/main/core/game/system/timer/impl/Poison.kt @@ -0,0 +1,73 @@ +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.node.entity.combat.ImpactHandler +import core.game.world.repository.Repository +import org.json.simple.* + +/** + * A timer that replicates the behavior of poison mechanics. Runs every 30 ticks. + * If you wish to construct an instance of this Timer, consider using the ContentAPI function `applyPoison` instead. + * If that doesn't suit your needs, then use `Poison.getTimer (source, severity)` and then `registerTimer(entity, timer)` + * Poison mechanics are driven by the `severity` value, which is not a 1:1 representation of damage. Instead the formula `floor((severity + 4) / 5)` is used. + * Every time the damage is applied, the severity decreases by 1. Poison ends when severity reaches 0. + * Example: 30 Severity. Deals 6 damage 5 times, then 5 damage 5 times, and so on. +**/ +class Poison : PersistTimer (30, "poison", flags = arrayOf(TimerFlag.ClearOnDeath)) { + 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() + } + + override fun parse (root: JSONObject, entity: Entity) { + val uid = root["source-uid"].toString().toInt() + damageSource = Repository.getPlayerByUid (uid) ?: entity + severity = root["severity"].toString().toInt() + } + + override fun onRegister (entity: Entity) { + if (entity is Player) { + sendMessage(entity, "You have been poisoned.") + entity.debug ("[Poison] -> Received for $severity severity.") + } + if (damageSource is Player) + (damageSource as? Player)?.debug ("[Poison] -> Applied for $severity severity.") + } + + override fun run (entity: Entity) : Boolean { + entity.impactHandler.manualHit ( + damageSource, + getDamageFromSeverity (severity--), + ImpactHandler.HitsplatType.POISON + ) + + if (severity == 0 && entity is Player) + sendMessage(entity, "The poison has worn off.") + return severity > 0 + } + + override fun getTimer (vararg args: Any) : RSTimer { + val timer = Poison() + timer.damageSource = args[0] as? Entity ?: return timer + timer.severity = args[1] as? Int ?: return timer + return timer + } + + private fun getDamageFromSeverity (severity: Int) : Int { + return (severity + 4) / 5 + } +} diff --git a/Server/src/main/core/game/system/timer/impl/PoisonImmunity.kt b/Server/src/main/core/game/system/timer/impl/PoisonImmunity.kt new file mode 100644 index 000000000..1695fce7b --- /dev/null +++ b/Server/src/main/core/game/system/timer/impl/PoisonImmunity.kt @@ -0,0 +1,51 @@ +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 poison immunity mechanics. Runs every tick. + * Will notify the player of various levels of remaining poison 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 PoisonImmunity : PersistTimer (1, "poison: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 onRegister (entity: Entity) { + removeTimer(entity) + } + + 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) { + sendMessage(entity, colorize("%RYour poison immunity has expired.")) + playAudio(entity, Sounds.DRAGON_POTION_FINISHED_2607) + } + + return ticksRemaining > 0 + } + + override fun getTimer (vararg args: Any) : RSTimer { + val t = PoisonImmunity() + t.ticksRemaining = args.getOrNull(0) as? Int ?: 100 + return t + } +} diff --git a/Server/src/main/core/game/system/timer/impl/SkillRestore.kt b/Server/src/main/core/game/system/timer/impl/SkillRestore.kt new file mode 100644 index 000000000..b6b456618 --- /dev/null +++ b/Server/src/main/core/game/system/timer/impl/SkillRestore.kt @@ -0,0 +1,101 @@ +package core.game.system.timer.impl + +import core.api.* +import core.api.Event +import core.game.node.entity.Entity +import core.game.node.entity.player.Player +import core.game.node.entity.player.link.prayer.PrayerType +import core.game.node.entity.skill.Skills +import core.game.system.timer.* +import core.game.event.* + +import org.rs09.consts.Items + +class SkillRestore : RSTimer (1, "skillrestore", isAuto = true, isSoft = true) { + val ticksSinceLastRestore = IntArray (25) + val restoreTicks = IntArray (25) { 100 } + + override fun run (entity: Entity) : Boolean { + var skills = entity.skills + + for (i in 0 until 24) { + 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)) + } else { + val max = getStatLevel (entity, i) + val current = getDynLevel (entity, i) + + if (current != max) + skills.updateLevel (i, if (current < max) 1 else -1, max) + } + ticksSinceLastRestore[i] = 0 + } + } + + if (entity is Player && ticksSinceLastRestore[24]++ >= 50) { + entity.settings.setSpecialEnergy (kotlin.math.min (100, entity.settings.specialEnergy + 10)) + ticksSinceLastRestore[24] = 0 + } + + return true + } + + override fun onRegister (entity: Entity) { + entity.hook (Event.PrayerActivated, PrayerActivatedHook) + entity.hook (Event.PrayerDeactivated, PrayerDeactivatedHook) + (entity as? Player)?.debug("Registered skill restoration timer.") + } + + private fun getHealAmount (entity: Entity) : Int { + if (entity !is Player) return 1 + + val gloves = getItemFromEquipment (entity, EquipmentSlot.HANDS) + if (gloves == null || gloves.id != Items.REGEN_BRACELET_11133) + return 1 + else return 2 + } + + object PrayerActivatedHook : EventHook { + override fun process (entity: Entity, event: PrayerActivatedEvent) { + val restore = getOrStartTimer (entity) + + when (event.type) { + PrayerType.RAPID_HEAL -> { + restore.restoreTicks [Skills.HITPOINTS] = 50 + restore.ticksSinceLastRestore [Skills.HITPOINTS] = 0 + } + PrayerType.RAPID_RESTORE -> { + for (i in 0 until 24) { + if (i == Skills.HITPOINTS || i == Skills.PRAYER || i == Skills.SUMMONING) continue + restore.restoreTicks [i] = 50 + restore.ticksSinceLastRestore [i] = 0 + } + } + else -> {} + } + } + } + + object PrayerDeactivatedHook : EventHook { + override fun process (entity: Entity, event: PrayerDeactivatedEvent) { + val restore = getOrStartTimer (entity) + + when (event.type) { + PrayerType.RAPID_HEAL -> { + restore.restoreTicks [Skills.HITPOINTS] = 100 + restore.ticksSinceLastRestore [Skills.HITPOINTS] = 0 + } + PrayerType.RAPID_RESTORE -> { + for (i in 0 until 24) { + if (i == Skills.HITPOINTS || i == Skills.PRAYER || i == Skills.SUMMONING) continue + restore.restoreTicks [i] = 100 + restore.ticksSinceLastRestore [i] = 0 + } + } + else -> {} + } + } + } +} diff --git a/Server/src/main/core/game/system/timer/impl/Skulled.kt b/Server/src/main/core/game/system/timer/impl/Skulled.kt new file mode 100644 index 000000000..fee4474ab --- /dev/null +++ b/Server/src/main/core/game/system/timer/impl/Skulled.kt @@ -0,0 +1,27 @@ +package core.game.system.timer.impl + +import core.game.node.entity.Entity +import core.game.node.entity.player.Player +import core.game.system.timer.PersistTimer +import core.game.system.timer.RSTimer +import core.game.system.timer.TimerFlag + +class Skulled : PersistTimer (1, "skulled", flags = arrayOf(TimerFlag.ClearOnDeath)) { + override fun onRegister (entity: Entity) { + if (entity !is Player) return + entity.skullManager.setSkullIcon(0) + entity.skullManager.setSkulled(true) + } + + override fun run (entity: Entity) : Boolean { + if (entity !is Player) return false + entity.skullManager.reset() + return false + } + + override fun getTimer (vararg args: Any) : RSTimer { + val t = Skulled() + t.runInterval = args.getOrNull(0) as? Int ?: 500 + return t + } +} diff --git a/Server/src/main/core/game/system/timer/impl/Teleblock.kt b/Server/src/main/core/game/system/timer/impl/Teleblock.kt new file mode 100644 index 000000000..47f33a8d4 --- /dev/null +++ b/Server/src/main/core/game/system/timer/impl/Teleblock.kt @@ -0,0 +1,25 @@ +package core.game.system.timer.impl + +import core.api.sendMessage +import core.game.node.entity.Entity +import core.game.node.entity.player.Player +import core.game.system.timer.PersistTimer +import core.game.system.timer.RSTimer +import core.game.system.timer.TimerFlag + +class Teleblock : PersistTimer (1, "teleblock", flags = arrayOf(TimerFlag.ClearOnDeath)) { + override fun run (entity: Entity) : Boolean { + return false + } + + override fun onRegister (entity: Entity) { + if (entity !is Player) return + sendMessage (entity, "You have been teleblocked.") + } + + override fun getTimer (vararg args: Any) : RSTimer { + val t = Teleblock() + t.runInterval = args.getOrNull(0) as? Int ?: 100 + return t + } +} diff --git a/Server/src/main/core/game/world/DeadlockDetector.kt b/Server/src/main/core/game/world/DeadlockDetector.kt index cd2f968c8..f64d97ec6 100644 --- a/Server/src/main/core/game/world/DeadlockDetector.kt +++ b/Server/src/main/core/game/world/DeadlockDetector.kt @@ -1,11 +1,13 @@ package core.game.world +import core.api.log +import core.tools.Log import core.tools.SystemLogger import java.lang.management.ManagementFactory class DeadlockDetector : Runnable { override fun run() { - val mbean = ManagementFactory.getThreadMXBean(); + val mbean = ManagementFactory.getThreadMXBean() val deadLockedThreads = mbean.findDeadlockedThreads() @@ -17,9 +19,9 @@ class DeadlockDetector : Runnable { if (threadInfo != null) { for (thread in Thread.getAllStackTraces().keys) { if (thread.id == threadInfo.threadId) { - SystemLogger.logErr(this::class.java, threadInfo.toString().trim()) + log(this::class.java, Log.ERR, threadInfo.toString().trim()) for (ste in thread.stackTrace) { - SystemLogger.logErr(this::class.java, "\t" + ste.toString().trim { it <= ' ' }) + log(this::class.java, Log.ERR, "\t" + ste.toString().trim { it <= ' ' }) } } } diff --git a/Server/src/main/core/game/world/GameSettings.kt b/Server/src/main/core/game/world/GameSettings.kt index 71352c6c2..c75a55efd 100644 --- a/Server/src/main/core/game/world/GameSettings.kt +++ b/Server/src/main/core/game/world/GameSettings.kt @@ -82,6 +82,9 @@ class GameSettings var enable_doubling_money_scammers: Boolean, var wild_pvp_enabled: Boolean, var jad_practice_enabled: Boolean, + var ge_announcement_limit: Int, + var smartpathfinder_bfs: Boolean, + var enable_castle_wars: Boolean, /**"Lobby" interface * The message of the week models to display @@ -136,6 +139,9 @@ class GameSettings val enable_doubling_money_scammers = if(data.containsKey("enable_doubling_money_scammers")) data["enable_doubling_money_scammers"] as Boolean else false val wild_pvp_enabled = if(data.containsKey("wild_pvp_enabled")) data["wild_pvp_enabled"] as Boolean else true val jad_practice_enabled = if(data.containsKey("jad_practice_enabled")) data["jad_practice_enabled"] as Boolean else true + val ge_announcement_limit = data["ge_announcement_limit"].toString().toInt() + val smartpathfinder_bfs = if(data.containsKey("smartpathfinder_bfs")) data["smartpathfinder_bfs"] as Boolean else false + val enable_castle_wars = if(data.containsKey("enable_castle_wars")) data["enable_castle_wars"] as Boolean else false val allow_token_purchase = data["allow_token_purchase"] as Boolean val message_of_the_week_identifier = data["message_of_the_week_identifier"].toString().toInt() val message_of_the_week_text = data["message_of_the_week_text"].toString() @@ -165,6 +171,9 @@ class GameSettings enable_doubling_money_scammers, wild_pvp_enabled, jad_practice_enabled, + ge_announcement_limit, + smartpathfinder_bfs, + enable_castle_wars, message_of_the_week_identifier, message_of_the_week_text ) @@ -188,4 +197,4 @@ class GameSettings return properties } } -} \ No newline at end of file +} diff --git a/Server/src/main/core/game/world/GameWorld.kt b/Server/src/main/core/game/world/GameWorld.kt index 3778ddf4c..2c0b55e2c 100644 --- a/Server/src/main/core/game/world/GameWorld.kt +++ b/Server/src/main/core/game/world/GameWorld.kt @@ -17,12 +17,11 @@ import core.ServerConstants import core.ServerStore import core.auth.AuthProvider import core.auth.Auth -import core.tools.SystemLogger -import core.tools.SystemLogger.logInfo import core.game.system.config.ConfigParser import core.game.world.repository.Repository import core.plugin.ClassScanner import core.storage.AccountStorageProvider +import core.tools.Log import core.worker.MajorUpdateWorker import java.text.SimpleDateFormat import java.util.* @@ -163,13 +162,14 @@ object GameWorld { */ @Throws(Throwable::class) fun prompt(run: Boolean, directory: String?){ - logInfo(GameWorld::class.java, "Prompting ${settings?.name} Game World...") + log(GameWorld::class.java, Log.FINE, "Prompting ${settings?.name} Game World...") Cache.init(ServerConstants.CACHE_PATH) //go overboard with checks to make sure dev mode authenticator never triggers on live Auth.configure() ConfigParser().prePlugin() ClassScanner.scanClasspath() ClassScanner.loadPureInterfaces() + ClassScanner.loadTimers() val s = worldPersists.filterIsInstance().first() s.parse() worldPersists.filter { it !is ServerStore }.forEach { it.parse() } @@ -187,7 +187,6 @@ object GameWorld { } System.gc() - SystemLogger.initTradeLogger() } /** @@ -218,4 +217,4 @@ object GameWorld { generateLocation() } else random_location } -} \ No newline at end of file +} diff --git a/Server/src/main/core/game/world/ImmerseWorld.kt b/Server/src/main/core/game/world/ImmerseWorld.kt index 22b20c1ef..2adee483f 100644 --- a/Server/src/main/core/game/world/ImmerseWorld.kt +++ b/Server/src/main/core/game/world/ImmerseWorld.kt @@ -11,17 +11,26 @@ 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 { override fun startup() { - spawnBots() + if(GameWorld.settings?.max_adv_bots!! > 0) { + spawnBots() + } } companion object { 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) @@ -34,16 +43,17 @@ class ImmerseWorld : StartupListener { immerseWilderness() immerseFishingGuild() immerseAdventurer() + immerseFalador() // immerseSlayer() immerseGE() } } } - 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() } } @@ -52,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() { @@ -209,6 +222,10 @@ class ImmerseWorld : StartupListener { CoalMiner(), skillingBotAssembler.produce(SkillingBotAssembler.Wealth.POOR, Location.create(3037, 9737, 0)) ) + GeneralBotCreator( + CannonballSmelter(), + skillingBotAssembler.produce(SkillingBotAssembler.Wealth.AVERAGE, Location.create(3013, 3356, 0)) + ) } fun immerseSlayer() { diff --git a/Server/src/main/core/game/world/PulseRunner.kt b/Server/src/main/core/game/world/PulseRunner.kt index a1feea43d..e320cd7c9 100644 --- a/Server/src/main/core/game/world/PulseRunner.kt +++ b/Server/src/main/core/game/world/PulseRunner.kt @@ -1,9 +1,13 @@ package core.game.world +import core.api.log import core.game.system.task.Pulse import core.game.bots.GeneralBotCreator +import core.tools.Log import core.tools.SystemLogger +import core.ServerConstants import java.util.concurrent.LinkedBlockingQueue +import core.integrations.grafana.* class PulseRunner { private val pulses = LinkedBlockingQueue() @@ -17,22 +21,40 @@ class PulseRunner { fun updateAll() { val pulseCount = pulses.size + var totalTimeBotPulses = 0 + var totalTimeOtherPulses = 0 for (i in 0 until pulseCount) { val pulse = pulses.take() val elapsedTime = measure { try { - if (!pulse.update()) { + if (!pulse.update() && pulse.isRunning) { pulses.add(pulse) } } catch (e: Exception) { - SystemLogger.logErr(this::class.java, "Pulse execution error. Stack trace below.") + log(this::class.java, Log.ERR, "Pulse execution error. Stack trace below.") e.printStackTrace() } } + var pulseName = pulse::class.java.name + + if (pulse is GeneralBotCreator.BotScriptPulse || pulseName.contains("ScriptAPI")) { + totalTimeBotPulses += elapsedTime.toInt() + } else { + totalTimeOtherPulses += elapsedTime.toInt() + } + + Grafana.addPulseLength (pulseName, elapsedTime.toInt()) + Grafana.countPulse (pulseName) + notifyIfTooLong(pulse, elapsedTime) } + + if (ServerConstants.GRAFANA_LOGGING) { + Grafana.botPulseTime = totalTimeBotPulses + Grafana.otherPulseTime = totalTimeOtherPulses + } } private fun measure(logic: () -> Unit): Long { @@ -46,16 +68,16 @@ class PulseRunner { private fun notifyIfTooLong(pulse: Pulse, elapsedTime: Long) { if (elapsedTime >= 100) { if (pulse is GeneralBotCreator.BotScriptPulse) { - SystemLogger.logWarn(this::class.java, "CRITICALLY long bot-script tick - ${pulse.botScript.javaClass.name} took $elapsedTime ms") + log(this::class.java, Log.WARN, "CRITICALLY long bot-script tick - ${pulse.botScript.javaClass.name} took $elapsedTime ms") } else { - SystemLogger.logWarn(this::class.java, "CRITICALLY long running pulse - ${pulse.javaClass.name} took $elapsedTime ms") + log(this::class.java, Log.WARN, "CRITICALLY long running pulse - ${pulse.javaClass.name} took $elapsedTime ms") } } else if (elapsedTime >= 30) { if (pulse is GeneralBotCreator.BotScriptPulse) { - SystemLogger.logWarn(this::class.java, "Long bot-script tick - ${pulse.botScript.javaClass.name} took $elapsedTime ms") + log(this::class.java, Log.WARN, "Long bot-script tick - ${pulse.botScript.javaClass.name} took $elapsedTime ms") } else { - SystemLogger.logWarn(this::class.java, "Long running pulse - ${pulse.javaClass.name} took $elapsedTime ms") + log(this::class.java, Log.WARN, "Long running pulse - ${pulse.javaClass.name} took $elapsedTime ms") } } } -} \ No newline at end of file +} diff --git a/Server/src/main/core/game/world/map/BuildRegionChunk.java b/Server/src/main/core/game/world/map/BuildRegionChunk.java index 1c7bf97a3..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,8 @@ 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; import core.net.packet.IoBuffer; @@ -13,6 +15,11 @@ import core.net.packet.out.ClearScenery; import core.net.packet.out.ConstructGroundItem; import core.net.packet.out.ConstructScenery; +import static core.api.ContentAPIKt.log; + +import java.util.ArrayList; +import java.util.Arrays; + /** * A region chunk, used for easily modifying objects. * @author Emperor @@ -23,7 +30,7 @@ public class BuildRegionChunk extends RegionChunk { /** * The maximum amount of objects to be stored on one tile in the chunk. */ - public static final int ARRAY_SIZE = 5; + public static final int ARRAY_SIZE = 10; /** * The list of changes made. @@ -39,13 +46,17 @@ public class BuildRegionChunk extends RegionChunk { public BuildRegionChunk(Location base, int rotation, RegionPlane plane) { super(base, rotation, plane); this.objects = new Scenery[ARRAY_SIZE][8][8]; - for (int x = 0; x < SIZE; x++) { - for (int y = 0; y < SIZE; y++) { - if(super.objects[x][y] != null) { - this.objects[0][x][y] = new Scenery(super.objects[x][y]); - } - } - } + } + + public BuildRegionChunk(Location base, int rotation, RegionPlane plane, Scenery[][] objects) { + this(base, rotation, plane); + for (int x = 0; x < SIZE; x++) { + for (int y = 0; y < SIZE; y++) { + if(objects[x][y] != null) { + this.objects[0][x][y] = new Scenery(objects[x][y]); + } + } + } } @Override @@ -66,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; } } } @@ -83,7 +92,7 @@ public class BuildRegionChunk extends RegionChunk { @Override public void rotate(Direction direction) { if (rotation != 0) { - SystemLogger.logErr(this.getClass(), "Region chunk was already rotated!"); + log(this.getClass(), Log.ERR, "Region chunk was already rotated!"); return; } Scenery[][][] copy = new Scenery[ARRAY_SIZE][SIZE][SIZE]; @@ -92,15 +101,24 @@ 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(); - rotation = (direction.toInteger() + (direction.toInteger() % 2 == 0 ? 2 : 0)) % 4; + switch(direction) { + case NORTH: rotation = 0; break; + case EAST: rotation = 1; break; + case SOUTH: rotation = 2; break; + case WEST: rotation = 3; break; + default: rotation = (direction.toInteger() + (direction.toInteger() % 2 == 0 ? 2 : 0)) % 4; + log(this.getClass(), Log.ERR, "Attempted to rotate a chunk in a non-cardinal direction - using fallback rotation code. This should be investigated!"); + break; + }; for (int i = 0; i < objects.length; i++) { for (int x = 0; x < SIZE; x++) { for (int y = 0; y < SIZE; y++) { @@ -141,6 +159,22 @@ public class BuildRegionChunk extends RegionChunk { return chunk; } + @Override public void rebuildFlags(RegionPlane from) { + for (int x = 0; x < 8; x++) { + for (int y = 0; y < 8; y++) { + Location loc = currentBase.transform(x,y,0); + Location fromLoc = base.transform(x,y,0); + plane.getFlags().getLandscape()[loc.getLocalX()][loc.getLocalY()] = from.getFlags().getLandscape()[fromLoc.getLocalX()][fromLoc.getLocalY()]; + plane.getFlags().clearFlag(x, y); + for (int i = 0; i < ARRAY_SIZE; i++) { + Scenery obj = objects[i][x][y]; + if (obj != null) + LandscapeParser.applyClippingFlagsFor(plane, loc.getLocalX(), loc.getLocalY(), obj); + } + } + } + } + @Override public void clear() { super.clear(); @@ -236,7 +270,7 @@ public class BuildRegionChunk extends RegionChunk { System.err.print(", "); } } - SystemLogger.logErr(this.getClass(), "]!"); + log(this.getClass(), Log.ERR, "]!"); throw new IllegalStateException("Insufficient array length for storing all objects! "); } @@ -284,4 +318,19 @@ public class BuildRegionChunk extends RegionChunk { public Scenery[][] getObjects(int index) { return objects[index]; } + + @Override + public void setCurrentBase(Location currentBase) { + for (int i = 0; i < objects.length; i++) { + for (int x = 0; x < objects[i].length; x++) { + for (int y = 0; y < objects[i][x].length; y++) { + if (objects[i][x][y] != null) { + Location newLoc = currentBase.transform(x, y, 0); + objects[i][x][y].setLocation(newLoc); + } + } + } + } + super.setCurrentBase(currentBase); + } } diff --git a/Server/src/main/core/game/world/map/Direction.java b/Server/src/main/core/game/world/map/Direction.java index 5b34c2f50..4555f931b 100644 --- a/Server/src/main/core/game/world/map/Direction.java +++ b/Server/src/main/core/game/world/map/Direction.java @@ -1,5 +1,7 @@ package core.game.world.map; +import core.game.world.map.path.ClipMaskSupplier; + /** * Represents a direction. * @author Emperor @@ -254,6 +256,29 @@ public enum Direction { return true; } + public boolean canMoveFrom(int z, int x, int y, ClipMaskSupplier clipMaskSupplier) { + int dx, dy; + boolean ret = true; + for (int f : traversal) { + switch(f) { + case 0x12c0120: dx = 0; dy = 1; break; // north + case 0x12c0180: dx = 1; dy = 0; break; // east + case 0x12c01e0: dx = 1; dy = 1; break; // northeast + case 0x12c0102: dx = 0; dy = -1; break; // south + case 0x12c0183: dx = 1; dy = -1; break; // southeast + case 0x12c0108: dx = -1; dy = 0; break; // west + case 0x12c010e: dx = -1; dy = -1; break; // southwest + case 0x12c0138: dx = -1; dy = 1; break; // northwest + default: return false; + } + int flag = clipMaskSupplier.getClippingFlag(z, x+dx, y+dy); + if ((flag & f) != 0) { + ret = false; + } + } + return ret; + } + /** * Sets the traversal. * @param traversal The traversal to set. @@ -261,4 +286,4 @@ public enum Direction { private void setTraversal(int[] traversal) { this.traversal = traversal; } -} \ No newline at end of file +} diff --git a/Server/src/main/core/game/world/map/Location.java b/Server/src/main/core/game/world/map/Location.java index 53c5562a7..ea7d1794c 100644 --- a/Server/src/main/core/game/world/map/Location.java +++ b/Server/src/main/core/game/world/map/Location.java @@ -6,9 +6,11 @@ import core.game.world.map.path.Path; import core.game.world.map.path.Pathfinder; import core.tools.RandomFunction; import org.jetbrains.annotations.NotNull; +import core.api.utils.Vector; import java.util.ArrayList; import java.util.List; +import java.lang.Math; /** * Represents a location on the world map. @@ -277,18 +279,28 @@ public final class Location extends Node { public ArrayList getSurroundingTiles() { ArrayList locs = new ArrayList<>(); - locs.add(transform(0,1,0)); //N - locs.add(transform(1,1,0)); //NE - locs.add(transform(1,0,0)); //E - locs.add(transform(1,-1,0)); //SE - locs.add(transform(0,-1,0)); //S locs.add(transform(-1,-1,0));//SW - locs.add(transform(-1,0,0));//W + locs.add(transform(0,-1,0)); //S + locs.add(transform(1,-1,0)); //SE + locs.add(transform(1,0,0)); //E + locs.add(transform(1,1,0)); //NE + locs.add(transform(0,1,0)); //N locs.add(transform(-1,1,0));//NW + locs.add(transform(-1,0,0));//W return locs; } + public ArrayList getCardinalTiles() { + ArrayList locs = new ArrayList<>(); + + locs.add(transform(-1, 0, 0)); + locs.add(transform(0, -1, 0)); + locs.add(transform(1, 0, 0)); + locs.add(transform(0, 1, 0)); + return locs; + } + /** * Gets a square of 3 x 3 tiles as an ArrayList */ @@ -544,4 +556,8 @@ public final class Location extends Node { if (sb.length() == 0) return null; return Direction.valueOf(sb.toString()); } + + public Location transform (Vector vector) { + return Location.create(this.x + (int) Math.floor(vector.getX()), this.y + (int) Math.floor(vector.getY())); + } } diff --git a/Server/src/main/core/game/world/map/Region.java b/Server/src/main/core/game/world/map/Region.java index b63536e35..ff87d8e6b 100644 --- a/Server/src/main/core/game/world/map/Region.java +++ b/Server/src/main/core/game/world/map/Region.java @@ -10,7 +10,7 @@ import core.game.world.map.build.DynamicRegion; import core.game.world.map.build.LandscapeParser; import core.game.world.map.build.MapscapeParser; import core.game.world.map.zone.RegionZone; -import core.tools.SystemLogger; +import core.tools.Log; import core.game.system.config.XteaParser; import core.game.world.GameWorld; import core.game.world.repository.Repository; @@ -21,6 +21,8 @@ import java.util.HashMap; import java.util.List; import java.util.concurrent.TimeUnit; +import static core.api.ContentAPIKt.log; + /** * Represents a region. * @@ -59,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); @@ -265,16 +272,20 @@ public class Region { } } + public boolean flagInactive(boolean force) { + if (unload(this, force)) { + active = false; + return true; + } else { + return false; + } + } + /** * Flags the region as inactive. */ - protected boolean flagInactive() { - if(unload(this)) { - active = false; - return true; - } else { - return false; - } + public boolean flagInactive() { + return flagInactive(false); } /** @@ -303,6 +314,7 @@ public class Region { int mapscapeId = Cache.getIndexes()[5].getArchiveId("m" + regionX + "_"+ regionY); if (mapscapeId < 0 && !dynamic) { + r.setLoaded(true); return; } @@ -337,19 +349,23 @@ public class Region { } } + public static boolean unload(Region r) { + return unload(r, false); + } + /** * Unloads a region. * @param r The region. */ - private static boolean unload(Region r) { - if (r.isViewed()) { - SystemLogger.logErr(CommunicationInfo.class, "Players viewing region!"); + public static boolean unload(Region r, boolean force) { + if (!force && r.isViewed()) { + log(CommunicationInfo.class, Log.ERR, "Players viewing region!"); r.flagActive(); return false; } for (RegionPlane p : r.planes) { - if (!p.getPlayers().isEmpty()) { - SystemLogger.logErr(CommunicationInfo.class, "Players still in region!"); + if (!force && !p.getPlayers().isEmpty()) { + log(CommunicationInfo.class, Log.ERR, "Players still in region!"); r.flagActive(); return false; } @@ -362,7 +378,10 @@ public class Region { } } } - return true; + if (r.isBuild()) + r.setLoaded(false); + r.activityPulse.stop(); + return true; } /** @@ -392,7 +411,7 @@ public class Region { public int decrementViewAmount() { synchronized (this) { if (viewAmount < 1) { - //SystemLogger.logErr(this.getClass(), "View amount is " + (viewAmount - 1)); + //log(this.getClass(), Log.ERR, "View amount is " + (viewAmount - 1)); viewAmount++; } return --viewAmount; @@ -458,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 816ac5b0c..7b3fb71c8 100644 --- a/Server/src/main/core/game/world/map/RegionChunk.java +++ b/Server/src/main/core/game/world/map/RegionChunk.java @@ -4,6 +4,7 @@ import core.game.node.entity.player.Player; import core.game.node.item.GroundItem; import core.game.node.item.Item; import core.game.node.scenery.Scenery; +import core.tools.Log; import core.tools.SystemLogger; import core.game.world.map.build.DynamicRegion; import core.game.world.map.build.LandscapeParser; @@ -17,38 +18,40 @@ import core.net.packet.out.UpdateAreaPosition; import java.util.ArrayList; import java.util.List; +import static core.api.ContentAPIKt.log; + /** * Represents a region chunk. * @author Emperor * */ 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. */ @@ -58,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. @@ -83,7 +86,7 @@ public class RegionChunk { * @return The region chunk. */ public BuildRegionChunk copy(RegionPlane plane) { - return new BuildRegionChunk(base, rotation, plane); + return new BuildRegionChunk(base, rotation, plane, this.objects); } /** @@ -146,20 +149,76 @@ public class 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; } } } 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. */ @@ -175,14 +234,14 @@ public class RegionChunk { player.getSession().write(buffer); } } - + /** * Rotates the chunk. * @param direction The direction. */ public void rotate(Direction direction) { if (rotation != 0) { - SystemLogger.logErr(this.getClass(), "Region chunk was already rotated!"); + log(this.getClass(), Log.ERR, "Region chunk was already rotated!"); return; } Scenery[][] copy = new Scenery[SIZE][SIZE]; @@ -220,7 +279,7 @@ public class RegionChunk { } } } - + /** * Gets the new coordinates for an object/chunk tile when rotating. * @param x The current x-coordinate. @@ -259,7 +318,7 @@ public class RegionChunk { } return items; } - + /** * Sets the items. * @param items The items to set. @@ -277,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. @@ -340,7 +399,7 @@ public class RegionChunk { public void resetFlags() { flags.clear(); } - + /** * Gets the region plane. * @return The plane. @@ -365,4 +424,18 @@ public class RegionChunk { this.currentBase = currentBase; } -} \ No newline at end of file + public void rebuildFlags(RegionPlane from) { + for (int x = 0; x < 8; x++) { + for (int y = 0; y < 8; y++) { + Location loc = currentBase.transform(x,y,0); + Location fromLoc = base.transform(x,y,0); + plane.getFlags().getLandscape()[loc.getLocalX()][loc.getLocalY()] = from.getFlags().getLandscape()[fromLoc.getLocalX()][fromLoc.getLocalY()]; + plane.getFlags().clearFlag(x, y); + Scenery obj = objects[x][y]; + if (obj != null) + LandscapeParser.flagScenery(plane, loc.getLocalX(), loc.getLocalY(), obj, false, true); + } + } + } + +} diff --git a/Server/src/main/core/game/world/map/RegionManager.kt b/Server/src/main/core/game/world/map/RegionManager.kt index e205863b4..46022fd01 100644 --- a/Server/src/main/core/game/world/map/RegionManager.kt +++ b/Server/src/main/core/game/world/map/RegionManager.kt @@ -1,11 +1,13 @@ package core.game.world.map +import core.api.log import core.game.node.Node import core.game.node.entity.Entity 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.zone.ZoneBorders +import core.tools.Log import core.tools.RandomFunction import core.tools.SystemLogger import java.util.* @@ -43,7 +45,7 @@ object RegionManager { LOCK.unlock() return REGION_CACHE[regionId]!! } - SystemLogger.logErr(this::class.java, "UNABLE TO OBTAIN LOCK WHEN GETTING REGION BY ID. RETURNING BLANK REGION.") + log(this::class.java, Log.ERR, "UNABLE TO OBTAIN LOCK WHEN GETTING REGION BY ID. RETURNING BLANK REGION.") return Region(0,0) } @@ -310,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 } /** @@ -754,6 +770,31 @@ object RegionManager { return players } + /** + * Gets a list of local players within a symmetric bounding box. + * @param l The location. + * @param xdist The distance from the location on the x plane that is considered within bounds. + * @param ydist The distance from the location on the y plane that is considered within bounds. + * @return The list of players. + */ + @JvmStatic + fun getLocalPlayersBoundingBox(l: Location, xdist: Int, ydist: Int): MutableList { + val players: MutableList = LinkedList() + for (regionX in ((l.regionX - 6) shr 3)..((l.regionX + 6) shr 3)) { + for (regionY in ((l.regionY - 6) shr 3)..((l.regionY + 6) shr 3)) { + for (player in forId((regionX shl 8) or regionY).planes[l.z].players) { + if (player.location.x >= l.getX() - xdist && + player.location.x <= l.getX() + xdist && + player.location.y >= l.getY() - ydist && + player.location.y <= l.getY() + ydist) { + players.add(player) + } + } + } + } + return players + } + /** * Gets a list of local players. * @param l The location. @@ -853,6 +894,15 @@ object RegionManager { } } + @JvmStatic + fun removeRegion(id: Int) { + if (lock.tryLock() || LOCK.tryLock(10000, TimeUnit.MILLISECONDS)) { + val r = REGION_CACHE.remove(id) + r?.flagInactive(true) + LOCK.unlock() + } + } + /** * Gets the regionCache. * @return The regionCache. diff --git a/Server/src/main/core/game/world/map/RegionPlane.java b/Server/src/main/core/game/world/map/RegionPlane.java index 15b68bc03..6557bf62b 100644 --- a/Server/src/main/core/game/world/map/RegionPlane.java +++ b/Server/src/main/core/game/world/map/RegionPlane.java @@ -139,6 +139,10 @@ public final class RegionPlane { } return chunks[chunkX][chunkY] = new RegionChunk(region.getBaseLocation().transform(chunkX << 3, chunkY << 3, plane), 0, this); } + + public void setRegionChunk(int chunkX, int chunkY, RegionChunk chunk) { + chunks[chunkX][chunkY] = chunk; + } /** * Removes a scenery. @@ -197,6 +201,17 @@ public final class RegionPlane { public Scenery[][] getObjects() { return objects; } + + public List getObjectList() { + ArrayList list = new ArrayList(); + for (int x = 0; x < REGION_SIZE; x++) { + for (int y = 0; y < REGION_SIZE; y++) { + if (objects[x][y] != null) + list.add(objects[x][y]); + } + } + return list; + } /** * Clears this region plane. @@ -418,4 +433,4 @@ public final class RegionPlane { return chunks; } -} \ No newline at end of file +} diff --git a/Server/src/main/core/game/world/map/build/DynamicRegion.java b/Server/src/main/core/game/world/map/build/DynamicRegion.java index 56451386c..2ccbabc69 100644 --- a/Server/src/main/core/game/world/map/build/DynamicRegion.java +++ b/Server/src/main/core/game/world/map/build/DynamicRegion.java @@ -215,14 +215,18 @@ public final class DynamicRegion extends Region { int regionX = ((regionId >> 8) & 0xFF) << 6; int regionY = (regionId & 0xFF) << 6; DynamicRegion region = new DynamicRegion(regionId, to.getRegionX() >> 3, to.getRegionY() >> 3); + Region base = RegionManager.forId(regionId); + Region.load(base); for (int offsetX = 0; offsetX < 8; offsetX++) { for (int offsetY = 0; offsetY < 8; offsetY++) { int x = regionX + (offsetX << 3); int y = regionY + (offsetY << 3); for (int plane = 0; plane < 4; plane++) { - RegionChunk c = region.chunks[plane][offsetX][offsetY]; + RegionChunk c = base.getPlanes()[plane].getRegionChunk(offsetX, offsetY); if (c == null) { region.chunks[plane][offsetX][offsetY] = c = new RegionChunk(Location.create(0, 0, 0), 0, region.getPlanes()[plane]); + } else { + region.replaceChunk(plane, offsetX, offsetY, (c = c.copy(region.getPlanes()[plane])), base); } c.setRotation(0); c.setBase(Location.create(x, y, plane)); @@ -330,46 +334,16 @@ public final class DynamicRegion extends Region { } } } else { - chunk.clear(); Region.load(fromRegion); Location l = chunk.getBase(); - chunk.setCurrentBase(getBaseLocation().transform(x << 3, y << 3, 0)); - Location regionBase = fromRegion.getBaseLocation(); RegionPlane rp = fromRegion.getPlanes()[l.getZ()]; - for (int i = 0; i < 8; i++) { - for (int j = 0; j < 8; j++) { - int toX = (x << 3) + i; - int toY = (y << 3) + j; - int fromX = (l.getX() - regionBase.getX()) + i; - int fromY = (l.getY() - regionBase.getY()) + j; - p.getFlags().getLandscape()[toX][toY] = rp.getFlags().getLandscape()[fromX][fromY]; - p.getFlags().flag(fromX, fromY, rp.getFlags().getFlag(fromX, fromY)); - p.getProjectileFlags().flag(fromX, fromY, rp.getFlags().getFlag(fromX, fromY)); - Scenery[] objects = { rp.getChunkObject(fromX, fromY) }; - RegionChunk ch = rp.getChunks()[fromX >> 3][fromY >> 3]; - if (ch instanceof BuildRegionChunk) { - BuildRegionChunk bc = (BuildRegionChunk) ch; - objects = new Scenery[BuildRegionChunk.ARRAY_SIZE]; - for (int k = 0; k < objects.length; k++) { - objects[k] = bc.get(i, j, k); - } - } - for (Scenery object : objects) { - if (object != null) { - object = object.transform(object.getId(), object.getRotation(), getBaseLocation().transform(toX, toY, 0)); - LandscapeParser.flagScenery(p, toX, toY, object, false, true);//chunk instanceof BuildRegionChunk);//.addGameObject(object, true); - } else { - p.add(null, toX, toY, true); - } - } - } - } - + chunk.setCurrentBase(getBaseLocation().transform(x << 3, y << 3, z)); + chunk.rebuildFlags(rp); } } @Override - public boolean flagInactive() { + public boolean flagInactive(boolean force) { if (!permanent) { if (parentRegion != null && parentRegion.isActive()) { parentRegion.checkInactive(); @@ -382,7 +356,7 @@ public final class DynamicRegion extends Region { } } } - if(!super.flagInactive()) { + if(!super.flagInactive(force)) { return false; } for (RegionPlane plane : getPlanes()) { @@ -407,7 +381,7 @@ public final class DynamicRegion extends Region { boolean allLinkedInactive = true; if (linked != null) { for (DynamicRegion r : linked) { - allLinkedInactive &= r.flagInactive(); + allLinkedInactive &= r.flagInactive(force); } } return allLinkedInactive; diff --git a/Server/src/main/core/game/world/map/build/LandscapeParser.java b/Server/src/main/core/game/world/map/build/LandscapeParser.java index b3df2188a..81025e7d6 100644 --- a/Server/src/main/core/game/world/map/build/LandscapeParser.java +++ b/Server/src/main/core/game/world/map/build/LandscapeParser.java @@ -47,7 +47,7 @@ public final class LandscapeParser { int z = location >> 12; r.setObjectCount(r.getObjectCount() + 1); if (x < 0 || y < 0 || x >= 64 || y >= 64) { - System.out.println("Object out of bounds: " + objectId + " - " + x + ", " + y + ", " + z); + } else { if ((mapscape[1][x][y] & 0x2) == 2) { z--; @@ -88,6 +88,22 @@ public final class LandscapeParser { */ public static void flagScenery(RegionPlane plane, int localX, int localY, Scenery object, boolean landscape, boolean storeObjects) { Region.load(plane.getRegion()); + SceneryDefinition def = object.getDefinition(); + object.setActive(true); + boolean add = storeObjects || !landscape || def.getChildObject(null).hasActions(); + if (add) { + addPlaneObject(plane, object, localX, localY, landscape, storeObjects); + } + + if (!applyClippingFlagsFor(plane, localX, localY, object)) + return; + + if (!storeObjects && !add && (!def.getChildObject(null).getName().equals("null"))) { + addPlaneObject(plane, object, localX, localY, landscape, false); + } + } + + public static boolean applyClippingFlagsFor(RegionPlane plane, int localX, int localY, Scenery object) { SceneryDefinition def = object.getDefinition(); int sizeX; int sizeY; @@ -98,15 +114,6 @@ public final class LandscapeParser { sizeX = def.sizeY; sizeY = def.sizeX; } - - object.setActive(true); - boolean add = storeObjects || !landscape || def.getChildObject(null).hasActions(); - if (add) { - addPlaneObject(plane, object, localX, localY, landscape, storeObjects); - } - // if (localX == 34 && localY == 32 && plane.getRegion().getId() == 14746) { - // System.out.println(object + ", " + Arrays.toString(object.getDefinition().getOptions()) + ", " + object.getDefinition().projectileClipped); - // } int type = object.getType(); if (type == 22) { //Tile plane.getFlags().getLandscape()[localX][localY] = true; @@ -133,11 +140,9 @@ public final class LandscapeParser { } } } else { - return; - } - if (!storeObjects && !add && (!def.getChildObject(null).getName().equals("null"))) { - addPlaneObject(plane, object, localX, localY, landscape, false); + return false; } + return true; } /** diff --git a/Server/src/main/core/game/world/map/path/DumbPathfinder.java b/Server/src/main/core/game/world/map/path/DumbPathfinder.java index 962932ea2..8bcfb32e0 100644 --- a/Server/src/main/core/game/world/map/path/DumbPathfinder.java +++ b/Server/src/main/core/game/world/map/path/DumbPathfinder.java @@ -83,9 +83,7 @@ public final class DumbPathfinder extends Pathfinder { Direction last = null; for (int i = 0; i < points.size() - 1; i++) { Point p = points.get(i); - if (p.getDirection() != last) { - path.getPoints().add(p); - } + path.getPoints().add(p); } path.getPoints().add(points.get(points.size() - 1)); } @@ -426,4 +424,4 @@ public final class DumbPathfinder extends Pathfinder { return new Direction[0]; } -} \ No newline at end of file +} diff --git a/Server/src/main/core/game/world/map/path/Pathfinder.java b/Server/src/main/core/game/world/map/path/Pathfinder.java index 4202fa9bb..9fce3a782 100644 --- a/Server/src/main/core/game/world/map/path/Pathfinder.java +++ b/Server/src/main/core/game/world/map/path/Pathfinder.java @@ -2,8 +2,10 @@ package core.game.world.map.path; import core.game.node.Node; import core.game.node.entity.Entity; +import core.game.node.entity.npc.NPC; import core.game.node.item.GroundItem; import core.game.node.scenery.Scenery; +import core.game.world.map.Direction; import core.game.world.map.Location; import core.game.world.map.RegionManager; @@ -73,6 +75,20 @@ public abstract class Pathfinder { */ public static final int NORTH_EAST_FLAG = NORTH_FLAG | EAST_FLAG; + public static int flagForDirection(Direction d) { + switch(d) { + case NORTH_WEST: return NORTH_WEST_FLAG; + case NORTH: return NORTH_FLAG; + case NORTH_EAST: return NORTH_EAST_FLAG; + case WEST: return WEST_FLAG; + case EAST: return EAST_FLAG; + case SOUTH_WEST: return SOUTH_WEST_FLAG; + case SOUTH: return SOUTH_FLAG; + case SOUTH_EAST: return SOUTH_EAST_FLAG; + default: return 0; + } + } + /** * Finds a path from the location to the end location. * @param location The start location. @@ -109,7 +125,13 @@ public abstract class Pathfinder { * @return The path. */ public static Path find(Entity mover, Node destination, boolean near, Pathfinder finder) { - return find(mover.getLocation(), mover.size(), destination, near, finder, RegionManager::getClippingFlag); + ClipMaskSupplier cms = null; + if (mover instanceof NPC) { + cms = ((NPC) mover).behavior.getClippingSupplier(((NPC) mover)); + } + if (cms == null) + cms = RegionManager::getClippingFlag; + return find(mover.getLocation(), mover.size(), destination, near, finder, cms); } public static Path findWater(Entity mover, Node destination, boolean near, Pathfinder finder){ @@ -129,6 +151,10 @@ public abstract class Pathfinder { return find(start, destination, true, SMART); } + public static Path find(Location start, Node destination, int moverSize) { + return find(start, moverSize, destination, true, SMART, RegionManager::getClippingFlag); + } + /** * Finds a path from the start location to the end location. * @param destination The destination node. @@ -689,4 +715,4 @@ public abstract class Pathfinder { } return false; } -} \ No newline at end of file +} diff --git a/Server/src/main/core/game/world/map/path/SmartPathfinder.kt b/Server/src/main/core/game/world/map/path/SmartPathfinder.kt index ece18eef2..2dcae4a9c 100644 --- a/Server/src/main/core/game/world/map/path/SmartPathfinder.kt +++ b/Server/src/main/core/game/world/map/path/SmartPathfinder.kt @@ -1,7 +1,20 @@ package core.game.world.map.path +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.tools.* +import core.api.* +import core.api.utils.Vector +import core.ServerConstants + +import java.util.Comparator +import java.util.PriorityQueue + +import java.io.* +import javax.imageio.ImageIO +import java.awt.image.BufferedImage class SmartPathfinder /** @@ -76,17 +89,38 @@ internal constructor() : Pathfinder() { * @param dir The direction. * @param currentCost The current cost. */ - fun check(x: Int, y: Int, dir: Int, currentCost: Int) { - queueX[writePathPosition] = x - queueY[writePathPosition] = y - via[x][y] = dir - cost[x][y] = currentCost - writePathPosition = writePathPosition + 1 and 0xfff + fun check(x: Int, y: Int, dir: Int, currentCost: Int, diagonalPenalty: Int = 0) { + if(cost[x][y] > currentCost + diagonalPenalty) { + queueX[writePathPosition] = x + queueY[writePathPosition] = y + via[x][y] = dir + cost[x][y] = currentCost + diagonalPenalty + writePathPosition = writePathPosition + 1 and 0xfff + } } - override fun find(start: Location?, moverSize: Int, end: Location?, sizeX: Int, sizeY: Int, rotation: Int, type: Int, walkingFlag: Int, near: Boolean, clipMaskSupplier: ClipMaskSupplier?): Path { + override fun find(start: Location?, moverSize: Int, dest: Location?, sizeX: Int, sizeY: Int, rotation: Int, type: Int, walkingFlag: Int, near: Boolean, clipMaskSupplier: ClipMaskSupplier?): Path { reset() - assert(start != null && end != null) + assert(start != null && dest != null) + var vec = Vector.betweenLocs(start!!, dest!!) + var mag = kotlin.math.floor(vec.magnitude()) + var end = dest!! + if (mag > ServerConstants.MAX_PATHFIND_DISTANCE) { + try { + if (mag < 50.0) { //truncate the path if it's realistically long + vec = vec.normalized() * (ServerConstants.MAX_PATHFIND_DISTANCE - 1) + end = start!!.transform(vec) + } else throw Exception("Pathfinding distance exceeds server max! -> " + mag.toString() + " {" + start + "->" + end + "}") + } catch (e: Exception) { + val sw = StringWriter() + val pw = PrintWriter(sw) + e.printStackTrace(pw) + log(this::class.java, Log.FINE, sw.toString()) + val p = Path() + p.isMoveNear = true + return p + } + } val path = Path() foundPath = false for (x in 0..103) { @@ -106,13 +140,29 @@ internal constructor() : Pathfinder() { check(curX, curY, 99, 0) try { if (moverSize < 2) { - checkSingleTraversal(end, sizeX, sizeY, type, rotation, walkingFlag, location, clipMaskSupplier!!) + if(GameWorld.settings?.smartpathfinder_bfs ?: false) { + checkSingleTraversal(end, sizeX, sizeY, type, rotation, walkingFlag, location, clipMaskSupplier!!) + } else { + checkSingleTraversalAstar(end, sizeX, sizeY, type, rotation, walkingFlag, location, clipMaskSupplier!!) + } } else if (moverSize == 2) { checkDoubleTraversal(end, sizeX, sizeY, type, rotation, walkingFlag, location, clipMaskSupplier!!) } else { checkVariableTraversal(end, moverSize, sizeX, sizeY, type, rotation, walkingFlag, location, clipMaskSupplier!!) } } catch (e: Exception) {} + var debugImg = if(false) { BufferedImage(4*104+2, 104, BufferedImage.TYPE_INT_RGB) } else { null } + if(debugImg != null) { + for(y in 0 until 104) { + for(x in 0 until 104) { + debugImg.setRGB(x, 103-y, via[x][y] * (((1 shl 24)-1)/12)) + val c = Math.min(4*Math.min(cost[x][y], 64), 255) + debugImg.setRGB(105+x, 103-y, (c shl 16) or (c shl 8) or c) + debugImg.setRGB(2*105+x, 103-y, clipMaskSupplier!!.getClippingFlag(location.z, location.x + x, location.y + y)) + } + } + } + if (!foundPath) { if (near) { var fullCost = 1000 @@ -159,11 +209,9 @@ internal constructor() : Pathfinder() { if (++attempts > queueX.size) { return path } - if (directionFlag != previousDirection) { - previousDirection = directionFlag - queueX[readPosition] = curX - queueY[readPosition++] = curY - } + previousDirection = directionFlag + queueX[readPosition] = curX + queueY[readPosition++] = curY if (directionFlag and WEST_FLAG != 0) { curX++ } else if (directionFlag and EAST_FLAG != 0) { @@ -174,8 +222,20 @@ internal constructor() : Pathfinder() { } else if (directionFlag and NORTH_FLAG != 0) { curY-- } + if(debugImg != null) { + debugImg.setRGB(3*105+curX, 103-curY, 0x0000ff) + } directionFlag = via[curX][curY] } + if(debugImg != null) { + debugImg.setRGB(3*105+start.sceneX, 103-start.sceneY, 0xff0000) + debugImg.setRGB(3*105+dstX, 103-dstY, 0x00ff00) + if(GameWorld.settings?.smartpathfinder_bfs ?: false) { + ImageIO.write(debugImg, "png", File(String.format("bfs_%04d_%04d_%04d_%04d.png", start.x, start.y, end.x, end.y))) + } else { + ImageIO.write(debugImg, "png", File(String.format("astar_%04d_%04d_%04d_%04d.png", start.x, start.y, end.x, end.y))) + } + } val size = readPosition-- var absX = location.x + queueX[readPosition] var absY = location.y + queueY[readPosition] @@ -187,9 +247,94 @@ internal constructor() : Pathfinder() { path.points.add(Point(absX, absY)) } path.setSuccesful(true) + if (end != dest) + path.isMoveNear = true return path } + class UIntAsPointComparator(val end: Location) : Comparator { + override fun compare(p: UInt, q: UInt): Int { + val pc: UInt = (p and 0x00ff0000u) shr 16 + val px: UInt = (p and 0x0000ff00u) shr 8 + val py: UInt = (p and 0x000000ffu) + val qc: UInt = (q and 0x00ff0000u) shr 16 + val qx: UInt = (q and 0x0000ff00u) shr 8 + val qy: UInt = (q and 0x000000ffu) + //val dp = pc.toInt() + Math.abs(end.sceneX - (px.toInt())) + Math.abs(end.sceneY - (py.toInt())) + //val dq = qc.toInt() + Math.abs(end.sceneX - (qx.toInt())) + Math.abs(end.sceneY - (qy.toInt())) + val dp = pc.toDouble() + Math.max(Math.abs(end.sceneX - px.toInt()), Math.abs(end.sceneY - py.toInt())).toDouble() + val dq = qc.toDouble() + Math.max(Math.abs(end.sceneX - qx.toInt()), Math.abs(end.sceneY - qy.toInt())).toDouble() + if(dp < dq) { + return -1 + } else if(dq < dp) { + return 1 + } else { + return 0 + } + } + override fun equals(other: Any?): Boolean { + if(other is UIntAsPointComparator) { + return end == other.end + } else { + return false + } + } + override fun hashCode(): Int { + return end.hashCode() + } + } + + private fun checkSingleTraversalAstar(end: Location, sizeX: Int, sizeY: Int, type: Int, rotation: Int, walkingFlag: Int, location: Location, clipMaskSupplier: ClipMaskSupplier) { + val z = location.z + var queue = PriorityQueue(4096, UIntAsPointComparator(end)) + queue.add(((curX.toUInt()) shl 8) or (curY.toUInt())) + while(!foundPath && !queue.isEmpty()) { + val point = queue.poll() + val curCost = ((point and 0xff0000u) shr 16).toInt() + curX = ((point and 0x0000ff00u) shr 8).toInt() + curY = (point and 0x000000ffu).toInt() + val absX = location.x + curX + val absY = location.y + curY + if (curX == dstX && curY == dstY) { + foundPath = true + break + } + if (type != 0) { + if ((type < 5 || type == 10) && canDoorInteract(absX, absY, 1, end.x, end.y, type - 1, rotation, z, clipMaskSupplier)) { + foundPath = true + break + } + if (type < 10 && canDecorationInteract(absX, absY, 1, end.x, end.y, type - 1, rotation, z, clipMaskSupplier)) { + foundPath = true + break + } + } + if (sizeX != 0 && sizeY != 0 && canInteract(absX, absY, 1, end.x, end.y, sizeX, sizeY, walkingFlag, z, clipMaskSupplier)) { + foundPath = true + break + } + val newCost = curCost + 1 + //val orthogonalsFirst = arrayOf(Direction.EAST, Direction.NORTH, Direction.WEST, Direction.SOUTH, Direction.NORTH_EAST, Direction.NORTH_WEST, Direction.SOUTH_WEST, Direction.SOUTH_EAST) + val orthogonalsFirst = arrayOf(Direction.SOUTH, Direction.WEST, Direction.NORTH, Direction.EAST, Direction.SOUTH_WEST, Direction.NORTH_WEST, Direction.SOUTH_EAST, Direction.NORTH_EAST) + //val orthogonalsFirst = arrayOf(Direction.SOUTH, Direction.WEST, Direction.NORTH, Direction.EAST) + //for(dir in Direction.values()) { + for(dir in orthogonalsFirst) { + val newSceneX: Int = curX + dir.stepX + val newSceneY: Int = curY + dir.stepY + if(0 <= newSceneX && newSceneX < 104 && 0 <= newSceneY && newSceneY < 104 && via[newSceneX][newSceneY] == 0) { + if(dir.canMoveFrom(z, absX, absY, clipMaskSupplier)) { + val diagonalPenalty = Math.abs(dir.stepX) + Math.abs(dir.stepY) - 1 + val flag = flagForDirection(dir) + check(newSceneX, newSceneY, flag, newCost, diagonalPenalty) + if(via[newSceneX][newSceneY] == flag) { + queue.add(((newCost + diagonalPenalty).toUInt() shl 16) or (newSceneX.toUInt() shl 8) or newSceneY.toUInt()) + } + } + } + } + } + } + /** * Checks possible traversal for a size 1 entity. * @param end The destination location. @@ -443,4 +588,4 @@ internal constructor() : Pathfinder() { } } } -} \ No newline at end of file +} diff --git a/Server/src/main/core/game/world/map/zone/ZoneBorders.java b/Server/src/main/core/game/world/map/zone/ZoneBorders.java index f371fae2d..b43fa5d5f 100644 --- a/Server/src/main/core/game/world/map/zone/ZoneBorders.java +++ b/Server/src/main/core/game/world/map/zone/ZoneBorders.java @@ -2,6 +2,7 @@ package core.game.world.map.zone; import core.game.node.Node; import core.game.world.map.Location; +import core.game.world.map.RegionManager; import core.tools.RandomFunction; import java.util.ArrayList; @@ -97,6 +98,10 @@ public final class ZoneBorders { this.zeroPlaneCheck = zeroPlaneCheck; } + public ZoneBorders(Location l1, Location l2) { + this(l1.getX(), l1.getY(), l2.getX(), l2.getY(), l1.getZ()); + } + /** * Creates zone borders for the given region id. * @param regionId The region id. @@ -234,6 +239,16 @@ public final class ZoneBorders { return new Location(x, y, plane); } + public Location getRandomWalkableLoc() { + Location loc = getRandomLoc(); + int tries = 0; // prevent bad code from DOSing server + while (!RegionManager.isTeleportPermitted(loc) && tries < 20) { + loc = getRandomLoc(); + tries += 1; + } + return loc; + } + /** * Adds an exception. * @param exception The exception to add. 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 73d02232e..4be51e122 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; @@ -13,6 +14,10 @@ import core.game.world.map.Location; import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import java.util.Set; + +import core.game.world.map.Region; +import org.rs09.consts.Items; /** * Handles the zones for an entity. @@ -20,6 +25,36 @@ import java.util.List; */ public final class ZoneMonitor { + /** + * 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 MID_WILDY_TELEPORT_JEWELLERY = Set.of( + Items.AMULET_OF_GLORY_1704, + Items.AMULET_OF_GLORY1_1706, + Items.AMULET_OF_GLORY2_1708, + Items.AMULET_OF_GLORY3_1710, + Items.AMULET_OF_GLORYT_10362, + Items.AMULET_OF_GLORYT1_10360, + Items.AMULET_OF_GLORYT2_10358, + Items.AMULET_OF_GLORYT3_10356, + Items.SKILLS_NECKLACE_11113, + Items.SKILLS_NECKLACE1_11111, + Items.SKILLS_NECKLACE2_11109, + Items.SKILLS_NECKLACE3_11107, + Items.COMBAT_BRACELET_11126, + Items.COMBAT_BRACELET1_11124, + Items.COMBAT_BRACELET2_11122, + Items.COMBAT_BRACELET3_11120, + Items.RING_OF_WEALTH_14638, + Items.RING_OF_WEALTH1_14640, + Items.RING_OF_WEALTH2_14642, + Items.RING_OF_WEALTH3_14644, + Items.RING_OF_LIFE_2570 + ); + /** * The entity. */ @@ -203,7 +238,7 @@ public final class ZoneMonitor { * @return {@code True} if so. */ public boolean teleport(int type, Node node) { - if (type != -1 && entity.isTeleBlocked()) { + if (type != -1 && entity.isTeleBlocked() && !canTeleportByJewellery(type, node)&& !canTeleportByAncient(type)) { if (entity.isPlayer()) { entity.asPlayer().sendMessage("A magical force has stopped you from teleporting."); } @@ -217,6 +252,59 @@ public final class ZoneMonitor { return true; } + /** + * Checks if a player can teleport with a jewellery piece in >= 1 <= 30 wilderness level + * @return {@code True} if so. + */ + 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) + return false; + + if (entity.getZoneMonitor().isRestricted(ZoneRestriction.TELEPORT)) { + return false; + } + + if (entity.getLocks().isTeleportLocked()) { + if (entity.isPlayer()) { + Player p = entity.asPlayer(); + if (p.getSkullManager().getLevel() >= 1 && p.getSkullManager().getLevel() <= 30) { + return true; + } + } + } + + return false; + } + /** + * Snowscape custom allowing teleports with Ancient Magicks in >= 1 <= 30 wilderness level + * @return {@code True} if so. + */ + private boolean canTeleportByAncient(int type) { + if (type != 0) { + return false; + } + if (entity.timers.getTimer("teleblock") != null) + return false; + + if (entity.getZoneMonitor().isRestricted(ZoneRestriction.TELEPORT)) { + return false; + } + + if (entity.getLocks().isTeleportLocked()) { + if (entity.isPlayer()) { + Player p = entity.asPlayer(); + if (p.getSpellBookManager().getSpellBook() == 193 && p.getSkullManager().getLevel() >= 1 && p.getSkullManager().getLevel() <= 48) { + return true; + } + } + } + + return false; + } + /** * Checks if the death should start for an entity. * @param entity the entity. @@ -336,21 +424,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 036e43ad1..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,9 +31,22 @@ 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, + + /** + * 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, ; /** @@ -43,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 6cf4c4ab8..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 @@ -6,6 +6,7 @@ import core.game.event.UseWithEvent; import core.game.component.Component; import content.data.LightSource; import core.game.interaction.Option; +import core.game.interaction.QueueStrength; import core.game.node.Node; import core.game.node.entity.Entity; import core.game.node.entity.combat.CombatStyle; @@ -21,8 +22,7 @@ import org.jetbrains.annotations.NotNull; import core.api.Event; import core.game.world.GameWorld; -import static core.api.ContentAPIKt.getItemName; -import static core.api.ContentAPIKt.runTask; +import static core.api.ContentAPIKt.*; /** * Handles a dark area. @@ -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)); @@ -156,9 +157,11 @@ public final class DarkZone extends MapZone implements EventHook{ public void updateOverlay(Player player) { LightSource source = LightSource.getActiveLightSource(player); if(SkillcapePerks.isActive(SkillcapePerks.CONSTANT_GLOW,player)){ - if(player.getInterfaceManager().getOverlay().getId() == DARKNESS_OVERLAY.getId()) - player.getInterfaceManager().closeOverlay(); - return; + queueScript(player, 1, QueueStrength.SOFT, false, (Integer stage) -> { + if(player.getInterfaceManager().getOverlay().getId() == DARKNESS_OVERLAY.getId()) + player.getInterfaceManager().closeOverlay(); + return stopExecuting(player); + }); } int overlay = -1; if (player.getInterfaceManager().getOverlay() != null) { 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 5ddbd964c..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 @@ -92,6 +95,7 @@ public final class MultiwayCombatZone extends MapZone { registerRegion(14939);//Kalphite Stronghold Cave registerRegion(9532); //Isle north of Jatizso registerRegion(10810); //Eastern rock crabs + registerRegion(12590); //desert bandits } @Override 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 6d38f312b..f6882e52b 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 @@ -1,8 +1,9 @@ package core.game.world.map.zone.impl; +import content.global.handlers.item.equipment.brawling_gloves.BrawlingGloves; +import content.global.skill.summoning.familiar.Familiar; import core.game.component.Component; import core.game.interaction.Option; -import content.global.handlers.item.equipment.brawling_gloves.BrawlingGloves; import core.game.node.Node; import core.game.node.entity.Entity; import core.game.node.entity.combat.CombatStyle; @@ -11,17 +12,18 @@ 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.entity.player.info.Rights; -import content.global.skill.summoning.familiar.Familiar; +import core.game.node.entity.player.link.SkullManager; import core.game.node.item.GroundItemManager; import core.game.node.item.Item; +import core.game.world.GameWorld; import core.game.world.map.Location; import core.game.world.map.zone.MapZone; import core.game.world.map.zone.ZoneBorders; import core.game.world.map.zone.ZoneRestriction; -import core.tools.RandomFunction; -import org.rs09.consts.NPCs; -import core.game.world.GameWorld; import core.game.world.repository.Repository; +import core.tools.RandomFunction; +import org.rs09.consts.Items; +import org.rs09.consts.NPCs; /** @@ -37,7 +39,10 @@ public final class WildernessZone extends MapZone { /** * The wilderness zone. */ - private static final WildernessZone INSTANCE = new WildernessZone(new ZoneBorders(2944, 3525, 3400, 3975), new ZoneBorders(3070, 9924, 3135, 10002), ZoneBorders.forRegion(12193), ZoneBorders.forRegion(11937)); + 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"; /** * The zone borders. @@ -69,7 +74,7 @@ public final class WildernessZone extends MapZone { double x = combatLevel; double A = 44044.5491; double B = -7360.19548; - return (int) (A + (B * Math.log(x))) / 2; + return (int) (A + (B * Math.log(x))); } /** @@ -87,31 +92,35 @@ public final class WildernessZone extends MapZone { } private void rollWildernessExclusiveLoot(Entity e, Entity killer) { - //Roll for PVP gear and Brawling Gloves from revenants - if (e instanceof NPC && killer instanceof Player && (e.asNpc().getName().contains("Revenant") || e.getId() == NPCs.CHAOS_ELEMENTAL_3200)) { + if (!(killer instanceof Player)) return; + if (!(e instanceof NPC)) return; + if (!(e.getId() == NPCs.CHAOS_ELEMENTAL_3200 || e.asNpc().getName().contains("Revenant"))) return; - boolean gloveDrop = e.getId() == NPCs.CHAOS_ELEMENTAL_3200 ? RandomFunction.roll(75) : RandomFunction.roll(100); - if (gloveDrop) { - 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() + "!"); - } + 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); - int combatLevel = e.asNpc().getDefinition().getCombatLevel(); - int dropRate = getNewDropRate(combatLevel); - for (int j : PVP_GEAR) { - boolean chance = RandomFunction.roll(dropRate); - 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 (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()); + Repository.sendNews(killer.getUsername() + " has received " + reward.getName().toLowerCase() + " from a " + e.asNpc().getName() + "!"); + } + 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); } + GroundItemManager.create(reward, ((NPC) e).getDropLocation(), killer.asPlayer()); + Repository.sendNews(killer.asPlayer().getUsername() + " has received a " + reward.getName() + " from a " + e.asNpc().getName() + "!"); } } } @@ -134,8 +143,6 @@ public final class WildernessZone extends MapZone { return super.interact(e, target, option); } - - @Override public boolean enter(Entity e) { if (e instanceof Player) { @@ -210,7 +217,7 @@ public final class WildernessZone extends MapZone { if (p.getSkullManager().isWildernessDisabled()) { return; } - p.getInterfaceManager().openWildernessOverlay(new Component(381)); + p.getInterfaceManager().openOverlay(new Component(381)); p.getSkullManager().setLevel(getWilderness(p)); p.getPacketDispatch().sendString("Level: " + p.getSkullManager().getLevel(), 381, 1); if(GameWorld.getSettings().getWild_pvp_enabled()) { @@ -226,6 +233,10 @@ public final class WildernessZone extends MapZone { if (p.getDetails().getRights() == Rights.ADMINISTRATOR) { return true; } + // Snowscape: one of the checks needed to allow Ancient spells to work deeper in the wilderness. + if (type == 0 && p.getSpellBookManager().getSpellBook() == 193 && checkTeleport(p, 48)) { + return true; + } if (!checkTeleport(p, (node != null && node instanceof Item && (((Item) node).getName().contains("glory") || ((Item) node).getName().contains("slaying")) ? 30 : 20))) { return false; } @@ -290,17 +301,24 @@ public final class WildernessZone extends MapZone { return false; } + public static boolean isInZone (Location l) { + for (ZoneBorders z : INSTANCE.borders) { + if (z.insideBorder(l)) return true; + } + return false; + } + /** * The wilderness level. * @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/objectparser/ObjectParser.java b/Server/src/main/core/game/world/objectparser/ObjectParser.java index 0fcec7846..39b445e31 100644 --- a/Server/src/main/core/game/world/objectparser/ObjectParser.java +++ b/Server/src/main/core/game/world/objectparser/ObjectParser.java @@ -19,7 +19,7 @@ public class ObjectParser implements StartupListener { if(ServerConstants.OBJECT_PARSER_PATH == null) return; File f = new File(ServerConstants.OBJECT_PARSER_PATH); if(!f.exists()){ - System.out.println("[ObjectParser]: Can't find file " + ServerConstants.OBJECT_PARSER_PATH); + return; } @@ -30,7 +30,7 @@ public class ObjectParser implements StartupListener { NodeList parseList = doc.getElementsByTagName("ObjectAction"); - System.out.println("Parsing " + parseList.getLength() + " object modifications."); + for(int i = 0; i < parseList.getLength(); i++){ Node parseNode = parseList.item(i); diff --git a/Server/src/main/core/game/world/repository/DisconnectionQueue.kt b/Server/src/main/core/game/world/repository/DisconnectionQueue.kt index 4448cbd16..dd0c7ee98 100644 --- a/Server/src/main/core/game/world/repository/DisconnectionQueue.kt +++ b/Server/src/main/core/game/world/repository/DisconnectionQueue.kt @@ -1,12 +1,12 @@ package core.game.world.repository +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 java.util.* -import java.util.concurrent.ConcurrentHashMap +import core.tools.Log +import core.tools.secondsToTicks /** * Handles disconnecting players queuing. @@ -16,13 +16,14 @@ class DisconnectionQueue { /**A * The pending disconnections queue. */ - private val queue: MutableMap + private val queue = HashMap() + private val queueTimers = HashMap() /** * Updates all entries. */ fun update() { - if (queue.isEmpty() || GameWorld.ticks % 3 != 0 && GameWorld.settings?.isDevMode != true) { + if (queue.isEmpty() || GameWorld.ticks % 3 != 0) { return } //make a copy of current entries as to avoid concurrency exceptions @@ -31,6 +32,21 @@ class DisconnectionQueue { //loop through entries and disconnect each entries.forEach { if(finish(it.value,false)) queue.remove(it.key) + 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 + 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) + remove(it.key) + log(this::class.java, Log.WARN, "Force-clearing ${it.key} after 15 minutes of being in the disconnection queue!") + } + } + } } } @@ -46,15 +62,12 @@ class DisconnectionQueue { */ private fun finish(entry: DisconnectionEntry?, force: Boolean): Boolean { val player = entry!!.player - if (!force && player.allowRemoval()) { + if (!force && !player.allowRemoval()) { return false } - if (entry.isClear) { - SystemLogger.logInfo(this::class.java, "Clearing player...") - player.clear() - } + player.packetDispatch.sendLogout() + player.finishClear() Repository.removePlayer(player) - SystemLogger.logInfo(this::class.java, "Player cleared. Removed ${player.details.username}") try { if(player.communication.clan != null) player.communication.clan.leave(player, false) @@ -69,9 +82,11 @@ class DisconnectionQueue { Thread.currentThread().name = "PlayerSave SQL" save(player, true) } + log(this::class.java, Log.INFO, "Player cleared. Removed ${player.details.username}.") return true } save(player, false) + log(this::class.java, Log.INFO, "Player cleared. Removed ${player.details.username}.") return true } @@ -89,100 +104,29 @@ class DisconnectionQueue { * Clears the queue. */ fun clear() { - for (entry in queue.values) { + for (entry in queue.values.toTypedArray()) { finish(entry, true) } queue.clear() } - fun safeClear(){ - for(entry in queue.values){ - finish(entry,false) - } - queue.clear() - } - /** - * Adds a player to the disconnection queue. - * @param player The player. - * @param clear If the player should be cleared. - */ - /** - * Adds a player to the disconnection queue. - * @param player The player. - */ @JvmOverloads fun add(player: Player, clear: Boolean = false) { if(queue[player.name] != null) return queue[player.name] = DisconnectionEntry(player, clear) + log(this::class.java, Log.INFO, "Queueing ${player.name} for disconnection.") } - /** - * Checks if the queue contains the player name. - * @param name The name. - * @return `True` if so. - */ operator fun contains(name: String?): Boolean { return queue.containsKey(name) } - /** - * Removes a queued player. - * @param name The name. - */ fun remove(name: String?) { queue.remove(name) + queueTimers.remove(name) } - /** - * Represents an entry in the disconnection queue, holding the disconnected - * player and time stamp of disconnection. - * @author Emperor - */ - internal inner class DisconnectionEntry( - /** - * The player. - */ - val player: Player, - /** - * If the `Player#clear()` method should be called. - */ - var isClear: Boolean) { - /** - * Gets the timeStamp. - * @return The timeStamp. - */ - /** - * Sets the timeStamp. - * @param timeStamp The timeStamp to set. - */ - /** - * The time of disconnection. - */ - var timeStamp: Int - - /** - * Gets the player. - * @return The player. - */ - - /** - * Gets the clear. - * @return The clear. - */ - /** - * Sets the clear. - * @param isClear The clear to set. - */ - - /** - * Constructs a new `DisconnectionQueue` `Object`. - * @param player The disconnecting player. - * @param clear If the player should be cleared. - */ - init { - timeStamp = GameWorld.ticks - } - } + internal data class DisconnectionEntry(val player: Player, var isClear: Boolean) {} /** * Saves the player. @@ -191,17 +135,10 @@ class DisconnectionQueue { */ fun save(player: Player, sql: Boolean): Boolean { try { - PlayerParser.save(player) - return true + PlayerParser.saveImmediately(player) } catch (t: Throwable) { t.printStackTrace() } return false } - /** - * Constructs a new `DisconnectionQueue` `Object`. - */ - init { - queue = ConcurrentHashMap() - } -} \ No newline at end of file +} diff --git a/Server/src/main/core/game/world/repository/Repository.kt b/Server/src/main/core/game/world/repository/Repository.kt index 11962a3e8..575bcb0f2 100644 --- a/Server/src/main/core/game/world/repository/Repository.kt +++ b/Server/src/main/core/game/world/repository/Repository.kt @@ -142,9 +142,8 @@ object Repository { if (players[i].details.uid == player.details.uid) { val oldPl = players[i] players.remove(oldPl) - oldPl.clear(true) - oldPl.session.disconnect() - break; + oldPl.clear() + break } } } @@ -160,7 +159,6 @@ object Repository { playerNames.remove(player.name) UpdateSequence.renderablePlayers.remove(player) player.session.disconnect() - player.clear(true) } /** @@ -193,6 +191,11 @@ object Repository { } else playerNames[name.toLowerCase().replace(" ".toRegex(), "_")] } + @JvmStatic + fun getPlayerByUid(uid: Int) : Player? { + return uid_map[uid] + } + /** * Gets the renderableNpcs. * @return The renderableNpcs. @@ -200,4 +203,4 @@ object Repository { @JvmStatic val renderableNpcs: List get() = RENDERABLE_NPCS -} \ No newline at end of file +} diff --git a/Server/src/main/core/game/world/update/UpdateMasks.kt b/Server/src/main/core/game/world/update/UpdateMasks.kt index feb8eddc5..072148a1c 100644 --- a/Server/src/main/core/game/world/update/UpdateMasks.kt +++ b/Server/src/main/core/game/world/update/UpdateMasks.kt @@ -5,82 +5,42 @@ import core.game.node.entity.combat.ImpactHandler import core.game.node.entity.player.Player import core.game.world.update.flag.UpdateFlag import core.game.world.update.flag.context.HitMark -import core.game.world.update.flag.npc.NPCHitFlag -import core.game.world.update.flag.npc.NPCHitFlag1 -import core.game.world.update.flag.player.AppearanceFlag -import core.game.world.update.flag.player.HitUpdateFlag -import core.game.world.update.flag.player.HitUpdateFlag1 +import core.game.world.update.flag.* import core.net.packet.IoBuffer +import core.api.* +import core.tools.* import java.util.concurrent.atomic.AtomicBoolean -/** - * Holds an entity's update masks. - * @author Emperor - */ -class UpdateMasks { - /** - * The mask data. - */ - private var maskData = 0 - - /** - * The update masks array. - */ - private val masks = arrayOfNulls?>(SIZE) - - /** - * Gets the appearanceStamp. - * @return The appearanceStamp. - */ - /** - * Sets the appearanceStamp. - * @param appearanceStamp The appearanceStamp to set. - */ - /** - * The appearance time stamp. - */ +class UpdateMasks (val owner: Entity) { var appearanceStamp: Long = 0 - - /** - * The synced mask data. - */ - private var syncedMask = 0 - - /** - * The update masks array. - */ - private val syncedMasks = arrayOfNulls?>(SIZE) - - /** - * If the update masks are being updated. - */ + private val type = EFlagType.of (owner) private val updating = AtomicBoolean() - /** - * Registers an update flag. - * @param flag The update flag. - * @return `True` if successful. - */ - /** - * Registers an update flag. - * @param flag The update flag. - * @return `True` if successful. - */ + private var presenceFlags = 0 + private var syncedPresenceFlags = 0 + private val elements = arrayOfNulls(SIZE) + private val syncedElements = arrayOfNulls(SIZE) + private data class MaskElement (val encoder: EFlagProvider, val context: Any?) + @JvmOverloads - fun register(flag: UpdateFlag<*>, synced: Boolean = false): Boolean { - var synced = synced - if (updating.get()) { + fun register(flag: EntityFlag, context: Any?, sync: Boolean = false) : Boolean { + var synced = sync + var provider = EntityFlags.getFlagFor (type, flag) + if (provider == null) { + logWithStack(this::class.java, Log.ERR, "Tried to use flag ${flag.name} which is not available for ${type.name} in this revision.") return false } - if (flag is AppearanceFlag) { + if (updating.get()) + return false + if (flag == EntityFlag.Appearance) { appearanceStamp = System.currentTimeMillis() synced = true } if (synced) { - syncedMasks[flag.ordinal()] = flag - syncedMask = syncedMask or flag.data() + syncedElements[provider.ordinal] = MaskElement (provider, context) + syncedPresenceFlags = syncedPresenceFlags or provider.presenceFlag } - maskData = maskData or flag.data() - masks[flag.ordinal()] = flag + elements[provider.ordinal] = MaskElement (provider, context) + presenceFlags = presenceFlags or provider.presenceFlag return true } @@ -90,9 +50,9 @@ class UpdateMasks { * @return `True` if the mask got removed. */ fun unregisterSynced(ordinal: Int): Boolean { - if (syncedMasks[ordinal] != null) { - syncedMask = syncedMask and syncedMasks[ordinal]!!.data().inv() - syncedMasks[ordinal] = null + if (syncedElements[ordinal] != null) { + syncedPresenceFlags = syncedPresenceFlags and syncedElements[ordinal]!!.encoder.presenceFlag.inv() + syncedElements[ordinal] = null return true } return false @@ -105,16 +65,16 @@ class UpdateMasks { * @param buffer The buffer to write on. */ fun write(p: Player?, e: Entity?, buffer: IoBuffer) { - var maskData = maskData + var maskData = presenceFlags if (maskData >= 0x100) { maskData = maskData or if (e is Player) 0x10 else 0x8 buffer.put(maskData).put(maskData shr 8) } else { buffer.put(maskData) } - for (i in masks.indices) { - val flag = masks[i] - flag?.writeDynamic(buffer, p) + for (i in elements.indices) { + val element = elements[i] + element?.encoder?.writeToDynamic(buffer, element.context, p!!) } } @@ -126,10 +86,11 @@ class UpdateMasks { * @param appearance If the appearance mask should be written. */ fun writeSynced(p: Player?, e: Entity?, buffer: IoBuffer, appearance: Boolean) { - var maskData = maskData - var synced = syncedMask - if (!appearance && synced and AppearanceFlag.getData() != 0) { - synced = synced and AppearanceFlag.getData().inv() + var maskData = presenceFlags + var synced = syncedPresenceFlags + var appearanceFlag = EntityFlags.getPresenceFlag(type, EntityFlag.Appearance) + if (!appearance && synced and appearanceFlag != 0) { + synced = synced and appearanceFlag.inv() } maskData = maskData or synced if (maskData >= 0x100) { @@ -138,15 +99,15 @@ class UpdateMasks { } else { buffer.put(maskData) } - for (i in masks.indices) { - var flag = masks[i] - if (flag == null) { - flag = syncedMasks[i] - if (!appearance && flag is AppearanceFlag) { + for (i in elements.indices) { + var element = elements[i] + if (element == null) { + element = syncedElements[i] + if (!appearance && element != null && element.encoder.flag == EntityFlag.Appearance) { continue } } - flag?.writeDynamic(buffer, p) + element?.encoder?.writeToDynamic(buffer, element.context, p!!) } } @@ -173,13 +134,8 @@ class UpdateMasks { * @param secondary If the hit update is secondary. */ private fun registerHitUpdate(e: Entity, impact: ImpactHandler.Impact, secondary: Boolean): HitMark { - val player = e is Player val mark = HitMark(impact.amount, impact.type.ordinal, e) - if (player) { - register(if (secondary) HitUpdateFlag1(mark) else HitUpdateFlag(mark)) - } else { - register(if (secondary) NPCHitFlag1(mark) else NPCHitFlag(mark)) - } + register(if (secondary) EntityFlag.SecondaryHit else EntityFlag.PrimaryHit, mark) return mark } @@ -187,10 +143,10 @@ class UpdateMasks { * Resets the update masks. */ fun reset() { - for (i in masks.indices) { - masks[i] = null + for (i in elements.indices) { + elements[i] = null } - maskData = 0 + presenceFlags = 0 updating.set(false) } @@ -203,14 +159,14 @@ class UpdateMasks { * @return `True` if so. */ val isUpdateRequired: Boolean - get() = maskData != 0 + get() = presenceFlags != 0 /** * Checks if synced update masks have been registered. * @return `True` if so. */ fun hasSynced(): Boolean { - return syncedMask != 0 + return syncedPresenceFlags != 0 } companion object { @@ -219,4 +175,4 @@ class UpdateMasks { */ const val SIZE = 11 } -} \ No newline at end of file +} diff --git a/Server/src/main/core/game/world/update/UpdateSequence.kt b/Server/src/main/core/game/world/update/UpdateSequence.kt index 0c0e19448..bc17fbd65 100644 --- a/Server/src/main/core/game/world/update/UpdateSequence.kt +++ b/Server/src/main/core/game/world/update/UpdateSequence.kt @@ -9,6 +9,7 @@ import core.net.packet.PacketRepository import core.net.packet.context.PlayerContext import core.net.packet.out.ClearMinimapFlag import core.game.world.repository.Repository +import core.integrations.grafana.* /** * The entity update sequence. @@ -32,15 +33,23 @@ class UpdateSequence playersList = renderablePlayers npcList = Repository.renderableNpcs lobbyList!!.map{ PacketRepository.send(ClearMinimapFlag::class.java, PlayerContext(it)) } - renderablePlayers.forEach(Player::tick) + + var npcTickStart = System.currentTimeMillis() npcList!!.forEach(NPC::tick) + Grafana.npcTickTime = (System.currentTimeMillis() - npcTickStart).toInt() + + var playerTickStart = System.currentTimeMillis() + renderablePlayers.forEach(Player::tick) + Grafana.playerTickTime = (System.currentTimeMillis() - playerTickStart).toInt() } /** * Runs the updating part of the sequence. */ fun run() { + var playerRenderStart = System.currentTimeMillis() renderablePlayers.forEach(Player::update) + Grafana.playerRenderTime = (System.currentTimeMillis() - playerRenderStart).toInt() } /** @@ -72,4 +81,4 @@ class UpdateSequence val renderablePlayers = InitializingNodeList() } -} \ No newline at end of file +} diff --git a/Server/src/main/core/game/world/update/flag/EFlagProvider.kt b/Server/src/main/core/game/world/update/flag/EFlagProvider.kt new file mode 100644 index 000000000..8f9094590 --- /dev/null +++ b/Server/src/main/core/game/world/update/flag/EFlagProvider.kt @@ -0,0 +1,29 @@ +package core.game.world.update.flag + +import core.net.packet.IoBuffer +import core.game.node.entity.Entity +import kotlin.reflect.* +import core.api.* +import core.tools.* + +enum class EFlagType { + Player, + NPC; + companion object { + @JvmStatic fun of (e: Entity) : EFlagType { + if (e is core.game.node.entity.player.Player) return Player + else return NPC + } + } +} + +open class EFlagProvider (val revision: Int, val type: EFlagType, val presenceFlag: Int, val ordinal: Int, val flag: EntityFlag) { + open fun writeTo (buffer: IoBuffer, context: Any?) {} + open fun writeToDynamic (buffer: IoBuffer, context: Any?, e: Entity) { + writeTo (buffer, context) + } + + fun logInvalidType (context: Any?, expected: KType) { + logWithStack(this::class.java, Log.ERR, "Invalid context of type ${context?.let { it::class.java.simpleName } ?: "null"} passed to ${this::class.simpleName} flag which expects $expected.") + } +} diff --git a/Server/src/main/core/game/world/update/flag/EntityFlag.kt b/Server/src/main/core/game/world/update/flag/EntityFlag.kt new file mode 100644 index 000000000..85fcb8429 --- /dev/null +++ b/Server/src/main/core/game/world/update/flag/EntityFlag.kt @@ -0,0 +1,16 @@ +package core.game.world.update.flag + +enum class EntityFlag { + Chat, + ForceChat, + PrimaryHit, + SecondaryHit, + Animate, + SpotAnim, + Appearance, + FaceEntity, + FaceLocation, + ForceMove, + AnimSeq, + TypeSwap +} diff --git a/Server/src/main/core/game/world/update/flag/EntityFlags.kt b/Server/src/main/core/game/world/update/flag/EntityFlags.kt new file mode 100644 index 000000000..3d3c2bf42 --- /dev/null +++ b/Server/src/main/core/game/world/update/flag/EntityFlags.kt @@ -0,0 +1,42 @@ +package core.game.world.update.flag + +import core.api.* +import core.ServerConstants +import kotlin.reflect.* +import kotlin.reflect.full.* + +object EntityFlags { + lateinit var flagProviders : HashMap + + init { + flagProviders = HashMap() + registerFlagProviders (PlayerFlags530::class) + registerFlagProviders (NPCFlags530::class) + } + + fun registerFlagProviders (parent: KClass<*>) { + val clazzes = parent.sealedSubclasses + for (clazz in clazzes) { + val p = clazz.primaryConstructor?.call() as? EFlagProvider ?: continue + flagProviders[getMapToken(p.revision, p.type, p.flag)] = p + } + } + + fun getFlagFor (type: EFlagType, flag: EntityFlag) : EFlagProvider? { + val revision = 530 + return flagProviders[getMapToken(revision, type, flag)] + } + + @JvmStatic fun getOrdinal (type: EFlagType, flag: EntityFlag) : Int { + return getFlagFor(type, flag)?.ordinal ?: -1 + } + + @JvmStatic fun getPresenceFlag (type: EFlagType, flag: EntityFlag) : Int { + return getFlagFor(type, flag)?.presenceFlag ?: 0 + } + + private fun getMapToken (revision: Int, type: EFlagType, flag: EntityFlag) : Int { + var token = ((revision shl 8) + (type.ordinal shl 4) + flag.ordinal) + return token + } +} diff --git a/Server/src/main/core/game/world/update/flag/NPCFlags530.kt b/Server/src/main/core/game/world/update/flag/NPCFlags530.kt new file mode 100644 index 000000000..4a8e98745 --- /dev/null +++ b/Server/src/main/core/game/world/update/flag/NPCFlags530.kt @@ -0,0 +1,108 @@ +package core.game.world.update.flag + +import core.net.packet.IoBuffer +import core.game.world.update.flag.context.* +import core.game.world.update.flag.* +import core.game.world.map.Location +import core.game.node.entity.Entity +import core.game.node.entity.impl.ForceMovement +import core.game.node.entity.player.Player +import core.tools.* +import core.api.* + +import java.nio.charset.StandardCharsets +import kotlin.reflect.* + +sealed class NPCFlags530 (p: Int, o: Int, f: EntityFlag) : EFlagProvider (530, EFlagType.NPC, p, o, f) { + class PrimaryHit : NPCFlags530 (0x40, 0, EntityFlag.PrimaryHit) { + override fun writeTo (buffer: IoBuffer, context: Any?) { + if (context !is HitMark) { + logInvalidType (context, typeOf()) + return + } + buffer.p1 (context.damage) + buffer.p1neg (context.type) + + val e = context.entity + var ratio = 255 + val max = e.skills.maximumLifepoints + if (e.skills.lifepoints < max) + ratio = e.skills.lifepoints * 255 / max + buffer.p1sub (ratio) + } + } + class SecondaryHit : NPCFlags530 (0x2, 1, EntityFlag.SecondaryHit) { + override fun writeTo (buffer: IoBuffer, context: Any?) { + if (context !is HitMark) { + logInvalidType (context, typeOf()) + return + } + buffer.p1neg (context.damage) + buffer.p1sub (context.type) + } + } + class Animate : NPCFlags530 (0x10, 2, EntityFlag.Animate) { + override fun writeTo (buffer: IoBuffer, context: Any?) { + if (context !is Animation) { + logInvalidType (context, typeOf()) + return + } + buffer.p2 (context.id) + buffer.p1 (context.delay) + } + } + class FaceEntity : NPCFlags530 (0x4, 3, EntityFlag.FaceEntity) { + override fun writeTo (buffer: IoBuffer, context: Any?) { + if (context !is Entity?) { + logInvalidType (context, typeOf()) + return + } + if (context == null) { + buffer.p2add (-1) + } else { + buffer.p2add (context.getClientIndex()) + } + } + } + class SpotAnim : NPCFlags530 (0x80, 4, EntityFlag.SpotAnim) { + override fun writeTo (buffer: IoBuffer, context: Any?) { + if (context !is Graphics) { + logInvalidType (context, typeOf()) + return + } + buffer.p2add (context.id) + buffer.ip4 ((context.height shl 16) + context.delay) + } + } + class TypeSwap : NPCFlags530 (0x1, 5, EntityFlag.TypeSwap) { + override fun writeTo (buffer: IoBuffer, context: Any?) { + if (context !is Int) { + logInvalidType (context, typeOf()) + return + } + buffer.ip2 (context) + } + } + class ForceChat : NPCFlags530 (0x20, 6, EntityFlag.ForceChat) { + override fun writeTo (buffer: IoBuffer, context: Any?) { + if (context !is String) { + logInvalidType (context, typeOf()) + return + } + buffer.putString (context) + } + } + class AnimationSequence : NPCFlags530 (0x100, 7, EntityFlag.AnimSeq) { + //TODO + } + class FaceLocation : NPCFlags530 (0x200, 8, EntityFlag.FaceLocation) { + override fun writeTo (buffer: IoBuffer, context: Any?) { + if (context !is Location) { + logInvalidType (context, typeOf()) + return + } + buffer.p2add ((context.x shl 1) + 1) + buffer.p2 ((context.y shl 1) + 1) + } + } +} diff --git a/Server/src/main/core/game/world/update/flag/PlayerFlags530.kt b/Server/src/main/core/game/world/update/flag/PlayerFlags530.kt new file mode 100644 index 000000000..bcc1e3356 --- /dev/null +++ b/Server/src/main/core/game/world/update/flag/PlayerFlags530.kt @@ -0,0 +1,214 @@ +package core.game.world.update.flag + +import core.net.packet.IoBuffer +import core.game.world.update.flag.context.* +import core.game.world.update.flag.* +import core.game.world.map.Location +import core.game.node.entity.Entity +import core.game.node.entity.impl.ForceMovement +import core.game.node.entity.player.Player +import core.tools.* +import core.api.* +import core.game.world.GameWorld + +import java.nio.charset.StandardCharsets +import kotlin.reflect.* +import kotlin.math.max + +sealed class PlayerFlags530 (p: Int, o: Int, f: EntityFlag) : EFlagProvider (530, EFlagType.Player, p, o, f) { + class Chat : PlayerFlags530 (0x80, 0, EntityFlag.Chat) { + override fun writeTo (buffer: IoBuffer, context: Any?) { + if (context !is ChatMessage) { + logInvalidType (context, typeOf()) + return + } + buffer.ip2 (context.effects) + if (context.isQuickChat) + buffer.p1 (4) + else + buffer.p1 (context.chatIcon) + val chatBuf = ByteArray(256) + chatBuf[0] = context.text.length.toByte() + val offset = 1 + StringUtils.encryptPlayerChat ( + chatBuf, + 0, 1, + context.text.length, + context.text.toByteArray(StandardCharsets.UTF_8) + ) + buffer.p1 (offset + 1) + buffer.putReverse (chatBuf, 0, offset) + } + } + class PrimaryHit : PlayerFlags530 (0x1, 1, EntityFlag.PrimaryHit) { + override fun writeTo (buffer: IoBuffer, context: Any?) { + if (context !is HitMark) { + logInvalidType (context, typeOf()) + return + } + buffer.psmarts (context.damage) + buffer.p1add (context.type) + + var ratio = 255 + var e = context.entity + var max = e.skills.maximumLifepoints + if (max > e.skills.lifepoints) + ratio = e.skills.lifepoints * 255 / max + buffer.p1sub (ratio) + } + } + class Animate : PlayerFlags530 (0x8, 2, EntityFlag.Animate) { + override fun writeTo (buffer: IoBuffer, context: Any?) { + if (context !is Animation) { + logInvalidType (context, typeOf()) + return + } + buffer.p2 (context.id) + buffer.p1 (context.delay) + } + } + class Appearance : PlayerFlags530 (0x4, 3, EntityFlag.Appearance) { + override fun writeTo (buffer: IoBuffer, context: Any?) { + if (context !is Player) { + logInvalidType (context, typeOf()) + return + } + val appearance = context.appearance + appearance.prepareBodyData(context) + var settings = appearance.gender.toByte().toInt() + val nonPvp = context.skullManager.isWilderness && context.skullManager.isWildernessDisabled //nonPvp is always false + if (context.size() > 1) + settings += (context.size() - 1) shl 3 + if (nonPvp) + settings += 0x4 //flag the player as being non-pvp, which shows skill level instead of combat level. + + buffer.p1 (0) //stand-in for size. + val startPos = buffer.toByteBuffer().position() + + buffer.p1 (settings) + buffer.p1 (appearance.skullIcon) + buffer.p1 (appearance.headIcon) + val npcId = appearance.npcId + if (npcId == -1) { + val parts = appearance.bodyParts + for (i in 0 until 12) { + if (parts[i] == 0) buffer.p1 (0) + else buffer.p2 (parts[i]) + } + } else { + buffer.p2 (-1) + buffer.p2 (npcId) + buffer.p1 (255) + } + arrayOf ( appearance.hair, appearance.torso, appearance.legs, appearance.feet, appearance.skin ).forEach { part -> + buffer.p1 (part.color) + } + buffer.p2 (appearance.renderAnimation) + buffer.p8 (StringUtils.stringToLong(context.username)) + if (nonPvp) { + buffer.p1 (context.properties.currentCombatLevel) //with summoning + buffer.p2 (context.skills.getTotalLevel()) + } else { + //client-side code determines how the combat level will be displayed, based on + //the combat level values that are sent + if ((GameWorld.settings!!.isPvp || (GameWorld.settings!!.wild_pvp_enabled && context.skullManager.isWilderness)) + && !context.familiarManager.isUsingSummoning) { + //client will display separate base and summoning combat levels (e.g. "50+5") + //TODO: Split combat levels should also be used for Bounty Hunter + buffer.p1 (context.properties.combatLevel) //without summoning + buffer.p1 (context.properties.combatLevel + context.familiarManager.summoningCombatLevel) //with summoning + } else { + //client will display a combined combat level (e.g. "55") + buffer.p1 (context.properties.currentCombatLevel) //with summoning + buffer.p1 (context.properties.currentCombatLevel) //with summoning + } + if (GameWorld.settings!!.isPvp) { + //displays a player's combat level in white if the player is not within combat range + buffer.p1 (context.skullManager.level) //combat range + } else { + //disables the client code that makes combat levels white + buffer.p1 (-1) //combat range + } + } + buffer.p1 (0) //this is the sound radius, and if set, 4 shorts need to be set as well which include the sound IDs + //to play to everyone in that radius when the player is rendered onscreen for the first time. + buffer.psizeadd (buffer.toByteBuffer().position() - startPos) + } + } + class FaceEntity : PlayerFlags530 (0x2, 4, EntityFlag.FaceEntity) { + override fun writeTo (buffer: IoBuffer, context: Any?) { + if (context !is Entity?) { + logInvalidType (context, typeOf()) + return + } + if (context == null) + buffer.p2add (-1) + else + buffer.p2add (context.getClientIndex()) + } + } + class ForceMove : PlayerFlags530 (0x400, 5, EntityFlag.ForceMove) { + override fun writeToDynamic (buffer: IoBuffer, context: Any?, e: Entity) { + if (context !is ForceMoveCtx) { + logInvalidType (context, typeOf()) + return + } + if (e !is Player) { + logInvalidType (context, typeOf()) + return + } + val l = e.playerFlags.getLastSceneGraph() + //start location + buffer.p1neg (context.start.getSceneX(l)) + buffer.p1 (context.start.getSceneY(l)) + //end location + buffer.p1add (context.dest.getSceneX(l)) + buffer.p1 (context.dest.getSceneY(l)) + //arrival times (in client cycles) + buffer.ip2 (context.startArrive) //# of client cycles to start location + buffer.ip2 (max(context.startArrive + 1, context.startArrive + context.destArrive)) //# of client cycles to end location + buffer.p1neg (context.direction.toInteger()) //direction of movement + } + } + class ForceChat : PlayerFlags530 (0x20, 6, EntityFlag.ForceChat) { + override fun writeTo (buffer: IoBuffer, context: Any?) { + if (context !is String) { + logInvalidType (context, typeOf()) + return + } + buffer.putString(context) + } + } + class SecondaryHit : PlayerFlags530 (0x200, 7, EntityFlag.SecondaryHit) { + override fun writeTo (buffer: IoBuffer, context: Any?) { + if (context !is HitMark) { + logInvalidType (context, typeOf()) + return + } + buffer.psmarts (context.damage) + buffer.p1sub (context.type) + } + } + class AnimationSequence : PlayerFlags530 (0x800, 8, EntityFlag.AnimSeq) { + //TODO + } + class SpotAnim : PlayerFlags530 (0x100, 9, EntityFlag.SpotAnim) { + override fun writeTo (buffer: IoBuffer, context: Any?) { + if (context !is Graphics) { + logInvalidType (context, typeOf()) + return + } + buffer.ip2 (context.id) + buffer.mp4 ((context.height shl 16) + context.delay) + } + } + class FaceLocation : PlayerFlags530 (0x40, 10, EntityFlag.FaceLocation) { + override fun writeTo (buffer: IoBuffer, context: Any?) { + if (context !is Location) { + logInvalidType (context, typeOf()) + return + } + buffer.p2 ((context.x shl 1) + 1) + buffer.ip2add ((context.y shl 1) + 1) + } + } +} diff --git a/Server/src/main/core/game/world/update/flag/context/ChatMessage.kt b/Server/src/main/core/game/world/update/flag/context/ChatMessage.kt index e1f6c7819..c7ee24fa1 100644 --- a/Server/src/main/core/game/world/update/flag/context/ChatMessage.kt +++ b/Server/src/main/core/game/world/update/flag/context/ChatMessage.kt @@ -1,6 +1,7 @@ package core.game.world.update.flag.context import core.game.node.entity.player.Player +import core.game.node.entity.player.info.Rights /** * Represents a chat message. @@ -35,6 +36,8 @@ class ChatMessage var numChars = numChars private set + var chatIcon = Rights.getChatIcon(player) + @JvmField var isQuickChat = false -} \ No newline at end of file +} diff --git a/Server/src/main/core/game/world/update/flag/context/ForceMoveCtx.kt b/Server/src/main/core/game/world/update/flag/context/ForceMoveCtx.kt new file mode 100644 index 000000000..2c28531c8 --- /dev/null +++ b/Server/src/main/core/game/world/update/flag/context/ForceMoveCtx.kt @@ -0,0 +1,5 @@ +package core.game.world.update.flag.context + +import core.game.world.map.* + +data class ForceMoveCtx (val start: Location, val dest: Location, val startArrive: Int, val destArrive: Int, val direction: Direction) diff --git a/Server/src/main/core/game/world/update/flag/context/HitMark.java b/Server/src/main/core/game/world/update/flag/context/HitMark.java index ea6428624..7df5c73f7 100644 --- a/Server/src/main/core/game/world/update/flag/context/HitMark.java +++ b/Server/src/main/core/game/world/update/flag/context/HitMark.java @@ -27,6 +27,8 @@ public class HitMark { */ private final Entity entity; + public boolean showHealthBar = true; + /** * Constructs a new {@code HitMark} {@code Object}. * @param damage The amount of damage. @@ -39,6 +41,13 @@ public class HitMark { this.entity = entity; } + public HitMark(int damage, int type, Entity entity, boolean showHealthBar) { + this.damage = damage; + this.type = type; + this.entity = entity; + this.showHealthBar = showHealthBar; + } + /** * Gets the damage. * @return The damage. @@ -77,4 +86,4 @@ public class HitMark { public void setLifepoints(int lifepoints) { this.lifepoints = lifepoints; } -} \ No newline at end of file +} diff --git a/Server/src/main/core/game/world/update/flag/npc/NPCAnimation.java b/Server/src/main/core/game/world/update/flag/npc/NPCAnimation.java deleted file mode 100644 index b87ae5dd9..000000000 --- a/Server/src/main/core/game/world/update/flag/npc/NPCAnimation.java +++ /dev/null @@ -1,45 +0,0 @@ -package core.game.world.update.flag.npc; - -import core.game.world.update.flag.UpdateFlag; -import core.game.world.update.flag.context.Animation; -import core.net.packet.IoBuffer; - -/** - * The NPC animation update flag. - * @author Emperor - * - */ -public final class NPCAnimation extends UpdateFlag { - - /** - * Constructs a new {@code NPCAnimation} {@code Object}. - * @param context The animation. - */ - public NPCAnimation(Animation context) { - super(context); - } - - @Override - public void write(IoBuffer buffer) { - buffer.putShort(context.getId()).put(context.getDelay()); - } - - @Override - public int data() { - return maskData(); - } - - @Override - public int ordinal() { - return 2; - } - - /** - * Gets the mask data. - * @return The mask data. - */ - public static int maskData() { - return 0x10; - } - -} \ No newline at end of file diff --git a/Server/src/main/core/game/world/update/flag/npc/NPCFaceEntity.java b/Server/src/main/core/game/world/update/flag/npc/NPCFaceEntity.java deleted file mode 100644 index 5f4098db3..000000000 --- a/Server/src/main/core/game/world/update/flag/npc/NPCFaceEntity.java +++ /dev/null @@ -1,45 +0,0 @@ -package core.game.world.update.flag.npc; - -import core.game.node.entity.Entity; -import core.game.world.update.flag.UpdateFlag; -import core.net.packet.IoBuffer; - -/** - * The face entity update flag for NPCs. - * @author Emperor - * - */ -public final class NPCFaceEntity extends UpdateFlag { - - /** - * Constructs a new {@code NPCFaceEntity} {@code Object}. - * @param context The context. - */ - public NPCFaceEntity(Entity context) { - super(context); - } - - @Override - public void write(IoBuffer buffer) { - buffer.putShortA(context == null ? -1 : context.getClientIndex()); - } - - @Override - public int data() { - return 0x4; - } - - @Override - public int ordinal() { - return getOrdinal(); - } - - /** - * Gets the mask data. - * @return The mask data. - */ - public static int getOrdinal() { - return 3; - } - -} \ No newline at end of file diff --git a/Server/src/main/core/game/world/update/flag/npc/NPCFaceLocation.java b/Server/src/main/core/game/world/update/flag/npc/NPCFaceLocation.java deleted file mode 100644 index 922d14258..000000000 --- a/Server/src/main/core/game/world/update/flag/npc/NPCFaceLocation.java +++ /dev/null @@ -1,45 +0,0 @@ -package core.game.world.update.flag.npc; - -import core.game.world.map.Location; -import core.game.world.update.flag.UpdateFlag; -import core.net.packet.IoBuffer; - -/** - * The NPC face coordinates update flag. - * @author Emperor - * - */ -public final class NPCFaceLocation extends UpdateFlag { - - /** - * Constructs a new {@code NPCFaceLocation} {@code Object}. - * @param context The location to face. - */ - public NPCFaceLocation(Location context) { - super(context); - } - - @Override - public void write(IoBuffer buffer) { - buffer.putShortA((context.getX() << 1) + 1).putShort((context.getY() << 1) + 1); - } - - @Override - public int data() { - return 0x200; - } - - @Override - public int ordinal() { - return getOrdinal(); - } - - /** - * Gets the mask ordinal. - * @return The ordinal. - */ - public static int getOrdinal() { - return 8; - } - -} \ No newline at end of file diff --git a/Server/src/main/core/game/world/update/flag/npc/NPCForceChat.java b/Server/src/main/core/game/world/update/flag/npc/NPCForceChat.java deleted file mode 100644 index 1a6b66658..000000000 --- a/Server/src/main/core/game/world/update/flag/npc/NPCForceChat.java +++ /dev/null @@ -1,43 +0,0 @@ -package core.game.world.update.flag.npc; - -import core.game.world.update.flag.UpdateFlag; -import core.net.packet.IoBuffer; - -/** - * The NPC force chat update flag. - * @author Emperor - * - */ -public final class NPCForceChat extends UpdateFlag { - - /** - * Constructs a new {@code NPCForceChat} {@code Object}. - * @param context The chat message. - */ - public NPCForceChat(String context) { - super(context); - } - - @Override - public void write(IoBuffer buffer) { - buffer.putString(context); - } - - @Override - public int data() { - return maskData(); - } - - @Override - public int ordinal() { - return 6; - } - - /** - * Gets the mask data. - * @return The mask data. - */ - public static int maskData() { - return 0x20; - } -} \ No newline at end of file diff --git a/Server/src/main/core/game/world/update/flag/npc/NPCGraphic.java b/Server/src/main/core/game/world/update/flag/npc/NPCGraphic.java deleted file mode 100644 index f15c01fa6..000000000 --- a/Server/src/main/core/game/world/update/flag/npc/NPCGraphic.java +++ /dev/null @@ -1,45 +0,0 @@ -package core.game.world.update.flag.npc; - -import core.game.world.update.flag.UpdateFlag; -import core.game.world.update.flag.context.Graphics; -import core.net.packet.IoBuffer; - -/** - * Handles an NPC's graphic update flag. - * @author Emperor - * - */ -public final class NPCGraphic extends UpdateFlag { - - /** - * Constructs a new {@code NPCGraphic} {@code Object}. - * @param context The graphics. - */ - public NPCGraphic(Graphics context) { - super(context); - } - - @Override - public void write(IoBuffer buffer) { - buffer.putShortA(context.getId()).putLEInt(context.getHeight() << 16 | context.getDelay()); - } - - @Override - public int data() { - return maskData(); - } - - @Override - public int ordinal() { - return 4; - } - - /** - * Gets the mask data. - * @return The mask data. - */ - public static int maskData() { - return 0x80; - } - -} \ No newline at end of file diff --git a/Server/src/main/core/game/world/update/flag/npc/NPCHitFlag.java b/Server/src/main/core/game/world/update/flag/npc/NPCHitFlag.java deleted file mode 100644 index 7fbde01ac..000000000 --- a/Server/src/main/core/game/world/update/flag/npc/NPCHitFlag.java +++ /dev/null @@ -1,56 +0,0 @@ -package core.game.world.update.flag.npc; - -import core.game.node.entity.Entity; -import core.game.world.update.flag.UpdateFlag; -import core.game.world.update.flag.context.HitMark; -import core.net.packet.IoBuffer; - -/** - * The NPC's main hit update flag. - * @author Emperor - * - */ -public final class NPCHitFlag extends UpdateFlag { - - /** - * Constructs a new {@code NPCHitFlag} {@code Object}. - * @param context The hit mark. - */ - public NPCHitFlag(HitMark context) { - super(context); - } - - @Override - public void write(IoBuffer buffer) { - Entity e = context.getEntity(); - int max = e.getSkills().getMaximumLifepoints(); - int ratio = 0; - if (max > 0) { - if (max < e.getSkills().getLifepoints()) { - ratio = 255; - } else { - ratio = e.getSkills().getLifepoints() * 255 / max; - } - } - buffer.put(context.getDamage()).putC(context.getType()).putS(ratio); - } - - @Override - public int data() { - return maskData(); - } - - @Override - public int ordinal() { - return 0; - } - - /** - * Gets the mask data. - * @return The mask data. - */ - public static int maskData() { - return 0x40; - } - -} \ No newline at end of file diff --git a/Server/src/main/core/game/world/update/flag/npc/NPCHitFlag1.java b/Server/src/main/core/game/world/update/flag/npc/NPCHitFlag1.java deleted file mode 100644 index e443f6f0f..000000000 --- a/Server/src/main/core/game/world/update/flag/npc/NPCHitFlag1.java +++ /dev/null @@ -1,45 +0,0 @@ -package core.game.world.update.flag.npc; - -import core.game.world.update.flag.UpdateFlag; -import core.game.world.update.flag.context.HitMark; -import core.net.packet.IoBuffer; - -/** - * The NPC's supporting hit update flag. - * @author Emperor - * - */ -public class NPCHitFlag1 extends UpdateFlag { - - /** - * Constructs a new {@code NPCHitFlag1} {@code Object}. - * @param context The hit mark. - */ - public NPCHitFlag1(HitMark context) { - super(context); - } - - @Override - public void write(IoBuffer buffer) { - buffer.putC(context.getDamage()).putS(context.getType()); - } - - @Override - public int data() { - return maskData(); - } - - @Override - public int ordinal() { - return 1; - } - - /** - * Gets the mask data. - * @return The mask data. - */ - public static int maskData() { - return 0x2; - } - -} \ No newline at end of file diff --git a/Server/src/main/core/game/world/update/flag/npc/NPCSwitchId.java b/Server/src/main/core/game/world/update/flag/npc/NPCSwitchId.java deleted file mode 100644 index a72c45f1e..000000000 --- a/Server/src/main/core/game/world/update/flag/npc/NPCSwitchId.java +++ /dev/null @@ -1,44 +0,0 @@ -package core.game.world.update.flag.npc; - -import core.game.world.update.flag.UpdateFlag; -import core.net.packet.IoBuffer; - -/** - * The switch NPC id update flag. - * @author Emperor - * - */ -public final class NPCSwitchId extends UpdateFlag { - - /** - * Constructs a new {@code NPCSwitchId} {@code Object}. - * @param context The new NPC id. - */ - public NPCSwitchId(int context) { - super(context); - } - - @Override - public void write(IoBuffer buffer) { - buffer.putLEShort(context); - } - - @Override - public int data() { - return 0x1; - } - - @Override - public int ordinal() { - return getOrdinal(); - } - - /** - * Gets the mask ordinal. - * @return The ordinal. - */ - public static int getOrdinal() { - return 5; - } - -} \ No newline at end of file diff --git a/Server/src/main/core/game/world/update/flag/player/AnimationFlag.java b/Server/src/main/core/game/world/update/flag/player/AnimationFlag.java deleted file mode 100644 index 925602e21..000000000 --- a/Server/src/main/core/game/world/update/flag/player/AnimationFlag.java +++ /dev/null @@ -1,43 +0,0 @@ -package core.game.world.update.flag.player; - -import core.game.world.update.flag.UpdateFlag; -import core.game.world.update.flag.context.Animation; -import core.net.packet.IoBuffer; - -/** - * Handles the animation update flag. - * @author Emperor - */ -public final class AnimationFlag extends UpdateFlag { - - /** - * Constructs a new {@code AnimationFlag} {@code Object}. - */ - public AnimationFlag(Animation context) { - super(context); - } - - @Override - public void write(IoBuffer buffer) { - buffer.putShort(context.getId()); - buffer.put(context.getDelay()); - } - - @Override - public int data() { - return maskData(); - } - - @Override - public int ordinal() { - return 2; - } - - /** - * Gets the mask data of the animation update flag. - * @return The mask data. - */ - public static int maskData() { - return 0x8; - } -} \ No newline at end of file diff --git a/Server/src/main/core/game/world/update/flag/player/AppearanceFlag.java b/Server/src/main/core/game/world/update/flag/player/AppearanceFlag.java deleted file mode 100644 index 3fb42f2bb..000000000 --- a/Server/src/main/core/game/world/update/flag/player/AppearanceFlag.java +++ /dev/null @@ -1,91 +0,0 @@ -package core.game.world.update.flag.player; - -import core.game.node.entity.player.Player; -import core.game.node.entity.player.link.appearance.Appearance; -import core.game.node.entity.player.link.appearance.BodyPart; -import core.game.world.update.flag.UpdateFlag; -import core.net.packet.IoBuffer; -import core.tools.StringUtils; - -/** - * Handles the appearance update flag. - * @author Emperor - * - */ -public final class AppearanceFlag extends UpdateFlag { - - /** - * Constructs a new {@code AppearanceFlag} {@code Object}. - * @param player The player. - */ - public AppearanceFlag(Player player) { - super(player); - } - - @Override - public void write(IoBuffer buffer) { - Appearance appearance = context.getAppearance(); - appearance.prepareBodyData(context); - IoBuffer block = new IoBuffer(); - int settings = appearance.getGender().toByte(); - if (context.size() > 1) { - settings |= (context.size() - 1) << 3; - } - block.put(settings); // settings hash. - block.put(appearance.getSkullIcon()); // Skull icon - block.put(appearance.getHeadIcon()); // Head icon - int npcId = appearance.getNpcId(); - if (npcId == -1) { - int[] parts = appearance.getBodyParts(); - for (int i = 0; i < 12; i++) { - int value = parts[i]; - if (value == 0) { - block.put(0); - } else { - block.putShort(value); - } - } - } else { - block.putShort(-1); - block.putShort(npcId); - block.put(255); - } - final BodyPart[] colors = new BodyPart[] { appearance.getHair(), appearance.getTorso(), appearance.getLegs(), appearance.getFeet(), appearance.getSkin() }; - for (int i = 0; i < colors.length; i++) {// colours - block.put(colors[i].getColor()); - } - block.putShort(appearance.getRenderAnimation()); - block.putLong(StringUtils.stringToLong(context.getUsername())); - block.put(context.getProperties().getCurrentCombatLevel()); - block.putShort(0); - block.put(0); - buffer.putA(block.toByteBuffer().position()); - buffer.put(block); - } - - @Override - public int data() { - return getData(); - } - - @Override - public int ordinal() { - return getOrdinal(); - } - - /** - * Gets the ordinal for this flag. - * @return The flag ordinal. - */ - public static int getOrdinal() { - return 3; - } - - /** - * Gets the mask data. - * @return The mask data. - */ - public static int getData() { - return 0x4; - } -} \ No newline at end of file diff --git a/Server/src/main/core/game/world/update/flag/player/ChatFlag.java b/Server/src/main/core/game/world/update/flag/player/ChatFlag.java deleted file mode 100644 index cdefd314d..000000000 --- a/Server/src/main/core/game/world/update/flag/player/ChatFlag.java +++ /dev/null @@ -1,59 +0,0 @@ -package core.game.world.update.flag.player; - -import core.game.node.entity.player.info.Rights; -import core.game.world.update.flag.UpdateFlag; -import core.game.world.update.flag.context.ChatMessage; -import core.net.packet.IoBuffer; -import core.tools.StringUtils; - -import java.nio.charset.StandardCharsets; - -/** - * Handles the chat flag. - * @author Emperor - */ -public class ChatFlag extends UpdateFlag { - - /** - * Constructs a new {@code ChatFlag.java} {@code Object}. - * @param context The context. - */ - public ChatFlag(ChatMessage context) { - super(context); - } - - @Override - public void write(IoBuffer buffer) { - byte[] chatStr = new byte[256]; - chatStr[0] = (byte) context.getText().length(); - int offset = 1 + StringUtils.encryptPlayerChat(chatStr, 0, 1, context.getText().length(), context.getText().getBytes(StandardCharsets.UTF_8)); - buffer.putLEShort(context.getEffects()); // 0x8000 does something (you'd - // need to send something - // extra. - if(context.isQuickChat){ - buffer.put((byte) 4); - } else { - buffer.put((byte) Rights.getChatIcon(context.getPlayer())); - } - buffer.put(offset + 1); - buffer.putReverse(chatStr, 0, offset); - } - - @Override - public int data() { - return maskData(); - } - - @Override - public int ordinal() { - return 0; - } - - /** - * Gets the mask data of the chat update flag. - * @return The mask data. - */ - public static int maskData() { - return 0x80; - } -} \ No newline at end of file diff --git a/Server/src/main/core/game/world/update/flag/player/FaceEntityFlag.java b/Server/src/main/core/game/world/update/flag/player/FaceEntityFlag.java deleted file mode 100644 index fa2327027..000000000 --- a/Server/src/main/core/game/world/update/flag/player/FaceEntityFlag.java +++ /dev/null @@ -1,45 +0,0 @@ -package core.game.world.update.flag.player; - -import core.game.node.entity.Entity; -import core.game.world.update.flag.UpdateFlag; -import core.net.packet.IoBuffer; - -/** - * The face entity update flag. - * @author Emperor - * - */ -public final class FaceEntityFlag extends UpdateFlag { - - /** - * Constructs a new {@code FaceEntityFlag} {@code Object}. - * @param context The entity to face. - */ - public FaceEntityFlag(Entity context) { - super(context); - } - - @Override - public void write(IoBuffer buffer) { - buffer.putShortA(context == null ? -1 : context.getClientIndex()); - } - - @Override - public int data() { - return 0x2; - } - - @Override - public int ordinal() { - return getOrdinal(); - } - - /** - * Gets the mask ordinal. - * @return The ordinal. - */ - public static int getOrdinal() { - return 4; - } - -} \ No newline at end of file diff --git a/Server/src/main/core/game/world/update/flag/player/FaceLocationFlag.java b/Server/src/main/core/game/world/update/flag/player/FaceLocationFlag.java deleted file mode 100644 index 022d33a71..000000000 --- a/Server/src/main/core/game/world/update/flag/player/FaceLocationFlag.java +++ /dev/null @@ -1,84 +0,0 @@ -package core.game.world.update.flag.player; - -import core.game.node.Node; -import core.game.node.scenery.Scenery; -import core.game.world.map.Location; -import core.game.world.update.flag.UpdateFlag; -import core.net.packet.IoBuffer; - -/** - * Handles the face coordinates update flag. - * @author Emperor - */ -public class FaceLocationFlag extends UpdateFlag { - - /** - * Constructs a new {@code FaceLocationFlag} {@code Object}. - * @param location The location to face. - */ - public FaceLocationFlag(Location location) { - super(location); - } - - /** - * Gets the face location of the node. - * @param n The facing node. - * @param node The node to face. - * @return The location. - */ - public static Location getFaceLocation(Node n, Node node) { - int x = node.size() >> 1; - int y = node.size() >> 1; - if (node instanceof Scenery) { - Scenery o = (Scenery) node; - x = o.getDefinition().sizeX >> 1; - y = o.getDefinition().sizeY >> 1; - if (o.getRotation() % 2 != 0) { - x = y; - y = o.getDefinition().sizeX >> 1; - } - if (n.getLocation().equals(o.getLocation()) && node.size() == 1) { - switch (o.getRotation()) { - case 0: - x -= 1; - break; - case 1: - y += 1; - break; - case 2: - x += 1; - break; - case 3: - y -= 1; - break; - } - } - } - return node.getLocation().transform(x, y, 0); - } - - @Override - public void write(IoBuffer buffer) { - buffer.putShort((context.getX() << 1) + 1); - buffer.putLEShortA((context.getY() << 1) + 1); - } - - @Override - public int data() { - return 0x40; - } - - @Override - public int ordinal() { - return getOrdinal(); - } - - /** - * Gets the mask ordinal. - * @return The ordinal. - */ - public static int getOrdinal() { - return 10; - } - -} \ No newline at end of file diff --git a/Server/src/main/core/game/world/update/flag/player/ForceChatFlag.java b/Server/src/main/core/game/world/update/flag/player/ForceChatFlag.java deleted file mode 100644 index 318d008ed..000000000 --- a/Server/src/main/core/game/world/update/flag/player/ForceChatFlag.java +++ /dev/null @@ -1,44 +0,0 @@ -package core.game.world.update.flag.player; - -import core.game.world.update.flag.UpdateFlag; -import core.net.packet.IoBuffer; - -/** - * The force chat update flag. - * @author Emperor - * - */ -public final class ForceChatFlag extends UpdateFlag { - - /** - * Constructs a new {@code ForceChatFlag} {@code Object}. - * @param context The chat message. - */ - public ForceChatFlag(String context) { - super(context); - } - - @Override - public void write(IoBuffer buffer) { - buffer.putString(context); - } - - @Override - public int data() { - return maskData(); - } - - @Override - public int ordinal() { - return 6; - } - - /** - * Gets the mask data. - * @return The mask data. - */ - public static int maskData() { - return 0x20; - } - -} \ No newline at end of file diff --git a/Server/src/main/core/game/world/update/flag/player/ForceMovementFlag.java b/Server/src/main/core/game/world/update/flag/player/ForceMovementFlag.java deleted file mode 100644 index 5625e51a0..000000000 --- a/Server/src/main/core/game/world/update/flag/player/ForceMovementFlag.java +++ /dev/null @@ -1,60 +0,0 @@ -package core.game.world.update.flag.player; - -import core.game.node.entity.Entity; -import core.game.node.entity.impl.ForceMovement; -import core.game.node.entity.player.Player; -import core.game.world.map.Location; -import core.game.world.update.flag.UpdateFlag; -import core.net.packet.IoBuffer; - -/** - * Handles the force movement player update flag. - * @author Emperor - * - */ -public final class ForceMovementFlag extends UpdateFlag { - - /** - * Constructs a new {@code ForceMovementFlag} {@code Object}. - * @param forceMovement The force movement data. - */ - public ForceMovementFlag(ForceMovement forceMovement) { - super(forceMovement); - } - - @Override - public void write(IoBuffer buffer) { - } - - @Override - public void writeDynamic(IoBuffer buffer, Entity e) { - Location l = ((Player) e).getPlayerFlags().getLastSceneGraph(); - buffer.putC(context.getStart().getSceneX(l)) // Start location - .put(context.getStart().getSceneY(l)).putA(context.getDestination().getSceneX(l)) // Destination - // location - .put(context.getDestination().getSceneY(l)).putLEShort(context.getCommenceSpeed() * 30)// Commencing - // speed - .putLEShort((context.getCommenceSpeed() * 30) + (context.getPathSpeed() * 30 + 1)) // Path - // speed - .putC(context.getDirection().toInteger()); - } - - @Override - public int data() { - return maskData(); - } - - @Override - public int ordinal() { - return 5; - } - - /** - * Gets the mask data. - * @return The mask data. - */ - public static int maskData() { - return 0x400; - } - -} \ No newline at end of file diff --git a/Server/src/main/core/game/world/update/flag/player/GraphicFlag.java b/Server/src/main/core/game/world/update/flag/player/GraphicFlag.java deleted file mode 100644 index 661a9472b..000000000 --- a/Server/src/main/core/game/world/update/flag/player/GraphicFlag.java +++ /dev/null @@ -1,45 +0,0 @@ -package core.game.world.update.flag.player; - -import core.game.world.update.flag.UpdateFlag; -import core.game.world.update.flag.context.Graphics; -import core.net.packet.IoBuffer; - -/** - * Handles the graphic update flag. - * @author Emperor - * - */ -public final class GraphicFlag extends UpdateFlag { - - /** - * Constructs a new {@code GraphicFlag} {@code Object}. - * @param context The context. - */ - public GraphicFlag(Graphics context) { - super(context); - } - - @Override - public void write(IoBuffer buffer) { - buffer.putLEShort(context.getId()); - buffer.putIntB(context.getHeight() << 16 | context.getDelay()); - } - - @Override - public int data() { - return maskData(); - } - - @Override - public int ordinal() { - return 9; - } - - /** - * Gets the mask data of the graphic update flag. - * @return The mask data. - */ - public static int maskData() { - return 0x100; - } -} \ No newline at end of file diff --git a/Server/src/main/core/game/world/update/flag/player/HitUpdateFlag.java b/Server/src/main/core/game/world/update/flag/player/HitUpdateFlag.java deleted file mode 100644 index 702befba9..000000000 --- a/Server/src/main/core/game/world/update/flag/player/HitUpdateFlag.java +++ /dev/null @@ -1,52 +0,0 @@ -package core.game.world.update.flag.player; - -import core.game.node.entity.Entity; -import core.game.world.update.flag.UpdateFlag; -import core.game.world.update.flag.context.HitMark; -import core.net.packet.IoBuffer; - -/** - * The main hit update flag. - * @author Emperor - * - */ -public final class HitUpdateFlag extends UpdateFlag { - - /** - * Constructs a new {@code HitUpdateFlag} {@code Object}. - * @param context The hit mark. - */ - public HitUpdateFlag(HitMark context) { - super(context); - } - - @Override - public void write(IoBuffer buffer) { - Entity e = context.getEntity(); - int max = e.getSkills().getMaximumLifepoints(); - int ratio = 255; - if (max > e.getSkills().getLifepoints()) { - ratio = e.getSkills().getLifepoints() * 255 / max; - } - buffer.putSmart(context.getDamage()).putA(context.getType()).putS(ratio); - } - - @Override - public int data() { - return maskData(); - } - - @Override - public int ordinal() { - return 1; - } - - /** - * Gets the mask data. - * @return The mask data. - */ - public static int maskData() { - return 0x1; - } - -} \ No newline at end of file diff --git a/Server/src/main/core/game/world/update/flag/player/HitUpdateFlag1.java b/Server/src/main/core/game/world/update/flag/player/HitUpdateFlag1.java deleted file mode 100644 index a4f35bb4e..000000000 --- a/Server/src/main/core/game/world/update/flag/player/HitUpdateFlag1.java +++ /dev/null @@ -1,45 +0,0 @@ -package core.game.world.update.flag.player; - -import core.game.world.update.flag.UpdateFlag; -import core.game.world.update.flag.context.HitMark; -import core.net.packet.IoBuffer; - -/** - * The supportive hit update flag. - * @author Emperor - * - */ -public final class HitUpdateFlag1 extends UpdateFlag { - - /** - * Constructs a new {@code HitUpdateFlag1} {@code Object}. - * @param context The hit mark. - */ - public HitUpdateFlag1(HitMark context) { - super(context); - } - - @Override - public void write(IoBuffer buffer) { - buffer.putSmart(context.getDamage()).putS(context.getType()); - } - - @Override - public int data() { - return maskData(); - } - - @Override - public int ordinal() { - return 7; - } - - /** - * Gets the mask data. - * @return The mask data. - */ - public static int maskData() { - return 0x200; - } - -} \ No newline at end of file diff --git a/Server/src/main/core/game/worldevents/WorldEvent.kt b/Server/src/main/core/game/worldevents/WorldEvent.kt index fae25a3dd..705d458cd 100644 --- a/Server/src/main/core/game/worldevents/WorldEvent.kt +++ b/Server/src/main/core/game/worldevents/WorldEvent.kt @@ -1,54 +1,41 @@ 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(){ } /** * Used to log world event messages in a standard and organized way. */ fun log(message: String){ - SystemLogger.logInfo(this::class.java, "[World Events($name)] $message") + core.api.log(this::class.java, Log.FINE, "[World Events($name)] $message") } - - /** - * Used to start, or "fire", world events. - */ - open fun fireEvent() {} } @@ -73,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/HolidayRandomEventNPC.kt b/Server/src/main/core/game/worldevents/holiday/HolidayRandomEventNPC.kt new file mode 100644 index 000000000..824451832 --- /dev/null +++ b/Server/src/main/core/game/worldevents/holiday/HolidayRandomEventNPC.kt @@ -0,0 +1,85 @@ +package core.game.worldevents.holiday + +import core.api.* +import core.game.interaction.MovementPulse +import core.game.node.entity.impl.PulseType +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.game.world.map.path.Pathfinder +import core.tools.secondsToTicks +import org.rs09.consts.Sounds +import kotlin.reflect.full.createInstance + +abstract class HolidayRandomEventNPC(id:Int) : NPC(id) { + lateinit var player: Player + var spawnLocation: Location? = null + var initialized = false + var finalized = false + var timerPaused = false + var ticksLeft = secondsToTicks(30) + + open fun create(player: Player, type: String = ""): HolidayRandomEventNPC { + val event = this::class.createInstance() + event.player = player + event.spawnLocation = RegionManager.getSpawnLocation(player, this) + return event + } + + open fun terminate() { + pulseManager.clear(PulseType.STANDARD) + if (initialized && !finalized) { + poofClear(this) + playGlobalAudio(this.location, Sounds.SMOKEPUFF2_1931, 100) + } + finalized = true + } + + open fun follow() { + pulseManager.run((object : MovementPulse(this, player, Pathfinder.DUMB) { + override fun pulse(): Boolean { + face(player) + return false + } + }), PulseType.STANDARD) + } + + override fun tick() { + super.tick() + if (player.getAttribute("holiday-npc", null) != this) { + terminate() + return + } + if (!player.getAttribute("holidayrandom:pause", false)) { + ticksLeft-- + } + if (!pulseManager.hasPulseRunning() && !finalized) { + follow() + } + if (!player.isActive || !player.location.withinDistance(location, 10)) { + terminate() + } + if (ticksLeft <= 0 && initialized) { + terminate() + initialized = false + } + } + + override fun init() { + initialized = true + finalized = false + timerPaused = false + spawnLocation ?: terminate() + location = spawnLocation + player.setAttribute("holiday-npc", this) + super.init() + } + + override fun clear() { + super.clear() + if(player.getAttribute("holiday-npc", null) == this) player.removeAttribute("holiday-npc") + } + + abstract fun talkTo(npc: NPC) +} \ No newline at end of file diff --git a/Server/src/main/core/game/worldevents/holiday/HolidayRandomEvents.kt b/Server/src/main/core/game/worldevents/holiday/HolidayRandomEvents.kt new file mode 100644 index 000000000..f1560ba0f --- /dev/null +++ b/Server/src/main/core/game/worldevents/holiday/HolidayRandomEvents.kt @@ -0,0 +1,49 @@ +package core.game.worldevents.holiday + +import core.game.worldevents.holiday.christmas.randoms.* +import core.game.worldevents.holiday.halloween.randoms.* + +enum class HolidayRandomEvents(val npc: HolidayRandomEventNPC, val type: String) { + BLACK_CAT(npc = BlackCatHolidayRandomNPC(), "halloween"), + SPIDER(npc = SpiderHolidayRandomNPC(), "halloween"), + GHOST(npc = GhostHolidayRandomNPC(), "halloween"), + ZOMBIE(npc = ZombieHolidayRandomNPC(), "halloween"), + WITCH(npc = WitchHolidayRandomNPC(), "halloween"), + DEATH(npc = DeathHolidayRandomNPC(), "halloween"), + VAMPIRE(npc = VampireHolidayRandomNPC(), "halloween"), + CHOIR(npc = ChoirHolidayRandomNPC(), "christmas"), + SANTA(npc = SantaHolidayRandomNPC(), "christmas"), + SNOWMAN_FIGHT(npc = SnowmanFightHolidayRandom(), "christmas"), + JACK_FROST(npc = JackFrostHolidayRandomNPC(), "christmas"), + SNOWMAN(npc = SnowmanHolidayRandomNPC(), "christmas"), + SNOWSTORM(npc = SnowStormHolidayRandomNPC(), "christmas"), + COOK(npc = CookHolidayRandomNPC(), "christmas"); + + companion object { + @JvmField + val halloweenEventsList = ArrayList() + val christmasEventsList = ArrayList() + val holidayRandomIDs = HolidayRandomEvents.values().map { it.npc.id }.toList() + + init { + populateMappings() + } + + fun getHolidayRandom(type: String): HolidayRandomEvents { + return when (type) { + "halloween" -> halloweenEventsList.random() + "christmas" -> christmasEventsList.random() + else -> throw Exception("Invalid event type!") + } + } + + private fun populateMappings() { + for (event in values()) { + when(event.type) { + "halloween" -> halloweenEventsList.add(event) + "christmas" -> christmasEventsList.add(event) + } + } + } + } +} diff --git a/Server/src/main/core/game/worldevents/holiday/HolidayRandoms.kt b/Server/src/main/core/game/worldevents/holiday/HolidayRandoms.kt new file mode 100644 index 000000000..cf63a2e6d --- /dev/null +++ b/Server/src/main/core/game/worldevents/holiday/HolidayRandoms.kt @@ -0,0 +1,271 @@ +package core.game.worldevents.holiday + +import content.global.ame.RandomEventNPC +import core.ServerConstants +import core.api.* +import core.game.node.entity.Entity +import core.game.node.entity.player.Player +import core.game.system.command.Privilege +import core.game.system.timer.PersistTimer +import core.game.world.map.zone.ZoneRestriction +import core.game.world.repository.Repository +import core.tools.RandomFunction +import core.tools.colorize +import core.tools.minutesToTicks +import org.json.simple.JSONObject +import java.time.LocalDate +import java.time.Month + +class HolidayRandoms() : PersistTimer(0, "holiday", isAuto = true), Commands { + var paused = false + var nextRandom: HolidayRandomEvents? = null + var currentHoliday: String? = null + val halloweenStartDate = LocalDate.of(LocalDate.now().year, Month.OCTOBER, 17) + val halloweenEndDate = LocalDate.of(LocalDate.now().year, Month.NOVEMBER, 7) + val christmasStartDate = LocalDate.of(LocalDate.now().year, Month.DECEMBER, 1) + val christmasEndDate = LocalDate.of(LocalDate.now().year, Month.DECEMBER, 31) + + override fun run(entity: Entity): Boolean { + if (entity !is Player) return false + + setNextExecution() + + if (!canSpawn(entity)) { + delayNextExecution() + return true + } + + val eventSelection = rollEventPool() + val eventNpc = eventSelection.npc.create(entity) + if (eventNpc.spawnLocation == null) { + entity.debug("[HolidayRandom] Attempted to spawn random, but spawnLoc was null.") + delayNextExecution() + return true + } + + eventNpc.init() + setAttribute(entity, EVENT_NPC, eventNpc) + setAttribute(eventNpc, EVENT_NPC, eventNpc) + entity.debug("[HolidayRandom] Fired ${eventSelection.name}.") + return true + } + + override fun onRegister(entity: Entity) { + if (entity !is Player || entity.isArtificial || !ServerConstants.HOLIDAY_EVENT_RANDOMS) { + entity.timers.removeTimer(this) + return + } + + val player = entity.asPlayer() + when (checkIfHoliday()) { + "halloween" -> { + sendMessage(player, colorize("%OA chill goes down your spine...")) + currentHoliday = "halloween" + } + "christmas" -> { + sendMessage(player, colorize("%GHappy Holidays.")) + currentHoliday = "christmas" + } + "none" -> player.timers.removeTimer(this) + } + + if (runInterval == 0) + setNextExecution() + } + + fun checkIfHoliday(): String { + val currentDate = LocalDate.now() + if ((!currentDate.isBefore(halloweenStartDate) && !currentDate.isAfter(halloweenEndDate)) || ServerConstants.FORCE_HALLOWEEN_EVENTS) + return "halloween" + + if ((!currentDate.isBefore(christmasStartDate) && !currentDate.isAfter(christmasEndDate)) || ServerConstants.FORCE_CHRISTMAS_EVENTS) + return "christmas" + + return "none" + } + + override fun save(root: JSONObject, entity: Entity) { + root["ticksRemaining"] = (nextExecution - getWorldTicks()).toString() + } + + override fun parse(root: JSONObject, entity: Entity) { + runInterval = (root["ticksRemaining"]?.toString()?.toIntOrNull() ?: 0) + nextExecution = getWorldTicks() + runInterval + } + + private fun canSpawn(entity: Entity) : Boolean { + if (entity.zoneMonitor.isRestricted(ZoneRestriction.RANDOM_EVENTS) || getAttribute(entity, "re-npc", null) != null) + return false + + val current = getAttribute(entity, EVENT_NPC, null) + if (current != null) + return false + + if (paused || entity.inCombat()) + return false + + return true + } + + private fun delayNextExecution() { + runInterval = 50 + nextExecution = getWorldTicks() + runInterval + + } + + private fun setNextExecution() { + runInterval = RandomFunction.random(MIN_DELAY_TICKS, MAX_DELAY_TICKS + 1) + nextExecution = getWorldTicks() + runInterval + } + + private fun rollEventPool() : HolidayRandomEvents { + if (nextRandom != null) { + val result = nextRandom!! + nextRandom = null + return result + } + return when (currentHoliday) { + "halloween" -> HolidayRandomEvents.getHolidayRandom("halloween") + "christmas" -> HolidayRandomEvents.getHolidayRandom("christmas") + else -> throw Exception("Invalid event type!") + } + } + + override fun defineCommands() { + define("hrevent", Privilege.ADMIN, "::hrevent [-p] player name [-e event name]", "Spawns a holiday random event for the target player.
Optional -e parameter to pass a specific event.") { player, args -> + if (args.size == 1) { + val possible = HolidayRandomEvents.values() + for (event in possible) { + notify(player, event.name.lowercase()) + } + return@define + } + + val arg = parseCommandArgs(args.joinToString(" ")) + val target = Repository.getPlayerByName(arg.targetPlayer) + + if (getTimer(player) == null) + reject(player, "No holiday random events are active. To force a holiday's random events use ::forcehrevents") + + if (target == null) + reject(player, "Unable to find user ${arg.targetPlayer}.") + + forceEvent(target!!, arg.targetEvent) + } + + define("forcehrevents", Privilege.ADMIN, "::forcehrevents [eventname]", "Force enable holiday random events.") { player, args -> + if (args.size == 1) { + notify(player, "Holidays: halloween, christmas") + return@define + } + val event = args[1] + if (checkIfHoliday() != "none") + reject(player, "Holiday randoms are already enabled: ${checkIfHoliday()}. Use ::stophrevents first.") + ServerConstants.HOLIDAY_EVENT_RANDOMS = true + when (event) { + "halloween" -> { + ServerConstants.FORCE_HALLOWEEN_EVENTS = true + for (p in Repository.players) { + if (getTimer(p) != null || p.isArtificial) { + continue + } + notify(p, colorize("%RHalloween Randoms are now enabled!")) + registerTimer(p, HolidayRandoms()) + } + } + "christmas" -> { + ServerConstants.FORCE_CHRISTMAS_EVENTS = true + for (p in Repository.players) { + if (getTimer(p) != null || p.isArtificial) { + continue + } + notify(p, colorize("%GChristmas Randoms are now enabled!")) + registerTimer(p, HolidayRandoms()) + } + } + else -> reject(player, "Invalid event!") + } + } + + define("stophrevents", Privilege.ADMIN, "::stophrevents", "Stops all holiday random events.") { player, _ -> + if (checkIfHoliday() == "none" || !ServerConstants.HOLIDAY_EVENT_RANDOMS) + reject(player, "No holiday random events are currently active.") + ServerConstants.HOLIDAY_EVENT_RANDOMS = false + ServerConstants.FORCE_HALLOWEEN_EVENTS = false + ServerConstants.FORCE_CHRISTMAS_EVENTS = false + for (p in Repository.players) { + if (getTimer(p) == null) { + continue + } + removeHolidayTimer(p) + notify(p, "Holiday random events are now disabled!") + } + } + } + + data class CommandArgs (val targetPlayer: String, val targetEvent: HolidayRandomEvents?) + + companion object { + const val EVENT_NPC = "holiday-npc" + val MIN_DELAY_TICKS = minutesToTicks(30) + val MAX_DELAY_TICKS = minutesToTicks(90) + + fun terminateEventNpc (player: Player) { + getEventNpc(player)?.terminate() + } + + fun getEventNpc (player: Player) : HolidayRandomEventNPC? { + return getAttribute(player, EVENT_NPC, null) + } + + fun pause (player: Player) { + val timer = getTimer(player) ?: return + timer.paused = true + } + + fun unpause (player: Player) { + val timer = getTimer(player) ?: return + timer.paused = false + } + + fun removeHolidayTimer (player: Player) { + val timer = getTimer(player) ?: return + removeTimer(player, timer) + } + + fun forceEvent (player: Player, event: HolidayRandomEvents? = null) { + val timer = getTimer(player) ?: return + timer.nextExecution = getWorldTicks() + timer.nextRandom = event + } + + fun parseCommandArgs (args: String, commandName: String = "hrevent") : CommandArgs { + val tokens = args.split(" ") + val modeTokens = arrayOf("-p", "-e") + + var userString = "" + var eventString = "" + var lastMode = "-p" + + for (token in tokens) { + when (token) { + commandName -> continue + in modeTokens -> lastMode = token + else -> when (lastMode) { + "-p" -> userString += "$token " + "-e" -> eventString += "$token " + } + } + } + + val username = userString.trim().lowercase().replace(" ", "_") + val eventName = eventString.trim().uppercase().replace(" ", "_") + + var event: HolidayRandomEvents? = null + + try { event = HolidayRandomEvents.valueOf(eventName) } catch (_: Exception) {} + + return CommandArgs(username, event) + } + } +} \ No newline at end of file diff --git a/Server/src/main/core/game/worldevents/holiday/ResetHolidayAppearance.kt b/Server/src/main/core/game/worldevents/holiday/ResetHolidayAppearance.kt new file mode 100644 index 000000000..079c40c51 --- /dev/null +++ b/Server/src/main/core/game/worldevents/holiday/ResetHolidayAppearance.kt @@ -0,0 +1,23 @@ +package core.game.worldevents.holiday + +import core.api.playAudio +import core.api.sendMessage +import core.api.visualize +import core.game.node.entity.Entity +import core.game.node.entity.player.Player +import core.game.system.timer.RSTimer +import core.tools.minutesToTicks +import core.tools.secondsToTicks +import org.rs09.consts.Sounds + +class ResetHolidayAppearance() : RSTimer(minutesToTicks(1), "reset-holiday-appearance") { + override fun run(entity: Entity): Boolean { + if (entity is Player) { + entity.asPlayer().appearance.transformNPC(-1) + playAudio(entity.asPlayer(), Sounds.WEAKEN_HIT_3010) + visualize(entity, -1, 86) + } + entity.timers.removeTimer(this) + return true + } +} \ No newline at end of file diff --git a/Server/src/main/core/game/worldevents/holiday/christmas/Giftmas.kt b/Server/src/main/core/game/worldevents/holiday/christmas/Giftmas.kt index 76749653b..e77df1a18 100644 --- a/Server/src/main/core/game/worldevents/holiday/christmas/Giftmas.kt +++ b/Server/src/main/core/game/worldevents/holiday/christmas/Giftmas.kt @@ -123,7 +123,7 @@ class Giftmas : Commands, StartupListener, LoginListener, InteractionListener { private val MESSAGE_DAILYXP_REACHED_SKILLING = colorize("%RYou have reached your daily limit of presents from skilling!") private val MESSAGE_DAILYXP_REACHED_COMBAT = colorize("%RYou have reached your daily limit of presents from combat!") private val MESSAGE_PRESENT_GRANTED = colorize("%GYou find a present while training!") - private val MBOX_LOOT = WeightBasedTable.create( + val MBOX_LOOT = WeightBasedTable.create( WeightedItem(Items.TOY_HORSEY_2520, 1, 1, 0.025), WeightedItem(Items.TOY_HORSEY_2522, 1, 1, 0.025), WeightedItem(Items.TOY_HORSEY_2524, 1, 1, 0.025), diff --git a/Server/src/main/core/game/worldevents/holiday/christmas/randoms/ChoirHolidayRandomNPC.kt b/Server/src/main/core/game/worldevents/holiday/christmas/randoms/ChoirHolidayRandomNPC.kt new file mode 100644 index 000000000..b63be9fdd --- /dev/null +++ b/Server/src/main/core/game/worldevents/holiday/christmas/randoms/ChoirHolidayRandomNPC.kt @@ -0,0 +1,63 @@ +package core.game.worldevents.holiday.christmas.randoms + +import core.api.* +import core.game.interaction.QueueStrength +import core.game.node.entity.npc.NPC +import core.game.worldevents.holiday.HolidayRandomEventNPC +import core.tools.RandomFunction +import org.rs09.consts.NPCs + +class ChoirHolidayRandomNPC() : HolidayRandomEventNPC(NPCs.ZANARIS_CHOIR_3312) { + override fun init() { + super.init() + queueScript(this, 4, QueueStrength.SOFT) { stage: Int -> + when (stage) { + 0 -> { + face(player) + when (RandomFunction.getRandom(3)) { + 0 -> { + sendChat(this, "Jingle bells, jingle bells!") + sendChat(this, "Jingle all the way!", 4) + sendChat(this, "Oh what fun it is to ride", 8) + sendChat(this, "On a one unicorn open sleigh", 12) + sendChat(this, "HEY!", 15) + return@queueScript delayScript(this, 16) + } + 1 -> { + sendChat(this, "Silver bells") + sendChat(this, "Silver bells", 5) + sendChat(this, "It's Christmas time in the city", 10) + sendChat(this, "Ring a ling", 15) + sendChat(this, "Hear them ring", 20) + sendChat(this, "Soon it will be Christmas day", 25) + return@queueScript delayScript(this, 26) + } + 2 -> { + sendChat(this, "Deck the halls with boughs of ranarr") + sendChat(this, "Fa la la la la la la la!", 5) + sendChat(this, "Tis the season to be jolly", 10) + sendChat(this, "Fa la la la la la la la!", 15) + return@queueScript delayScript(this, 16) + } + 3 -> { + sendChat(this, "O Wintumber Tree, O Wintumber tree,") + sendChat(this, "How lovely are your branches!", 5) + sendChat(this, "O Wintumber Tree, O Wintumber tree,", 10) + sendChat(this, "Of all the trees most lovely", 15) + return@queueScript delayScript(this, 16) + } + else -> return@queueScript keepRunning(this) + } + } + 1 -> { + terminate() + return@queueScript stopExecuting(this) + } + else -> return@queueScript stopExecuting(this) + } + } + } + + override fun talkTo(npc: NPC) { + } +} \ No newline at end of file diff --git a/Server/src/main/core/game/worldevents/holiday/christmas/randoms/CookHolidayRandomDialogue.kt b/Server/src/main/core/game/worldevents/holiday/christmas/randoms/CookHolidayRandomDialogue.kt new file mode 100644 index 000000000..012ccf8a5 --- /dev/null +++ b/Server/src/main/core/game/worldevents/holiday/christmas/randoms/CookHolidayRandomDialogue.kt @@ -0,0 +1,50 @@ +package core.game.worldevents.holiday.christmas.randoms + +import core.api.addItemOrDrop +import core.game.dialogue.DialogueFile +import core.game.dialogue.FacialExpression +import core.game.worldevents.holiday.HolidayRandoms +import org.rs09.consts.Items + +class CookHolidayRandomDialogue : DialogueFile() { + private val cakes = listOf(Items.CAKE_1891, Items.CHOCOLATE_CAKE_1897, Items.MINT_CAKE_9475) + override fun handle(componentID: Int, buttonID: Int) { + if (HolidayRandoms.getEventNpc(player!!) == null) + player!!.dialogueInterpreter.close() + + when (stage) { + 0 -> npcl(FacialExpression.HAPPY, "Greetings, ${player!!.username.capitalize()}. Every year around christmas I give away cake to the community. Would you care for a fresh baked cake?").also { stage++ } + 1 -> options("Sure, I will take a cake.", "Why are you giving cakes away?", "No, thanks.").also { stage++ } + 2 -> when (buttonID) { + 1 -> playerl(FacialExpression.HAPPY, "Sure, I will take a cake.").also { stage = 40 } + 2 -> playerl(FacialExpression.HALF_ASKING, "Why are you giving away cakes?").also { stage = 20 } + 3 -> playerl(FacialExpression.NEUTRAL, "No, thanks.").also { stage = 10 } + } + 10 -> npcl(FacialExpression.HAPPY, "Okay, I will find someone else who wants it. Merry Christmas, ${player!!.username.capitalize()}.").also { stage++ } + 11 -> { + HolidayRandoms.terminateEventNpc(player!!) + end() + } + 20 -> npcl(FacialExpression.NEUTRAL, "It's just something I do this time of year... Errr... well...").also { stage++ } + 21 -> playerl(FacialExpression.HALF_ASKING, "Is there something else?").also { stage++ } + 22 -> npcl(FacialExpression.NEUTRAL, "I used to be the cook for the old Duke of Lumbridge. Visiting dignitaries praised me for my fine banquets!"). also { stage++ } + 23 -> npcl(FacialExpression.ANNOYED, "But then someone found a rule that said that only one family could hold that post.").also { stage++ } + 24 -> npcl(FacialExpression.ANGRY, "Overnight I was fired and replaced by some fool who can't even bake a cake without help!").also { stage++ } + 25 -> playerl(FacialExpression.NEUTRAL, "I'm sorry to hear that. It sounds as if you are a great cook!").also { stage++ } + 26 -> npcl(FacialExpression.HAPPY, "Thanks, I am!").also { stage++ } + 27 -> npcl(FacialExpression.LAUGH, "I also do this each year to show everyone how incapable that sorry excuse of a cook they replaced me with is.").also { stage++ } + 28 -> npcl(FacialExpression.HAPPY, "So, did you want a cake?").also { stage++ } + 29 -> options("Sure, I will take a cake.", "No, thanks.").also { stage++ } + 30 -> when (buttonID) { + 1 -> playerl(FacialExpression.HAPPY, "Sure, I will take a cake.").also { stage = 40 } + 2 -> playerl(FacialExpression.NEUTRAL, "No, thanks.").also { stage = 10 } + } + 40 -> npcl(FacialExpression.HAPPY, "Here is a the finest cake you will ever have! Merry Christmas, ${player!!.username.capitalize()}.").also { stage++ } + 41 -> { + HolidayRandoms.terminateEventNpc(player!!) + addItemOrDrop(player!!, cakes.random()) + end() + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/core/game/worldevents/holiday/christmas/randoms/CookHolidayRandomNPC.kt b/Server/src/main/core/game/worldevents/holiday/christmas/randoms/CookHolidayRandomNPC.kt new file mode 100644 index 000000000..3a33d4838 --- /dev/null +++ b/Server/src/main/core/game/worldevents/holiday/christmas/randoms/CookHolidayRandomNPC.kt @@ -0,0 +1,32 @@ +package core.game.worldevents.holiday.christmas.randoms + +import core.api.* +import core.game.node.entity.npc.NPC +import core.game.worldevents.holiday.HolidayRandomEventNPC +import core.tools.RandomFunction +import core.tools.minutesToTicks +import org.rs09.consts.NPCs + +class CookHolidayRandomNPC : HolidayRandomEventNPC(NPCs.COOK_4239) { + private val cookLines = listOf("@name, are you there?", "Would you like a fresh baked cake, @name?", "Happy Holidays, @name!") + private var hasTalkedWith = false + + override fun init() { + ticksLeft = minutesToTicks(3) + sendChat(this, "Happy Holidays, ${player.username.capitalize()}!", 2) + super.init() + } + + override fun tick() { + if (RandomFunction.roll(15) && !hasTalkedWith) + sendChat(this, cookLines.random().replace("@name",player.username.capitalize())) + + super.tick() + } + + override fun talkTo(npc: NPC) { + face(player) + hasTalkedWith = true + openDialogue(player, CookHolidayRandomDialogue(), npc) + } +} \ No newline at end of file diff --git a/Server/src/main/core/game/worldevents/holiday/christmas/randoms/JackFrostHolidayRandomNPC.kt b/Server/src/main/core/game/worldevents/holiday/christmas/randoms/JackFrostHolidayRandomNPC.kt new file mode 100644 index 000000000..01aace80a --- /dev/null +++ b/Server/src/main/core/game/worldevents/holiday/christmas/randoms/JackFrostHolidayRandomNPC.kt @@ -0,0 +1,64 @@ +package core.game.worldevents.holiday.christmas.randoms + + +import core.api.getPathableRandomLocalCoordinate +import core.api.* +import core.game.interaction.QueueStrength +import core.game.node.entity.impl.Projectile +import core.game.node.entity.npc.NPC +import core.game.world.map.path.Pathfinder +import core.game.world.update.flag.context.Graphics +import core.game.worldevents.holiday.HolidayRandomEventNPC +import core.tools.minutesToTicks +import org.rs09.consts.NPCs +import org.rs09.consts.Sounds + +class JackFrostHolidayRandomNPC : HolidayRandomEventNPC(NPCs.JACK_FROST_8517) { + + override fun init() { + ticksLeft = minutesToTicks(1) + spawnLocation = getPathableRandomLocalCoordinate(this, 3, player.location) + super.init() + } + + override fun tick() { + if (getWorldTicks() % 5 == 0) { + if (this.location.withinDistance(player.location, 1) || this.location.getDistance(player.location).toInt() > 3) { + val path = Pathfinder.find(this, getPathableRandomLocalCoordinate(this, 3, player.location)) + path.walk(this) + } else { + throwSnowball() + } + } + super.tick() + } + + override fun talkTo(npc: NPC) { + sendMessage(player, "He does not seem interested in talking.") + } + + override fun follow() { + } + + private fun throwSnowball() { + queueScript(this, 0, QueueStrength.SOFT) { stage: Int -> + when (stage) { + 0 -> { + face(this, player) + visualize(this, 7530, -1) + return@queueScript delayScript(this, 1) + } + 1 -> { + Projectile.create(this, player, 861, 30, 10).send() + return@queueScript delayScript(this, this.location.getDistance(player.location).toInt()) + } + 2 -> { + player.graphics(Graphics(1282)) + playGlobalAudio(player.location, Sounds.GUBLINCH_SNOWBALL_3295) + return@queueScript stopExecuting(this) + } + else -> return@queueScript stopExecuting(this) + } + } + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..79991e5bb --- /dev/null +++ b/Server/src/main/core/game/worldevents/holiday/christmas/randoms/SantaHolidayRandomDialogue.kt @@ -0,0 +1,54 @@ +package core.game.worldevents.holiday.christmas.randoms + +import core.api.addItem +import core.api.addItemOrDrop +import core.api.getAttribute +import core.game.dialogue.DialogueFile +import core.game.dialogue.FacialExpression +import core.game.worldevents.holiday.HolidayRandoms +import core.game.worldevents.holiday.christmas.Giftmas +import core.tools.END_DIALOGUE +import core.tools.RandomFunction +import org.rs09.consts.Items + +class SantaHolidayRandomDialogue : DialogueFile() { + + override fun handle(componentID: Int, buttonID: Int) { + if (HolidayRandoms.getEventNpc(player!!) == null) + player!!.dialogueInterpreter.close() + + when (stage) { + 0 -> npcl(FacialExpression.JOLLY, "Ho Ho Ho!").also { stage++ } + 1 -> npcl(FacialExpression.HAPPY, "Merry Christmas, ${player!!.username}! Would you like to know if you are on the nice or naughty list?").also { stage = 10 } + + 10 -> options("Yes, please.", "No, thanks."). also { stage = 11 } + 11 -> when (buttonID) { + 1 -> playerl(FacialExpression.HAPPY, "Yes, please.").also { stage = 20 } + 2 -> { + playerl(FacialExpression.NEUTRAL, "No, thanks.") + stage = END_DIALOGUE + HolidayRandoms.terminateEventNpc(player!!) + } + } + 20 -> if (getAttribute(HolidayRandoms.getEventNpc(player!!)!!, "playerisnice", false)) { + npcl(FacialExpression.HAPPY, "Let me check my list... ${player!!.username}.. You are on the nice list!").also { stage = 30 } + } else { + npcl(FacialExpression.SAD, "Let me check my list... ${player!!.username}.. You are on the naughty list!").also { stage = 40 } + } + 30 -> npcl(FacialExpression.HAPPY, "Since you have been a good ${if (player!!.isMale) "boy" else "girl"} this year, you get a gift. Merry Christmas, ${player!!.username}!").also { stage = 31 } + 31 -> { + val loot = Giftmas.MBOX_LOOT.roll().first() + addItemOrDrop(player!!, loot.id, loot.amount) + HolidayRandoms.terminateEventNpc(player!!) + end() + } + 40 -> npcl(FacialExpression.NEUTRAL, "If you work on being a good ${if (player!!.isMale) "boy" else "girl"} from now on you will make it on the nice list!").also { stage = 41 } + 41 -> npcl(FacialExpression.NEUTRAL, "Since you are on the naughty list, you get coal.").also { stage = 42 } + 42 -> { + addItemOrDrop(player!!, Items.COAL_454, RandomFunction.random(1, 21)) + HolidayRandoms.terminateEventNpc(player!!) + end() + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/core/game/worldevents/holiday/christmas/randoms/SantaHolidayRandomNPC.kt b/Server/src/main/core/game/worldevents/holiday/christmas/randoms/SantaHolidayRandomNPC.kt new file mode 100644 index 000000000..84a384af7 --- /dev/null +++ b/Server/src/main/core/game/worldevents/holiday/christmas/randoms/SantaHolidayRandomNPC.kt @@ -0,0 +1,31 @@ +package core.game.worldevents.holiday.christmas.randoms + +import core.api.* +import core.game.node.entity.npc.NPC +import core.game.worldevents.holiday.HolidayRandomEventNPC +import core.tools.RandomFunction +import core.tools.minutesToTicks +import org.rs09.consts.NPCs + +class SantaHolidayRandomNPC() : HolidayRandomEventNPC(NPCs.SANTA_CLAUS_8540) { + + override fun init() { + spawnLocation = getPathableRandomLocalCoordinate(this, 1, player.location) + super.init() + ticksLeft = minutesToTicks(2) + setAttribute(this, "playerisnice", !RandomFunction.roll(3)) + sendChat(this, "Ho Ho Ho! Merry Christmas, ${player.username.capitalize()}!", 2) + } + + override fun tick() { + if (RandomFunction.roll(10)) + sendChat(this, "Ho Ho Ho! Merry Christmas, ${player.username.capitalize()}!") + + super.tick() + } + + override fun talkTo(npc: NPC) { + face(player) + openDialogue(player, SantaHolidayRandomDialogue(), npc) + } +} \ No newline at end of file diff --git a/Server/src/main/core/game/worldevents/holiday/christmas/randoms/SnowStormHolidayRandomNPC.kt b/Server/src/main/core/game/worldevents/holiday/christmas/randoms/SnowStormHolidayRandomNPC.kt new file mode 100644 index 000000000..c10d937f2 --- /dev/null +++ b/Server/src/main/core/game/worldevents/holiday/christmas/randoms/SnowStormHolidayRandomNPC.kt @@ -0,0 +1,72 @@ +package core.game.worldevents.holiday.christmas.randoms + +import core.api.* +import core.game.node.entity.npc.NPC +import core.game.world.map.RegionManager +import core.game.world.map.path.Pathfinder +import core.game.worldevents.holiday.HolidayRandomEventNPC +import core.tools.RandomFunction +import core.tools.minutesToTicks +import org.rs09.consts.NPCs +import org.rs09.consts.Sounds + +class SnowStormHolidayRandomNPC : HolidayRandomEventNPC(NPCs.SNOW_6740) { + private lateinit var snowTwoNPC: NPC + private lateinit var snowThreeNPC: NPC + private lateinit var snowFourNPC: NPC + private lateinit var snowFiveNPC: NPC + private lateinit var snowNPCs: List + private val songs = listOf(189, 208, 209, 532, 547, 592) + private var snowFinalized = false + + override fun init() { + snowTwoNPC = create(NPCs.SNOW_6740, getPathableRandomLocalCoordinate(player, 3, player.location)) + snowThreeNPC = create(NPCs.SNOW_6740, getPathableRandomLocalCoordinate(player, 3, player.location)) + snowFourNPC = create(NPCs.SNOW_6740, getPathableRandomLocalCoordinate(player, 3, player.location)) + snowFiveNPC = create(NPCs.SNOW_6740, getPathableRandomLocalCoordinate(player, 3, player.location)) + snowNPCs = listOf(this, snowTwoNPC, snowThreeNPC, snowFourNPC, snowFiveNPC) + ticksLeft = minutesToTicks(2) + super.init() + snowTwoNPC.init() + snowThreeNPC.init() + snowFourNPC.init() + snowFiveNPC.init() + val nearbyPlayers = RegionManager.getLocalPlayers(player.location, 8) + for (p in nearbyPlayers) { + p.musicPlayer.unlock(songs.random(), true) + } + } + + override fun tick() { + if (getWorldTicks() % 8 == 0) { + moveSnow() + } + super.tick() + } + + private fun moveSnow() { + for (snow in snowNPCs) { + val snowPath = Pathfinder.find(snow, getPathableRandomLocalCoordinate(player, 4, player.location)) + snowPath.walk(snow) + + if (RandomFunction.roll(4)) + playGlobalAudio(snow.location, Sounds.ICY_WIND_2078) + } + } + + override fun follow() { + } + + override fun terminate() { + if (!snowFinalized) { + snowFinalized = true + for (snow in snowNPCs) { + poofClear(snow) + } + } + super.terminate() + } + + override fun talkTo(npc: NPC) { + } +} \ No newline at end of file diff --git a/Server/src/main/core/game/worldevents/holiday/christmas/randoms/SnowmanFightBehavior.kt b/Server/src/main/core/game/worldevents/holiday/christmas/randoms/SnowmanFightBehavior.kt new file mode 100644 index 000000000..b874bd8f0 --- /dev/null +++ b/Server/src/main/core/game/worldevents/holiday/christmas/randoms/SnowmanFightBehavior.kt @@ -0,0 +1,21 @@ +package core.game.worldevents.holiday.christmas.randoms + +import core.api.getAttribute +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.worldevents.holiday.HolidayRandoms +import core.tools.RandomFunction + +class SnowmanFightBehavior : NPCBehavior() { + override fun onDeathStarted(self: NPC, killer: Entity) { + val holidayRandomPlayer = getAttribute(self, "holidayrandomplayer", null) ?: return + HolidayRandoms.terminateEventNpc(holidayRandomPlayer) + } + + override fun beforeAttackFinalized(self: NPC, victim: Entity, state: BattleState) { + state.estimatedHit = RandomFunction.getRandom(1) + } +} \ No newline at end of file diff --git a/Server/src/main/core/game/worldevents/holiday/christmas/randoms/SnowmanFightHolidayRandom.kt b/Server/src/main/core/game/worldevents/holiday/christmas/randoms/SnowmanFightHolidayRandom.kt new file mode 100644 index 000000000..15e598bf9 --- /dev/null +++ b/Server/src/main/core/game/worldevents/holiday/christmas/randoms/SnowmanFightHolidayRandom.kt @@ -0,0 +1,52 @@ +package core.game.worldevents.holiday.christmas.randoms + +import core.api.* +import core.game.node.entity.npc.NPC +import core.game.worldevents.holiday.HolidayRandomEventNPC +import core.tools.minutesToTicks +import org.rs09.consts.NPCs + +class SnowmanFightHolidayRandom : HolidayRandomEventNPC(NPCs.BARBARIAN_SNOWMAN_6742) { + private lateinit var otherSnowman: NPC + private val snowmen = listOf(NPCs.DRAGON_SNOWMAN_6743, NPCs.PIRATE_SNOWMAN_6745, NPCs.DWARF_SNOWMAN_6744) + private var snowmenFinalized = false + + override fun init() { + otherSnowman = create(snowmen.random(), getPathableRandomLocalCoordinate(player, 3, player.location)) + ticksLeft = minutesToTicks(3) + setAttribute(this, "holidayrandomplayer", player) + setAttribute(otherSnowman, "holidayrandomplayer", player) + setAttribute(otherSnowman, "holiday_random_extra_npc", true) + this.behavior = SnowmanFightBehavior() + otherSnowman.behavior = SnowmanFightBehavior() + super.init() + otherSnowman.init() + + this.attack(otherSnowman) + otherSnowman.attack(this) + } + + override fun tick() { + if (!this.inCombat() && getWorldTicks() % 20 == 0) { + this.teleport(otherSnowman.location) + this.attack(otherSnowman) + otherSnowman.attack(this) + } + super.tick() + } + + override fun terminate() { + if (!snowmenFinalized) { + snowmenFinalized = true + poofClear(otherSnowman) + } + super.terminate() + } + + override fun follow() { + } + + override fun talkTo(npc: NPC) { + sendMessage(player, "They seem a little busy.") + } +} \ No newline at end of file diff --git a/Server/src/main/core/game/worldevents/holiday/christmas/randoms/SnowmanHolidayRandomDialogue.kt b/Server/src/main/core/game/worldevents/holiday/christmas/randoms/SnowmanHolidayRandomDialogue.kt new file mode 100644 index 000000000..ac43c2fd5 --- /dev/null +++ b/Server/src/main/core/game/worldevents/holiday/christmas/randoms/SnowmanHolidayRandomDialogue.kt @@ -0,0 +1,68 @@ +package core.game.worldevents.holiday.christmas.randoms + +import core.api.* +import core.game.dialogue.DialogueFile +import core.game.dialogue.FacialExpression +import core.game.interaction.QueueStrength +import core.game.node.entity.player.link.emote.Emotes +import core.game.worldevents.holiday.HolidayRandoms +import org.rs09.consts.Items + + +class SnowmanHolidayRandomDialogue : DialogueFile() { + override fun handle(componentID: Int, buttonID: Int) { + if (HolidayRandoms.getEventNpc(player!!) == null) + player!!.dialogueInterpreter.close() + + when (stage) { + 0 -> npcl(FacialExpression.CHILD_NEUTRAL, "Greetings, ${player!!.username.capitalize()}. I'm visiting from the Land of Snow, but I need some help."). also { stage++ } + 1 -> options("What do you need?", "No, sorry.") .also { stage++ } + 2 -> when(buttonID) { + 1 -> playerl(FacialExpression.HALF_ASKING,"What do you need?").also { stage = 10 } + 2 -> playerl(FacialExpression.NEUTRAL,"No, sorry.").also { stage = 30 } + } + 10 -> npcl(FacialExpression.CHILD_NEUTRAL, "I visit Gielinor every year during this time when it is the coldest here, but it is still a little warm for us snowmen.").also { stage++ } + 11 -> npcl(FacialExpression.CHILD_NEUTRAL, "I'm really enjoying my visit, but I have started to melt. Would you happen to have a snowball?").also { stage++ } + 12 -> { + if (inInventory(player!!, Items.SNOWBALL_11951, 1)) { + playerl(FacialExpression.HAPPY, "Actually, I do! You can have it.").also { stage = 20 } + } else { + playerl(FacialExpression.NEUTRAL, "I don't have a snowball, sorry.").also { stage = 30 } + } + } + 20 -> { + npcl(FacialExpression.CHILD_NEUTRAL, "Thank you, ${player!!.username.capitalize()}!").also { stage++ } + } + 21 -> { + if (player!!.emoteManager.isUnlocked(Emotes.SNOWMAN_DANCE)) { + npcl(FacialExpression.CHILD_NEUTRAL, "Please take this lamp I found on my travels. Happy holidays, ${player!!.username.capitalize()}!").also { stage = 22 } + } else { + npcl(FacialExpression.CHILD_NEUTRAL, "Before I leave I will show you how to dance like us snowmen. Happy holidays, ${player!!.username.capitalize()}!").also { stage = 23 } + } + } + 22 -> { + if (removeItem(player!!, Items.SNOWBALL_11951)) + addItem(player!!, Items.LAMP_2528) + + end() + HolidayRandoms.terminateEventNpc(player!!) + } + 23 -> { + if (removeItem(player!!, Items.SNOWBALL_11951)) + player!!.emoteManager.unlock(Emotes.SNOWMAN_DANCE) + + queueScript(player!!, 2,QueueStrength.SOFT) { + sendDialogue(player!!, "You have unlocked the Snowman Dance emote!") + return@queueScript stopExecuting(player!!) + } + end() + HolidayRandoms.terminateEventNpc(player!!) + } + 30 -> npcl(FacialExpression.CHILD_NEUTRAL, "I must be going before I melt. Happy Holidays, ${player!!.username.capitalize()}.").also { stage++ } + 31 -> { + end() + HolidayRandoms.terminateEventNpc(player!!) + } + } + } +} \ No newline at end of file diff --git a/Server/src/main/core/game/worldevents/holiday/christmas/randoms/SnowmanHolidayRandomNPC.kt b/Server/src/main/core/game/worldevents/holiday/christmas/randoms/SnowmanHolidayRandomNPC.kt new file mode 100644 index 000000000..6248e710e --- /dev/null +++ b/Server/src/main/core/game/worldevents/holiday/christmas/randoms/SnowmanHolidayRandomNPC.kt @@ -0,0 +1,32 @@ +package core.game.worldevents.holiday.christmas.randoms + +import core.api.* +import core.game.node.entity.npc.NPC +import core.game.worldevents.holiday.HolidayRandomEventNPC +import core.tools.RandomFunction +import core.tools.minutesToTicks +import org.rs09.consts.NPCs + +class SnowmanHolidayRandomNPC : HolidayRandomEventNPC(NPCs.SNOWMAN_6746) { + private val snowmanLines = listOf("@name, are you there?", "Excuse me, @name?", "@name, could I please speak with you?") + private var hasTalkedWith = false + + override fun init() { + super.init() + ticksLeft = minutesToTicks(2) + sendChat(this, "Happy holidays, ${player.username.capitalize()}!", 2) + } + + override fun tick() { + if (RandomFunction.roll(15) && !hasTalkedWith) + sendChat(this, snowmanLines.random().replace("@name",player.username.capitalize())) + + super.tick() + } + + override fun talkTo(npc: NPC) { + face(player) + hasTalkedWith = true + openDialogue(player, SnowmanHolidayRandomDialogue(), npc) + } +} \ No newline at end of file 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 27969e6e8..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(core.game.dialogue.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(core.game.dialogue.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(core.game.dialogue.FacialExpression.THINKING, "What kind of rewards can I claim?").also { stage = 10 } - 2 -> player(core.game.dialogue.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..d64bb704b 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,321 @@ 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 false + //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/BlackCatHolidayRandomNPC.kt b/Server/src/main/core/game/worldevents/holiday/halloween/randoms/BlackCatHolidayRandomNPC.kt new file mode 100644 index 000000000..b893bfa1c --- /dev/null +++ b/Server/src/main/core/game/worldevents/holiday/halloween/randoms/BlackCatHolidayRandomNPC.kt @@ -0,0 +1,31 @@ +package core.game.worldevents.holiday.halloween.randoms + +import core.api.* +import core.game.interaction.QueueStrength +import core.game.node.entity.combat.ImpactHandler.HitsplatType +import core.game.node.entity.npc.NPC +import core.game.worldevents.holiday.HolidayRandomEventNPC + +import core.game.worldevents.holiday.HolidayRandoms +import org.rs09.consts.Sounds + +class BlackCatHolidayRandomNPC() : HolidayRandomEventNPC(4607) { + override fun init() { + super.init() + queueScript(this, 8, QueueStrength.SOFT) { + playGlobalAudio(this.location, Sounds.CAT_ATTACK_333) + sendChat("HISS") + if (player.location.withinDistance(this.location, 3)) { + this.face(player) + sendMessage(player, "The cat scratches you and runs away.") + val hit = if (player.skills.lifepoints < 5) 0 else 1 + impact(player, hit, HitsplatType.NORMAL) + } + HolidayRandoms.terminateEventNpc(player) + return@queueScript stopExecuting(this) + } + } + + override fun talkTo(npc: NPC) { + } +} \ No newline at end of file diff --git a/Server/src/main/core/game/worldevents/holiday/halloween/randoms/DeathHolidayRandomNPC.kt b/Server/src/main/core/game/worldevents/holiday/halloween/randoms/DeathHolidayRandomNPC.kt new file mode 100644 index 000000000..ffe2ebc7e --- /dev/null +++ b/Server/src/main/core/game/worldevents/holiday/halloween/randoms/DeathHolidayRandomNPC.kt @@ -0,0 +1,42 @@ +package core.game.worldevents.holiday.halloween.randoms + +import core.api.* +import core.game.interaction.QueueStrength +import core.game.node.entity.npc.NPC +import core.game.worldevents.holiday.HolidayRandomEventNPC +import core.game.worldevents.holiday.HolidayRandoms +import core.tools.RandomFunction +import org.rs09.consts.Sounds + +class DeathHolidayRandomNPC() : HolidayRandomEventNPC(2862) { + override fun init() { + super.init() + playJingle(player, 337) + queueScript(this, 6, QueueStrength.SOFT) { stage: Int -> + when (stage) { + 0 -> { + this.face(player) + visualize(this, 864, -1) + playGlobalAudio(this.location, Sounds.ZOMBIE_MOAN_2324) + return@queueScript delayScript(this, 1) + } + 1 -> { + when(RandomFunction.getRandom(2)) { + 0 -> sendChat(this, "Your end is near, ${player.username.capitalize()}...") + 1 -> sendChat(this, "Time is running out, ${player.username.capitalize()}...") + 2 -> sendChat(this, "Tick tock, ${player.username.capitalize()}...") + } + return@queueScript delayScript(this, 4) + } + 2 -> { + HolidayRandoms.terminateEventNpc(player) + return@queueScript stopExecuting(this) + } + else -> return@queueScript stopExecuting(this) + } + } + } + + override fun talkTo(npc: NPC) { + } +} \ No newline at end of file diff --git a/Server/src/main/core/game/worldevents/holiday/halloween/randoms/GhostHolidayRandomNPC.kt b/Server/src/main/core/game/worldevents/holiday/halloween/randoms/GhostHolidayRandomNPC.kt new file mode 100644 index 000000000..f5defed09 --- /dev/null +++ b/Server/src/main/core/game/worldevents/holiday/halloween/randoms/GhostHolidayRandomNPC.kt @@ -0,0 +1,27 @@ +package core.game.worldevents.holiday.halloween.randoms + +import core.api.* +import core.game.node.entity.npc.NPC +import core.game.worldevents.holiday.HolidayRandomEventNPC +import core.tools.RandomFunction +import org.rs09.consts.Sounds + +class GhostHolidayRandomNPC() : HolidayRandomEventNPC(2716) { + override fun init() { + super.init() + this.isAggressive = false + playGlobalAudio(this.location, Sounds.BIGGHOST_APPEAR_1595) + animate(player, 2836) + sendChat(this, "WooooOOOooOOo") + sendMessage(player, "The air suddenly gets colder...") + } + + override fun tick() { + super.tick() + if (RandomFunction.roll(10)) + playGlobalAudio(this.location, Sounds.BIGGHOST_LAUGH_1600) + } + + override fun talkTo(npc: NPC) { + } +} \ No newline at end of file diff --git a/Server/src/main/core/game/worldevents/holiday/halloween/randoms/SpiderHolidayRandomBehavior.kt b/Server/src/main/core/game/worldevents/holiday/halloween/randoms/SpiderHolidayRandomBehavior.kt new file mode 100644 index 000000000..df23e763d --- /dev/null +++ b/Server/src/main/core/game/worldevents/holiday/halloween/randoms/SpiderHolidayRandomBehavior.kt @@ -0,0 +1,22 @@ +package core.game.worldevents.holiday.halloween.randoms + +import core.api.getAttribute +import core.api.playGlobalAudio +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.worldevents.holiday.HolidayRandomEventNPC +import org.rs09.consts.Sounds + +class SpiderHolidayRandomBehavior : NPCBehavior() { + override fun canBeAttackedBy(self: NPC, attacker: Entity, style: CombatStyle, shouldSendMessage: Boolean): Boolean { + return false + } + + override fun onDeathStarted(self: NPC, killer: Entity) { + getAttribute(self, "holiday-npc", null)?.terminate() + playGlobalAudio(self.location, Sounds.SMALL_SPIDER_DEATH_3608) + super.onDeathFinished(self, killer) + } +} \ 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 new file mode 100644 index 000000000..dea7ff063 --- /dev/null +++ b/Server/src/main/core/game/worldevents/holiday/halloween/randoms/SpiderHolidayRandomNPC.kt @@ -0,0 +1,50 @@ +package core.game.worldevents.holiday.halloween.randoms + +import core.api.* +import core.game.interaction.QueueStrength +import core.game.node.entity.combat.ImpactHandler +import core.game.node.entity.npc.NPC +import core.game.world.update.flag.context.Animation +import core.game.worldevents.holiday.HolidayRandomEventNPC +import core.game.worldevents.holiday.HolidayRandoms +import org.rs09.consts.Sounds + +class SpiderHolidayRandomNPC() : HolidayRandomEventNPC(61) { + override fun init() { + super.init() + this.behavior = SpiderHolidayRandomBehavior() + playGlobalAudio(this.location, Sounds.SPIDER_4375) + var stomped = false + queueScript(this,4, QueueStrength.SOFT) { stage: Int -> + when (stage) { + 0 -> { + sendChat(player, "Eww a spider!") + return@queueScript delayScript(this, 2) + } + 1 -> { + if (player.location.withinDistance(this.location, 3)) { + player.animate(Animation(4278)) + playGlobalAudio(this.location, Sounds.UNARMED_KICK_2565) + sendMessage(player, "You stomp the spider.") + stomped = true + } + return@queueScript delayScript(this, 1) + } + 2 -> { + if (stomped) { + impact(this, 1, ImpactHandler.HitsplatType.NORMAL) + } else { + sendMessage(player, "The spider runs away.") + playGlobalAudio(this.location, Sounds.SPIDER_4375) + HolidayRandoms.terminateEventNpc(player) + } + return@queueScript stopExecuting(this) + } + else -> return@queueScript stopExecuting(this) + } + } + } + + override fun talkTo(npc: NPC) { + } +} \ No newline at end of file diff --git a/Server/src/main/core/game/worldevents/holiday/halloween/randoms/VampireHolidayRandomNPC.kt b/Server/src/main/core/game/worldevents/holiday/halloween/randoms/VampireHolidayRandomNPC.kt new file mode 100644 index 000000000..2fc8ad225 --- /dev/null +++ b/Server/src/main/core/game/worldevents/holiday/halloween/randoms/VampireHolidayRandomNPC.kt @@ -0,0 +1,49 @@ +package core.game.worldevents.holiday.halloween.randoms + +import core.api.* +import core.game.interaction.QueueStrength +import core.game.node.entity.combat.ImpactHandler +import core.game.node.entity.npc.NPC +import core.game.worldevents.holiday.HolidayRandomEventNPC +import core.game.worldevents.holiday.HolidayRandoms +import core.game.worldevents.holiday.ResetHolidayAppearance +import core.tools.RandomFunction +import core.tools.colorize +import org.rs09.consts.Sounds + +class VampireHolidayRandomNPC() : HolidayRandomEventNPC(1023) { + override fun init() { + super.init() + playGlobalAudio(this.location, Sounds.REGULAR_VAMPIRE_APPEAR_1897) + visualize(this, -1 , 1863) + queueScript(this, 3, QueueStrength.SOFT) { stage: Int -> + when (stage) { + 0 -> { + this.face(player) + visualize(this, 7183 , -1) + playGlobalAudio(this.location, Sounds.VAMPIRE_ATTACK_879) + if (RandomFunction.roll(2)) { + player.timers.registerTimer(ResetHolidayAppearance()) + sendMessage(player, colorize("%RThe vampire bites your neck!")) + val hit = if (player.skills.lifepoints < 5) 0 else 2 + impact(player, hit, ImpactHandler.HitsplatType.NORMAL) + player.appearance.transformNPC(1023) + } else { + sendMessage(player, "The vampire tries to bite your neck and misses!") + impact(player, 0, ImpactHandler.HitsplatType.NORMAL) + } + return@queueScript delayScript(this, 8) + } + 1 -> { + visualize(this, 10530, 1863) + HolidayRandoms.terminateEventNpc(player) + return@queueScript stopExecuting(this) + } + else -> return@queueScript stopExecuting(this) + } + } + } + + override fun talkTo(npc: NPC) { + } +} \ No newline at end of file diff --git a/Server/src/main/core/game/worldevents/holiday/halloween/randoms/WitchHolidayRandomNPC.kt b/Server/src/main/core/game/worldevents/holiday/halloween/randoms/WitchHolidayRandomNPC.kt new file mode 100644 index 000000000..bb4100be6 --- /dev/null +++ b/Server/src/main/core/game/worldevents/holiday/halloween/randoms/WitchHolidayRandomNPC.kt @@ -0,0 +1,152 @@ +package core.game.worldevents.holiday.halloween.randoms + +import core.api.* +import core.game.interaction.QueueStrength +import core.game.node.entity.combat.ImpactHandler +import core.game.node.entity.npc.NPC +import core.game.worldevents.holiday.ResetHolidayAppearance +import core.game.worldevents.holiday.HolidayRandomEventNPC +import core.game.worldevents.holiday.HolidayRandoms +import core.tools.RandomFunction +import org.rs09.consts.Sounds + +class WitchHolidayRandomNPC() : HolidayRandomEventNPC(611) { + override fun init() { + super.init() + when (RandomFunction.getRandom(4)) { + 0 -> { + queueScript(this, 6, QueueStrength.SOFT) { stage: Int -> + when (stage) { + 0 -> { + sendChat(this, "Brackium Emendo!") + this.face(player) + playGlobalAudio(this.location, Sounds.CONFUSE_CAST_AND_FIRE_119) + animate(this, 1978) + spawnProjectile(this, player, 109) + return@queueScript delayScript(this, 2) + } + 1 -> { + player.appearance.transformNPC(3138) + playAudio(player, Sounds.SKELETON_RESURRECT_1687) + registerTimer(player, ResetHolidayAppearance()) + return@queueScript delayScript(this, 4) + } + 2 -> { + sendChat(this, "That was not right...") + visualize(this, 857, -1) + HolidayRandoms.terminateEventNpc(player) + return@queueScript stopExecuting(this) + } + else -> return@queueScript stopExecuting(this) + } + } + } + 1 -> { + queueScript(this, 6, QueueStrength.SOFT) { stage: Int -> + when (stage) { + 0 -> { + sendChat(this, "Bombarda!") + this.face(player) + playGlobalAudio(this.location, Sounds.CURSE_CAST_AND_FIRE_127) + animate(this, 1978) + spawnProjectile(this, player, 109) + return@queueScript delayScript(this, 2) + } + 1 -> { + playGlobalAudio(player.location, Sounds.EXPLOSION_1487) + visualize(player, -1, 659) + val hit = if (player.skills.lifepoints < 5) 0 else 2 + impact(player, hit, ImpactHandler.HitsplatType.NORMAL) + return@queueScript delayScript(this, 2) + } + 2 -> { + HolidayRandoms.terminateEventNpc(player) + return@queueScript stopExecuting(this) + } + else -> return@queueScript stopExecuting(this) + } + } + } + 2 -> { + queueScript(this, 6, QueueStrength.SOFT) {stage: Int -> + when (stage) { + 0 -> { + sendChat(this, "Tarantallegra!") + this.face(player) + playGlobalAudio(this.location, Sounds.WEAKEN_CAST_AND_FIRE_3011) + animate(this, 1978) + spawnProjectile(this, player, 109) + return@queueScript delayScript(this, 2) + } + 1 -> { + animate(player, 3543, true) + sendMessage(player, "You suddenly burst into dance.") + return@queueScript delayScript(this, 2) + } + 2 -> { + visualize(this, 861, -1) + playGlobalAudio(this.location, Sounds.HUMAN_LAUGH_1_3071) + HolidayRandoms.terminateEventNpc(player) + return@queueScript stopExecuting(this) + } + else -> return@queueScript stopExecuting(this) + } + } + } + 3 -> { + queueScript(this, 6, QueueStrength.SOFT) {stage: Int -> + when (stage) { + 0 -> { + sendChat(this, "Vespertilio!") + this.face(player) + playGlobalAudio(this.location, Sounds.WEAKEN_CAST_AND_FIRE_3011) + animate(this, 1978) + spawnProjectile(this, player, 109) + return@queueScript delayScript(this, 2) + } + 1 -> { + visualize(player, 10530, 1863) + playGlobalAudio(player.location, Sounds.VAMPIRE_SUMMON_1899) + return@queueScript delayScript(this, 4) + } + 2 -> { + player.appearance.transformNPC(6835) + HolidayRandoms.terminateEventNpc(player) + registerTimer(player, ResetHolidayAppearance()) + return@queueScript stopExecuting(this) + } + else -> return@queueScript stopExecuting(this) + } + } + } + 4 -> { + queueScript(this, 6, QueueStrength.SOFT) {stage: Int -> + when (stage) { + 0 -> { + sendChat(this, "Sella!") + this.face(player) + playGlobalAudio(this.location, Sounds.WEAKEN_CAST_AND_FIRE_3011) + animate(this, 1978) + spawnProjectile(this, player, 109) + return@queueScript delayScript(this, 2) + } + 1 -> { + player.appearance.transformNPC(3293) + playGlobalAudio(player.location, Sounds.KR_JUDGE_HAMMER_3822) + registerTimer(player, ResetHolidayAppearance()) + return@queueScript delayScript(this, 4) + } + 2 -> { + HolidayRandoms.terminateEventNpc(player) + return@queueScript stopExecuting(this) + } + else -> return@queueScript stopExecuting(this) + } + } + } + } + } + + override fun talkTo(npc: NPC) { + } +} \ No newline at end of file diff --git a/Server/src/main/core/game/worldevents/holiday/halloween/randoms/ZombieHolidayRandomNPC.kt b/Server/src/main/core/game/worldevents/holiday/halloween/randoms/ZombieHolidayRandomNPC.kt new file mode 100644 index 000000000..6cc7e313d --- /dev/null +++ b/Server/src/main/core/game/worldevents/holiday/halloween/randoms/ZombieHolidayRandomNPC.kt @@ -0,0 +1,60 @@ +package core.game.worldevents.holiday.halloween.randoms + +import core.api.* +import core.game.interaction.QueueStrength +import core.game.node.entity.combat.ImpactHandler +import core.game.node.entity.npc.NPC +import core.game.worldevents.holiday.ResetHolidayAppearance +import core.game.worldevents.holiday.HolidayRandomEventNPC +import core.game.worldevents.holiday.HolidayRandoms +import core.tools.RandomFunction +import core.tools.colorize +import org.rs09.consts.Sounds + +class ZombieHolidayRandomNPC() : HolidayRandomEventNPC(2714) { + override fun init() { + super.init() + this.isAggressive = false + playGlobalAudio(this.location, Sounds.ZOMBIE_DEATH_922) + playJingle(player, 314) + sendMessage(player, "A zombie crawls out of the ground beneath you...") + animate(player, 7272) + queueScript(this, 2, QueueStrength.SOFT) { stage: Int -> + when (stage) { + 0 -> { + when (RandomFunction.getRandom(2)) { + 0 -> sendChat(this, "Brainnnss!") + 1 -> sendChat(this, "RArrgghhh") + 2 -> sendChat(this, "Flesshhh") + } + return@queueScript delayScript(this, 1) + } + 1 -> { + this.face(player) + visualize(this, 5568 , -1) + playGlobalAudio(this.location, Sounds.ZOMBIE_ATTACK_918) + if (RandomFunction.roll(2)) { + player.timers.registerTimer(ResetHolidayAppearance()) + sendMessage(player, colorize("%RThe zombie bites you!")) + val hit = if (player.skills.lifepoints < 5) 0 else 2 + impact(player, hit, ImpactHandler.HitsplatType.NORMAL) + player.appearance.transformNPC(2866) + } else { + impact(player, 0, ImpactHandler.HitsplatType.NORMAL) + sendMessage(player, "The zombie tries to bite you and misses!") + } + return@queueScript delayScript(this, 8) + } + 2 -> { + animate(this, 5575) + HolidayRandoms.terminateEventNpc(player) + return@queueScript stopExecuting(this) + } + else -> return@queueScript stopExecuting(this) + } + } + } + + override fun talkTo(npc: NPC) { + } +} \ No newline at end of file diff --git a/Server/src/main/core/integrations/discord/Discord.kt b/Server/src/main/core/integrations/discord/Discord.kt index 7e07d69d9..acb6755f7 100644 --- a/Server/src/main/core/integrations/discord/Discord.kt +++ b/Server/src/main/core/integrations/discord/Discord.kt @@ -1,6 +1,6 @@ package core.integrations.discord -import core.api.getItemName +import core.api.* import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import org.json.simple.JSONArray @@ -11,147 +11,193 @@ import java.io.DataOutputStream import java.io.InputStreamReader import java.net.HttpURLConnection import java.net.URL +import java.util.ArrayList -object Discord { - private const val COLOR_NEW_BUY_OFFER = 47789 - private const val COLOR_NEW_SALE_OFFER = 5752709 - private const val COLOR_OFFER_UPDATE = 15588691 +class Discord : TickListener { + override fun tick() { + if (!removeList.isEmpty()) { + pendingMessages.removeAll(removeList) + removeList.clear() + } + if (!pendingMessages.isEmpty() && !flushingPends) { + flushingPends = true + GlobalScope.launch { + try { + val count = pendingMessages.size + for (i in 0 until count) { + val (url, content) = pendingMessages[i] + sendJsonPost(url, content) + removeList.add(pendingMessages[i]) + } + } finally { + flushingPends = false + } + } + } + } - fun postNewOffer(isSale: Boolean, itemId: Int, value: Int, qty: Int, user: String) { - if (ServerConstants.DISCORD_GE_WEBHOOK.isEmpty()) return - GlobalScope.launch { - val offer = encodeOfferJson(isSale, itemId, value, qty, user) + companion object { + private const val COLOR_NEW_BUY_OFFER = 47789 + private const val COLOR_NEW_SALE_OFFER = 5752709 + private const val COLOR_OFFER_UPDATE = 15588691 + + private val pendingMessages = ArrayList>() + private var flushingPends = false + private val removeList = ArrayList>() + + fun postNewOffer(isSale: Boolean, itemId: Int, value: Int, qty: Int, user: String) { + if (ServerConstants.DISCORD_GE_WEBHOOK.isEmpty()) return + GlobalScope.launch { + val offer = encodeOfferJson(isSale, itemId, value, qty, user) + try { + sendJsonPost(ServerConstants.DISCORD_GE_WEBHOOK, offer) + } catch (e: Exception) { + e.printStackTrace() + } + } + } + + fun postOfferUpdate(isSale: Boolean, itemId: Int, value: Int, amtLeft: Int) { + if (ServerConstants.DISCORD_GE_WEBHOOK.isEmpty()) return + GlobalScope.launch { + val offer = encodeUpdateJson(isSale, itemId, value, amtLeft) + try { + sendJsonPost(ServerConstants.DISCORD_GE_WEBHOOK, offer) + } catch (e: Exception) { + e.printStackTrace() + } + } + } + + @JvmStatic + fun postPlayerAlert(player: String, type: String) { + if (ServerConstants.DISCORD_MOD_WEBHOOK.isEmpty()) return + GlobalScope.launch { + val alert = encodeUserAlert(type, player) + try { + sendJsonPost(ServerConstants.DISCORD_MOD_WEBHOOK, alert) + } catch (e: Exception) { + e.printStackTrace() + } + } + } + + @JvmStatic fun sendToOpenRSC(player: String, type: String) { + if (ServerConstants.DISCORD_OPENRSC_HOOK.isEmpty()) return + GlobalScope.launch { + val alert = encodeUserAlert(type, player) + try { + sendJsonPost(ServerConstants.DISCORD_OPENRSC_HOOK, alert) + } catch (e: Exception) { + e.printStackTrace() + } + } + } + + private fun encodeUpdateJson(sale: Boolean, itemId: Int, value: Int, amtLeft: Int): String { + val obj = JSONObject() + val embeds = JSONArray() + val embed = JSONObject() + + val fields = arrayOf( + EmbedField("Item", getItemName(itemId), false), + EmbedField("Amount Remaining", "%,d".format(amtLeft), true), + EmbedField("Price", "%,d".format(value) + "gp", true), + ) + + embed["title"] = if (sale) "Sell Offer Updated" else "Buy Offer Updated" + embed["color"] = COLOR_OFFER_UPDATE + embed["thumbnail"] = getItemImage(itemId) + embed["fields"] = getFields(fields) + + embeds.add(embed) + obj["embeds"] = embeds + + return obj.toJSONString() + } + + private fun encodeOfferJson(isSale: Boolean, itemId: Int, value: Int, qty: Int, user: String): String { + val obj = JSONObject() + val embeds = JSONArray() + val embed = JSONObject() + + val fields = arrayOf( + EmbedField("Player", user, false), + EmbedField("Item", getItemName(itemId), false), + EmbedField("Amount", "%,d".format(qty), true), + EmbedField("Price", "%,d".format(value) + "gp", true), + ) + + embed["title"] = if (isSale) "New Sell Offer" else "New Buy Offer" + embed["color"] = if (isSale) COLOR_NEW_SALE_OFFER else COLOR_NEW_BUY_OFFER + embed["thumbnail"] = getItemImage(itemId) + embed["fields"] = getFields(fields) + + embeds.add(embed) + obj["embeds"] = embeds + + return obj.toJSONString() + } + + private fun encodeUserAlert(type: String, player: String) : String { + val obj = JSONObject() + val embeds = JSONArray() + val embed = JSONObject() + + val fields = arrayOf( + EmbedField("Player", player, false), + EmbedField("Type", type, false) + ) + + embed["title"] = "Player Alert" + embed["fields"] = getFields(fields) + embeds.add(embed) + obj["embeds"] = embeds + return obj.toJSONString() + } + + private fun getFields(fields: Array): JSONArray { + val arr = JSONArray() + + for (field in fields) { + val o = JSONObject() + o["name"] = field.name + o["value"] = field.value + if (field.inline) o["inline"] = true + arr.add(o) + } + + return arr + } + + data class EmbedField(val name: String, val value: String, val inline: Boolean) + + fun getItemImage(id: Int) : JSONObject { + val obj = JSONObject() + obj["url"] = "https://github.com/2009scape/2009scape.github.io/raw/master/services/m%3Ddata/img/items/$id.png" + return obj + } + + private fun sendJsonPost(url: String = ServerConstants.DISCORD_GE_WEBHOOK, data: String) { try { - sendJsonPost(ServerConstants.DISCORD_GE_WEBHOOK, offer) + val conn = URL(url).openConnection() as HttpURLConnection + conn.doOutput = true + conn.requestMethod = "POST" + conn.setRequestProperty("Content-Type", "application/json") + conn.useCaches = false + + DataOutputStream(conn.outputStream).use { it.writeBytes(data) } + BufferedReader(InputStreamReader(conn.inputStream)).use { br -> + var line: String? + while (br.readLine().also { line = it } != null) { + println(line) + } + } } catch (e: Exception) { - e.printStackTrace() + pendingMessages.add(Pair(url, data)) } } } - - fun postOfferUpdate(isSale: Boolean, itemId: Int, value: Int, amtLeft: Int) { - if (ServerConstants.DISCORD_GE_WEBHOOK.isEmpty()) return - GlobalScope.launch { - val offer = encodeUpdateJson(isSale, itemId, value, amtLeft) - try { - sendJsonPost(ServerConstants.DISCORD_GE_WEBHOOK, offer) - } catch (e: Exception) { - e.printStackTrace() - } - } - } - - fun postPlayerAlert(player: String, type: String) { - if (ServerConstants.DISCORD_MOD_WEBHOOK.isEmpty()) return - GlobalScope.launch { - val alert = encodeUserAlert(type, player) - try { - sendJsonPost(ServerConstants.DISCORD_MOD_WEBHOOK, alert) - } catch (e: Exception) { - e.printStackTrace() - } - } - } - - private fun encodeUpdateJson(sale: Boolean, itemId: Int, value: Int, amtLeft: Int): String { - val obj = JSONObject() - val embeds = JSONArray() - val embed = JSONObject() - - val fields = arrayOf( - EmbedField("Item", getItemName(itemId), false), - EmbedField("Amount Remaining", "%,d".format(amtLeft), true), - EmbedField("Price", "%,d".format(value) + "gp", true), - ) - - embed["title"] = if (sale) "Sell Offer Updated" else "Buy Offer Updated" - embed["color"] = COLOR_OFFER_UPDATE - embed["thumbnail"] = getItemImage(itemId) - embed["fields"] = getFields(fields) - - embeds.add(embed) - obj["embeds"] = embeds - - return obj.toJSONString() - } - - private fun encodeOfferJson(isSale: Boolean, itemId: Int, value: Int, qty: Int, user: String): String { - val obj = JSONObject() - val embeds = JSONArray() - val embed = JSONObject() - - val fields = arrayOf( - EmbedField("Player", user, false), - EmbedField("Item", getItemName(itemId), false), - EmbedField("Amount", "%,d".format(qty), true), - EmbedField("Price", "%,d".format(value) + "gp", true), - ) - - embed["title"] = if (isSale) "New Sell Offer" else "New Buy Offer" - embed["color"] = if (isSale) COLOR_NEW_SALE_OFFER else COLOR_NEW_BUY_OFFER - embed["thumbnail"] = getItemImage(itemId) - embed["fields"] = getFields(fields) - - embeds.add(embed) - obj["embeds"] = embeds - - return obj.toJSONString() - } - - private fun encodeUserAlert(type: String, player: String) : String { - val obj = JSONObject() - val embeds = JSONArray() - val embed = JSONObject() - - val fields = arrayOf( - EmbedField("Player", player, false), - EmbedField("Type", type, false) - ) - - embed["title"] = "Player Alert" - embed["fields"] = getFields(fields) - embeds.add(embed) - obj["embeds"] = embeds - return obj.toJSONString() - } - - private fun getFields(fields: Array): JSONArray { - val arr = JSONArray() - - for (field in fields) { - val o = JSONObject() - o["name"] = field.name - o["value"] = field.value - if (field.inline) o["inline"] = true - arr.add(o) - } - - return arr - } - - data class EmbedField(val name: String, val value: String, val inline: Boolean) - - fun getItemImage(id: Int) : JSONObject { - val obj = JSONObject() - obj["url"] = "https://github.com/2009scape/2009scape.github.io/raw/master/services/m%3Ddata/img/items/$id.png" - return obj - } - - private fun sendJsonPost(url: String = ServerConstants.DISCORD_GE_WEBHOOK, data: String) { - val conn = URL(url).openConnection() as HttpURLConnection - conn.doOutput = true - conn.requestMethod = "POST" - conn.setRequestProperty("Content-Type", "application/json") - conn.useCaches = false - - DataOutputStream(conn.outputStream).use { it.writeBytes(data) } - BufferedReader(InputStreamReader(conn.inputStream)).use { br -> - var line: String? - while (br.readLine().also { line = it } != null) { - println(line) - } - } - } -} \ No newline at end of file +} diff --git a/Server/src/main/core/integrations/grafana/Grafana.kt b/Server/src/main/core/integrations/grafana/Grafana.kt new file mode 100644 index 000000000..5309615f4 --- /dev/null +++ b/Server/src/main/core/integrations/grafana/Grafana.kt @@ -0,0 +1,183 @@ +package core.integrations.grafana + +import core.ServerConstants +import core.api.StartupListener +import core.game.bots.AIRepository +import core.integrations.sqlite.SQLiteProvider +import core.tools.cycle +import kotlinx.coroutines.Job +import org.json.simple.JSONObject +import java.util.LinkedList +import java.util.concurrent.LinkedBlockingDeque + +class Grafana : StartupListener { + override fun startup() { + if (!ServerConstants.GRAFANA_LOGGING) return + db = SQLiteProvider (ServerConstants.GRAFANA_PATH + "grafana.db", expectedTables) + db.initTables() + db.pruneOldData(ServerConstants.GRAFANA_TTL_DAYS) + } + + data class GrafanaData ( + val playerTickTime: Int, + val npcTickTime: Int, + val playerRenderTime: Int, + val totalTickTime: Int, + val packetProcessTime: Int, + val botPulseTime: Int, + val otherPulseTime: Int, + val pulseTimes: Array>, + val pulseCounts: Array>, + val botCount: Int, + val timeSecs: Int + ) + + companion object { + var playerTickTime = 0 + var npcTickTime = 0 + var playerRenderTime = 0 + var totalTickTime = 0 + var packetProcessTime = 0 + var botPulseTime = 0 + var otherPulseTime = 0 + lateinit var db: SQLiteProvider + var cycleData = LinkedList() + var currentTask: Job? = null + + var pulseTimes = HashMap() + var pulseCounts = HashMap() + + fun addPulseLength(pulseName: String, time: Int) { + if (!this::db.isInitialized) return + pulseTimes[pulseName] = (pulseTimes[pulseName] ?: 0) + time + } + + fun countPulse(pulseName: String) { + if (!this::db.isInitialized) return + pulseCounts[pulseName] = (pulseCounts[pulseName] ?: 0) + 1 + } + + fun startTick() { + if (!this::db.isInitialized) return + playerTickTime = 0 + npcTickTime = 0 + playerRenderTime = 0 + totalTickTime = 0 + packetProcessTime = 0 + botPulseTime = 0 + otherPulseTime = 0 + pulseTimes.clear() + pulseCounts.clear() + } + + fun endTick() { + if (!this::db.isInitialized) return + val thisCycle = GrafanaData ( + playerTickTime, + npcTickTime, + playerRenderTime, + totalTickTime, + packetProcessTime, + botPulseTime, + otherPulseTime, + pulseTimes.entries.toTypedArray(), + pulseCounts.entries.toTypedArray(), + AIRepository.PulseRepository.size, + getNowTime() + ) + cycleData.add(thisCycle) + + if (cycleData.size < 50) return + if (currentTask?.isActive == true) return + + currentTask = db.runAsync { + with (it.prepareStatement(INSERT_TOP_PULSES)) { + for (i in 0 until 50) { + val topSorted = cycleData[i].pulseTimes.sortedByDescending { entry -> entry.value } + val rootObj = JSONObject() + val contentObj = JSONObject() + for (j in 0 until 5) { + contentObj[topSorted[j].key] = topSorted[j].value + } + rootObj["pulses"] = contentObj + setString(1, rootObj.toJSONString()) + setInt(2, cycleData[i].timeSecs) + execute() + } + } + + with (it.prepareStatement(INSERT_PULSE_COUNT)) { + for (i in 0 until 50) { + val topSorted = cycleData[i].pulseCounts.sortedByDescending { entry -> entry.value } + val rootObj = JSONObject() + val contentObj = JSONObject() + for (j in 0 until 5) { + contentObj[topSorted[j].key] = topSorted[j].value + } + rootObj["pulses"] = contentObj + setString(1, rootObj.toJSONString()) + setInt(2, cycleData[i].timeSecs) + execute() + } + } + + with (it.prepareStatement(INSERT_BOT_COUNT)) { + for (i in 0 until 50) { + setInt(1, cycleData[i].botCount) + setInt(2, cycleData[i].timeSecs) + execute() + } + } + + with (it.prepareStatement(INSERT_TICK_MEAS)) { + for (i in 0 until 50) { + setInt(1, cycleData[i].botPulseTime) + setInt(2, cycleData[i].otherPulseTime) + setInt(3, cycleData[i].npcTickTime) + setInt(4, cycleData[i].playerTickTime) + setInt(5, cycleData[i].playerRenderTime) + setInt(6, cycleData[i].packetProcessTime) + setInt(7, cycleData[i].totalTickTime - (cycleData[i].botPulseTime + cycleData[i].otherPulseTime + cycleData[i].npcTickTime + cycleData[i].playerTickTime + cycleData[i].playerRenderTime + cycleData[i].packetProcessTime)) + setInt(8, cycleData[i].timeSecs) + execute() + } + } + + cycleData.removeAll(cycleData.subList(0, 49).toSet()) + } + } + + private fun getNowTime () : Int { + return (System.currentTimeMillis() / 1000L).toInt() + } + + private const val INSERT_TOP_PULSES = "INSERT INTO top_pulses (pulse_info, ts) VALUES (?, ?);" + private const val INSERT_PULSE_COUNT = "INSERT INTO high_volume_pulses (pulse_info, ts) VALUES (?, ?);" + private const val INSERT_BOT_COUNT = "INSERT INTO bot_counts (count, ts) VALUES (?, ?);" + private const val INSERT_TICK_MEAS = "INSERT INTO tick_lengths (bot_pulses, misc_pulses, npc_tick, player_tick, player_render, packet_incoming, other, ts) VALUES (?,?,?,?,?,?,?,?);" + private var expectedTables = hashMapOf( + "bot_counts" to "CREATE TABLE \"bot_counts\" (\n" + + "\t\"count\"\tINTEGER,\n" + + "\t\"ts\"\tINTEGER\n" + + ")", + "high_volume_pulses" to "CREATE TABLE \"high_volume_pulses\" (\n" + + "\t\"pulse_info\"\tTEXT,\n" + + "\t\"ts\"\tINTEGER\n" + + ")", + "tick_lengths" to "CREATE TABLE \"tick_lengths\" (\n" + + "\t\"bot_pulses\"\tINTEGER,\n" + + "\t\"misc_pulses\"\tINTEGER,\n" + + "\t\"npc_tick\"\tINTEGER,\n" + + "\t\"player_tick\"\tINTEGER,\n" + + "\t\"player_render\"\tINTEGER,\n" + + "\t\"packet_incoming\"\tINTEGER,\n" + + "\t\"other\"\tINTEGER,\n" + + "\t\"ts\"\tINTEGER\n" + + ")", + "top_pulses" to "CREATE TABLE \"top_pulses\" (\n" + + "\t\"pulse_info\"\tTEXT,\n" + + "\t\"ts\"\tINTEGER\n" + + ")" + ) + } +} \ No newline at end of file diff --git a/Server/src/main/core/integrations/sqlite/SQLiteProvider.kt b/Server/src/main/core/integrations/sqlite/SQLiteProvider.kt new file mode 100644 index 000000000..4155bf339 --- /dev/null +++ b/Server/src/main/core/integrations/sqlite/SQLiteProvider.kt @@ -0,0 +1,94 @@ +package core.integrations.sqlite + +import core.api.log +import core.tools.Log +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.launch +import org.sqlite.SQLiteDataSource +import java.io.File +import java.sql.Connection +import java.util.concurrent.TimeUnit +import java.util.concurrent.locks.ReentrantLock + +class SQLiteProvider (private val path: String, private val expectedTables: HashMap? = null) { + private var connection: Connection? = null + private var connectionRefs = 0 + private var obtainConnectionLock = ReentrantLock() + private var dbRunLock = ReentrantLock() + private val coroutineScope = CoroutineScope(Dispatchers.IO) + private lateinit var sqlitePath: String + + fun initTables(tableCreatedCallback: ((String) -> Unit)? = null) { + val pathTokens = File(path).absolutePath.split(File.separator) + val file = pathTokens.last() + val parentDir = pathTokens.dropLast(1).joinToString(File.separator) + + if (!File(parentDir).exists()) + File(parentDir).mkdirs() + sqlitePath = parentDir + File.separator + file + if (expectedTables?.isEmpty() != false) return + run { + with(it.prepareStatement(TABLE_CHECK)) { + for ((table, create) in expectedTables) { + setString(1, table) + val res = executeQuery() + if (!res.next()) + with(it.createStatement()) { execute(create); tableCreatedCallback?.invoke(table) } + } + } + } + } + + fun pruneOldData (daysToKeep: Int, timestampFieldName: String = "ts") { + if (expectedTables?.isEmpty() != false) return + val timeDiff = daysToKeep * 24 * 60 * 60 + val nowTime = System.currentTimeMillis() / 1000 + run { + with (it.createStatement()) { + for ((table, _) in expectedTables) { + execute("DELETE FROM $table WHERE $timestampFieldName <= ${nowTime - timeDiff};") + } + } + } + } + + fun runAsync (closure: (conn: Connection) -> Unit) : Job { + return coroutineScope.launch { run (closure) } + } + + fun run (closure: (conn: Connection) -> Unit) { + dbRunLock.tryLock(10000L, TimeUnit.MILLISECONDS) + + connectionRefs++ + val con = connect() + closure.invoke(con) + connectionRefs-- + + if(connectionRefs == 0) { + con.close() + } + + dbRunLock.unlock() + } + + private fun connect(): Connection + { + obtainConnectionLock.tryLock(10000L, TimeUnit.MILLISECONDS) + + if (connection == null || connection!!.isClosed) + { + val ds = SQLiteDataSource() + ds.url = "jdbc:sqlite:$sqlitePath" + connection = ds.connection + } + + obtainConnectionLock.unlock() + return connection!! + } + + companion object { + const val TABLE_CHECK = "SELECT name FROM sqlite_master WHERE type='table' AND name=?;" + } +} \ No newline at end of file diff --git a/Server/src/main/core/net/IoEventHandler.java b/Server/src/main/core/net/IoEventHandler.java index f6d63d869..981b70dbf 100644 --- a/Server/src/main/core/net/IoEventHandler.java +++ b/Server/src/main/core/net/IoEventHandler.java @@ -61,18 +61,19 @@ public class IoEventHandler { IoSession session = (IoSession) key.attachment(); try { if (channel.read(buffer) == -1) { - if (session != null && session.getPlayer() != null) { - Repository.getDisconnectionQueue().add(session.getPlayer()); + if (session != null) { + session.disconnect(); } key.cancel(); return; } } catch (IOException e) { - if (e.getMessage().contains("reset by peer")) { + if (e.getMessage().contains("reset by peer") && session != null) { session.disconnect(); - if (session.getPlayer() != null) - session.getPlayer().clear(true); - } else e.printStackTrace(); + } else { + key.cancel(); + return; + } } buffer.flip(); if (session == null) { diff --git a/Server/src/main/core/net/IoSession.java b/Server/src/main/core/net/IoSession.java index a9a0b0f21..7e61dc28c 100644 --- a/Server/src/main/core/net/IoSession.java +++ b/Server/src/main/core/net/IoSession.java @@ -216,17 +216,12 @@ public class IoSession { key.cancel(); SocketChannel channel = (SocketChannel) key.channel(); channel.socket().close(); - if (object instanceof Player) { - final Player p = getPlayer(); - GameWorld.getPulser().submit(new Pulse(0) { - @Override - public boolean pulse() { - if (p.isActive() && !p.getSession().active) { - p.clear(); - } - return true; - } - }); + if (getPlayer() != null) { + try { + getPlayer().clear(); + } catch (Exception e) { + e.printStackTrace(); + } } object = null; } catch (IOException e) { diff --git a/Server/src/main/core/net/amsc/MSEventHandler.java b/Server/src/main/core/net/amsc/MSEventHandler.java index 8fc2420f1..70bea403d 100644 --- a/Server/src/main/core/net/amsc/MSEventHandler.java +++ b/Server/src/main/core/net/amsc/MSEventHandler.java @@ -1,5 +1,6 @@ package core.net.amsc; +import core.tools.Log; import core.tools.SystemLogger; import core.net.IoEventHandler; import core.net.IoSession; @@ -10,6 +11,8 @@ import java.nio.channels.Selector; import java.nio.channels.SocketChannel; import java.util.concurrent.Executors; +import static core.api.ContentAPIKt.log; + /** * Handles the management server events. * @author Emperor @@ -38,7 +41,7 @@ public final class MSEventHandler extends IoEventHandler { } catch (Throwable t) { t.printStackTrace(); } - SystemLogger.logErr(this.getClass(), "Failed connecting to Management Server!"); + log(this.getClass(), Log.ERR, "Failed connecting to Management Server!"); WorldCommunicator.terminate(); } diff --git a/Server/src/main/core/net/amsc/MSPacketRepository.java b/Server/src/main/core/net/amsc/MSPacketRepository.java index c8ea619be..f1072e6fd 100644 --- a/Server/src/main/core/net/amsc/MSPacketRepository.java +++ b/Server/src/main/core/net/amsc/MSPacketRepository.java @@ -92,6 +92,9 @@ public final class MSPacketRepository { buffer.put(publicSetting); buffer.put(privateSetting); buffer.put(tradeSetting); - WorldCommunicator.getSession().write(buffer); + if (WorldCommunicator.getSession() != null) + WorldCommunicator.getSession().write(buffer); + else + player.sendMessage("Privacy settings unavailable at the moment. Please try again later."); } -} \ No newline at end of file +} diff --git a/Server/src/main/core/net/amsc/WorldCommunicator.java b/Server/src/main/core/net/amsc/WorldCommunicator.java index 91e59e075..1c634d1d0 100644 --- a/Server/src/main/core/net/amsc/WorldCommunicator.java +++ b/Server/src/main/core/net/amsc/WorldCommunicator.java @@ -67,7 +67,6 @@ public final class WorldCommunicator { */ public static void connect() { try { - SystemLogger.logInfo(WorldCommunicator.class, "Attempting to connect to management server..."); setState(ManagementServerState.CONNECTING); /*if (isLocallyHosted()) { SystemLogger.log("Management server is hosted on local machine!"); @@ -77,7 +76,6 @@ public final class WorldCommunicator { //} reactor.start(); } catch (Throwable e) { - SystemLogger.logErr(WorldCommunicator.class, "Unable to connect to management server"); e.printStackTrace(); terminate(); } @@ -187,7 +185,6 @@ public final class WorldCommunicator { if (WorldCommunicator.state != state) { WorldCommunicator.state = state; state.set(); - SystemLogger.logInfo(WorldCommunicator.class, "Management server status: " + state + "."); } } diff --git a/Server/src/main/core/net/event/GameReadEvent.java b/Server/src/main/core/net/event/GameReadEvent.java index 37ba17f7d..f8dc8a948 100644 --- a/Server/src/main/core/net/event/GameReadEvent.java +++ b/Server/src/main/core/net/event/GameReadEvent.java @@ -1,5 +1,6 @@ package core.net.event; +import core.tools.Log; import core.tools.SystemLogger; import core.net.IoReadEvent; import core.net.IoSession; @@ -10,6 +11,8 @@ import core.net.packet.in.Packet; import java.nio.ByteBuffer; +import static core.api.ContentAPIKt.log; + /** * Handles game packet reading. * @author Emperor @@ -65,15 +68,13 @@ public final class GameReadEvent extends IoReadEvent { if (session == null || session.getPlayer() == null) { continue; } - if (opcode >= PACKET_SIZES.length) { - break; - } int header = PACKET_SIZES[opcode]; int size = header; if (header < 0) { size = getPacketSize(buffer, opcode, header, last); } if (size == -1) { + session.getPlayer().incrementInvalidPacketCount(); break; } if (buffer.remaining() < size) { @@ -93,16 +94,20 @@ public final class GameReadEvent extends IoReadEvent { byte[] data = new byte[size]; buffer.get(data); IoBuffer buf = new IoBuffer(opcode, null, ByteBuffer.wrap(data)); - //SystemLogger.log("Packet opcode " + opcode + "received."); - //IncomingPacket packet = PacketRepository.getIncoming(opcode); session.setLastPing(System.currentTimeMillis()); last = opcode; + //Authentic per-player per-tick limit on inbound packets + if (session.getPlayer().opCounts[opcode]++ >= 10) { + log(this.getClass(), Log.FINE, "Skipping packet " + opcode + " because already received more than 10!"); + return; + } + Packet processed = Decoders530.process(session.getPlayer(), opcode, buf); if (processed instanceof Packet.UnhandledOp) { - SystemLogger.logWarn(this.getClass(), "Unhandled opcode: " + opcode); + log(this.getClass(), Log.WARN, "Unhandled opcode: " + opcode); } else if (processed instanceof Packet.DecodingError) { - SystemLogger.logErr(this.getClass(), ((Packet.DecodingError) processed).getMessage()); + log(this.getClass(), Log.ERR, ((Packet.DecodingError) processed).getMessage()); } else if (!(processed instanceof Packet.NoProcess)) { PacketProcessor.enqueue(processed); } @@ -132,11 +137,7 @@ public final class GameReadEvent extends IoReadEvent { } return buffer.getShort() & 0xFFFF; } - if(header == -3){ - System.out.println(buffer.remaining()); - } - SystemLogger.logErr(this.getClass(), "Invalid packet [opcode=" + opcode + ", last=" + last + ", queued=" + usedQueuedBuffer + "], header=" + header+"!"); return -1; } -} \ No newline at end of file +} diff --git a/Server/src/main/core/net/event/HSReadEvent.java b/Server/src/main/core/net/event/HSReadEvent.java index 883ce2254..bf98d5ad0 100644 --- a/Server/src/main/core/net/event/HSReadEvent.java +++ b/Server/src/main/core/net/event/HSReadEvent.java @@ -4,6 +4,7 @@ import core.net.IoReadEvent; import core.net.IoSession; import core.net.lobby.WorldList; import core.net.registry.AccountRegister; +import core.tools.Log; import core.tools.RandomFunction; import core.tools.SystemLogger; @@ -11,6 +12,8 @@ import java.nio.ByteBuffer; import java.util.HashMap; import java.util.Map; +import static core.api.ContentAPIKt.log; + /** * Handles handshake read events. * @author Emperor @@ -47,7 +50,7 @@ public final class HSReadEvent extends IoReadEvent { int revision = buffer.getInt(); //int sub_revision = buffer.getInt(); buffer.flip(); - System.out.println(buffer.limit()); + if (revision != 530 ){//|| sub_revision != Constants.CLIENT_BUILD) { session.disconnect(); break; @@ -64,7 +67,7 @@ public final class HSReadEvent extends IoReadEvent { WorldList.sendUpdate(session, updateStamp); break; default: - SystemLogger.logInfo(this.getClass(), "PKT " + opcode); + log(this.getClass(), Log.FINE, "PKT " + opcode); session.disconnect(); break; } diff --git a/Server/src/main/core/net/event/JS5ReadEvent.java b/Server/src/main/core/net/event/JS5ReadEvent.java index ded5d14d5..4b1571d44 100644 --- a/Server/src/main/core/net/event/JS5ReadEvent.java +++ b/Server/src/main/core/net/event/JS5ReadEvent.java @@ -25,7 +25,7 @@ public final class JS5ReadEvent extends IoReadEvent { public void read(IoSession session, ByteBuffer buffer) { while (buffer.hasRemaining()) { int opcode = buffer.get() & 0xFF; - //System.out.println("JS5 initiated! Opcode: " + opcode); + if (buffer.remaining() < 3) { queueBuffer(opcode); return; @@ -34,11 +34,11 @@ public final class JS5ReadEvent extends IoReadEvent { case 0: case 1: int request = ByteBufferUtils.getMedium(buffer); - //System.out.println("Requested file " + request); + int container = request >> 16 & 0xFF; - //System.out.println("In container " + container); + int archive = request & 0xFFFF; - //System.out.println("In archive " + archive); + session.getJs5Queue().queue(container, archive, opcode == 1); break; case 2: // music @@ -71,7 +71,7 @@ public final class JS5ReadEvent extends IoReadEvent { session.disconnect(); return; default: - System.out.println("Unhandled JS5 opcode " + opcode + "!"); + buffer.get(); buffer.getShort(); break; diff --git a/Server/src/main/core/net/event/LoginReadEvent.kt b/Server/src/main/core/net/event/LoginReadEvent.kt index b80501b12..fb713b1db 100644 --- a/Server/src/main/core/net/event/LoginReadEvent.kt +++ b/Server/src/main/core/net/event/LoginReadEvent.kt @@ -15,23 +15,28 @@ import java.nio.ByteBuffer */ class LoginReadEvent(session: IoSession?, buffer: ByteBuffer?) : IoReadEvent(session, buffer) { override fun read(session: IoSession, buffer: ByteBuffer) { - val (response, info) = Login.decodeFromBuffer(buffer) - if(response != AuthResponse.Success || info == null) { - session.write(response) - return - } - val (authResponse, accountInfo) = GameWorld.authenticator.checkLogin(info.username, info.password) + try { + val (response, info) = Login.decodeFromBuffer(buffer) + if (response != AuthResponse.Success || info == null) { + session.write(response) + return + } + val (authResponse, accountInfo) = GameWorld.authenticator.checkLogin(info.username, info.password) - if(authResponse != AuthResponse.Success || accountInfo == null) { - session.write(authResponse) - return + if (authResponse != AuthResponse.Success || accountInfo == null) { + session.write(authResponse) + return + } + val details = PlayerDetails(info.username) + details.accountInfo = accountInfo + details.communication.parse(accountInfo) + session.clientInfo = ClientInfo(info.displayMode, info.windowMode, info.screenWidth, info.screenHeight) + session.isaacPair = info.isaacPair + session.associatedUsername = info.username + Login.proceedWith(session, details, info.opcode) + } catch (e: Exception) { + e.printStackTrace() + session.write(AuthResponse.UnexpectedError) } - val details = PlayerDetails(info.username) - details.accountInfo = accountInfo - details.communication.parse(accountInfo) - session.clientInfo = ClientInfo(info.displayMode, info.windowMode, info.screenWidth, info.screenHeight) - session.isaacPair = info.isaacPair - session.associatedUsername = info.username - Login.proceedWith(session, details, info.opcode) } } \ No newline at end of file diff --git a/Server/src/main/core/net/event/MSReadEvent.java b/Server/src/main/core/net/event/MSReadEvent.java index 9a6f3da88..5e12d6963 100644 --- a/Server/src/main/core/net/event/MSReadEvent.java +++ b/Server/src/main/core/net/event/MSReadEvent.java @@ -1,11 +1,14 @@ package core.net.event; +import core.tools.Log; import core.tools.SystemLogger; import core.net.IoReadEvent; import core.net.IoSession; import java.nio.ByteBuffer; +import static core.api.ContentAPIKt.log; + /** * Handles reading Management server packets. * @author Emperor @@ -85,8 +88,7 @@ public final class MSReadEvent extends IoReadEvent { } return buffer.getShort() & 0xFFFF; } - SystemLogger.logErr(this.getClass(), "Invalid packet [opcode=" + opcode + ", last=" + last + ", queued=" + usedQueuedBuffer + "]!"); return -1; } -} \ No newline at end of file +} diff --git a/Server/src/main/core/net/event/RegistryReadEvent.java b/Server/src/main/core/net/event/RegistryReadEvent.java index 60e3d855d..bfbedd61a 100644 --- a/Server/src/main/core/net/event/RegistryReadEvent.java +++ b/Server/src/main/core/net/event/RegistryReadEvent.java @@ -1,5 +1,6 @@ package core.net.event; +import core.tools.Log; import core.tools.SystemLogger; import core.game.world.GameWorld; import core.net.IoReadEvent; @@ -10,6 +11,8 @@ import core.net.producer.MSEventProducer; import java.nio.ByteBuffer; +import static core.api.ContentAPIKt.log; + /** * Handles world registry read events. * @author Emperor @@ -36,23 +39,18 @@ public final class RegistryReadEvent extends IoReadEvent { switch (opcode) { case 0: WorldCommunicator.setState(ManagementServerState.NOT_AVAILABLE); - SystemLogger.logErr(this.getClass(), "Failed registering world to AMS - [id=" + GameWorld.getSettings().getWorldId() + ", cause=World id out of bounds]!"); + log(this.getClass(), Log.ERR, "Failed registering world to AMS - [id=" + GameWorld.getSettings().getWorldId() + ", cause=World id out of bounds]!"); break; case 1: session.setProducer(PRODUCER); WorldCommunicator.setState(ManagementServerState.AVAILABLE); - SystemLogger.logInfo(this.getClass(), "Successfully registered world to AMS - [id=" + GameWorld.getSettings().getWorldId() + "]!"); break; case 2: - WorldCommunicator.setState(ManagementServerState.NOT_AVAILABLE); - SystemLogger.logErr(this.getClass(), "Failed registering world to AMS - [id=" + GameWorld.getSettings().getWorldId() + ", cause=World id already in use]!"); + case 3: + WorldCommunicator.setState(ManagementServerState.NOT_AVAILABLE); break; - case 3: - WorldCommunicator.setState(ManagementServerState.NOT_AVAILABLE); - SystemLogger.logErr(this.getClass(), "Failed registering world to AMS - [id=" + GameWorld.getSettings().getWorldId() + ", cause=Exception in AMS]!"); - break; - default: - System.out.println("??" + opcode); + default: + break; } } diff --git a/Server/src/main/core/net/lobby/WorldList.java b/Server/src/main/core/net/lobby/WorldList.java index 0da990c44..1a9a719dd 100644 --- a/Server/src/main/core/net/lobby/WorldList.java +++ b/Server/src/main/core/net/lobby/WorldList.java @@ -119,7 +119,7 @@ public final class WorldList { * Populates the world list. */ static { - addWorld(new WorldDefinition(1, 0, FLAG_MEMBERS | FLAG_LOOTSHARE, "2009Scape Classic", "127.0.0.1", "Anywhere, USA", COUNTRY_USA)); + addWorld(new WorldDefinition(1, 0, FLAG_MEMBERS | FLAG_LOOTSHARE, "Snowscape custom server", "127.0.0.1", "Canada", COUNTRY_CANADA)); } /** @@ -211,4 +211,4 @@ public final class WorldList { public static void flagUpdate() { WorldList.updateStamp = GameWorld.getTicks(); } -} \ No newline at end of file +} diff --git a/Server/src/main/core/net/packet/IoBuffer.java b/Server/src/main/core/net/packet/IoBuffer.java index 219d9c1a0..279798007 100644 --- a/Server/src/main/core/net/packet/IoBuffer.java +++ b/Server/src/main/core/net/packet/IoBuffer.java @@ -92,6 +92,228 @@ public class IoBuffer { return this; } + /** + * What follows are put/get methods using authentic naming. + * The older methods are kept for the sake of backwards compatibility within the codebase. + */ + public IoBuffer p1 (int value) { + buf.put((byte) value); + return this; + } + + public IoBuffer p1add (int value) { + buf.put((byte) (value + 128)); + return this; + } + + public IoBuffer p1sub (int value) { + buf.put((byte) (128 - value)); + return this; + } + + public IoBuffer p1neg (int value) { + buf.put ((byte) -value); + return this; + } + + public IoBuffer p2 (int value) { + buf.put((byte) (value >> 8)); + buf.put((byte) value); + return this; + } + + public IoBuffer p2add (int value) { + buf.put((byte) (value >> 8)); + buf.put((byte) (value + 128)); + return this; + } + + public IoBuffer ip2 (int value) { + buf.put((byte) value); + buf.put((byte) (value >> 8)); + return this; + } + + public IoBuffer ip2add (int value) { + buf.put((byte) (value + 128)); + buf.put((byte) (value >> 8)); + return this; + } + + public IoBuffer p3 (int value) { + buf.put((byte) (value >> 16)); + buf.put((byte) (value >> 8)); + buf.put((byte) value); + return this; + } + + public IoBuffer ip3 (int value) { + buf.put((byte) value); + buf.put((byte) (value >> 8)); + buf.put((byte) (value >> 16)); + return this; + } + + public IoBuffer p4 (int value) { + buf.put((byte) (value >> 24)); + buf.put((byte) (value >> 16)); + buf.put((byte) (value >> 8)); + buf.put((byte) value); + return this; + } + + public IoBuffer ip4 (int value) { + buf.put((byte) value); + buf.put((byte) (value >> 8)); + buf.put((byte) (value >> 16)); + buf.put((byte) (value >> 24)); + return this; + } + + public IoBuffer mp4 (int value) { + buf.put((byte) (value >> 16)); + buf.put((byte) (value >> 24)); + buf.put((byte) value); + buf.put((byte) (value >> 8)); + return this; + } + + public IoBuffer imp4 (int value) { + buf.put((byte) (value >> 8)); + buf.put((byte) value); + buf.put((byte) (value >> 24)); + buf.put((byte) (value >> 16)); + return this; + } + + public IoBuffer p8 (long value) { + buf.put((byte) (value >> 56)); + buf.put((byte) (value >> 48)); + buf.put((byte) (value >> 40)); + buf.put((byte) (value >> 32)); + buf.put((byte) (value >> 24)); + buf.put((byte) (value >> 16)); + buf.put((byte) (value >> 8)); + buf.put((byte) value); + return this; + } + + public IoBuffer pVarInt (int value) { + if ((value & 0xffffff80) != 0) { + if ((value & 0xffffc000) != 0) { + if ((value & 0xFFE00000) != 0) { + if ((value & 0xF0000000) != 0) { + this.p1(value >>> 28 | 0x80); + } + this.p1(value >>> 21 | 0x80); + } + this.p1(value >>> 14 | 0x80); + } + this.p1(value >>> 7 | 0x80); + } + return this.p1(value & 0x7F); + } + + public IoBuffer pVarLong (int size, long value) { + int bytes = size - 1; + if (bytes < 0 || bytes > 7) + throw new IllegalArgumentException(); + for (int shift = bytes * 8; shift >= 0; shift -= 8) + this.p1 ((byte) (value >> shift)); + return this; + } + + public IoBuffer psmarts (int value) { + if (value >= 0 && value < 128) + this.p1(value); + else if (value >= 0 && value < 0x8000) + this.p2(value + 0x8000); + else + throw new IllegalArgumentException("smart out of range: $value"); + return this; + } + + public IoBuffer psize (int length) { + buf.put (buf.position() - length - 1, (byte) length); + return this; + } + + public IoBuffer psizeadd (int length) { + buf.put (buf.position() - length - 1, (byte) (length + 128)); + return this; + } + + public int g1() { + return buf.get() & 0xFF; + } + + public int g1b() { return buf.get(); } + + public int g1add() { + return (buf.get() - 128) & 0xFF; + } + + public int g1neg() { + return -(buf.get() & 0xFF); + } + + public int g1sub() { + return (128 - buf.get()) & 0xFF; + } + + public int g2() { + return ((buf.get() & 0xFF) << 8) + (buf.get() & 0xFF); + } + + public int g2add() { + return ((buf.get() & 0xff) << 8) + ((buf.get() - 128) & 0xFF); + } + + public int g2b() { + int value = ((buf.get() & 0xFF) << 8) + (buf.get() & 0xFF); + if (value > 32767) + value -= 0x10000; + return value; + } + + public int ig2() { + return (buf.get() & 0xFF) + ((buf.get() & 0xFF) << 8); + } + + public int ig2add() { + return ((buf.get() - 128) & 0xFF) + ((buf.get() & 0xFF) << 8); + } + + public int g3() { + return ((buf.get() & 0xFF) << 16) + ((buf.get() & 0xFF) << 8) + (buf.get() & 0xFF); + } + + public int ig3() { + return (buf.get() & 0xFF) + ((buf.get() & 0xFF) << 8) + ((buf.get() & 0xFF) << 16); + } + + public int g4() { + return ((buf.get() & 0xFF) << 24) + ((buf.get() & 0xFF) << 16) + ((buf.get() & 0xFF) << 8) + (buf.get() & 0xFF); + } + + public int ig4() { + return (buf.get() & 0xFF) + ((buf.get() & 0xFF) << 8) + ((buf.get() & 0xFF) << 16) + ((buf.get() & 0xFF) << 24); + } + + public int m4() { + return ((buf.get() & 0xFF) << 16) + ((buf.get() & 0xFF) << 24) + (buf.get() & 0xFF) + ((buf.get() & 0xFF) << 8); + } + + public int im4() { + return ((buf.get() & 0xFF) << 8) + (buf.get() & 0xFF) + ((buf.get() & 0xFF) << 24) + ((buf.get() & 0xFF) << 16); + } + + public long g8() { + long low = (long) this.g4() & 0xFFFFFFFFL; + long high = (long) this.g4() & 0xFFFFFFFFL; + return high + (low << 32); + } + /** * @param val * @return @@ -557,8 +779,9 @@ public class IoBuffer { * @return */ public String getJagString() { - buf.get(); - return ByteBufferUtils.getString(buf); + byte b = buf.get(); + if (b == 0) return ""; + return ((char) b) + ByteBufferUtils.getString(buf); } /** @@ -635,4 +858,4 @@ public class IoBuffer { return bitPosition; } -} \ 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 33ebf86c2..fdba276be 100644 --- a/Server/src/main/core/net/packet/PacketProcessor.kt +++ b/Server/src/main/core/net/packet/PacketProcessor.kt @@ -9,18 +9,13 @@ import core.cache.def.impl.NPCDefinition import core.cache.def.impl.SceneryDefinition import core.game.container.Container import core.game.container.impl.BankContainer -import core.game.interaction.PluginInteractionManager -import core.game.interaction.Interaction -import core.game.interaction.MovementPulse -import core.game.interaction.NodeUsageEvent -import core.game.interaction.Option -import core.game.interaction.UseWithHandler import core.game.node.Node import core.game.node.entity.player.Player 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 @@ -31,7 +26,7 @@ import core.game.system.task.Pulse import core.game.world.map.Location import core.game.world.map.RegionManager import core.game.world.update.flag.context.ChatMessage -import core.game.world.update.flag.player.ChatFlag +import core.game.world.update.flag.* import core.net.amsc.MSPacketRepository import core.net.packet.context.PlayerContext import core.net.packet.out.ClearMinimapFlag @@ -44,24 +39,27 @@ import core.game.ge.GrandExchange.Companion.getOfferStats import core.game.ge.GrandExchange.Companion.getRecommendedPrice import core.game.ge.GrandExchangeOffer import core.game.ge.PriceIndex -import core.game.interaction.IntType -import core.game.interaction.InteractionListeners -import core.game.interaction.InterfaceListeners import content.global.handlers.iface.ge.StockMarket import content.global.skill.magic.SpellListener import content.global.skill.magic.SpellListeners import content.global.skill.magic.SpellUtils +import core.api.log +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.command.CommandSystem +import core.game.system.communication.GlobalChat import core.game.world.GameWorld import core.game.world.repository.Repository import core.net.packet.`in`.Packet import core.net.packet.`in`.RunScript +import core.tools.Log import core.worker.ManagementEvents +import core.api.utils.Vector import java.io.PrintWriter import java.io.StringWriter +import java.lang.Math.min import java.util.* object PacketProcessor { @@ -77,12 +75,16 @@ object PacketProcessor { val pw = PrintWriter(sw) var pkt: Packet while (countThisCycle-- > 0) { - pkt = queue.tryPop(Packet.NoProcess()) + pkt = queue.tryPop(Packet.NoProcess()) ?: return + if (pkt is Packet.NoProcess) { + queue.clear() + return + } try { process(pkt) } catch (e: Exception) { e.printStackTrace(pw) - SystemLogger.logErr(this::class.java, "Error Processing ${pkt::class.java.simpleName}: $sw") + log(this::class.java, Log.ERR, "Error Processing ${pkt::class.java.simpleName}: $sw") } } } @@ -157,6 +159,10 @@ object PacketProcessor { } } is Packet.ReportAbuse -> { + if (pkt.target.isNullOrEmpty()) { + sendMessage(pkt.player, "Invalid player name.") + return + } if (!GameWorld.accountStorage.checkUsernameTaken(pkt.target.lowercase())) { sendMessage(pkt.player, "Invalid player name.") return @@ -202,18 +208,33 @@ object PacketProcessor { if (pkt.player.details.isMuted) pkt.player.sendMessage("You have been muted due to breaking a rule.") else { - if (pkt.message.startsWith("/") && pkt.player.communication.clan != null) { - val builder = ClanMessage.newBuilder() - builder.sender = pkt.player.name - builder.clanName = pkt.player.communication.clan.owner.lowercase().replace(" ", "_") - builder.message = pkt.message.substring(1) - builder.rank = pkt.player.rights.ordinal - ManagementEvents.publish(builder.build()) + if (ServerConstants.ENABLE_GLOBALCHAT && 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()) + GlobalChat.process(pkt.player.username, message, Rights.getChatIcon(pkt.player)) + } + return + } + else if (pkt.message.startsWith("/") && pkt.player.communication.clan != null) { + val messages = splitChatMessage(pkt.message.substring(1), pkt.player.communication.clan.name.length + pkt.player.name.length, pkt.player.details.rights.ordinal != 0) + for (message in messages) { + if (message.isBlank()) continue + val builder = ClanMessage.newBuilder() + builder.sender = pkt.player.name + builder.clanName = pkt.player.communication.clan.owner.lowercase().replace(" ", "_") + builder.message = message + builder.rank = Rights.getChatIcon(pkt.player) + ManagementEvents.publish(builder.build()) + } return } PlayerMonitor.logChat(pkt.player, "public", pkt.message) val ctx = ChatMessage(pkt.player, pkt.message, pkt.effects, pkt.message.length) - pkt.player.updateMasks.register(ChatFlag(ctx)) + pkt.player.updateMasks.register(EntityFlag.Chat, ctx) } } is Packet.ChatSetting -> { @@ -250,7 +271,7 @@ object PacketProcessor { } is Packet.ItemExamine -> { val def = ItemDefinition.forId(pkt.id) ?: return - pkt.player.debug("[ITEM] ID: ${pkt.id} Value: ${def.value}") + pkt.player.debug("[ITEM] ID: ${pkt.id} Value: ${def.value} Model: ${def.interfaceModelId}") pkt.player.sendMessage(def.examine) } is Packet.SceneryExamine -> { @@ -273,7 +294,7 @@ object PacketProcessor { } - else -> SystemLogger.logWarn(this::class.java, "Unprocessed Packet: ${pkt::class.java.simpleName}") + else -> log(this::class.java, Log.WARN, "Unprocessed Packet: ${pkt::class.java.simpleName}") } } @@ -287,8 +308,25 @@ object PacketProcessor { pkt.player.interfaceManager.switchWindowMode(pkt.windowMode) } is Packet.TrackingAfkTimeout -> { - if (pkt.player.details.rights != Rights.ADMINISTRATOR) - pkt.player.packetDispatch.sendLogout() + /* After 5 minutes idle, the client sends an afk packet every 10 seconds. + *This code counts the number of afk packets recieved, resetting the count if they are more than 100 ticks apart. + *This allows us to customize the afk logout timer + */ + if (pkt.player.details.rights != Rights.ADMINISTRATOR) { + if (GameWorld.ticks - pkt.player.getAttribute("afk:lasttick", 0) > 100) { + pkt.player.setAttribute("afk:count", 0) + } else { + pkt.player.setAttribute("afk:count", pkt.player.getAttribute("afk:count", 0) + 1) + } + pkt.player.setAttribute("afk:lasttick", GameWorld.ticks) + if (pkt.player.getAttribute("afk:count", 0) == 300) { + pkt.player.sendMessage("You have been idle for 55 minutes and will be logged out soon.") + } + if (pkt.player.getAttribute("afk:count", 0) >= 330) { + pkt.player.isAfkLogout = true + pkt.player.packetDispatch.sendLogout() + } + } } is Packet.TrackingCameraPos -> { //TODO Refactor the player monitor to be actually useful and log this @@ -297,6 +335,7 @@ object PacketProcessor { //TODO see above todo } + else -> {} } } @@ -436,15 +475,22 @@ object PacketProcessor { //there's more data in this packet, we're just not using it } + var loc = Location.create(x,y,player.location.z) var canWalk = !player.locks.isMovementLocked + val vec = Vector.betweenLocs(player.location, loc) + if (vec.magnitude() > ServerConstants.MAX_PATHFIND_DISTANCE) { + val newVec = vec.normalized() * (ServerConstants.MAX_PATHFIND_DISTANCE - 1) + loc = player.location.transform(newVec) + } + if (canWalk && player.interfaceManager.isOpened && !player.interfaceManager.opened.definition.isWalkable) canWalk = canWalk && player.interfaceManager.close() if (canWalk && player.interfaceManager.hasChatbox() && !player.interfaceManager.chatbox.definition.isWalkable) player.interfaceManager.closeChatbox() if (!canWalk || !player.dialogueInterpreter.close()) { - player.debug("[WALK ACTION]-- NO HANDLE: PLAYER LOCKED OR INTERFACES SAY NO") + player.debug("[WALK ACTION]-- Action canceled. Either player is locked, interfaces can't close, or distance is beyond server pathfinding limit.") return sendClearMinimap(player) } @@ -456,8 +502,9 @@ object PacketProcessor { player.face(null) player.faceLocation(null) + player.scripts.reset() - player.pulseManager.run(object : MovementPulse(player, Location.create(x,y,player.location.z), isRunning) { + player.pulseManager.run(object : MovementPulse(player, loc, isRunning) { override fun pulse(): Boolean { if (isRunning) player.walkingQueue.isRunning = false @@ -492,7 +539,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 @@ -546,9 +593,9 @@ object PacketProcessor { player = pkt.player } else if (pkt is Packet.UseWithGroundItem) { val container = getLikelyContainerForIface(pkt.player, pkt.iface) ?: return - item = container[pkt.slot] + item = container[pkt.slot] ?: return itemId = pkt.usedId - node = GroundItemManager.get(pkt.withId, Location.create(pkt.x, pkt.y, pkt.player.location.z), pkt.player) + node = GroundItemManager.get(pkt.withId, Location.create(pkt.x, pkt.y, pkt.player.location.z), pkt.player) ?: return nodeId = pkt.withId type = IntType.GROUNDITEM player = pkt.player @@ -568,6 +615,10 @@ object PacketProcessor { if (node.id != nodeId) return sendClearMinimap(player) + if (player.locks.isInteractionLocked() || player.locks.isMovementLocked()) + return sendClearMinimap(player) + + player.scripts.reset() if (player.zoneMonitor.useWith(item, node)) return if (InteractionListeners.run(item, node, type, player)) @@ -589,13 +640,14 @@ object PacketProcessor { private fun processGroundItemAction(pkt: Packet.GroundItemAction) { val item = GroundItemManager.get(pkt.id, Location.create(pkt.x, pkt.y, pkt.player.location.z), pkt.player) val player = pkt.player + player.scripts.reset() if (item == null) { return sendClearMinimap(player) } val option = item.interaction[pkt.optIndex] if (option == null) { - Interaction.handleInvalidInteraction(player, item, Option.NULL) + InteractPlugin.handleInvalidInteraction(player, item, Option.NULL) return sendClearMinimap(player) } if (PluginInteractionManager.handle(player, item, option)) @@ -612,6 +664,7 @@ object PacketProcessor { if (pkt.otherIndex !in 1 until ServerConstants.MAX_PLAYERS) { return sendClearMinimap(player) } + player.scripts.reset() val other = Repository.players[pkt.otherIndex] if (other == null || !other.isActive) return sendClearMinimap(player) @@ -632,6 +685,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) { @@ -641,7 +699,7 @@ object PacketProcessor { if (scenery == null || scenery.id != objId || !scenery.isActive) { player.debug("[SCENERY INTERACT] NULL OR MISMATCH OR INACTIVE") - Interaction.handleInvalidInteraction(player, scenery, Option.NULL) + InteractPlugin.handleInvalidInteraction(player, scenery, Option.NULL) return sendClearMinimap(player) } @@ -650,7 +708,7 @@ object PacketProcessor { if (option == null) { player.debug("[SCENERY INTERACT] NULL OPTION") - Interaction.handleInvalidInteraction(player, scenery, Option.NULL) + InteractPlugin.handleInvalidInteraction(player, scenery, Option.NULL) return sendClearMinimap(player) } @@ -664,6 +722,7 @@ object PacketProcessor { } player.debug("------------------------------------------------") + player.scripts.reset() if (InteractionListeners.run(wrapperChild.id, IntType.SCENERY, option.name, player, wrapperChild)) return if (PluginInteractionManager.handle(player, wrapperChild)) @@ -680,7 +739,7 @@ object PacketProcessor { val option = wrapperChild.interaction[pkt.optIndex] if (option == null) { - Interaction.handleInvalidInteraction(pkt.player, npc, Option.NULL) + InteractPlugin.handleInvalidInteraction(pkt.player, npc, Option.NULL) return sendClearMinimap(pkt.player) } @@ -695,6 +754,7 @@ object PacketProcessor { } pkt.player.debug("---------------------------------") + pkt.player.scripts.reset() if (InteractionListeners.run(wrapperChild.id, IntType.NPC,option.name,pkt.player,npc)) return if (PluginInteractionManager.handle(pkt.player, wrapperChild, option)) @@ -713,6 +773,7 @@ object PacketProcessor { if (pkt.player.locks.isInteractionLocked) return item.interaction.handleItemOption(pkt.player, option, container) + pkt.player.scripts.reset() pkt.player.debug("[ITEM INTERACT] ID: ${item.id}, Slot: ${pkt.slot}, Opt: ${option.name}") } @@ -755,5 +816,24 @@ object PacketProcessor { container.insert(slot, secondSlot, false) } container.refresh() - } -} \ No newline at end of file + } + + fun splitChatMessage(message: String, clanLength: Int, rankPresent: Boolean) : ArrayList { + val messages = ArrayList() + + val effectiveCutoff = BASE_CHAT_CUTOFF - (clanLength + if (rankPresent) 9 else 0) + var counter = 0 + for (token in message.split(" ")) { + if (counter + token.length > effectiveCutoff) + break + counter += token.length + 1 + } + messages.add(message.substring(0, min(counter, message.length))) + if (counter < message.length) + messages.add(message.substring(counter, message.length)) + + return messages + } + + const val BASE_CHAT_CUTOFF = 81 +} diff --git a/Server/src/main/core/net/packet/PacketRepository.java b/Server/src/main/core/net/packet/PacketRepository.java index e1f41fb43..60c6fddb0 100644 --- a/Server/src/main/core/net/packet/PacketRepository.java +++ b/Server/src/main/core/net/packet/PacketRepository.java @@ -2,10 +2,16 @@ package core.net.packet; import core.net.packet.out.GrandExchangePacket; import core.net.packet.out.*; +import core.tools.Log; import core.tools.SystemLogger; +import java.io.PrintWriter; +import java.io.StringWriter; import java.util.HashMap; import java.util.Map; +import core.game.bots.AIPlayer; + +import static core.api.ContentAPIKt.log; /** * The packet repository. @@ -64,12 +70,14 @@ public final class PacketRepository { OUTGOING_PACKETS.put(CommunicationMessage.class, new CommunicationMessage()); // OUTGOING_PACKETS.put(UpdateClanChat.class, new UpdateClanChat()); // OUTGOING_PACKETS.put(UpdateGroundItemAmount.class, new UpdateGroundItemAmount()); // - OUTGOING_PACKETS.put(WeightUpdate.class, new WeightUpdate()); // + //OUTGOING_PACKETS.put(WeightUpdate.class, new WeightUpdate()); // OUTGOING_PACKETS.put(UpdateRandomFile.class, new UpdateRandomFile()); // 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(VarcUpdate.class, new VarcUpdate()); + OUTGOING_PACKETS.put(InterfaceSetAngle.class, new InterfaceSetAngle()); } /** @@ -79,14 +87,22 @@ public final class PacketRepository { */ @SuppressWarnings({ "rawtypes", "unchecked" }) public static void send(Class clazz, Context context) { + if(context.getPlayer() instanceof AIPlayer) return; if(context.getPlayer().getSession() == null) return; OutgoingPacket p = OUTGOING_PACKETS.get(clazz); if (p == null) { - SystemLogger.logErr(PacketRepository.class, "Invalid outgoing packet [handler=" + clazz + ", context=" + context + "]."); + log(PacketRepository.class, Log.ERR, "Invalid outgoing packet [handler=" + clazz + ", context=" + context + "]."); return; } if(!context.getPlayer().isArtificial()) { - PacketWriteQueue.handle(p, context); + try { + PacketWriteQueue.handle(p, context); + } catch (Exception e) { + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + e.printStackTrace(pw); + log(PacketRepository.class, Log.ERR, "Error writing packet out: " + sw); + } } } diff --git a/Server/src/main/core/net/packet/PacketWriteQueue.kt b/Server/src/main/core/net/packet/PacketWriteQueue.kt index 294a54452..fad453300 100644 --- a/Server/src/main/core/net/packet/PacketWriteQueue.kt +++ b/Server/src/main/core/net/packet/PacketWriteQueue.kt @@ -1,22 +1,18 @@ package core.net.packet -import core.api.TickListener +import core.api.log +import core.api.tryPop import core.net.packet.out.* +import core.tools.Log import core.tools.SystemLogger -import java.lang.IndexOutOfBoundsException +import java.io.PrintWriter +import java.io.StringWriter import java.util.* import java.util.concurrent.locks.ReentrantLock -import kotlin.NoSuchElementException - -class PacketWriteQueue : TickListener { - override fun tick() { - flush() - } +class PacketWriteQueue { companion object { - private val queueLock = ReentrantLock() - private val packetsToQueue = LinkedList?>() - private val packetsToWrite = LinkedList?>() + private val packetsToWrite = LinkedList>() @JvmStatic fun handle(packet: OutgoingPacket, context: T) { @@ -25,6 +21,7 @@ class PacketWriteQueue : TickListener { is UpdateSceneGraph, is BuildDynamicScene, is InstancedLocationUpdate, + is Logout, is ClearRegionChunk -> packet.send(context) //Rest get queued up and sent at the end of the tick (authentic) else -> push(packet, context) @@ -33,46 +30,28 @@ class PacketWriteQueue : TickListener { @JvmStatic fun push(packet: OutgoingPacket, context: T) { - if (queueLock.isHeldByCurrentThread) - packetsToQueue.add(QueuedPacket(packet, context)) - else - packetsToWrite.add(QueuedPacket(packet, context)) + if (context == null) { + log(this::class.java, Log.ERR, "${packet::class.java.simpleName} tried to queue with a null context!") + return + } + packetsToWrite.add(QueuedPacket(packet, context)) } @JvmStatic fun flush() { - queueLock.lock() - - var hasEnded = false - while (!hasEnded) { + var countThisCycle = packetsToWrite.size + val sw = StringWriter() + val pw = PrintWriter(sw) + while (countThisCycle-- > 0) { + val pkt = packetsToWrite.tryPop(null) ?: continue try { - val packet = packetsToWrite.pop() - write(packet?.out ?: continue, packet.context ?: continue) - } catch (e: NoSuchElementException) { - hasEnded = true - } - } - - if (packetsToWrite.isNotEmpty()) { - SystemLogger.logWarn(this::class.java, "Packet queue was NOT empty! Remaining packets: ${packetsToWrite.size}") - try { - for (pkt: QueuedPacket<*>? in packetsToWrite) SystemLogger.logWarn(this::class.java, "${pkt?.out?.javaClass?.simpleName ?: "NULL"} <- ${pkt?.context ?: "NULL"}") - } catch (ignored: NullPointerException) { - //do nothing, we don't care, this can happen when everything is working as intended. - } catch (ignored: IndexOutOfBoundsException) { - //do nothing, we don't care, this can happen when everything is working as intended. + write(pkt.out, pkt.context) } catch (e: Exception) { - e.printStackTrace() - } finally { - packetsToWrite.clear() + e.printStackTrace(pw) + log(this::class.java, Log.ERR, "Error flushing packet ${pkt.out::class.java}: $sw") + continue } } - - queueLock.unlock() - - while (packetsToQueue.isNotEmpty()) { - packetsToWrite.add(packetsToQueue.pop()) - } } @Suppress("UNCHECKED_CAST") @@ -80,12 +59,11 @@ class PacketWriteQueue : TickListener { val pack = out as? OutgoingPacket val ctx = context as? T if (pack == null || ctx == null) { - SystemLogger.logWarn(this::class.java, "Failed packet casting") - return + throw IllegalStateException("Failed packet casting") } pack.send(ctx) } } } -class QueuedPacket(val out: OutgoingPacket, val context: T) \ No newline at end of file +class QueuedPacket(val out: OutgoingPacket, val context: T) diff --git a/Server/src/main/core/net/packet/QCRepository.kt b/Server/src/main/core/net/packet/QCRepository.kt index 42238e15d..b2b1ccb2a 100644 --- a/Server/src/main/core/net/packet/QCRepository.kt +++ b/Server/src/main/core/net/packet/QCRepository.kt @@ -1,6 +1,7 @@ package core.net.packet import content.global.skill.slayer.SlayerManager +import core.api.* import core.cache.Cache import core.cache.def.impl.DataMap import core.cache.def.impl.ItemDefinition @@ -10,7 +11,7 @@ import core.game.node.entity.player.Player import core.game.node.entity.skill.Skills import core.game.system.task.Pulse import core.game.world.update.flag.context.ChatMessage -import core.game.world.update.flag.player.ChatFlag +import core.game.world.update.flag.* import proto.management.ClanMessage import core.game.world.GameWorld.Pulser import core.net.packet.`in`.QCPacketType @@ -53,7 +54,7 @@ object QCRepository { ctx.isQuickChat = true Pulser.submit(object : Pulse(0, player) { override fun pulse(): Boolean { - player.updateMasks.register(ChatFlag(ctx)) + player.updateMasks.register(EntityFlag.Chat, ctx) return true } }) @@ -89,7 +90,7 @@ object QCRepository { //My current slayer assignment is else if(qcString.contains("My current Slayer assignment is")){ val amount = SlayerManager.getInstance(player!!).amount - val taskName = NPCDefinition.forId(SlayerManager.getInstance(player!!).task?.ids?.get(0) ?: 0).name.toLowerCase() + val taskName = getSlayerTask(player)?.name?.toLowerCase() ?: "None" if(amount ?: 0 > 0){ qcString = qcString.replace("complete", "$amount $taskName") } @@ -317,4 +318,4 @@ object QCRepository { private fun getQCString(index: Int): String{ return ByteBufferUtils.getString(ByteBuffer.wrap(quickChatIndex.getFileData(1, index))) } -} \ 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/in/Decoders530.kt b/Server/src/main/core/net/packet/in/Decoders530.kt index bb567da69..6181f25dd 100644 --- a/Server/src/main/core/net/packet/in/Decoders530.kt +++ b/Server/src/main/core/net/packet/in/Decoders530.kt @@ -1,11 +1,15 @@ package core.net.packet.`in` +import core.api.log +import core.Util.clamp import core.game.node.entity.player.Player import core.net.packet.IoBuffer +import core.tools.Log import core.tools.StringUtils import core.tools.SystemLogger import java.io.PrintWriter import java.io.StringWriter +import java.nio.BufferUnderflowException enum class Decoders530(val opcode: Int) { /****************************************** @@ -632,8 +636,13 @@ enum class Decoders530(val opcode: Int) { COMMAND(44) { override fun decode(player: Player, buffer: IoBuffer): Packet { if (buffer.toByteBuffer().remaining() > 1) { - val message = buffer.string.lowercase() - return Packet.Command(player, message) + try { + val message = buffer.string.lowercase() + return Packet.Command(player, message) + } catch (e: BufferUnderflowException) { + player.sendMessage("Something went wrong there! Please try again.") + return Packet.NoProcess() + } } return Packet.NoProcess() } @@ -650,9 +659,11 @@ enum class Decoders530(val opcode: Int) { }, CHAT_MESSAGE(237) { override fun decode(player: Player, buffer: IoBuffer): Packet { - val effects = buffer.short + val effectPrefix = clamp(buffer.get(), 0, 11) + val effectSuffix = clamp(buffer.get(), 0, 5) val numChars = buffer.smart val message = StringUtils.decryptPlayerChat(buffer, numChars) + val effects = (effectPrefix shl 8) or effectSuffix return Packet.ChatMessage(player, effects, message) } }, @@ -689,7 +700,7 @@ enum class Decoders530(val opcode: Int) { 3,4 -> QCPacketType.STANDARD 5 -> QCPacketType.SINGLE 7 -> QCPacketType.DOUBLE - else -> QCPacketType.UNHANDLED.also { SystemLogger.logWarn(this::class.java, "UNHANDLED QC PACKET TYPE Size ${x.array().size}") } + else -> QCPacketType.UNHANDLED.also { log(this::class.java, Log.WARN, "UNHANDLED QC PACKET TYPE Size ${x.array().size}") } } val forClan = (buffer.get() and 0xFF) == 1 @@ -708,7 +719,8 @@ enum class Decoders530(val opcode: Int) { buffer.get() //discard selection_b_index = buffer.get() } - QCPacketType.UNHANDLED -> SystemLogger.logWarn(this::class.java, "Unhandled packet type, skipping remaining buffer contents.") + QCPacketType.UNHANDLED -> log(this::class.java, Log.WARN, "Unhandled packet type, skipping remaining buffer contents.") + else -> {} } return Packet.QuickChat(player, selection_a_index, selection_b_index, forClan, multiplier, offset, packetType) } @@ -743,7 +755,7 @@ enum class Decoders530(val opcode: Int) { val showGroundDeco = (prefs shr 5) and 1 == 1 val selectiveRoofs = (prefs shr 4) and 1 == 1 val allLayersVisible = (prefs shr 3) and 1 == 1 - val brightness = prefs and 0x7; + val brightness = prefs and 0x7 //added in the above so that we don't have to figure it out later, not doing more //because we currently have genuinely no use for this information. return Packet.PlayerPrefsUpdate(player, prefs) @@ -789,4 +801,4 @@ enum class Decoders530(val opcode: Int) { } } } -} \ No newline at end of file +} diff --git a/Server/src/main/core/net/packet/in/Login.kt b/Server/src/main/core/net/packet/in/Login.kt index 4d4988e3c..df243d9d6 100644 --- a/Server/src/main/core/net/packet/in/Login.kt +++ b/Server/src/main/core/net/packet/in/Login.kt @@ -1,5 +1,6 @@ package core.net.packet.`in` +import com.moandjiezana.toml.Toml import core.cache.crypto.ISAACCipher import core.cache.crypto.ISAACPair import core.cache.misc.buffer.ByteBufferUtils @@ -15,13 +16,17 @@ import core.ServerConstants import core.ServerStore import core.ServerStore.Companion.addToList import core.ServerStore.Companion.getList +import core.api.log import core.auth.AuthResponse import core.game.node.entity.player.info.* import core.game.node.entity.player.info.login.LoginParser import core.tools.SystemLogger import core.game.world.GameWorld import core.game.world.repository.Repository +import core.tools.Log import core.worker.ManagementEvents.publish +import java.io.File +import java.io.IOException import java.math.BigInteger import java.nio.BufferUnderflowException import java.nio.ByteBuffer @@ -32,6 +37,9 @@ object Login { private const val RECONNECT_LOGIN_OP = 18 const val CACHE_INDEX_COUNT = 29 + private var exceptionData: Toml? = null + private var lastModifiedData = 0L + fun decodeFromBuffer(buffer: ByteBuffer) : Pair { try { val info = LoginInfo.createDefault() @@ -45,7 +53,7 @@ object Login { return Pair(AuthResponse.Updated, null) } if (info.opcode != NORMAL_LOGIN_OP && info.opcode != RECONNECT_LOGIN_OP) { - SystemLogger.logInfo(this::class.java, "Invalid Login Opcode: ${info.opcode}") + log(this::class.java, Log.WARN, "Invalid Login Opcode: ${info.opcode}") return Pair(AuthResponse.InvalidLoginServer, null) } @@ -81,8 +89,11 @@ object Login { } return Pair(AuthResponse.Success, info) + } catch (buf: BufferUnderflowException) { + //some issue in either the data they sent us or how we read it, either way out of scope of this class's handling. + return Pair(AuthResponse.UnexpectedError, null) } catch (e: Exception) { - SystemLogger.logErr(this::class.java, "Exception encountered during login packet parsing! See stack trace below.") + log(this::class.java, Log.ERR, "Exception encountered during login packet parsing! See stack trace below.") e.printStackTrace() return Pair(AuthResponse.UnexpectedError, null) } @@ -119,7 +130,7 @@ object Login { fun proceedWith(session: IoSession, details: PlayerDetails, opcode: Int) { if (Repository.uid_map.contains(details.uid)) { session.write(AuthResponse.AlreadyOnline) - return; + return } details.session = session details.info.translate(UIDInfo(details.ipAddress, "DEPRECATED", "DEPRECATED", "DEPRECATED")) @@ -156,17 +167,33 @@ object Login { } catch (e: Exception) { e.printStackTrace() session.disconnect() - Repository.removePlayer(player) - player.clear(true) } } private fun checkAccountLimit(ipAddress: String, username: String): Boolean { + var accountLimit = ServerConstants.DAILY_ACCOUNT_LIMIT + + if (File(ServerConstants.CONFIG_PATH + "account_limit_exceptions.conf").exists()) { + try { + val f = File(ServerConstants.CONFIG_PATH + "account_limit_exceptions.conf") + if (f.lastModified() != lastModifiedData) { + exceptionData = Toml().read(f) + lastModifiedData = f.lastModified() + } + + if (exceptionData?.contains("exceptions.\"${ipAddress}\"") == true) { + accountLimit = exceptionData?.getLong("exceptions.\"${ipAddress}\"", 0L)?.toInt() ?: accountLimit + } + } catch (e: Exception) { + e.printStackTrace() + } + } + val archive = ServerStore.getArchive("daily-accounts") val accounts = archive.getList(ipAddress) if (username in accounts) return true - if (accounts.size >= ServerConstants.DAILY_ACCOUNT_LIMIT) + if (accounts.size >= accountLimit) return false archive.addToList(ipAddress, username) diff --git a/Server/src/main/core/net/packet/in/QuickChatPacketHandler.kt b/Server/src/main/core/net/packet/in/QuickChatPacketHandler.kt index 30e6815b7..785775288 100644 --- a/Server/src/main/core/net/packet/in/QuickChatPacketHandler.kt +++ b/Server/src/main/core/net/packet/in/QuickChatPacketHandler.kt @@ -1,11 +1,13 @@ package core.net.packet.`in` +import core.api.log import core.game.node.entity.player.Player import core.net.packet.IncomingPacket import core.net.packet.IoBuffer import core.net.packet.QCRepository import core.tools.SystemLogger import core.game.world.GameWorld +import core.tools.Log /** * Decodes the quick chat packet @@ -31,7 +33,7 @@ class QuickChatPacketHandler : IncomingPacket { 3,4 -> QCPacketType.STANDARD 5 -> QCPacketType.SINGLE 7 -> QCPacketType.DOUBLE - else -> QCPacketType.UNHANDLED.also { SystemLogger.logWarn(this::class.java, "UNHANDLED QC PACKET TYPE Size ${x.array().size}") } + else -> QCPacketType.UNHANDLED.also { log(this::class.java, Log.WARN, "UNHANDLED QC PACKET TYPE Size ${x.array().size}") } } val forClan = (buffer.get() and 0xFF) == 1 @@ -50,19 +52,20 @@ class QuickChatPacketHandler : IncomingPacket { buffer.get() //discard selection_b_index = buffer.get() } - QCPacketType.UNHANDLED -> SystemLogger.logWarn(this::class.java, "Unhandled packet type, skipping remaining buffer contents.") + QCPacketType.UNHANDLED -> log(this::class.java, Log.WARN, "Unhandled packet type, skipping remaining buffer contents.") + else -> {} } //Prints the values of each byte in the buffer to server log //If the world is in dev mode if(GameWorld.settings?.isDevMode == true) { - SystemLogger.logInfo(this::class.java, "Begin QuickChat Packet Buffer Dump---------") - SystemLogger.logInfo(this::class.java, "Packet Type: ${packetType.name} Chat Type: ${if(forClan) "Clan" else "Public"}") + log(this::class.java, Log.FINE, "Begin QuickChat Packet Buffer Dump---------") + log(this::class.java, Log.FINE, "Packet Type: ${packetType.name} Chat Type: ${if(forClan) "Clan" else "Public"}") x?.array()?.forEach { - SystemLogger.logInfo(this::class.java, "$it") + log(this::class.java, Log.FINE, "$it") } - SystemLogger.logInfo(this::class.java, "End QuickChat Packet Buffer Dump-----------") + log(this::class.java, Log.FINE, "End QuickChat Packet Buffer Dump-----------") } QCRepository.sendQC(player,multiplier,offset,packetType,selection_a_index,selection_b_index,forClan) diff --git a/Server/src/main/core/net/packet/in/RunScript.kt b/Server/src/main/core/net/packet/in/RunScript.kt index 48b88504b..b3f900714 100644 --- a/Server/src/main/core/net/packet/in/RunScript.kt +++ b/Server/src/main/core/net/packet/in/RunScript.kt @@ -1,6 +1,6 @@ package core.net.packet.`in` -import core.api.sendDialogue +import core.api.* import core.game.node.entity.player.Player /** @@ -12,6 +12,8 @@ object RunScript { fun processInput(player: Player, value: Any, script: ((Any) -> Boolean)) { if (value is Int && value <= 0) return + + val type = player.getAttribute("input-type", InputType.NUMERIC) var input = value @@ -26,10 +28,18 @@ object RunScript { input = input.replace("k", "000").replace("m", "000000") } + if (type == InputType.NUMERIC || type == InputType.AMOUNT) + input = input.toString().toIntOrNull() ?: input + try { script(input) } catch (_: NumberFormatException) { sendDialogue(player, "That number's a bit large, don't you think?") + } catch (_: ClassCastException) { + sendDialogue(player, "Something went wrong here. Try again.") + } finally { + removeAttribute(player, "runscript") + removeAttribute(player, "input-type") } } } diff --git a/Server/src/main/core/net/packet/out/AudioPacket.java b/Server/src/main/core/net/packet/out/AudioPacket.java index e2520910a..beb04da91 100644 --- a/Server/src/main/core/net/packet/out/AudioPacket.java +++ b/Server/src/main/core/net/packet/out/AudioPacket.java @@ -15,15 +15,15 @@ public class AudioPacket implements OutgoingPacket { public static IoBuffer write(IoBuffer buffer, Audio audio, Location loc) { if(loc == null) { buffer.put((byte) 172); - buffer.putShort(audio.getId()); - buffer.put((byte) audio.getVolume()); - buffer.putShort(audio.getDelay()); + buffer.putShort(audio.id); + buffer.put((byte) audio.loops); + buffer.putShort(audio.delay); } else { buffer.put((byte) 97); buffer.put((byte) (loc.getChunkOffsetX() << 4 | loc.getChunkOffsetY())); - buffer.putShort(audio.getId()); - buffer.put((byte) (audio.getRadius() << 4 | audio.getVolume() & 7)); - buffer.put((byte) audio.getDelay()); + buffer.putShort(audio.id); + buffer.put((byte) (audio.radius << 4 | audio.loops & 7)); + buffer.put((byte) audio.delay); } return buffer; } diff --git a/Server/src/main/core/net/packet/out/CommunicationMessage.java b/Server/src/main/core/net/packet/out/CommunicationMessage.java index 0af702b1c..5cd5e592d 100644 --- a/Server/src/main/core/net/packet/out/CommunicationMessage.java +++ b/Server/src/main/core/net/packet/out/CommunicationMessage.java @@ -41,7 +41,6 @@ public final class CommunicationMessage 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/InterfaceSetAngle.java b/Server/src/main/core/net/packet/out/InterfaceSetAngle.java new file mode 100644 index 000000000..835419cf4 --- /dev/null +++ b/Server/src/main/core/net/packet/out/InterfaceSetAngle.java @@ -0,0 +1,26 @@ +package core.net.packet.out; + +import core.game.node.entity.player.Player; +import core.net.packet.context.DefaultContext; +import core.net.packet.IoBuffer; +import core.net.packet.OutgoingPacket; + +public final class InterfaceSetAngle implements OutgoingPacket { + @Override + public void send(DefaultContext context) { + Player player = context.getPlayer(); + Object[] objects = context.getObjects(); + int pitch = (Integer)objects[0]; + int scale = (Integer)objects[1]; + int yaw = (Integer)objects[2]; + int interfaceId = (Integer)objects[3]; + int childId = (Integer)objects[4]; + IoBuffer buffer = new IoBuffer(132); + buffer.putShort(pitch); + buffer.putShortA(player.getInterfaceManager().getPacketCount(1)); + buffer.putLEShortA(scale); + buffer.putLEShortA(yaw); + buffer.putInt(interfaceId << 16 | childId); + buffer.cypherOpcode(context.getPlayer().getSession().getIsaacPair().getOutput());context.getPlayer().getSession().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 2ba0c864a..a93435b18 100644 --- a/Server/src/main/core/net/packet/out/UpdateSceneGraph.java +++ b/Server/src/main/core/net/packet/out/UpdateSceneGraph.java @@ -23,8 +23,8 @@ public final class UpdateSceneGraph implements OutgoingPacket for (int regionX = (player.getLocation().getRegionX() - 6) / 8; regionX <= ((player.getLocation().getRegionX() + 6) / 8); regionX++) { 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 < keys.length; i++) { - buffer.putIntB(keys[i]); + for (int i = 0; i < 4; i++) { + buffer.putIntB(keys[i]); } } } diff --git a/Server/src/main/core/net/registry/AccountRegister.java b/Server/src/main/core/net/registry/AccountRegister.java index 349613841..dff9f3fc0 100644 --- a/Server/src/main/core/net/registry/AccountRegister.java +++ b/Server/src/main/core/net/registry/AccountRegister.java @@ -6,6 +6,7 @@ import core.net.Constants; import core.net.IoSession; import core.ServerConstants; import core.auth.UserAccountInfo; +import core.tools.Log; import core.tools.SystemLogger; import core.game.world.GameWorld; import core.net.packet.in.Login; @@ -14,6 +15,8 @@ import java.nio.ByteBuffer; import java.util.regex.Matcher; import java.util.regex.Pattern; +import static core.api.ContentAPIKt.log; + /** * Handles the registry of new accounts. * @author Vexia @@ -50,11 +53,11 @@ public class AccountRegister { break; } if (invalidUsername(username)) { - System.out.println("AHAHHA " + username); + response(session,RegistryResponse.INVALID_USERNAME); break; } - System.out.println(username); + if (!GameWorld.getAuthenticator().canCreateAccountWith(info)) { response(session, RegistryResponse.NOT_AVAILBLE_USER); return; @@ -62,11 +65,10 @@ public class AccountRegister { response(session, RegistryResponse.SUCCESS); break; case 36://Register details - SystemLogger.logInfo(AccountRegister.class, "Made it to final stage"); buffer.get(); //Useless size being written that is already written in the RSA block buffer = Login.decryptRSABuffer(buffer, ServerConstants.EXPONENT, ServerConstants.MODULUS); if(buffer.get() != 10){ //RSA header (aka did this decrypt properly) - SystemLogger.logInfo(AccountRegister.class, "Decryption failed during registration :("); + log(AccountRegister.class, Log.ERR, "Decryption failed during registration :("); response(session, RegistryResponse.CANNOT_CREATE); break; } @@ -115,7 +117,7 @@ public class AccountRegister { }); break; default: - SystemLogger.logErr(AccountRegister.class, "Unhandled account registry opcode = " + opcode); + log(AccountRegister.class, Log.ERR, "Unhandled account registry opcode = " + opcode); break; } } diff --git a/Server/src/main/core/plugin/ClassScanner.kt b/Server/src/main/core/plugin/ClassScanner.kt index 92bf3b8de..2ad6e5f4b 100644 --- a/Server/src/main/core/plugin/ClassScanner.kt +++ b/Server/src/main/core/plugin/ClassScanner.kt @@ -1,26 +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.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 /** @@ -63,18 +69,34 @@ object ClassScanner { loadContentInterfacesFrom(scanResults) logStartup("Loaded $amountLoaded content interfaces.") } catch (t: Throwable) { - SystemLogger.logErr(this::class.java, "Error initializing Plugins -> " + t.localizedMessage + " for file -> " + lastLoaded) + log(this::class.java, Log.ERR, "Error initializing Plugins -> " + t.localizedMessage + " for file -> " + lastLoaded) t.printStackTrace() } catch (e: Exception) { - SystemLogger.logErr(this::class.java, "Error initializing Plugins -> " + e.localizedMessage + " for file -> " + lastLoaded) + log(this::class.java, Log.ERR, "Error initializing Plugins -> " + e.localizedMessage + " for file -> " + lastLoaded) e.printStackTrace() } } + fun loadTimers () { + scanResults.getSubclasses ("core.game.system.timer.RSTimer").filter { !it.isAbstract }.forEach { + try { + val clazz = it.loadClass().newInstance() as RSTimer + TimerRegistry.registerTimer (clazz) + } catch (e: Exception) { + log(this::class.java, Log.ERR, "Error registering timer instance: ${it.simpleName}") + e.printStackTrace() + } + } + } + private fun loadContentInterfacesFrom(scanResults: ScanResult) { 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) @@ -84,6 +106,7 @@ object ClassScanner { if(clazz is InteractionListener) clazz.defineListeners().also { clazz.defineDestinationOverrides() } if(clazz is InterfaceListener) clazz.defineInterfaceListeners() if(clazz is Commands) clazz.defineCommands() + if(clazz is NPCBehavior) NPCBehavior.register(clazz.ids, clazz) if(clazz is PersistPlayer) { PlayerSaver.contentHooks.add(clazz) PlayerSaveParser.contentHooks.add(clazz) @@ -108,12 +131,12 @@ object ClassScanner { } for(border in clazz.defineAreaBorders()) zone.register(border) ZoneBuilder.configure(zone) - SystemLogger.logInfo(this::class.java, "Configured zone: ${clazz.javaClass.simpleName + "MapArea"}") + log(this::class.java, Log.FINE, "Configured zone: ${clazz.javaClass.simpleName + "MapArea"}") MapArea.zoneMaps[clazz.javaClass.simpleName + "MapArea"] = zone } amountLoaded++ } catch (e: Exception) { - SystemLogger.logErr(this::class.java, "Error loading content: ${it.simpleName}, ${e.localizedMessage}") + log(this::class.java, Log.ERR, "Error loading content: ${it.simpleName}, ${e.localizedMessage}") e.printStackTrace() } } @@ -134,10 +157,10 @@ object ClassScanner { definePlugin(clazz) } } catch (t: Throwable) { - SystemLogger.logErr(this::class.java, "Failed to load plugin ${p.name}."); + log(this::class.java, Log.ERR, "Failed to load plugin ${p.name}.") if (t is NoSuchMethodException && p.superclass.simpleName == core.game.dialogue.DialoguePlugin::class.simpleName) { - SystemLogger.logErr(this::class.java, + log(this::class.java, Log.ERR, "Make sure the constructor signature matches " + "`${p.simpleName}(player: Player? = null) : DialoguePlugin(player)'." ) @@ -194,7 +217,7 @@ object ClassScanner { plugin.newInstance(null) QuestRepository.register(plugin as Quest) } - else -> SystemLogger.logWarn(this::class.java, "Unknown Manifest: " + manifest.type) + else -> log(this::class.java, Log.WARN, "Unknown Manifest: " + manifest.type) } } numPlugins++ diff --git a/Server/src/main/core/storage/InMemoryStorageProvider.kt b/Server/src/main/core/storage/InMemoryStorageProvider.kt index 5337afe35..d0780cea0 100644 --- a/Server/src/main/core/storage/InMemoryStorageProvider.kt +++ b/Server/src/main/core/storage/InMemoryStorageProvider.kt @@ -10,10 +10,11 @@ class InMemoryStorageProvider : AccountStorageProvider { } override fun getAccountInfo(username: String): UserAccountInfo { - return storage[username] ?: UserAccountInfo.createDefault() + return storage[username] ?: UserAccountInfo.createDefault().also { it.uid = username.hashCode(); storage[username] = it } } override fun store(info: UserAccountInfo) { + info.uid = info.username.hashCode() storage[info.username] = info } diff --git a/Server/src/main/core/tools/Globals.kt b/Server/src/main/core/tools/Globals.kt index ed2f85f40..cb070f762 100644 --- a/Server/src/main/core/tools/Globals.kt +++ b/Server/src/main/core/tools/Globals.kt @@ -7,14 +7,18 @@ const val GREEN = "" const val BLUE = "" const val PURPLE = "" -fun colorize(line: String): String{ - val new = line.replace("%R", RED) - .replace("%O", ORANGE) - .replace("%Y", YELLOW) - .replace("%G", GREEN) - .replace("%B", BLUE) - .replace("%P", PURPLE).append("") + " " - return new +private val pattern = Regex("%[0-9a-fA-F]{6}") +private val testData = arrayOf("This is a string with no colors.", "This %R is a string with one color.", "This %R %G %B is a string with multiple colors.", "This %ffffff is an arbitrary hex string.") + +fun colorize(line: String): String { + return line.replace("%R", RED) + .replace("%O", ORANGE) + .replace("%Y", YELLOW) + .replace("%G", GREEN) + .replace("%B", BLUE) + .replace("%P", PURPLE) + .replace(pattern) { matchResult -> "" } + .append("") + " " } fun colorize(line: String, hexColor: String): String{ 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 a162c25e6..61d3275ad 100644 --- a/Server/src/main/core/tools/RandomFunction.java +++ b/Server/src/main/core/tools/RandomFunction.java @@ -49,8 +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){ - return random(chance + 1) == chance / 2; + public static boolean roll(int chance) { + if (chance <= 1) return true; + return random(chance) == 1; } /** @@ -269,7 +270,7 @@ public class RandomFunction { rand -= item.weight; } //We should get here if and only if the weighted chance table is empty. - //System.out.println("ERROR ROLLING WEIGHTED CHANCE: WEIGHT SUM AND INDIVIDUAL WEIGHTS DO NOT MATCH!!"); + return null; } diff --git a/Server/src/main/core/tools/StringUtils.java b/Server/src/main/core/tools/StringUtils.java index b0a941986..84ac0e3e0 100644 --- a/Server/src/main/core/tools/StringUtils.java +++ b/Server/src/main/core/tools/StringUtils.java @@ -409,6 +409,7 @@ public final class StringUtils { * @return The string. */ public static String longToString(long l) { + try { int i = 0; char ac[] = new char[32]; while (l != 0L) { @@ -417,6 +418,9 @@ public final class StringUtils { ac[11 - i++] = VALID_CHARS[(int) (l1 - l * 37L)]; } return new String(ac, 12 - i, i); + } catch (ArrayIndexOutOfBoundsException e) { + return ""; + } } /** diff --git a/Server/src/main/core/tools/SystemLogger.kt b/Server/src/main/core/tools/SystemLogger.kt index 48334133f..dc5b96560 100644 --- a/Server/src/main/core/tools/SystemLogger.kt +++ b/Server/src/main/core/tools/SystemLogger.kt @@ -2,11 +2,12 @@ package core.tools import com.github.ajalt.mordant.rendering.TextColors import com.github.ajalt.mordant.terminal.* +import com.google.protobuf.ByteString.Output import core.ServerConstants -import java.io.BufferedWriter -import java.io.File -import java.io.FileWriter -import java.io.Writer +import core.game.world.GameWorld +import core.api.* +import java.io.OutputStream +import java.io.PrintStream import java.text.SimpleDateFormat import java.util.* @@ -17,105 +18,84 @@ import java.util.* */ object SystemLogger { val t = Terminal() - val formatter = SimpleDateFormat("HH:mm:ss") - var tradeLog: Writer? = null - var tradeLogWriter: BufferedWriter? = null + val errT = t.forStdErr() + val formatter = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXX") - @JvmStatic - fun initTradeLogger(){ - if(!File(ServerConstants.LOGS_PATH + "trade" + File.separator + "test").exists()){ - File(ServerConstants.LOGS_PATH + "trade" + File.separator + "test").mkdirs() - } - tradeLog = FileWriter(ServerConstants.LOGS_PATH + "trade" + File.separator + SimpleDateFormat("dd-MM-yyyy").format(System.currentTimeMillis()) + ".log") - tradeLogWriter = BufferedWriter(tradeLog!!) - } - - @JvmStatic() - fun flushLogs() { - try { - tradeLogWriter?.flush() - tradeLogWriter?.close() - } catch(ignored: Exception) {} - } - - fun getTime(): String{ + private fun getTime(): String{ return "[" + formatter.format(Date(System.currentTimeMillis())) +"]" } @JvmStatic - fun logInfo(clazz: Class<*>, vararg messages: String){ - for(m in messages){ - val msg = "${getTime()}: [${clazz.simpleName}] $m" - if(m.isNotBlank()) { + fun processLogEntry(clazz: Class<*>, log: Log, message: String) { + when (log) { + Log.DEBUG -> { + if (GameWorld.settings?.isDevMode != true) + return + val msg = TextColors.cyan("${getTime()}: [${clazz.simpleName}] $message") t.println(msg) } - } - } - @JvmStatic - fun logErr(clazz: Class<*>, message: String){ - val msg = "${getTime()}: [${clazz.simpleName}] $message" - if(message.isNotBlank()) { - t.println(msg) - } - } + Log.FINE -> { + if (ServerConstants.LOG_LEVEL < LogLevel.VERBOSE) + return + val msg = TextColors.gray("${getTime()}: [${clazz.simpleName}] $message") + t.println(msg) + } - @JvmStatic - fun logWarn(clazz: Class<*>, message: String){ - val msg = "${getTime()}: [${clazz.simpleName}] $message" - if(message.isNotBlank()) { - t.println(msg) - } - } + Log.INFO -> { + if (ServerConstants.LOG_LEVEL < LogLevel.DETAILED) + return - @JvmStatic - fun logAlert(clazz: Class<*>, message: String){ - val msg = "${getTime()}: [${clazz.simpleName}] $message" - if(message.isNotBlank()) { - t.println(msg) - } - } + val msg = "${getTime()}: [${clazz.simpleName}] $message" + t.println(msg) + } - @JvmStatic - fun logAI(clazz: Class<*>, message: String){ - val msg = "${getTime()}: [${clazz.simpleName}] $message" - if(message.isNotBlank()) { - t.println(msg) - } - } + Log.WARN -> { + if (ServerConstants.LOG_LEVEL < LogLevel.CAUTIOUS) + return - @JvmStatic - fun logRE(message: String){ - if(message.isNotBlank()) t.println("${getTime()}: ${TextColors.gray("[RAND] $message")}") + val msg = TextColors.yellow("${getTime()}: [${clazz.simpleName}] $message") + t.println(msg) + } + + Log.ERR -> { + val msg = "${getTime()}: [${clazz.simpleName}] $message" + errT.println(msg) + } + } } @JvmStatic fun logGE(message: String){ - if(message.isNotBlank()) t.println("${getTime()}: ${TextColors.gray("[ GE] $message")}") - } - - @JvmStatic - fun logTrade(message: String){ - try { - if (message.isNotBlank()) { - if (tradeLogWriter == null) logWarn(this::class.java, "Trade Logger is null!") - tradeLogWriter?.write("${getTime()}: $message") - tradeLogWriter?.newLine() - } - } catch(ignored: Exception){} + log(this::class.java, Log.FINE, "[ GE] $message") } @JvmStatic fun logStartup(message: String) { - logInfo(this::class.java, "[STARTUP] $message") + log(this::class.java, Log.FINE, "[STARTUP] $message") } @JvmStatic fun logShutdown(message: String) { - logInfo(this::class.java,"[SHUTDOWN] $message") + log(this::class.java, Log.FINE, "[SHUTDOWN] $message") } fun logMS(s: String) { - if(s.isNotBlank()) t.println("${getTime()}: ${TextColors.gray("[ MS] $s")}") + log(this::class.java, Log.FINE, "[ MS] $s") } -} \ No newline at end of file +} + +enum class LogLevel { + SILENT, + CAUTIOUS, + DETAILED, + VERBOSE +} + +enum class Log { + FINE, + INFO, + WARN, + ERR, + DEBUG +} diff --git a/Server/src/main/core/tools/TickUtils.kt b/Server/src/main/core/tools/TickUtils.kt index 3d449d4ab..f7e26e5ac 100644 --- a/Server/src/main/core/tools/TickUtils.kt +++ b/Server/src/main/core/tools/TickUtils.kt @@ -2,6 +2,7 @@ package core.tools const val tick = 600 //ms const val second = 1000 //ms +const val cycle = 20 //ms fun secondsToTicks(seconds: Int): Int { val seconds = seconds * second //seconds -> ms @@ -11,4 +12,26 @@ fun secondsToTicks(seconds: Int): Int { fun ticksToSeconds(ticks: Int): Int { val ticksMs = ticks * tick return ticksMs / 1000 -} \ No newline at end of file +} + +fun cyclesToTicks (cycles: Int) : Int { + val cyclesPerTick = tick / cycle + return kotlin.math.ceil (cycles / cyclesPerTick.toDouble()).toInt() +} + +fun ticksToCycles (ticks: Int) : Int { + return ticks * (tick / cycle) +} + +fun minutesToTicks(minutes: Int): Int { + val minutesMs = minutes * 60 * 1000 + return minutesMs / tick +} + +fun ticksToMinutes(ticks: Int): Int { + val ticksMs = ticks * tick + return ticksMs / 1000 / 60 +} + +const val ticksPerSecond = second / tick +const val ticksPerMinute = 60 * ticksPerSecond diff --git a/Server/src/main/core/tools/TimeStamp.java b/Server/src/main/core/tools/TimeStamp.java index 13934b1fc..f0762e846 100644 --- a/Server/src/main/core/tools/TimeStamp.java +++ b/Server/src/main/core/tools/TimeStamp.java @@ -51,7 +51,7 @@ public final class TimeStamp { long current = System.currentTimeMillis(); long difference = current - interval; if (debug || difference > 100) { - System.out.println("Interval " + info + " - time elapsed=" + difference + "ms."); + } interval = current; return difference; @@ -65,7 +65,7 @@ public final class TimeStamp { long current = System.currentTimeMillis(); long difference = current - start; if (debug) { - System.out.println("Interval " + info + " - time elapsed=" + difference + "ms."); + } return difference; } diff --git a/Server/src/main/core/tools/mysql/Results.java b/Server/src/main/core/tools/mysql/Results.java index 6efc2333b..b803566e6 100644 --- a/Server/src/main/core/tools/mysql/Results.java +++ b/Server/src/main/core/tools/mysql/Results.java @@ -22,7 +22,7 @@ public class Results { return result; } catch (Exception e) { - System.out.println("Column '" + column + "' not found."); + } return null; @@ -38,7 +38,7 @@ public class Results { return result; } catch (Exception e) { - System.out.println("Column '" + column + "' not found."); + } return -1; @@ -60,7 +60,7 @@ public class Results { return columns; } catch (Exception e) { - System.out.println("Unable to gather columns."); + } return null; @@ -72,7 +72,7 @@ public class Results { try { return !set().next(); } catch (Exception e) { - System.out.println("Error occurred, while checking for results."); + } return true; diff --git a/Server/src/main/core/worker/MajorUpdateWorker.kt b/Server/src/main/core/worker/MajorUpdateWorker.kt index 104ab5a50..29d48faa8 100644 --- a/Server/src/main/core/worker/MajorUpdateWorker.kt +++ b/Server/src/main/core/worker/MajorUpdateWorker.kt @@ -1,16 +1,20 @@ 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.tools.SystemLogger +import core.api.log +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 @@ -31,16 +35,25 @@ class MajorUpdateWorker { started = true Thread.sleep(600L) while (running) { + 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) { - player?.details?.session?.disconnect() player?.session?.lastPing = Long.MAX_VALUE - player?.clear(true) + player?.session?.disconnect() + } + if (!player.isActive && !Repository.disconnectionQueue.contains(player.name) && player.getAttribute("logged-in-fully", false)) { + //if player has somehow been set as inactive without being queued for disconnection, do that now. This is a failsafe, and should not be relied on. + //if you made a change, and now this is suddenly getting triggered a lot, your change is probably bad. + player?.session?.disconnect() + log(MajorUpdateWorker::class.java, Log.WARN, "Manually disconnecting ${player.name} because they were set as inactive without being disconnected. This is bad.") } } @@ -53,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) } @@ -70,35 +89,53 @@ class MajorUpdateWorker { } val end = System.currentTimeMillis() + Grafana.totalTickTime = (end - start).toInt() + Grafana.endTick() /* ServerMonitor.eventQueue.add(GuiEvent.UpdateTickTime(end - start)) ServerMonitor.eventQueue.add(GuiEvent.UpdatePulseCount(GameWorld.Pulser.TASKS.size))*/ Thread.sleep(max(600 - (end - start), 0)) } - SystemLogger.logInfo(this::class.java, "Update worker stopped.") + 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) { - PacketProcessor.processQueue() - - if (!skipPulseUpdate) { - GameWorld.Pulser.updateAll() - } - try { + var packetStart = System.currentTimeMillis() + PacketProcessor.processQueue() + Grafana.packetProcessTime = (System.currentTimeMillis() - packetStart).toInt() + + //disconnect all players waiting to be disconnected + Repository.disconnectionQueue.update() + + if (!skipPulseUpdate) { + GameWorld.Pulser.updateAll() + } + GameWorld.tickListeners.forEach { it.tick() } + sequence.start() sequence.run() sequence.end() + + //increment global ticks variable + GameWorld.pulse() + //tick all manager plugins + Managers.tick() } catch (e: Exception) { e.printStackTrace() + } finally { + try { + PacketWriteQueue.flush() + } catch (e: Exception) { + e.printStackTrace() + } } - //increment global ticks variable - GameWorld.pulse() - //disconnect all players waiting to be disconnected - Repository.disconnectionQueue.update() - GameWorld.tickListeners.forEach { it.tick() } - //tick all manager plugins - Managers.tick() } fun start() { @@ -112,4 +149,4 @@ class MajorUpdateWorker { running = false worker.interrupt() } -} \ No newline at end of file +} diff --git a/Server/src/main/core/worker/ManagementEvents.kt b/Server/src/main/core/worker/ManagementEvents.kt index 61922c708..5207f079c 100644 --- a/Server/src/main/core/worker/ManagementEvents.kt +++ b/Server/src/main/core/worker/ManagementEvents.kt @@ -270,13 +270,12 @@ object ManagementEvents { initializeClanWith(info) } else { SystemLogger.logMS("Creating default server clan") - if (GameWorld.settings!!.enable_default_clan && event.clanOwner == ServerConstants.SERVER_NAME) { + if (GameWorld.settings!!.enable_default_clan && event.clanOwner == ServerConstants.SERVER_NAME.toLowerCase()) { //Create a user with the default clan and some basic settings and stick them in the account storage if (info == UserAccountInfo.createDefault()) { - info.username = ServerConstants.SERVER_NAME + info.username = ServerConstants.SERVER_NAME.toLowerCase() info.password = ServerConstants.MS_SECRET_KEY info.rights = 2 - SystemLogger.logAlert(this::class.java, "Creating default server account: ${info.username}, password is your MS_SECRET_KEY!") GameWorld.authenticator.createAccountWith(info) info = GameWorld.accountStorage.getAccountInfo(event.clanOwner) } @@ -367,4 +366,4 @@ object ManagementEvents { private fun shouldWaitForClanInfo(clanName: String): Boolean { return ClanRepository.get(clanName) == null && hasRequestedClanInfo[clanName] == null } -} \ No newline at end of file +} diff --git a/Server/src/test/kotlin/APITests.kt b/Server/src/test/kotlin/APITests.kt index 6080c7f77..e76e85852 100644 --- a/Server/src/test/kotlin/APITests.kt +++ b/Server/src/test/kotlin/APITests.kt @@ -1,16 +1,27 @@ +import content.data.consumables.Consumables import core.api.IfaceSettingsBuilder import core.api.splitLines import content.global.skill.slayer.Master import content.global.skill.slayer.SlayerManager import content.global.skill.slayer.Tasks +import core.game.node.item.Item +import core.api.utils.Vector +import core.game.world.map.Direction import org.json.simple.JSONObject import org.json.simple.parser.JSONParser import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Test +import org.rs09.consts.Items class APITests { - val testPlayer = TestUtils.getMockPlayer("test") - val testPlayer2 = TestUtils.getMockPlayer("test2") + var testPlayer: MockPlayer + var testPlayer2: MockPlayer + + init { + TestUtils.preTestSetup() + testPlayer = TestUtils.getMockPlayer("test") + testPlayer2 = TestUtils.getMockPlayer("test2") + } @Test fun testIfaceSettings(){ var builder = IfaceSettingsBuilder() @@ -172,4 +183,191 @@ class APITests { Assertions.assertEquals(expectedLine2, lines.getOrNull(1) ?: "") Assertions.assertEquals(expectedLine3, lines.getOrNull(2) ?: "") } + + @Test fun consumableStackableItemShouldNotRemoveStack() { + val stackableItem = Item(Items.PURPLE_SWEETS_10476, 999) + TestUtils.getMockPlayer("Inventory Consumable Stack Slot Tester").use { player -> + // Inventory setup + player.inventory.clear() + player.inventory.add(stackableItem, false, 0) + + // Setup + val consumable = Consumables.getConsumableById(stackableItem.id) + consumable.consumable.consume(player.inventory.get(0), player) + TestUtils.advanceTicks(2, false) + + // Get item in that slot, + val updatedConsumable = player.inventory.get(0) + + // Maintains slot clicked + Amount is decremented + Assertions.assertEquals(0, updatedConsumable.slot) + Assertions.assertEquals(998, updatedConsumable.amount) + } + } + + @Test fun consumableMultiPieceItemShouldBeRemovedFromCorrectSlot() { + val consumables: Array = arrayOf( + Item(Items.CAKE_1891, 8), + Item(Items.TWO_THIRDS_CAKE_1893, 8), + Item(Items.SLICE_OF_CAKE_1895, 8) + ) + + TestUtils.getMockPlayer("Inventory Consumable Multi Piece Tester").use { player -> + // Inventory setup + player.inventory.clear() + player.inventory.add(*consumables) + + val lastWholeCakeContainerIndex = 7 + val lastWholeCake = player.inventory.get(lastWholeCakeContainerIndex) + + val consumable = Consumables.getConsumableById(lastWholeCake.id) + consumable.consumable.consume(player.inventory.get(lastWholeCakeContainerIndex), player) + TestUtils.advanceTicks(2, false) + + // Cake amounts are correct + val wholeCakeAmount = player.inventory.getAmount(Items.CAKE_1891) + val twoThirdsCakeAmount = player.inventory.getAmount(Items.TWO_THIRDS_CAKE_1893) + Assertions.assertEquals(7, wholeCakeAmount) + Assertions.assertEquals(9, twoThirdsCakeAmount) + + // Cake was replaced in correct spot + val inventorySlot0 = player.inventory.get(0) + val inventorySlot7 = player.inventory.get(7) + Assertions.assertEquals(Items.CAKE_1891, inventorySlot0.id) + Assertions.assertEquals(Items.TWO_THIRDS_CAKE_1893, inventorySlot7.id) + } + } + + @Test fun consumableMultiPieceItemShouldAddReturnItemToCorrectSlot() { + val PIE_DISH_NONCONSUMABLE_2313 = Items.PIE_DISH_2313 + val consumables: Array = arrayOf( + Item(Items.APPLE_PIE_2323, 8), + Item(Items.HALF_AN_APPLE_PIE_2335, 8), + Item(Items.REDBERRY_PIE_2325, 8) + ) + + TestUtils.getMockPlayer("Inventory Consumable Multi Piece With Return Tester").use { player -> + // Inventory setup + player.inventory.clear() + player.inventory.add(*consumables) + + val lastWholePieContainerIndex = 7 + val lastWholePie = player.inventory.get(lastWholePieContainerIndex) + + val wholePieConsumable = Consumables.getConsumableById(lastWholePie.id) + wholePieConsumable.consumable.consume(player.inventory.get(lastWholePieContainerIndex), player) + TestUtils.advanceTicks(2, false) + + // Pie amounts are correct + var wholePieAmount = player.inventory.getAmount(Items.APPLE_PIE_2323) + var halfPieAmount = player.inventory.getAmount(Items.HALF_AN_APPLE_PIE_2335) + Assertions.assertEquals(7, wholePieAmount) + Assertions.assertEquals(9, halfPieAmount) + + // Pie was replaced in correct spot + val inventorySlot0 = player.inventory.get(0) + val inventorySlot7 = player.inventory.get(7) + Assertions.assertEquals(Items.APPLE_PIE_2323, inventorySlot0.id) + Assertions.assertEquals(Items.HALF_AN_APPLE_PIE_2335, inventorySlot7.id) + + // Tests for pie halves + pie tins + val firstHalfPieContainerIndex = 7 + val firstHalfPie = player.inventory.get(firstHalfPieContainerIndex) + val halfPieConsumable = Consumables.getConsumableById(firstHalfPie.id) + halfPieConsumable.consumable.consume(player.inventory.get(firstHalfPieContainerIndex), player) + TestUtils.advanceTicks(2, false) + + // Pie amounts are correct + halfPieAmount = player.inventory.getAmount(Items.HALF_AN_APPLE_PIE_2335) + val pieDishAmount = player.inventory.getAmount(PIE_DISH_NONCONSUMABLE_2313) + Assertions.assertEquals(8, halfPieAmount) + Assertions.assertEquals(1, pieDishAmount) + + val updatedSlot7 = player.inventory.get(7) + Assertions.assertEquals(PIE_DISH_NONCONSUMABLE_2313, updatedSlot7.id) + } + } + + @Test fun consumableItemShouldNotHaveReturnItem() { + val consumables: Array = arrayOf( + Item(Items.TROUT_333, 8), + Item(Items.SHARK_385, 8), + Item(Items.LOBSTER_379, 8) + ) + TestUtils.getMockPlayer("Inventory Consumable No Return Item Tester").use { player -> + // Inventory setup + player.inventory.clear() + player.inventory.add(*consumables) + + // Feed the player copious amounts of fish + val lastTroutContainerIndex = 7 + val lastTrout = player.inventory.get(lastTroutContainerIndex) + + val troutConsumable = Consumables.getConsumableById(lastTrout.id) + troutConsumable.consumable.consume(player.inventory.get(lastTroutContainerIndex), player) + TestUtils.advanceTicks(4, false) + + val sharkConsumable = Consumables.getConsumableById(Items.SHARK_385) + for (n in 0..7) { + sharkConsumable.consumable.consume(player.inventory.get(n + 8), player) + TestUtils.advanceTicks(4, false) + } + + val lobsterConsumable = Consumables.getConsumableById(Items.LOBSTER_379) + for (n in 16..23 step 2) { + lobsterConsumable.consumable.consume(player.inventory.get(n), player) + TestUtils.advanceTicks(4, false) + } + + // Trout amounts are correct + val troutAmount = player.inventory.getAmount(Items.TROUT_333) + Assertions.assertEquals(7, troutAmount) + + // Trout was removed from the correct spot + val inventorySlot0 = player.inventory.get(0) + val inventorySlot7: Item? = player.inventory.get(7) + Assertions.assertEquals(Items.TROUT_333, inventorySlot0.id) + Assertions.assertNull(inventorySlot7) + + val sharkAmount = player.inventory.getAmount(Items.SHARK_385) + Assertions.assertEquals(0, sharkAmount) + for (n in 8..15) { + val inventoryItem: Item? = player.inventory.get(n) + Assertions.assertNull(inventoryItem) + } + + val lobsterAmount = player.inventory.getAmount(Items.LOBSTER_379) + Assertions.assertEquals(4, lobsterAmount) + + for (n in 16..23) { + if (n % 2 == 0) { + val inventoryItem: Item? = player.inventory.get(n) + Assertions.assertNull(inventoryItem) + } else { + val inventoryItem: Item = player.inventory.get(n) + Assertions.assertEquals(Items.LOBSTER_379, inventoryItem.id) + } + } + } + } + + @Test fun vectorToDirectionShouldReturnExpectedDirections() { + val testData = arrayOf( + Pair(Vector(1.0, 0.0), Direction.EAST), + Pair(Vector(-1.0, 0.0), Direction.WEST), + Pair(Vector(0.0, 1.0), Direction.NORTH), + Pair(Vector(0.0, -1.0), Direction.SOUTH), + Pair(Vector(1.0, 1.0), Direction.NORTH_EAST), + Pair(Vector(1.0, -1.0), Direction.SOUTH_EAST), + Pair(Vector(-1.0, 1.0), Direction.NORTH_WEST), + Pair(Vector(-1.0, -1.0), Direction.SOUTH_WEST), + Pair(Vector(15.0, 0.0), Direction.EAST), + Pair(Vector(15.0, 1.0), Direction.EAST), + Pair(Vector(-15.0, -9.7), Direction.SOUTH_WEST) + ) + + for ((vec, expDir) in testData) { + Assertions.assertEquals(expDir, vec.toDirection(), "Vector: $vec") + } + } } \ No newline at end of file diff --git a/Server/src/test/kotlin/ExchangeTests.kt b/Server/src/test/kotlin/ExchangeTests.kt index 9c5cabfd8..baac462fd 100644 --- a/Server/src/test/kotlin/ExchangeTests.kt +++ b/Server/src/test/kotlin/ExchangeTests.kt @@ -1,3 +1,4 @@ +import content.global.handlers.iface.ge.StockMarket import core.game.ge.OfferState import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch @@ -11,6 +12,8 @@ import core.game.ge.GEDB import core.game.ge.GrandExchange import core.game.ge.GrandExchangeOffer import core.game.ge.PriceIndex +import core.game.node.item.Item +import org.rs09.consts.Items import java.io.File @TestInstance(TestInstance.Lifecycle.PER_CLASS) class ExchangeTests { @@ -21,10 +24,10 @@ import java.io.File GEDB.init(TEST_DB_PATH) } - fun generateOffer(itemId: Int, amount: Int, price: Int, sale: Boolean, username: String = "test ${System.currentTimeMillis()}") : GrandExchangeOffer { + fun generateOffer(itemId: Int, amount: Int, price: Int, sale: Boolean, username: String = "test ${System.currentTimeMillis()}", offerState: OfferState = OfferState.REGISTERED) : GrandExchangeOffer { val offer = GrandExchangeOffer() val uid = username.hashCode() // normally this would be the account's uid but in the test we don't have an account - offer.offerState = OfferState.REGISTERED + offer.offerState = offerState offer.itemID = itemId offer.offeredValue = price offer.amount = amount @@ -37,7 +40,23 @@ import java.io.File return offer } - @AfterAll fun cleanup() { + fun generateUnsentOffer (itemId: Int, amount: Int, price: Int, sale: Boolean, username: String, offerState: OfferState = OfferState.PENDING) : GrandExchangeOffer { + val offer = GrandExchangeOffer() + val uid = username.hashCode() // normally this would be the account's uid but in the test we don't have an account + offer.offerState = offerState + offer.itemID = itemId + offer.offeredValue = price + offer.amount = amount + offer.timeStamp = System.currentTimeMillis() + offer.index = 0 + offer.isBot = false + offer.playerUID = uid + offer.sell = sale + offer.uid = 0L + return offer + } + + @AfterAll @JvmStatic fun cleanup() { File(TEST_DB_PATH).delete() } } @@ -115,4 +134,73 @@ import java.io.File Assertions.assertEquals(false, b.isCancelled) } } + + @Test fun offerWithCombinedNotedAndUnnotedAmountShouldSuceed() { + TestUtils.getMockPlayer("combinedNotendAndUnnotedExcTest").use { p -> + val offer = generateUnsentOffer(4151, 1000, 1500, true, p.name, OfferState.PENDING) + val mkt = StockMarket() + p.inventory.add(Item(Items.ABYSSAL_WHIP_4151, 10)) + p.inventory.add(Item(Items.ABYSSAL_WHIP_4152, 990)) + Assertions.assertEquals(StockMarket.OfferConfirmResult.Success, mkt.confirmOffer(p, offer, 0)) + Assertions.assertEquals(0, p.inventory.getAmount(4151)) + Assertions.assertEquals(0, p.inventory.getAmount(4152)) + } + } + + @Test fun offerWithOnlyNotedAmountShouldSucceed() { + TestUtils.getMockPlayer("onlyNotedOfferSucceed").use { p -> + val offer = generateUnsentOffer(4151, 1000, 1500, true, p.name, OfferState.PENDING) + val mkt = StockMarket() + p.inventory.add(Item(Items.ABYSSAL_WHIP_4152, 1000)) + Assertions.assertEquals(StockMarket.OfferConfirmResult.Success, mkt.confirmOffer(p, offer, 0)) + Assertions.assertEquals(0, p.inventory.getAmount(4151)) + Assertions.assertEquals(0, p.inventory.getAmount(4152)) + } + } + + @Test fun offerWithMoreUnnotedItemsThanOfferAmountShouldSucceed() { + TestUtils.getMockPlayer("onlyUnnotedOfferWithExtraItemsSucceed").use { p -> + val offer = generateUnsentOffer(4151, 10, 1500, true, p.name, OfferState.PENDING) + val mkt = StockMarket() + p.inventory.add(Item(Items.ABYSSAL_WHIP_4151, 20)) + Assertions.assertEquals(StockMarket.OfferConfirmResult.Success, mkt.confirmOffer(p, offer, 0)) + Assertions.assertEquals(10, p.inventory.getAmount(4151)) + Assertions.assertEquals(0, p.inventory.getAmount(4152)) + } + } + + @Test fun offerWithOnlyUnnotedAmountShouldSucceed() { + TestUtils.getMockPlayer("onlyUnnotedOfferSucceed").use { p -> + val offer = generateUnsentOffer(4151, 10, 1500, true, p.name, OfferState.PENDING) + val mkt = StockMarket() + p.inventory.add(Item(Items.ABYSSAL_WHIP_4151, 10)) + Assertions.assertEquals(StockMarket.OfferConfirmResult.Success, mkt.confirmOffer(p, offer, 0)) + Assertions.assertEquals(0, p.inventory.getAmount(4151)) + Assertions.assertEquals(0, p.inventory.getAmount(4152)) + } + } + + @Test fun offerWithNotEnoughNotedItemsShouldFail() { + TestUtils.getMockPlayer("combinedNotedAndUnnotedFailure").use { p -> + val offer = generateUnsentOffer(4151, 1000, 1500, true, p.name, OfferState.PENDING) + val mkt = StockMarket() + p.inventory.add(Item(Items.ABYSSAL_WHIP_4151, 10)) + p.inventory.add(Item(Items.ABYSSAL_WHIP_4152, 15)) + Assertions.assertEquals(StockMarket.OfferConfirmResult.NotEnoughItemsOrCoins, mkt.confirmOffer(p, offer, 0)) + Assertions.assertEquals(10, p.inventory.getAmount(4151)) + Assertions.assertEquals(15, p.inventory.getAmount(4152)) + } + } + + @Test fun offerWithNotEnoughUnnotedItemsShouldFail() { + TestUtils.getMockPlayer("combinedNotedAndUnnotedFailure2").use { p -> + val offer = generateUnsentOffer(4151, 1000, 1500, true, p.name, OfferState.PENDING) + val mkt = StockMarket() + p.inventory.add(Item(Items.ABYSSAL_WHIP_4151, 10)) + p.inventory.add(Item(Items.ABYSSAL_WHIP_4152, 900)) + Assertions.assertEquals(StockMarket.OfferConfirmResult.NotEnoughItemsOrCoins, mkt.confirmOffer(p, offer, 0)) + Assertions.assertEquals(10, p.inventory.getAmount(4151)) + Assertions.assertEquals(900, p.inventory.getAmount(4152)) + } + } } diff --git a/Server/src/test/kotlin/HouseManagerTests.kt b/Server/src/test/kotlin/HouseManagerTests.kt deleted file mode 100644 index ab6b24241..000000000 --- a/Server/src/test/kotlin/HouseManagerTests.kt +++ /dev/null @@ -1,126 +0,0 @@ -import core.game.node.entity.player.link.music.MusicEntry -import content.global.skill.construction.HouseLocation -import content.global.skill.construction.HouseManager -import content.global.skill.construction.Servant -import content.global.skill.construction.ServantType -import core.game.world.map.RegionManager -import core.game.world.map.path.Pathfinder -import org.junit.jupiter.api.Assertions -import org.junit.jupiter.api.Test - -class HouseManagerTests { - companion object { - init {TestUtils.preTestSetup()} - } - - val manager = HouseManager() - val testPlayer = TestUtils.getMockPlayer("test") - - @Test fun constructShouldLoadTheConstructedRegion() { - val newManager = HouseManager() - newManager.createNewHouseAt(HouseLocation.RIMMINGTON) //add a room to it, already tested below - newManager.construct() - Assertions.assertNotEquals(0, newManager.houseRegion.planes[0].getRegionChunk(4, 3).objects.size) - } - - @Test fun constructShouldRegisterNewRegionToRegionManager() { - val newManager = HouseManager() - newManager.construct() - Assertions.assertEquals(true, RegionManager.forId(newManager.houseRegion.id) == newManager.houseRegion) - } - - @Test fun constructShouldSetTheRegionInTheHouseManager() { - val newManager = HouseManager() - newManager.construct() - Assertions.assertNotEquals(null, newManager.houseRegion) - } - - @Test fun constructShouldSetTheRegionBorders() { - val newManager = HouseManager() - newManager.construct() - Assertions.assertNotEquals(null, newManager.houseRegion.borders) - } - - @Test fun constructShouldSetUpdateAllPlanes() { - val newManager = HouseManager() - newManager.construct() - Assertions.assertEquals(true, newManager.houseRegion.isUpdateAllPlanes) - } - - @Test fun constructShouldReplacePlanes1And2UnusedChunksAndAllPlane3ChunksWithEmptyChunks() { - val newManager = HouseManager() - newManager.construct() - for(z in 1..3) - for (objs in newManager.houseRegion.planes[z].objects) - for (obj in objs) Assertions.assertEquals(null, obj) - } - - @Test fun leaveShouldPlaceThePlayerAtTheHouseLocationExitLocation() { - val newManager = HouseManager() - val newPlayer = TestUtils.getMockPlayer("test3") - newManager.construct() - newManager.enter(newPlayer, false) - TestUtils.advanceTicks(5) - HouseManager.leave(newPlayer) - Assertions.assertEquals(newManager.location.exitLocation, newPlayer.location) - } - - @Test fun toggleBuildingModeShouldChangeBuildingMode() { - val newManager = HouseManager() - val newPlayer = TestUtils.getMockPlayer("test4") - newManager.enter(newPlayer, false) - TestUtils.advanceTicks(5) - newManager.toggleBuildingMode(newPlayer, true) - Assertions.assertEquals(true, newManager.isBuildingMode) - } - - @Test fun createShouldPlaceGardenInRooms() { - manager.createNewHouseAt(HouseLocation.RIMMINGTON) - Assertions.assertEquals(true, manager.hasRoomAt(0, 4, 3)) - } - - @Test fun enterShouldConstructDynamicRegionIfItHasNotBeenConstructed() { - manager.enter(testPlayer, false) - Assertions.assertEquals(true, manager.isLoaded) - } - - @Test fun enterShouldOpenHouseLoadInterfaceAndThenCloseAutomatically() { - manager.enter(testPlayer, false) - Assertions.assertEquals(399, testPlayer.interfaceManager.opened.id) - TestUtils.advanceTicks(5) - Assertions.assertNotEquals(null, testPlayer.interfaceManager.opened) - } - - @Test fun enterShouldSendServantIfHasOne() { - manager.servant = Servant(ServantType.BUTLER) - manager.enter(testPlayer, false) - TestUtils.advanceTicks(5) - Assertions.assertEquals(true, manager.servant.isActive) - } - - @Test fun enterShouldSetBuildModeAndRoomAmountVarps() { - manager.enter(testPlayer, false) - Assertions.assertEquals(true, testPlayer.varpManager.get(261).varbits.isNotEmpty()) - Assertions.assertEquals(true, testPlayer.varpManager.get(262).varbits.isNotEmpty()) - } - - @Test fun enterShouldUnlockPOHMusicTrack() { - manager.enter(testPlayer, false) - Assertions.assertEquals(true, testPlayer.musicPlayer.unlocked.contains(MusicEntry.forId(454).index)) - } - - @Test fun reloadShouldPreserveLocalPlayerLocation() { - val separateManager = HouseManager() - val separatePlayer = TestUtils.getMockPlayer("test2") - separateManager.enter(separatePlayer, false) - TestUtils.advanceTicks(5) - Pathfinder.find(separatePlayer, separatePlayer.location.transform(10,10,0)).walk(separatePlayer) - TestUtils.advanceTicks(20) - val localX = separatePlayer.location.localX - val localY = separatePlayer.location.localY - separateManager.reload(separatePlayer, true) - TestUtils.advanceTicks(20) - Assertions.assertEquals(localX, separatePlayer.location.localX) - Assertions.assertEquals(localY, separatePlayer.location.localY) - } -} \ No newline at end of file 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 7487edf84..0a4fa7bc8 100644 --- a/Server/src/test/kotlin/QuestTests.kt +++ b/Server/src/test/kotlin/QuestTests.kt @@ -1,11 +1,17 @@ +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 import org.junit.jupiter.api.Test class QuestTests { - val testPlayer = TestUtils.getMockPlayer("test") - class TestQuest : Quest("Test Quest", 0, 0, 1, 1, 0, 1, 2) { + lateinit var testPlayer: MockPlayer + init { + TestUtils.preTestSetup() + testPlayer = TestUtils.getMockPlayer("test") + } + + class TestQuest : Quest(Quests.TEST_QUEST, 0, 0, 1, 1, 0, 1, 2) { override fun newInstance(`object`: Any?): Quest { return this } @@ -20,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() { @@ -49,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/RunecraftingTests.kt b/Server/src/test/kotlin/RunecraftingTests.kt new file mode 100644 index 000000000..0a16e6d0d --- /dev/null +++ b/Server/src/test/kotlin/RunecraftingTests.kt @@ -0,0 +1,51 @@ +import content.global.skill.runecrafting.RuneCraftPulse; +import content.global.skill.runecrafting.Rune; + +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test + +class RunecraftTests { + fun rollRc(rcLevel: Int, rune: Rune, revision: Int, lo: Double, hi: Double) { + var total = 0.0 + for(i in 0 until 3000) { + total += RuneCraftPulse.getMultiplier(rcLevel, rune, revision, false) + } + val average = total / 3000.0 + Assertions.assertTrue(lo <= average && average <= hi, "rollRc: ${rcLevel} ${rune.name} ${revision}: ${average}") + } + + @Test fun testNatureRcMultipliers() { + rollRc(44, Rune.NATURE, 530, 1.0, 1.0); + rollRc(44, Rune.NATURE, 573, 1.0, 1.0); + rollRc(44, Rune.NATURE, 581, 1.0, 1.0); + + rollRc(68, Rune.NATURE, 530, 1.0, 1.0); + rollRc(68, Rune.NATURE, 573, 1.45, 1.55); + rollRc(68, Rune.NATURE, 581, 1.45, 1.55); + + rollRc(91, Rune.NATURE, 530, 2.0, 2.0); + rollRc(91, Rune.NATURE, 573, 2.0, 2.0); + rollRc(91, Rune.NATURE, 581, 2.0, 2.0); + + rollRc(90, Rune.NATURE, 530, 1.0, 1.0); + rollRc(90, Rune.NATURE, 573, 1.95, 2.0); + rollRc(90, Rune.NATURE, 581, 1.95, 2.0); + + rollRc(99, Rune.NATURE, 530, 2.0, 2.0); + rollRc(99, Rune.NATURE, 573, 2.0, 2.0); + rollRc(99, Rune.NATURE, 581, 2.04, 2.14); + } + @Test fun testLawRcMultipliers() { + rollRc(54, Rune.LAW, 530, 1.0, 1.0); + rollRc(54, Rune.LAW, 573, 1.0, 1.0); + rollRc(54, Rune.LAW, 581, 1.0, 1.0); + + rollRc(77, Rune.LAW, 530, 1.0, 1.0); + rollRc(77, Rune.LAW, 573, 1.0, 1.0); + rollRc(77, Rune.LAW, 581, 1.35, 1.45); + + rollRc(99, Rune.LAW, 530, 1.0, 1.0); + rollRc(99, Rune.LAW, 573, 1.0, 1.0); + rollRc(99, Rune.LAW, 581, 1.75, 1.85); + } +} diff --git a/Server/src/test/kotlin/ShopTests.kt b/Server/src/test/kotlin/ShopTests.kt index 218280204..025fd13f1 100644 --- a/Server/src/test/kotlin/ShopTests.kt +++ b/Server/src/test/kotlin/ShopTests.kt @@ -1,11 +1,12 @@ + import core.game.node.entity.player.link.IronmanMode import core.game.node.item.Item +import core.game.shops.Shop +import core.game.shops.Shops import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.rs09.consts.Items -import core.game.shops.Shop -import core.game.shops.Shops import kotlin.math.min import kotlin.math.roundToInt @@ -228,7 +229,7 @@ class ShopTests { } @Test fun shouldAllowStandardPlayerToBuy() { - testPlayer.inventory.add(Item(995, 100000)) + testPlayer.inventory.add(Item(995, 125000)) testPlayer.setAttribute("shop-cont", general.getContainer(testPlayer)) testPlayer.setAttribute("shop-main", true) val status = general.buy(testPlayer, 0, 1) @@ -236,7 +237,7 @@ class ShopTests { } @Test fun shouldAllowStandardPlayerToBuyOverstock() { - testPlayer.inventory.add(Item(995, 100000)) + testPlayer.inventory.add(Item(995, 125000)) testPlayer.setAttribute("shop-cont", general.getContainer(testPlayer)) testPlayer.setAttribute("shop-main", true) general.getContainer(testPlayer).add(Item(4151, 100)) @@ -245,7 +246,7 @@ class ShopTests { } @Test fun shouldAllowStandardPlayerToBuyPlayerStock() { - testPlayer.inventory.add(Item(995, 100000)) + testPlayer.inventory.add(Item(995, 125000)) testPlayer.setAttribute("shop-cont", general.getContainer(testPlayer)) testPlayer.setAttribute("shop-main", false) general.playerStock.add(Item(4151, 100)) diff --git a/Server/src/test/kotlin/TestUtils.kt b/Server/src/test/kotlin/TestUtils.kt index 23de9d029..b8d8d45cd 100644 --- a/Server/src/test/kotlin/TestUtils.kt +++ b/Server/src/test/kotlin/TestUtils.kt @@ -1,33 +1,56 @@ +import content.global.skill.farming.timers.CropGrowth +import core.ServerConstants +import core.api.log import core.cache.Cache import core.cache.crypto.ISAACCipher import core.cache.crypto.ISAACPair +import core.game.interaction.ScriptProcessor +import core.game.node.Node +import core.game.node.entity.combat.equipment.WeaponInterface +import core.game.node.entity.npc.NPC import core.game.node.entity.player.Player +import core.game.node.entity.player.VarpManager import core.game.node.entity.player.info.PlayerDetails import core.game.node.entity.player.info.Rights +import core.game.node.entity.player.info.login.PlayerSaveParser +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.quest.QuestRepository +import core.game.node.entity.skill.Skills +import core.game.node.item.GroundItem import core.game.node.item.Item -import core.net.IoSession -import core.net.packet.IoBuffer -import org.rs09.consts.Items -import core.ServerConstants import core.game.shops.Shop import core.game.shops.ShopItem -import core.tools.SystemLogger import core.game.system.config.ConfigParser import core.game.system.config.ServerConfigParser +import core.game.system.timer.TimerRegistry +import core.game.system.timer.impl.AntiMacro +import core.game.system.timer.impl.Disease +import core.game.system.timer.impl.Poison import core.game.world.GameWorld +import core.game.world.map.Location +import core.game.world.map.RegionManager import core.game.world.repository.Repository import core.game.world.update.UpdateSequence +import core.net.IoSession +import core.net.packet.IoBuffer +import core.net.packet.PacketProcessor +import core.tools.Log +import org.rs09.consts.Items import java.net.URI import java.nio.ByteBuffer object TestUtils { var uidCounter = 0 + const val PLAYER_DEATH_TICKS = 14 - fun getMockPlayer(name: String, ironman: IronmanMode = IronmanMode.NONE, rights: Rights = Rights.ADMINISTRATOR): Player { - val p = MockPlayer(name) + fun getMockPlayer(name: String, ironman: IronmanMode = IronmanMode.NONE, rights: Rights = Rights.ADMINISTRATOR, isBot: Boolean = false): MockPlayer { + val p = MockPlayer(name, isBot) p.ironmanManager.mode = ironman p.details.accountInfo.uid = uidCounter++ + p.setPlaying(true) + p.playerFlags.lastSceneGraph = p.location ?: ServerConstants.HOME_LOCATION Repository.addPlayer(p) //Update sequence has a separate list of players for some reason... UpdateSequence.renderablePlayers.add(p) @@ -58,29 +81,107 @@ object TestUtils { Cache.init(this::class.java.getResource("cache").path.toString()) ConfigParser().prePlugin() ConfigParser().postPlugin() + registerTimers() } } + fun registerTimers() { //allow timers to be registered for use by tests + TimerRegistry.registerTimer(Poison()) + TimerRegistry.registerTimer(Disease()) + TimerRegistry.registerTimer(CropGrowth()) + TimerRegistry.registerTimer(AntiMacro()) + } + fun loadFile(path: String) : URI? { return this::class.java.getResource(path)?.toURI() } fun advanceTicks(amount: Int, skipPulseUpdates: Boolean = true) { - SystemLogger.logInfo(this::class.java, "Advancing ticks by $amount.") + log(this::class.java, Log.FINE, "Advancing ticks by $amount.") for(i in 0 until amount) { GameWorld.majorUpdateWorker.handleTickActions(skipPulseUpdates) } } + + fun simulateInteraction (player: Player, target: Node, optionIndex: Int, iface: Int = -1, child: Int = -1) { + when (target) { + is GroundItem -> PacketProcessor.enqueue(core.net.packet.`in`.Packet.GroundItemAction(player, optionIndex, target.id, target.location.x, target.location.y)) + is Item -> PacketProcessor.enqueue(core.net.packet.`in`.Packet.ItemAction(player, optionIndex, target.id, target.slot, iface, child)) + is NPC -> PacketProcessor.enqueue(core.net.packet.`in`.Packet.NpcAction(player, optionIndex, target.clientIndex)) + is core.game.node.scenery.Scenery -> PacketProcessor.enqueue(core.net.packet.`in`.Packet.SceneryAction(player, optionIndex, target.id, target.location.x, target.location.y)) + } + advanceTicks(1, true) + } } -class MockPlayer(name: String) : Player(PlayerDetails(name)) { - init { +class MockPlayer(name: String, val isBot: Boolean) : Player(PlayerDetails(name)), AutoCloseable { + var hasInit = false + init { configureBasicProperties(); flagTutComplete(false); init(); flagTutComplete(true) } + + fun configureBasicProperties() { this.details.session = MockSession() + this.location = ServerConstants.HOME_LOCATION + this.properties.attackStyle = WeaponInterface.AttackStyle(0, WeaponInterface.BONUS_CRUSH) + this.artificial = isBot + } + + fun flagTutComplete(complete: Boolean) { + this.setAttribute("/save:tutorial:complete", complete) + this.setAttribute("/save:rules:confirmed", complete) } override fun update() { //do nothing. This is for rendering stuff. We don't render a mock player. Not until the spaghetti is less spaghetti. } + + override fun init() { + if (hasInit) return + hasInit = true + super.init() + } + + override fun close() { + Repository.removePlayer(this) + UpdateSequence.renderablePlayers.remove(this) + finishClear() + } + + override fun setLocation(location: Location?) { + super.setLocation(location) + RegionManager.move(this) + this.playerFlags.lastSceneGraph = location + } + + fun relog(ticksToWait: Int = -1) { + val json = PlayerSaver(this).populate() + val parse = PlayerSaveParser(this) + parse.saveFile = json + + close() + timers.clearTimers() + inventory.clear() + bank.clear() + equipment.clear() + skills = Skills(this) + savedData = SavedData(this) + questRepository = QuestRepository(this) + varpManager = VarpManager(this) + varpMap.clear() + saveVarp.clear() + scripts = ScriptProcessor(this) + clearAttributes() + hasInit = false + + if (ticksToWait > 0) TestUtils.advanceTicks(ticksToWait, false) + + configureBasicProperties() + parse.parseData() + init() + } + + override fun debug(string: String?) { + log (this::class.java, Log.DEBUG, "[$name] -> Received Debug: $string") + } } class MockSession : IoSession(null, null) { diff --git a/Server/src/test/kotlin/content/CombatTests.kt b/Server/src/test/kotlin/content/CombatTests.kt new file mode 100644 index 000000000..762ed8406 --- /dev/null +++ b/Server/src/test/kotlin/content/CombatTests.kt @@ -0,0 +1,138 @@ +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 +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 { + TestUtils.preTestSetup() + } + + @Test + fun swingHandlersFlaggedAsIgnoreStatsShouldNotInfluenceDamage() { + val testRangeHandler = RangeSwingHandler(SwingHandlerFlag.IGNORE_STAT_BOOSTS_DAMAGE) + val testMeleeHandler = MeleeSwingHandler(SwingHandlerFlag.IGNORE_STAT_BOOSTS_DAMAGE) + + TestUtils.getMockPlayer("ignorestatdamage").use { p -> + p.skills.setLevel(Skills.RANGE, 99) + val baselineRanged = testRangeHandler.calculateHit(p, p, 1.0) + p.properties.bonuses[14] = 250 + Assertions.assertEquals(baselineRanged, testRangeHandler.calculateHit(p, p, 1.0)) + + p.skills.setLevel(Skills.STRENGTH, 99) + val baselineMelee = testMeleeHandler.calculateHit(p, p, 1.0) + p.properties.bonuses[11] = 250 + Assertions.assertEquals(baselineMelee, testMeleeHandler.calculateHit(p, p, 1.0)) + } + } + + @Test + fun swingHandlersFlaggedAsIgnoreStatsShouldNotInfluenceAccuracy() { + val testRangeHandler = RangeSwingHandler(SwingHandlerFlag.IGNORE_STAT_BOOSTS_ACCURACY) + val testMeleeHandler = MeleeSwingHandler(SwingHandlerFlag.IGNORE_STAT_BOOSTS_ACCURACY) + val testMagicHandler = MagicSwingHandler(SwingHandlerFlag.IGNORE_STAT_BOOSTS_ACCURACY) + + TestUtils.getMockPlayer("ignorestataccuracy").use { p -> + p.properties.attackStyle = WeaponInterface.AttackStyle(0, WeaponInterface.BONUS_RANGE) + p.skills.setLevel(Skills.RANGE, 99) + val baselineRanged = testRangeHandler.calculateAccuracy(p) + p.properties.bonuses[WeaponInterface.BONUS_RANGE] = 250 + Assertions.assertEquals(baselineRanged, testRangeHandler.calculateAccuracy(p)) + + p.properties.attackStyle = WeaponInterface.AttackStyle(0, WeaponInterface.BONUS_STAB) + p.skills.setLevel(Skills.STRENGTH, 99) + val baselineMelee = testMeleeHandler.calculateAccuracy(p) + p.properties.bonuses[WeaponInterface.BONUS_STAB] = 250 + Assertions.assertEquals(baselineMelee, testMeleeHandler.calculateAccuracy(p)) + + p.properties.attackStyle = WeaponInterface.AttackStyle(0, WeaponInterface.BONUS_MAGIC) + p.skills.setLevel(Skills.MAGIC, 99) + val baselineMagic = testMagicHandler.calculateAccuracy(p) + p.properties.bonuses[WeaponInterface.BONUS_MAGIC] = 250 + Assertions.assertEquals(baselineMagic, testMagicHandler.calculateAccuracy(p)) + } + } + + @Test + fun swingHandlersFlaggedAsIgnorePrayerShouldNotInfluenceDamage() { + val testRangeHandler = RangeSwingHandler(SwingHandlerFlag.IGNORE_PRAYER_BOOSTS_DAMAGE) + val testMeleeHandler = MeleeSwingHandler(SwingHandlerFlag.IGNORE_PRAYER_BOOSTS_DAMAGE) + + TestUtils.getMockPlayer("ignoreprayerdamage").use { p -> + p.skills.setStaticLevel(Skills.PRAYER, 99) + p.skills.prayerPoints = 1000.0 + + p.skills.setLevel(Skills.RANGE, 99) + val baselineRanged = testRangeHandler.calculateHit(p, p, 1.0) + p.prayer.toggle(PrayerType.EAGLE_EYE) + Assertions.assertEquals(baselineRanged, testRangeHandler.calculateHit(p, p, 1.0)) + p.prayer.toggle(PrayerType.EAGLE_EYE) + + p.skills.setLevel(Skills.STRENGTH, 99) + val baselineMelee = testMeleeHandler.calculateHit(p, p, 1.0) + p.prayer.toggle(PrayerType.SUPERHUMAN_STRENGTH) + Assertions.assertEquals(baselineMelee, testMeleeHandler.calculateHit(p, p, 1.0)) + p.prayer.toggle(PrayerType.SUPERHUMAN_STRENGTH) + } + } + + @Test + fun swingHandlersFlaggedAsIgnorePrayerShouldNotInfluenceAccuracy() { + val testRangeHandler = RangeSwingHandler(SwingHandlerFlag.IGNORE_PRAYER_BOOSTS_ACCURACY) + val testMeleeHandler = MeleeSwingHandler(SwingHandlerFlag.IGNORE_PRAYER_BOOSTS_ACCURACY) + val testMagicHandler = MagicSwingHandler(SwingHandlerFlag.IGNORE_PRAYER_BOOSTS_ACCURACY) + + TestUtils.getMockPlayer("ignoreprayeraccuracy").use { p -> + p.skills.setStaticLevel(Skills.PRAYER, 99) + p.skills.prayerPoints = 1000.0 + + p.skills.setLevel(Skills.RANGE, 99) + val baselineRanged = testRangeHandler.calculateAccuracy(p) + p.prayer.toggle(PrayerType.EAGLE_EYE) + Assertions.assertEquals(baselineRanged, testRangeHandler.calculateAccuracy(p)) + p.prayer.toggle(PrayerType.EAGLE_EYE) + + p.skills.setLevel(Skills.ATTACK, 99) + val baselineMelee = testMeleeHandler.calculateAccuracy(p) + p.prayer.toggle(PrayerType.INCREDIBLE_REFLEXES) + Assertions.assertEquals(baselineMelee, testMeleeHandler.calculateAccuracy(p)) + p.prayer.toggle(PrayerType.INCREDIBLE_REFLEXES) + + p.skills.setLevel(Skills.MAGIC, 99) + val baselineMagic = testMagicHandler.calculateAccuracy(p) + p.prayer.toggle(PrayerType.MYSTIC_MIGHT) + Assertions.assertEquals(baselineMagic, testMagicHandler.calculateAccuracy(p)) + p.prayer.toggle(PrayerType.MYSTIC_MIGHT) + } + } + + @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.equipment.replace(Item(Items.DRAGON_ARROW_11212), EquipmentSlot.AMMO.ordinal) + updateBonuses(p) + Assertions.assertEquals(damageBaseline, handler.calculateHit(p, p, 1.0)) + } + } +} \ No newline at end of file diff --git a/Server/src/test/kotlin/content/DeathTests.kt b/Server/src/test/kotlin/content/DeathTests.kt index 5fcca6641..e91c121c7 100644 --- a/Server/src/test/kotlin/content/DeathTests.kt +++ b/Server/src/test/kotlin/content/DeathTests.kt @@ -2,6 +2,8 @@ package content import TestUtils import core.api.asItem +import core.api.setAttribute +import core.game.global.action.DropListener import core.game.node.entity.player.info.Rights import core.game.node.entity.player.link.IronmanMode import core.game.world.map.Location @@ -151,6 +153,38 @@ class DeathTests { Assertions.assertEquals(true, newGrave?.isActive) } + @Test fun graveDeserializedFromServerStoreShouldNaturallyExpire() { + TestUtils.getMockPlayer("graveExpirationFromDeserializationTest").use {p -> + val inventory = arrayOf( + Items.BRONZE_2H_SWORD_1307.asItem(), + Items.BRONZE_AXE_1351.asItem() + ) + val startTime = GameWorld.ticks + val grave = GraveController.produceGrave(GraveType.MEM_PLAQUE) + grave.initialize(p, Location.create(0,0,0), inventory) + val expectedTicksRemaining = secondsToTicks(GraveType.MEM_PLAQUE.durationMinutes * 60) - (GameWorld.ticks - startTime) + Assertions.assertEquals(expectedTicksRemaining, grave.ticksRemaining) + + GraveController.serializeToServerStore() + GraveController.activeGraves.remove(p.details.uid) + GraveController.deserializeFromServerStore() + + var newGrave = GraveController.activeGraves[p.details.uid] + Assertions.assertNotNull(newGrave) + Assertions.assertEquals(expectedTicksRemaining, newGrave?.ticksRemaining ?: -1) + Assertions.assertEquals(2, newGrave?.getItems()?.size ?: -1) + + TestUtils.advanceTicks(expectedTicksRemaining + 1, false) + Assertions.assertEquals(null, GraveController.activeGraves[p.details.uid]) + + GraveController.serializeToServerStore() + GraveController.deserializeFromServerStore() + + newGrave = GraveController.activeGraves[p.details.uid] + Assertions.assertNull(newGrave) + } + } + @Test fun regularDeathShouldSpawnGraveWithItems() { val inventory = arrayOf( Items.RUNE_SCIMITAR_1333.asItem(), @@ -301,7 +335,8 @@ class DeathTests { Assertions.assertNotNull(g) Assertions.assertEquals(p.location, g?.location) - val canDrop = core.game.global.action.DropItemHandler.drop(p, p.inventory[0]) + setAttribute(p, "interact:option", "drop") + val canDrop = DropListener.drop(p, p.inventory[0]) Assertions.assertEquals(false, canDrop) } } \ No newline at end of file diff --git a/Server/src/test/kotlin/content/DropTableTests.kt b/Server/src/test/kotlin/content/DropTableTests.kt new file mode 100644 index 000000000..001524cd0 --- /dev/null +++ b/Server/src/test/kotlin/content/DropTableTests.kt @@ -0,0 +1,49 @@ +package content + +import TestUtils +import content.global.handlers.npc.ChromaticDragonBehavior +import core.ServerConstants +import core.api.utils.NPCDropTable +import core.api.utils.WeightedItem +import core.game.node.entity.npc.NPC +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 +import org.rs09.consts.NPCs + +class DropTableTests { + init { + TestUtils.preTestSetup() + } + @Test fun chromaticDragonsShouldDropEggsOnlyAfter99Summoning() { + val npc = NPC.create(NPCs.BLACK_DRAGON_54, ServerConstants.HOME_LOCATION) + val oldTable = npc.definition.dropTables.table + + val testTable = NPCDropTable() //manufacture a fake table that's guaranteed to give an egg, for testing safety. + testTable.add(WeightedItem(Items.BLACK_DRAGON_EGG_12480, 1, 1, 1.0)) + + npc.definition.dropTables.table = testTable + npc.behavior = ChromaticDragonBehavior() + + TestUtils.getMockPlayer("chromaticeggdrop").use { p -> + ChromaticDragonBehavior.EGG_RATE = 1 //guarantee egg drop if other parameters are satisfied + var hasEgg = false + + var items = npc.definition.dropTables.roll(npc, p, 1) + for (item in items) + if (item.id == Items.BLACK_DRAGON_EGG_12480) hasEgg = true + Assertions.assertEquals(false, hasEgg) + + p.skills.setStaticLevel(Skills.SUMMONING, 99) + + items = npc.definition.dropTables.roll(npc, p, 1) + for (item in items) + if (item.id == Items.BLACK_DRAGON_EGG_12480) hasEgg = true + Assertions.assertEquals(true, hasEgg) + } + + npc.definition.dropTables.table = oldTable + } +} \ No newline at end of file diff --git a/Server/src/test/kotlin/content/EquipTests.kt b/Server/src/test/kotlin/content/EquipTests.kt index 83a781638..96a903a8a 100644 --- a/Server/src/test/kotlin/content/EquipTests.kt +++ b/Server/src/test/kotlin/content/EquipTests.kt @@ -2,6 +2,8 @@ package content import TestUtils import core.api.EquipmentSlot +import core.api.addItem +import core.api.impact import core.game.node.entity.skill.Skills import core.game.node.item.Item import org.junit.jupiter.api.Assertions @@ -11,6 +13,7 @@ import core.game.global.action.EquipHandler import core.game.interaction.InteractionListener import core.game.interaction.IntType import core.game.interaction.InteractionListeners +import core.game.node.entity.player.info.Rights class EquipTests { companion object { @@ -141,4 +144,26 @@ class EquipTests { Assertions.assertEquals(p.inventory.getSlot(Item(Items.ABYSSAL_WHIP_4151)), 25) Assertions.assertEquals(p.inventory.getSlot(Item(Items.RUNE_DEFENDER_8850)), 0) } + + @Test fun graveDeathWithEquippedItemShouldFireUnequipHooks() { + TestUtils.getMockPlayer("graveunequip").use {p -> + var hookFired = false + val tempHook = object : InteractionListener { + override fun defineListeners() { + onUnequip(Items.ABYSSAL_WHIP_4151) { player, node -> + hookFired = true + return@onUnequip true + } + } + } + + tempHook.defineListeners() + p.details.rights = Rights.REGULAR_PLAYER + addItem(p, Items.ABYSSAL_WHIP_4151, 28) + p.equipment.replace(Item(Items.ABYSSAL_WHIP_4151), EquipmentSlot.WEAPON.ordinal) + impact(p, p.skills.lifepoints) + TestUtils.advanceTicks(25, false) + Assertions.assertEquals(true, hookFired) + } + } } diff --git a/Server/src/test/kotlin/content/ListenerTests.kt b/Server/src/test/kotlin/content/ListenerTests.kt index e7709522d..b6316eda5 100644 --- a/Server/src/test/kotlin/content/ListenerTests.kt +++ b/Server/src/test/kotlin/content/ListenerTests.kt @@ -9,6 +9,7 @@ import core.game.interaction.IntType import core.game.interaction.InteractionListeners class ListenerTests : InteractionListener { + init {TestUtils.preTestSetup()} @Test fun doubleDefinedListenerShouldThrowIllegalStateException() { on(0, IntType.ITEM, "touch") { _, _ -> return@on true} Assertions.assertThrows(IllegalStateException::class.java) { diff --git a/Server/src/test/kotlin/content/RandomEventManagerTests.kt b/Server/src/test/kotlin/content/RandomEventManagerTests.kt deleted file mode 100644 index a6f1cffde..000000000 --- a/Server/src/test/kotlin/content/RandomEventManagerTests.kt +++ /dev/null @@ -1,76 +0,0 @@ -package content - -import TestUtils -import core.game.node.item.Item -import org.junit.jupiter.api.Assertions -import org.junit.jupiter.api.Test -import org.rs09.consts.Items -import content.global.ame.RandomEventNPC -import core.game.world.GameWorld - -class RandomEventManagerTests { - companion object {init { - TestUtils.preTestSetup() - }} - - @Test fun loginShouldEnableManager() { - val p = TestUtils.getMockPlayer("Bill") - content.global.ame.RandomEventManager().login(p) - val manager = content.global.ame.RandomEventManager.getInstance(p) - Assertions.assertNotNull(manager) - Assertions.assertEquals(true, manager!!.enabled) - } - - @Test fun loginShouldSetNextSpawn() { - val p = TestUtils.getMockPlayer("Bill") - content.global.ame.RandomEventManager().login(p) - val manager = content.global.ame.RandomEventManager.getInstance(p) - Assertions.assertNotNull(manager) - Assertions.assertEquals(true, manager!!.nextSpawn > GameWorld.ticks) - } - - @Test fun shouldSpawnRandomEventWithinMAXTICKSGivenNoRestrictions() { - val p = TestUtils.getMockPlayer("Bill") - p.setAttribute("tutorial:complete", true) //tutorial has to be complete to spawn randoms - content.global.ame.RandomEventManager().login(p) - TestUtils.advanceTicks(content.global.ame.RandomEventManager.MAX_DELAY_TICKS + 5) - Assertions.assertNotNull(p.getAttribute("re-npc", null)) - } - - @Test fun teleportAndNotePunishmentShouldNotAffectAlreadyNotedItems() { - val p = TestUtils.getMockPlayer("Shitforbrains") - p.setAttribute("tutorial:complete", true) - content.global.ame.RandomEventManager().login(p) - - p.inventory.add(Item(Items.RAW_SHARK_384, 1000)) - content.global.ame.RandomEventManager.getInstance(p)?.fireEvent() - p.getAttribute("re-npc")!!.noteAndTeleport() - - Assertions.assertEquals(1000, p.inventory.getAmount(Items.RAW_SHARK_384)) - } - - @Test fun teleportAndNotePunishmentShouldNoteNotableUnnotedItems() { - val p = TestUtils.getMockPlayer("shitforbrains2") - p.setAttribute("tutorial:complete", true) - content.global.ame.RandomEventManager().login(p) - - p.inventory.add(Item(4151, 5)) - content.global.ame.RandomEventManager.getInstance(p)?.fireEvent() - p.getAttribute("re-npc")!!.noteAndTeleport() - - Assertions.assertEquals(5, p.inventory.getAmount(4152)) - Assertions.assertEquals(0, p.inventory.getAmount(4151)) - } - - @Test fun teleportAndNotePunishmentShouldNotAffectUnnotableItems() { - val p = TestUtils.getMockPlayer("shitforbrains3") - p.setAttribute("tutorial:complete", true) - content.global.ame.RandomEventManager().login(p) - - p.inventory.add(Item(Items.AIR_RUNE_556, 30)) - content.global.ame.RandomEventManager.getInstance(p)?.fireEvent() - p.getAttribute("re-npc")!!.noteAndTeleport() - - Assertions.assertEquals(30, p.inventory.getAmount(Items.AIR_RUNE_556)) - } -} \ No newline at end of file diff --git a/Server/src/test/kotlin/content/RandomEventTests.kt b/Server/src/test/kotlin/content/RandomEventTests.kt new file mode 100644 index 000000000..9ab1aa02f --- /dev/null +++ b/Server/src/test/kotlin/content/RandomEventTests.kt @@ -0,0 +1,226 @@ +package content + +import TestUtils +import content.global.ame.RandomEventNPC +import content.global.ame.RandomEvents +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 org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +class RandomEventTests { + init { + TestUtils.preTestSetup() + } + + @Test fun loginShouldRegisterRandomEventTimer() { + TestUtils.getMockPlayer("antimacroAutoRegister").use { p -> + Assertions.assertNotNull(getTimer(p)) + } + } + + @Test fun loginShouldSetNextSpawn() { + TestUtils.getMockPlayer("antimacroautosetsnextspawnifunset").use { p -> + val timer = getTimer(p) ?: Assertions.fail("AntiMacro timer is null.") + Assertions.assertNotEquals(getWorldTicks(), timer.nextExecution) + Assertions.assertEquals(true, timer.nextExecution - getWorldTicks() >= AntiMacro.MIN_DELAY_TICKS) + Assertions.assertEquals(true, timer.nextExecution - getWorldTicks() <= AntiMacro.MAX_DELAY_TICKS) + } + } + + @Test fun remainingDelayShouldPersistRelog() { + TestUtils.getMockPlayer("antimacrotimeremainingpersists").use { p -> + val timer = getTimer(p) ?: Assertions.fail("AntiMacro timer is null.") + timer.nextExecution = getWorldTicks() + 666 + p.relog(ticksToWait = 100) + Assertions.assertEquals(getWorldTicks() + 666, getTimer(p)!!.nextExecution) + } + } + + @Test fun delayShouldBeRestartedOnceDepleted() { + TestUtils.getMockPlayer("delayrestartoncedepleted").use { p -> + val timer = getTimer(p) ?: Assertions.fail("AntiMacro timer is null.") + TestUtils.advanceTicks(2, false) + timer.nextExecution = 5 //run in 5 ticks + TestUtils.advanceTicks(5, false) + Assertions.assertEquals(true, timer.nextExecution - getWorldTicks() >= AntiMacro.MIN_DELAY_TICKS) + Assertions.assertEquals(true, timer.nextExecution - getWorldTicks() <= AntiMacro.MAX_DELAY_TICKS) + } + } + + @Test fun shouldSpawnRandomEventGivenNoRestrictions() { + TestUtils.getMockPlayer("antimacroshouldspawnrandom").use {p -> + val timer = getTimer(p) ?: Assertions.fail("AntiMacro timer is null!") + TestUtils.advanceTicks(5, false) + timer.nextExecution = getWorldTicks() + 5 + TestUtils.advanceTicks(10, false) + Assertions.assertNotNull(p.getAttribute(AntiMacro.EVENT_NPC, null)) + Assertions.assertEquals(true, p.getAttribute(AntiMacro.EVENT_NPC).isActive) + } + } + + @Test fun shouldNotSpawnForArtificialPlayer() { + TestUtils.getMockPlayer("antimacroshouldspawnrandom", isBot = true).use {p -> + val timer = getTimer(p) + Assertions.assertEquals(null, timer) + } + } + + @Test fun teleportAndNotePunishmentShouldNotAffectAlreadyNotedItems() { + TestUtils.getMockPlayer("teleportpunishment1").use {p -> + val timer = getTimer(p) ?: Assertions.fail("AntiMacro timer is null!") + TestUtils.advanceTicks(5, false) + timer.nextExecution = getWorldTicks() + 5 + TestUtils.advanceTicks(10, false) + + addItem(p, Items.RAW_SHARK_384, 1000) + getAttribute(p, AntiMacro.EVENT_NPC, null)!!.noteAndTeleport() + + Assertions.assertEquals(1000, amountInInventory(p, Items.RAW_SHARK_384)) + } + } + + @Test fun teleportAndNotePunishmentShouldNoteNotableUnnotedItems() { + TestUtils.getMockPlayer("teleportpunishment2").use {p -> + val timer = getTimer(p) ?: Assertions.fail("AntiMacro timer is null!") + TestUtils.advanceTicks(5, false) + timer.nextExecution = getWorldTicks() + 5 + TestUtils.advanceTicks(10, false) + + addItem(p, Items.ABYSSAL_WHIP_4151, 5) + getAttribute(p, AntiMacro.EVENT_NPC, null)!!.noteAndTeleport() + + Assertions.assertEquals(5, amountInInventory(p, Items.ABYSSAL_WHIP_4152)) + Assertions.assertEquals(0, amountInInventory(p, Items.ABYSSAL_WHIP_4151)) + } + } + + @Test fun teleportAndNotePunishmentShouldNotAffectUnnotableItems() { + TestUtils.getMockPlayer("teleportpunishment3").use {p -> + val timer = getTimer(p) ?: Assertions.fail("AntiMacro timer is null!") + TestUtils.advanceTicks(5, false) + timer.nextExecution = getWorldTicks() + 5 + TestUtils.advanceTicks(10, false) + + addItem(p, Items.AIR_RUNE_556, 30) + getAttribute(p, AntiMacro.EVENT_NPC, null)!!.noteAndTeleport() + + Assertions.assertEquals(30, amountInInventory(p, Items.AIR_RUNE_556)) + } + } + + @Test fun randomEventShouldNotSpawnInEventRestrictedArea() { + + TestUtils.getMockPlayer("antimacronospawninrestrictedzone").use { p -> + val timer = getTimer(p) ?: Assertions.fail("AntiMacro timer was null!") + TestUtils.advanceTicks(5, false) + + //Wilderness is Random Event restricted + WildernessZone.getInstance().configure() + val loc = Location.create(3131, 3595) + p.location = loc + + Assertions.assertEquals(loc, p.location) + + timer.nextExecution = getWorldTicks() + 5 + TestUtils.advanceTicks(10, false) + Assertions.assertNull(AntiMacro.getEventNpc(p)) + } + } + + //FIXME + //@Test fun randomEventShouldNotSpawnIfOneAlreadyActive() { + // TestUtils.getMockPlayer("antimacronospawnifalreadyhas").use { p -> + // val timer = getTimer(p) ?: Assertions.fail("AntiMacro timer was null!") + // TestUtils.advanceTicks(5, false) + // timer.nextExecution = getWorldTicks() + 5 + // TestUtils.advanceTicks(10, false) + // Assertions.assertNotNull(AntiMacro.getEventNpc(p)) + + // val previousEvent = AntiMacro.getEventNpc(p) + + // timer.nextExecution = getWorldTicks() + 5 + // TestUtils.advanceTicks(10, false) + // Assertions.assertNotNull(AntiMacro.getEventNpc(p)) + // Assertions.assertEquals(previousEvent, AntiMacro.getEventNpc(p)) + // } + //} + + //FIXME + //@Test fun randomEventShouldSpawnIfCurrentOneIsInactive() { + // TestUtils.getMockPlayer("antimacrospawnifcurrentinactive").use { p -> + // val timer = getTimer(p) ?: Assertions.fail("AntiMacro timer was null!") + // TestUtils.advanceTicks(5, false) + // timer.nextExecution = getWorldTicks() + 5 + // TestUtils.advanceTicks(10, false) + // Assertions.assertNotNull(AntiMacro.getEventNpc(p)) + + // val previousEvent = AntiMacro.getEventNpc(p) + // AntiMacro.terminateEventNpc(p) + + // timer.nextExecution = getWorldTicks() + 5 + // TestUtils.advanceTicks(10, false) + // Assertions.assertNotNull(AntiMacro.getEventNpc(p)) + // Assertions.assertNotEquals(previousEvent, AntiMacro.getEventNpc(p)) + // } + //} + + @Test fun randomEventSystemShouldSupportPauseAndUnpause() { + TestUtils.getMockPlayer("antimacropause").use { p -> + val timer = getTimer(p) ?: Assertions.fail("AntiMacro timer was null!") + TestUtils.advanceTicks(5, false) + timer.nextExecution = getWorldTicks() + 5 + + AntiMacro.pause(p) + TestUtils.advanceTicks(10, false) + Assertions.assertNull(AntiMacro.getEventNpc(p)) + + AntiMacro.unpause(p) + timer.nextExecution = getWorldTicks() + 5 + TestUtils.advanceTicks(10, false) + Assertions.assertNotNull(AntiMacro.getEventNpc(p)) + } + } + + @Test fun shouldBeAbleToForceRandomEvent() { + TestUtils.getMockPlayer("antimacroforcerand").use { p -> + TestUtils.advanceTicks(5, false) + AntiMacro.forceEvent(p) + TestUtils.advanceTicks(1, false) + Assertions.assertNotNull(AntiMacro.getEventNpc(p)) + } + } + + @Test fun shouldBeAbleToForceSpecificRandomEvent() { + TestUtils.getMockPlayer("antimacroforcespecific").use { p -> + TestUtils.advanceTicks(5, false) + AntiMacro.forceEvent(p, RandomEvents.TREE_SPIRIT) + TestUtils.advanceTicks(1, false) + Assertions.assertNotNull(AntiMacro.getEventNpc(p)) + Assertions.assertEquals(NPCs.TREE_SPIRIT_438, AntiMacro.getEventNpc(p)?.originalId) + } + } + + @Test fun parseAntiMacroCommandArgsShouldReturnExpectedValues() { + val testData = arrayOf( + Triple("revent -p test", "test", null), + Triple("revent -p test ", "test", null), + Triple("revent -p test -e certer ", "test", RandomEvents.CERTER), + Triple("revent -p test_user -e Sandwich lady", "test_user", RandomEvents.SANDWICH_LADY), + Triple("revent -p test user -e sandwich Lady ", "test_user", RandomEvents.SANDWICH_LADY), + Triple("revent -e sandwich Lady -p test user ", "test_user", RandomEvents.SANDWICH_LADY), + Triple("revent test", "test", null), + Triple("revent test -e sAndwich Lady", "test", RandomEvents.SANDWICH_LADY) + ) + + for ((commandStr, expectedUser, expectedEvent) in testData) { + val (resultUser, resultEvent) = AntiMacro.parseCommandArgs(commandStr, "revent") + Assertions.assertEquals(expectedUser, resultUser) + Assertions.assertEquals(expectedEvent, resultEvent) + } + } +} diff --git a/Server/src/test/kotlin/content/activity/blastfurnace/BFBeltOreTests.kt b/Server/src/test/kotlin/content/activity/blastfurnace/BFBeltOreTests.kt new file mode 100644 index 000000000..0d8f33627 --- /dev/null +++ b/Server/src/test/kotlin/content/activity/blastfurnace/BFBeltOreTests.kt @@ -0,0 +1,44 @@ +package content.activity.blastfurnace + +import TestUtils +import content.minigame.blastfurnace.BFBeltOre +import content.minigame.blastfurnace.BlastFurnace +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test +import org.rs09.consts.Items + +class BFBeltOreTests { + init {TestUtils.preTestSetup()} + + @Test fun oreShouldMoveCloserToPotWhenTicked() { + TestUtils.getMockPlayer("bf-oremove-test").use { p -> + p.location = BFBeltOre.ORE_START_LOC + val ore = BlastFurnace.addOreToBelt(p, Items.IRON_ORE_440, 5) + Assertions.assertEquals(BFBeltOre.ORE_START_LOC, ore.location) + Assertions.assertEquals(BFBeltOre.ORE_START_LOC, ore.npcInstance?.location) + + ore.tick() + val expectedLoc = BFBeltOre.ORE_START_LOC.transform(0, -1, 0) + Assertions.assertEquals(expectedLoc, ore.location) + Assertions.assertEquals(expectedLoc, ore.npcInstance?.properties?.teleportLocation) + + TestUtils.advanceTicks(1, false) + ore.tick() + Assertions.assertEquals(expectedLoc, ore.npcInstance?.location) + } + } + + @Test fun oreShouldBeAddedToOreContainerAfterReachingEnd() { + TestUtils.getMockPlayer("bf-oremoveadd-test").use { p -> + p.location = BFBeltOre.ORE_START_LOC + val ore = BlastFurnace.addOreToBelt(p, Items.IRON_ORE_440, 5) + + for (i in 0 until 4) + if (ore.tick()) BlastFurnace.getPlayerState(p).oresOnBelt.remove(ore) + + val container = BlastFurnace.getOreContainer(p) + Assertions.assertEquals(5, container.getOreAmount(Items.IRON_ORE_440)) + Assertions.assertEquals(0, BlastFurnace.getAmountOnBelt(p, Items.IRON_ORE_440)) + } + } +} \ No newline at end of file diff --git a/Server/src/test/kotlin/content/activity/blastfurnace/BFPlayerStateTests.kt b/Server/src/test/kotlin/content/activity/blastfurnace/BFPlayerStateTests.kt new file mode 100644 index 000000000..cafd2694d --- /dev/null +++ b/Server/src/test/kotlin/content/activity/blastfurnace/BFPlayerStateTests.kt @@ -0,0 +1,101 @@ +package content.activity.blastfurnace + +import TestUtils +import content.global.skill.smithing.smelting.Bar +import content.minigame.blastfurnace.BFPlayerState +import content.minigame.blastfurnace.BlastFurnace +import core.api.addItem +import core.api.amountInInventory +import core.api.setVarbit +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test +import org.rs09.consts.Items + +class BFPlayerStateTests { + init { TestUtils.preTestSetup() } + + @Test fun processOreIntoBarsShouldDoNothingIfBarsNotCooled() { + TestUtils.getMockPlayer("bf-barsnotcooled").use { p -> + val state = BlastFurnace.getPlayerState(p) + state.container.addOre(Items.IRON_ORE_440, 28) + Assertions.assertEquals(true, state.processOresIntoBars()) + Assertions.assertEquals(28, state.container.getBarAmount(Bar.IRON)) + + state.container.addCoal(40) + state.container.addOre(Items.RUNITE_ORE_451, 10) + Assertions.assertEquals(false, state.processOresIntoBars()) + + Assertions.assertEquals(0, state.container.getBarAmount(Bar.RUNITE)) + } + } + + @Test fun processOreIntoBarsShouldDoNothingIfBarsUnchecked() { + TestUtils.getMockPlayer("bf-barsnotchecked").use { p -> + val state = BlastFurnace.getPlayerState(p) + state.container.addOre(Items.IRON_ORE_440, 28) + Assertions.assertEquals(true, state.processOresIntoBars()) + state.coolBars() + + state.container.addCoal(40) + state.container.addOre(Items.RUNITE_ORE_451, 10) + Assertions.assertEquals(false, state.processOresIntoBars()) + Assertions.assertEquals(0, state.container.getBarAmount(Bar.RUNITE)) + + state.checkBars() + Assertions.assertEquals(true, state.processOresIntoBars()) + Assertions.assertEquals(10, state.container.getBarAmount(Bar.RUNITE)) + } + } + + @Test fun shouldBeAbleToClaimBars() { + TestUtils.getMockPlayer("bf-barclaiminig").use { p -> + val state = BlastFurnace.getPlayerState(p) + state.container.addOre(Items.IRON_ORE_440, 28) + Assertions.assertEquals(true, state.processOresIntoBars()) + state.coolBars() + + Assertions.assertEquals(true, state.claimBars(Bar.IRON, 5)) + Assertions.assertEquals(5, amountInInventory(p, Items.IRON_BAR_2351)) + Assertions.assertEquals(23, state.container.getBarAmount(Bar.IRON)) + } + } + + @Test fun shouldNotBeAbleToClaimMoreBarsThanFreeInventorySlots() { + TestUtils.getMockPlayer("bf-claimbarslessslots").use { p -> + val state = BlastFurnace.getPlayerState(p) + state.container.addOre(Items.IRON_ORE_440, 28) + Assertions.assertEquals(true, state.processOresIntoBars()) + state.coolBars() + + addItem(p, Items.ABYSSAL_WHIP_4151, 27) + + Assertions.assertEquals(true, state.claimBars(Bar.IRON, 5)) + Assertions.assertEquals(1, amountInInventory(p, Items.IRON_BAR_2351)) + Assertions.assertEquals(27, state.container.getBarAmount(Bar.IRON)) + } + } + + @Test fun claimBarsShouldDoNothingAndGrantNoItemIfInventoryFull() { + TestUtils.getMockPlayer("bf-claimbarsnoslots").use { p -> + val state = BlastFurnace.getPlayerState(p) + state.container.addOre(Items.IRON_ORE_440, 28) + Assertions.assertEquals(true, state.processOresIntoBars()) + state.coolBars() + + addItem(p, Items.ABYSSAL_WHIP_4151, 28) + + Assertions.assertEquals(false, state.claimBars(Bar.IRON, 5)) + Assertions.assertEquals(0, amountInInventory(p, Items.IRON_BAR_2351)) + Assertions.assertEquals(28, state.container.getBarAmount(Bar.IRON)) + } + } + + @Test fun claimBarsShouldDoNothingIfBarsNotCooled() { + TestUtils.getMockPlayer("bf-claimbarsnotcooled").use { p -> + val state = BlastFurnace.getPlayerState(p) + state.container.addOre(Items.IRON_ORE_440, 28) + Assertions.assertEquals(true, state.processOresIntoBars()) + Assertions.assertEquals(false, state.claimBars(Bar.IRON, 5)) + } + } +} \ No newline at end of file diff --git a/Server/src/test/kotlin/content/activity/blastfurnace/BFSceneryControllerTests.kt b/Server/src/test/kotlin/content/activity/blastfurnace/BFSceneryControllerTests.kt new file mode 100644 index 000000000..046281852 --- /dev/null +++ b/Server/src/test/kotlin/content/activity/blastfurnace/BFSceneryControllerTests.kt @@ -0,0 +1,42 @@ +package content.activity.blastfurnace + +import content.minigame.blastfurnace.BFSceneryController +import core.api.getScenery +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test + +class BFSceneryControllerTests { + init { TestUtils.preTestSetup(); BFSceneryController().resetAllScenery() } + + @Test + fun updateBrokenShouldReplaceWithCorrectVariants() { + val scenCont = BFSceneryController() + + scenCont.updateBreakable(true, true, true, true) + Assertions.assertEquals(BFSceneryController.BROKEN_BELT, getScenery(BFSceneryController.beltGearRight)?.id) + Assertions.assertEquals(BFSceneryController.BROKEN_COG, getScenery(BFSceneryController.cogRightLoc)?.id) + Assertions.assertEquals(BFSceneryController.BROKEN_POT_PIPE, getScenery(BFSceneryController.potPipeLoc)?.id) + Assertions.assertEquals(BFSceneryController.BROKEN_PUMP_PIPE, getScenery(BFSceneryController.pumpPipeLoc)?.id) + + scenCont.updateBreakable(false, false, false, false) + Assertions.assertEquals(BFSceneryController.DEFAULT_BELT, getScenery(BFSceneryController.beltGearRight)?.id) + Assertions.assertEquals(BFSceneryController.DEFAULT_COG, getScenery(BFSceneryController.cogRightLoc)?.id) + Assertions.assertEquals(BFSceneryController.DEFAULT_POT_PIPE, getScenery(BFSceneryController.potPipeLoc)?.id) + Assertions.assertEquals(BFSceneryController.DEFAULT_PUMP_PIPE, getScenery(BFSceneryController.pumpPipeLoc)?.id) + } + + @Test + fun stoveIdShouldCorrespondToTemperature() { + val testData = arrayOf( + Pair(0, BFSceneryController.STOVE_COLD), + Pair(40, BFSceneryController.STOVE_WARM), + Pair(80, BFSceneryController.STOVE_HOT) + ) + for ((temp, expectedId) in testData) { + val cont = BFSceneryController() + cont.resetAllScenery() + cont.updateStove(temp) + Assertions.assertEquals(expectedId, getScenery(BFSceneryController.stoveLoc)!!.id) + } + } +} \ No newline at end of file diff --git a/Server/src/test/kotlin/content/activity/blastfurnace/BlastFurnaceAreaTests.kt b/Server/src/test/kotlin/content/activity/blastfurnace/BlastFurnaceAreaTests.kt new file mode 100644 index 000000000..8926443f1 --- /dev/null +++ b/Server/src/test/kotlin/content/activity/blastfurnace/BlastFurnaceAreaTests.kt @@ -0,0 +1,245 @@ +package content.activity.blastfurnace + +import TestUtils +import content.minigame.blastfurnace.BlastFurnace +import content.minigame.blastfurnace.BlastFurnaceListeners +import content.minigame.blastfurnace.BlastConsts +import core.ServerConstants +import core.api.addItem +import core.api.amountInInventory +import core.game.node.entity.skill.Skills +import core.game.world.map.Location +import core.game.world.map.RegionManager +import org.json.simple.JSONObject +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test +import org.rs09.consts.Items + +class BlastFurnaceAreaTests { + @Test fun shouldBeAbleToEnterBfArea() { + TestUtils.getMockPlayer("bf-enterable").use { p -> + p.skills.setStaticLevel(Skills.SMITHING, 60) + p.location = Location(2932, 10195, 0) + TestUtils.simulateInteraction(p, RegionManager.getObject(BlastConsts.STAIRLOC_ENTRANCE)!!, 0) + TestUtils.advanceTicks(5, false) + Assertions.assertEquals(BlastConsts.ENTRANCE_LOC, p.location) + } + } + + @Test fun shouldBeAbleToLeaveBfArea() { + TestUtils.getMockPlayer("bf-leavable").use { p -> + p.location = BlastConsts.ENTRANCE_LOC + TestUtils.simulateInteraction(p, RegionManager.getObject(BlastConsts.STAIRLOC_EXIT)!!, 0) + TestUtils.advanceTicks(5, false) + Assertions.assertEquals(BlastConsts.EXIT_LOC, p.location) + } + } + + @Test fun shouldNotBeAbleToEnterWithLessThan60Smithing() { + TestUtils.getMockPlayer("bf-60smith").use { p -> + p.location = Location(2932, 10195, 0) + TestUtils.simulateInteraction(p, RegionManager.getObject(BlastConsts.STAIRLOC_ENTRANCE)!!, 0) + TestUtils.advanceTicks(5, false) + Assertions.assertNotEquals(BlastConsts.ENTRANCE_LOC, p.location) + } + } + + @Test fun getFeePriceReturnsExpectedValues() { + val testData = arrayOf( + Triple(false, 10, 2500), + Triple(true, 10, 1250), + Triple(false, 60, 0), + Triple(true, 60, 0), + Triple(false, 59, 2500), + Triple(true, 59, 1250) + ) + for ((hasCharos, smithLevel, expected) in testData) + Assertions.assertEquals(expected, BlastFurnace.getEntranceFee(hasCharos, smithLevel)) + } + + @Test fun enterWithFeeShouldKickPlayerOutAfter10Minutes() { + TestUtils.getMockPlayer("bf-fee-kickout").use { p -> + BlastFurnace.enter(p, true) + TestUtils.advanceTicks(BlastConsts.FEE_ENTRANCE_DURATION + 2, false) + Assertions.assertNotEquals(BlastConsts.ENTRANCE_LOC, p.location) + } + } + + @Test fun shouldBeAbleToLeaveAndEnterFreelyWhileTimerActive() { + TestUtils.getMockPlayer("bf-fee-reentry").use { p -> + BlastFurnace.enter(p, true) + TestUtils.advanceTicks(2, false) + Assertions.assertEquals(BlastConsts.ENTRANCE_LOC, p.location) + TestUtils.simulateInteraction(p, RegionManager.getObject(BlastConsts.STAIRLOC_EXIT)!!, 0) + TestUtils.advanceTicks(2, false) + Assertions.assertEquals(BlastConsts.EXIT_LOC, p.location) + TestUtils.simulateInteraction(p, RegionManager.getObject(BlastConsts.STAIRLOC_ENTRANCE)!!, 0) + TestUtils.advanceTicks(2, false) + Assertions.assertEquals(BlastConsts.ENTRANCE_LOC, p.location) + + p.location = BlastConsts.EXIT_LOC + TestUtils.advanceTicks(BlastConsts.FEE_ENTRANCE_DURATION, false) + //should not allow free reentry if timer has run out + TestUtils.simulateInteraction(p, RegionManager.getObject(BlastConsts.STAIRLOC_ENTRANCE)!!, 0) + TestUtils.advanceTicks(2, false) + Assertions.assertEquals(BlastConsts.EXIT_LOC, p.location) + } + } + + @Test fun playerShouldOnlyBeTeleportedIfInsideBFArea() { + TestUtils.getMockPlayer("bf-fee-timeout-notele-outside-bf").use { p -> + BlastFurnace.enter(p, true) + TestUtils.advanceTicks(2, false) + Assertions.assertEquals(BlastConsts.ENTRANCE_LOC, p.location) + + p.location = ServerConstants.HOME_LOCATION + + TestUtils.advanceTicks(BlastConsts.FEE_ENTRANCE_DURATION, false) + Assertions.assertEquals(ServerConstants.HOME_LOCATION, p.location) + } + } + + @Test fun playerShouldBeAbleToPlaceOreOnBelt() { + TestUtils.getMockPlayer("bf-placeoreonbelt").use { p -> + addItem(p, Items.COAL_453, 2) + BlastFurnace.placeAllOre(p) + Assertions.assertEquals(2, BlastFurnace.getAmountOnBelt(p, Items.COAL_453)) + Assertions.assertEquals(0, amountInInventory(p, Items.COAL_453)) + } + } + + @Test fun playerShouldNotBeAbleToPlaceMoreOreThanCanFitOnBelt() { + TestUtils.getMockPlayer("bf-toomuchoreonbelt").use { p -> + val cont = BlastFurnace.getOreContainer(p) + cont.addCoal(BlastConsts.COAL_LIMIT - 15) + cont.addOre(Items.IRON_ORE_440, BlastConsts.ORE_LIMIT - 13) + + addItem(p, Items.COAL_453, 15) + addItem(p, Items.IRON_ORE_440, 13) + BlastFurnace.placeAllOre(p) + + addItem(p, Items.COAL_453, 15) + addItem(p, Items.IRON_ORE_440, 13) + BlastFurnace.placeAllOre(p) + + Assertions.assertEquals(15, amountInInventory(p, Items.COAL_453)) + Assertions.assertEquals(13, amountInInventory(p, Items.IRON_ORE_440)) + } + } + + @Test fun beltOreLimitsShouldAccountForCreatedBars() { + TestUtils.getMockPlayer("bf-baraccountedfor").use { p -> + val cont = BlastFurnace.getOreContainer(p) + cont.addOre(Items.IRON_ORE_440, BlastConsts.ORE_LIMIT - 15) + cont.convertToBars() + + addItem(p, Items.IRON_ORE_440, 20) + BlastFurnace.placeAllOre(p) + + Assertions.assertEquals(5, amountInInventory(p, Items.IRON_ORE_440)) + } + } + + @Test fun playerShouldBeAbleToOnlyPlaceSpecificOreOnBelt() { + TestUtils.getMockPlayer("bf-specific-oreplace").use { p -> + addItem(p, Items.IRON_ORE_440, 5) + addItem(p, Items.COAL_453, 3) + addItem(p, Items.RUNITE_ORE_451, 10) + BlastFurnace.placeAllOre(p, Items.RUNITE_ORE_451) + Assertions.assertEquals(0, amountInInventory(p, Items.RUNITE_ORE_451)) + Assertions.assertEquals(5, amountInInventory(p, Items.IRON_ORE_440)) + Assertions.assertEquals(3, amountInInventory(p, Items.COAL_453)) + } + } + + @Test fun playerShouldNotBeAbleToPlaceOresTheyLackTheLevelFor() { + TestUtils.getMockPlayer("bf-levelgate-oreplace").use { p -> + addItem(p, Items.RUNITE_ORE_451, 10) + BlastFurnace.placeAllOre(p, Items.RUNITE_ORE_451, accountForSkill = true) + Assertions.assertEquals(10, amountInInventory(p, Items.RUNITE_ORE_451)) + } + } + + @Test fun shouldNotBeAbleToOccupyExtraBronzeSlotsWithMoreThan28TinOrCopper() { + TestUtils.getMockPlayer("bf-bronze-orelimit").use { p -> + //Edge case - bronze bars have an edge case that allows 28 of both {copper, tin}, so this needs to make sure you can't, for example, add 56 copper. + addItem(p, Items.COPPER_ORE_436, 28) + BlastFurnace.placeAllOre(p) + addItem(p, Items.COPPER_ORE_436, 28) + BlastFurnace.placeAllOre(p) + + Assertions.assertEquals(28, BlastFurnace.getAmountOnBelt(p, Items.COPPER_ORE_436)) + Assertions.assertEquals(28, amountInInventory(p, Items.COPPER_ORE_436)) + + p.inventory.clear() + addItem(p, Items.TIN_ORE_438, 28) + BlastFurnace.placeAllOre(p) + addItem(p, Items.TIN_ORE_438, 28) + BlastFurnace.placeAllOre(p) + + Assertions.assertEquals(28, BlastFurnace.getAmountOnBelt(p, Items.TIN_ORE_438)) + Assertions.assertEquals(28, amountInInventory(p, Items.TIN_ORE_438)) + } + } + + @Test fun shouldNotBeAbleToPlaceMoreThan28TotalNonCoalOreOnTheBelt() { + TestUtils.getMockPlayer("bf-orelimit").use { p -> + addItem(p, Items.GOLD_ORE_444, 28) + BlastFurnace.placeAllOre(p) + addItem(p, Items.RUNITE_ORE_451, 28) + BlastFurnace.placeAllOre(p) + + Assertions.assertEquals(28, BlastFurnace.getAmountOnBelt(p, Items.GOLD_ORE_444)) + Assertions.assertEquals(0, amountInInventory(p, Items.GOLD_ORE_444)) + Assertions.assertEquals(0, BlastFurnace.getAmountOnBelt(p, Items.RUNITE_ORE_451)) + Assertions.assertEquals(28, amountInInventory(p, Items.RUNITE_ORE_451)) + } + } + + @Test fun shouldBeAbleToPlace28CopperAndTinOreOnTheBelt() { + TestUtils.getMockPlayer("bf-orelimit").use { p -> + addItem(p, Items.COPPER_ORE_436, 28) + BlastFurnace.placeAllOre(p) + addItem(p, Items.TIN_ORE_438, 28) + BlastFurnace.placeAllOre(p) + + Assertions.assertEquals(28, BlastFurnace.getAmountOnBelt(p, Items.COPPER_ORE_436)) + Assertions.assertEquals(0, amountInInventory(p, Items.COPPER_ORE_436)) + Assertions.assertEquals(28, BlastFurnace.getAmountOnBelt(p, Items.TIN_ORE_438)) + Assertions.assertEquals(0, amountInInventory(p, Items.TIN_ORE_438)) + } + } + + @Test fun BFAreaShouldPersistInfoAcrossPlayerRelogs() { + TestUtils.getMockPlayer("bf-persistence").use { p -> + var container = BlastFurnace.getOreContainer(p) + container.addOre(Items.IRON_ORE_440, 15) + container.addCoal(40) + + BlastFurnace.addOreToBelt(p, Items.IRON_ORE_440, 4) + BlastFurnace.addOreToBelt(p, Items.RUNITE_ORE_451, 10) + BlastFurnace.addOreToBelt(p, Items.COAL_453, 20) + + val area = BlastFurnace() + val saveObj = JSONObject() + area.savePlayer(p, saveObj) + + BlastFurnace.playerStates.clear() + + area.parsePlayer(p, saveObj) + container = BlastFurnace.getOreContainer(p) + Assertions.assertEquals(15, container.getOreAmount(Items.IRON_ORE_440)) + Assertions.assertEquals(40, container.coalAmount()) + Assertions.assertEquals(4, BlastFurnace.getAmountOnBelt(p, Items.IRON_ORE_440)) + Assertions.assertEquals(10, BlastFurnace.getAmountOnBelt(p, Items.RUNITE_ORE_451)) + Assertions.assertEquals(20, BlastFurnace.getAmountOnBelt(p, Items.COAL_453)) + } + } + + companion object { + init { + TestUtils.preTestSetup() + BlastFurnaceListeners().defineListeners() + } + } +} \ No newline at end of file diff --git a/Server/src/test/kotlin/content/activity/blastfurnace/BlastFurnaceOreContainerTests.kt b/Server/src/test/kotlin/content/activity/blastfurnace/BlastFurnaceOreContainerTests.kt new file mode 100644 index 000000000..70db557f4 --- /dev/null +++ b/Server/src/test/kotlin/content/activity/blastfurnace/BlastFurnaceOreContainerTests.kt @@ -0,0 +1,169 @@ +package content.activity.blastfurnace + +import content.global.skill.smithing.smelting.Bar +import content.minigame.blastfurnace.BFOreContainer +import content.minigame.blastfurnace.BlastConsts +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test +import org.rs09.consts.Items + +class BlastFurnaceOreContainerTests { + @Test fun shouldBeAbleToAddCoal() { + val cont = BFOreContainer() + cont.addCoal(15) + Assertions.assertEquals(15, cont.coalAmount()) + } + + @Test fun addCoalShouldReturnExtraAmountIfAddingMoreThanPossible() { + val cont = BFOreContainer() + cont.addCoal(BlastConsts.COAL_LIMIT - 26) + Assertions.assertEquals(2, cont.addCoal(28)) + } + + @Test fun shouldBeAbleToAddOres() { + val cont = BFOreContainer() + cont.addOre (Items.IRON_ORE_440, 20) + Assertions.assertEquals(20, cont.getOreAmount(Items.IRON_ORE_440)) + } + + @Test fun addOreShouldReturnExtraAmountIfAddingMoreThanPossible() { + val cont = BFOreContainer() + cont.addOre(Items.IRON_ORE_440, BlastConsts.ORE_LIMIT - 2) + Assertions.assertEquals(3, cont.addOre(Items.IRON_ORE_440, 5)) + } + + @Test fun addOreShouldReturnExtraAmountWhenAddingMoreCopperOrTinThanPossible() { + val contTin = BFOreContainer() + Assertions.assertEquals(28, contTin.addOre(Items.TIN_ORE_438, 56)) + + val contCopper = BFOreContainer() + Assertions.assertEquals(28, contCopper.addOre(Items.COPPER_ORE_436, 56)) + } + + @Test fun convertToBarsShouldYieldExpectedResults() { + data class Data (val coalAmount: Int, val oreAmount: Array>, val expectedOreAmounts: Array>, val expectedCoalAmount: Int, val expectedBarResult: Array>) + val testData = arrayOf( + Data( //standard case - 20 iron with no coal creates 20 iron bars + 0, arrayOf(Pair(Items.IRON_ORE_440, 20)), + arrayOf(Pair(Items.IRON_ORE_440, 0)), 0, arrayOf(Pair(Bar.IRON, 20)) + ), + Data ( //edge case - 20 iron with 10 coal produces 10 steel and 10 iron + 10, arrayOf(Pair(Items.IRON_ORE_440, 20)), + arrayOf(Pair(Items.IRON_ORE_440, 0)), 0, arrayOf(Pair(Bar.STEEL, 10), Pair(Bar.IRON, 10)) + ), + Data ( //standard case - 20 coal with 10 mithril produces 10 mithril bars + 20, arrayOf(Pair(Items.MITHRIL_ORE_447, 10)), + arrayOf(Pair(Items.MITHRIL_ORE_447, 0)), 0, arrayOf(Pair(Bar.MITHRIL, 10)) + ), + Data ( //standard case - 30 coal with 10 adamantite produces 10 addy bars + 30, arrayOf(Pair(Items.ADAMANTITE_ORE_449, 10)), + arrayOf(Pair(Items.ADAMANTITE_ORE_449, 0)), 0, arrayOf(Pair(Bar.ADAMANT, 10)) + ), + Data ( //standard case - 40 coal with 10 runite produces 10 runite bars + 40, arrayOf(Pair(Items.RUNITE_ORE_451, 10)), + arrayOf(Pair(Items.RUNITE_ORE_451, 0)), 0, arrayOf(Pair(Bar.RUNITE, 10)) + ), + Data ( //semi-edge case - 28 gold with 150 coal produces 28 gold bars with 150 coal remaining (doesn't use any coal when not needed) + 150, arrayOf(Pair(Items.GOLD_ORE_444, 28)), + arrayOf(Pair(Items.GOLD_ORE_444, 0)), 150, arrayOf(Pair(Bar.GOLD, 28)) + ), + Data ( //edge case - 18 silver and 10 runite with 58 coal produces 18 silver bars and 10 runite bars with 18 coal remaining + 58, arrayOf(Pair(Items.SILVER_ORE_442, 18), Pair(Items.RUNITE_ORE_451, 10)), + arrayOf(Pair(Items.SILVER_ORE_442, 0), Pair(Items.RUNITE_ORE_451, 0)), 18, arrayOf(Pair(Bar.SILVER, 18), Pair(Bar.RUNITE, 10)) + ), + Data ( //edge case - only 20 coal but 10 runite (half of what's needed) produces 5 runite bars, with 5 runite ore and 0 coal remaining. + 20, arrayOf(Pair(Items.RUNITE_ORE_451, 10)), + arrayOf(Pair(Items.RUNITE_ORE_451, 5)), 0, arrayOf(Pair(Bar.RUNITE, 5)) + ), + Data (//technically an edge case - 28 copper and 28 tin makes 28 bronze with nothing leftover. + 0, arrayOf(Pair(Items.COPPER_ORE_436, 28), Pair(Items.TIN_ORE_438, 28)), + arrayOf(Pair(Items.COPPER_ORE_436, 0), Pair(Items.TIN_ORE_438, 0)), 0, arrayOf(Pair(Bar.BRONZE, 28)) + ), + Data (//edge case - 10 copper and no tin makes nothing with 10 copper leftover + 0, arrayOf(Pair(Items.COPPER_ORE_436, 10)), + arrayOf(Pair(Items.COPPER_ORE_436, 10)), 0, arrayOf(Pair(Bar.BRONZE, 0)) + ), + Data (//edge case - 14 copper and 5 tin make 5 bronze bars with 9 copper leftover + 0, arrayOf(Pair(Items.COPPER_ORE_436, 14), Pair(Items.TIN_ORE_438, 5)), + arrayOf(Pair(Items.COPPER_ORE_436, 9), Pair(Items.TIN_ORE_438, 0)), 0, arrayOf(Pair(Bar.BRONZE, 5)) + ) + ) + + var index = 0 + for ((initialCoal, initialOres, expectedOres, expectedCoal, expectedBars) in testData) { + val cont = BFOreContainer() + cont.addCoal(initialCoal) + for ((ore, amount) in initialOres) cont.addOre(ore, amount) + cont.convertToBars() + + for ((ore, amount) in expectedOres) + Assertions.assertEquals(amount, cont.getOreAmount(ore), "Problem testcase was $index - Missing $ore") + for ((bar, amount) in expectedBars) + Assertions.assertEquals(amount, cont.getBarAmount(bar), "Problem testcase was $index - Missing ${bar.name}") + Assertions.assertEquals(expectedCoal, cont.coalAmount(), "Problem testcase was $index") + index++ + } + } + + @Test fun convertToBarsShouldNotConsumeMaterialsForAlreadyFilledBarType() { + val cont = BFOreContainer() + cont.addOre(Items.IRON_ORE_440, 28) + cont.convertToBars() + Assertions.assertEquals(28, cont.getBarAmount(Bar.IRON)) + + cont.addOre(Items.IRON_ORE_440, 28) + cont.convertToBars() + Assertions.assertEquals(28, cont.getBarAmount(Bar.IRON)) + Assertions.assertEquals(28, cont.getOreAmount(Items.IRON_ORE_440)) + } + + @Test fun oreContainerShouldCleanlySerializeAndDeserializeFromJson() { + val cont = BFOreContainer() + cont.addOre(Items.IRON_ORE_440, 28) + cont.convertToBars() + + cont.addOre(Items.RUNITE_ORE_451, 15) + cont.addOre(Items.MITHRIL_ORE_447, 13) + cont.addCoal(150) + + val json = cont.toJson() + val deserialized = BFOreContainer.fromJson(json) + + Assertions.assertEquals(28, deserialized.getBarAmount(Bar.IRON)) + Assertions.assertEquals(15, cont.getOreAmount(Items.RUNITE_ORE_451)) + Assertions.assertEquals(13, cont.getOreAmount(Items.MITHRIL_ORE_447)) + Assertions.assertEquals(150, cont.coalAmount()) + } + + @Test fun shouldBeAbleToRemoveBars() { + val cont = BFOreContainer() + cont.addOre(Items.IRON_ORE_440, 28) + cont.convertToBars() + + val bars = cont.takeBars(Bar.IRON, 15) + Assertions.assertEquals(15, bars?.amount) + Assertions.assertEquals(13, cont.getBarAmount(Bar.IRON)) + } + + @Test fun shouldNotBeAbleToRemoveMoreBarsThanPossible() { + val cont = BFOreContainer() + cont.addOre(Items.IRON_ORE_440, 28) + cont.convertToBars() + + val bars = cont.takeBars(Bar.IRON, 50) + Assertions.assertEquals(28, bars?.amount) + Assertions.assertEquals(0, cont.getBarAmount(Bar.IRON)) + } + + @Test fun convertToBarsShouldReturnXPReward() { + val cont = BFOreContainer() + cont.addOre(Items.IRON_ORE_440, 28) + + Assertions.assertEquals(350.0, cont.convertToBars()) + } + + @Test fun removingBarsWithNoStockReturnsNull() { + val cont = BFOreContainer() + Assertions.assertEquals(null, cont.takeBars(Bar.RUNITE, 1)) + } +} \ No newline at end of file diff --git a/Server/src/test/kotlin/content/activity/blastfurnace/BlastStateTests.kt b/Server/src/test/kotlin/content/activity/blastfurnace/BlastStateTests.kt new file mode 100644 index 000000000..02b959450 --- /dev/null +++ b/Server/src/test/kotlin/content/activity/blastfurnace/BlastStateTests.kt @@ -0,0 +1,95 @@ +package content.activity.blastfurnace + +import content.minigame.blastfurnace.BlastState +import content.minigame.blastfurnace.BlastConsts +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test + +class BlastStateTests { + @Test + fun tickPassedWithCokeInStoveShouldIncreaseStoveTemp() { + val state = BlastState(); state.disableBreaking = true + state.addCoke(1) + Assertions.assertEquals(0, state.stoveTemp) + + state.tick(true, false) + Assertions.assertEquals(1, state.stoveTemp) + } + + @Test + fun stoveTempShouldNeverExceed100() { + val state = BlastState(); state.disableBreaking = true + state.addCoke(BlastConsts.COKE_LIMIT) + + for (i in 0 until 150) + state.tick(true, false) + Assertions.assertEquals(100, state.stoveTemp) + } + + @Test fun cokeShouldDisappearFromStove() { + val state = BlastState(); state.disableBreaking = true + state.addCoke(1) + for (i in 0 until 10) + state.tick(true, false) + Assertions.assertEquals(0, state.cokeInStove) + } + + @Test fun stoveTempShouldLowerWithoutCoke() { + val state = BlastState(); state.disableBreaking = true + state.addCoke(1) + for (i in 0 until 10) + state.tick(true, false) + Assertions.assertEquals(10, state.stoveTemp) + for (i in 0 until 10) + state.tick(true, false) + Assertions.assertEquals(0, state.stoveTemp) + } + + @Test + fun stoveTempShouldNeverGoBelow0() { + val state = BlastState(); state.disableBreaking = true + for (i in 0 until 100) + state.tick(true, false) + Assertions.assertEquals(0, state.stoveTemp) + } + + @Test fun furnaceTempShouldIncreaseProportionallyToStoveTempWhilePumping() { + val testData = arrayOf( + Pair(0, 0), + Pair(1, 1), + Pair(4, 2), + Pair(7, 3) + ) + for ((cokeToAdd, expectedTempRise) in testData) { + val state = BlastState(); state.disableBreaking = true + state.addCoke(cokeToAdd) + for (i in 0 until state.cokeInStove * 10) + state.tick(false, false) + + state.tick(true, false) + Assertions.assertEquals(expectedTempRise, state.furnaceTemp) + } + } + + @Test fun pumpingShouldNotTransferHeatIfPipesBroken() { + val state1 = BlastState(); state1.disableBreaking = true + state1.addCoke(BlastConsts.COKE_LIMIT) + state1.pumpPipeBroken = true + for (i in 0 until 20) state1.tick(true, false) + Assertions.assertEquals(0, state1.furnaceTemp) + + val state2 = BlastState(); state2.disableBreaking = true + state2.addCoke(BlastConsts.COKE_LIMIT) + state2.potPipeBroken = true + for (i in 0 until 20) state2.tick(true, false) + Assertions.assertEquals(0, state2.furnaceTemp) + } + + @Test fun pumpingShouldNotTransferHeatIfBeltBroken() { + val state = BlastState(); state.disableBreaking = true + state.addCoke(BlastConsts.COKE_LIMIT) + state.beltBroken = true + for (i in 0 until 20) state.tick(true, false) + Assertions.assertEquals(0, state.furnaceTemp) + } +} \ No newline at end of file diff --git a/Server/src/test/kotlin/content/familiar/special/BloodDrainTests.kt b/Server/src/test/kotlin/content/familiar/special/BloodDrainTests.kt new file mode 100644 index 000000000..9bdc4584c --- /dev/null +++ b/Server/src/test/kotlin/content/familiar/special/BloodDrainTests.kt @@ -0,0 +1,117 @@ +package content.familiar.special + +import TestUtils +import content.global.skill.summoning.familiar.BloatedLeechNPC +import content.global.skill.summoning.familiar.FamiliarSpecial +import core.ServerConstants +import core.api.* +import core.game.node.entity.skill.Skills +import core.game.system.timer.TimerRegistry +import core.game.system.timer.impl.Disease +import core.game.system.timer.impl.Poison +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test +import org.rs09.consts.Items +import org.rs09.consts.NPCs + +class BloodDrainTests { + init { + TestUtils.preTestSetup() + } + @Test fun bloodDrainShouldNotRestorePrayer() { + TestUtils.getMockPlayer("bloodDrainPrayer").use { p -> + p.skills.setStaticLevel(Skills.PRAYER, 99) + p.skills.prayerPoints = 5.0 + addItem(p, Items.BLOOD_DRAIN_SCROLL_12444) + + val npc = BloatedLeechNPC(p, NPCs.BLOATED_LEECH_6843) + npc.location = ServerConstants.HOME_LOCATION + Assertions.assertEquals(true, npc.executeSpecialMove(FamiliarSpecial(p))) + Assertions.assertEquals(5.0, p.skills.prayerPoints) + } + } + + @Test fun bloodDrainShouldDamageOwner() { + TestUtils.getMockPlayer("bloodDrainHealth").use {p -> + addItem(p, Items.BLOOD_DRAIN_SCROLL_12444) + val npc = BloatedLeechNPC(p, NPCs.BLOATED_LEECH_6843) + npc.location = ServerConstants.HOME_LOCATION + + val npcBaseline = npc.skills.lifepoints + val ownerBaseline = p.skills.lifepoints + + Assertions.assertEquals(true, npc.executeSpecialMove(FamiliarSpecial(p))) + TestUtils.advanceTicks(1, false) + + Assertions.assertEquals(npcBaseline, npc.skills.lifepoints) + Assertions.assertEquals(true, p.skills.lifepoints < ownerBaseline) + } + } + + @Test fun bloodDrainShouldNotHeal() { + TestUtils.getMockPlayer("bloodDrainHealth2").use {p -> + addItem(p, Items.BLOOD_DRAIN_SCROLL_12444) + p.skills.lifepoints = 5 + + val npc = BloatedLeechNPC(p, NPCs.BLOATED_LEECH_6843) + npc.location = ServerConstants.HOME_LOCATION + + val ownerBaseline = p.skills.lifepoints + + Assertions.assertEquals(true, npc.executeSpecialMove(FamiliarSpecial(p))) + TestUtils.advanceTicks(1, false) + + Assertions.assertEquals(true, p.skills.lifepoints < ownerBaseline) + } + } + + @Test fun bloodDrainShouldOnlyRestorePercentOfStats() { + TestUtils.getMockPlayer("bloodDrainStats").use { p -> + addItem(p, Items.BLOOD_DRAIN_SCROLL_12444) + val npc = BloatedLeechNPC(p, NPCs.BLOATED_LEECH_6843) + npc.location = ServerConstants.HOME_LOCATION + + p.skills.setStaticLevel(Skills.STRENGTH, 99) + p.skills.setStaticLevel(Skills.FARMING, 99) + p.skills.setLevel(Skills.STRENGTH, 1) + p.skills.setLevel(Skills.FARMING, 1) + + Assertions.assertEquals(true,npc.executeSpecialMove(FamiliarSpecial(p))) + Assertions.assertEquals(21, p.skills.getLevel(Skills.STRENGTH)) + Assertions.assertEquals(21, p.skills.getLevel(Skills.FARMING)) + } + } + + @Test fun bloodDrainShouldNotBoostStats() { + TestUtils.getMockPlayer("bloodDrainStats2").use { p -> + addItem(p, Items.BLOOD_DRAIN_SCROLL_12444) + val npc = BloatedLeechNPC(p, NPCs.BLOATED_LEECH_6843) + npc.location = ServerConstants.HOME_LOCATION + + p.skills.setStaticLevel(Skills.STRENGTH, 99) + p.skills.setStaticLevel(Skills.FARMING, 99) + p.skills.setLevel(Skills.STRENGTH, 98) + p.skills.setLevel(Skills.FARMING, 98) + + Assertions.assertEquals(true,npc.executeSpecialMove(FamiliarSpecial(p))) + Assertions.assertEquals(99, p.skills.getLevel(Skills.STRENGTH)) + Assertions.assertEquals(99, p.skills.getLevel(Skills.FARMING)) + } + } + + @Test fun bloodDrainCuresPoisonAndDisease() { + TestUtils.getMockPlayer("bloodDrainAilments").use { p -> + addItem(p, Items.BLOOD_DRAIN_SCROLL_12444) + val npc = BloatedLeechNPC(p, NPCs.BLOATED_LEECH_6843) + npc.location = ServerConstants.HOME_LOCATION + + applyPoison(p, p, 40) + Assertions.assertNotNull(getOrStartTimer(p, 40)) + Assertions.assertEquals(true, hasTimerActive(p)) + Assertions.assertEquals(true, hasTimerActive(p)) + Assertions.assertEquals(true, npc.executeSpecialMove(FamiliarSpecial(p))) + Assertions.assertEquals(false, hasTimerActive(p)) + Assertions.assertEquals(false, hasTimerActive(p)) + } + } +} diff --git a/Server/src/test/kotlin/content/skill/agility/ShortcutTests.kt b/Server/src/test/kotlin/content/skill/agility/ShortcutTests.kt new file mode 100644 index 000000000..c8d95b78d --- /dev/null +++ b/Server/src/test/kotlin/content/skill/agility/ShortcutTests.kt @@ -0,0 +1,46 @@ +package content.skill.agility + +import content.global.skill.agility.shortcuts.StileShortcut +import core.game.world.map.Direction +import core.game.world.map.Location +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test + +class ShortcutTests { + @Test fun stileGetInteractionLocShouldReturnCorrectLoc() { + data class StileTestData (val playerLoc: Location, val expectedLoc: Location, val stileLoc: Location, val stileOrientation: StileShortcut.Orientation) + val testData = arrayOf( + //Horizontal + StileTestData(Location.create(3044, 3308, 0), Location.create(3045, 3305, 0), Location.create(3043, 3305, 0), StileShortcut.Orientation.Horizontal), + StileTestData(Location.create(3048, 3303, 0), Location.create(3045, 3305, 0), Location.create(3043, 3305, 0), StileShortcut.Orientation.Horizontal), + StileTestData(Location.create(3043, 3307, 0), Location.create(3042, 3305, 0), Location.create(3043, 3305, 0), StileShortcut.Orientation.Horizontal), + StileTestData(Location.create(3039, 3301, 0), Location.create(3042, 3305, 0), Location.create(3043, 3305, 0), StileShortcut.Orientation.Horizontal), + //Vertical + StileTestData(Location.create(3203, 3276, 0), Location.create(3197, 3275, 0), Location.create(3197, 3276, 0), StileShortcut.Orientation.Vertical), + StileTestData(Location.create(3195, 3274, 0), Location.create(3197, 3275, 0), Location.create(3197, 3276, 0), StileShortcut.Orientation.Vertical), + StileTestData(Location.create(3191, 3280, 0), Location.create(3197, 3278, 0), Location.create(3197, 3276, 0), StileShortcut.Orientation.Vertical), + StileTestData(Location.create(3206, 3281, 0), Location.create(3197, 3278, 0), Location.create(3197, 3276, 0), StileShortcut.Orientation.Vertical) + ) + + for ((pLoc, expLoc, sLoc, ori) in testData) { + Assertions.assertEquals(expLoc, StileShortcut.getInteractLocation(pLoc, sLoc, ori)) + } + } + + @Test fun stileGetOrientationShouldReturnCorrectOrientation() { + val testData = arrayOf( + Pair (Direction.NORTH, StileShortcut.Orientation.Vertical), + Pair (Direction.SOUTH, StileShortcut.Orientation.Vertical), + Pair (Direction.NORTH_WEST, StileShortcut.Orientation.Vertical), + Pair (Direction.NORTH_EAST, StileShortcut.Orientation.Vertical), + Pair (Direction.SOUTH_EAST, StileShortcut.Orientation.Vertical), + Pair (Direction.SOUTH_WEST, StileShortcut.Orientation.Vertical), + Pair (Direction.EAST, StileShortcut.Orientation.Horizontal), + Pair (Direction.WEST, StileShortcut.Orientation.Horizontal) + ) + + for ((dir, expOri) in testData) { + Assertions.assertEquals(expOri, StileShortcut.getOrientation(dir)) + } + } +} \ No newline at end of file diff --git a/Server/src/test/kotlin/content/skill/farming/PatchTests.kt b/Server/src/test/kotlin/content/skill/farming/PatchTests.kt new file mode 100644 index 000000000..2096b3638 --- /dev/null +++ b/Server/src/test/kotlin/content/skill/farming/PatchTests.kt @@ -0,0 +1,81 @@ +package content.skill.farming + +import TestUtils +import content.global.skill.farming.FarmingPatch +import content.global.skill.farming.PatchRaker +import content.global.skill.farming.PatchType +import content.global.skill.farming.Plantable +import content.global.skill.farming.timers.CropGrowth +import core.cache.def.impl.SceneryDefinition +import core.game.node.entity.skill.Skills +import core.game.system.timer.TimerRegistry +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test + +class PatchTests { + init { TestUtils.preTestSetup() } + @Test fun patchGettingDiseasedOrDyingShouldNotGoIntoInvalidState() { + //Dead/Watered (state | 0x40) and Diseased (state | 0x80) are both invalid stages for limpwurt when fully grown (stage 4/state 32), so we use this as our testcase + //specifically, limpwurt has an issue when the patch is ONLY watered (state | 0x40) OR diseased (state | 0x80), having both (state | 0x40 | 0x80) is fine. + TestUtils.getMockPlayer("invalidPatchState").use {p -> + p.skills.setStaticLevel(Skills.FARMING, 99) + p.skills.setLevel(Skills.FARMING, 99) + + val patch = FarmingPatch.S_FALADOR_FLOWER_C.getPatchFor(p) + patch.plant(Plantable.LIMPWURT_SEED) + + for (i in 0 until 4) { //grow to full growth while making sure we don't die/get diseased + patch.currentGrowthStage = 4 + patch.update() + } + + Assertions.assertEquals(32, patch.getCurrentState()) + + patch.isWatered = true + patch.update() + Assertions.assertEquals(false, patch.isDiseased) + Assertions.assertEquals(false, patch.isDead) + Assertions.assertEquals(false, patch.isWatered) + Assertions.assertEquals(32, patch.getCurrentState()) + + patch.isDiseased = true + patch.updateBit() + Assertions.assertEquals(false, patch.isDiseased) + Assertions.assertEquals(false, patch.isDead) + Assertions.assertEquals(false, patch.isWatered) + Assertions.assertEquals(32, patch.getCurrentState()) + } + } + + @Test fun rakingPatchAtInvalidStagesShouldDoNothing() { + TestUtils.getMockPlayer("Patchraker").use {p -> + val patch = FarmingPatch.S_FALADOR_FLOWER_C.getPatchFor(p) + patch.update() + + Assertions.assertEquals(0, patch.getCurrentState()) //assert that it's in the max weedy state + + PatchRaker.rake(p, patch.patch) + TestUtils.advanceTicks(25, false) + Assertions.assertEquals(3, patch.getCurrentState()) + + PatchRaker.rake(p, patch.patch) + TestUtils.advanceTicks(10, false) + Assertions.assertEquals(3, patch.getCurrentState()) + + patch.plant(Plantable.POTATO_SEED) + val plantedBaseline = patch.getCurrentState() + PatchRaker.rake(p, patch.patch) + TestUtils.advanceTicks(10, false) + Assertions.assertEquals(plantedBaseline, patch.getCurrentState()) + + patch.currentGrowthStage = Plantable.POTATO_SEED.stages + patch.update() + Assertions.assertEquals(true, patch.isGrown()) + + val stateBaseline = patch.getCurrentState() + PatchRaker.rake(p, patch.patch) + TestUtils.advanceTicks(10, false) + Assertions.assertEquals(stateBaseline, patch.getCurrentState()) + } + } +} \ No newline at end of file diff --git a/Server/src/test/kotlin/core/PathfinderTests.kt b/Server/src/test/kotlin/core/PathfinderTests.kt index b2da7a30c..f38eecb68 100644 --- a/Server/src/test/kotlin/core/PathfinderTests.kt +++ b/Server/src/test/kotlin/core/PathfinderTests.kt @@ -2,16 +2,29 @@ package core import TestUtils import content.global.skill.gather.GatheringSkillOptionListeners +import content.global.skill.gather.woodcutting.WoodcuttingListener +import core.api.log +import core.cache.def.impl.NPCDefinition +import core.game.interaction.* import core.game.node.scenery.Scenery import core.game.world.map.Location import core.game.world.map.RegionManager import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Test -import core.game.interaction.IntType -import core.game.interaction.InteractionListeners +import core.game.node.Node +import core.game.node.entity.impl.PulseType +import core.game.node.entity.npc.NPC +import core.game.node.entity.player.Player +import core.game.world.GameWorld +import core.game.world.map.Region +import core.net.packet.PacketProcessor +import core.plugin.ClassScanner +import core.plugin.Plugin +import core.tools.Log +import org.rs09.consts.NPCs class PathfinderTests { - companion object {init {TestUtils.preTestSetup(); GatheringSkillOptionListeners().defineListeners() }} + companion object {init {TestUtils.preTestSetup(); GatheringSkillOptionListeners().defineListeners(); WoodcuttingListener().defineListeners() }; val NPC_TEST_LOC = ServerConstants.HOME_LOCATION!!.transform(2, 10, 0)} @Test fun getOccupiedTilesShouldReturnCorrectSetOfTilesThatAnObjectOccupiesAtAllRotations() { //clay fireplace - 13609 - sizex: 1, sizey: 2 @@ -45,4 +58,203 @@ class PathfinderTests { TestUtils.advanceTicks(20, false) Assertions.assertEquals(Location.create(2722, 3475, 0), p.location) } + + @Test fun movementInteractionShouldTrigger() { + val npc = NPC.create(0, NPC_TEST_LOC) + npc.init() + + var intListenerRan = false + InteractionListeners.add(0, IntType.NPC.ordinal, arrayOf("testoptlistener"), method = {player: Player, node: Node -> + intListenerRan = true + return@add true + }) + + var pluginRan = false + val option = Option("testoption", 4) + val option2 = Option("testoptlistener", 0) + val testHandler = object : OptionHandler() { + override fun newInstance(arg: Any?): Plugin { + NPCDefinition.forId(0).handlers["option:testoption"] = this + return this + } + override fun handle(player: Player?, node: Node?, option: String?): Boolean { + pluginRan = true + return true + } + } + testHandler.newInstance(null) + npc.interaction.set(option) + npc.interaction.set(option2) + option.handler = testHandler + + TestUtils.getMockPlayer("interactionTest").use {p -> + p.location = ServerConstants.HOME_LOCATION + TestUtils.simulateInteraction(p, npc, 0) + TestUtils.advanceTicks(10, false) + Assertions.assertEquals(true, intListenerRan) + p.location = ServerConstants.HOME_LOCATION + TestUtils.simulateInteraction(p, npc, 4) + TestUtils.advanceTicks(10, false) + Assertions.assertEquals(true, pluginRan) + } + } + + @Test fun entityMovingToStationaryNPCShouldNotIdleIndefinitely() { + TestUtils.getMockPlayer("idlenpcdest").use {p -> + val startLoc = ServerConstants.HOME_LOCATION + p.location = startLoc + val npc = NPC.create(0, NPC_TEST_LOC) + npc.isNeverWalks = true + npc.init() + GameWorld.Pulser.submit(object : MovementPulse(p, npc) { + override fun pulse(): Boolean { + return true + } + }) + TestUtils.advanceTicks(10, false) + Assertions.assertNotEquals(startLoc, p.location) + } + } + + @Test fun entityTargetMovementPulseShouldNotStopOnSameTileAsEntity() { + TestUtils.getMockPlayer("entitystoptest").use {p -> + p.location = ServerConstants.HOME_LOCATION + val npc = NPC.create(0, NPC_TEST_LOC) + npc.isNeverWalks = true + npc.init() + GameWorld.Pulser.submit(object : MovementPulse(p, npc) { + override fun pulse(): Boolean { + return true + } + }) + TestUtils.advanceTicks(10, false) + Assertions.assertNotEquals(p.location, npc.location) + Assertions.assertEquals(1.0, p.location.getDistance(npc.location)) + } + } + + @Test fun entityTargetMovementPulseWithExplicitParamsShouldNotStopOnSameTile() { + TestUtils.getMockPlayer("entitystoptest2").use { p -> + p.location = ServerConstants.HOME_LOCATION + val npc = NPC.create(0, NPC_TEST_LOC) + npc.isNeverWalks = true + npc.init() + GameWorld.Pulser.submit(object : MovementPulse(p, npc, DestinationFlag.ENTITY) { + override fun pulse(): Boolean { + return true + } + }) + TestUtils.advanceTicks(10, false) + Assertions.assertNotEquals(p.location, npc.location) + Assertions.assertEquals(1.0, p.location.getDistance(npc.location)) + } + } + + @Test fun doubleMovementPulseToEntityShouldNotStopOnSameTile() { + TestUtils.getMockPlayer("entitystoptest3").use { p -> + p.location = ServerConstants.HOME_LOCATION + val npc = NPC.create(0, NPC_TEST_LOC) + npc.isNeverWalks = true + npc.init() + p.pulseManager.run(object : MovementPulse(p, npc) { + override fun pulse(): Boolean { + return true + } + }) + p.pulseManager.run(object : MovementPulse(p, npc) { + override fun pulse(): Boolean { + return true + } + }, PulseType.STANDARD) + TestUtils.advanceTicks(10, false) + Assertions.assertNotEquals(ServerConstants.HOME_LOCATION, p.location) + Assertions.assertNotEquals(p.location, npc.location) + Assertions.assertEquals(1.0, p.location.getDistance(npc.location)) + } + } + + @Test fun simulatedInteractionPacketWithMovementFromPluginShouldNotEndOnSameTile() { + val testHandler = object : OptionHandler() { + override fun newInstance(arg: Any?): Plugin { + NPCDefinition.forId(0).handlers["option:testoption"] = this + return this + } + override fun handle(player: Player?, node: Node?, option: String?): Boolean { + log(this::class.java, Log.ERR, "Interaction triggered") + return true + } + } + testHandler.newInstance(null) + val npc = NPC.create(0, NPC_TEST_LOC) + val option = Option("testoption", 4) + npc.interaction.set(option) + option.handler = testHandler + + TestUtils.getMockPlayer("entitystoptest4").use { p -> + p.location = ServerConstants.HOME_LOCATION + npc.isNeverWalks = true + npc.init() + TestUtils.simulateInteraction(p, npc, 4) + TestUtils.advanceTicks(20, false) + Assertions.assertNotEquals(ServerConstants.HOME_LOCATION, p.location) + Assertions.assertNotEquals(p.location, npc.location) + Assertions.assertEquals(1.0, p.location.getDistance(npc.location)) + } + } + + @Test fun simulatedInteractionPacketWithMovementFromListenerShouldNotEndOnSameTile() { + val npc = NPC.create(0, NPC_TEST_LOC) + npc.isNeverWalks = true + npc.init() + + InteractionListeners.add(0, IntType.NPC.ordinal, arrayOf("testoptlistener2"), method = {player: Player, node: Node -> + return@add true + }) + val opt = Option("testoptlistener2", 1) + npc.interaction.set(opt) + + TestUtils.getMockPlayer("entitystoptest5").use { p -> + p.location = ServerConstants.HOME_LOCATION + TestUtils.simulateInteraction(p, npc, 1) + TestUtils.advanceTicks(20, false) + Assertions.assertNotEquals(ServerConstants.HOME_LOCATION, p.location) + Assertions.assertNotEquals(p.location, npc.location) + Assertions.assertEquals(1.0, p.location.getDistance(npc.location)) + } + } + + @Test fun npcShouldReliablyReturnToSpawnLocationIfTooFar() { + //spawn a player into the area just to make sure it ticks... + TestUtils.getMockPlayer("areatest").use { p -> + val npc = NPC(1, Location.create(3240, 3226, 0)) + npc.isWalks = true + npc.isNeverWalks = false + npc.walkRadius = 5 + npc.init() + npc.properties.spawnLocation = ServerConstants.HOME_LOCATION + TestUtils.advanceTicks(5, false) + Assertions.assertEquals(true, npc.getAttribute("return-to-spawn", false)) + TestUtils.advanceTicks(50, false) + Assertions.assertEquals(true, npc.location.getDistance(ServerConstants.HOME_LOCATION) <= 9) + } + } + + @Test fun npcShouldReliablyReturnToSpawnEvenIfRegionUnloaded() { + //spawn a player into the area just to make sure it ticks... + TestUtils.getMockPlayer("areaunloadtest").use { p -> + val npc = NPC(1, Location.create(3240, 3226, 0)) + npc.isWalks = true + npc.isNeverWalks = false + npc.walkRadius = 5 + npc.init() + npc.properties.spawnLocation = ServerConstants.HOME_LOCATION + TestUtils.advanceTicks(3, false) + Assertions.assertEquals(true, npc.getAttribute("return-to-spawn", false)) + p.clear() + RegionManager.forId(npc.location.regionId).flagInactive(true) + TestUtils.advanceTicks(50, false) + Assertions.assertEquals(false, npc.getAttribute("return-to-spawn", false)) + Assertions.assertEquals(true, npc.location.getDistance(ServerConstants.HOME_LOCATION) <= 5) + } + } } \ No newline at end of file diff --git a/Server/src/test/kotlin/core/TimerTests.kt b/Server/src/test/kotlin/core/TimerTests.kt new file mode 100644 index 000000000..348439166 --- /dev/null +++ b/Server/src/test/kotlin/core/TimerTests.kt @@ -0,0 +1,99 @@ +package core + +import TestUtils +import core.api.* +import core.game.node.entity.Entity +import core.game.node.entity.skill.Skills +import core.game.system.timer.RSTimer +import core.game.system.timer.TimerFlag +import core.game.system.timer.impl.SkillRestore +import core.tools.Log +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test + +class TimerTests { + init { TestUtils.preTestSetup() } + + @Test fun timerWithNoFlagsShouldNotBeClearedOnDeath() { + TestUtils.getMockPlayer("noflagnoclear").use { p -> + var incrementer = 0 + val timer = object : RSTimer(1) { + override fun run(entity: Entity): Boolean { + incrementer++ + return true + } + } + + registerTimer(p, timer) + impact(p, p.skills.lifepoints) + TestUtils.advanceTicks(TestUtils.PLAYER_DEATH_TICKS, false) + closeInterface(p) //close the interface that opens after death - as it would pause the timer + + TestUtils.advanceTicks(18, false) + Assertions.assertEquals(20, incrementer) + } + } + + @Test fun timerWithClearOnDeathFlagShouldClearOnDeath() { + TestUtils.getMockPlayer("clearflagtimer").use { p -> + var incrementer = 0 + val timer = object : RSTimer(1, flags = arrayOf(TimerFlag.ClearOnDeath)) { + override fun run(entity: Entity): Boolean { + incrementer++ + return true + } + } + + registerTimer(p, timer) + impact(p, p.skills.lifepoints) + TestUtils.advanceTicks(TestUtils.PLAYER_DEATH_TICKS, false) + closeInterface(p) //close the interface that opens after death - as it would pause the timer + + TestUtils.advanceTicks(18, false) + Assertions.assertEquals(2, incrementer) + } + } + + @Test fun skillRestoreTimerShouldSlowlyRaiseLoweredStats() { + TestUtils.getMockPlayer("statrestore-slowrestore").use { p -> + val timer = SkillRestore() + registerTimer(p, timer) + p.skills.staticLevels[Skills.FARMING] = 20 + setTempLevel(p, Skills.FARMING, 10) + + TestUtils.advanceTicks(timer.restoreTicks[Skills.FARMING] + 3, false) + Assertions.assertEquals(11, getDynLevel(p, Skills.FARMING)) + } + } + @Test fun skillRestoreTimerShouldSlowlyLowerBoostedStats() { + TestUtils.getMockPlayer("statrestore-slowdrain").use { p -> + val timer = SkillRestore() + p.timers.registerTimer(timer) + setTempLevel(p, Skills.FARMING, 6) + + TestUtils.advanceTicks(timer.restoreTicks[Skills.FARMING] + 3, false) + Assertions.assertEquals(5, getDynLevel(p, Skills.FARMING)) + } + } + + @Test fun skillRestoreTimerShouldRaiseLoweredHp() { + TestUtils.getMockPlayer("statrestore-raiseloweredhp").use { p -> + val timer = SkillRestore() + registerTimer(p, timer) + p.skills.lifepoints /= 2 + TestUtils.advanceTicks(600, false) + Assertions.assertEquals(10, p.skills.lifepoints) + } + } + + @Test fun skillRestoreTimerShouldNeverLowerBoostedHp() { + TestUtils.getMockPlayer("statrestore-neverlowerhp").use { p -> + val timer = SkillRestore() + registerTimer(p, timer) + p.skills.lifepoints = 50 + + TestUtils.advanceTicks(500, false) + Assertions.assertEquals(50, p.skills.lifepoints) + } + } +} \ No newline at end of file diff --git a/Server/src/test/kotlin/core/auth/DevelopmentAuthenticatorTests.kt b/Server/src/test/kotlin/core/auth/DevelopmentAuthenticatorTests.kt index 05e8051af..e14efb11e 100644 --- a/Server/src/test/kotlin/core/auth/DevelopmentAuthenticatorTests.kt +++ b/Server/src/test/kotlin/core/auth/DevelopmentAuthenticatorTests.kt @@ -1,11 +1,8 @@ package core.auth +import core.storage.InMemoryStorageProvider import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Test -import core.auth.DevelopmentAuthenticator -import core.auth.AuthResponse -import core.auth.UserAccountInfo -import core.storage.InMemoryStorageProvider class DevelopmentAuthenticatorTests { private val authProvider = DevelopmentAuthenticator() 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/world/map/zone/ZoneMonitorTests.kt b/Server/src/test/kotlin/core/game/world/map/zone/ZoneMonitorTests.kt new file mode 100644 index 000000000..a1e733564 --- /dev/null +++ b/Server/src/test/kotlin/core/game/world/map/zone/ZoneMonitorTests.kt @@ -0,0 +1,94 @@ +package core.game.world.map.zone + +import MockPlayer +import core.api.hasTimerActive +import core.api.registerTimer +import core.game.node.entity.player.Player +import core.game.node.item.Item +import core.game.system.timer.impl.Teleblock +import core.game.world.map.Location +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test + +class ZoneMonitorTests { + + val GLORIES = intArrayOf(1710, 1708, 1706, 1704) + + init { + TestUtils.preTestSetup() + } + + /** + * Evalutes success of glory teleport using all glory charged items + */ + fun teleportWithGlory(expected : Boolean, p : Player) { + for (id in GLORIES) { + val glory = Item(id, 1) + Assertions.assertEquals(expected, p.zoneMonitor.teleport(1, glory)) + } + } + + @Nested + inner class GloryTeleportSuccess { + + @Test + fun successOnLevel21Wilderness() { + TestUtils.getMockPlayer("").use {p -> + // level 21 wilderness + p.skullManager.level = 21; + p.location = (Location.create(3067, 3683, 0)) + Assertions.assertTrue(p.locks.isTeleportLocked) + teleportWithGlory(true, p) + } + } + + @Test + fun successOnLevel30Wilderness() { + TestUtils.getMockPlayer("").use {p -> + // level 30 wilderness + p.location = (Location.create(3069, 3755, 0)) + p.skullManager.level = 30; + Assertions.assertTrue(p.locks.isTeleportLocked) + teleportWithGlory(true, p) + } + } + + @Test + fun successOnNoRestrictions() { + TestUtils.getMockPlayer("").use {p -> + // lumbridge + p.location = (Location.create(3222, 3218, 0)) + Assertions.assertFalse(p.locks.isTeleportLocked) + Assertions.assertFalse(hasTimerActive(p, "teleblock")) + Assertions.assertFalse(p.zoneMonitor.isRestricted(ZoneRestriction.TELEPORT)) + teleportWithGlory(true, p) + } + } + + } + + @Nested + inner class GloryTeleportFailure { + + @Test + fun failOnTeleBlock() { + TestUtils.getMockPlayer("").use { p -> + registerTimer(p, Teleblock()) + Assertions.assertTrue(hasTimerActive(p, "teleblock")) + teleportWithGlory(false, p) + } + } + + @Test + fun failOnLock() { + TestUtils.getMockPlayer("").use { p -> + p.locks.lockTeleport(100000) + Assertions.assertTrue(p.locks.isTeleportLocked) + teleportWithGlory(false, p) + } + } + + } + +} 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/kotlin/core/storage/SQLStorageProviderTests.kt b/Server/src/test/kotlin/core/storage/SQLStorageProviderTests.kt deleted file mode 100644 index c88745158..000000000 --- a/Server/src/test/kotlin/core/storage/SQLStorageProviderTests.kt +++ /dev/null @@ -1,188 +0,0 @@ -package core.storage - -import org.junit.jupiter.api.AfterAll -import org.junit.jupiter.api.Assertions -import org.junit.jupiter.api.Test -import core.ServerConstants -import core.auth.UserAccountInfo -import core.tools.SystemLogger -import java.sql.SQLDataException - -class SQLStorageProviderTests { - companion object { - val storage = SQLStorageProvider() - val testAccountNames = ArrayList() - init { - storage.configure("localhost", ServerConstants.DATABASE_NAME!!, ServerConstants.DATABASE_USER!!, ServerConstants.DATABASE_PASS!!) - val details = UserAccountInfo.createDefault() - details.rights = 2 - details.username = "test" - } - - @AfterAll @JvmStatic fun cleanup() { - SystemLogger.logInfo(this::class.java, "Cleaning up unit test accounts") - testAccountNames.forEach {name -> - SystemLogger.logInfo(this::class.java, "Removing test account $name") - val info = UserAccountInfo.createDefault() - info.username = name - storage.remove(info) - } - } - } - - @Test fun shouldReturnCorrectUserData() { - val data = UserAccountInfo.createDefault() - data.username = "test111" - data.password = "test" - data.rights = 2 - testAccountNames.add("test111") - storage.store(data) - val accountInfo = storage.getAccountInfo("test111") - Assertions.assertEquals(2, accountInfo.rights) - } - - @Test fun shouldAllowStoreUserData() { - val userData = UserAccountInfo.createDefault() - userData.username = "storageTest" - userData.password = "test" - testAccountNames.add("storageTest") - storage.store(userData) - val exists = storage.checkUsernameTaken("storageTest") - Assertions.assertEquals(true, exists) - } - - @Test fun shouldNotAllowDuplicateAccountStorage() { - val userData = UserAccountInfo.createDefault() - userData.username = "bilbo111" - userData.password = "test" - testAccountNames.add("bilbo111") - storage.store(userData) - Assertions.assertThrows(SQLDataException::class.java) { - storage.store(userData) - } - } - - @Test fun shouldAllowRemoveUserInfo() { - val userData = UserAccountInfo.createDefault() - userData.username = "bepis" - userData.password = "test" - testAccountNames.add("bepis") - storage.store(userData) - storage.remove(userData) - Assertions.assertEquals(false, storage.checkUsernameTaken("bepis")) - } - - @Test fun shouldUpdateUserData() { - val userData = UserAccountInfo.createDefault() - userData.username = "borpis" - userData.password = "test" - testAccountNames.add("borpis") - storage.store(userData) - userData.credits = 2 - storage.update(userData) - val data = storage.getAccountInfo(userData.username) - Assertions.assertEquals(2, data.credits, "Wrong data: $data") - } - - @Test fun shouldNotAllowStoreOrUpdateEmptyData() { - val info = UserAccountInfo.createDefault() - Assertions.assertThrows(IllegalStateException::class.java) { - storage.store(info) - } - Assertions.assertThrows(IllegalStateException::class.java) { - storage.update(info) - } - info.username = "test" - Assertions.assertThrows(IllegalStateException::class.java) { - storage.store(info) - } - Assertions.assertThrows(IllegalStateException::class.java) { - storage.update(info) - } - } - - @Test fun shouldSetDefaultValuesWhenDBFieldIsNull() { - val defaultData = UserAccountInfo.createDefault() - - //manually insert a definitely-mostly-null entry into the DB - val conn = storage.getConnection() - conn.use { - val stmt = conn.prepareStatement("INSERT INTO members (username) VALUES (?);") - stmt.setString(1, "nulltestacc") - testAccountNames.add("nulltestacc") - stmt.execute() - } - - var data: UserAccountInfo = UserAccountInfo.createDefault() - Assertions.assertDoesNotThrow { - data = storage.getAccountInfo("nulltestacc") - } - - Assertions.assertEquals(defaultData.password, data.password) - Assertions.assertEquals(defaultData.rights, data.rights) - Assertions.assertEquals(defaultData.credits, data.credits) - Assertions.assertEquals(defaultData.ip, data.ip) - Assertions.assertEquals(defaultData.lastUsedIp, data.lastUsedIp) - Assertions.assertEquals(defaultData.muteEndTime, data.muteEndTime) - Assertions.assertEquals(defaultData.banEndTime, data.banEndTime) - Assertions.assertEquals(defaultData.contacts, data.contacts) - Assertions.assertEquals(defaultData.blocked, data.blocked) - Assertions.assertEquals(defaultData.clanName, data.clanName) - Assertions.assertEquals(defaultData.currentClan, data.currentClan) - Assertions.assertEquals(defaultData.clanReqs, data.clanReqs) - Assertions.assertEquals(defaultData.timePlayed, data.timePlayed) - Assertions.assertEquals(defaultData.lastLogin, data.lastLogin) - Assertions.assertEquals(defaultData.online, data.online) - } - - @Test fun updatingPropertiesOnTheDatabaseEndShouldBePreservedWhenFetchingAccountInfo() { - val conn = storage.getConnection() - conn.use { - val stmt = conn.prepareStatement("INSERT INTO members (username) VALUES (?);") - stmt.setString(1, "dbupdateacc") - testAccountNames.add("dbupdateacc") - stmt.execute() - stmt.close() - - val stmt2 = conn.prepareStatement("UPDATE members SET rights = 2 WHERE username = \"dbupdateacc\";") - stmt2.execute() - } - - val info = storage.getAccountInfo("dbupdateacc") - Assertions.assertEquals(2, info.rights) - info.rights = 1 - storage.update(info) - - val info2 = storage.getAccountInfo("dbupdateacc") - Assertions.assertEquals(1, info2.rights) - - val conn2 = storage.getConnection() - conn2.use { - val stmt = conn2.prepareStatement("UPDATE members SET rights = 2 WHERE username = \"dbupdateacc\";") - stmt.execute() - } - - val info3 = storage.getAccountInfo("dbupdateacc") - Assertions.assertEquals(2, info3.rights) - } - - @Test fun shouldCorrectlyUpdateMultipleChangedValues() { - val userData = UserAccountInfo.createDefault() - userData.username = "borpis2" - userData.password = "test" - testAccountNames.add("borpis2") - storage.store(userData) - - val lastLogin = System.currentTimeMillis() - - userData.credits = 2 - userData.lastLogin = lastLogin - userData.currentClan = "3009scape" - storage.update(userData) - - val data = storage.getAccountInfo(userData.username) - Assertions.assertEquals(2, data.credits, "Wrong data: $data") - Assertions.assertEquals(lastLogin, data.lastLogin, "Wrong data: $data") - Assertions.assertEquals("3009scape", data.currentClan, "Wrong data: $data") - } -} diff --git a/Server/src/test/resources/test.conf b/Server/src/test/resources/test.conf index 15b5ddd9d..fd8697901 100644 --- a/Server/src/test/resources/test.conf +++ b/Server/src/test/resources/test.conf @@ -8,14 +8,14 @@ noauth_default_admin = true #NOTE: If we are not using auth, this determines whe [database] database_name = "global" -database_username = "root" -database_password = "" +database_username = "2009scapetest" +database_password = "2009scapetest" database_address = "127.0.0.1" database_port = "3306" [world] -name = "2009scape" +name = "2009Scape" debug = true dev = true start_gui = false @@ -25,7 +25,7 @@ world_id = "1" country_id = "0" members = true #activity as displayed on the world list -activity = "2009scape classic." +activity = "2009Scape Classic." pvp = false default_xp_rate = 5.0 allow_slayer_reroll = false @@ -49,6 +49,7 @@ max_adv_bots = 100 enable_doubling_money_scammers = true wild_pvp_enabled = false jad_practice_enabled = false +enable_castle_wars = false personalized_shops = false [paths] diff --git a/Server/worldprops/default.conf b/Server/worldprops/default.conf index c32123166..511ab41c6 100644 --- a/Server/worldprops/default.conf +++ b/Server/worldprops/default.conf @@ -1,22 +1,30 @@ [server] +#Log Level - the level of verbosity used for logs. +#"verbose" - ALL logs are shown +#"detailed" - FINE logs are hidden, which is generally bulk/debug info. +#"cautious" - FINE, INFO logs are hidden, meaning this level only shows warnings and errors. +#"silent" - FINE, INFO, WARN logs are hidden, meaning this level only shows errors. +log_level = "verbose" #Secret key - this is sent by the client during login. #Client/Server MUST match or connection is refused. secret_key = "2009scape_development" write_logs = true msip = "127.0.0.1" #preload the map (Increases memory usage by 2GB but makes game ticks smoother) -preload_map = false +preload_map = true #--------Note: If both of the below are false, no database is required to run the server.-------------- #true = login requires password to be correct, passwords are hashed before stored. false = login does not care about the correctness of a password. -use_auth = false #NOTE: THIS MUST BE SET TO TRUE IN PRODUCTION! +use_auth = true #NOTE: THIS MUST BE SET TO TRUE IN PRODUCTION! #true - account data (credits, playtime, etc) is persisted, false - account data is purely temporary #NOTE: this does not affect actual save data, like stats, inventory, etc. -persist_accounts = false #NOTE: THIS MUST BE SET TO TRUE IN PRODUCTION! +persist_accounts = true #NOTE: THIS MUST BE SET TO TRUE IN PRODUCTION! noauth_default_admin = true #NOTE: If we are not using auth, this determines whether or not players are admins by default. #------------------------------------------------------------------------------------------------------ #The limit on how many different accounts a player can log into per day. -daily_accounts_per_ip = 3 +daily_accounts_per_ip = 30 watchdog_enabled = false +connectivity_check_url = "https://google.com,https://2009scape.org" +connectivity_timeout = 500 [database] database_name = "global" @@ -25,13 +33,22 @@ database_password = "" database_address = "127.0.0.1" database_port = "3306" +[integrations] +grafana_logging = false +grafana_log_path = "@data/logs" +#how long grafana will keep old data for in days (anything older is pruned once on startup) +grafana_log_ttl_days = 7 +#discord_ge_webhook = "webhook link" +#discord_moderation_webhook = "webhook link" +#openrsc_integration_webhook = "webhook link" + [world] -name = "2009scape" +name = "SnowScape" #name used for announcements of bots selling items on the GE -name_ge = "2009scape" -debug = true -dev = true +name_ge = "SnowScape" +debug = false +dev = false start_gui = false daily_restart = true #world number @@ -39,9 +56,11 @@ world_id = "1" country_id = "0" members = true #activity as displayed on the world list -activity = "2009scape classic." +activity = "SnowScape" pvp = false -default_xp_rate = 5.0 +#Snowscape: this used to force player xp rate to match this number if set to anything other than 5.0, but that has been disabled +#now it does nothing, as the default exp rate is actually set by Skills.java when a new player is created. +default_xp_rate = 1.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 @@ -61,18 +80,38 @@ increased_door_time = false enable_botting = false max_adv_bots = 100 enable_doubling_money_scammers = true -wild_pvp_enabled = false +wild_pvp_enabled = true jad_practice_enabled = true +#minimum HA value for announcements of bots selling on ge +ge_announcement_limit = 500000 +enable_castle_wars = true personalized_shops = true -bots_influence_ge_price = true +bots_influence_ge_price = false #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 dragonfire shield attack (30 second cooldown instead of 2 minutes) +better_dfs = true +#new player announcement +new_player_announcement = true +#enables holiday random events (no effect on normal random events) +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 +#enable the enhanced deep wilderness, where the area past the members' fence applies a red skull that boosts brawler/pvp drop rates +enhanced_deep_wilderness = true +#enable opening the bank on left-clicking the booth, instead of starting dialogue with the banker +bank_booth_quick_open = true [paths] #path to the data folder, which contains the cache subfolder and such @@ -103,4 +142,4 @@ object_parser_path = "@data/ObjectParser.xml" #path logs are written to logs_path = "@data/logs" bot_data = "@data/botdata" -eco_data = "@data/eco" \ No newline at end of file +eco_data = "@data/eco" diff --git a/Tools/GE Tool.html b/Tools/GE Tool.html deleted file mode 100644 index 971c4e62b..000000000 --- a/Tools/GE Tool.html +++ /dev/null @@ -1,151 +0,0 @@ - - - 💰 GE Tool - - - - - - - -

-
-
-

Import Files

- -
-
- -
- -
- -
-
- -
-
-
- item_configs.json - YES - NO -
-
- -
-
- offer_dispatch.json - YES - NO -
-
-
-
-
- -
-
-
-

Offers

-
-
- - -
-
- - - - - - - - - - - - - - - - - -
Item IDNamePriceAmount
{{ itemId }}{{ offer.name }}{{ offer.value }} gp{{ offer.amount }}
-
-
-
-
- - - - - - - \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 000000000..02614f351 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,29 @@ +version: '3.3' +services: + app: + 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" + restart: always + ports: + - "3306:3306" + volumes: + - "2009scape_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" +volumes: + 2009scape_app: + 2009scape_db: diff --git a/make-release.py b/make-release.py index b9855d3c7..bae99d2ee 100755 --- a/make-release.py +++ b/make-release.py @@ -6,7 +6,7 @@ import os WEB_REPO = '../2009scape.github.io' -NEWS_DIR = 'services/m=news/archives' +NEWS_DIR = 'updates/_posts' LOG_DELIMITER = ';;;;;' DEBUG = False @@ -33,8 +33,8 @@ def get_changelog_html(tag: Tag) -> str: else: log_period = f'{tag.last_tag}..{tag.tag_name}' changelog = subprocess.check_output(['git', 'log', log_period, f'--format=%B{LOG_DELIMITER}']).decode('utf8').split(LOG_DELIMITER) - changelog_html = ''.join(['
  • ' + change.strip().replace('\n', '
    \n') + '
  • \n' for change in changelog if change.strip()]) - changelog_html = f'
      \n{changelog_html}
    ' + changelog_html = ''.join(['- ' + change.strip().replace('\n', '
    \n') + '\n' for change in changelog if change.strip()]) + changelog_html = f'\n{changelog_html}' print('generated changelog:', changelog_html, sep='\n') return changelog_html @@ -43,7 +43,7 @@ def make_news_post(tag: Tag) -> None: changelog_html = get_changelog_html(tag) os.chdir(os.path.join(WEB_REPO, NEWS_DIR)) current_date = datetime.datetime.now().strftime('%Y-%m-%d') - news_filename = current_date + '.html' + news_filename = f'{current_date}-More-changes-in-Gielinor.md' print('news filename:', news_filename) news_post = news_template(current_date, changelog_html) if DEBUG: @@ -56,111 +56,22 @@ def make_news_post(tag: Tag) -> None: def news_template(current_date: str, changelog: str) -> str: - return '''--- -title: More Changes in Gielinor + return f'''--- +title: More changes in Gielinor tags: news layout: newspost -collection: Game Updates -date: '''+ current_date + ''' 00:00:00 +0000 +date: {current_date} 00:00:00 +0000 authors: ryannathans excerpt: "There have been some more changes in Gielinor..." +modtype: "Lead Developer" +avatar: avatar8fa9.gif --- -
    -
    -
    -
    -
    -

    - {{ page.date | date: '%d-%B-%Y' }} -

    -
    -
    -
    -
    -
    -
    -
    -
    -
    {{ page.title }} -
    -
    -
    -
    - - - - - - - - -
    - -
    -
    -
    - - - - - - - - -
    -
    - {{ page.authors }} -
    - -
    ???
    -
    +Greetings Explorers -
    -
    -
    -
    - {{ page.date | date: '%d-%B-%Y' }} -
    -
    -
    -

    Greetings Explorers

    -

    There have been some more changes in Gielinor:

    ''' + changelog + ''' -
    -
    -
    -
    - - - - - - - - -
    - -
    -
    -
    -
    -
    -
    ''' +There have been some more changes in Gielinor: + +{changelog} +''' def main() -> None: diff --git a/run b/run index e1c4fdc10..ed57b2f4a 100755 --- a/run +++ b/run @@ -10,7 +10,7 @@ REFRESH_BUILD=1 GAMESERVER_ONLY=1 -GS_EXEC="cd $GS_SRC && java -Dnashorn.args=--no-deprecation-warning -jar $BUILD_DIR/server.jar" +GS_EXEC="cd $GS_SRC && java -Xmx7g -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